Compare commits

..

473 Commits

Author SHA1 Message Date
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
Tatsuhiro Tsujikawa
94d6320376 Update man pages 2015-02-27 00:57:13 +09:00
Tatsuhiro Tsujikawa
f3a0ab6552 nghttpd: Fix help output so that it can be generated in man/html nicely 2015-02-27 00:56:26 +09:00
Tatsuhiro Tsujikawa
568b744374 Bump up version number to 0.7.5, LT revision to 11:0:6 2015-02-27 00:53:43 +09:00
Tatsuhiro Tsujikawa
00e687506e COPYING: Add 2015 to copyright year 2015-02-27 00:49:36 +09:00
Tatsuhiro Tsujikawa
1c2c5bdd05 Revert "nghttpx: Add missing Downstream::end_upload_data() call in HTTP/2 and SPDY"
This reverts commit ef090d425e.
2015-02-27 00:32:48 +09:00
Tatsuhiro Tsujikawa
30acb41561 Merge branch 'bcard-bcard/address-nghttp2-server' 2015-02-27 00:04:58 +09:00
Tatsuhiro Tsujikawa
3715a89655 Merge branch 'bcard/address-nghttp2-server' of https://github.com/bcard/nghttp2 into bcard-bcard/address-nghttp2-server
Conflicts:
	src/nghttp.cc
2015-02-27 00:02:45 +09:00
Tatsuhiro Tsujikawa
ef090d425e nghttpx: Add missing Downstream::end_upload_data() call in HTTP/2 and SPDY
This ensures that all frontend code calls
Downstream::end_upload_data() when request was all received.
2015-02-26 23:50:21 +09:00
Tatsuhiro Tsujikawa
e1bb06d8ab nghttpx: Remove unused error check 2015-02-26 23:46:42 +09:00
Tatsuhiro Tsujikawa
d1793e3b5a Add missing entry to nghttp2_strerror() 2015-02-26 23:04:38 +09:00
Brian Card
1bdf664f4d Changing signature of numeric_name from numeric_name(addrinfo *addr)
to numeric_name(const struct sockaddr *sa, socklen_t salen) to remove
dependency on addrinfo struct.
2015-02-26 08:59:25 -05:00
Tatsuhiro Tsujikawa
05b8901d69 Call on_invalid_frame_recv_callback on bad HTTP messaging 2015-02-26 22:59:07 +09:00
Tatsuhiro Tsujikawa
1c0d617742 nghttpx: Rename WorkerConfig as LogConfig
This is a sign that we only use thread-local storage for logging only.
2015-02-26 00:02:29 +09:00
Tatsuhiro Tsujikawa
b161dfe573 nghttpx: Move graceful_shutdown flag from WorkerConfig to Worker
A part of an effort to eliminate thread_local WorkerConfig
2015-02-25 22:53:53 +09:00
Tatsuhiro Tsujikawa
c6d019da5f Update doc 2015-02-24 23:53:12 +09:00
Brian Card
b773d63b92 Moving nghttp's numeric_name function to util.cc and using this to generate the address name in HttpServer.cc 2015-02-24 09:50:18 -05:00
Tatsuhiro Tsujikawa
66b6787fbe src: Use "sensitive" to indicate "never indexed" header field 2015-02-24 23:12:12 +09:00
Tatsuhiro Tsujikawa
f2a498e3c4 Disallow upper-cased header field name 2015-02-24 18:45:59 +09:00
Tatsuhiro Tsujikawa
1a2bccd71c nghttpx: Share nghttp2_session_callbacks between objects 2015-02-24 15:21:10 +09:00
Tatsuhiro Tsujikawa
8417275368 nghttpx: Code cleanup 2015-02-24 15:11:09 +09:00
Tatsuhiro Tsujikawa
1646352f3c Update doc 2015-02-24 14:48:58 +09:00
Tatsuhiro Tsujikawa
c98cf045d6 nghttpx: Use omit minor version in case of HTTP/2 in via header and accesslog 2015-02-24 14:43:01 +09:00
Brian Card
933b9636e5 removing additional space after option variable 2015-02-23 07:15:54 -05:00
Brian Card
d10bce3dc8 fixing formatiing 2015-02-23 07:13:03 -05:00
Tatsuhiro Tsujikawa
814c7e68e0 Ignore regular headers if it includes illegal characters.
This commit only affects the library behaviour unless
nghttp2_option_set_no_http_messaging() is used.

We like strict validation against header field name and value against
RFC 7230, but we have already so much web sites and libraries in
public internet which do not obey these rules.  Simply just
terminating stream because of this may break web sites and it is too
disruptive.  So we decided that we should be conservative here so
those header fields containing illegal characters are just ignored.
But we are conservative only for regular headers.  We are strict for
pseudo headers since it is new to HTTP/2 and new implementations
should know the rules better.
2015-02-22 23:13:27 +09:00
Tatsuhiro Tsujikawa
c5c58ccd78 Add note for nghttpx UNIX domain socket support 2015-02-22 18:23:09 +09:00
Tatsuhiro Tsujikawa
1468bcd7b4 Update man pages 2015-02-22 18:10:55 +09:00
Tatsuhiro Tsujikawa
8b533e19bb nghttpx: Remove option name from unix path sample since it is a bit strange 2015-02-22 18:09:37 +09:00
Tatsuhiro Tsujikawa
a55a080b9c Update man pages 2015-02-22 18:02:57 +09:00
Tatsuhiro Tsujikawa
df32a534fc nghttpx: Rename ConnectionHandler::acceptor4_ as acceptor_
This change is motivated by that fact that we use it for UNIX domain
socket as well as IPv4.
2015-02-22 17:59:50 +09:00
Tatsuhiro Tsujikawa
e583a25a8b nghttpx: Fix error found by coverity scan 2015-02-22 17:53:12 +09:00
Tatsuhiro Tsujikawa
4430b06c71 Add parentheses around macro parameters 2015-02-22 17:43:14 +09:00
Tatsuhiro Tsujikawa
6051ff63e0 tests: Fix compile error with gcc-4.7 2015-02-22 17:43:00 +09:00
Tatsuhiro Tsujikawa
da2376effd nghttpx: Add host_unix field to DownstreamAddr to tell it is UNIX domain sock 2015-02-22 17:25:23 +09:00
Tatsuhiro Tsujikawa
0c4ae3dea5 nghttpx: Support UNIX domain socket on frontend
This commit also fixes environment variables used to tell inherited
file descriptors to new binary are stacked up each time new binary is
executed.
2015-02-22 17:25:23 +09:00
Tatsuhiro Tsujikawa
e457c9a414 src: Add util::strieq_l 2015-02-22 15:32:48 +09:00
Tatsuhiro Tsujikawa
997f9233bc nghttpx: Support UNIX domain socket in backend connections 2015-02-22 12:27:51 +09:00
Tatsuhiro Tsujikawa
49781da8f0 nghttpx: Don't need to const_cast to sockaddr* 2015-02-22 12:06:31 +09:00
Tatsuhiro Tsujikawa
7f9de007d0 nghttpx: Fix crash in http/1 backend when backend returns more bytes than CL
This is typically programming mistake that we did not check return
value before doing another.
2015-02-21 21:17:52 +09:00
Tatsuhiro Tsujikawa
c506386997 Add libxml2.m4 for convenience to build nghttp2 from git repo easily 2015-02-21 21:06:19 +09:00
Tatsuhiro Tsujikawa
3144f7de72 asio-sv2: Fix compile error with OS X
Use struct stat.st_mtime instead of st_mtim which seems to be Linux
specific.
2015-02-21 21:02:40 +09:00
Tatsuhiro Tsujikawa
c84a190ac7 nghttpx: Use return 0 instead of break for readability 2015-02-21 17:08:03 +09:00
Tatsuhiro Tsujikawa
a26a597453 nghttpx: Cast configuration value to rlim_t to avoid compile error on 32bit 2015-02-21 16:57:02 +09:00
Tatsuhiro Tsujikawa
20a689ef44 Update doc 2015-02-21 16:53:41 +09:00
Tatsuhiro Tsujikawa
62928ddbcd python: Remove validation offered by nghttp2 lib 2015-02-21 00:20:30 +09:00
Tatsuhiro Tsujikawa
04ef26473a asio-lib: Increase output buffer size 2015-02-21 00:08:10 +09:00
Tatsuhiro Tsujikawa
7897f5b94b asio-lib: Remove validation offered by nghttp2 lib 2015-02-21 00:06:05 +09:00
Tatsuhiro Tsujikawa
9302e3edf4 src: Use util::streq_l to compare against string literal 2015-02-20 23:57:40 +09:00
Tatsuhiro Tsujikawa
ab93a700ce src: Announce h2 ALPN 2015-02-20 23:50:17 +09:00
Tatsuhiro Tsujikawa
2fc1dd77d2 Update doc 2015-02-20 23:30:57 +09:00
Tatsuhiro Tsujikawa
e45c523dc7 Add nghttp2_option_set_no_http_messaging() API function
This API function with nonzero |val| parameter disables HTTP Messaging
validation in nghttp2 library, so that application can use nghttp2
library for non-HTTP use.
2015-02-20 23:26:56 +09:00
Tatsuhiro Tsujikawa
b3846d6c27 Rename NGHTTP2_OPTMASK_NO_HTTP_SEMANTICS with NGHTTP2_OPTMASK_NO_HTTP_MESSAGING 2015-02-20 23:07:48 +09:00
Tatsuhiro Tsujikawa
dec115cff3 Document HTTP Messaging enforcement performed by nghttp2 library 2015-02-20 23:06:14 +09:00
Tatsuhiro Tsujikawa
fe84ec5e8b tiny-nghttpd: Add token lookup for slight optimization 2015-02-20 21:50:02 +09:00
Tatsuhiro Tsujikawa
b39aa43537 Use C-style comment 2015-02-20 21:49:47 +09:00
Tatsuhiro Tsujikawa
2216fd2bc1 tiny-nghttpd: Remove validations offered by lib 2015-02-20 20:58:11 +09:00
Tatsuhiro Tsujikawa
83d9ee1fd1 Update http-parser 2015-02-20 19:51:41 +09:00
Tatsuhiro Tsujikawa
4424cf5b65 nghttpx: Fix 1 second delay in HTTP/2 backend connection 2015-02-20 19:48:35 +09:00
Tatsuhiro Tsujikawa
00a83a44b4 nghttp: Simplify checking 2015-02-20 19:35:54 +09:00
Tatsuhiro Tsujikawa
83952ef0af Insert '_' before header name nghttp2_http_flag to consistent with token 2015-02-20 19:30:34 +09:00
Tatsuhiro Tsujikawa
3dbd2d31bd Early return after :method is seen 2015-02-20 19:26:34 +09:00
Tatsuhiro Tsujikawa
f755ea3894 nghttpx: Simplify HttpsUpstream::on_read 2015-02-20 19:24:48 +09:00
Tatsuhiro Tsujikawa
50c4aa061f nghttpx: Response with 503 when re-submission to backend failed 2015-02-20 19:23:52 +09:00
Tatsuhiro Tsujikawa
6657966334 nghttpx: HttpsUpdatem: Don't send error_page if response state is MSG_COMPLETE 2015-02-20 01:25:18 +09:00
Tatsuhiro Tsujikawa
512aa8942a nghttp: Fix -H does not work with -u upgrade request 2015-02-20 01:16:49 +09:00
Tatsuhiro Tsujikawa
b371331297 Merge branch 'enforce-http-semantics' 2015-02-20 01:02:06 +09:00
Tatsuhiro Tsujikawa
489b4f307c nghttp, nghttpd, nghttpx: Remove validations libnghttp2 offers 2015-02-20 01:01:10 +09:00
Tatsuhiro Tsujikawa
b157d4ebb2 Validate HTTP semantics by default
Previously we did not check HTTP semantics and it is left out for
application.  Although checking is relatively easy, but they are
scattered and error prone.  We have implemented these checks in our
applications and also feel they are tedious.  To make application
development a bit easier, this commit adds basic HTTP semantics
validation to library code.  We do following checks:

server:

* HEADERS is either request header or trailer header.  Other type of
header is disallowed.

client:

* HEADERS is either zero or more non-final response header or final
  response header or trailer header.  Other type of header is
  disallowed.

For both:

* Check mandatory pseudo header fields.
* Make sure that content-length matches the amount of DATA we
  received.

If validation fails, RST_STREAM of type PROTOCOL_ERROR is issued.
2015-02-20 01:01:10 +09:00
Tatsuhiro Tsujikawa
9845729709 nghttp: Update resource timing terminology according to Resource Timing TR 2015-02-18 00:45:52 +09:00
Tatsuhiro Tsujikawa
dbfc02cba0 nghttpx: Issue RST_STREAM if END_STREAM seen without final response 2015-02-18 00:36:49 +09:00
Tatsuhiro Tsujikawa
d1a1e882bf nghttpx: Fix request re-submission bug in HTTP/2 backend 2015-02-17 23:15:53 +09:00
Tatsuhiro Tsujikawa
799778af69 Revert "nghttpx: Fix request resubmit bug on HTTP/2 backend connection check"
This reverts commit d45f5a51e4.
2015-02-17 22:28:03 +09:00
Tatsuhiro Tsujikawa
d45f5a51e4 nghttpx: Fix request resubmit bug on HTTP/2 backend connection check 2015-02-17 21:50:32 +09:00
Tatsuhiro Tsujikawa
e479abc4ab Update README.rst 2015-02-15 16:48:37 +09:00
Tatsuhiro Tsujikawa
37706c2930 Untabify README.rst 2015-02-15 16:38:42 +09:00
Tatsuhiro Tsujikawa
bb856fe4c5 nghttp: Fix unaligned field output in --stat 2015-02-15 16:24:04 +09:00
Tatsuhiro Tsujikawa
f4c0a243e7 Bump up version number to 0.7.5-DEV 2015-02-15 13:14:07 +09:00
Tatsuhiro Tsujikawa
ba9ea1831c Update man pages 2015-02-15 13:07:23 +09:00
Tatsuhiro Tsujikawa
c7126663df Bump up version number to 0.7.4, LT revision to 10:0:5 2015-02-15 13:06:35 +09:00
Tatsuhiro Tsujikawa
aa1339d9c0 Update README.rst 2015-02-15 13:00:19 +09:00
Tatsuhiro Tsujikawa
f87aaf1fbf nghttp: Show '*' to mark pushed resources in --stat output 2015-02-15 12:46:00 +09:00
Tatsuhiro Tsujikawa
685dabc2d6 nghttp: Don't request already pushed resources 2015-02-15 12:36:18 +09:00
Tatsuhiro Tsujikawa
4cafdb7670 nghttp: Show received size in --stat 2015-02-15 12:36:13 +09:00
Tatsuhiro Tsujikawa
a8889971db doc: Make html rule html-local 2015-02-15 12:18:13 +09:00
Tatsuhiro Tsujikawa
1fbaae837c Sort sphix theme files 2015-02-15 12:13:36 +09:00
Tatsuhiro Tsujikawa
e8c294b701 Add bash_completion files for nghttp, nghttpd, nghttpx and h2load 2015-02-15 12:05:27 +09:00
Tatsuhiro Tsujikawa
9c30211da9 Ignore all incoming bytes when first SETTINGS is not received 2015-02-15 01:20:10 +09:00
Tatsuhiro Tsujikawa
58d3b5b4a0 nghttpx: Fix occasional HTTP/2 backend connection failure with proxy
Previously if HTTP/1 proxy is used for backend connection, we read all
incoming bytes from proxy including response body, which may be part
of HTTP/2 protocol.  While investigating this issue, we found that
http_parser_execute() returns 1-less length when we call
http_parser_pause() inside on_headers_complete callback.  To
workaround this, we increment the return value by 1.  This commit also
fixes possible segmentation fault error, which could be caused by the
lack of stopping libev watcher in disconnect().
2015-02-15 01:09:10 +09:00
Tatsuhiro Tsujikawa
442572c1f4 Handle situation where request HEADERS in queue is reset by RST_STREAM
Previously we did not handle the situation where RST_STREAM is
submitted against a stream while requet HEADERS which opens that
stream is still in queue.  Due to max concurrent streams limit,
RST_STREAM is sent first, and then request HEADERS, which effectively
voids RST_STREAM.

In this commit, we checks RST_STREAM against currently pending request
HEADERS in queue and if stream ID matches, we mark that HEADERS as
canceled and RST_STREAM is not sent in this case.  The library will
call on_frame_not_sent_callback for the canceled HEADERS with error
code from RST_STREAM.
2015-02-13 23:48:16 +09:00
Tatsuhiro Tsujikawa
011e3b325d nghttpx: Cancel backend request when frontend HTTP/1 connection is lost 2015-02-13 22:41:50 +09:00
Tatsuhiro Tsujikawa
a473641e3f Update doc 2015-02-12 23:54:38 +09:00
Tatsuhiro Tsujikawa
6f4f252907 nghttpd: Handle return value from server.run() 2015-02-12 23:18:25 +09:00
Tatsuhiro Tsujikawa
0d2bbead9d Add test to submit PUSH_PROMISE without associated stream open 2015-02-12 23:09:01 +09:00
Tatsuhiro Tsujikawa
16475f6613 Update doc 2015-02-12 23:04:21 +09:00
Tatsuhiro Tsujikawa
0bdacd3e77 Code cleanup 2015-02-12 23:02:17 +09:00
Tatsuhiro Tsujikawa
57a50f981b Refactor session_prep_frame to eliminate framerv 2015-02-12 22:58:43 +09:00
Tatsuhiro Tsujikawa
115d7133a0 nghttpx: Don't check HEADERS category in on_frame_not_send_callback 2015-02-12 22:44:29 +09:00
Tatsuhiro Tsujikawa
eb94603c51 Make nghttp2_session_get_stream_user_data work inside nghttp2_on_frame_not_send_callback 2015-02-12 22:41:57 +09:00
Tatsuhiro Tsujikawa
7d4a6aa179 Add test for submission ordering of HEADERS and its RST_STREAM 2015-02-12 21:28:20 +09:00
Tatsuhiro Tsujikawa
354de30874 Make default min frame payload size to 16K
Previously we use 16K - 9 bytes (frame header) as frame payload size
so that whole frame fits in 1 TLS record size (16K).  But it turns out
that in proxy use case, we will receive 16K payload from backend and
we have to split it into 2 odd looking frames (16K - 9 and 9), and
latter is highly inefficient.  To avoid this situation, we decided to
use min frame payload size to 16K.  Since we operates on TLS as stream
of data, we are not so much restricted in its record size.
2015-02-12 00:09:18 +09:00
Tatsuhiro Tsujikawa
c462093555 src: Use same convention for len parameter in TLS I/O with cleartext I/O 2015-02-11 23:22:53 +09:00
Tatsuhiro Tsujikawa
364772f508 src: Fix error reported by coverity scan 2015-02-11 23:20:13 +09:00
Tatsuhiro Tsujikawa
ae0100a9ab nghttpx: Refactor worker interface 2015-02-11 22:49:03 +09:00
Tatsuhiro Tsujikawa
756e2b3e9b nghttp, h2load: Fix regression introduced in 795a22a
We missed wb.reset(), which makes write buffer's capacity becomes 0
and communication stalls eventually.
2015-02-11 21:42:11 +09:00
Tatsuhiro Tsujikawa
57d02f5c57 Update README.rst 2015-02-11 01:05:58 +09:00
Tatsuhiro Tsujikawa
73b999a662 src: Fix crash 2015-02-11 01:05:22 +09:00
Tatsuhiro Tsujikawa
4401f697e5 src: Try to write immediately after read 2015-02-11 00:44:30 +09:00
Tatsuhiro Tsujikawa
f360b5c1e3 src: Prefer std::equal 2015-02-11 00:20:33 +09:00
Tatsuhiro Tsujikawa
07fdaaba45 src: Use case-insensitive match when parsing Link header field 2015-02-10 23:29:45 +09:00
Tatsuhiro Tsujikawa
7fa62c9904 src: Clean up string utlity functions 2015-02-10 23:16:34 +09:00
Tatsuhiro Tsujikawa
3e2714810a src: Fix invalid memory access 2015-02-10 21:28:17 +09:00
Tatsuhiro Tsujikawa
68866f53b0 src: Use nullptr instead of 0, since they are char* 2015-02-09 23:45:20 +09:00
Tatsuhiro Tsujikawa
ad8e9a4741 src: Ignore URI with non-empty anchor parameter 2015-02-09 23:25:10 +09:00
Tatsuhiro Tsujikawa
3c5d8f446b src: Fix typo 2015-02-09 23:25:10 +09:00
Tatsuhiro Tsujikawa
2e425e3cb6 src: Support backslash escapes in quoted-string when parsing Link header 2015-02-09 23:25:10 +09:00
Tatsuhiro Tsujikawa
1b00bc1929 src: Support rel with quoted value in Link header parser 2015-02-09 23:25:10 +09:00
Tatsuhiro Tsujikawa
6b28e033de Fix compile error and memory leak 2015-02-09 22:37:11 +09:00
Tatsuhiro Tsujikawa
eec8870ac1 Fix bug that client may send PROTOCOL_ERROR upon canceled push stream
Previously we treat stream in NGHTTP2_STREAM_RESERVED state specially,
that is we don't increment or decrement streams counts if stream is in
that state.  Because of this, we don't change the stream state to
NGHTTP2_STREAM_CLOSING if stream is in NGHTTP2_STREAM_RESERVED.  But
it turns out that it causes a problem.  If client canceled pushed
stream before push response HEADERS, stream is still in
NGHTTP2_STREAM_RESERVED state.  If push response HEADERS arrived in
this state, library happily accepts it and passed to application.

With this commit, this bug was corrected.  We now change stream state
to NGHTTP2_STREAM_CLOSING even if it was in NGHTTP2_STREAM_RESERVED
state.  We now use NGHTTP2_STREAM_FLAG_PUSH to determine whether we
have to increase/decrase stream count.
2015-02-09 22:23:20 +09:00
Tatsuhiro Tsujikawa
d151759f8a nghttpx: Fix location rewrite, take 2 2015-02-08 21:26:47 +09:00
Tatsuhiro Tsujikawa
807d39abe3 nghttpx: Fix location rewrite does not work 2015-02-08 18:54:24 +09:00
Tatsuhiro Tsujikawa
7b81136bb3 Merge branch 'nghttpx-server-push' 2015-02-08 17:49:13 +09:00
Tatsuhiro Tsujikawa
75d7e5abe0 Update man pages 2015-02-08 17:48:18 +09:00
Tatsuhiro Tsujikawa
4d47c31ebe src: Refactor parse_next_link_header_once 2015-02-08 17:47:21 +09:00
Tatsuhiro Tsujikawa
9e723b6b1d src: Rename LinkHeader.url as LinkHeader.uri 2015-02-08 17:29:38 +09:00
Tatsuhiro Tsujikawa
7aff00496a nghttpx: Log push request headers 2015-02-08 17:25:21 +09:00
Tatsuhiro Tsujikawa
0efdeab1db nghttpx: Fix handling of return value from nghttp2_submit_push_promise 2015-02-08 17:21:27 +09:00
Tatsuhiro Tsujikawa
b01d0c88fe Document nghttpx server push feature 2015-02-08 17:13:36 +09:00
Tatsuhiro Tsujikawa
502b552b68 nghttpx: Add --no-server-push option 2015-02-08 16:19:12 +09:00
Tatsuhiro Tsujikawa
8c90e5314d src: Update doc 2015-02-08 16:10:01 +09:00
Tatsuhiro Tsujikawa
af960f1982 nghttpx: Don't push if http2 proxy is used 2015-02-08 16:10:01 +09:00
Tatsuhiro Tsujikawa
8b4291edcb integration: Add server push test 2015-02-08 16:10:01 +09:00
Tatsuhiro Tsujikawa
45a47936e0 integration: Update doc 2015-02-08 16:10:01 +09:00
Tatsuhiro Tsujikawa
7c09d5eb8d nghttpx: PUSH_PROMISE from client is handled by library 2015-02-08 16:10:01 +09:00
Tatsuhiro Tsujikawa
88f0bc70c4 nghttpx: Reworkd inherited request headers in PUSH_PROMISE 2015-02-08 16:10:01 +09:00
Tatsuhiro Tsujikawa
b14cfaf308 src: Store token in Header object to avoid additional lookups 2015-02-08 16:10:01 +09:00
Tatsuhiro Tsujikawa
8dd8d68b83 src: Make index of header int16_t 2015-02-08 16:10:01 +09:00
Tatsuhiro Tsujikawa
c55d7343ca nghttpx: Support server push using Link header field
nghttpx server push is initiated by looking for Link header field from
backend server response.  Currently we only enable server push for
HTTP/1 backend and without HTTP/2 proxy mode.  The URIs which have
rel=preload are eligible to resource to be pushed.
2015-02-08 16:10:00 +09:00
Tatsuhiro Tsujikawa
4dea318b5b nghttpx: Fix compile error if SOCK_NONBLOCK is undefined 2015-02-08 00:49:56 +09:00
Tatsuhiro Tsujikawa
795a22a320 src: Remove ringbuf.h, use buffer.h instead 2015-02-06 23:40:34 +09:00
Tatsuhiro Tsujikawa
b4b2ddad3b src: Rewrite defer function template 2015-02-06 23:27:15 +09:00
Tatsuhiro Tsujikawa
6ff67ae869 src: Move array_size to nghttp2 namespace 2015-02-06 22:44:09 +09:00
Tatsuhiro Tsujikawa
33879219ff Refactor ALPN/NPN protocol selection and introduce NGHTTP2_PROTO_ALPN macro 2015-02-06 22:35:30 +09:00
Tatsuhiro Tsujikawa
4956bdc4da src: Use std::copy_n 2015-02-06 21:35:03 +09:00
Tatsuhiro Tsujikawa
b165775811 nghttpx: Refactor CertLookupTree 2015-02-06 21:25:43 +09:00
Tatsuhiro Tsujikawa
90746cdd0e nghttpx: Fix compile error with OpenSSL 1.0.2 2015-02-06 21:14:04 +09:00
Tatsuhiro Tsujikawa
f93a2b71a1 Treat first SETTINGS bearing ACK as connection error 2015-02-06 01:22:41 +09:00
Tatsuhiro Tsujikawa
2dd6353e24 examples/client: Send SETTINGS 2015-02-06 01:22:29 +09:00
Tatsuhiro Tsujikawa
208abd8cc5 Fix debug output 2015-02-06 01:18:23 +09:00
Tatsuhiro Tsujikawa
4cda09beff src: Prefer std::array 2015-02-06 00:15:43 +09:00
Tatsuhiro Tsujikawa
9a2d36fc6c src: Use std::copy instead of std::copy_n since gcc-4.7 does not have it 2015-02-06 00:15:43 +09:00
Tatsuhiro Tsujikawa
ab6663c785 src: Use std::array instead of std::vector if size is compile time constant 2015-02-06 00:15:43 +09:00
Tatsuhiro Tsujikawa
54851ef7a6 src: Move make_unique to nghttp2 namespace 2015-02-06 00:15:43 +09:00
Tatsuhiro Tsujikawa
f8f9b36acd http2: Use std::array for indexing headers 2015-02-06 00:15:43 +09:00
Tatsuhiro Tsujikawa
6774fa6e07 buffer: Refactor 2015-02-06 00:15:43 +09:00
Tatsuhiro Tsujikawa
7baf6f781e memchunk: Refactor 2015-02-06 00:15:42 +09:00
Tatsuhiro Tsujikawa
2349a03882 nghttpx: Fix regression HTTP/2 upgrade does not work 2015-02-06 00:15:13 +09:00
Tatsuhiro Tsujikawa
3904550d5d nghttpd: Shut up travis 2015-02-05 22:49:19 +09:00
Tatsuhiro Tsujikawa
be3ee91e90 nghttpd: Fix compile error on travis 2015-02-05 21:50:20 +09:00
Tatsuhiro Tsujikawa
d4f87ce29f nghttpd: Fix multiple push configuration does not work 2015-02-05 21:31:16 +09:00
Tatsuhiro Tsujikawa
1216d7d912 nghttpx: Connection: Explicit assignment to double field 2015-02-05 21:26:55 +09:00
Tatsuhiro Tsujikawa
f3b247e4c8 nghttpx: Add missing rev start in HTTP/2 backend 2015-02-05 03:05:34 +09:00
Tatsuhiro Tsujikawa
7c75d9db98 nghttpx: Set nghttp2_option_set_peer_max_concurrent_streams for HTTP/2 backend 2015-02-05 03:05:34 +09:00
Tatsuhiro Tsujikawa
b2fb888363 Share I/O code with all upstreams/downstream objects 2015-02-05 03:05:34 +09:00
Tatsuhiro Tsujikawa
a4d729d36b nghttpx: Return HTTP error on downstream parser failure on HTTPS upstream 2015-02-04 21:30:05 +09:00
Tatsuhiro Tsujikawa
83200f3080 Merge branch 'rewrite-host' 2015-02-04 01:44:40 +09:00
Tatsuhiro Tsujikawa
a14c614c10 Document use case of --no-host-rewrite 2015-02-04 01:43:48 +09:00
Tatsuhiro Tsujikawa
a68c4c1e3c nghttpx: Add --no-host-rewrite option 2015-02-04 01:42:26 +09:00
Tatsuhiro Tsujikawa
82f90f9030 nghttpx: Rewrite :authority and host header field
We don't rewrite them if -s or -p is used
2015-02-04 01:42:19 +09:00
Tatsuhiro Tsujikawa
b707cfe986 nghttpx: Fix busy loop when HTTP/2 backend reset after connection established
We have now Downstream retry count to be limited to 5 times.  At 6th
failure, we send 503 message to client.
2015-02-03 01:47:04 +09:00
Tatsuhiro Tsujikawa
d37fc8f3a6 src: Fix compiler warning 2015-02-02 22:40:41 +09:00
Tatsuhiro Tsujikawa
9f5f724147 nghttpd, nghttpx: Don't log error NGHTTP2_ERR_BAD_PREFACE 2015-02-02 00:20:44 +09:00
Tatsuhiro Tsujikawa
e2bbc94616 Use NGHTTP2_PROTOCOL_ERROR when peer exceeds MAX_CONCURRENT_STREAMS limit
Kudos to h2spec to find this details
2015-02-02 00:14:17 +09:00
Tatsuhiro Tsujikawa
928b49a916 Update man pages 2015-02-01 18:33:24 +09:00
Tatsuhiro Tsujikawa
56c2fd6c5b help2rst.py: Strip trailing ':' after section 2015-02-01 18:32:51 +09:00
Tatsuhiro Tsujikawa
267f877255 Update man pages 2015-02-01 18:19:16 +09:00
Tatsuhiro Tsujikawa
36e216d24a src: Delete unused source files 2015-02-01 18:12:57 +09:00
Tatsuhiro Tsujikawa
73d231b1bb Update README.rst 2015-02-01 18:08:54 +09:00
Tatsuhiro Tsujikawa
cabb7c73cd nghttp: Widen column for complete and request to account for + 2015-02-01 18:07:53 +09:00
Tatsuhiro Tsujikawa
3a37ed97f4 nghttp: Rewrite statistics output with -s option
Now timing information (completion, request, processing), status code
and request path are listed in the order by completion time.  This
ordering is very convenient to check resource prioritization
validation.
2015-02-01 17:59:49 +09:00
Tatsuhiro Tsujikawa
0f14c93fa4 nghttp: Refactor 2015-02-01 17:21:13 +09:00
Tatsuhiro Tsujikawa
f321ee5a61 nghttp: Record request time just before transmission of request 2015-02-01 16:58:58 +09:00
Tatsuhiro Tsujikawa
e9eae3fb61 doc: Add output section to h2load man page 2015-02-01 16:36:58 +09:00
Tatsuhiro Tsujikawa
17de036d85 h2load: Code cleanup 2015-01-31 23:54:03 +09:00
Tatsuhiro Tsujikawa
a91e0de06c h2load: Add request stats (time for request min, max, mean and sd) 2015-01-31 23:49:30 +09:00
Tatsuhiro Tsujikawa
bbc34904c1 Workaround stream treated inferior when it hits connection window limit 2015-01-31 15:49:10 +09:00
Tatsuhiro Tsujikawa
f1049a66e2 nghttpx: Detach DownstreamConnection early 2015-01-31 01:11:55 +09:00
Tatsuhiro Tsujikawa
5a497b9f30 nghttpx: Update doc 2015-01-30 21:33:18 +09:00
Tatsuhiro Tsujikawa
b4ad0a30af Fix typo 2015-01-30 21:32:41 +09:00
Tatsuhiro Tsujikawa
1816738b3c nghttpx: Change frontend write buffer size to 32768 2015-01-30 21:32:21 +09:00
Tatsuhiro Tsujikawa
4b0b036d3b Update man pages 2015-01-29 23:29:52 +09:00
Tatsuhiro Tsujikawa
0a0618baac nghttpx: Add test for util::duration_str, rename util::parse_duration_with_unit 2015-01-29 23:28:47 +09:00
Tatsuhiro Tsujikawa
e03f36eeeb nghttpx: Use <DURATION> instead of <T> 2015-01-29 23:23:30 +09:00
Tatsuhiro Tsujikawa
6b1ef95d3f nghttpx: Replace RingBuf with sequential Buffer
It turns out that we don't need circular buffer functionality.  We
replaced RingBuf with simple sequential Buffer.
2015-01-29 22:57:56 +09:00
Tatsuhiro Tsujikawa
147bc45658 nghttpx: Refactor memchunk a bit 2015-01-29 21:18:30 +09:00
Tatsuhiro Tsujikawa
00555dc7bb nghttpx: Use TCP_DEFER_ACCEPT if available 2015-01-29 21:14:44 +09:00
Tatsuhiro Tsujikawa
d1a4002b22 nghttpx: Remove --accept-delay and --num-accept options 2015-01-29 20:58:47 +09:00
Tatsuhiro Tsujikawa
8ddad1a53d nghttpx: Remove shrinking memchunks for now
It requires more careful optimization.  Remove it for now.
2015-01-28 21:25:22 +09:00
Tatsuhiro Tsujikawa
96e66b1a81 nghttpx: Make num_accept 0 on graceful shutdown
Make num_accept unlimited so that we can accept all pending
connections waiting in listen queue.
2015-01-28 21:02:31 +09:00
Tatsuhiro Tsujikawa
19429abd07 nghttpx: Make --accept-delay default to 10ms 2015-01-28 21:00:47 +09:00
Tatsuhiro Tsujikawa
243a8135a6 Merge branch 'B4dM4n-supplementary_group_access' 2015-01-28 20:58:38 +09:00
Fabian Möller
3167aa4081 nghttpx: set the supplementary group access list 2015-01-28 20:56:05 +09:00
Tatsuhiro Tsujikawa
10a4926648 Merge branch 'szepeviktor-patch-1' 2015-01-28 20:51:45 +09:00
Tatsuhiro Tsujikawa
67f50770f5 Merge branch 'patch-1' of https://github.com/szepeviktor/nghttp2 into szepeviktor-patch-1 2015-01-28 20:51:03 +09:00
Tatsuhiro Tsujikawa
93daa98608 src: Make option suggestion work with unknown option followed by '=' 2015-01-28 00:58:42 +09:00
Viktor Szépe
63675f0a47 Better usability for apt packages 2015-01-27 16:58:08 +01:00
Tatsuhiro Tsujikawa
f8765be817 nghttpx: Make --backend-keep-alive-timeout default to 2s 2015-01-28 00:47:09 +09:00
Tatsuhiro Tsujikawa
f0c7839f25 nghttpx: Clarify --num-accept=0 case 2015-01-28 00:39:56 +09:00
Tatsuhiro Tsujikawa
6a39de0ae5 nghttpx: Accept s or ms as unit for <T> argument 2015-01-28 00:36:44 +09:00
Tatsuhiro Tsujikawa
402ebb277f nghttpx: Add --num-accept and --accept-delay options 2015-01-27 23:47:56 +09:00
Tatsuhiro Tsujikawa
4e68ca8233 integration: Add tests about via header field manipulation
This test reveals bug in SPDY upstream code, and contains its fix.
2015-01-27 00:19:57 +09:00
Tatsuhiro Tsujikawa
95b3e2f140 integration: Fix typo 2015-01-27 00:19:41 +09:00
Tatsuhiro Tsujikawa
f412ae442b Bump up version number to v0.7.4-DEV 2015-01-25 23:02:43 +09:00
Tatsuhiro Tsujikawa
01313c1241 Update man pages 2015-01-25 23:00:43 +09:00
Tatsuhiro Tsujikawa
99afea05b9 Bump up version number to 0.7.3 2015-01-25 22:58:43 +09:00
Tatsuhiro Tsujikawa
b99b83812b nghttpd: RST_STREAM if te header field contains value other than trailers 2015-01-25 22:58:43 +09:00
Tatsuhiro Tsujikawa
0b48448270 nghttpx: RST_STREAM for invalid request header values 2015-01-25 22:58:43 +09:00
Tatsuhiro Tsujikawa
2a56a3d9ea nghttpx: Fix te request header handling
Checking against "trailers" is enough for now.
2015-01-25 22:58:43 +09:00
Tatsuhiro Tsujikawa
1883bdaf1d Bump up version number to 0.7.3-DEV 2015-01-25 22:53:34 +09:00
Tatsuhiro Tsujikawa
82cd23e40b Update man pages 2015-01-25 21:49:12 +09:00
Tatsuhiro Tsujikawa
d56e167c54 Bump up version number to 0.7.2, LT revision to 9:0:4 2015-01-25 21:48:36 +09:00
Tatsuhiro Tsujikawa
3e5765831f integration: Add missing alt-server.key and alt-server.crt to EXTRA_DIST 2015-01-25 21:35:45 +09:00
Tatsuhiro Tsujikawa
b9c6162cd5 nghttp: Use compatible cipher list 2015-01-25 17:53:17 +09:00
Tatsuhiro Tsujikawa
68510f1282 nghttpx: SpdyUpstream: Handle error from error_reply 2015-01-25 15:37:09 +09:00
Tatsuhiro Tsujikawa
4b58b25c19 nghttpx: Refactor code to build cert_tree, add SNI test 2015-01-25 15:36:14 +09:00
Brian Card
516a2f0efc Adding an address parameter that allows nghttpd to bind to a non-default address. Both IPv4 and IPv6 addresses are supported. In addition with verbose mode enable the address that the webserver binds to is now printed in addition to the port. 2015-01-24 15:26:49 -05:00
Tatsuhiro Tsujikawa
fbd9bcb00e Use _LDADD to specify libraries instead of _LDFLAGS
This is because _LDFLAGS comes before _LDADD.  If we specify a library
and another library in _LDADD depends on it, we get undefined
reference error.
2015-01-25 00:58:30 +09:00
Tatsuhiro Tsujikawa
682db00ba9 integration: Add tests for TE request header field 2015-01-24 16:02:03 +09:00
Tatsuhiro Tsujikawa
434e80dc7b nghttpx: Reset stream if TE header field contains other than trailer 2015-01-24 15:31:59 +09:00
Tatsuhiro Tsujikawa
5c93a113f3 Document about make itprep 2015-01-24 01:05:20 +09:00
Tatsuhiro Tsujikawa
6b4b7bef23 nghttpx: Reset stream if request header field name is malformed 2015-01-24 00:37:26 +09:00
Tatsuhiro Tsujikawa
8d5c893929 integration: Add Cookie crumbling tests 2015-01-24 00:27:12 +09:00
Tatsuhiro Tsujikawa
b943fbb002 integration: Add test for assembling Cookie works toward HTTP/1 link 2015-01-24 00:17:11 +09:00
Tatsuhiro Tsujikawa
9a89db575a nghttpx: Reject multiple Content-Length even if their values are identical 2015-01-24 00:07:28 +09:00
Tatsuhiro Tsujikawa
0fcfe16dc5 integration: Document each test cases 2015-01-24 00:01:14 +09:00
Tatsuhiro Tsujikawa
990f9ed4de integration: Split up single file to 3 based on frontend type 2015-01-23 23:37:11 +09:00
Tatsuhiro Tsujikawa
0f4b0966ef nghttpx: Merge all options settings in one SSL_CTX_set_options 2015-01-23 23:32:01 +09:00
Tatsuhiro Tsujikawa
ecfd593076 nghttpx: Call ConnectBlocker::on_success when connection is established 2015-01-23 23:00:18 +09:00
Tatsuhiro Tsujikawa
3c6b75fb2b Add make itprep target to go get dependencies 2015-01-23 22:55:42 +09:00
Tatsuhiro Tsujikawa
d1f06b09cd integration: Add TLS key and certificate for testing 2015-01-23 21:25:21 +09:00
Tatsuhiro Tsujikawa
3a94ad709c Merge branch 'acesso-patch-1' 2015-01-23 21:17:33 +09:00
acesso
7ea8037ee1 Use fcntl and FD_CLOEXEC if O_CLOEXEC is undefined
Same reported at #87 but at src/shrpx_config.cc src/instead of util.cc
2015-01-23 21:17:06 +09:00
Tatsuhiro Tsujikawa
064bfcc9d2 Fix test failure 2015-01-23 01:29:41 +09:00
Tatsuhiro Tsujikawa
a0028ea8ef nghttpx: Add logging when downstream connect is blocked by connect_blocker 2015-01-23 00:15:38 +09:00
Tatsuhiro Tsujikawa
d174ffeb00 memchunk: Rename append_cstr as append
We may later add append(const T &), where we require T for .size() and
.data().  We can coexist former append_cstr and new one.  If we pass
string literal, append(const (&)[N]) is selected.
2015-01-22 23:54:30 +09:00
Tatsuhiro Tsujikawa
5a93bedf72 nghttpx: Make error page modern 2015-01-22 23:51:22 +09:00
Tatsuhiro Tsujikawa
4a0dba08b9 nghttpx: Set Connection: close after graceful shutdown in HTTP/1 upstream 2015-01-22 23:46:35 +09:00
Tatsuhiro Tsujikawa
b685747643 Add nghttp2_submit_shutdown_notice() to start graceful shutdown
nghttp2_submit_shutdown_notice() is used to notify the client that
graceful shutdown is started.  We expect that after this call, the
server application should send another GOAWAY using
nghttp2_submit_goaway() with appropriate last_stream_id.  In this
commit, we also added nghttp2_session_get_last_proc_stream_id(), which
can be used as last_stream_id parameter.

This commit implements graceful shutdown in nghttpx.  The integration
test for graceful shutdown is also added.
2015-01-22 23:21:58 +09:00
Tatsuhiro Tsujikawa
76a97b9718 nghttpx: Update last_write_time_ after SSL_write
SSL_write may return error indicating blocking until all given data
are written.  Because of this, it is preferable to update
last_write_time_ after SSL_write regardless of its return value.
2015-01-22 21:14:16 +09:00
Tatsuhiro Tsujikawa
ac1cc56fbb nghttpx: Stop wev_ on h1 backend connect failure 2015-01-22 21:13:34 +09:00
Tatsuhiro Tsujikawa
f604cbae70 nghttpx: Fix shutdown too early with QUIT signal if num_worker == 1 2015-01-22 01:46:25 +09:00
Tatsuhiro Tsujikawa
91ae5291cc nghttpx: Cache string representation of time for logging 2015-01-21 23:52:30 +09:00
Tatsuhiro Tsujikawa
5770c6bd04 nghttpx: Return 503 on hard disconnect in HTTP/2 backend 2015-01-21 23:30:48 +09:00
Tatsuhiro Tsujikawa
7492f628aa nghttpx: Use ConnectBlocker on h1 backend connect attempt failure 2015-01-21 23:11:47 +09:00
Tatsuhiro Tsujikawa
2f788aa214 nghttpx: Return 503 when h1 backend connect attempt failed 2015-01-21 23:08:13 +09:00
Tatsuhiro Tsujikawa
cee22df073 nghttpx: Always close connection in HttpsUpstream::error_reply 2015-01-21 23:03:39 +09:00
Tatsuhiro Tsujikawa
e219d6a94f nghttpx: Clarify error_reply upstream method
There is no application level failure in h1 upstream.  For h2, SPDY
upstreams, don't call DIE(), instead return -1 to delete handler.
2015-01-21 22:55:00 +09:00
Tatsuhiro Tsujikawa
41e72064e0 nghttpx: Log error in INFO level when frame cannot be sent 2015-01-21 22:49:00 +09:00
Tatsuhiro Tsujikawa
f302f1a830 integration: Fix typo 2015-01-21 22:47:22 +09:00
Tatsuhiro Tsujikawa
c0fc726955 nghttpx: Call signal_write in HttpsUpstream::on_downstream_abort_request 2015-01-21 22:45:52 +09:00
Tatsuhiro Tsujikawa
041d9863c2 nghttpx: Set request_start_time_ at Downstream ctor
.. so that we can avoid the problem that request_start_time_ is 0
2015-01-21 22:28:15 +09:00
Tatsuhiro Tsujikawa
3c5ca63b6f nghttpx: Fix TLS write limit does not work 2015-01-21 21:47:25 +09:00
Tatsuhiro Tsujikawa
53a41c953d nghttpx: Don't call ev_TYPE_set macro while watcher is active 2015-01-21 21:43:49 +09:00
Tatsuhiro Tsujikawa
c39f2b2c91 Merge branch 'antbryan-patch-2' 2015-01-21 21:19:16 +09:00
Ant Bryan
603b0ae501 Update README.rst
small text fixes
2015-01-20 23:54:22 -05:00
Tatsuhiro Tsujikawa
9938a4e952 Remove AM_EXTRA_RECURSIVE_TARGETS since travis automake is too old 2015-01-21 02:07:16 +09:00
Tatsuhiro Tsujikawa
8997e4369d nghttpx: Adjust backend buffers 2015-01-21 01:47:43 +09:00
Tatsuhiro Tsujikawa
5a6d6ccbd4 nghttpx: Read from backend eagerly in all upstreams 2015-01-21 01:41:17 +09:00
Tatsuhiro Tsujikawa
426fbda799 Update README about integration tests 2015-01-21 01:06:28 +09:00
Tatsuhiro Tsujikawa
16e91746d9 nghttpx: Return 400 error if multiple CLs are received in SPDY upstream
This change adds SPDY upstream tests.
2015-01-21 01:03:56 +09:00
Tatsuhiro Tsujikawa
b9a9a23b1e nghttpx: Close connection when error_reply is used for HTTP/1 upstream 2015-01-20 23:37:00 +09:00
Tatsuhiro Tsujikawa
8059380fb0 nghttpx: Don't need to set response state to MSG_COMPLETE after error_reply 2015-01-20 23:33:45 +09:00
Tatsuhiro Tsujikawa
039d9db5a3 integration: Make multiline string a bit readable 2015-01-20 23:30:38 +09:00
Tatsuhiro Tsujikawa
b9f41e34ef nghttpx: Return error when downstream HTTP/1 connection attempt failed 2015-01-20 23:27:30 +09:00
Tatsuhiro Tsujikawa
ca7288ae38 nghttpx: Return 502 if HTTP/1 downstream receives multiple CLs 2015-01-20 23:11:54 +09:00
Tatsuhiro Tsujikawa
a789008f17 integration: Treat blocking error as fatal 2015-01-20 22:59:09 +09:00
Tatsuhiro Tsujikawa
53142222fe integration: Code cleanup 2015-01-20 22:56:36 +09:00
Tatsuhiro Tsujikawa
f1bec6f05c nghttpx: Return 400 for multiple CLs for HTTP/1 upstream 2015-01-20 22:55:01 +09:00
Tatsuhiro Tsujikawa
506177e1bd integration: Ensure that proxy does not forward bad request 2015-01-20 22:31:21 +09:00
Tatsuhiro Tsujikawa
91151f1f56 integration: Fix minor typo 2015-01-20 22:25:56 +09:00
Tatsuhiro Tsujikawa
daec7c16d3 nghttpx: Don't discard data on HTTP/1 backend EOF
Also HTTP/1 frontend testing method was added.
2015-01-20 22:19:28 +09:00
Tatsuhiro Tsujikawa
6e446934d4 integration: Add recursive it target 2015-01-20 21:19:52 +09:00
Tatsuhiro Tsujikawa
9ab71305d1 integration: Add Makefile and make it to run integration tests 2015-01-20 01:14:22 +09:00
Tatsuhiro Tsujikawa
ae1aac26a7 integration: Set test naming convention 2015-01-20 00:59:09 +09:00
Tatsuhiro Tsujikawa
8eb2160890 integration: Add tests for HTTP/2 backend using go-nghttp2 2015-01-20 00:45:51 +09:00
Tatsuhiro Tsujikawa
a440bdf15e nghttpx: Response 502 if HTTP/2 backend receives invalid Content-Length 2015-01-19 23:44:23 +09:00
Tatsuhiro Tsujikawa
8004ea9726 nghttpx: Return 400 if request CL is invalid or multiple CLs 2015-01-19 22:40:37 +09:00
Tatsuhiro Tsujikawa
5436c95788 integration: Add HTTP/2 chunked request body test 2015-01-19 22:20:56 +09:00
Tatsuhiro Tsujikawa
467565589c integration: Use smaller interval to check server availability 2015-01-19 21:25:59 +09:00
Tatsuhiro Tsujikawa
8f45bf7b9e integration: Use []hpack.HeaderField instead of http.Header in requestParam
With array, we can control the order of header field directly.
2015-01-19 21:24:18 +09:00
Tatsuhiro Tsujikawa
09939cf6bc integration: Code cleanup
Don't close channel to avoid potential write-after-close.
Use time.After instead of time.NewTimer
2015-01-19 21:18:35 +09:00
Tatsuhiro Tsujikawa
e8053ac931 nghttpx: Check Content-Length only when Transfer-Encoding is not found 2015-01-19 21:16:47 +09:00
Tatsuhiro Tsujikawa
fff785178d Add integration tests for nghttpx using golang testing framework
The integration tests reside in integration-tests directory.  To run
integration tests, cd integration-tests, then run "go test".  Tests
depends on https://github.com/bradfitz/http2.
2015-01-19 00:27:24 +09:00
Tatsuhiro Tsujikawa
af24f8394e nghttpx: Fix nghttp2 error code use in SPDY upstream 2015-01-17 21:35:36 +09:00
Tatsuhiro Tsujikawa
62b9e4bb56 nghttpx: Validate received request body length against content-length 2015-01-17 21:33:23 +09:00
Tatsuhiro Tsujikawa
441f1cc282 nghttpx: Validate received response body length against content-length 2015-01-17 21:33:23 +09:00
Tatsuhiro Tsujikawa
f92110c54c nghttpx: Http2Upstream: Try to read data from backend if buffer is empty 2015-01-17 17:37:32 +09:00
Tatsuhiro Tsujikawa
1cb6d5cb6d Define NOTHREADS to 1 if thread_local keyword is not available 2015-01-17 15:52:28 +09:00
Tatsuhiro Tsujikawa
3817798905 Compile with g++-4.7
g++-4.7 lacks thread_local, which can be workaround by
--disable-threads.  What left remaining is std::map::emplace, which is
what this change deals with.  First check availability of
std::map::emplace, if there is none, use std::map::insert.
2015-01-17 15:32:49 +09:00
Tatsuhiro Tsujikawa
13a14ecda8 nghttpx: Workaround IRIX's struct sockaddr which contains union 2015-01-17 14:59:24 +09:00
Tatsuhiro Tsujikawa
2b458666ba nghttpd: Fix compiler warning 2015-01-16 23:22:10 +09:00
Tatsuhiro Tsujikawa
06a8d684a6 Fixed typo 2015-01-16 22:29:22 +09:00
Tatsuhiro Tsujikawa
18d42b411b Update man pages 2015-01-16 00:10:16 +09:00
Tatsuhiro Tsujikawa
cbd878bbd5 nghttp, nghttpd: Allow unit for --header-table-size option 2015-01-16 00:07:52 +09:00
Tatsuhiro Tsujikawa
49eeed8420 help2rst.py: Update number of indentation 2015-01-16 00:07:52 +09:00
Tatsuhiro Tsujikawa
5f36d91afd nghttp, nghttpd, h2load: Same indentation with nghttpx 2015-01-16 00:07:52 +09:00
Tatsuhiro Tsujikawa
a08ce38bcf nghttpd: Cache date by comparing ev_now 2015-01-15 23:27:52 +09:00
Tatsuhiro Tsujikawa
5d204fc3aa nghttpx: Add more option categories 2015-01-15 23:19:35 +09:00
Tatsuhiro Tsujikawa
84ead30e58 nghttpx: Remove NGHTTP2_HCAT_REQUEST halding since already handled in lib 2015-01-15 23:16:24 +09:00
Tatsuhiro Tsujikawa
b11e1afc91 nghttpx: Remove unused noop function 2015-01-15 23:14:25 +09:00
Tatsuhiro Tsujikawa
c3aa02f003 nghttpd: Use Http2Handler::submit_rst_stream 2015-01-15 23:11:14 +09:00
Tatsuhiro Tsujikawa
d142830109 nghttpd: Issue RST_STREAM if content-length does not match uploaded bytes 2015-01-15 23:07:25 +09:00
Tatsuhiro Tsujikawa
22e41bab3f nghttpd: Issue RST_STREAM if received header field contains invalid chars 2015-01-15 22:45:18 +09:00
Tatsuhiro Tsujikawa
b0078a2379 Suppress to send frames other than GOAWAY if NGHTTP2_GOAWAY_TERM_ON_SEND is set
This change makes sure that GOAWAY which terminates session is
immediately sent without blocked by other frames.
NGHTTP2_ERR_SESSION_CLOSING library error code was added to indicate
this situation to callback.
2015-01-15 22:32:29 +09:00
Tatsuhiro Tsujikawa
50109bb307 Use NGHTTP2_STREAM_CLOSED when DATA arrived to stream which is not open 2015-01-14 23:31:21 +09:00
Tatsuhiro Tsujikawa
aa1c8d1fa4 nghttpx: Don't forward Trailer header field 2015-01-14 21:33:22 +09:00
Tatsuhiro Tsujikawa
1de20c1232 nghttpx: Ignore trailer headers in HTTP/1.1 2015-01-14 21:28:31 +09:00
Tatsuhiro Tsujikawa
8fe093de1d nghttpx: Set initial backlog to 512 2015-01-14 21:24:12 +09:00
Tatsuhiro Tsujikawa
f004361ef2 nghttpx: Add --backend-request-buffer option 2015-01-13 23:30:28 +09:00
Tatsuhiro Tsujikawa
d6db38a318 nghttpx: Clean up integer configuration range checking 2015-01-13 23:23:35 +09:00
Tatsuhiro Tsujikawa
c88a5291b7 nghttpx: Add --backend-response-buffer option 2015-01-13 23:20:06 +09:00
Tatsuhiro Tsujikawa
0d614cf103 nghttpx: Longer help message 2015-01-13 23:02:18 +09:00
Tatsuhiro Tsujikawa
29d6cfae80 nghttpx: Add explanation about units in <SIZE> 2015-01-13 22:42:52 +09:00
Tatsuhiro Tsujikawa
c48a6e73e8 nghttpx: Clean up metavar 2015-01-13 22:39:35 +09:00
Tatsuhiro Tsujikawa
956c11388c nghttpx: Allow units (k, m, and g) in --{read,write}-{rate,burst}
So that you can specify --read-rate=1M --read-burst=4M
2015-01-13 21:54:53 +09:00
Tatsuhiro Tsujikawa
5e8eb926f2 nghttpx: Fix server error with -n1 and --tls-ctx-per-worker 2015-01-13 21:53:53 +09:00
Tatsuhiro Tsujikawa
1e4f8f27fd nghttpx: Add --tls-ctx-per-worker option
When same SSL_CTX is used by multiple thread simultaneously we have to
setup some number of mutex locks for it.  We could not check how this
locking affects scalability since we have 4 cores at best in our
development machine.  Good side of sharing SSL_CTX across threads is
we can share session ID pool.

If --tls-ctx-per-worker is enabled, SSL_CTX is created per thread
basis and we can eliminate mutex locks.  The downside is session ID is
no longer shared, which means if session ID generated by one thread
cannot be acceptable by another thread.  But we have now session
ticket enabled and its keys are shared by all threads.
2015-01-13 00:25:02 +09:00
Tatsuhiro Tsujikawa
0ea041e8cb nghttpx: Add doc and clean up 2015-01-12 22:47:30 +09:00
Tatsuhiro Tsujikawa
e048deb64c nghttpx: Fix error message 2015-01-12 22:35:45 +09:00
Tatsuhiro Tsujikawa
8ece08e1a3 Bump up version number to 0.7.2-DEV 2015-01-11 19:24:21 +09:00
213 changed files with 20380 additions and 8013 deletions

2
.gitignore vendored
View File

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

View File

@@ -1,6 +1,6 @@
The MIT License
Copyright (c) 2012, 2014 Tatsuhiro Tsujikawa
Copyright (c) 2012, 2014, 2015 Tatsuhiro Tsujikawa
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@@ -20,7 +20,8 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
SUBDIRS = lib third-party src examples python tests doc contrib
SUBDIRS = lib third-party src examples python tests integration-tests \
doc contrib
ACLOCAL_AMFLAGS = -I m4

View File

@@ -42,9 +42,9 @@ implementation.
* https://nghttp2.org/ (TLS + ALPN/NPN)
NPN offer ``h2-14``, ``spdy/3.1`` and ``http/1.1``.
This endpoint requires TLSv1.2 for HTTP/2 connection.
This endpoint supports ``h2``, ``h2-16``, ``h2-14``, ``spdy/3.1``
and ``http/1.1`` via ALPN/NPN and requires TLSv1.2 for HTTP/2
connection.
* http://nghttp2.org/ (Upgrade / Direct)
@@ -110,25 +110,9 @@ The Python bindings require the following packages:
* python >= 2.7
If you are using Ubuntu 14.04 LTS, you need the following packages
installed:
installed::
* make
* binutils
* autoconf
* automake
* autotools-dev
* libtool
* pkg-config
* zlib1g-dev
* libcunit1-dev
* libssl-dev
* libxml2-dev
* libev-dev
* libevent-dev
* libjansson-dev
* libjemalloc-dev
* cython
* python3.4-dev
apt-get install make binutils autoconf automake autotools-dev libtool pkg-config zlib1g-dev libcunit1-dev libssl-dev libxml2-dev libev-dev libevent-dev libjansson-dev libjemalloc-dev cython python3.4-dev
spdylay is not packaged in Ubuntu, so you need to build it yourself:
http://tatsuhiro-t.github.io/spdylay/
@@ -172,6 +156,34 @@ The generated documents will not be installed with ``make install``.
The online documentation is available at
https://nghttp2.org/documentation/
Unit tests
----------
Unit tests are done by simply running `make check`.
Integration tests
-----------------
We have the integration tests for nghttpx proxy server. The tests are
written in `Go programming language <http://golang.org/>`_ and uses
its testing framework. We depend on the following libraries:
* https://github.com/bradfitz/http2
* https://github.com/tatsuhiro-t/go-nghttp2
* https://golang.org/x/net/spdy
To download the above packages, after settings ``GOPATH``, run the
following command under ``integration-tests`` directory::
$ make itprep
To run the tests, run the following command under
``integration-tests`` directory::
$ make it
Inside the tests, we use port 3009 to run test subject server.
Client, Server and Proxy programs
---------------------------------
@@ -186,7 +198,7 @@ with prior knowledge, HTTP Upgrade and NPN/ALPN TLS extension.
It has verbose output mode for framing information. Here is sample
output from ``nghttp`` client::
$ src/nghttp -nv https://nghttp2.org
$ nghttp -nv https://nghttp2.org
[ 0.033][NPN] server offers:
* h2-14
* spdy/3.1
@@ -255,7 +267,7 @@ output from ``nghttp`` client::
The HTTP Upgrade is performed like this::
$ src/nghttp -nvu http://nghttp2.org
$ nghttp -nvu http://nghttp2.org
[ 0.013] HTTP Upgrade request
GET / HTTP/1.1
Host: nghttp2.org
@@ -322,6 +334,35 @@ The HTTP Upgrade is performed like this::
[ 0.038] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
(last_stream_id=0, error_code=NO_ERROR(0), opaque_data(0)=[])
With ``-s`` option, ``nghttp`` prints out some timing information for
requests, sorted by completion time::
$ nghttp -nas https://nghttp2.org/
***** Statistics *****
Request timing:
complete: relative time from protocol handshake to stream close
request: relative time from protocol handshake to request
transmission. If '*' is shown, this was pushed by server.
process: time for request and response
code: HTTP status code
size: number of bytes received as response body without
inflation.
URI: request URI
sorted by 'complete'
complete request process code size request path
+11.07ms +120us 10.95ms 200 9K /
+16.77ms * +8.80ms 7.98ms 200 8K /stylesheets/screen.css
+27.00ms +11.16ms 15.84ms 200 3K /javascripts/octopress.js
+27.40ms +11.16ms 16.24ms 200 3K /javascripts/modernizr-2.0.js
+76.14ms +11.17ms 64.97ms 200 171K /images/posts/with-pri-blog.png
+88.52ms +11.17ms 77.36ms 200 174K /images/posts/without-pri-blog.png
With ``-r`` option, ``nghttp`` writes more detailed timing data to
given file in HAR format.
nghttpd - server
++++++++++++++++
@@ -338,7 +379,7 @@ HTTP/2 connection. No HTTP Upgrade is supported.
Just like ``nghttp``, it has verbose output mode for framing
information. Here is sample output from ``nghttpd`` server::
$ src/nghttpd --no-tls -v 8080
$ nghttpd --no-tls -v 8080
IPv4: listen on port 8080
IPv6: listen on port 8080
[id=1] [ 15.921] send SETTINGS frame <length=10, flags=0x00, stream_id=0>
@@ -390,7 +431,8 @@ nghttpx - proxy
+++++++++++++++
``nghttpx`` is a multi-threaded reverse proxy for ``h2-14``, SPDY and
HTTP/1.1 and powers nghttp2.org site. It has several operation modes:
HTTP/1.1 and powers nghttp2.org site and supports HTTP/2 server push.
It has several operation modes:
================== ============================ ============== =============
Mode option Frontend Backend Note
@@ -419,8 +461,6 @@ SSL/TLS in the backend connection by deafult. To disable SSL/TLS, use
``nghttpx`` supports configuration file. See ``--conf`` option and
sample configuration file ``nghttpx.conf.sample``.
``nghttpx`` does not support server push.
In the default mode, (without any of ``--http2-proxy``,
``--http2-bridge``, ``--client-proxy`` and ``--client`` options),
``nghttpx`` works as reverse proxy to the backend server::
@@ -434,7 +474,7 @@ SPDY proxy)::
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy
[secure proxy] (e.g., Squid, ATS)
The ``Client`` in the above is needs to be configured to use
The ``Client`` in the above needs to be configured to use
``nghttpx`` as secure proxy.
At the time of this writing, Chrome is the only browser which supports
@@ -502,8 +542,11 @@ library. The UI of ``h2load`` is heavily inspired by ``weighttp``
(https://github.com/lighttpd/weighttp). The typical usage is as
follows::
$ src/h2load -n1000 -c10 -m10 https://127.0.0.1:8443/
$ h2load -n100000 -c100 -m100 https://localhost:8443/
starting benchmark...
spawning thread #0: 100 concurrent clients, 100000 total requests
Protocol: TLSv1.2
Cipher: ECDHE-RSA-AES128-GCM-SHA256
progress: 10% done
progress: 20% done
progress: 30% done
@@ -515,15 +558,17 @@ follows::
progress: 90% done
progress: 100% done
finished in 0 sec, 152 millisec and 152 microsec, 6572 req/s, 749 kbytes/s
requests: 1000 total, 1000 started, 1000 done, 0 succeeded, 1000 failed, 0 errored
status codes: 0 2xx, 0 3xx, 1000 4xx, 0 5xx
traffic: 141100 bytes total, 840 bytes headers, 116000 bytes data
finished in 7.10s, 14092 req/s, 55.67MB/s
requests: 100000 total, 100000 started, 100000 done, 100000 succeeded, 0 failed, 0 errored
status codes: 100000 2xx, 0 3xx, 0 4xx, 0 5xx
traffic: 414200800 bytes total, 2723100 bytes headers, 409600000 bytes data
min max mean sd +/- sd
time for request: 283.86ms 1.46s 659.70ms 150.87ms 84.68%
The above example issued total 1000 requests, using 10 concurrent
clients (thus 10 HTTP/2 sessions), and maximum 10 streams per client.
With ``-t`` option, ``h2load`` will use multiple native threads to
avoid saturating single core on client side.
The above example issued total 100000 requests, using 100 concurrent
clients (in other words, 100 HTTP/2 sessions), and maximum 100 streams
per client. With ``-t`` option, ``h2load`` will use multiple native
threads to avoid saturating single core on client side.
.. warning::
@@ -972,7 +1017,7 @@ libnghttp2_asio: High level HTTP/2 C++ library
libnghttp2_asio is C++ library built on top of libnghttp2 and provides
high level abstraction API to build HTTP/2 applications. It depends
on Boost::ASIO library and OpenSSL. Currently libnghttp2_asio
provides server side API.
provides client and server API.
libnghttp2_asio is not built by default. Use ``--enable-asio-lib``
configure flag to build libnghttp2_asio. The required Boost libraries
@@ -988,19 +1033,75 @@ HTTP/2 server looks like this:
.. code-block:: cpp
#include <nghttp2/asio_http2.h>
#include <nghttp2/asio_http2_server.h>
using namespace nghttp2::asio_http2;
using namespace nghttp2::asio_http2::server;
int main(int argc, char *argv[]) {
boost::system::error_code ec;
http2 server;
server.listen("*", 3000, [](const std::shared_ptr<request> &req,
const std::shared_ptr<response> &res) {
res->write_head(200);
res->end("hello, world");
server.handle("/", [](const request &req, const response &res) {
res.write_head(200);
res.end("hello, world\n");
});
if (server.listen_and_serve(ec, "localhost", "3000")) {
std::cerr << "error: " << ec.message() << std::endl;
}
}
Here is the sample code for client API use:
.. code-block:: cpp
#include <iostream>
#include <nghttp2/asio_http2_client.h>
using boost::asio::ip::tcp;
using namespace nghttp2::asio_http2;
using namespace nghttp2::asio_http2::client;
int main(int argc, char *argv[]) {
boost::system::error_code ec;
boost::asio::io_service io_service;
// connect to localhost:3000
session sess(io_service, "localhost", "3000");
sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
boost::system::error_code ec;
auto req = sess.submit(ec, "GET", "http://localhost:3000/");
req->on_response([](const response &res) {
// print status code and response header fields.
std::cerr << "HTTP/2 " << res.status_code() << std::endl;
for (auto &kv : res.header()) {
std::cerr << kv.first << ": " << kv.second.value << "\n";
}
std::cerr << std::endl;
res.on_data([](const uint8_t *data, std::size_t len) {
std::cerr.write(reinterpret_cast<const char *>(data), len);
std::cerr << std::endl;
});
});
req->on_close([&sess](uint32_t error_code) {
// shutdown session after first request was done.
sess.shutdown();
});
});
sess.on_error([](const boost::system::error_code &ec) {
std::cerr << "error: " << ec.message() << std::endl;
});
io_service.run();
}
For more details, see the documentation of libnghttp2_asio.

View File

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

View File

@@ -1,6 +1,6 @@
dnl nghttp2 - HTTP/2 C Library
dnl Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa
dnl Copyright (c) 2012, 2013, 2014, 2015 Tatsuhiro Tsujikawa
dnl Permission is hereby granted, free of charge, to any person obtaining
dnl a copy of this software and associated documentation files (the
@@ -25,14 +25,14 @@ dnl Do not change user variables!
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
AC_PREREQ(2.61)
AC_INIT([nghttp2], [0.7.1], [t-tujikawa@users.sourceforge.net])
AC_INIT([nghttp2], [0.7.6], [t-tujikawa@users.sourceforge.net])
LT_PREREQ([2.2.6])
LT_INIT()
dnl See versioning rule:
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
AC_SUBST(LT_CURRENT, 8)
AC_SUBST(LT_REVISION, 1)
AC_SUBST(LT_AGE, 3)
AC_SUBST(LT_CURRENT, 12)
AC_SUBST(LT_REVISION, 0)
AC_SUBST(LT_AGE, 7)
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"`
@@ -49,6 +49,9 @@ AC_CANONICAL_TARGET
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([subdir-objects])
# comment out for now since this requires automake 1.13 or higher and
# travis has older one.
# AM_EXTRA_RECURSIVE_TARGETS([it])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -197,12 +200,28 @@ std::vector<std::future<int>> v;
[have_std_future=no
AC_MSG_RESULT([no])])
# Check that std::map::emplace is available for g++-4.7.
AC_MSG_CHECKING([whether std::map::emplace is available])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
[[
#include <map>
]],
[[
std::map<int, int>().emplace(1, 2);
]])],
[AC_DEFINE([HAVE_STD_MAP_EMPLACE], [1],
[Define to 1 if you have the `std::map::emplace`.])
have_std_map_emplace=yes
AC_MSG_RESULT([yes])],
[have_std_map_emplace=no
AC_MSG_RESULT([no])])
AC_LANG_POP()
# Checks for libraries.
# Additional libraries required for tests.
TESTLDFLAGS=
TESTLDADD=
# Additional libraries required for programs under src directory.
APPLDFLAGS=
@@ -211,7 +230,7 @@ LIBS_OLD=$LIBS
# Search for dlsym function, which is used in tests. Linux needs -ldl,
# but netbsd does not need it.
AC_SEARCH_LIBS([dlsym], [dl])
TESTLDFLAGS="$LIBS $TESTLDFLAGS"
TESTLDADD="$LIBS $TESTLDADD"
LIBS=$LIBS_OLD
LIBS_OLD=$LIBS
@@ -354,7 +373,7 @@ fi
# spdylay (for src/nghttpx and src/h2load)
have_spdylay=no
if test "x${request_spdylay}" != "xno"; then
PKG_CHECK_MODULES([LIBSPDYLAY], [libspdylay >= 1.3.0],
PKG_CHECK_MODULES([LIBSPDYLAY], [libspdylay >= 1.3.2],
[have_spdylay=yes], [have_spdylay=no])
if test "x${have_spdylay}" = "xyes"; then
AC_DEFINE([HAVE_SPDYLAY], [1], [Define to 1 if you have `spdylay` library.])
@@ -515,6 +534,10 @@ if test "x$have_struct_tm_tm_gmtoff" = "xyes"; then
[Define to 1 if you have `struct tm.tm_gmtoff` member.])
fi
# Check size of pointer to decide we need 8 bytes alignment
# adjustment.
AC_CHECK_SIZEOF([int *])
# Checks for library functions.
if test "x$cross_compiling" != "xyes"; then
AC_FUNC_MALLOC
@@ -592,13 +615,16 @@ if test "x$debug" != "xno"; then
AC_DEFINE([DEBUGBUILD], [1], [Define to 1 to enable debug output.])
fi
enable_threads=yes
# Some platform does not have working std::future. We disable
# threading for those platforms to exclude std::future use.
if test "x$threads" != "xyes" || test "x$have_std_future" != "xyes"; then
# threading for those platforms.
if test "x$threads" != "xyes" ||
test "x$have_std_future" != "xyes"; then
enable_threads=no
AC_DEFINE([NOTHREADS], [1], [Define to 1 if you want to disable threads.])
fi
AC_SUBST([TESTLDFLAGS])
AC_SUBST([TESTLDADD])
AC_SUBST([APPLDFLAGS])
AC_CONFIG_FILES([
@@ -616,6 +642,9 @@ AC_CONFIG_FILES([
examples/Makefile
python/Makefile
python/setup.py
integration-tests/Makefile
integration-tests/config.go
integration-tests/setenv
doc/Makefile
doc/conf.py
doc/index.rst
@@ -631,6 +660,8 @@ AC_CONFIG_FILES([
doc/nghttp2.h.rst
doc/nghttp2ver.h.rst
doc/asio_http2.h.rst
doc/asio_http2_server.h.rst
doc/asio_http2_client.h.rst
doc/contribute.rst
contrib/Makefile
])
@@ -682,4 +713,5 @@ AC_MSG_NOTICE([summary of build options:
Libnghttp2_asio:${enable_asio_lib}
Examples: ${enable_examples}
Python bindings:${enable_python_bindings}
Threading: ${enable_threads}
])

View File

@@ -41,24 +41,28 @@ EXTRA_DIST = \
sources/python-apiref.rst \
sources/building-android-binary.rst \
sources/contribute.rst \
_themes/sphinx_rtd_theme/footer.html \
_themes/sphinx_rtd_theme/theme.conf \
_themes/sphinx_rtd_theme/layout_old.html \
_themes/sphinx_rtd_theme/__init__.py \
_themes/sphinx_rtd_theme/layout.html \
_themes/sphinx_rtd_theme/search.html \
_themes/sphinx_rtd_theme/breadcrumbs.html \
_themes/sphinx_rtd_theme/versions.html \
_themes/sphinx_rtd_theme/footer.html \
_themes/sphinx_rtd_theme/layout.html \
_themes/sphinx_rtd_theme/layout_old.html \
_themes/sphinx_rtd_theme/search.html \
_themes/sphinx_rtd_theme/searchbox.html \
_themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf \
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg \
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff \
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot \
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf \
_themes/sphinx_rtd_theme/static/js/theme.js \
_themes/sphinx_rtd_theme/static/css/theme.css \
_themes/sphinx_rtd_theme/static/css/badge_only.css \
$(man_MANS)
_themes/sphinx_rtd_theme/static/css/theme.css \
_themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf \
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot \
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg \
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf \
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff \
_themes/sphinx_rtd_theme/static/js/theme.js \
_themes/sphinx_rtd_theme/theme.conf \
_themes/sphinx_rtd_theme/versions.html \
$(man_MANS) \
bash_completion/nghttp \
bash_completion/nghttpd \
bash_completion/nghttpx \
bash_completion/h2load
# Makefile for Sphinx documentation
#
@@ -104,7 +108,7 @@ clean-local:
-rm apiref.rst
-rm -rf $(BUILDDIR)/*
html: apiref.rst
html-local: apiref.rst
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."

View File

@@ -31,3 +31,70 @@ do not consume client connection preface unless
value. The applications are responsible to receive it before calling
these functions if :type:`nghttp2_session` is configured as server and
`nghttp2_option_set_recv_client_preface()` is not used.
HTTP Messaging
--------------
By default, nghttp2 library checks HTTP messaging rules described in
`HTTP/2 specification, section 8
<https://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-8>`_.
Everything described in that section is not validated however. We
briefly describe what the library does in this area. In the following
description, without loss of generality we omit CONTINUATION frame
since they must follow HEADERS frame and are processed atomically. In
other words, they are just one big HEADERS frame. To disable these
validations, use `nghttp2_option_set_no_http_messaging()`.
For HTTP request, including those carried by PUSH_PROMISE, HTTP
message starts with one HEADERS frame containing request headers. It
is followed by zero or more DATA frames containing request body, which
is followed by zero or one HEADERS containing trailer headers. The
request headers must include ":scheme", ":method" and ":path" pseudo
header fields unless ":method" is not "CONNECT". ":authority" is
optional, but nghttp2 requires either ":authority" or "Host" header
field must be present. If ":method" is "CONNECT", the request headers
must include ":method" and ":authority" and must omit ":scheme" and
":path".
For HTTP response, HTTP message starts with zero or more HEADERS
frames containing non-final response (status code 1xx). They are
followed by one HEADERS frame containing final response headers
(non-1xx). It is followed by zero or more DATA frames containing
response body, which is followed by zero or one HEADERS containing
trailer headers. The non-final and final response headers must
contain ":status" pseudo header field containing 3 digits only.
All request and response headers must include exactly one valid value
for each pseudo header field. Additionally nghttp2 requires all
request headers must not include more than one "Host" header field.
HTTP/2 prohibits connection-specific header fields. The following
header fields must not appear: "Connection", "Keep-Alive",
"Proxy-Connection", "Transfer-Encoding" and "Upgrade". Additionally,
"TE" header field must not include any value other than "trailers".
Each header field name and value must obey the field-name and
field-value production rules described in `RFC 7230, section
3.2. <https://tools.ietf.org/html/rfc7230#section-3.2>`_.
Additionally, all field name must be lower cased. While the pseudo
header fields must satisfy these rules, we just ignore illegal regular
headers (this means that these header fields are not passed to
application callback). This is because these illegal header fields
are floating around in existing internet and resetting stream just
because of this may break many web sites. This is especially true if
we forward to or translate from HTTP/1 traffic.
With the above validations, nghttp2 library guarantees that header
field name passed to `nghttp2_on_header_callback()` is not empty.
Also required pseudo headers are all present and not empty.
nghttp2 enforces "Content-Length" validation as well. All request or
response headers must not contain more than one "Content-Length"
header field. If "Content-Length" header field is present, it must be
parsed as 64 bit signed integer. The sum of data length in the
following DATA frames must match with the number in "Content-Length"
header field if it is present (this does not include padding bytes).
Any deviation results in stream error of type PROTOCOL_ERROR. If
error is found in PUSH_PROMISE frame, stream error is raised against
promised stream.

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

@@ -0,0 +1,19 @@
_h2load()
{
local cur prev split=false
COMPREPLY=()
COMP_WORDBREAKS=${COMP_WORDBREAKS//=}
cmd=${COMP_WORDS[0]}
_get_comp_words_by_ref cur prev
case $cur in
-*)
COMPREPLY=( $( compgen -W '--threads --connection-window-bits --input-file --help --requests --verbose --version --window-bits --clients --no-tls-proto --header --max-concurrent-streams ' -- "$cur" ) )
;;
*)
_filedir
return 0
esac
return 0
}
complete -F _h2load h2load

View File

@@ -0,0 +1,74 @@
#!/usr/bin/env python
import subprocess
from StringIO import StringIO
import re
import sys
import os.path
class Option:
def __init__(self, long_opt, short_opt):
self.long_opt = long_opt
self.short_opt = short_opt
def get_all_options(cmd):
opt_pattern = re.compile(r' (?:(-.), )?(--[^\s\[=]+)(\[)?')
proc = subprocess.Popen([cmd, "--help"], stdout=subprocess.PIPE)
stdoutdata, stderrdata = proc.communicate()
cur_option = None
opts = {}
for line in StringIO(stdoutdata):
match = opt_pattern.match(line)
if not match:
continue
long_opt = match.group(2)
short_opt = match.group(1)
opts[long_opt] = Option(long_opt, short_opt)
return opts
def output_case(out, name, opts):
out.write('''\
_{name}()
{{
local cur prev split=false
COMPREPLY=()
COMP_WORDBREAKS=${{COMP_WORDBREAKS//=}}
cmd=${{COMP_WORDS[0]}}
_get_comp_words_by_ref cur prev
'''.format(name=name))
# Complete option name.
out.write('''\
case $cur in
-*)
COMPREPLY=( $( compgen -W '\
''')
for opt in opts.itervalues():
out.write(opt.long_opt)
out.write(' ')
out.write('''\
' -- "$cur" ) )
;;
''')
# If no option found for completion then complete with files.
out.write('''\
*)
_filedir
return 0
esac
return 0
}}
complete -F _{name} {name}
'''.format(name=name))
if __name__ == '__main__':
if len(sys.argv) < 2:
print "Generates bash_completion using `/path/to/cmd --help'"
print "Usage: make_bash_completion.py /path/to/cmd"
exit(1)
name = os.path.basename(sys.argv[1])
opts = get_all_options(sys.argv[1])
output_case(sys.stdout, name, opts)

View File

@@ -0,0 +1,19 @@
_nghttp()
{
local cur prev split=false
COMPREPLY=()
COMP_WORDBREAKS=${COMP_WORDBREAKS//=}
cmd=${COMP_WORDS[0]}
_get_comp_words_by_ref cur prev
case $cur in
-*)
COMPREPLY=( $( compgen -W '--verbose --no-dep --get-assets --har --header-table-size --multiply --padding --dep-idle --continuation --connection-window-bits --peer-max-concurrent-streams --timeout --data --no-content-length --version --color --cert --upgrade --remote-name --weight --help --key --null-out --window-bits --stat --header ' -- "$cur" ) )
;;
*)
_filedir
return 0
esac
return 0
}
complete -F _nghttp nghttp

View File

@@ -0,0 +1,19 @@
_nghttpd()
{
local cur prev split=false
COMPREPLY=()
COMP_WORDBREAKS=${COMP_WORDBREAKS//=}
cmd=${COMP_WORDS[0]}
_get_comp_words_by_ref cur prev
case $cur in
-*)
COMPREPLY=( $( compgen -W '--error-gzip --push --header-table-size --htdocs --padding --verbose --version --help --daemon --verify-client --workers --no-tls --color --early-response --dh-param-file ' -- "$cur" ) )
;;
*)
_filedir
return 0
esac
return 0
}
complete -F _nghttpd nghttpd

View File

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

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "H2LOAD" "1" "January 11, 2015" "0.7.1" "nghttp2"
.TH "H2LOAD" "1" "February 27, 2015" "0.7.5" "nghttp2"
.SH NAME
h2load \- HTTP/2 benchmarking tool
.
@@ -39,15 +39,14 @@ benchmarking tool for HTTP/2 and SPDY server
.INDENT 0.0
.TP
.B <URI>
Specify URI to access. Multiple URIs can be
specified. URIs are used in this order for each
client. All URIs are used, then first URI is
used and then 2nd URI, and so on. The scheme,
host and port in the subsequent URIs, if present,
are ignored. Those in the first URI are used
solely.
Specify URI to access. Multiple URIs can be specified.
URIs are used in this order for each client. All URIs
are used, then first URI is used and then 2nd URI, and
so on. The scheme, host and port in the subsequent
URIs, if present, are ignored. Those in the first URI
are used solely.
.UNINDENT
.SH OPTIONS:
.SH OPTIONS
.INDENT 0.0
.TP
.B \-n, \-\-requests=<N>
@@ -72,38 +71,36 @@ Default: \fB1\fP
.INDENT 0.0
.TP
.B \-i, \-\-input\-file=<FILE>
Path of a file with multiple URIs are seperated
by EOLs. This option will disable URIs getting
from command\-line. If \(aq\-\(aq is given as <FILE>,
URIs will be read from stdin. URIs are used in
this order for each client. All URIs are used,
then first URI is used and then 2nd URI, and so
on. The scheme, host and port in the subsequent
URIs, if present, are ignored. Those in the
first URI are used solely.
Path of a file with multiple URIs are seperated by EOLs.
This option will disable URIs getting from command\-line.
If \(aq\-\(aq is given as <FILE>, URIs will be read from stdin.
URIs are used in this order for each client. All URIs
are used, then first URI is used and then 2nd URI, and
so on. The scheme, host and port in the subsequent
URIs, if present, are ignored. Those in the first URI
are used solely.
.UNINDENT
.INDENT 0.0
.TP
.B \-m, \-\-max\-concurrent\-streams=(auto|<N>)
Max concurrent streams to issue per session. If
"auto" is given, the number of given URIs is
used.
Max concurrent streams to issue per session. If "auto"
is given, the number of given URIs is used.
.sp
Default: \fBauto\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-w, \-\-window\-bits=<N>
Sets the stream level initial window size to
(2**<N>)\-1. For SPDY, 2**<N> is used instead.
Sets the stream level initial window size to (2**<N>)\-1.
For SPDY, 2**<N> is used instead.
.UNINDENT
.INDENT 0.0
.TP
.B \-W, \-\-connection\-window\-bits=<N>
Sets the connection level initial window size to
(2**<N>)\-1. For SPDY, if <N> is strictly less
than 16, this option is ignored. Otherwise
2**<N> is used for SPDY.
(2**<N>)\-1. For SPDY, if <N> is strictly less than 16,
this option is ignored. Otherwise 2**<N> is used for
SPDY.
.UNINDENT
.INDENT 0.0
.TP
@@ -113,10 +110,9 @@ Add/Override a header to the requests.
.INDENT 0.0
.TP
.B \-p, \-\-no\-tls\-proto=<PROTOID>
Specify ALPN identifier of the protocol to be
used when accessing http URI without SSL/TLS.
Available protocols: spdy/2, spdy/3, spdy/3.1 and
h2c\-14
Specify ALPN identifier of the protocol to be used when
accessing http URI without SSL/TLS.
Available protocols: spdy/2, spdy/3, spdy/3.1 and h2c\-14
.sp
Default: \fBh2c\-14\fP
.UNINDENT
@@ -135,6 +131,77 @@ Display version information and exit.
.B \-h, \-\-help
Display this help and exit.
.UNINDENT
.SH OUTPUT
.INDENT 0.0
.TP
.B requests
.INDENT 7.0
.TP
.B total
The number of requests h2load was instructed to make.
.TP
.B started
The number of requests h2load has started.
.TP
.B done
The number of requests completed.
.TP
.B succeeded
The number of requests completed successfully. Only HTTP status
code 2xx or3xx are considered as success.
.TP
.B failed
The number of requests failed, including HTTP level failures
(non\-successful HTTP status code).
.TP
.B errored
The number of requests failed, except for HTTP level failures.
status code. This is the subset of the number reported in
\fBfailed\fP and most likely the network level failures or stream
was reset by RST_STREAM.
.UNINDENT
.TP
.B status codes
The number of status code h2load received.
.TP
.B traffic
.INDENT 7.0
.TP
.B total
The number of bytes received from the server "on the wire". If
requests were made via TLS, this value is the number of decrpyted
bytes.
.TP
.B headers
The number of response header bytes from the server without
decompression. For HTTP/2, this is the sum of the payload of
HEADERS frame. For SPDY, this is the sum of the payload of
SYN_REPLY frame.
.TP
.B data
The number of response body bytes received from the server.
.UNINDENT
.TP
.B time for request
.INDENT 7.0
.TP
.B min
The minimum time taken for request and response.
.TP
.B max
The maximum time taken for request and response.
.TP
.B mean
The mean time taken for request and response.
.TP
.B sd
The standard deviation of the time for request and response.
.TP
.B +/\- sd
The fraction of the number of requests within standard deviation
range (mean +/\- sd) against total number of successful requests.
.UNINDENT
.UNINDENT
.SH SEE ALSO
.sp
\fInghttp(1)\fP, \fInghttpd(1)\fP, \fInghttpx(1)\fP

View File

@@ -14,16 +14,15 @@ benchmarking tool for HTTP/2 and SPDY server
.. describe:: <URI>
Specify URI to access. Multiple URIs can be
specified. URIs are used in this order for each
client. All URIs are used, then first URI is
used and then 2nd URI, and so on. The scheme,
host and port in the subsequent URIs, if present,
are ignored. Those in the first URI are used
solely.
Specify URI to access. Multiple URIs can be specified.
URIs are used in this order for each client. All URIs
are used, then first URI is used and then 2nd URI, and
so on. The scheme, host and port in the subsequent
URIs, if present, are ignored. Those in the first URI
are used solely.
OPTIONS:
--------
OPTIONS
-------
.. option:: -n, --requests=<N>
@@ -45,35 +44,33 @@ OPTIONS:
.. option:: -i, --input-file=<FILE>
Path of a file with multiple URIs are seperated
by EOLs. This option will disable URIs getting
from command-line. If '-' is given as <FILE>,
URIs will be read from stdin. URIs are used in
this order for each client. All URIs are used,
then first URI is used and then 2nd URI, and so
on. The scheme, host and port in the subsequent
URIs, if present, are ignored. Those in the
first URI are used solely.
Path of a file with multiple URIs are seperated by EOLs.
This option will disable URIs getting from command-line.
If '-' is given as <FILE>, URIs will be read from stdin.
URIs are used in this order for each client. All URIs
are used, then first URI is used and then 2nd URI, and
so on. The scheme, host and port in the subsequent
URIs, if present, are ignored. Those in the first URI
are used solely.
.. option:: -m, --max-concurrent-streams=(auto|<N>)
Max concurrent streams to issue per session. If
"auto" is given, the number of given URIs is
used.
Max concurrent streams to issue per session. If "auto"
is given, the number of given URIs is used.
Default: ``auto``
.. option:: -w, --window-bits=<N>
Sets the stream level initial window size to
(2**<N>)-1. For SPDY, 2\*\*<N> is used instead.
Sets the stream level initial window size to (2\*\*<N>)-1.
For SPDY, 2**<N> is used instead.
.. option:: -W, --connection-window-bits=<N>
Sets the connection level initial window size to
(2**<N>)-1. For SPDY, if <N> is strictly less
than 16, this option is ignored. Otherwise
2**<N> is used for SPDY.
(2**<N>)-1. For SPDY, if <N> is strictly less than 16,
this option is ignored. Otherwise 2\*\*<N> is used for
SPDY.
.. option:: -H, --header=<HEADER>
@@ -81,10 +78,9 @@ OPTIONS:
.. option:: -p, --no-tls-proto=<PROTOID>
Specify ALPN identifier of the protocol to be
used when accessing http URI without SSL/TLS.
Available protocols: spdy/2, spdy/3, spdy/3.1 and
h2c-14
Specify ALPN identifier of the protocol to be used when
accessing http URI without SSL/TLS.
Available protocols: spdy/2, spdy/3, spdy/3.1 and h2c-14
Default: ``h2c-14``
@@ -100,6 +96,57 @@ OPTIONS:
Display this help and exit.
OUTPUT
------
requests
total
The number of requests h2load was instructed to make.
started
The number of requests h2load has started.
done
The number of requests completed.
succeeded
The number of requests completed successfully. Only HTTP status
code 2xx or3xx are considered as success.
failed
The number of requests failed, including HTTP level failures
(non-successful HTTP status code).
errored
The number of requests failed, except for HTTP level failures.
status code. This is the subset of the number reported in
``failed`` and most likely the network level failures or stream
was reset by RST_STREAM.
status codes
The number of status code h2load received.
traffic
total
The number of bytes received from the server "on the wire". If
requests were made via TLS, this value is the number of decrpyted
bytes.
headers
The number of response header bytes from the server without
decompression. For HTTP/2, this is the sum of the payload of
HEADERS frame. For SPDY, this is the sum of the payload of
SYN_REPLY frame.
data
The number of response body bytes received from the server.
time for request
min
The minimum time taken for request and response.
max
The maximum time taken for request and response.
mean
The mean time taken for request and response.
sd
The standard deviation of the time for request and response.
+/- sd
The fraction of the number of requests within standard deviation
range (mean +/- sd) against total number of successful requests.
SEE ALSO
--------

View File

@@ -1,3 +1,54 @@
OUTPUT
------
requests
total
The number of requests h2load was instructed to make.
started
The number of requests h2load has started.
done
The number of requests completed.
succeeded
The number of requests completed successfully. Only HTTP status
code 2xx or3xx are considered as success.
failed
The number of requests failed, including HTTP level failures
(non-successful HTTP status code).
errored
The number of requests failed, except for HTTP level failures.
status code. This is the subset of the number reported in
``failed`` and most likely the network level failures or stream
was reset by RST_STREAM.
status codes
The number of status code h2load received.
traffic
total
The number of bytes received from the server "on the wire". If
requests were made via TLS, this value is the number of decrpyted
bytes.
headers
The number of response header bytes from the server without
decompression. For HTTP/2, this is the sum of the payload of
HEADERS frame. For SPDY, this is the sum of the payload of
SYN_REPLY frame.
data
The number of response body bytes received from the server.
time for request
min
The minimum time taken for request and response.
max
The maximum time taken for request and response.
mean
The mean time taken for request and response.
sd
The standard deviation of the time for request and response.
+/- sd
The fraction of the number of requests within standard deviation
range (mean +/- sd) against total number of successful requests.
SEE ALSO
--------

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "NGHTTP" "1" "January 11, 2015" "0.7.1" "nghttp2"
.TH "NGHTTP" "1" "February 27, 2015" "0.7.5" "nghttp2"
.SH NAME
nghttp \- HTTP/2 experimental client
.
@@ -41,14 +41,13 @@ HTTP/2 experimental client
.B <URI>
Specify URI to access.
.UNINDENT
.SH OPTIONS:
.SH OPTIONS
.INDENT 0.0
.TP
.B \-v, \-\-verbose
Print debug information such as reception and
transmission of frames and name/value pairs.
Specifying this option multiple times increases
verbosity.
transmission of frames and name/value pairs. Specifying
this option multiple times increases verbosity.
.UNINDENT
.INDENT 0.0
.TP
@@ -59,20 +58,19 @@ Discard downloaded data.
.TP
.B \-O, \-\-remote\-name
Save download data in the current directory. The
filename is dereived from URI. If URI ends with
\(aq\fI/\fP\(aq, \(aqindex.html\(aq is used as a filename. Not
implemented yet.
filename is dereived from URI. If URI ends with \(aq\fI/\fP\(aq,
\(aqindex.html\(aq is used as a filename. Not implemented
yet.
.UNINDENT
.INDENT 0.0
.TP
.B \-t, \-\-timeout=<N>
Timeout each request after <N> seconds.
.B \-t, \-\-timeout=<SEC>
Timeout each request after <SEC> seconds.
.UNINDENT
.INDENT 0.0
.TP
.B \-w, \-\-window\-bits=<N>
Sets the stream level initial window size to
2**<N>\-1.
Sets the stream level initial window size to 2**<N>\-1.
.UNINDENT
.INDENT 0.0
.TP
@@ -83,14 +81,13 @@ Sets the connection level initial window size to
.INDENT 0.0
.TP
.B \-a, \-\-get\-assets
Download assets such as stylesheets, images and
script files linked from the downloaded resource.
Only links whose origins are the same with the
linking resource will be downloaded. nghttp
prioritizes resources using HTTP/2 dependency
based priority. The priority order, from highest
to lowest, is html itself, css, javascript and
images.
Download assets such as stylesheets, images and script
files linked from the downloaded resource. Only links
whose origins are the same with the linking resource
will be downloaded. nghttp prioritizes resources using
HTTP/2 dependency based priority. The priority order,
from highest to lowest, is html itself, css, javascript
and images.
.UNINDENT
.INDENT 0.0
.TP
@@ -100,74 +97,70 @@ Print statistics.
.INDENT 0.0
.TP
.B \-H, \-\-header=<HEADER>
Add a header to the requests. Example:
\fI\%\-H\fP\(aq:method: PUT\(aq
Add a header to the requests. Example: \fI\%\-H\fP\(aq:method: PUT\(aq
.UNINDENT
.INDENT 0.0
.TP
.B \-\-cert=<CERT>
Use the specified client certificate file. The
file must be in PEM format.
Use the specified client certificate file. The file
must be in PEM format.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-key=<KEY>
Use the client private key file. The file must
be in PEM format.
Use the client private key file. The file must be in
PEM format.
.UNINDENT
.INDENT 0.0
.TP
.B \-d, \-\-data=<FILE>
Post FILE to server. If \(aq\-\(aq is given, data will
be read from stdin.
Post FILE to server. If \(aq\-\(aq is given, data will be read
from stdin.
.UNINDENT
.INDENT 0.0
.TP
.B \-m, \-\-multiply=<N>
Request each URI <N> times. By default, same URI
is not requested twice. This option disables it
too.
Request each URI <N> times. By default, same URI is not
requested twice. This option disables it too.
.UNINDENT
.INDENT 0.0
.TP
.B \-u, \-\-upgrade
Perform HTTP Upgrade for HTTP/2. This option is
ignored if the request URI has https scheme. If
\fI\-d\fP is used, the HTTP upgrade request is performed
with OPTIONS method.
Perform HTTP Upgrade for HTTP/2. This option is ignored
if the request URI has https scheme. If \fI\-d\fP is used, the
HTTP upgrade request is performed with OPTIONS method.
.UNINDENT
.INDENT 0.0
.TP
.B \-p, \-\-weight=<WEIGHT>
Sets priority group weight. The valid value
range is [1, 256], inclusive.
Sets priority group weight. The valid value range is
[1, 256], inclusive.
.sp
Default: \fB16\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-M, \-\-peer\-max\-concurrent\-streams=<N>
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value
of remote endpoint as if it is received in
SETTINGS frame. The default is large enough as
it is seen as unlimited.
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value of
remote endpoint as if it is received in SETTINGS frame.
The default is large enough as it is seen as unlimited.
.UNINDENT
.INDENT 0.0
.TP
.B \-c, \-\-header\-table\-size=<N>
.B \-c, \-\-header\-table\-size=<SIZE>
Specify decoder header table size.
.UNINDENT
.INDENT 0.0
.TP
.B \-b, \-\-padding=<N>
Add at most <N> bytes to a frame payload as
padding. Specify 0 to disable padding.
Add at most <N> bytes to a frame payload as padding.
Specify 0 to disable padding.
.UNINDENT
.INDENT 0.0
.TP
.B \-r, \-\-har=<FILE>
Output HTTP transactions <FILE> in HAR format.
If \(aq\-\(aq is given, data is written to stdout.
Output HTTP transactions <FILE> in HAR format. If \(aq\-\(aq
is given, data is written to stdout.
.UNINDENT
.INDENT 0.0
.TP
@@ -187,14 +180,12 @@ Don\(aqt send content\-length header field.
.INDENT 0.0
.TP
.B \-\-no\-dep
Don\(aqt send dependency based priority hint to
server.
Don\(aqt send dependency based priority hint to server.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-dep\-idle
Use idle streams as anchor nodes to express
priority.
Use idle streams as anchor nodes to express priority.
.UNINDENT
.INDENT 0.0
.TP
@@ -206,6 +197,9 @@ Display version information and exit.
.B \-h, \-\-help
Display this help and exit.
.UNINDENT
.sp
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
10 * 1024). Units are K, M and G (powers of 1024).
.SH SEE ALSO
.sp
\fInghttpd(1)\fP, \fInghttpx(1)\fP, \fIh2load(1)\fP

View File

@@ -16,15 +16,14 @@ HTTP/2 experimental client
Specify URI to access.
OPTIONS:
--------
OPTIONS
-------
.. option:: -v, --verbose
Print debug information such as reception and
transmission of frames and name/value pairs.
Specifying this option multiple times increases
verbosity.
transmission of frames and name/value pairs. Specifying
this option multiple times increases verbosity.
.. option:: -n, --null-out
@@ -33,18 +32,17 @@ OPTIONS:
.. option:: -O, --remote-name
Save download data in the current directory. The
filename is dereived from URI. If URI ends with
'*/*', 'index.html' is used as a filename. Not
implemented yet.
filename is dereived from URI. If URI ends with '*/*',
'index.html' is used as a filename. Not implemented
yet.
.. option:: -t, --timeout=<N>
.. option:: -t, --timeout=<SEC>
Timeout each request after <N> seconds.
Timeout each request after <SEC> seconds.
.. option:: -w, --window-bits=<N>
Sets the stream level initial window size to
2\*\*<N>-1.
Sets the stream level initial window size to 2\*\*<N>-1.
.. option:: -W, --connection-window-bits=<N>
@@ -53,14 +51,13 @@ OPTIONS:
.. option:: -a, --get-assets
Download assets such as stylesheets, images and
script files linked from the downloaded resource.
Only links whose origins are the same with the
linking resource will be downloaded. nghttp
prioritizes resources using HTTP/2 dependency
based priority. The priority order, from highest
to lowest, is html itself, css, javascript and
images.
Download assets such as stylesheets, images and script
files linked from the downloaded resource. Only links
whose origins are the same with the linking resource
will be downloaded. nghttp prioritizes resources using
HTTP/2 dependency based priority. The priority order,
from highest to lowest, is html itself, css, javascript
and images.
.. option:: -s, --stat
@@ -68,64 +65,60 @@ OPTIONS:
.. option:: -H, --header=<HEADER>
Add a header to the requests. Example:
:option:`-H`\':method: PUT'
Add a header to the requests. Example: :option:`-H`\':method: PUT'
.. option:: --cert=<CERT>
Use the specified client certificate file. The
file must be in PEM format.
Use the specified client certificate file. The file
must be in PEM format.
.. option:: --key=<KEY>
Use the client private key file. The file must
be in PEM format.
Use the client private key file. The file must be in
PEM format.
.. option:: -d, --data=<FILE>
Post FILE to server. If '-' is given, data will
be read from stdin.
Post FILE to server. If '-' is given, data will be read
from stdin.
.. option:: -m, --multiply=<N>
Request each URI <N> times. By default, same URI
is not requested twice. This option disables it
too.
Request each URI <N> times. By default, same URI is not
requested twice. This option disables it too.
.. option:: -u, --upgrade
Perform HTTP Upgrade for HTTP/2. This option is
ignored if the request URI has https scheme. If
:option:`-d` is used, the HTTP upgrade request is performed
with OPTIONS method.
Perform HTTP Upgrade for HTTP/2. This option is ignored
if the request URI has https scheme. If :option:`-d` is used, the
HTTP upgrade request is performed with OPTIONS method.
.. option:: -p, --weight=<WEIGHT>
Sets priority group weight. The valid value
range is [1, 256], inclusive.
Sets priority group weight. The valid value range is
[1, 256], inclusive.
Default: ``16``
.. option:: -M, --peer-max-concurrent-streams=<N>
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value
of remote endpoint as if it is received in
SETTINGS frame. The default is large enough as
it is seen as unlimited.
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value of
remote endpoint as if it is received in SETTINGS frame.
The default is large enough as it is seen as unlimited.
.. option:: -c, --header-table-size=<N>
.. option:: -c, --header-table-size=<SIZE>
Specify decoder header table size.
.. option:: -b, --padding=<N>
Add at most <N> bytes to a frame payload as
padding. Specify 0 to disable padding.
Add at most <N> bytes to a frame payload as padding.
Specify 0 to disable padding.
.. option:: -r, --har=<FILE>
Output HTTP transactions <FILE> in HAR format.
If '-' is given, data is written to stdout.
Output HTTP transactions <FILE> in HAR format. If '-'
is given, data is written to stdout.
.. option:: --color
@@ -141,13 +134,11 @@ OPTIONS:
.. option:: --no-dep
Don't send dependency based priority hint to
server.
Don't send dependency based priority hint to server.
.. option:: --dep-idle
Use idle streams as anchor nodes to express
priority.
Use idle streams as anchor nodes to express priority.
.. option:: --version
@@ -157,6 +148,10 @@ OPTIONS:
Display this help and exit.
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
10 * 1024). Units are K, M and G (powers of 1024).
SEE ALSO
--------

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "NGHTTPD" "1" "January 11, 2015" "0.7.1" "nghttp2"
.TH "NGHTTPD" "1" "February 27, 2015" "0.7.5" "nghttp2"
.SH NAME
nghttpd \- HTTP/2 experimental server
.
@@ -44,45 +44,48 @@ Specify listening port number.
.INDENT 0.0
.TP
.B <PRIVATE_KEY>
Set path to server\(aqs private key. Required
unless \fI\%\-\-no\-tls\fP is specified.
Set path to server\(aqs private key. Required unless
\fI\%\-\-no\-tls\fP is specified.
.UNINDENT
.INDENT 0.0
.TP
.B <CERT>
Set path to server\(aqs certificate. Required
unless \fI\%\-\-no\-tls\fP is specified.
Set path to server\(aqs certificate. Required unless
\fI\%\-\-no\-tls\fP is specified.
.UNINDENT
.SH OPTIONS
.INDENT 0.0
.TP
.B \-a, \-\-address=<ADDR>
The address to bind to. If not specified the default IP
address determined by getaddrinfo is used.
.UNINDENT
.SH OPTIONS:
.INDENT 0.0
.TP
.B \-D, \-\-daemon
Run in a background. If \fI\-D\fP is used, the current
working directory is changed to \(aq\fI/\fP\(aq. Therefore
if this option is used, \fI\%\-d\fP option must be
specified.
Run in a background. If \fI\-D\fP is used, the current working
directory is changed to \(aq\fI/\fP\(aq. Therefore if this option
is used, \fI\%\-d\fP option must be specified.
.UNINDENT
.INDENT 0.0
.TP
.B \-V, \-\-verify\-client
The server sends a client certificate request.
If the client did not return a certificate, the
handshake is terminated. Currently, this option
just requests a client certificate and does not
verify it.
The server sends a client certificate request. If the
client did not return a certificate, the handshake is
terminated. Currently, this option just requests a
client certificate and does not verify it.
.UNINDENT
.INDENT 0.0
.TP
.B \-d, \-\-htdocs=<PATH>
Specify document root. If this option is not
specified, the document root is the current
working directory.
Specify document root. If this option is not specified,
the document root is the current working directory.
.UNINDENT
.INDENT 0.0
.TP
.B \-v, \-\-verbose
Print debug information such as reception/
transmission of frames and name/value pairs.
Print debug information such as reception/ transmission
of frames and name/value pairs.
.UNINDENT
.INDENT 0.0
.TP
@@ -91,7 +94,7 @@ Disable SSL/TLS.
.UNINDENT
.INDENT 0.0
.TP
.B \-c, \-\-header\-table\-size=<N>
.B \-c, \-\-header\-table\-size=<SIZE>
Specify decoder header table size.
.UNINDENT
.INDENT 0.0
@@ -102,22 +105,21 @@ Force colored log output.
.INDENT 0.0
.TP
.B \-p, \-\-push=<PATH>=<PUSH_PATH,...>
Push resources <PUSH_PATH>s when <PATH> is
requested. This option can be used repeatedly to
specify multiple push configurations. <PATH> and
<PUSH_PATH>s are relative to document root. See
\fI\%\-\-htdocs\fP option. Example: \fI\-p\fP/=/foo.png
\fI\-p\fP/doc=/bar.css
Push resources <PUSH_PATH>s when <PATH> is requested.
This option can be used repeatedly to specify multiple
push configurations. <PATH> and <PUSH_PATH>s are
relative to document root. See \fI\%\-\-htdocs\fP option.
Example: \fI\-p\fP/=/foo.png \fI\-p\fP/doc=/bar.css
.UNINDENT
.INDENT 0.0
.TP
.B \-b, \-\-padding=<N>
Add at most <N> bytes to a frame payload as
padding. Specify 0 to disable padding.
Add at most <N> bytes to a frame payload as padding.
Specify 0 to disable padding.
.UNINDENT
.INDENT 0.0
.TP
.B \-n, \-\-workers=<CORE>
.B \-n, \-\-workers=<N>
Set the number of worker threads.
.sp
Default: \fB1\fP
@@ -130,16 +132,15 @@ Make error response gzipped.
.INDENT 0.0
.TP
.B \-\-dh\-param\-file=<PATH>
Path to file that contains DH parameters in PEM
format. Without this option, DHE cipher suites
are not available.
Path to file that contains DH parameters in PEM format.
Without this option, DHE cipher suites are not
available.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-early\-response
Start sending response when request HEADERS is
received, rather than complete request is
received.
Start sending response when request HEADERS is received,
rather than complete request is received.
.UNINDENT
.INDENT 0.0
.TP
@@ -151,6 +152,9 @@ Display version information and exit.
.B \-h, \-\-help
Display this help and exit.
.UNINDENT
.sp
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
10 * 1024). Units are K, M and G (powers of 1024).
.SH SEE ALSO
.sp
\fInghttp(1)\fP, \fInghttpx(1)\fP, \fIh2load(1)\fP

View File

@@ -18,48 +18,51 @@ HTTP/2 experimental server
.. describe:: <PRIVATE_KEY>
Set path to server's private key. Required
unless :option:`--no-tls` is specified.
Set path to server's private key. Required unless
:option:`--no-tls` is specified.
.. describe:: <CERT>
Set path to server's certificate. Required
unless :option:`--no-tls` is specified.
Set path to server's certificate. Required unless
:option:`--no-tls` is specified.
OPTIONS:
--------
OPTIONS
-------
.. option:: -a, --address=<ADDR>
The address to bind to. If not specified the default IP
address determined by getaddrinfo is used.
.. option:: -D, --daemon
Run in a background. If :option:`-D` is used, the current
working directory is changed to '*/*'. Therefore
if this option is used, :option:`-d` option must be
specified.
Run in a background. If :option:`-D` is used, the current working
directory is changed to '*/*'. Therefore if this option
is used, :option:`-d` option must be specified.
.. option:: -V, --verify-client
The server sends a client certificate request.
If the client did not return a certificate, the
handshake is terminated. Currently, this option
just requests a client certificate and does not
verify it.
The server sends a client certificate request. If the
client did not return a certificate, the handshake is
terminated. Currently, this option just requests a
client certificate and does not verify it.
.. option:: -d, --htdocs=<PATH>
Specify document root. If this option is not
specified, the document root is the current
working directory.
Specify document root. If this option is not specified,
the document root is the current working directory.
.. option:: -v, --verbose
Print debug information such as reception/
transmission of frames and name/value pairs.
Print debug information such as reception/ transmission
of frames and name/value pairs.
.. option:: --no-tls
Disable SSL/TLS.
.. option:: -c, --header-table-size=<N>
.. option:: -c, --header-table-size=<SIZE>
Specify decoder header table size.
@@ -69,19 +72,18 @@ OPTIONS:
.. option:: -p, --push=<PATH>=<PUSH_PATH,...>
Push resources <PUSH_PATH>s when <PATH> is
requested. This option can be used repeatedly to
specify multiple push configurations. <PATH> and
<PUSH_PATH>s are relative to document root. See
:option:`--htdocs` option. Example: :option:`\-p`/=/foo.png
:option:`-p`\/doc=/bar.css
Push resources <PUSH_PATH>s when <PATH> is requested.
This option can be used repeatedly to specify multiple
push configurations. <PATH> and <PUSH_PATH>s are
relative to document root. See :option:`--htdocs` option.
Example: :option:`-p`\/=/foo.png :option:`-p`\/doc=/bar.css
.. option:: -b, --padding=<N>
Add at most <N> bytes to a frame payload as
padding. Specify 0 to disable padding.
Add at most <N> bytes to a frame payload as padding.
Specify 0 to disable padding.
.. option:: -n, --workers=<CORE>
.. option:: -n, --workers=<N>
Set the number of worker threads.
@@ -93,15 +95,14 @@ OPTIONS:
.. option:: --dh-param-file=<PATH>
Path to file that contains DH parameters in PEM
format. Without this option, DHE cipher suites
are not available.
Path to file that contains DH parameters in PEM format.
Without this option, DHE cipher suites are not
available.
.. option:: --early-response
Start sending response when request HEADERS is
received, rather than complete request is
received.
Start sending response when request HEADERS is received,
rather than complete request is received.
.. option:: --version
@@ -111,6 +112,10 @@ OPTIONS:
Display this help and exit.
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
10 * 1024). Units are K, M and G (powers of 1024).
SEE ALSO
--------

View File

@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "NGHTTPX" "1" "January 11, 2015" "0.7.1" "nghttp2"
.TH "NGHTTPX" "1" "February 27, 2015" "0.7.5" "nghttp2"
.SH NAME
nghttpx \- HTTP/2 experimental proxy
.
@@ -39,29 +39,29 @@ A reverse proxy for HTTP/2, HTTP/1 and SPDY.
.INDENT 0.0
.TP
.B <PRIVATE_KEY>
Set path to server\(aqs private key. Required
unless \fI\%\-p\fP, \fI\%\-\-client\fP or \fI\%\-\-frontend\-no\-tls\fP are
given.
Set path to server\(aqs private key. Required unless \fI\%\-p\fP,
\fI\%\-\-client\fP or \fI\%\-\-frontend\-no\-tls\fP are given.
.UNINDENT
.INDENT 0.0
.TP
.B <CERT>
Set path to server\(aqs certificate. Required
unless \fI\%\-p\fP, \fI\%\-\-client\fP or \fI\%\-\-frontend\-no\-tls\fP are
given.
Set path to server\(aqs certificate. Required unless \fI\%\-p\fP,
\fI\%\-\-client\fP or \fI\%\-\-frontend\-no\-tls\fP are given.
.UNINDENT
.SH OPTIONS:
.SH OPTIONS
.sp
The options are categorized into several groups.
.SS Connections:
.SS Connections
.INDENT 0.0
.TP
.B \-b, \-\-backend=<HOST,PORT>
Set backend host and port. For HTTP/1 backend,
multiple backend addresses are accepted by
repeating this option. HTTP/2 backend does not
support multiple backend addresses and the first
occurrence of this option is used.
Set backend host and port. For HTTP/1 backend, multiple
backend addresses are accepted by repeating this option.
HTTP/2 backend does not support multiple backend
addresses and the first occurrence of this option is
used. UNIX domain socket can be specified by prefixing
path name with "unix:" (e.g.,
unix:/var/run/backend.sock)
.sp
Default: \fB127.0.0.1,80\fP
.UNINDENT
@@ -69,18 +69,18 @@ Default: \fB127.0.0.1,80\fP
.TP
.B \-f, \-\-frontend=<HOST,PORT>
Set frontend host and port. If <HOST> is \(aq*\(aq, it
assumes all addresses including both IPv4 and
IPv6.
assumes all addresses including both IPv4 and IPv6.
UNIX domain socket can be specified by prefixing path
name with "unix:" (e.g., unix:/var/run/nghttpx.sock)
.sp
Default: \fB*,3000\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-backlog=<NUM>
Set listen backlog size. If \fI\-1\fP is given,
libevent will choose suitable value.
.B \-\-backlog=<N>
Set listen backlog size.
.sp
Default: \fB128\fP
Default: \fB512\fP
.UNINDENT
.INDENT 0.0
.TP
@@ -96,222 +96,223 @@ Resolve backend hostname to IPv6 address only.
.TP
.B \-\-backend\-http\-proxy\-uri=<URI>
Specify proxy URI in the form
\fI\%http:/\fP/[<USER>:<PASS>@]<PROXY>:<PORT>. If a
proxy requires authentication, specify <USER> and
<PASS>. Note that they must be properly
percent\-encoded. This proxy is used when the
backend connection is HTTP/2. First, make a
CONNECT request to the proxy and it connects to
the backend on behalf of nghttpx. This forms
tunnel. After that, nghttpx performs SSL/TLS
handshake with the downstream through the tunnel.
The timeouts when connecting and making CONNECT
request can be specified by
\fI\%\-\-backend\-read\-timeout\fP and
\fI\%http:/\fP/[<USER>:<PASS>@]<PROXY>:<PORT>. If a proxy
requires authentication, specify <USER> and <PASS>.
Note that they must be properly percent\-encoded. This
proxy is used when the backend connection is HTTP/2.
First, make a CONNECT request to the proxy and it
connects to the backend on behalf of nghttpx. This
forms tunnel. After that, nghttpx performs SSL/TLS
handshake with the downstream through the tunnel. The
timeouts when connecting and making CONNECT request can
be specified by \fI\%\-\-backend\-read\-timeout\fP and
\fI\%\-\-backend\-write\-timeout\fP options.
.UNINDENT
.SS Performance:
.SS Performance
.INDENT 0.0
.TP
.B \-n, \-\-workers=<CORES>
.B \-n, \-\-workers=<N>
Set the number of worker threads.
.sp
Default: \fB1\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-read\-rate=<RATE>
Set maximum average read rate on frontend
connection. Setting 0 to this option means read
rate is unlimited.
.B \-\-read\-rate=<SIZE>
Set maximum average read rate on frontend connection.
Setting 0 to this option means read rate is unlimited.
.sp
Default: \fB0\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-read\-burst=<SIZE>
Set maximum read burst size on frontend
connection. Setting 0 to this option means read
burst size is unlimited.
Set maximum read burst size on frontend connection.
Setting 0 to this option means read burst size is
unlimited.
.sp
Default: \fB0\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-write\-rate=<RATE>
Set maximum average write rate on frontend
connection. Setting 0 to this option means write
rate is unlimited.
.B \-\-write\-rate=<SIZE>
Set maximum average write rate on frontend connection.
Setting 0 to this option means write rate is unlimited.
.sp
Default: \fB0\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-write\-burst=<SIZE>
Set maximum write burst size on frontend
connection. Setting 0 to this option means write
burst size is unlimited.
Set maximum write burst size on frontend connection.
Setting 0 to this option means write burst size is
unlimited.
.sp
Default: \fB0\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-worker\-read\-rate=<RATE>
Set maximum average read rate on frontend
connection per worker. Setting 0 to this option
means read rate is unlimited. Not implemented
yet.
.B \-\-worker\-read\-rate=<SIZE>
Set maximum average read rate on frontend connection per
worker. Setting 0 to this option means read rate is
unlimited. Not implemented yet.
.sp
Default: \fB0\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-worker\-read\-burst=<SIZE>
Set maximum read burst size on frontend
connection per worker. Setting 0 to this option
means read burst size is unlimited. Not
implemented yet.
Set maximum read burst size on frontend connection per
worker. Setting 0 to this option means read burst size
is unlimited. Not implemented yet.
.sp
Default: \fB0\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-worker\-write\-rate=<RATE>
Set maximum average write rate on frontend
connection per worker. Setting 0 to this option
means write rate is unlimited. Not implemented
yet.
.B \-\-worker\-write\-rate=<SIZE>
Set maximum average write rate on frontend connection
per worker. Setting 0 to this option means write rate
is unlimited. Not implemented yet.
.sp
Default: \fB0\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-worker\-write\-burst=<SIZE>
Set maximum write burst size on frontend
connection per worker. Setting 0 to this option
means write burst size is unlimited. Not
implemented yet.
Set maximum write burst size on frontend connection per
worker. Setting 0 to this option means write burst size
is unlimited. Not implemented yet.
.sp
Default: \fB0\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-worker\-frontend\-connections=<NUM>
Set maximum number of simultaneous connections
frontend accepts. Setting 0 means unlimited.
.B \-\-worker\-frontend\-connections=<N>
Set maximum number of simultaneous connections frontend
accepts. Setting 0 means unlimited.
.sp
Default: \fB0\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-backend\-http1\-connections\-per\-host=<NUM>
.B \-\-backend\-http1\-connections\-per\-host=<N>
Set maximum number of backend concurrent HTTP/1
connections per host. This option is meaningful
when \fI\%\-s\fP option is used. To limit the number of
connections per frontend for default mode, use
connections per host. This option is meaningful when \fI\%\-s\fP
option is used. To limit the number of connections per
frontend for default mode, use
\fI\%\-\-backend\-http1\-connections\-per\-frontend\fP\&.
.sp
Default: \fB8\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-backend\-http1\-connections\-per\-frontend=<NUM>
.B \-\-backend\-http1\-connections\-per\-frontend=<N>
Set maximum number of backend concurrent HTTP/1
connections per frontend. This option is only
used for default mode. 0 means unlimited. To
limit the number of connections per host for
HTTP/2 or SPDY proxy mode (\-s option), use
\fI\%\-\-backend\-http1\-connections\-per\-host\fP\&.
connections per frontend. This option is only used for
default mode. 0 means unlimited. To limit the number
of connections per host for HTTP/2 or SPDY proxy mode
(\-s option), use \fI\%\-\-backend\-http1\-connections\-per\-host\fP\&.
.sp
Default: \fB0\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-rlimit\-nofile=<N>
Set maximum number of open files (RLIMIT_NOFILE)
to <N>. If 0 is given, nghttpx does not set the
limit.
Set maximum number of open files (RLIMIT_NOFILE) to <N>.
If 0 is given, nghttpx does not set the limit.
.sp
Default: \fB0\fP
.UNINDENT
.SS Timeout:
.INDENT 0.0
.TP
.B \-\-frontend\-http2\-read\-timeout=<SEC>
.B \-\-backend\-request\-buffer=<SIZE>
Set buffer size used to store backend request.
.sp
Default: \fB16K\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-backend\-response\-buffer=<SIZE>
Set buffer size used to store backend response.
.sp
Default: \fB16K\fP
.UNINDENT
.SS Timeout
.INDENT 0.0
.TP
.B \-\-frontend\-http2\-read\-timeout=<DURATION>
Specify read timeout for HTTP/2 and SPDY frontend
connection.
.sp
Default: \fB180\fP
Default: \fB180s\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-frontend\-read\-timeout=<SEC>
Specify read timeout for HTTP/1.1 frontend
connection.
.B \-\-frontend\-read\-timeout=<DURATION>
Specify read timeout for HTTP/1.1 frontend connection.
.sp
Default: \fB180\fP
Default: \fB180s\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-frontend\-write\-timeout=<SEC>
Specify write timeout for all frontend
connections.
.B \-\-frontend\-write\-timeout=<DURATION>
Specify write timeout for all frontend connections.
.sp
Default: \fB30\fP
Default: \fB30s\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-stream\-read\-timeout=<SEC>
Specify read timeout for HTTP/2 and SPDY streams.
0 means no timeout.
.B \-\-stream\-read\-timeout=<DURATION>
Specify read timeout for HTTP/2 and SPDY streams. 0
means no timeout.
.sp
Default: \fB0\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-stream\-write\-timeout=<SEC>
Specify write timeout for HTTP/2 and SPDY
streams. 0 means no timeout.
.B \-\-stream\-write\-timeout=<DURATION>
Specify write timeout for HTTP/2 and SPDY streams. 0
means no timeout.
.sp
Default: \fB0\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-backend\-read\-timeout=<SEC>
.B \-\-backend\-read\-timeout=<DURATION>
Specify read timeout for backend connection.
.sp
Default: \fB180\fP
Default: \fB180s\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-backend\-write\-timeout=<SEC>
.B \-\-backend\-write\-timeout=<DURATION>
Specify write timeout for backend connection.
.sp
Default: \fB30\fP
Default: \fB30s\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-backend\-keep\-alive\-timeout=<SEC>
Specify keep\-alive timeout for backend
connection.
.B \-\-backend\-keep\-alive\-timeout=<DURATION>
Specify keep\-alive timeout for backend connection.
.sp
Default: \fB600\fP
Default: \fB2s\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-listener\-disable\-timeout=<SEC>
After accepting connection failed, connection
listener is disabled for a given time in seconds.
Specifying 0 disables this feature.
.B \-\-listener\-disable\-timeout=<DURATION>
After accepting connection failed, connection listener
is disabled for a given amount of time. Specifying 0
disables this feature.
.sp
Default: \fB0\fP
.UNINDENT
.SS SSL/TLS:
.SS SSL/TLS
.INDENT 0.0
.TP
.B \-\-ciphers=<SUITE>
Set allowed cipher list. The format of the
string is described in OpenSSL ciphers(1).
Set allowed cipher list. The format of the string is
described in OpenSSL ciphers(1).
.UNINDENT
.INDENT 0.0
.TP
@@ -323,56 +324,52 @@ Don\(aqt verify backend server\(aqs certificate if \fI\%\-p\fP,
.INDENT 0.0
.TP
.B \-\-cacert=<PATH>
Set path to trusted CA certificate file if \fI\%\-p\fP,
\fI\%\-\-client\fP or \fI\%\-\-http2\-bridge\fP are given and
\fI\%\-\-backend\-no\-tls\fP is not given. The file must be
in PEM format. It can contain multiple
certificates. If the linked OpenSSL is
configured to load system wide certificates, they
are loaded at startup regardless of this option.
Set path to trusted CA certificate file if \fI\%\-p\fP, \fI\%\-\-client\fP
or \fI\%\-\-http2\-bridge\fP are given and \fI\%\-\-backend\-no\-tls\fP is not
given. The file must be in PEM format. It can contain
multiple certificates. If the linked OpenSSL is
configured to load system wide certificates, they are
loaded at startup regardless of this option.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-private\-key\-passwd\-file=<FILEPATH>
Path to file that contains password for the
server\(aqs private key. If none is given and the
private key is password protected it\(aqll be
requested interactively.
.B \-\-private\-key\-passwd\-file=<PATH>
Path to file that contains password for the server\(aqs
private key. If none is given and the private key is
password protected it\(aqll be requested interactively.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-subcert=<KEYPATH>:<CERTPATH>
Specify additional certificate and private key
file. nghttpx will choose certificates based on
the hostname indicated by client using TLS SNI
extension. This option can be used multiple
times.
Specify additional certificate and private key file.
nghttpx will choose certificates based on the hostname
indicated by client using TLS SNI extension. This
option can be used multiple times.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-backend\-tls\-sni\-field=<HOST>
Explicitly set the content of the TLS SNI
extension. This will default to the backend HOST
name.
Explicitly set the content of the TLS SNI extension.
This will default to the backend HOST name.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-dh\-param\-file=<PATH>
Path to file that contains DH parameters in PEM
format. Without this option, DHE cipher suites
are not available.
Path to file that contains DH parameters in PEM format.
Without this option, DHE cipher suites are not
available.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-npn\-list=<LIST>
Comma delimited list of ALPN protocol identifier
sorted in the order of preference. That means
most desirable protocol comes first. This is
used in both ALPN and NPN. The parameter must be
delimited by a single comma only and any white
spaces are treated as a part of protocol string.
Comma delimited list of ALPN protocol identifier sorted
in the order of preference. That means most desirable
protocol comes first. This is used in both ALPN and
NPN. The parameter must be delimited by a single comma
only and any white spaces are treated as a part of
protocol string.
.sp
Default: \fBh2\-16,h2\-14,spdy/3.1,http/1.1\fP
Default: \fBh2,h2\-16,h2\-14,spdy/3.1,http/1.1\fP
.UNINDENT
.INDENT 0.0
.TP
@@ -382,82 +379,89 @@ Require and verify client certificate.
.INDENT 0.0
.TP
.B \-\-verify\-client\-cacert=<PATH>
Path to file that contains CA certificates to
verify client certificate. The file must be in
PEM format. It can contain multiple
certificates.
Path to file that contains CA certificates to verify
client certificate. The file must be in PEM format. It
can contain multiple certificates.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-client\-private\-key\-file=<PATH>
Path to file that contains client private key
used in backend client authentication.
Path to file that contains client private key used in
backend client authentication.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-client\-cert\-file=<PATH>
Path to file that contains client certificate
used in backend client authentication.
Path to file that contains client certificate used in
backend client authentication.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-tls\-proto\-list=<LIST>
Comma delimited list of SSL/TLS protocol to be
enabled. The following protocols are available:
TLSv1.2, TLSv1.1 and TLSv1.0. The name matching
is done in case\-insensitive manner. The
parameter must be delimited by a single comma
only and any white spaces are treated as a part
of protocol string.
Comma delimited list of SSL/TLS protocol to be enabled.
The following protocols are available: TLSv1.2, TLSv1.1
and TLSv1.0. The name matching is done in
case\-insensitive manner. The parameter must be
delimited by a single comma only and any white spaces
are treated as a part of protocol string.
.sp
Default: \fBTLSv1.2,TLSv1.1\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-tls\-ticket\-key\-file=<FILE>
Path to file that contains 48 bytes random data
to construct TLS session ticket parameters. This
options can be used repeatedly to specify
multiple ticket parameters. If several files are
given, only the first key is used to encrypt TLS
session tickets. Other keys are accepted but
server will issue new session ticket with first
key. This allows session key rotation. Please
note that key rotation does not occur
automatically. User should rearrange files or
change options values and restart nghttpx
gracefully. If opening or reading given file
fails, all loaded keys are discarded and it is
treated as if none of this option is given. If
this option is not given or an error occurred
.B \-\-tls\-ticket\-key\-file=<PATH>
Path to file that contains 48 bytes random data to
construct TLS session ticket parameters. This options
can be used repeatedly to specify multiple ticket
parameters. If several files are given, only the first
key is used to encrypt TLS session tickets. Other keys
are accepted but server will issue new session ticket
with first key. This allows session key rotation.
Please note that key rotation does not occur
automatically. User should rearrange files or change
options values and restart nghttpx gracefully. If
opening or reading given file fails, all loaded keys are
discarded and it is treated as if none of this option is
given. If this option is not given or an error occurred
while opening or reading a file, key is generated
automatically and renewed every 12hrs. At most 2
keys are stored in memory.
automatically and renewed every 12hrs. At most 2 keys
are stored in memory.
.UNINDENT
.SS HTTP/2 and SPDY:
.INDENT 0.0
.TP
.B \-c, \-\-http2\-max\-concurrent\-streams=<NUM>
Set the maximum number of the concurrent streams
in one HTTP/2 and SPDY session.
.B \-\-tls\-ctx\-per\-worker
Create OpenSSL\(aqs SSL_CTX per worker, so that no internal
locking is required. This may improve scalability with
multi threaded configuration. If this option is
enabled, session ID is no longer shared accross SSL_CTX
objects, which means session ID generated by one worker
is not acceptable by another worker. On the other hand,
session ticket key is shared across all worker threads.
.UNINDENT
.SS HTTP/2 and SPDY
.INDENT 0.0
.TP
.B \-c, \-\-http2\-max\-concurrent\-streams=<N>
Set the maximum number of the concurrent streams in one
HTTP/2 and SPDY session.
.sp
Default: \fB100\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-frontend\-http2\-window\-bits=<N>
Sets the per\-stream initial window size of HTTP/2
SPDY frontend connection. For HTTP/2, the size
is 2**<N>\-1. For SPDY, the size is 2**<N>.
Sets the per\-stream initial window size of HTTP/2 SPDY
frontend connection. For HTTP/2, the size is 2**<N>\-1.
For SPDY, the size is 2**<N>.
.sp
Default: \fB16\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-frontend\-http2\-connection\-window\-bits=<N>
Sets the per\-connection window size of HTTP/2 and
SPDY frontend connection. For HTTP/2, the size
is 2**<N>\-1. For SPDY, the size is 2**<N>.
Sets the per\-connection window size of HTTP/2 and SPDY
frontend connection. For HTTP/2, the size is
2**<N>\-1. For SPDY, the size is 2**<N>.
.sp
Default: \fB16\fP
.UNINDENT
@@ -477,8 +481,8 @@ Default: \fB16\fP
.INDENT 0.0
.TP
.B \-\-backend\-http2\-connection\-window\-bits=<N>
Sets the per\-connection window size of HTTP/2
backend connection to 2**<N>\-1.
Sets the per\-connection window size of HTTP/2 backend
connection to 2**<N>\-1.
.sp
Default: \fB16\fP
.UNINDENT
@@ -495,20 +499,27 @@ Don\(aqt crumble cookie header field.
.INDENT 0.0
.TP
.B \-\-padding=<N>
Add at most <N> bytes to a HTTP/2 frame payload
as padding. Specify 0 to disable padding. This
option is meant for debugging purpose and not
intended to enhance protocol security.
Add at most <N> bytes to a HTTP/2 frame payload as
padding. Specify 0 to disable padding. This option is
meant for debugging purpose and not intended to enhance
protocol security.
.UNINDENT
.SS Mode:
.INDENT 0.0
.TP
.B \-\-no\-server\-push
Disable HTTP/2 server push. Server push is only
supported by default mode and HTTP/2 frontend. SPDY
frontend does not support server push.
.UNINDENT
.SS Mode
.INDENT 0.0
.TP
.B (default mode)
Accept HTTP/2, SPDY and HTTP/1.1 over SSL/TLS.
If \fI\%\-\-frontend\-no\-tls\fP is used, accept HTTP/2 and
HTTP/1.1. The incoming HTTP/1.1 connection can
be upgraded to HTTP/2 through HTTP Upgrade. The
protocol to the backend is HTTP/1.1.
Accept HTTP/2, SPDY and HTTP/1.1 over SSL/TLS. If
\fI\%\-\-frontend\-no\-tls\fP is used, accept HTTP/2 and HTTP/1.1.
The incoming HTTP/1.1 connection can be upgraded to
HTTP/2 through HTTP Upgrade. The protocol to the
backend is HTTP/1.1.
.UNINDENT
.INDENT 0.0
.TP
@@ -518,59 +529,56 @@ Like default mode, but enable secure proxy mode.
.INDENT 0.0
.TP
.B \-\-http2\-bridge
Like default mode, but communicate with the
backend in HTTP/2 over SSL/TLS. Thus the
incoming all connections are converted to HTTP/2
connection and relayed to the backend. See
\fI\%\-\-backend\-http\-proxy\-uri\fP option if you are behind
the proxy and want to connect to the outside
Like default mode, but communicate with the backend in
HTTP/2 over SSL/TLS. Thus the incoming all connections
are converted to HTTP/2 connection and relayed to the
backend. See \fI\%\-\-backend\-http\-proxy\-uri\fP option if you are
behind the proxy and want to connect to the outside
HTTP/2 proxy.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-client
Accept HTTP/2 and HTTP/1.1 without SSL/TLS. The
incoming HTTP/1.1 connection can be upgraded to
HTTP/2 connection through HTTP Upgrade. The
protocol to the backend is HTTP/2. To use
nghttpx as a forward proxy, use \fI\%\-p\fP option
instead.
incoming HTTP/1.1 connection can be upgraded to HTTP/2
connection through HTTP Upgrade. The protocol to the
backend is HTTP/2. To use nghttpx as a forward proxy,
use \fI\%\-p\fP option instead.
.UNINDENT
.INDENT 0.0
.TP
.B \-p, \-\-client\-proxy
Like \fI\%\-\-client\fP option, but it also requires the
request path from frontend must be an absolute
URI, suitable for use as a forward proxy.
Like \fI\%\-\-client\fP option, but it also requires the request
path from frontend must be an absolute URI, suitable for
use as a forward proxy.
.UNINDENT
.SS Logging:
.SS Logging
.INDENT 0.0
.TP
.B \-L, \-\-log\-level=<LEVEL>
Set the severity level of log output. <LEVEL>
must be one of INFO, NOTICE, WARN, ERROR and
FATAL.
Set the severity level of log output. <LEVEL> must be
one of INFO, NOTICE, WARN, ERROR and FATAL.
.sp
Default: \fBNOTICE\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-accesslog\-file=<PATH>
Set path to write access log. To reopen file,
send USR1 signal to nghttpx.
Set path to write access log. To reopen file, send USR1
signal to nghttpx.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-accesslog\-syslog
Send access log to syslog. If this option is
used, \fI\%\-\-accesslog\-file\fP option is ignored.
Send access log to syslog. If this option is used,
\fI\%\-\-accesslog\-file\fP option is ignored.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-accesslog\-format=<FORMAT>
Specify format string for access log. The
default format is combined format. The following
variables are available:
Specify format string for access log. The default
format is combined format. The following variables are
available:
.INDENT 7.0
.IP \(bu 2
$remote_addr: client IP address.
@@ -583,24 +591,24 @@ $request: HTTP request line.
.IP \(bu 2
$status: HTTP response status code.
.IP \(bu 2
$body_bytes_sent: the number of bytes sent to
client as response body.
$body_bytes_sent: the number of bytes sent to client
as response body.
.IP \(bu 2
$http_<VAR>: value of HTTP request header <VAR>
where \(aq_\(aq in <VAR> is replaced with \(aq\-\(aq.
$http_<VAR>: value of HTTP request header <VAR> where
\(aq_\(aq in <VAR> is replaced with \(aq\-\(aq.
.IP \(bu 2
$remote_port: client port.
.IP \(bu 2
$server_port: server port.
.IP \(bu 2
$request_time: request processing time in
seconds with milliseconds resolution.
$request_time: request processing time in seconds with
milliseconds resolution.
.IP \(bu 2
$pid: PID of the running process.
.IP \(bu 2
$alpn: ALPN identifier of the protocol which
generates the response. For HTTP/1, ALPN is
always http/1.1, regardless of minor version.
$alpn: ALPN identifier of the protocol which generates
the response. For HTTP/1, ALPN is always http/1.1,
regardless of minor version.
.UNINDENT
.sp
Default: \fB$remote_addr \- \- [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"\fP
@@ -608,16 +616,16 @@ Default: \fB$remote_addr \- \- [$time_local] "$request" $status $body_bytes_sent
.INDENT 0.0
.TP
.B \-\-errorlog\-file=<PATH>
Set path to write error log. To reopen file,
send USR1 signal to nghttpx.
Set path to write error log. To reopen file, send USR1
signal to nghttpx.
.sp
Default: \fB/dev/stderr\fP
.UNINDENT
.INDENT 0.0
.TP
.B \-\-errorlog\-syslog
Send error log to syslog. If this option is
used, \fI\%\-\-errorlog\-file\fP option is ignored.
Send error log to syslog. If this option is used,
\fI\%\-\-errorlog\-file\fP option is ignored.
.UNINDENT
.INDENT 0.0
.TP
@@ -626,86 +634,92 @@ Set syslog facility to <FACILITY>.
.sp
Default: \fBdaemon\fP
.UNINDENT
.SS Misc:
.SS HTTP
.INDENT 0.0
.TP
.B \-\-add\-x\-forwarded\-for
Append X\-Forwarded\-For header field to the
downstream request.
Append X\-Forwarded\-For header field to the downstream
request.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-strip\-incoming\-x\-forwarded\-for
Strip X\-Forwarded\-For header field from inbound
client requests.
Strip X\-Forwarded\-For header field from inbound client
requests.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-no\-via
Don\(aqt append to Via header field. If Via header
field is received, it is left unaltered.
Don\(aqt append to Via header field. If Via header field
is received, it is left unaltered.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-no\-location\-rewrite
Don\(aqt rewrite location header field on
Don\(aqt rewrite location header field on \fI\%\-\-http2\-bridge\fP,
\fI\%\-\-client\fP and default mode. For \fI\%\-\-http2\-proxy\fP and
\fI\%\-\-client\-proxy\fP mode, location header field will not be
altered regardless of this option.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-no\-host\-rewrite
Don\(aqt rewrite host and :authority header fields on
\fI\%\-\-http2\-bridge\fP, \fI\%\-\-client\fP and default mode. For
\fI\%\-\-http2\-proxy\fP and \fI\%\-\-client\-proxy\fP mode, location
header field will not be altered regardless of
this option.
\fI\%\-\-http2\-proxy\fP and \fI\%\-\-client\-proxy\fP mode, these headers
will not be altered regardless of this option.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-altsvc=<PROTOID,PORT[,HOST,[ORIGIN]]>
Specify protocol ID, port, host and origin of
alternative service. <HOST> and <ORIGIN> are
optional. They are advertised in alt\-svc header
field or HTTP/2 ALTSVC frame. This option can be
used multiple times to specify multiple
alternative services. Example: \fI\%\-\-altsvc\fP=h2,443
alternative service. <HOST> and <ORIGIN> are optional.
They are advertised in alt\-svc header field or HTTP/2
ALTSVC frame. This option can be used multiple times to
specify multiple alternative services. Example:
\fI\%\-\-altsvc\fP=h2,443
.UNINDENT
.INDENT 0.0
.TP
.B \-\-add\-response\-header=<HEADER>
Specify additional header field to add to
response header set. This option just appends
header field and won\(aqt replace anything already
set. This option can be used several times to
specify multiple header fields.
Specify additional header field to add to response
header set. This option just appends header field and
won\(aqt replace anything already set. This option can be
used several times to specify multiple header fields.
Example: \fI\%\-\-add\-response\-header\fP="foo: bar"
.UNINDENT
.SS Debug
.INDENT 0.0
.TP
.B \-\-frontend\-http2\-dump\-request\-header=<PATH>
Dumps request headers received by HTTP/2 frontend
to the file denoted in <PATH>. The output is
done in HTTP/1 header field format and each
header block is followed by an empty line. This
option is not thread safe and MUST NOT be used
with option \fI\%\-n\fP<N>, where <N> >= 2.
Dumps request headers received by HTTP/2 frontend to the
file denoted in <PATH>. The output is done in HTTP/1
header field format and each header block is followed by
an empty line. This option is not thread safe and MUST
NOT be used with option \fI\%\-n\fP<N>, where <N> >= 2.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-frontend\-http2\-dump\-response\-header=<PATH>
Dumps response headers sent from HTTP/2 frontend
to the file denoted in <PATH>. The output is
done in HTTP/1 header field format and each
header block is followed by an empty line. This
option is not thread safe and MUST NOT be used
with option \fI\%\-n\fP<N>, where <N> >= 2.
Dumps response headers sent from HTTP/2 frontend to the
file denoted in <PATH>. The output is done in HTTP/1
header field format and each header block is followed by
an empty line. This option is not thread safe and MUST
NOT be used with option \fI\%\-n\fP<N>, where <N> >= 2.
.UNINDENT
.INDENT 0.0
.TP
.B \-o, \-\-frontend\-frame\-debug
Print HTTP/2 frames in frontend to stderr. This
option is not thread safe and MUST NOT be used
with option \fI\%\-n\fP=N, where N >= 2.
Print HTTP/2 frames in frontend to stderr. This option
is not thread safe and MUST NOT be used with option
\fI\%\-n\fP=N, where N >= 2.
.UNINDENT
.SS Process
.INDENT 0.0
.TP
.B \-D, \-\-daemon
Run in a background. If \fI\%\-D\fP is used, the current
working directory is changed to \(aq\fI/\fP\(aq.
Run in a background. If \fI\%\-D\fP is used, the current working
directory is changed to \(aq\fI/\fP\(aq.
.UNINDENT
.INDENT 0.0
.TP
@@ -715,9 +729,10 @@ Set path to save PID of this program.
.INDENT 0.0
.TP
.B \-\-user=<USER>
Run this program as <USER>. This option is
intended to be used to drop root privileges.
Run this program as <USER>. This option is intended to
be used to drop root privileges.
.UNINDENT
.SS Misc
.INDENT 0.0
.TP
.B \-\-conf=<PATH>
@@ -735,6 +750,13 @@ Print version and exit.
.B \-h, \-\-help
Print this help and exit.
.UNINDENT
.sp
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
10 * 1024). Units are K, M and G (powers of 1024).
.sp
The <DURATION> argument is an integer and an optional unit (e.g., 1s
is 1 second and 500ms is 500 milliseconds). Units are s or ms. If
a unit is omitted, a second is used as unit.
.SH FILES
.INDENT 0.0
.TP
@@ -779,6 +801,50 @@ path with same command\-line arguments and environment variables.
After new process comes up, sending SIGQUIT to the original process
to perform hot swapping.
.UNINDENT
.SH SERVER PUSH
.sp
nghttpx supports HTTP/2 server push in default mode. nghttpx looks
for Link header field (\fI\%RFC 5988\fP) in response headers from
backend server and extracts URI\-reference with parameter
\fBrel=preload\fP (see \fI\%preload\fP)
and pushes those URIs to the frontend client. Here is a sample Link
header field to initiate server push:
.INDENT 0.0
.INDENT 3.5
.sp
.nf
.ft C
Link: </fonts/font.woff>; rel=preload
Link: </css/theme.css>; rel=preload
.ft P
.fi
.UNINDENT
.UNINDENT
.sp
Currently, the following restrictions are applied for server push:
.INDENT 0.0
.IP 1. 3
URI\-reference must not contain authority. If it exists, it is not
pushed. \fB/fonts/font.woff\fP and \fBcss/theme.css\fP are eligible to
be pushed. \fBhttps://example.org/fonts/font.woff\fP and
\fB//example.org/css/theme.css\fP are not.
.IP 2. 3
The associated stream must have method "GET" or "POST". The
associated stream\(aqs status code must be 200.
.UNINDENT
.sp
These limitations may be loosened in the future release.
.SH UNIX DOMAIN SOCKET
.sp
nghttpx supports UNIX domain socket with a filename for both frontend
and backend connections.
.sp
Please note that current nghttpx implementation does not delete a
socket with a filename. And on start up, if nghttpx detects that the
specified socket already exists in the file system, nghttpx first
deletes it. However, if SIGUSR2 is used to execute new binary and
both old and new configurations use same filename, new binary does not
delete the socket and continues to use it.
.SH SEE ALSO
.sp
\fInghttp(1)\fP, \fInghttpd(1)\fP, \fIh2load(1)\fP

View File

@@ -14,48 +14,50 @@ A reverse proxy for HTTP/2, HTTP/1 and SPDY.
.. describe:: <PRIVATE_KEY>
Set path to server's private key. Required
unless :option:`-p`\, :option:`--client` or :option:`\--frontend-no-tls` are
given.
Set path to server's private key. Required unless :option:`-p`\,
:option:`--client` or :option:`\--frontend-no-tls` are given.
.. describe:: <CERT>
Set path to server's certificate. Required
unless :option:`-p`\, :option:`--client` or :option:`\--frontend-no-tls` are
given.
Set path to server's certificate. Required unless :option:`-p`\,
:option:`--client` or :option:`\--frontend-no-tls` are given.
OPTIONS:
--------
OPTIONS
-------
The options are categorized into several groups.
Connections:
~~~~~~~~~~~~
Connections
~~~~~~~~~~~
.. option:: -b, --backend=<HOST,PORT>
Set backend host and port. For HTTP/1 backend,
multiple backend addresses are accepted by
repeating this option. HTTP/2 backend does not
support multiple backend addresses and the first
occurrence of this option is used.
Set backend host and port. For HTTP/1 backend, multiple
backend addresses are accepted by repeating this option.
HTTP/2 backend does not support multiple backend
addresses and the first occurrence of this option is
used. UNIX domain socket can be specified by prefixing
path name with "unix:" (e.g.,
unix:/var/run/backend.sock)
Default: ``127.0.0.1,80``
.. option:: -f, --frontend=<HOST,PORT>
Set frontend host and port. If <HOST> is '\*', it
assumes all addresses including both IPv4 and
IPv6.
assumes all addresses including both IPv4 and IPv6.
UNIX domain socket can be specified by prefixing path
name with "unix:" (e.g., unix:/var/run/nghttpx.sock)
Default: ``*,3000``
.. option:: --backlog=<NUM>
.. option:: --backlog=<N>
Set listen backlog size. If :option:`-1` is given,
libevent will choose suitable value.
Set listen backlog size.
Default: ``128``
Default: ``512``
.. option:: --backend-ipv4
@@ -68,208 +70,207 @@ Connections:
.. option:: --backend-http-proxy-uri=<URI>
Specify proxy URI in the form
http://[<USER>:<PASS>@]<PROXY>:<PORT>. If a
proxy requires authentication, specify <USER> and
<PASS>. Note that they must be properly
percent-encoded. This proxy is used when the
backend connection is HTTP/2. First, make a
CONNECT request to the proxy and it connects to
the backend on behalf of nghttpx. This forms
tunnel. After that, nghttpx performs SSL/TLS
handshake with the downstream through the tunnel.
The timeouts when connecting and making CONNECT
request can be specified by
:option:`--backend-read-timeout` and
http://[<USER>:<PASS>@]<PROXY>:<PORT>. If a proxy
requires authentication, specify <USER> and <PASS>.
Note that they must be properly percent-encoded. This
proxy is used when the backend connection is HTTP/2.
First, make a CONNECT request to the proxy and it
connects to the backend on behalf of nghttpx. This
forms tunnel. After that, nghttpx performs SSL/TLS
handshake with the downstream through the tunnel. The
timeouts when connecting and making CONNECT request can
be specified by :option:`--backend-read-timeout` and
:option:`--backend-write-timeout` options.
Performance:
~~~~~~~~~~~~
Performance
~~~~~~~~~~~
.. option:: -n, --workers=<CORES>
.. option:: -n, --workers=<N>
Set the number of worker threads.
Default: ``1``
.. option:: --read-rate=<RATE>
.. option:: --read-rate=<SIZE>
Set maximum average read rate on frontend
connection. Setting 0 to this option means read
rate is unlimited.
Set maximum average read rate on frontend connection.
Setting 0 to this option means read rate is unlimited.
Default: ``0``
.. option:: --read-burst=<SIZE>
Set maximum read burst size on frontend
connection. Setting 0 to this option means read
burst size is unlimited.
Set maximum read burst size on frontend connection.
Setting 0 to this option means read burst size is
unlimited.
Default: ``0``
.. option:: --write-rate=<RATE>
.. option:: --write-rate=<SIZE>
Set maximum average write rate on frontend
connection. Setting 0 to this option means write
rate is unlimited.
Set maximum average write rate on frontend connection.
Setting 0 to this option means write rate is unlimited.
Default: ``0``
.. option:: --write-burst=<SIZE>
Set maximum write burst size on frontend
connection. Setting 0 to this option means write
burst size is unlimited.
Set maximum write burst size on frontend connection.
Setting 0 to this option means write burst size is
unlimited.
Default: ``0``
.. option:: --worker-read-rate=<RATE>
.. option:: --worker-read-rate=<SIZE>
Set maximum average read rate on frontend
connection per worker. Setting 0 to this option
means read rate is unlimited. Not implemented
yet.
Set maximum average read rate on frontend connection per
worker. Setting 0 to this option means read rate is
unlimited. Not implemented yet.
Default: ``0``
.. option:: --worker-read-burst=<SIZE>
Set maximum read burst size on frontend
connection per worker. Setting 0 to this option
means read burst size is unlimited. Not
implemented yet.
Set maximum read burst size on frontend connection per
worker. Setting 0 to this option means read burst size
is unlimited. Not implemented yet.
Default: ``0``
.. option:: --worker-write-rate=<RATE>
.. option:: --worker-write-rate=<SIZE>
Set maximum average write rate on frontend
connection per worker. Setting 0 to this option
means write rate is unlimited. Not implemented
yet.
Set maximum average write rate on frontend connection
per worker. Setting 0 to this option means write rate
is unlimited. Not implemented yet.
Default: ``0``
.. option:: --worker-write-burst=<SIZE>
Set maximum write burst size on frontend
connection per worker. Setting 0 to this option
means write burst size is unlimited. Not
implemented yet.
Set maximum write burst size on frontend connection per
worker. Setting 0 to this option means write burst size
is unlimited. Not implemented yet.
Default: ``0``
.. option:: --worker-frontend-connections=<NUM>
.. option:: --worker-frontend-connections=<N>
Set maximum number of simultaneous connections
frontend accepts. Setting 0 means unlimited.
Set maximum number of simultaneous connections frontend
accepts. Setting 0 means unlimited.
Default: ``0``
.. option:: --backend-http1-connections-per-host=<NUM>
.. option:: --backend-http1-connections-per-host=<N>
Set maximum number of backend concurrent HTTP/1
connections per host. This option is meaningful
when :option:`-s` option is used. To limit the number of
connections per frontend for default mode, use
connections per host. This option is meaningful when :option:`-s`
option is used. To limit the number of connections per
frontend for default mode, use
:option:`--backend-http1-connections-per-frontend`\.
Default: ``8``
.. option:: --backend-http1-connections-per-frontend=<NUM>
.. option:: --backend-http1-connections-per-frontend=<N>
Set maximum number of backend concurrent HTTP/1
connections per frontend. This option is only
used for default mode. 0 means unlimited. To
limit the number of connections per host for
HTTP/2 or SPDY proxy mode (-s option), use
:option:`--backend-http1-connections-per-host`\.
connections per frontend. This option is only used for
default mode. 0 means unlimited. To limit the number
of connections per host for HTTP/2 or SPDY proxy mode
(-s option), use :option:`--backend-http1-connections-per-host`\.
Default: ``0``
.. option:: --rlimit-nofile=<N>
Set maximum number of open files (RLIMIT_NOFILE)
to <N>. If 0 is given, nghttpx does not set the
limit.
Set maximum number of open files (RLIMIT_NOFILE) to <N>.
If 0 is given, nghttpx does not set the limit.
Default: ``0``
.. option:: --backend-request-buffer=<SIZE>
Timeout:
~~~~~~~~
Set buffer size used to store backend request.
.. option:: --frontend-http2-read-timeout=<SEC>
Default: ``16K``
.. option:: --backend-response-buffer=<SIZE>
Set buffer size used to store backend response.
Default: ``16K``
Timeout
~~~~~~~
.. option:: --frontend-http2-read-timeout=<DURATION>
Specify read timeout for HTTP/2 and SPDY frontend
connection.
Default: ``180``
Default: ``180s``
.. option:: --frontend-read-timeout=<SEC>
.. option:: --frontend-read-timeout=<DURATION>
Specify read timeout for HTTP/1.1 frontend
connection.
Specify read timeout for HTTP/1.1 frontend connection.
Default: ``180``
Default: ``180s``
.. option:: --frontend-write-timeout=<SEC>
.. option:: --frontend-write-timeout=<DURATION>
Specify write timeout for all frontend
connections.
Specify write timeout for all frontend connections.
Default: ``30``
Default: ``30s``
.. option:: --stream-read-timeout=<SEC>
.. option:: --stream-read-timeout=<DURATION>
Specify read timeout for HTTP/2 and SPDY streams.
0 means no timeout.
Specify read timeout for HTTP/2 and SPDY streams. 0
means no timeout.
Default: ``0``
.. option:: --stream-write-timeout=<SEC>
.. option:: --stream-write-timeout=<DURATION>
Specify write timeout for HTTP/2 and SPDY
streams. 0 means no timeout.
Specify write timeout for HTTP/2 and SPDY streams. 0
means no timeout.
Default: ``0``
.. option:: --backend-read-timeout=<SEC>
.. option:: --backend-read-timeout=<DURATION>
Specify read timeout for backend connection.
Default: ``180``
Default: ``180s``
.. option:: --backend-write-timeout=<SEC>
.. option:: --backend-write-timeout=<DURATION>
Specify write timeout for backend connection.
Default: ``30``
Default: ``30s``
.. option:: --backend-keep-alive-timeout=<SEC>
.. option:: --backend-keep-alive-timeout=<DURATION>
Specify keep-alive timeout for backend
connection.
Specify keep-alive timeout for backend connection.
Default: ``600``
Default: ``2s``
.. option:: --listener-disable-timeout=<SEC>
.. option:: --listener-disable-timeout=<DURATION>
After accepting connection failed, connection
listener is disabled for a given time in seconds.
Specifying 0 disables this feature.
After accepting connection failed, connection listener
is disabled for a given amount of time. Specifying 0
disables this feature.
Default: ``0``
SSL/TLS:
~~~~~~~~
SSL/TLS
~~~~~~~
.. option:: --ciphers=<SUITE>
Set allowed cipher list. The format of the
string is described in OpenSSL ciphers(1).
Set allowed cipher list. The format of the string is
described in OpenSSL ciphers(1).
.. option:: -k, --insecure
@@ -279,51 +280,47 @@ SSL/TLS:
.. option:: --cacert=<PATH>
Set path to trusted CA certificate file if :option:`-p`\,
:option:`--client` or :option:`\--http2-bridge` are given and
:option:`--backend-no-tls` is not given. The file must be
in PEM format. It can contain multiple
certificates. If the linked OpenSSL is
configured to load system wide certificates, they
are loaded at startup regardless of this option.
Set path to trusted CA certificate file if :option:`-p`\, :option:`--client`
or :option:`--http2-bridge` are given and :option:`\--backend-no-tls` is not
given. The file must be in PEM format. It can contain
multiple certificates. If the linked OpenSSL is
configured to load system wide certificates, they are
loaded at startup regardless of this option.
.. option:: --private-key-passwd-file=<FILEPATH>
.. option:: --private-key-passwd-file=<PATH>
Path to file that contains password for the
server's private key. If none is given and the
private key is password protected it'll be
requested interactively.
Path to file that contains password for the server's
private key. If none is given and the private key is
password protected it'll be requested interactively.
.. option:: --subcert=<KEYPATH>:<CERTPATH>
Specify additional certificate and private key
file. nghttpx will choose certificates based on
the hostname indicated by client using TLS SNI
extension. This option can be used multiple
times.
Specify additional certificate and private key file.
nghttpx will choose certificates based on the hostname
indicated by client using TLS SNI extension. This
option can be used multiple times.
.. option:: --backend-tls-sni-field=<HOST>
Explicitly set the content of the TLS SNI
extension. This will default to the backend HOST
name.
Explicitly set the content of the TLS SNI extension.
This will default to the backend HOST name.
.. option:: --dh-param-file=<PATH>
Path to file that contains DH parameters in PEM
format. Without this option, DHE cipher suites
are not available.
Path to file that contains DH parameters in PEM format.
Without this option, DHE cipher suites are not
available.
.. option:: --npn-list=<LIST>
Comma delimited list of ALPN protocol identifier
sorted in the order of preference. That means
most desirable protocol comes first. This is
used in both ALPN and NPN. The parameter must be
delimited by a single comma only and any white
spaces are treated as a part of protocol string.
Comma delimited list of ALPN protocol identifier sorted
in the order of preference. That means most desirable
protocol comes first. This is used in both ALPN and
NPN. The parameter must be delimited by a single comma
only and any white spaces are treated as a part of
protocol string.
Default: ``h2-16,h2-14,spdy/3.1,http/1.1``
Default: ``h2,h2-16,h2-14,spdy/3.1,http/1.1``
.. option:: --verify-client
@@ -331,78 +328,84 @@ SSL/TLS:
.. option:: --verify-client-cacert=<PATH>
Path to file that contains CA certificates to
verify client certificate. The file must be in
PEM format. It can contain multiple
certificates.
Path to file that contains CA certificates to verify
client certificate. The file must be in PEM format. It
can contain multiple certificates.
.. option:: --client-private-key-file=<PATH>
Path to file that contains client private key
used in backend client authentication.
Path to file that contains client private key used in
backend client authentication.
.. option:: --client-cert-file=<PATH>
Path to file that contains client certificate
used in backend client authentication.
Path to file that contains client certificate used in
backend client authentication.
.. option:: --tls-proto-list=<LIST>
Comma delimited list of SSL/TLS protocol to be
enabled. The following protocols are available:
TLSv1.2, TLSv1.1 and TLSv1.0. The name matching
is done in case-insensitive manner. The
parameter must be delimited by a single comma
only and any white spaces are treated as a part
of protocol string.
Comma delimited list of SSL/TLS protocol to be enabled.
The following protocols are available: TLSv1.2, TLSv1.1
and TLSv1.0. The name matching is done in
case-insensitive manner. The parameter must be
delimited by a single comma only and any white spaces
are treated as a part of protocol string.
Default: ``TLSv1.2,TLSv1.1``
.. option:: --tls-ticket-key-file=<FILE>
.. option:: --tls-ticket-key-file=<PATH>
Path to file that contains 48 bytes random data
to construct TLS session ticket parameters. This
options can be used repeatedly to specify
multiple ticket parameters. If several files are
given, only the first key is used to encrypt TLS
session tickets. Other keys are accepted but
server will issue new session ticket with first
key. This allows session key rotation. Please
note that key rotation does not occur
automatically. User should rearrange files or
change options values and restart nghttpx
gracefully. If opening or reading given file
fails, all loaded keys are discarded and it is
treated as if none of this option is given. If
this option is not given or an error occurred
Path to file that contains 48 bytes random data to
construct TLS session ticket parameters. This options
can be used repeatedly to specify multiple ticket
parameters. If several files are given, only the first
key is used to encrypt TLS session tickets. Other keys
are accepted but server will issue new session ticket
with first key. This allows session key rotation.
Please note that key rotation does not occur
automatically. User should rearrange files or change
options values and restart nghttpx gracefully. If
opening or reading given file fails, all loaded keys are
discarded and it is treated as if none of this option is
given. If this option is not given or an error occurred
while opening or reading a file, key is generated
automatically and renewed every 12hrs. At most 2
keys are stored in memory.
automatically and renewed every 12hrs. At most 2 keys
are stored in memory.
.. option:: --tls-ctx-per-worker
Create OpenSSL's SSL_CTX per worker, so that no internal
locking is required. This may improve scalability with
multi threaded configuration. If this option is
enabled, session ID is no longer shared accross SSL_CTX
objects, which means session ID generated by one worker
is not acceptable by another worker. On the other hand,
session ticket key is shared across all worker threads.
HTTP/2 and SPDY:
~~~~~~~~~~~~~~~~
HTTP/2 and SPDY
~~~~~~~~~~~~~~~
.. option:: -c, --http2-max-concurrent-streams=<NUM>
.. option:: -c, --http2-max-concurrent-streams=<N>
Set the maximum number of the concurrent streams
in one HTTP/2 and SPDY session.
Set the maximum number of the concurrent streams in one
HTTP/2 and SPDY session.
Default: ``100``
.. option:: --frontend-http2-window-bits=<N>
Sets the per-stream initial window size of HTTP/2
SPDY frontend connection. For HTTP/2, the size
is 2**<N>-1. For SPDY, the size is 2\*\*<N>.
Sets the per-stream initial window size of HTTP/2 SPDY
frontend connection. For HTTP/2, the size is 2\*\*<N>-1.
For SPDY, the size is 2\*\*<N>.
Default: ``16``
.. option:: --frontend-http2-connection-window-bits=<N>
Sets the per-connection window size of HTTP/2 and
SPDY frontend connection. For HTTP/2, the size
is 2**<N>-1. For SPDY, the size is 2\*\*<N>.
Sets the per-connection window size of HTTP/2 and SPDY
frontend connection. For HTTP/2, the size is
2**<N>-1. For SPDY, the size is 2\*\*<N>.
Default: ``16``
@@ -413,14 +416,14 @@ HTTP/2 and SPDY:
.. option:: --backend-http2-window-bits=<N>
Sets the initial window size of HTTP/2 backend
connection to 2**<N>-1.
connection to 2\*\*<N>-1.
Default: ``16``
.. option:: --backend-http2-connection-window-bits=<N>
Sets the per-connection window size of HTTP/2
backend connection to 2\*\*<N>-1.
Sets the per-connection window size of HTTP/2 backend
connection to 2\*\*<N>-1.
Default: ``16``
@@ -434,22 +437,29 @@ HTTP/2 and SPDY:
.. option:: --padding=<N>
Add at most <N> bytes to a HTTP/2 frame payload
as padding. Specify 0 to disable padding. This
option is meant for debugging purpose and not
intended to enhance protocol security.
Add at most <N> bytes to a HTTP/2 frame payload as
padding. Specify 0 to disable padding. This option is
meant for debugging purpose and not intended to enhance
protocol security.
.. option:: --no-server-push
Disable HTTP/2 server push. Server push is only
supported by default mode and HTTP/2 frontend. SPDY
frontend does not support server push.
Mode:
~~~~~
Mode
~~~~
.. describe:: (default mode)
Accept HTTP/2, SPDY and HTTP/1.1 over SSL/TLS.
If :option:`--frontend-no-tls` is used, accept HTTP/2 and
HTTP/1.1. The incoming HTTP/1.1 connection can
be upgraded to HTTP/2 through HTTP Upgrade. The
protocol to the backend is HTTP/1.1.
Accept HTTP/2, SPDY and HTTP/1.1 over SSL/TLS. If
:option:`--frontend-no-tls` is used, accept HTTP/2 and HTTP/1.1.
The incoming HTTP/1.1 connection can be upgraded to
HTTP/2 through HTTP Upgrade. The protocol to the
backend is HTTP/1.1.
.. option:: -s, --http2-proxy
@@ -457,89 +467,86 @@ Mode:
.. option:: --http2-bridge
Like default mode, but communicate with the
backend in HTTP/2 over SSL/TLS. Thus the
incoming all connections are converted to HTTP/2
connection and relayed to the backend. See
:option:`--backend-http-proxy-uri` option if you are behind
the proxy and want to connect to the outside
Like default mode, but communicate with the backend in
HTTP/2 over SSL/TLS. Thus the incoming all connections
are converted to HTTP/2 connection and relayed to the
backend. See :option:`--backend-http-proxy-uri` option if you are
behind the proxy and want to connect to the outside
HTTP/2 proxy.
.. option:: --client
Accept HTTP/2 and HTTP/1.1 without SSL/TLS. The
incoming HTTP/1.1 connection can be upgraded to
HTTP/2 connection through HTTP Upgrade. The
protocol to the backend is HTTP/2. To use
nghttpx as a forward proxy, use :option:`-p` option
instead.
incoming HTTP/1.1 connection can be upgraded to HTTP/2
connection through HTTP Upgrade. The protocol to the
backend is HTTP/2. To use nghttpx as a forward proxy,
use :option:`-p` option instead.
.. option:: -p, --client-proxy
Like :option:`--client` option, but it also requires the
request path from frontend must be an absolute
URI, suitable for use as a forward proxy.
Like :option:`--client` option, but it also requires the request
path from frontend must be an absolute URI, suitable for
use as a forward proxy.
Logging:
~~~~~~~~
Logging
~~~~~~~
.. option:: -L, --log-level=<LEVEL>
Set the severity level of log output. <LEVEL>
must be one of INFO, NOTICE, WARN, ERROR and
FATAL.
Set the severity level of log output. <LEVEL> must be
one of INFO, NOTICE, WARN, ERROR and FATAL.
Default: ``NOTICE``
.. option:: --accesslog-file=<PATH>
Set path to write access log. To reopen file,
send USR1 signal to nghttpx.
Set path to write access log. To reopen file, send USR1
signal to nghttpx.
.. option:: --accesslog-syslog
Send access log to syslog. If this option is
used, :option:`--accesslog-file` option is ignored.
Send access log to syslog. If this option is used,
:option:`--accesslog-file` option is ignored.
.. option:: --accesslog-format=<FORMAT>
Specify format string for access log. The
default format is combined format. The following
variables are available:
Specify format string for access log. The default
format is combined format. The following variables are
available:
* $remote_addr: client IP address.
* $time_local: local time in Common Log format.
* $time_iso8601: local time in ISO 8601 format.
* $request: HTTP request line.
* $status: HTTP response status code.
* $body_bytes_sent: the number of bytes sent to
client as response body.
* $http_<VAR>: value of HTTP request header <VAR>
where '_' in <VAR> is replaced with '-'.
* $body_bytes_sent: the number of bytes sent to client
as response body.
* $http_<VAR>: value of HTTP request header <VAR> where
'_' in <VAR> is replaced with '-'.
* $remote_port: client port.
* $server_port: server port.
* $request_time: request processing time in
seconds with milliseconds resolution.
* $request_time: request processing time in seconds with
milliseconds resolution.
* $pid: PID of the running process.
* $alpn: ALPN identifier of the protocol which
generates the response. For HTTP/1, ALPN is
always http/1.1, regardless of minor version.
* $alpn: ALPN identifier of the protocol which generates
the response. For HTTP/1, ALPN is always http/1.1,
regardless of minor version.
Default: ``$remote_addr - - [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"``
.. option:: --errorlog-file=<PATH>
Set path to write error log. To reopen file,
send USR1 signal to nghttpx.
Set path to write error log. To reopen file, send USR1
signal to nghttpx.
Default: ``/dev/stderr``
.. option:: --errorlog-syslog
Send error log to syslog. If this option is
used, :option:`--errorlog-file` option is ignored.
Send error log to syslog. If this option is used,
:option:`--errorlog-file` option is ignored.
.. option:: --syslog-facility=<FACILITY>
@@ -548,78 +555,89 @@ Logging:
Default: ``daemon``
Misc:
~~~~~
HTTP
~~~~
.. option:: --add-x-forwarded-for
Append X-Forwarded-For header field to the
downstream request.
Append X-Forwarded-For header field to the downstream
request.
.. option:: --strip-incoming-x-forwarded-for
Strip X-Forwarded-For header field from inbound
client requests.
Strip X-Forwarded-For header field from inbound client
requests.
.. option:: --no-via
Don't append to Via header field. If Via header
field is received, it is left unaltered.
Don't append to Via header field. If Via header field
is received, it is left unaltered.
.. option:: --no-location-rewrite
Don't rewrite location header field on
Don't rewrite location header field on :option:`--http2-bridge`\,
:option:`--client` and default mode. For :option:`\--http2-proxy` and
:option:`--client-proxy` mode, location header field will not be
altered regardless of this option.
.. option:: --no-host-rewrite
Don't rewrite host and :authority header fields on
:option:`--http2-bridge`\, :option:`--client` and default mode. For
:option:`--http2-proxy` and :option:`\--client-proxy` mode, location
header field will not be altered regardless of
this option.
:option:`--http2-proxy` and :option:`\--client-proxy` mode, these headers
will not be altered regardless of this option.
.. option:: --altsvc=<PROTOID,PORT[,HOST,[ORIGIN]]>
Specify protocol ID, port, host and origin of
alternative service. <HOST> and <ORIGIN> are
optional. They are advertised in alt-svc header
field or HTTP/2 ALTSVC frame. This option can be
used multiple times to specify multiple
alternative services. Example: :option:`--altsvc`\=h2,443
alternative service. <HOST> and <ORIGIN> are optional.
They are advertised in alt-svc header field or HTTP/2
ALTSVC frame. This option can be used multiple times to
specify multiple alternative services. Example:
:option:`--altsvc`\=h2,443
.. option:: --add-response-header=<HEADER>
Specify additional header field to add to
response header set. This option just appends
header field and won't replace anything already
set. This option can be used several times to
specify multiple header fields.
Specify additional header field to add to response
header set. This option just appends header field and
won't replace anything already set. This option can be
used several times to specify multiple header fields.
Example: :option:`--add-response-header`\="foo: bar"
Debug
~~~~~
.. option:: --frontend-http2-dump-request-header=<PATH>
Dumps request headers received by HTTP/2 frontend
to the file denoted in <PATH>. The output is
done in HTTP/1 header field format and each
header block is followed by an empty line. This
option is not thread safe and MUST NOT be used
with option :option:`-n`\<N>, where <N> >= 2.
Dumps request headers received by HTTP/2 frontend to the
file denoted in <PATH>. The output is done in HTTP/1
header field format and each header block is followed by
an empty line. This option is not thread safe and MUST
NOT be used with option :option:`-n`\<N>, where <N> >= 2.
.. option:: --frontend-http2-dump-response-header=<PATH>
Dumps response headers sent from HTTP/2 frontend
to the file denoted in <PATH>. The output is
done in HTTP/1 header field format and each
header block is followed by an empty line. This
option is not thread safe and MUST NOT be used
with option :option:`-n`\<N>, where <N> >= 2.
Dumps response headers sent from HTTP/2 frontend to the
file denoted in <PATH>. The output is done in HTTP/1
header field format and each header block is followed by
an empty line. This option is not thread safe and MUST
NOT be used with option :option:`-n`\<N>, where <N> >= 2.
.. option:: -o, --frontend-frame-debug
Print HTTP/2 frames in frontend to stderr. This
option is not thread safe and MUST NOT be used
with option :option:`-n`\=N, where N >= 2.
Print HTTP/2 frames in frontend to stderr. This option
is not thread safe and MUST NOT be used with option
:option:`-n`\=N, where N >= 2.
Process
~~~~~~~
.. option:: -D, --daemon
Run in a background. If :option:`-D` is used, the current
working directory is changed to '*/*'.
Run in a background. If :option:`-D` is used, the current working
directory is changed to '*/*'.
.. option:: --pid-file=<PATH>
@@ -627,8 +645,12 @@ Misc:
.. option:: --user=<USER>
Run this program as <USER>. This option is
intended to be used to drop root privileges.
Run this program as <USER>. This option is intended to
be used to drop root privileges.
Misc
~~~~
.. option:: --conf=<PATH>
@@ -644,6 +666,14 @@ Misc:
Print this help and exit.
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
10 * 1024). Units are K, M and G (powers of 1024).
The <DURATION> argument is an integer and an optional unit (e.g., 1s
is 1 second and 500ms is 500 milliseconds). Units are s or ms. If
a unit is omitted, a second is used as unit.
FILES
-----
@@ -688,6 +718,48 @@ SIGUSR2
After new process comes up, sending SIGQUIT to the original process
to perform hot swapping.
SERVER PUSH
-----------
nghttpx supports HTTP/2 server push in default mode. nghttpx looks
for Link header field (`RFC 5988
<http://tools.ietf.org/html/rfc5988>`_) in response headers from
backend server and extracts URI-reference with parameter
``rel=preload`` (see `preload
<http://w3c.github.io/preload/#interoperability-with-http-link-header>`_)
and pushes those URIs to the frontend client. Here is a sample Link
header field to initiate server push:
.. code-block:: http
Link: </fonts/font.woff>; rel=preload
Link: </css/theme.css>; rel=preload
Currently, the following restrictions are applied for server push:
1. URI-reference must not contain authority. If it exists, it is not
pushed. ``/fonts/font.woff`` and ``css/theme.css`` are eligible to
be pushed. ``https://example.org/fonts/font.woff`` and
``//example.org/css/theme.css`` are not.
2. The associated stream must have method "GET" or "POST". The
associated stream's status code must be 200.
These limitations may be loosened in the future release.
UNIX DOMAIN SOCKET
------------------
nghttpx supports UNIX domain socket with a filename for both frontend
and backend connections.
Please note that current nghttpx implementation does not delete a
socket with a filename. And on start up, if nghttpx detects that the
specified socket already exists in the file system, nghttpx first
deletes it. However, if SIGUSR2 is used to execute new binary and
both old and new configurations use same filename, new binary does not
delete the socket and continues to use it.
SEE ALSO
--------

View File

@@ -42,6 +42,48 @@ SIGUSR2
After new process comes up, sending SIGQUIT to the original process
to perform hot swapping.
SERVER PUSH
-----------
nghttpx supports HTTP/2 server push in default mode. nghttpx looks
for Link header field (`RFC 5988
<http://tools.ietf.org/html/rfc5988>`_) in response headers from
backend server and extracts URI-reference with parameter
``rel=preload`` (see `preload
<http://w3c.github.io/preload/#interoperability-with-http-link-header>`_)
and pushes those URIs to the frontend client. Here is a sample Link
header field to initiate server push:
.. code-block:: http
Link: </fonts/font.woff>; rel=preload
Link: </css/theme.css>; rel=preload
Currently, the following restrictions are applied for server push:
1. URI-reference must not contain authority. If it exists, it is not
pushed. ``/fonts/font.woff`` and ``css/theme.css`` are eligible to
be pushed. ``https://example.org/fonts/font.woff`` and
``//example.org/css/theme.css`` are not.
2. The associated stream must have method "GET" or "POST". The
associated stream's status code must be 200.
These limitations may be loosened in the future release.
UNIX DOMAIN SOCKET
------------------
nghttpx supports UNIX domain socket with a filename for both frontend
and backend connections.
Please note that current nghttpx implementation does not delete a
socket with a filename. And on start up, if nghttpx detects that the
specified socket already exists in the file system, nghttpx first
deletes it. However, if SIGUSR2 is used to execute new binary and
both old and new configurations use same filename, new binary does not
delete the socket and continues to use it.
SEE ALSO
--------

View File

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

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

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

5
examples/.gitignore vendored
View File

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

View File

@@ -33,12 +33,10 @@ AM_CPPFLAGS = \
@LIBEVENT_OPENSSL_CFLAGS@ \
@OPENSSL_CFLAGS@ \
@DEFS@
AM_LDFLAGS = \
LDADD = $(top_builddir)/lib/libnghttp2.la \
$(top_builddir)/third-party/libhttp-parser.la \
@LIBEVENT_OPENSSL_LIBS@ \
@OPENSSL_LIBS@
LDADD = \
$(top_builddir)/lib/libnghttp2.la \
$(top_builddir)/third-party/libhttp-parser.la
noinst_PROGRAMS = client libevent-client libevent-server deflate
@@ -60,26 +58,34 @@ endif # ENABLE_TINY_NGHTTPD
if ENABLE_ASIO_LIB
noinst_PROGRAMS += asio-sv asio-sv2 asio-sv3
noinst_PROGRAMS += asio-sv asio-sv2 asio-cl asio-cl2
ASIOCPPFLAGS = ${BOOST_CPPFLAGS} ${AM_CPPFLAGS}
ASIOLDFLAGS = @JEMALLOC_LIBS@
ASIOLDADD = $(top_builddir)/src/libnghttp2_asio.la
ASIOLDADD = $(top_builddir)/lib/libnghttp2.la \
$(top_builddir)/src/libnghttp2_asio.la @JEMALLOC_LIBS@ \
$(top_builddir)/third-party/libhttp-parser.la \
${BOOST_LDFLAGS} \
${BOOST_ASIO_LIB} \
${BOOST_THREAD_LIB} \
${BOOST_SYSTEM_LIB} \
@OPENSSL_LIBS@ \
@APPLDFLAGS@
asio_sv_SOURCES = asio-sv.cc
asio_sv_CPPFLAGS = ${ASIOCPPFLAGS}
asio_sv_LDFLAGS = ${ASIOLDFLAGS}
asio_sv_LDADD = ${ASIOLDADD}
asio_sv2_SOURCES = asio-sv2.cc
asio_sv2_CPPFLAGS = ${ASIOCPPFLAGS}
asio_sv2_LDFLAGS = ${ASIOLDFLAGS}
asio_sv2_LDADD = ${ASIOLDADD}
asio_sv3_SOURCES = asio-sv3.cc
asio_sv3_CPPFLAGS = ${ASIOCPPFLAGS}
asio_sv3_LDFLAGS = ${ASIOLDFLAGS}
asio_sv3_LDADD = ${ASIOLDADD}
asio_cl_SOURCES = asio-cl.cc
asio_cl_CPPFLAGS = ${ASIOCPPFLAGS}
asio_cl_LDADD = ${ASIOLDADD}
asio_cl2_SOURCES = asio-cl2.cc
asio_cl2_CPPFLAGS = ${ASIOCPPFLAGS}
asio_cl2_LDADD = ${ASIOLDADD}
endif # ENABLE_ASIO_LIB

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

View File

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

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

@@ -558,6 +558,8 @@ static void fetch_uri(const struct URI *uri) {
diec("nghttp2_session_client_new", rv);
}
nghttp2_submit_settings(connection.session, NGHTTP2_FLAG_NONE, NULL, 0);
/* Submit the HTTP request to the outbound queue. */
submit_request(&connection, &req);

View File

@@ -179,13 +179,57 @@ static char *io_buf_add_str(io_buf *buf, const void *src, size_t len) {
return (char *)start;
}
static int memseq(const uint8_t *a, size_t alen, const char *b) {
const uint8_t *last = a + alen;
static int memeq(const void *a, const void *b, size_t n) {
return memcmp(a, b, n) == 0;
}
for (; a != last && *b && *a == *b; ++a, ++b)
;
#define streq(A, B, N) ((sizeof((A)) - 1) == (N) && memeq((A), (B), (N)))
return a == last && *b == 0;
typedef enum {
NGHTTP2_TOKEN__AUTHORITY,
NGHTTP2_TOKEN__METHOD,
NGHTTP2_TOKEN__PATH,
NGHTTP2_TOKEN__SCHEME,
NGHTTP2_TOKEN_HOST,
} nghttp2_token;
/* Inspired by h2o header lookup. https://github.com/h2o/h2o */
static int lookup_token(const uint8_t *name, size_t namelen) {
switch (namelen) {
case 5:
switch (name[namelen - 1]) {
case 'h':
if (streq(":pat", name, 4)) {
return NGHTTP2_TOKEN__PATH;
}
break;
}
break;
case 7:
switch (name[namelen - 1]) {
case 'd':
if (streq(":metho", name, 6)) {
return NGHTTP2_TOKEN__METHOD;
}
break;
case 'e':
if (streq(":schem", name, 6)) {
return NGHTTP2_TOKEN__SCHEME;
}
break;
}
break;
case 10:
switch (name[namelen - 1]) {
case 'y':
if (streq(":authorit", name, 9)) {
return NGHTTP2_TOKEN__AUTHORITY;
}
break;
}
break;
}
return -1;
}
static char *cpydig(char *buf, int n, size_t len) {
@@ -925,8 +969,7 @@ static int on_header_callback(nghttp2_session *session,
const nghttp2_frame *frame, const uint8_t *name,
size_t namelen, const uint8_t *value,
size_t valuelen, uint8_t flags _U_,
void *user_data) {
connection *conn = user_data;
void *user_data _U_) {
stream *strm;
if (frame->hd.type != NGHTTP2_HEADERS ||
@@ -940,74 +983,42 @@ static int on_header_callback(nghttp2_session *session,
return 0;
}
if (!nghttp2_check_header_name(name, namelen) ||
!nghttp2_check_header_value(value, valuelen)) {
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
if (memseq(name, namelen, ":method")) {
if (strm->method) {
stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
switch (lookup_token(name, namelen)) {
case NGHTTP2_TOKEN__METHOD:
strm->method = io_buf_add_str(&strm->scrbuf, value, valuelen);
if (!strm->method) {
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
strm->methodlen = valuelen;
return 0;
}
if (memseq(name, namelen, ":scheme")) {
if (strm->scheme) {
stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
break;
case NGHTTP2_TOKEN__SCHEME:
strm->scheme = io_buf_add_str(&strm->scrbuf, value, valuelen);
if (!strm->scheme) {
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
strm->schemelen = valuelen;
return 0;
}
if (memseq(name, namelen, ":authority")) {
if (strm->authority) {
stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
break;
case NGHTTP2_TOKEN__AUTHORITY:
strm->authority = io_buf_add_str(&strm->scrbuf, value, valuelen);
if (!strm->authority) {
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
strm->authoritylen = valuelen;
return 0;
}
if (memseq(name, namelen, ":path")) {
if (strm->path) {
stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
break;
case NGHTTP2_TOKEN__PATH:
strm->path = io_buf_add_str(&strm->scrbuf, value, valuelen);
if (!strm->path) {
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
strm->pathlen = valuelen;
return 0;
}
if (name[0] == ':') {
stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
if (memseq(name, namelen, "host") && !strm->host) {
break;
case NGHTTP2_TOKEN_HOST:
strm->host = io_buf_add_str(&strm->scrbuf, value, valuelen);
if (!strm->host) {
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
strm->hostlen = valuelen;
break;
}
return 0;
@@ -1029,12 +1040,6 @@ static int on_frame_recv_callback(nghttp2_session *session,
return 0;
}
if (!strm->method || !strm->scheme || !strm->path ||
(!strm->authority && !strm->host)) {
stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
return 0;
}
if (!strm->host) {
strm->host = strm->authority;
strm->hostlen = strm->authoritylen;

View File

@@ -20,6 +20,12 @@ HEADERS = [
"alt-svc",
"content-length",
"location",
"trailer",
"link",
"accept-encoding",
"accept-language",
"cache-control",
"user-agent",
# disallowed h1 headers
'connection',
'keep-alive',
@@ -78,7 +84,7 @@ int lookup_token(const uint8_t *name, size_t namelen) {
case '{}':'''.format(c)
for k in headers:
print '''\
if (util::streq("{}", name, {})) {{
if (util::streq_l("{}", name, {})) {{
return {};
}}'''.format(k[:-1], size - 1, to_enum_hd(k))
print '''\

85
genlibtokenlookup.py Executable file
View File

@@ -0,0 +1,85 @@
#!/usr/bin/env python
HEADERS = [
':authority',
':method',
':path',
':scheme',
':status',
"content-length",
"host",
"te",
'connection',
'keep-alive',
'proxy-connection',
'transfer-encoding',
'upgrade'
]
def to_enum_hd(k):
res = 'NGHTTP2_TOKEN_'
for c in k.upper():
if c == ':' or c == '-':
res += '_'
continue
res += c
return res
def build_header(headers):
res = {}
for k in headers:
size = len(k)
if size not in res:
res[size] = {}
ent = res[size]
c = k[-1]
if c not in ent:
ent[c] = []
ent[c].append(k)
return res
def gen_enum():
print '''\
typedef enum {'''
for k in sorted(HEADERS):
print '''\
{},'''.format(to_enum_hd(k))
print '''\
NGHTTP2_TOKEN_MAXIDX,
} nghttp2_token;'''
def gen_index_header():
print '''\
static int lookup_token(const uint8_t *name, size_t namelen) {
switch (namelen) {'''
b = build_header(HEADERS)
for size in sorted(b.keys()):
ents = b[size]
print '''\
case {}:'''.format(size)
print '''\
switch (name[namelen - 1]) {'''
for c in sorted(ents.keys()):
headers = sorted(ents[c])
print '''\
case '{}':'''.format(c)
for k in headers:
print '''\
if (streq("{}", name, {})) {{
return {};
}}'''.format(k[:-1], size - 1, to_enum_hd(k))
print '''\
break;'''
print '''\
}
break;'''
print '''\
}
return -1;
}'''
if __name__ == '__main__':
gen_enum()
print ''
gen_index_header()

View File

@@ -8,7 +8,7 @@ import sys
import re
import argparse
arg_indent = ' ' * 21
arg_indent = ' ' * 14
def help2man(infile):
# We assume that first line is usage line like this:
@@ -87,7 +87,7 @@ DESCRIPTION
continue
if line.startswith(' ') and in_arg:
if not line.startswith(arg_indent):
sys.stderr.write('warning: argument description is not indented correctly. We need {} spaces as indentation.'.format(len(arg_indent)))
sys.stderr.write('warning: argument description is not indented correctly. We need {} spaces as indentation.\n'.format(len(arg_indent)))
print '{}'.format(format_arg_text(line[len(arg_indent):]))
continue
@@ -96,8 +96,8 @@ DESCRIPTION
in_arg = False
if line == 'Options:':
print 'OPTIONS:'
print '--------'
print 'OPTIONS'
print '-------'
print ''
continue
@@ -137,7 +137,7 @@ DESCRIPTION
if not line.startswith(' ') and line.endswith(':'):
# subsection
subsec = line.strip()
subsec = line.strip()[:-1]
print '{}'.format(subsec)
print '{}'.format('~' * len(subsec))
print ''

View File

@@ -0,0 +1,43 @@
# nghttp2 - HTTP/2 C Library
# Copyright (c) 2015 Tatsuhiro Tsujikawa
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
EXTRA_DIST = \
nghttpx_http1_test.go \
nghttpx_http2_test.go \
nghttpx_spdy_test.go \
server_tester.go \
server.key \
server.crt \
alt-server.key \
alt-server.crt \
setenv
.PHONY: itprep it
itprep:
go get -d -v github.com/bradfitz/http2
go get -d -v github.com/tatsuhiro-t/go-nghttp2
go get -d -v golang.org/x/net/spdy
it:
sh setenv go test -v

View File

@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDhzCCAm+gAwIBAgIJANfuEldiquMNMA0GCSqGSIb3DQEBCwUAMFoxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQxEzARBgNVBAMMCmFsdC1kb21haW4wHhcNMTUwMTI1MDYy
NTQxWhcNMjUwMTIyMDYyNTQxWjBaMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29t
ZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRMwEQYD
VQQDDAphbHQtZG9tYWluMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
0IwhDOGDipGrJQ9IoRSzPdkU/Ii4aJgGKHlXminym42X0VI3IW61RLvOHRlHVmVH
JQjFuDo2x+y81t9NlDg3HGUbSpzOzpm6StiutB7c4hreT5G4r0YKya1ugiemN0+p
qjIPJWm2jVnf448eZvUKRKEQ9W0MLZjiNjVGKrKlwo7fIlXg4N3+YixLYffAT1NV
d1T6V5jzlbruj15gK2nGjMQ9D1h1t9vTbTxY+mtk72aX0Y64IE6pPBWLFSSH8ozU
idDoL3AZwz2Jker+ALKK8CM4uho/RPpyW1C06HH+HLdH2MqEjDOROde/Nzxm668O
gK/JWGIEyUqYiUXx0yhFxwIDAQABo1AwTjAdBgNVHQ4EFgQU/Y0GDN2uPjbyePcu
95ZvYEK/gHIwHwYDVR0jBBgwFoAU/Y0GDN2uPjbyePcu95ZvYEK/gHIwDAYDVR0T
BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAodD6LVCzL3wfsZ6TxTzf9TfgIdbj
ilL3SEMT/xnfTXT3SLYScTRqQIAI29Y7dOLMq89p4hY2wmeUEhBUAz+y9G2JVr8o
6EbxXrQpWgNJogELqoNnMdrDxB5RsmDDKEJ/rLjDfSkjWbK7B2PZsqVTDgjekCFw
u6FqTIjn/O1O/L5tjwxwxjHmQod/maFCvXoDOVBuwdHnkp298tqlvsHfHO8m++Wj
+XYB8plMIjpeTh9v4w9Jc4QZ59lK/3Tt4qaENeQrMEubKSY/Zen7L2bzhk+cChWT
GSGz9uNXieoZaH79D0wnyZaSZ5Ds4ActMevnGg3iYXuzuFqx8Pungn74Vg==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDQjCEM4YOKkasl
D0ihFLM92RT8iLhomAYoeVeaKfKbjZfRUjchbrVEu84dGUdWZUclCMW4OjbH7LzW
302UODccZRtKnM7OmbpK2K60HtziGt5PkbivRgrJrW6CJ6Y3T6mqMg8labaNWd/j
jx5m9QpEoRD1bQwtmOI2NUYqsqXCjt8iVeDg3f5iLEth98BPU1V3VPpXmPOVuu6P
XmAracaMxD0PWHW329NtPFj6a2TvZpfRjrggTqk8FYsVJIfyjNSJ0OgvcBnDPYmR
6v4AsorwIzi6Gj9E+nJbULTocf4ct0fYyoSMM5E51783PGbrrw6Ar8lYYgTJSpiJ
RfHTKEXHAgMBAAECggEBALTrjFSXY72YB+h7rN+JjMIwDIPUvF6I3HbKZhQpJf6K
xNVkRM2tNHavku0tm/S4ohLf3F+pqRKiL2Udjjjy1+S7VgTRqpwTQ0lhV5aNW8SP
2KMg4R61XfB+k+s4KHu9kYxEJ12mqydPe+r3o0FgfYryTDsOYk1AX6b1aqzqFOGF
7GaqLALSbKU59tcJJ1SZNBbpIKFUrAT9nZt9dW02/foqP5bzUk43Yjw48xmLwegc
bMXXcpZhNZSktltvwRw7Q4Foc9kuRlMdTAnAD9PnMCcZwicS/YeVVF6Rz4fGviKv
7/kPHQ7g4YpFktVDzuZ5xw6GDVFeJ6uGMVUX8+EePvkCgYEA+/nrcn82nFHCxm8Q
0iiUhi/AoXjZg+O5Ytaje9O/YNoX+c4ywe13h0+TXKH79O0KfTwXeJyDgPZbAIFV
9oURellRYUzKDafnBHis2f+Ywn6GqHL5e2X30ZxIp1GK46pcvne1YuvJhgGmiVay
vd7sRx09OKU124dG22rIFCis6asCgYEA0+CsA6LrEwQ/aPJYASY3VHNO/WoAOnPg
Cwsg+02XWsPEwP//lNmpanz8TUm2URS063ZK8bx7t3ejvDgBdsRwwjiMlDp7XTUU
3Zk+mhCV2qkMi02aKemvz29bDhmh5JoH7W3IwsXtJYO0yZDYrDR3ioiKRccioPoE
b/Nq781sEFUCgYEA4xqx9xRpaCLY5nicNI6WrwrDF8YQZisNn+PMnYKP7v8itOgA
H4GkRbSXINpueKZc2dsbXH3UmJtyEdaAYBw3UIrIKmZHhl9afFE3mZQhXssjGxfl
fC6/WZD+eq+n+uJFjPXf6jSSAdHjA828dB1D4CSeVTuyexZF6uUnR+QRVNkCgYEA
i+pb7XLSpZYygY03zFp+Q0h6KyKqz+7hTqmkuA8/GfMZpRHop1UtaWLsAeXhfZ2c
87kEOKptUHSzLYIWhWWnyLorK1+LQ7vf8Y5XJso5C1KDNCKk4XSuYt94U9FddWa6
QXI0F1s5BYL6Cfma++0R2+va08Vy+rbf40XtojoXWJkCgYEA0hMQSCvok7is27nQ
G80KXfmghU2eEB7zif3T00/fwJycxEbmnNeof+SKmhdY4ZgqTscfOxlQPflV/eqB
xs4GnFDDeM0F8KH0BimOXxr7sJPFCg22PCCQQcRtM/KoU+ip/kNmTfwrsC0xMFPU
HD8M1JCZF2eLMekXXP3cB0U4sUs=
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,5 @@
package nghttp2
const (
buildDir = "@top_builddir@"
)

View File

@@ -0,0 +1,440 @@
package nghttp2
import (
"bufio"
"fmt"
"github.com/bradfitz/http2/hpack"
"io"
"net/http"
"syscall"
"testing"
)
// TestH1H1PlainGET tests whether simple HTTP/1 GET request works.
func TestH1H1PlainGET(t *testing.T) {
st := newServerTester(nil, t, noopHandler)
defer st.Close()
res, err := st.http1(requestParam{
name: "TestH1H1PlainGET",
})
if err != nil {
t.Fatalf("Error st.http1() = %v", err)
}
want := 200
if got := res.status; got != want {
t.Errorf("status = %v; want %v", got, want)
}
}
// TestH1H1PlainGETClose tests whether simple HTTP/1 GET request with
// Connetion: close request header field works.
func TestH1H1PlainGETClose(t *testing.T) {
st := newServerTester(nil, t, noopHandler)
defer st.Close()
res, err := st.http1(requestParam{
name: "TestH1H1PlainGETClose",
header: []hpack.HeaderField{
pair("Connection", "close"),
},
})
if err != nil {
t.Fatalf("Error st.http1() = %v", err)
}
want := 200
if got := res.status; got != want {
t.Errorf("status = %v; want %v", got, want)
}
}
// TestH1H1MultipleRequestCL tests that server rejects request which
// contains multiple Content-Length header fields.
func TestH1H1MultipleRequestCL(t *testing.T) {
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
t.Errorf("server should not forward bad request")
})
defer st.Close()
if _, err := io.WriteString(st.conn, fmt.Sprintf(`GET / HTTP/1.1
Host: %v
Test-Case: TestH1H1MultipleRequestCL
Content-Length: 0
Content-Length: 0
`, st.authority)); err != nil {
t.Fatalf("Error io.WriteString() = %v", err)
}
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
if err != nil {
t.Fatalf("Error http.ReadResponse() = %v", err)
}
want := 400
if got := resp.StatusCode; got != want {
t.Errorf("status: %v; want %v", got, want)
}
}
// TestH1H1ConnectFailure tests that server handles the situation that
// connection attempt to HTTP/1 backend failed.
func TestH1H1ConnectFailure(t *testing.T) {
st := newServerTester(nil, t, noopHandler)
defer st.Close()
// shutdown backend server to simulate backend connect failure
st.ts.Close()
res, err := st.http1(requestParam{
name: "TestH1H1ConnectFailure",
})
if err != nil {
t.Fatalf("Error st.http1() = %v", err)
}
want := 503
if got := res.status; got != want {
t.Errorf("status: %v; want %v", got, want)
}
}
// TestH1H1GracefulShutdown tests graceful shutdown.
func TestH1H1GracefulShutdown(t *testing.T) {
st := newServerTester(nil, t, noopHandler)
defer st.Close()
res, err := st.http1(requestParam{
name: "TestH1H1GracefulShutdown-1",
})
if err != nil {
t.Fatalf("Error st.http1() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("status: %v; want %v", got, want)
}
st.cmd.Process.Signal(syscall.SIGQUIT)
res, err = st.http1(requestParam{
name: "TestH1H1GracefulShutdown-2",
})
if err != nil {
t.Fatalf("Error st.http1() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("status: %v; want %v", got, want)
}
if got, want := res.connClose, true; got != want {
t.Errorf("res.connClose: %v; want %v", got, want)
}
want := io.EOF
if _, err := st.conn.Read(nil); err == nil || err != want {
t.Errorf("st.conn.Read(): %v; want %v", err, want)
}
}
// TestH1H1HostRewrite tests that server rewrites Host header field
func TestH1H1HostRewrite(t *testing.T) {
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("request-host", r.Host)
})
defer st.Close()
res, err := st.http1(requestParam{
name: "TestH1H1HostRewrite",
})
if err != nil {
t.Fatalf("Error st.http1() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("status: %v; want %v", got, want)
}
if got, want := res.header.Get("request-host"), st.backendHost; got != want {
t.Errorf("request-host: %v; want %v", got, want)
}
}
// TestH1H1HTTP10 tests that server can accept HTTP/1.0 request
// without Host header field
func TestH1H1HTTP10(t *testing.T) {
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("request-host", r.Host)
})
defer st.Close()
if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H1HTTP10\r\n\r\n"); err != nil {
t.Fatalf("Error io.WriteString() = %v", err)
}
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
if err != nil {
t.Fatalf("Error http.ReadResponse() = %v", err)
}
if got, want := resp.StatusCode, 200; got != want {
t.Errorf("status: %v; want %v", got, want)
}
if got, want := resp.Header.Get("request-host"), st.backendHost; got != want {
t.Errorf("request-host: %v; want %v", got, want)
}
}
// TestH1H1HTTP10NoHostRewrite tests that server generates host header
// field using actual backend server even if --no-http-rewrite is
// used.
func TestH1H1HTTP10NoHostRewrite(t *testing.T) {
st := newServerTester([]string{"--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("request-host", r.Host)
})
defer st.Close()
if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H1HTTP10NoHostRewrite\r\n\r\n"); err != nil {
t.Fatalf("Error io.WriteString() = %v", err)
}
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
if err != nil {
t.Fatalf("Error http.ReadResponse() = %v", err)
}
if got, want := resp.StatusCode, 200; got != want {
t.Errorf("status: %v; want %v", got, want)
}
if got, want := resp.Header.Get("request-host"), st.backendHost; got != want {
t.Errorf("request-host: %v; want %v", got, want)
}
}
// TestH1H1RequestTrailer tests request trailer part is forwarded to
// backend.
func TestH1H1RequestTrailer(t *testing.T) {
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
buf := make([]byte, 4096)
for {
_, err := r.Body.Read(buf)
if err == io.EOF {
break
}
if err != nil {
t.Fatalf("r.Body.Read() = %v", err)
}
}
if got, want := r.Trailer.Get("foo"), "bar"; got != want {
t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want)
}
})
defer st.Close()
res, err := st.http1(requestParam{
name: "TestH1H1RequestTrailer",
body: []byte("1"),
trailer: []hpack.HeaderField{
pair("foo", "bar"),
},
})
if err != nil {
t.Fatalf("Error st.http1() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("res.status: %v; want %v", got, want)
}
}
// TestH1H2ConnectFailure tests that server handles the situation that
// connection attempt to HTTP/2 backend failed.
func TestH1H2ConnectFailure(t *testing.T) {
st := newServerTester([]string{"--http2-bridge"}, t, noopHandler)
defer st.Close()
// simulate backend connect attempt failure
st.ts.Close()
res, err := st.http1(requestParam{
name: "TestH1H2ConnectFailure",
})
if err != nil {
t.Fatalf("Error st.http1() = %v", err)
}
want := 503
if got := res.status; got != want {
t.Errorf("status: %v; want %v", got, want)
}
}
// TestH1H2NoHost tests that server rejects request without Host
// header field for HTTP/2 backend.
func TestH1H2NoHost(t *testing.T) {
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
t.Errorf("server should not forward bad request")
})
defer st.Close()
// without Host header field, we expect 400 response
if _, err := io.WriteString(st.conn, "GET / HTTP/1.1\r\nTest-Case: TestH1H2NoHost\r\n\r\n"); err != nil {
t.Fatalf("Error io.WriteString() = %v", err)
}
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
if err != nil {
t.Fatalf("Error http.ReadResponse() = %v", err)
}
want := 400
if got := resp.StatusCode; got != want {
t.Errorf("status: %v; want %v", got, want)
}
}
// TestH1H2HTTP10 tests that server can accept HTTP/1.0 request
// without Host header field
func TestH1H2HTTP10(t *testing.T) {
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("request-host", r.Host)
})
defer st.Close()
if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H2HTTP10\r\n\r\n"); err != nil {
t.Fatalf("Error io.WriteString() = %v", err)
}
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
if err != nil {
t.Fatalf("Error http.ReadResponse() = %v", err)
}
if got, want := resp.StatusCode, 200; got != want {
t.Errorf("status: %v; want %v", got, want)
}
if got, want := resp.Header.Get("request-host"), st.backendHost; got != want {
t.Errorf("request-host: %v; want %v", got, want)
}
}
// TestH1H2HTTP10NoHostRewrite tests that server generates host header
// field using actual backend server even if --no-http-rewrite is
// used.
func TestH1H2HTTP10NoHostRewrite(t *testing.T) {
st := newServerTester([]string{"--http2-bridge", "--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("request-host", r.Host)
})
defer st.Close()
if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H2HTTP10NoHostRewrite\r\n\r\n"); err != nil {
t.Fatalf("Error io.WriteString() = %v", err)
}
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
if err != nil {
t.Fatalf("Error http.ReadResponse() = %v", err)
}
if got, want := resp.StatusCode, 200; got != want {
t.Errorf("status: %v; want %v", got, want)
}
if got, want := resp.Header.Get("request-host"), st.backendHost; got != want {
t.Errorf("request-host: %v; want %v", got, want)
}
}
// TestH1H2CrumbleCookie tests that Cookies are crumbled and assembled
// when forwarding to HTTP/2 backend link. go-nghttp2 server
// concatenates crumbled Cookies automatically, so this test is not
// much effective now.
func TestH1H2CrumbleCookie(t *testing.T) {
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
if got, want := r.Header.Get("Cookie"), "alpha; bravo; charlie"; got != want {
t.Errorf("Cookie: %v; want %v", got, want)
}
})
defer st.Close()
res, err := st.http1(requestParam{
name: "TestH1H2CrumbleCookie",
header: []hpack.HeaderField{
pair("Cookie", "alpha; bravo; charlie"),
},
})
if err != nil {
t.Fatalf("Error st.http1() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("status: %v; want %v", got, want)
}
}
// TestH1H2GenerateVia tests that server generates Via header field to and
// from backend server.
func TestH1H2GenerateVia(t *testing.T) {
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
if got, want := r.Header.Get("Via"), "1.1 nghttpx"; got != want {
t.Errorf("Via: %v; want %v", got, want)
}
})
defer st.Close()
res, err := st.http1(requestParam{
name: "TestH1H2GenerateVia",
})
if err != nil {
t.Fatalf("Error st.http1() = %v", err)
}
if got, want := res.header.Get("Via"), "2 nghttpx"; got != want {
t.Errorf("Via: %v; want %v", got, want)
}
}
// TestH1H2AppendVia tests that server adds value to existing Via
// header field to and from backend server.
func TestH1H2AppendVia(t *testing.T) {
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
if got, want := r.Header.Get("Via"), "foo, 1.1 nghttpx"; got != want {
t.Errorf("Via: %v; want %v", got, want)
}
w.Header().Add("Via", "bar")
})
defer st.Close()
res, err := st.http1(requestParam{
name: "TestH1H2AppendVia",
header: []hpack.HeaderField{
pair("via", "foo"),
},
})
if err != nil {
t.Fatalf("Error st.http1() = %v", err)
}
if got, want := res.header.Get("Via"), "bar, 2 nghttpx"; got != want {
t.Errorf("Via: %v; want %v", got, want)
}
}
// TestH1H2NoVia tests that server does not add value to existing Via
// header field to and from backend server.
func TestH1H2NoVia(t *testing.T) {
st := newServerTester([]string{"--http2-bridge", "--no-via"}, t, func(w http.ResponseWriter, r *http.Request) {
if got, want := r.Header.Get("Via"), "foo"; got != want {
t.Errorf("Via: %v; want %v", got, want)
}
w.Header().Add("Via", "bar")
})
defer st.Close()
res, err := st.http1(requestParam{
name: "TestH1H2NoVia",
header: []hpack.HeaderField{
pair("via", "foo"),
},
})
if err != nil {
t.Fatalf("Error st.http1() = %v", err)
}
if got, want := res.header.Get("Via"), "bar"; got != want {
t.Errorf("Via: %v; want %v", got, want)
}
}

View File

@@ -0,0 +1,737 @@
package nghttp2
import (
"crypto/tls"
"fmt"
"github.com/bradfitz/http2"
"github.com/bradfitz/http2/hpack"
"io"
"io/ioutil"
"net/http"
"syscall"
"testing"
)
// TestH2H1PlainGET tests whether simple HTTP/2 GET request works.
func TestH2H1PlainGET(t *testing.T) {
st := newServerTester(nil, t, noopHandler)
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1PlainGET",
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
want := 200
if res.status != want {
t.Errorf("status = %v; want %v", res.status, want)
}
}
// TestH2H1AddXff tests that server generates X-Forwarded-For header
// field when forwarding request to backend.
func TestH2H1AddXff(t *testing.T) {
st := newServerTester([]string{"--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
xff := r.Header.Get("X-Forwarded-For")
want := "127.0.0.1"
if xff != want {
t.Errorf("X-Forwarded-For = %v; want %v", xff, want)
}
})
defer st.Close()
_, err := st.http2(requestParam{
name: "TestH2H1AddXff",
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
}
// TestH2H1AddXff2 tests that server appends X-Forwarded-For header
// field to existing one when forwarding request to backend.
func TestH2H1AddXff2(t *testing.T) {
st := newServerTester([]string{"--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
xff := r.Header.Get("X-Forwarded-For")
want := "host, 127.0.0.1"
if xff != want {
t.Errorf("X-Forwarded-For = %v; want %v", xff, want)
}
})
defer st.Close()
_, err := st.http2(requestParam{
name: "TestH2H1AddXff2",
header: []hpack.HeaderField{
pair("x-forwarded-for", "host"),
},
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
}
// TestH2H1StripXff tests that --strip-incoming-x-forwarded-for
// option.
func TestH2H1StripXff(t *testing.T) {
st := newServerTester([]string{"--strip-incoming-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
if xff, found := r.Header["X-Forwarded-For"]; found {
t.Errorf("X-Forwarded-For = %v; want nothing", xff)
}
})
defer st.Close()
_, err := st.http2(requestParam{
name: "TestH2H1StripXff1",
header: []hpack.HeaderField{
pair("x-forwarded-for", "host"),
},
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
}
// TestH2H1StripAddXff tests that --strip-incoming-x-forwarded-for and
// --add-x-forwarded-for options.
func TestH2H1StripAddXff(t *testing.T) {
args := []string{
"--strip-incoming-x-forwarded-for",
"--add-x-forwarded-for",
}
st := newServerTester(args, t, func(w http.ResponseWriter, r *http.Request) {
xff := r.Header.Get("X-Forwarded-For")
want := "127.0.0.1"
if xff != want {
t.Errorf("X-Forwarded-For = %v; want %v", xff, want)
}
})
defer st.Close()
_, err := st.http2(requestParam{
name: "TestH2H1StripAddXff",
header: []hpack.HeaderField{
pair("x-forwarded-for", "host"),
},
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
}
// TestH2H1GenerateVia tests that server generates Via header field to and
// from backend server.
func TestH2H1GenerateVia(t *testing.T) {
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
if got, want := r.Header.Get("Via"), "2 nghttpx"; got != want {
t.Errorf("Via: %v; want %v", got, want)
}
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1GenerateVia",
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.header.Get("Via"), "1.1 nghttpx"; got != want {
t.Errorf("Via: %v; want %v", got, want)
}
}
// TestH2H1AppendVia tests that server adds value to existing Via
// header field to and from backend server.
func TestH2H1AppendVia(t *testing.T) {
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
if got, want := r.Header.Get("Via"), "foo, 2 nghttpx"; got != want {
t.Errorf("Via: %v; want %v", got, want)
}
w.Header().Add("Via", "bar")
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1AppendVia",
header: []hpack.HeaderField{
pair("via", "foo"),
},
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.header.Get("Via"), "bar, 1.1 nghttpx"; got != want {
t.Errorf("Via: %v; want %v", got, want)
}
}
// TestH2H1NoVia tests that server does not add value to existing Via
// header field to and from backend server.
func TestH2H1NoVia(t *testing.T) {
st := newServerTester([]string{"--no-via"}, t, func(w http.ResponseWriter, r *http.Request) {
if got, want := r.Header.Get("Via"), "foo"; got != want {
t.Errorf("Via: %v; want %v", got, want)
}
w.Header().Add("Via", "bar")
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1NoVia",
header: []hpack.HeaderField{
pair("via", "foo"),
},
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.header.Get("Via"), "bar"; got != want {
t.Errorf("Via: %v; want %v", got, want)
}
}
// TestH2H1HostRewrite tests that server rewrites host header field
func TestH2H1HostRewrite(t *testing.T) {
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("request-host", r.Host)
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1HostRewrite",
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("status: %v; want %v", got, want)
}
if got, want := res.header.Get("request-host"), st.backendHost; got != want {
t.Errorf("request-host: %v; want %v", got, want)
}
}
// TestH2H1NoHostRewrite tests that server does not rewrite host
// header field
func TestH2H1NoHostRewrite(t *testing.T) {
st := newServerTester([]string{"--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("request-host", r.Host)
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1NoHostRewrite",
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("status: %v; want %v", got, want)
}
if got, want := res.header.Get("request-host"), st.frontendHost; got != want {
t.Errorf("request-host: %v; want %v", got, want)
}
}
// TestH2H1BadRequestCL tests that server rejects request whose
// content-length header field value does not match its request body
// size.
func TestH2H1BadRequestCL(t *testing.T) {
st := newServerTester(nil, t, noopHandler)
defer st.Close()
// we set content-length: 1024, but the actual request body is
// 3 bytes.
res, err := st.http2(requestParam{
name: "TestH2H1BadRequestCL",
method: "POST",
header: []hpack.HeaderField{
pair("content-length", "1024"),
},
body: []byte("foo"),
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
want := http2.ErrCodeProtocol
if res.errCode != want {
t.Errorf("res.errCode = %v; want %v", res.errCode, want)
}
}
// TestH2H1BadResponseCL tests that server returns error when
// content-length response header field value does not match its
// response body size.
func TestH2H1BadResponseCL(t *testing.T) {
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
// we set content-length: 1024, but only send 3 bytes.
w.Header().Add("Content-Length", "1024")
w.Write([]byte("foo"))
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1BadResponseCL",
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
want := http2.ErrCodeProtocol
if res.errCode != want {
t.Errorf("res.errCode = %v; want %v", res.errCode, want)
}
}
// TestH2H1LocationRewrite tests location header field rewriting
// works.
func TestH2H1LocationRewrite(t *testing.T) {
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
// TODO we cannot get st.ts's port number here.. 8443
// is just a place holder. We ignore it on rewrite.
w.Header().Add("Location", "http://127.0.0.1:8443/p/q?a=b#fragment")
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1LocationRewrite",
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
want := fmt.Sprintf("http://127.0.0.1:%v/p/q?a=b#fragment", serverPort)
if got := res.header.Get("Location"); got != want {
t.Errorf("Location: %v; want %v", got, want)
}
}
// TestH2H1ChunkedRequestBody tests that chunked request body works.
func TestH2H1ChunkedRequestBody(t *testing.T) {
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
want := "[chunked]"
if got := fmt.Sprint(r.TransferEncoding); got != want {
t.Errorf("Transfer-Encoding: %v; want %v", got, want)
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
t.Fatalf("Error reading r.body: %v", err)
}
want = "foo"
if got := string(body); got != want {
t.Errorf("body: %v; want %v", got, want)
}
})
defer st.Close()
_, err := st.http2(requestParam{
name: "TestH2H1ChunkedRequestBody",
method: "POST",
body: []byte("foo"),
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
}
// TestH2H1MultipleRequestCL tests that server rejects request with
// multiple Content-Length request header fields.
func TestH2H1MultipleRequestCL(t *testing.T) {
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
t.Errorf("server should not forward bad request")
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1MultipleRequestCL",
header: []hpack.HeaderField{
pair("content-length", "1"),
pair("content-length", "1"),
},
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.errCode, http2.ErrCodeProtocol; got != want {
t.Errorf("res.errCode: %v; want %v", got, want)
}
}
// TestH2H1InvalidRequestCL tests that server rejects request with
// Content-Length which cannot be parsed as a number.
func TestH2H1InvalidRequestCL(t *testing.T) {
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
t.Errorf("server should not forward bad request")
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1InvalidRequestCL",
header: []hpack.HeaderField{
pair("content-length", ""),
},
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.errCode, http2.ErrCodeProtocol; got != want {
t.Errorf("res.errCode: %v; want %v", got, want)
}
}
// TestH2H1ConnectFailure tests that server handles the situation that
// connection attempt to HTTP/1 backend failed.
func TestH2H1ConnectFailure(t *testing.T) {
st := newServerTester(nil, t, noopHandler)
defer st.Close()
// shutdown backend server to simulate backend connect failure
st.ts.Close()
res, err := st.http2(requestParam{
name: "TestH2H1ConnectFailure",
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
want := 503
if got := res.status; got != want {
t.Errorf("status: %v; want %v", got, want)
}
}
// TestH2H1AssembleCookies tests that crumbled cookies in HTTP/2
// request is assembled into 1 when forwarding to HTTP/1 backend link.
func TestH2H1AssembleCookies(t *testing.T) {
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
if got, want := r.Header.Get("Cookie"), "alpha; bravo; charlie"; got != want {
t.Errorf("Cookie: %v; want %v", got, want)
}
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1AssembleCookies",
header: []hpack.HeaderField{
pair("cookie", "alpha"),
pair("cookie", "bravo"),
pair("cookie", "charlie"),
},
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("status: %v; want %v", got, want)
}
}
// TestH2H1TETrailers tests that server accepts TE request header
// field if it has trailers only.
func TestH2H1TETrailers(t *testing.T) {
st := newServerTester(nil, t, noopHandler)
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1TETrailers",
header: []hpack.HeaderField{
pair("te", "trailers"),
},
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("status: %v; want %v", got, want)
}
}
// TestH2H1TEGzip tests that server resets stream if TE request header
// field contains gzip.
func TestH2H1TEGzip(t *testing.T) {
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
t.Error("server should not forward bad request")
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1TEGzip",
header: []hpack.HeaderField{
pair("te", "gzip"),
},
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.errCode, http2.ErrCodeProtocol; got != want {
t.Errorf("res.errCode = %v; want %v", res.errCode, want)
}
}
// TestH2H1SNI tests server's TLS SNI extension feature. It must
// choose appropriate certificate depending on the indicated
// server_name from client.
func TestH2H1SNI(t *testing.T) {
st := newServerTesterTLSConfig([]string{"--subcert=" + testDir + "/alt-server.key:" + testDir + "/alt-server.crt"}, t, noopHandler, &tls.Config{
ServerName: "alt-domain",
})
defer st.Close()
tlsConn := st.conn.(*tls.Conn)
connState := tlsConn.ConnectionState()
cert := connState.PeerCertificates[0]
if got, want := cert.Subject.CommonName, "alt-domain"; got != want {
t.Errorf("CommonName: %v; want %v", got, want)
}
}
// TestH2H1ServerPush tests server push using Link header field from
// backend server.
func TestH2H1ServerPush(t *testing.T) {
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
// only resources marked as rel=preload are pushed
w.Header().Add("Link", "</css/main.css>; rel=preload, </foo>, </css/theme.css>; rel=preload")
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1ServerPush",
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("res.status: %v; want %v", got, want)
}
if got, want := len(res.pushResponse), 2; got != want {
t.Fatalf("len(res.pushResponse): %v; want %v", got, want)
}
mainCSS := res.pushResponse[0]
if got, want := mainCSS.status, 200; got != want {
t.Errorf("mainCSS.status: %v; want %v", got, want)
}
themeCSS := res.pushResponse[1]
if got, want := themeCSS.status, 200; got != want {
t.Errorf("themeCSS.status: %v; want %v", got, want)
}
}
// TestH2H1RequestTrailer tests request trailer part is forwarded to
// backend.
func TestH2H1RequestTrailer(t *testing.T) {
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
buf := make([]byte, 4096)
for {
_, err := r.Body.Read(buf)
if err == io.EOF {
break
}
if err != nil {
t.Fatalf("r.Body.Read() = %v", err)
}
}
if got, want := r.Trailer.Get("foo"), "bar"; got != want {
t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want)
}
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H1RequestTrailer",
body: []byte("1"),
trailer: []hpack.HeaderField{
pair("foo", "bar"),
},
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("res.status: %v; want %v", got, want)
}
}
// TestH2H1GracefulShutdown tests graceful shutdown.
func TestH2H1GracefulShutdown(t *testing.T) {
st := newServerTester(nil, t, noopHandler)
defer st.Close()
fmt.Fprint(st.conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
if err := st.fr.WriteSettings(); err != nil {
t.Fatalf("st.fr.WriteSettings(): %v", err)
}
header := []hpack.HeaderField{
pair(":method", "GET"),
pair(":scheme", "http"),
pair(":authority", st.authority),
pair(":path", "/"),
}
for _, h := range header {
_ = st.enc.WriteField(h)
}
if err := st.fr.WriteHeaders(http2.HeadersFrameParam{
StreamID: 1,
EndStream: false,
EndHeaders: true,
BlockFragment: st.headerBlkBuf.Bytes(),
}); err != nil {
t.Fatalf("st.fr.WriteHeaders(): %v", err)
}
// send SIGQUIT signal to nghttpx to perform graceful shutdown
st.cmd.Process.Signal(syscall.SIGQUIT)
// after signal, finish request body
if err := st.fr.WriteData(1, true, nil); err != nil {
t.Fatalf("st.fr.WriteData(): %v", err)
}
numGoAway := 0
for {
fr, err := st.readFrame()
if err != nil {
if err == io.EOF {
want := 2
if got := numGoAway; got != want {
t.Fatalf("numGoAway: %v; want %v", got, want)
}
return
}
t.Fatalf("st.readFrame(): %v", err)
}
switch f := fr.(type) {
case *http2.GoAwayFrame:
numGoAway += 1
want := http2.ErrCodeNo
if got := f.ErrCode; got != want {
t.Fatalf("f.ErrCode(%v): %v; want %v", numGoAway, got, want)
}
switch numGoAway {
case 1:
want := (uint32(1) << 31) - 1
if got := f.LastStreamID; got != want {
t.Fatalf("f.LastStreamID(%v): %v; want %v", numGoAway, got, want)
}
case 2:
want := uint32(1)
if got := f.LastStreamID; got != want {
t.Fatalf("f.LastStreamID(%v): %v; want %v", numGoAway, got, want)
}
case 3:
t.Fatalf("too many GOAWAYs received")
}
}
}
}
// TestH2H2MultipleResponseCL tests that server returns error if
// multiple Content-Length response header fields are received.
func TestH2H2MultipleResponseCL(t *testing.T) {
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("content-length", "1")
w.Header().Add("content-length", "1")
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H2MultipleResponseCL",
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.errCode, http2.ErrCodeInternal; got != want {
t.Errorf("res.errCode: %v; want %v", got, want)
}
}
// TestH2H2InvalidResponseCL tests that server returns error if
// Content-Length response header field value cannot be parsed as a
// number.
func TestH2H2InvalidResponseCL(t *testing.T) {
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("content-length", "")
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H2InvalidResponseCL",
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.errCode, http2.ErrCodeInternal; got != want {
t.Errorf("res.errCode: %v; want %v", got, want)
}
}
// TestH2H2ConnectFailure tests that server handles the situation that
// connection attempt to HTTP/2 backend failed.
func TestH2H2ConnectFailure(t *testing.T) {
st := newServerTester([]string{"--http2-bridge"}, t, noopHandler)
defer st.Close()
// simulate backend connect attempt failure
st.ts.Close()
res, err := st.http2(requestParam{
name: "TestH2H2ConnectFailure",
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
want := 503
if got := res.status; got != want {
t.Errorf("status: %v; want %v", got, want)
}
}
// TestH2H2HostRewrite tests that server rewrites host header field
func TestH2H2HostRewrite(t *testing.T) {
st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("request-host", r.Host)
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H2HostRewrite",
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("status: %v; want %v", got, want)
}
if got, want := res.header.Get("request-host"), st.backendHost; got != want {
t.Errorf("request-host: %v; want %v", got, want)
}
}
// TestH2H2NoHostRewrite tests that server does not rewrite host
// header field
func TestH2H2NoHostRewrite(t *testing.T) {
st := newServerTester([]string{"--http2-bridge", "--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("request-host", r.Host)
})
defer st.Close()
res, err := st.http2(requestParam{
name: "TestH2H2NoHostRewrite",
})
if err != nil {
t.Fatalf("Error st.http2() = %v", err)
}
if got, want := res.status, 200; got != want {
t.Errorf("status: %v; want %v", got, want)
}
if got, want := res.header.Get("request-host"), st.frontendHost; got != want {
t.Errorf("request-host: %v; want %v", got, want)
}
}

View File

@@ -0,0 +1,192 @@
package nghttp2
import (
"github.com/bradfitz/http2/hpack"
"golang.org/x/net/spdy"
"net/http"
"testing"
)
// TestS3H1PlainGET tests whether simple SPDY GET request works.
func TestS3H1PlainGET(t *testing.T) {
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, noopHandler)
defer st.Close()
res, err := st.spdy(requestParam{
name: "TestS3H1PlainGET",
})
if err != nil {
t.Fatalf("Error st.spdy() = %v", err)
}
want := 200
if got := res.status; got != want {
t.Errorf("status = %v; want %v", got, want)
}
}
// TestS3H1BadRequestCL tests that server rejects request whose
// content-length header field value does not match its request body
// size.
func TestS3H1BadRequestCL(t *testing.T) {
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, noopHandler)
defer st.Close()
// we set content-length: 1024, but the actual request body is
// 3 bytes.
res, err := st.spdy(requestParam{
name: "TestS3H1BadRequestCL",
method: "POST",
header: []hpack.HeaderField{
pair("content-length", "1024"),
},
body: []byte("foo"),
})
if err != nil {
t.Fatalf("Error st.spdy() = %v", err)
}
want := spdy.ProtocolError
if got := res.spdyRstErrCode; got != want {
t.Errorf("res.spdyRstErrCode = %v; want %v", got, want)
}
}
// TestS3H1MultipleRequestCL tests that server rejects request with
// multiple Content-Length request header fields.
func TestS3H1MultipleRequestCL(t *testing.T) {
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
t.Errorf("server should not forward bad request")
})
defer st.Close()
res, err := st.spdy(requestParam{
name: "TestS3H1MultipleRequestCL",
header: []hpack.HeaderField{
pair("content-length", "1"),
pair("content-length", "1"),
},
})
if err != nil {
t.Fatalf("Error st.spdy() = %v", err)
}
want := 400
if got := res.status; got != want {
t.Errorf("status: %v; want %v", got, want)
}
}
// TestS3H1InvalidRequestCL tests that server rejects request with
// Content-Length which cannot be parsed as a number.
func TestS3H1InvalidRequestCL(t *testing.T) {
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
t.Errorf("server should not forward bad request")
})
defer st.Close()
res, err := st.spdy(requestParam{
name: "TestS3H1InvalidRequestCL",
header: []hpack.HeaderField{
pair("content-length", ""),
},
})
if err != nil {
t.Fatalf("Error st.spdy() = %v", err)
}
want := 400
if got := res.status; got != want {
t.Errorf("status: %v; want %v", got, want)
}
}
// TestS3H1GenerateVia tests that server generates Via header field to and
// from backend server.
func TestS3H1GenerateVia(t *testing.T) {
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
if got, want := r.Header.Get("Via"), "1.1 nghttpx"; got != want {
t.Errorf("Via: %v; want %v", got, want)
}
})
defer st.Close()
res, err := st.spdy(requestParam{
name: "TestS3H1GenerateVia",
})
if err != nil {
t.Fatalf("Error st.spdy() = %v", err)
}
if got, want := res.header.Get("Via"), "1.1 nghttpx"; got != want {
t.Errorf("Via: %v; want %v", got, want)
}
}
// TestS3H1AppendVia tests that server adds value to existing Via
// header field to and from backend server.
func TestS3H1AppendVia(t *testing.T) {
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
if got, want := r.Header.Get("Via"), "foo, 1.1 nghttpx"; got != want {
t.Errorf("Via: %v; want %v", got, want)
}
w.Header().Add("Via", "bar")
})
defer st.Close()
res, err := st.spdy(requestParam{
name: "TestS3H1AppendVia",
header: []hpack.HeaderField{
pair("via", "foo"),
},
})
if err != nil {
t.Fatalf("Error st.spdy() = %v", err)
}
if got, want := res.header.Get("Via"), "bar, 1.1 nghttpx"; got != want {
t.Errorf("Via: %v; want %v", got, want)
}
}
// TestS3H1NoVia tests that server does not add value to existing Via
// header field to and from backend server.
func TestS3H1NoVia(t *testing.T) {
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--no-via"}, t, func(w http.ResponseWriter, r *http.Request) {
if got, want := r.Header.Get("Via"), "foo"; got != want {
t.Errorf("Via: %v; want %v", got, want)
}
w.Header().Add("Via", "bar")
})
defer st.Close()
res, err := st.spdy(requestParam{
name: "TestS3H1NoVia",
header: []hpack.HeaderField{
pair("via", "foo"),
},
})
if err != nil {
t.Fatalf("Error st.spdy() = %v", err)
}
if got, want := res.header.Get("Via"), "bar"; got != want {
t.Errorf("Via: %v; want %v", got, want)
}
}
// TestS3H2ConnectFailure tests that server handles the situation that
// connection attempt to HTTP/2 backend failed.
func TestS3H2ConnectFailure(t *testing.T) {
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--http2-bridge"}, t, noopHandler)
defer st.Close()
// simulate backend connect attempt failure
st.ts.Close()
res, err := st.spdy(requestParam{
name: "TestS3H2ConnectFailure",
})
if err != nil {
t.Fatalf("Error st.spdy() = %v", err)
}
want := 503
if got := res.status; got != want {
t.Errorf("status: %v; want %v", got, want)
}
}

View File

@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDhTCCAm2gAwIBAgIJAOvIx8xIxgyOMA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCTEyNy4wLjAuMTAeFw0xNTAxMjMxMjI0
MjdaFw0yNTAxMjAxMjI0MjdaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l
LVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV
BAMMCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMuI
QZRI/iBaxPTjTWGemt8tCEfzZWxuIW3hY/gIhwJDfH2SbourBh1s9vqcqhBq5vmo
kdfVQXAnNLjIG1uhWmcHuNnKrE5hU82N6i9RsmuM5TQRvhsamHri4G+EXJMu9GqF
Mso8g7MWpRSGKf+8gfjAVNwfCHFiu8oBcMmy3l54MFHgRLSveAMhiPB0e3Xlnpr5
2bS/oGTx5ynwPgBpEn2FrpT4Z/aLCLzJ/ysgNH8BXEh7n/v7xM3vd5grqB039rd5
JoxlWvp+4XpzKp5upaqmOcVUq4pDSFUQ3w6C+v33Z3OK6Qaon7GMxLv3Us3b7PZ3
1CLoWJR2o3OSnUfO/gUCAwEAAaNQME4wHQYDVR0OBBYEFLc5JWPUUVx4GJesogMV
w2Rz0L3yMB8GA1UdIwQYMBaAFLc5JWPUUVx4GJesogMVw2Rz0L3yMAwGA1UdEwQF
MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAP/cJWpM+GEjmVYHFacKTdbXBMox2Xn
QY2NLm00WPOGvKnO7czMFfX/pEmiq71kD45rLLfbaJP205QpxqiAIvhFhuq50Co7
sTDtwcDTPLX9H7Ugjt4sTMPiwC14uVXFfoT/J46zMjXwP00qKyfszc2tkIgHfrTl
h4M1hkdfmMximir/Ii7TdYYJ3oGS8tdcYb6D4DZwAljKmxF6iUOwFCUgpTmqDBT5
irXY8D27DzuNN5Pg07rwAlwXLCzrJE10UtO4MmRVXwpzmoaRQD4/tna6bZzdetvs
gPdGP6W1o0q85gullieMJWeKyQA/wasoE7fypn4pHAdTZm/vH+v7GHg=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDLiEGUSP4gWsT0
401hnprfLQhH82VsbiFt4WP4CIcCQ3x9km6LqwYdbPb6nKoQaub5qJHX1UFwJzS4
yBtboVpnB7jZyqxOYVPNjeovUbJrjOU0Eb4bGph64uBvhFyTLvRqhTLKPIOzFqUU
hin/vIH4wFTcHwhxYrvKAXDJst5eeDBR4ES0r3gDIYjwdHt15Z6a+dm0v6Bk8ecp
8D4AaRJ9ha6U+Gf2iwi8yf8rIDR/AVxIe5/7+8TN73eYK6gdN/a3eSaMZVr6fuF6
cyqebqWqpjnFVKuKQ0hVEN8Ogvr992dziukGqJ+xjMS791LN2+z2d9Qi6FiUdqNz
kp1Hzv4FAgMBAAECggEACG26GYP0Ui6wHVwUZkiFLVzWDPS9bIIbDEvbMfhYbvWQ
gDrCLTKF7E4I5FP8jvV+XzRl5cRFE3nsKwLObzr9XWrqcsp73DsXl1mbKx58/ws0
qrVZZBHz4pLmrHeUxduZ75dYhRuAcLgtWe48awTJdR2x5fO7C8cE89afbxrjLpJE
tVyiw6vVB0GfWTZodxtAFMTX1KVm4bTngXfg0NF1FBNHAX3Cm6t4YCE41hKSc0IQ
Jr3C4e9uj8poze1B17k79bGB8HNMbbc8Ws0sdbxi5xnY+HUA/mYQrmGXo8sdqiYC
EYCMqPm3iJrCmmpHukGf2Vt9k1aLlJ+lxOclSwFO+QKBgQDoRmoprfdmU20LyxYH
eVeVqggqmhNohwnuhIvOAyrWGUkbDsssqx2Vv82z0WHAAkwEvQ984UzaYWCCL3m3
+JzpF2dz6aKhXIaYnXBlk3STMGUCDT5ysPvsin9z/unzkffh3vrbDBARGFYWG18x
eUyTDOVVeTZNHUJXGjRyiftCkwKBgQDgUkR6dHU4ciSt7Y0UkyAgtZ7POR41T05L
bcxbjJeqm6qlj+oP9WUk7JxeSEFUbrMiROABLPPqTwmGo4xrDRx/e7WrqN6QBKC+
Y8CfalrKRb0np60x7Mxx0kbmHp5cwv9QDKznKViOYSgKxFrOFZyMAEXQdZ3FvjXF
OQWrw86kBwKBgQDXuxa9MWO3uUJtkqkaNfw/+FVvY/0kt09lJdxHci+l/IQmyl2w
Vhm7TRK7sXvtfvSl7gblgMgFiC2/nGKbmR/7ag5e3R98aVhlhMywuvyp/GfEORLI
KVNChfwMezVFUUx+j8BEFHcTuZuzGqcWZ0fUyER0V4k0pDlKdv9BZqBkWwKBgCdP
o3qGQCilMDJex/OMGPxCd9M+4kFbZZAobMC6cbXPU+dxwgYL7i67XGfVZ8WBJNlj
kpICK7irIzM6JBh6krzwlBTCIkbA2N6kopQNUl3SPOTfKKXwJp/nxs77HKuK7K09
m2tjPoatFhRU9sjY1rdeMN3oTr7hp5CpfonsZaEvAoGAEPsZcDd4N9ap5bgaeDy9
NOfLsIyaxT5k6moRIiy83QPihvCuECP16+r6M5tiSfgt/PtCimdjhRiqXzIHNRhh
Nfsv13vUtZgt8cYXuTdI4a8feKI7Q4876ME8Qp3WM5/UNZWq6/sWCuZFqbXUhqM0
mwNEi5Zddzf8VsSL2gCraQg=
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,669 @@
package nghttp2
import (
"bufio"
"bytes"
"crypto/tls"
"errors"
"fmt"
"github.com/bradfitz/http2"
"github.com/bradfitz/http2/hpack"
"github.com/tatsuhiro-t/go-nghttp2"
"golang.org/x/net/spdy"
"io"
"io/ioutil"
"net"
"net/http"
"net/http/httptest"
"net/url"
"os/exec"
"sort"
"strconv"
"strings"
"testing"
"time"
)
const (
serverBin = buildDir + "/src/nghttpx"
serverPort = 3009
testDir = buildDir + "/integration-tests"
)
func pair(name, value string) hpack.HeaderField {
return hpack.HeaderField{
Name: name,
Value: value,
}
}
type serverTester struct {
args []string // command-line arguments
cmd *exec.Cmd // test frontend server process, which is test subject
url string // test frontend server URL
t *testing.T
ts *httptest.Server // backend server
frontendHost string // frontend server host
backendHost string // backend server host
conn net.Conn // connection to frontend server
h2PrefaceSent bool // HTTP/2 preface was sent in conn
nextStreamID uint32 // next stream ID
fr *http2.Framer // HTTP/2 framer
spdyFr *spdy.Framer // SPDY/3.1 framer
headerBlkBuf bytes.Buffer // buffer to store encoded header block
enc *hpack.Encoder // HTTP/2 HPACK encoder
header http.Header // received header fields
dec *hpack.Decoder // HTTP/2 HPACK decoder
authority string // server's host:port
frCh chan http2.Frame // used for incoming HTTP/2 frame
spdyFrCh chan spdy.Frame // used for incoming SPDY frame
errCh chan error
}
// newServerTester creates test context for plain TCP frontend
// connection.
func newServerTester(args []string, t *testing.T, handler http.HandlerFunc) *serverTester {
return newServerTesterInternal(args, t, handler, false, nil)
}
// newServerTester creates test context for TLS frontend connection.
func newServerTesterTLS(args []string, t *testing.T, handler http.HandlerFunc) *serverTester {
return newServerTesterInternal(args, t, handler, true, nil)
}
// newServerTester creates test context for TLS frontend connection
// with given clientConfig
func newServerTesterTLSConfig(args []string, t *testing.T, handler http.HandlerFunc, clientConfig *tls.Config) *serverTester {
return newServerTesterInternal(args, t, handler, true, clientConfig)
}
// newServerTesterInternal creates test context. If frontendTLS is
// true, set up TLS frontend connection.
func newServerTesterInternal(args []string, t *testing.T, handler http.HandlerFunc, frontendTLS bool, clientConfig *tls.Config) *serverTester {
ts := httptest.NewUnstartedServer(handler)
backendTLS := false
for _, k := range args {
switch k {
case "--http2-bridge":
backendTLS = true
}
}
if backendTLS {
nghttp2.ConfigureServer(ts.Config, &nghttp2.Server{})
// According to httptest/server.go, we have to set
// NextProtos separately for ts.TLS. NextProtos set
// in nghttp2.ConfigureServer is effectively ignored.
ts.TLS = new(tls.Config)
ts.TLS.NextProtos = append(ts.TLS.NextProtos, "h2-14")
ts.StartTLS()
args = append(args, "-k")
} else {
ts.Start()
}
scheme := "http"
if frontendTLS {
scheme = "https"
args = append(args, testDir+"/server.key", testDir+"/server.crt")
} else {
args = append(args, "--frontend-no-tls")
}
backendURL, err := url.Parse(ts.URL)
if err != nil {
t.Fatalf("Error parsing URL from httptest.Server: %v", err)
}
// URL.Host looks like "127.0.0.1:8080", but we want
// "127.0.0.1,8080"
b := "-b" + strings.Replace(backendURL.Host, ":", ",", -1)
args = append(args, fmt.Sprintf("-f127.0.0.1,%v", serverPort), b,
"--errorlog-file="+testDir+"/log.txt", "-LINFO")
authority := fmt.Sprintf("127.0.0.1:%v", serverPort)
st := &serverTester{
cmd: exec.Command(serverBin, args...),
t: t,
ts: ts,
url: fmt.Sprintf("%v://%v", scheme, authority),
frontendHost: fmt.Sprintf("127.0.0.1:%v", serverPort),
backendHost: backendURL.Host,
nextStreamID: 1,
authority: authority,
frCh: make(chan http2.Frame),
spdyFrCh: make(chan spdy.Frame),
errCh: make(chan error),
}
if err := st.cmd.Start(); err != nil {
st.t.Fatalf("Error starting %v: %v", serverBin, err)
}
retry := 0
for {
var conn net.Conn
var err error
if frontendTLS {
var tlsConfig *tls.Config
if clientConfig == nil {
tlsConfig = new(tls.Config)
} else {
tlsConfig = clientConfig
}
tlsConfig.InsecureSkipVerify = true
tlsConfig.NextProtos = []string{"h2-14", "spdy/3.1"}
conn, err = tls.Dial("tcp", authority, tlsConfig)
} else {
conn, err = net.Dial("tcp", authority)
}
if err != nil {
retry += 1
if retry >= 100 {
st.Close()
st.t.Fatalf("Error server is not responding too long; server command-line arguments may be invalid")
}
time.Sleep(150 * time.Millisecond)
continue
}
if frontendTLS {
tlsConn := conn.(*tls.Conn)
cs := tlsConn.ConnectionState()
if !cs.NegotiatedProtocolIsMutual {
st.Close()
st.t.Fatalf("Error negotiated next protocol is not mutual")
}
}
st.conn = conn
break
}
st.fr = http2.NewFramer(st.conn, st.conn)
spdyFr, err := spdy.NewFramer(st.conn, st.conn)
if err != nil {
st.Close()
st.t.Fatalf("Error spdy.NewFramer: %v", err)
}
st.spdyFr = spdyFr
st.enc = hpack.NewEncoder(&st.headerBlkBuf)
st.dec = hpack.NewDecoder(4096, func(f hpack.HeaderField) {
st.header.Add(f.Name, f.Value)
})
return st
}
func (st *serverTester) Close() {
if st.conn != nil {
st.conn.Close()
}
if st.cmd != nil {
st.cmd.Process.Kill()
st.cmd.Wait()
}
if st.ts != nil {
st.ts.Close()
}
}
func (st *serverTester) readFrame() (http2.Frame, error) {
go func() {
f, err := st.fr.ReadFrame()
if err != nil {
st.errCh <- err
return
}
st.frCh <- f
}()
select {
case f := <-st.frCh:
return f, nil
case err := <-st.errCh:
return nil, err
case <-time.After(5 * time.Second):
return nil, errors.New("timeout waiting for frame")
}
}
func (st *serverTester) readSpdyFrame() (spdy.Frame, error) {
go func() {
f, err := st.spdyFr.ReadFrame()
if err != nil {
st.errCh <- err
return
}
st.spdyFrCh <- f
}()
select {
case f := <-st.spdyFrCh:
return f, nil
case err := <-st.errCh:
return nil, err
case <-time.After(2 * time.Second):
return nil, errors.New("timeout waiting for frame")
}
}
type requestParam struct {
name string // name for this request to identify the request in log easily
streamID uint32 // stream ID, automatically assigned if 0
method string // method, defaults to GET
scheme string // scheme, defaults to http
authority string // authority, defaults to backend server address
path string // path, defaults to /
header []hpack.HeaderField // additional request header fields
body []byte // request body
trailer []hpack.HeaderField // trailer part
}
// wrapper for request body to set trailer part
type chunkedBodyReader struct {
trailer []hpack.HeaderField
trailerWritten bool
body io.Reader
req *http.Request
}
func (cbr *chunkedBodyReader) Read(p []byte) (n int, err error) {
// document says that we have to set http.Request.Trailer
// after request was sent and before body returns EOF.
if !cbr.trailerWritten {
cbr.trailerWritten = true
for _, h := range cbr.trailer {
cbr.req.Trailer.Set(h.Name, h.Value)
}
}
return cbr.body.Read(p)
}
func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
method := "GET"
if rp.method != "" {
method = rp.method
}
var body io.Reader
var cbr *chunkedBodyReader
if rp.body != nil {
body = bytes.NewBuffer(rp.body)
if len(rp.trailer) != 0 {
cbr = &chunkedBodyReader{
trailer: rp.trailer,
body: body,
}
body = cbr
}
}
req, err := http.NewRequest(method, st.url, body)
if err != nil {
return nil, err
}
for _, h := range rp.header {
req.Header.Add(h.Name, h.Value)
}
req.Header.Add("Test-Case", rp.name)
if cbr != nil {
cbr.req = req
// this makes request use chunked encoding
req.ContentLength = -1
req.Trailer = make(http.Header)
for _, h := range cbr.trailer {
req.Trailer.Set(h.Name, "")
}
}
if err := req.Write(st.conn); err != nil {
return nil, err
}
resp, err := http.ReadResponse(bufio.NewReader(st.conn), req)
if err != nil {
return nil, err
}
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
resp.Body.Close()
res := &serverResponse{
status: resp.StatusCode,
header: resp.Header,
body: respBody,
connClose: resp.Close,
}
return res, nil
}
func (st *serverTester) spdy(rp requestParam) (*serverResponse, error) {
res := &serverResponse{}
var id spdy.StreamId
if rp.streamID != 0 {
id = spdy.StreamId(rp.streamID)
if id >= spdy.StreamId(st.nextStreamID) && id%2 == 1 {
st.nextStreamID = uint32(id) + 2
}
} else {
id = spdy.StreamId(st.nextStreamID)
st.nextStreamID += 2
}
method := "GET"
if rp.method != "" {
method = rp.method
}
scheme := "http"
if rp.scheme != "" {
scheme = rp.scheme
}
host := st.authority
if rp.authority != "" {
host = rp.authority
}
path := "/"
if rp.path != "" {
path = rp.path
}
header := make(http.Header)
header.Add(":method", method)
header.Add(":scheme", scheme)
header.Add(":host", host)
header.Add(":path", path)
header.Add(":version", "HTTP/1.1")
header.Add("test-case", rp.name)
for _, h := range rp.header {
header.Add(h.Name, h.Value)
}
var synStreamFlags spdy.ControlFlags
if len(rp.body) == 0 {
synStreamFlags = spdy.ControlFlagFin
}
if err := st.spdyFr.WriteFrame(&spdy.SynStreamFrame{
CFHeader: spdy.ControlFrameHeader{
Flags: synStreamFlags,
},
StreamId: id,
Headers: header,
}); err != nil {
return nil, err
}
if len(rp.body) != 0 {
if err := st.spdyFr.WriteFrame(&spdy.DataFrame{
StreamId: id,
Flags: spdy.DataFlagFin,
Data: rp.body,
}); err != nil {
return nil, err
}
}
loop:
for {
fr, err := st.readSpdyFrame()
if err != nil {
return res, err
}
switch f := fr.(type) {
case *spdy.SynReplyFrame:
if f.StreamId != id {
break
}
res.header = cloneHeader(f.Headers)
if _, err := fmt.Sscan(res.header.Get(":status"), &res.status); err != nil {
return res, fmt.Errorf("Error parsing status code: %v", err)
}
if f.CFHeader.Flags&spdy.ControlFlagFin != 0 {
break loop
}
case *spdy.DataFrame:
if f.StreamId != id {
break
}
res.body = append(res.body, f.Data...)
if f.Flags&spdy.DataFlagFin != 0 {
break loop
}
case *spdy.RstStreamFrame:
if f.StreamId != id {
break
}
res.spdyRstErrCode = f.Status
break loop
case *spdy.GoAwayFrame:
if f.Status == spdy.GoAwayOK {
break
}
res.spdyGoAwayErrCode = f.Status
break loop
}
}
return res, nil
}
func (st *serverTester) http2(rp requestParam) (*serverResponse, error) {
st.headerBlkBuf.Reset()
st.header = make(http.Header)
var id uint32
if rp.streamID != 0 {
id = rp.streamID
if id >= st.nextStreamID && id%2 == 1 {
st.nextStreamID = id + 2
}
} else {
id = st.nextStreamID
st.nextStreamID += 2
}
if !st.h2PrefaceSent {
st.h2PrefaceSent = true
fmt.Fprint(st.conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
if err := st.fr.WriteSettings(); err != nil {
return nil, err
}
}
res := &serverResponse{
streamID: id,
}
streams := make(map[uint32]*serverResponse)
streams[id] = res
method := "GET"
if rp.method != "" {
method = rp.method
}
_ = st.enc.WriteField(pair(":method", method))
scheme := "http"
if rp.scheme != "" {
scheme = rp.scheme
}
_ = st.enc.WriteField(pair(":scheme", scheme))
authority := st.authority
if rp.authority != "" {
authority = rp.authority
}
_ = st.enc.WriteField(pair(":authority", authority))
path := "/"
if rp.path != "" {
path = rp.path
}
_ = st.enc.WriteField(pair(":path", path))
_ = st.enc.WriteField(pair("test-case", rp.name))
for _, h := range rp.header {
_ = st.enc.WriteField(h)
}
err := st.fr.WriteHeaders(http2.HeadersFrameParam{
StreamID: id,
EndStream: len(rp.body) == 0 && len(rp.trailer) == 0,
EndHeaders: true,
BlockFragment: st.headerBlkBuf.Bytes(),
})
if err != nil {
return nil, err
}
if len(rp.body) != 0 {
// TODO we assume rp.body fits in 1 frame
if err := st.fr.WriteData(id, len(rp.trailer) == 0, rp.body); err != nil {
return nil, err
}
}
if len(rp.trailer) != 0 {
st.headerBlkBuf.Reset()
for _, h := range rp.trailer {
_ = st.enc.WriteField(h)
}
err := st.fr.WriteHeaders(http2.HeadersFrameParam{
StreamID: id,
EndStream: true,
EndHeaders: true,
BlockFragment: st.headerBlkBuf.Bytes(),
})
if err != nil {
return nil, err
}
}
loop:
for {
fr, err := st.readFrame()
if err != nil {
return res, err
}
switch f := fr.(type) {
case *http2.HeadersFrame:
_, err := st.dec.Write(f.HeaderBlockFragment())
if err != nil {
return res, err
}
sr, ok := streams[f.FrameHeader.StreamID]
if !ok {
st.header = make(http.Header)
break
}
sr.header = cloneHeader(st.header)
var status int
status, err = strconv.Atoi(sr.header.Get(":status"))
if err != nil {
return res, fmt.Errorf("Error parsing status code: %v", err)
}
sr.status = status
if f.StreamEnded() {
if streamEnded(res, streams, sr) {
break loop
}
}
case *http2.PushPromiseFrame:
_, err := st.dec.Write(f.HeaderBlockFragment())
if err != nil {
return res, err
}
sr := &serverResponse{
streamID: f.PromiseID,
reqHeader: cloneHeader(st.header),
}
streams[sr.streamID] = sr
case *http2.DataFrame:
sr, ok := streams[f.FrameHeader.StreamID]
if !ok {
break
}
sr.body = append(sr.body, f.Data()...)
if f.StreamEnded() {
if streamEnded(res, streams, sr) {
break loop
}
}
case *http2.RSTStreamFrame:
sr, ok := streams[f.FrameHeader.StreamID]
if !ok {
break
}
sr.errCode = f.ErrCode
if streamEnded(res, streams, sr) {
break loop
}
case *http2.GoAwayFrame:
if f.ErrCode == http2.ErrCodeNo {
break
}
res.errCode = f.ErrCode
res.connErr = true
break loop
case *http2.SettingsFrame:
if f.IsAck() {
break
}
if err := st.fr.WriteSettingsAck(); err != nil {
return res, err
}
}
}
sort.Sort(ByStreamID(res.pushResponse))
return res, nil
}
func streamEnded(mainSr *serverResponse, streams map[uint32]*serverResponse, sr *serverResponse) bool {
delete(streams, sr.streamID)
if mainSr.streamID != sr.streamID {
mainSr.pushResponse = append(mainSr.pushResponse, sr)
}
return len(streams) == 0
}
type serverResponse struct {
status int // HTTP status code
header http.Header // response header fields
body []byte // response body
streamID uint32 // stream ID in HTTP/2
errCode http2.ErrCode // error code received in HTTP/2 RST_STREAM or GOAWAY
connErr bool // true if HTTP/2 connection error
spdyGoAwayErrCode spdy.GoAwayStatus // status code received in SPDY RST_STREAM
spdyRstErrCode spdy.RstStreamStatus // status code received in SPDY GOAWAY
connClose bool // Conection: close is included in response header in HTTP/1 test
reqHeader http.Header // http request header, currently only sotres pushed request header
pushResponse []*serverResponse // pushed response
}
type ByStreamID []*serverResponse
func (b ByStreamID) Len() int {
return len(b)
}
func (b ByStreamID) Swap(i, j int) {
b[i], b[j] = b[j], b[i]
}
func (b ByStreamID) Less(i, j int) bool {
return b[i].streamID < b[j].streamID
}
func cloneHeader(h http.Header) http.Header {
h2 := make(http.Header, len(h))
for k, vv := range h {
vv2 := make([]string, len(vv))
copy(vv2, vv)
h2[k] = vv2
}
return h2
}
func noopHandler(w http.ResponseWriter, r *http.Request) {}

View File

@@ -0,0 +1,6 @@
#!/bin/sh -e
export CGO_CFLAGS="-I@abs_top_srcdir@/lib/includes -I@abs_top_builddir@/lib/includes"
export CGO_LDFLAGS="-L@abs_top_builddir@/lib/.libs"
export LD_LIBRARY_PATH="@abs_top_builddir@/lib/.libs"
"$@"

View File

@@ -45,7 +45,8 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \
nghttp2_priority_spec.c \
nghttp2_option.c \
nghttp2_callbacks.c \
nghttp2_mem.c
nghttp2_mem.c \
nghttp2_http.c
HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
nghttp2_frame.h \
@@ -58,7 +59,8 @@ HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
nghttp2_priority_spec.h \
nghttp2_option.h \
nghttp2_callbacks.h \
nghttp2_mem.h
nghttp2_mem.h \
nghttp2_http.h
libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS)
libnghttp2_la_LDFLAGS = -no-undefined \

View File

@@ -49,6 +49,24 @@ extern "C" {
*/
#define NGHTTP2_PROTO_VERSION_ID_LEN 5
/**
* @macro
*
* The seriazlied form of ALPN protocol identifier this library
* supports. Notice that first byte is the length of following
* protocol identifier. This is the same wire format of `TLS ALPN
* extension <https://tools.ietf.org/html/rfc7301>`_. This is useful
* to process incoming ALPN tokens in wire format.
*/
#define NGHTTP2_PROTO_ALPN "\x5h2-14"
/**
* @macro
*
* The length of :macro:`NGHTTP2_PROTO_ALPN`.
*/
#define NGHTTP2_PROTO_ALPN_LEN (sizeof(NGHTTP2_PROTO_ALPN) - 1)
/**
* @macro
*
@@ -201,7 +219,7 @@ typedef enum {
*/
NGHTTP2_ERR_INVALID_ARGUMENT = -501,
/**
* Ouf of buffer space.
* Out of buffer space.
*/
NGHTTP2_ERR_BUFFER_ERROR = -502,
/**
@@ -325,6 +343,16 @@ typedef enum {
* not been fully processed yet.
*/
NGHTTP2_ERR_DATA_EXIST = -529,
/**
* The current session is closing due to a connection error or
* `nghttp2_session_terminate_session()` is called.
*/
NGHTTP2_ERR_SESSION_CLOSING = -530,
/**
* Invalid HTTP header field was received and stream is going to be
* closed.
*/
NGHTTP2_ERR_HTTP_HEADER = -531,
/**
* The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is
* under unexpected condition and processing was terminated (e.g.,
@@ -360,7 +388,9 @@ typedef enum {
*/
NGHTTP2_NV_FLAG_NONE = 0,
/**
* Indicates that this name/value pair must not be indexed.
* Indicates that this name/value pair must not be indexed ("Literal
* Header Field never Indexed" representation must be used in HPACK
* encoding). Other implementation calls this bit as "sensitive".
*/
NGHTTP2_NV_FLAG_NO_INDEX = 0x01
} nghttp2_nv_flag;
@@ -438,7 +468,9 @@ typedef enum {
*/
NGHTTP2_WINDOW_UPDATE = 0x08,
/**
* The CONTINUATION frame.
* The CONTINUATION frame. This frame type won't be passed to any
* callbacks because the library processes this frame type and its
* preceding HEADERS/PUSH_PROMISE as a single frame.
*/
NGHTTP2_CONTINUATION = 0x09
} nghttp2_frame_type;
@@ -652,7 +684,14 @@ typedef enum {
/**
* Indicates EOF was sensed.
*/
NGHTTP2_DATA_FLAG_EOF = 0x01
NGHTTP2_DATA_FLAG_EOF = 0x01,
/**
* Indicates that END_STREAM flag must not be set even if
* NGHTTP2_DATA_FLAG_EOF is set. Usually this flag is used to send
* trailer header fields with `nghttp2_submit_request()` or
* `nghttp2_submit_response()`.
*/
NGHTTP2_DATA_FLAG_NO_END_STREAM = 0x02
} nghttp2_data_flag;
/**
@@ -665,6 +704,21 @@ typedef enum {
* them in |buf| and return number of data stored in |buf|. If EOF is
* reached, set :enum:`NGHTTP2_DATA_FLAG_EOF` flag in |*data_flags|.
*
* If this callback is set by `nghttp2_submit_request()`,
* `nghttp2_submit_response()` or `nghttp2_submit_headers()` and
* `nghttp2_submit_data()` with flag parameter
* :enum:`NGHTTP2_FLAG_END_STREAM` set, and
* :enum:`NGHTTP2_DATA_FLAG_EOF` flag is set to |*data_flags|, DATA
* frame will have END_STREAM flag set. Usually, this is expected
* behaviour and all are fine. One exception is send trailer header
* fields. You cannot send trailers after sending frame with
* END_STREAM set. To avoid this problem, one can set
* :enum:`NGHTTP2_DATA_FLAG_NO_END_STREAM` along with
* :enum:`NGHTTP2_DATA_FLAG_EOF` to signal the library not to set
* END_STREAM in DATA frame. Then application can use
* `nghttp2_submit_trailer()` to send trailers.
* `nghttp2_submit_trailer()` can be called inside this callback.
*
* If the application wants to postpone DATA frames (e.g.,
* asynchronous I/O, or reading data blocks for long time), it is
* achieved by returning :enum:`NGHTTP2_ERR_DEFERRED` without reading
@@ -1299,6 +1353,9 @@ typedef int (*nghttp2_on_frame_send_callback)(nghttp2_session *session,
* `nghttp2_session_recv()` and `nghttp2_session_send()` functions
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
*
* `nghttp2_session_get_stream_user_data()` can be used to get
* associated data.
*
* To set this callback to :type:`nghttp2_session_callbacks`, use
* `nghttp2_session_callbacks_set_on_frame_not_send_callback()`.
*/
@@ -1347,6 +1404,27 @@ typedef int (*nghttp2_on_stream_close_callback)(nghttp2_session *session,
* not need to care about that because the header name/value pairs are
* emitted transparently regardless of CONTINUATION frames.
*
* The server applications probably create an object to store
* information about new stream if ``frame->hd.type ==
* NGHTTP2_HEADERS`` and ``frame->headers.cat ==
* NGHTTP2_HCAT_REQUEST``. If |session| is configured as server side,
* ``frame->headers.cat`` is either ``NGHTTP2_HCAT_REQUEST``
* containing request headers or ``NGHTTP2_HCAT_HEADERS`` containing
* trailer headers and never get PUSH_PROMISE in this callback.
*
* For the client applications, ``frame->hd.type`` is either
* ``NGHTTP2_HEADERS`` or ``NGHTTP2_PUSH_PROMISE``. In case of
* ``NGHTTP2_HEADERS``, ``frame->headers.cat ==
* NGHTTP2_HCAT_RESPONSE`` means that it is the first response
* headers, but it may be non-final response which is indicated by 1xx
* status code. In this case, there may be zero or more HEADERS frame
* with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` which has
* non-final response code and finally client gets exactly one HEADERS
* frame with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS``
* containing final response headers (non-1xx status code). The
* trailer headers also has ``frame->headers.cat ==
* NGHTTP2_HCAT_HEADERS`` which does not containg any status code.
*
* The implementation of this function must return 0 if it succeeds or
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If nonzero value other than
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned, it is treated as
@@ -1372,7 +1450,8 @@ typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session,
*
* If :enum:`NGHTTP2_NV_FLAG_NO_INDEX` is set in |flags|, the receiver
* must not index this name/value pair when forwarding it to the next
* hop.
* hop. More specifically, "Literal Header Field never Indexed"
* representation must be used in HPACK encoding.
*
* When this callback is invoked, ``frame->hd.type`` is either
* :enum:`NGHTTP2_HEADERS` or :enum:`NGHTTP2_PUSH_PROMISE`. After all
@@ -1382,21 +1461,14 @@ typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session,
* :type:`nghttp2_on_frame_recv_callback` for the |frame| will not be
* invoked.
*
* The |name| may be ``NULL`` if the |namelen| is 0. The same thing
* can be said about the |value|.
* The |value| may be ``NULL`` if the |valuelen| is 0.
*
* Please note that nghttp2 library does not perform any validity
* check against the |name| and the |value|. For example, the
* |namelen| could be 0, and/or the |value| contains ``0x0a`` or
* ``0x0d``. The application must check them if it matters. The
* helper function `nghttp2_check_header_name()` and
* `nghttp2_check_header_value()` provide simple validation against
* HTTP2 header field construction rule.
*
* HTTP/2 specification requires that pseudo header fields (header
* field starting with ':') must appear in front of regular header
* fields. The library does not validate this requirement. The
* application must check them if it matters.
* Please note that unless `nghttp2_option_set_no_http_messaging()` is
* used, nghttp2 library does perform validation against the |name|
* and the |value| using `nghttp2_check_header_name()` and
* `nghttp2_check_header_value()`. In addition to this, nghttp2
* performs vaidation based on HTTP Messaging rule, which is briefly
* explained in `HTTP Messaging`_ section.
*
* If the application uses `nghttp2_session_mem_recv()`, it can return
* :enum:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()`
@@ -1864,6 +1936,18 @@ void nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option,
*/
void nghttp2_option_set_recv_client_preface(nghttp2_option *option, int val);
/**
* @function
*
* By default, nghttp2 library enforces subset of HTTP Messaging rules
* described in `HTTP/2 specification, section 8
* <https://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-8>`_.
* See `HTTP Messaging`_ section for details. For those applications
* who use nghttp2 library as non-HTTP use, give nonzero to |val| to
* disable this enforcement.
*/
void nghttp2_option_set_no_http_messaging(nghttp2_option *option, int val);
/**
* @function
*
@@ -2457,6 +2541,43 @@ int nghttp2_session_terminate_session2(nghttp2_session *session,
int32_t last_stream_id,
uint32_t error_code);
/**
* @function
*
* Signals to the client that the server started graceful shutdown
* procedure.
*
* This function is only usable for server. If this function is
* called with client side session, this function returns
* :enum:`NGHTTP2_ERR_INVALID_STATE`.
*
* To gracefully shutdown HTTP/2 session, server should call this
* function to send GOAWAY with last_stream_id (1u << 31) - 1. And
* after some delay (e.g., 1 RTT), send another GOAWAY with the stream
* ID that the server has some processing using
* `nghttp2_submit_goaway()`. See also
* `nghttp2_session_get_last_proc_stream_id()`.
*
* Unlike `nghttp2_submit_goaway()`, this function just sends GOAWAY
* and does nothing more. This is a mere indication to the client
* that session shutdown is imminent. The application should call
* `nghttp2_submit_goaway()` with appropriate last_stream_id after
* this call.
*
* If one or more GOAWAY frame have been already sent by either
* `nghttp2_submit_goaway()` or `nghttp2_session_terminate_session()`,
* this function has no effect.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* :enum:`NGHTTP2_ERR_NOMEM`
* Out of memory.
* :enum:`NGHTTP2_ERR_INVALID_STATE`
* The |session| is initialized as client.
*/
int nghttp2_submit_shutdown_notice(nghttp2_session *session);
/**
* @function
*
@@ -2737,6 +2858,53 @@ int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,
const nghttp2_nv *nva, size_t nvlen,
const nghttp2_data_provider *data_prd);
/**
* @function
*
* Submits trailer HEADERS against the stream |stream_id|.
*
* The |nva| is an array of name/value pair :type:`nghttp2_nv` with
* |nvlen| elements. The application is responsible not to include
* required pseudo-header fields (header field whose name starts with
* ":") in |nva|.
*
* This function creates copies of all name/value pairs in |nva|. It
* also lower-cases all names in |nva|. The order of elements in
* |nva| is preserved.
*
* For server, trailer must be followed by response HEADERS or
* response DATA. The library does not check that response HEADERS
* has already sent and if `nghttp2_submit_trailer()` is called before
* any response HEADERS submission (usually by
* `nghttp2_submit_response()`), the content of |nva| will be sent as
* reponse headers, which will result in error.
*
* This function has the same effect with `nghttp2_submit_headers()`,
* with flags = :enum:`NGHTTP2_FLAG_END_HEADERS` and both pri_spec and
* stream_user_data to NULL.
*
* To submit trailer after `nghttp2_submit_response()` is called, the
* application has to specify :type:`nghttp2_data_provider` to
* `nghttp2_submit_response()`. In side
* :type:`nghttp2_data_source_read_callback`, when setting
* :enum:`NGHTTP2_DATA_FLAG_EOF`, also set
* :enum:`NGHTTP2_DATA_FLAG_NO_END_STREAM`. After that, the
* application can send trailer using `nghttp2_submit_trailer()`.
* `nghttp2_submit_trailer()` can be used inside
* :type:`nghttp2_data_source_read_callback`.
*
* This function returns 0 if it succeeds and |stream_id| is -1.
* Otherwise, this function returns 0 if it succeeds, or one of the
* following negative error codes:
*
* :enum:`NGHTTP2_ERR_NOMEM`
* Out of memory.
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
* The |stream_id| is 0.
*/
int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id,
const nghttp2_nv *nva, size_t nvlen);
/**
* @function
*
@@ -2974,6 +3142,9 @@ int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags,
*
* The client side is not allowed to use this function.
*
* To submit response headers and data, use
* `nghttp2_submit_response()`.
*
* This function returns assigned promised stream ID if it succeeds,
* or one of the following negative error codes:
*
@@ -3056,6 +3227,13 @@ int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags,
* keep this memory after the return of this function. If the
* |opaque_data_len| is 0, the |opaque_data| could be ``NULL``.
*
* After successful transmission of GOAWAY, following things happen.
* All incoming streams having strictly more than |last_stream_id| are
* closed. All incoming HEADERS which starts new stream are simply
* ignored. After all active streams are handled, both
* `nghttp2_session_want_read()` and `nghttp2_session_want_write()`
* return 0 and the application can close session.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
@@ -3069,6 +3247,19 @@ int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags,
int32_t last_stream_id, uint32_t error_code,
const uint8_t *opaque_data, size_t opaque_data_len);
/**
* @function
*
* Returns the last stream ID of a stream for which
* :type:`nghttp2_on_frame_recv_callback` was invoked most recently.
* The returned value can be used as last_stream_id parameter for
* `nghttp2_submit_goaway()` and
* `nghttp2_session_terminate_session2()`.
*
* This function always succeeds.
*/
int32_t nghttp2_session_get_last_proc_stream_id(nghttp2_session *session);
/**
* @function
*

View File

@@ -55,10 +55,8 @@
/* Number of inbound buffer */
#define NGHTTP2_FRAMEBUF_MAX_NUM 5
/* The default length of DATA frame payload. This should be small enough
* for the data payload and the header to fit into 1 TLS record */
#define NGHTTP2_DATA_PAYLOADLEN \
((NGHTTP2_MAX_FRAME_SIZE_MIN) - (NGHTTP2_FRAME_HDLEN))
/* The default length of DATA frame payload. */
#define NGHTTP2_DATA_PAYLOADLEN NGHTTP2_MAX_FRAME_SIZE_MIN
/* Maximum headers payload length, calculated in compressed form.
This applies to transmission only. */

View File

@@ -37,8 +37,8 @@
#define MAKE_STATIC_ENT(I, N, V, NH, VH) \
{ \
{ \
{ (uint8_t *) N, (uint8_t *)V, sizeof(N) - 1, sizeof(V) - 1, 0 } \
, NH, VH, 1, NGHTTP2_HD_FLAG_NONE \
{ (uint8_t *)(N), (uint8_t *)(V), sizeof((N)) - 1, sizeof((V)) - 1, 0 } \
, (NH), (VH), 1, NGHTTP2_HD_FLAG_NONE \
} \
, I \
}

View File

@@ -293,6 +293,10 @@ const char *nghttp2_strerror(int error_code) {
return "Server push is disabled by peer";
case NGHTTP2_ERR_DATA_EXIST:
return "DATA frame already exists";
case NGHTTP2_ERR_SESSION_CLOSING:
return "The current session is closing";
case NGHTTP2_ERR_HTTP_HEADER:
return "Invalid HTTP header field was received";
case NGHTTP2_ERR_NOMEM:
return "Out of memory";
case NGHTTP2_ERR_CALLBACK_FAILURE:

548
lib/nghttp2_http.c Normal file
View File

@@ -0,0 +1,548 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "nghttp2_http.h"
#include <string.h>
#include <assert.h>
#include <stdio.h>
static int memeq(const void *a, const void *b, size_t n) {
return memcmp(a, b, n) == 0;
}
#define streq(A, B, N) ((sizeof((A)) - 1) == (N) && memeq((A), (B), (N)))
static char downcase(char c) {
return 'A' <= c && c <= 'Z' ? (c - 'A' + 'a') : c;
}
static int memieq(const void *a, const void *b, size_t n) {
size_t i;
const uint8_t *aa = a, *bb = b;
for (i = 0; i < n; ++i) {
if (downcase(aa[i]) != downcase(bb[i])) {
return 0;
}
}
return 1;
}
#define strieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))
typedef enum {
NGHTTP2_TOKEN__AUTHORITY,
NGHTTP2_TOKEN__METHOD,
NGHTTP2_TOKEN__PATH,
NGHTTP2_TOKEN__SCHEME,
NGHTTP2_TOKEN__STATUS,
NGHTTP2_TOKEN_CONNECTION,
NGHTTP2_TOKEN_CONTENT_LENGTH,
NGHTTP2_TOKEN_HOST,
NGHTTP2_TOKEN_KEEP_ALIVE,
NGHTTP2_TOKEN_PROXY_CONNECTION,
NGHTTP2_TOKEN_TE,
NGHTTP2_TOKEN_TRANSFER_ENCODING,
NGHTTP2_TOKEN_UPGRADE,
NGHTTP2_TOKEN_MAXIDX,
} nghttp2_token;
/*
* This function was generated by genlibtokenlookup.py. Inspired by
* h2o header lookup. https://github.com/h2o/h2o
*/
static int lookup_token(const uint8_t *name, size_t namelen) {
switch (namelen) {
case 2:
switch (name[namelen - 1]) {
case 'e':
if (streq("t", name, 1)) {
return NGHTTP2_TOKEN_TE;
}
break;
}
break;
case 4:
switch (name[namelen - 1]) {
case 't':
if (streq("hos", name, 3)) {
return NGHTTP2_TOKEN_HOST;
}
break;
}
break;
case 5:
switch (name[namelen - 1]) {
case 'h':
if (streq(":pat", name, 4)) {
return NGHTTP2_TOKEN__PATH;
}
break;
}
break;
case 7:
switch (name[namelen - 1]) {
case 'd':
if (streq(":metho", name, 6)) {
return NGHTTP2_TOKEN__METHOD;
}
break;
case 'e':
if (streq(":schem", name, 6)) {
return NGHTTP2_TOKEN__SCHEME;
}
if (streq("upgrad", name, 6)) {
return NGHTTP2_TOKEN_UPGRADE;
}
break;
case 's':
if (streq(":statu", name, 6)) {
return NGHTTP2_TOKEN__STATUS;
}
break;
}
break;
case 10:
switch (name[namelen - 1]) {
case 'e':
if (streq("keep-aliv", name, 9)) {
return NGHTTP2_TOKEN_KEEP_ALIVE;
}
break;
case 'n':
if (streq("connectio", name, 9)) {
return NGHTTP2_TOKEN_CONNECTION;
}
break;
case 'y':
if (streq(":authorit", name, 9)) {
return NGHTTP2_TOKEN__AUTHORITY;
}
break;
}
break;
case 14:
switch (name[namelen - 1]) {
case 'h':
if (streq("content-lengt", name, 13)) {
return NGHTTP2_TOKEN_CONTENT_LENGTH;
}
break;
}
break;
case 16:
switch (name[namelen - 1]) {
case 'n':
if (streq("proxy-connectio", name, 15)) {
return NGHTTP2_TOKEN_PROXY_CONNECTION;
}
break;
}
break;
case 17:
switch (name[namelen - 1]) {
case 'g':
if (streq("transfer-encodin", name, 16)) {
return NGHTTP2_TOKEN_TRANSFER_ENCODING;
}
break;
}
break;
}
return -1;
}
static int64_t parse_uint(const uint8_t *s, size_t len) {
int64_t n = 0;
size_t i;
if (len == 0) {
return -1;
}
for (i = 0; i < len; ++i) {
if ('0' <= s[i] && s[i] <= '9') {
if (n > INT64_MAX / 10) {
return -1;
}
n *= 10;
if (n > INT64_MAX - (s[i] - '0')) {
return -1;
}
n += s[i] - '0';
continue;
}
return -1;
}
return n;
}
static int lws(const uint8_t *s, size_t n) {
size_t i;
for (i = 0; i < n; ++i) {
if (s[i] != ' ' && s[i] != '\t') {
return 0;
}
}
return 1;
}
static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_nv *nv,
int flag) {
if (stream->http_flags & flag) {
return 0;
}
if (lws(nv->value, nv->valuelen)) {
return 0;
}
stream->http_flags |= flag;
return 1;
}
static int expect_response_body(nghttp2_stream *stream) {
return (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_HEAD) == 0 &&
stream->status_code / 100 != 1 && stream->status_code != 304 &&
stream->status_code != 204;
}
static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
int trailer) {
int token;
if (nv->name[0] == ':') {
if (trailer ||
(stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
}
token = lookup_token(nv->name, nv->namelen);
switch (token) {
case NGHTTP2_TOKEN__AUTHORITY:
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
break;
case NGHTTP2_TOKEN__METHOD:
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
if (streq("HEAD", nv->value, nv->valuelen)) {
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
} else if (streq("CONNECT", nv->value, nv->valuelen)) {
if (stream->stream_id % 2 == 0) {
/* we won't allow CONNECT for push */
return NGHTTP2_ERR_HTTP_HEADER;
}
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
if (stream->http_flags &
(NGHTTP2_HTTP_FLAG__PATH | NGHTTP2_HTTP_FLAG__SCHEME)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
}
break;
case NGHTTP2_TOKEN__PATH:
if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
return NGHTTP2_ERR_HTTP_HEADER;
}
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
break;
case NGHTTP2_TOKEN__SCHEME:
if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
return NGHTTP2_ERR_HTTP_HEADER;
}
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
break;
case NGHTTP2_TOKEN_HOST:
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
break;
case NGHTTP2_TOKEN_CONTENT_LENGTH: {
if (stream->content_length != -1) {
return NGHTTP2_ERR_HTTP_HEADER;
}
stream->content_length = parse_uint(nv->value, nv->valuelen);
if (stream->content_length == -1) {
return NGHTTP2_ERR_HTTP_HEADER;
}
break;
}
/* disallowed header fields */
case NGHTTP2_TOKEN_CONNECTION:
case NGHTTP2_TOKEN_KEEP_ALIVE:
case NGHTTP2_TOKEN_PROXY_CONNECTION:
case NGHTTP2_TOKEN_TRANSFER_ENCODING:
case NGHTTP2_TOKEN_UPGRADE:
return NGHTTP2_ERR_HTTP_HEADER;
case NGHTTP2_TOKEN_TE:
if (!strieq("trailers", nv->value, nv->valuelen)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
break;
default:
if (nv->name[0] == ':') {
return NGHTTP2_ERR_HTTP_HEADER;
}
}
if (nv->name[0] != ':') {
stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
}
return 0;
}
static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
int trailer) {
int token;
if (nv->name[0] == ':') {
if (trailer ||
(stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
}
token = lookup_token(nv->name, nv->namelen);
switch (token) {
case NGHTTP2_TOKEN__STATUS: {
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
if (nv->valuelen != 3) {
return NGHTTP2_ERR_HTTP_HEADER;
}
stream->status_code = parse_uint(nv->value, nv->valuelen);
if (stream->status_code == -1) {
return NGHTTP2_ERR_HTTP_HEADER;
}
break;
}
case NGHTTP2_TOKEN_CONTENT_LENGTH: {
if (stream->content_length != -1) {
return NGHTTP2_ERR_HTTP_HEADER;
}
stream->content_length = parse_uint(nv->value, nv->valuelen);
if (stream->content_length == -1) {
return NGHTTP2_ERR_HTTP_HEADER;
}
break;
}
/* disallowed header fields */
case NGHTTP2_TOKEN_CONNECTION:
case NGHTTP2_TOKEN_KEEP_ALIVE:
case NGHTTP2_TOKEN_PROXY_CONNECTION:
case NGHTTP2_TOKEN_TRANSFER_ENCODING:
case NGHTTP2_TOKEN_UPGRADE:
return NGHTTP2_ERR_HTTP_HEADER;
case NGHTTP2_TOKEN_TE:
if (!strieq("trailers", nv->value, nv->valuelen)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
break;
default:
if (nv->name[0] == ':') {
return NGHTTP2_ERR_HTTP_HEADER;
}
}
if (nv->name[0] != ':') {
stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
}
return 0;
}
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
nghttp2_frame *frame, nghttp2_nv *nv, int trailer) {
/* We are strict for pseudo header field. One bad character should
lead to fail. OTOH, we should be a bit forgiving for regular
headers, since existing public internet has so much illegal
headers floating around and if we kill the stream because of
this, we may disrupt many web sites and/or libraries. So we
become conservative here, and just ignore those illegal regular
headers. */
if (!nghttp2_check_header_name(nv->name, nv->namelen)) {
size_t i;
if (nv->namelen > 0 && nv->name[0] == ':') {
return NGHTTP2_ERR_HTTP_HEADER;
}
/* header field name must be lower-cased without exception */
for (i = 0; i < nv->namelen; ++i) {
char c = nv->name[i];
if ('A' <= c && c <= 'Z') {
return NGHTTP2_ERR_HTTP_HEADER;
}
}
/* When ignoring regular headers, we set this flag so that we
still enforce header field ordering rule for pseudo header
fields. */
stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
return NGHTTP2_ERR_IGN_HTTP_HEADER;
}
if (!nghttp2_check_header_value(nv->value, nv->valuelen)) {
assert(nv->namelen > 0);
if (nv->name[0] == ':') {
return NGHTTP2_ERR_HTTP_HEADER;
}
/* When ignoring regular headers, we set this flag so that we
still enforce header field ordering rule for pseudo header
fields. */
stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
return NGHTTP2_ERR_IGN_HTTP_HEADER;
}
if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
return http_request_on_header(stream, nv, trailer);
}
return http_response_on_header(stream, nv, trailer);
}
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
nghttp2_frame *frame) {
if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
if ((stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
return -1;
}
stream->content_length = -1;
} else if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
(stream->http_flags &
(NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
return -1;
}
if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
/* we are going to reuse data fields for upcoming response. Clear
them now, except for method flags. */
stream->http_flags &= NGHTTP2_HTTP_FLAG_METH_ALL;
stream->content_length = -1;
}
return 0;
}
int nghttp2_http_on_response_headers(nghttp2_stream *stream) {
if ((stream->http_flags & NGHTTP2_HTTP_FLAG__STATUS) == 0) {
return -1;
}
if (stream->status_code / 100 == 1) {
/* non-final response */
stream->http_flags = (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) |
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
stream->content_length = -1;
stream->status_code = -1;
return 0;
}
stream->http_flags &= ~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
if (!expect_response_body(stream)) {
stream->content_length = 0;
} else if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
stream->content_length = -1;
}
return 0;
}
int nghttp2_http_on_trailer_headers(nghttp2_stream *stream _U_,
nghttp2_frame *frame) {
if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
return -1;
}
return 0;
}
int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) {
if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
return -1;
}
if (stream->content_length != -1 &&
stream->content_length != stream->recv_content_length) {
return -1;
}
return 0;
}
int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) {
stream->recv_content_length += n;
if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
(stream->content_length != -1 &&
stream->recv_content_length > stream->content_length)) {
return -1;
}
return 0;
}
void nghttp2_http_record_request_method(nghttp2_stream *stream,
nghttp2_frame *frame) {
const nghttp2_nv *nva;
size_t nvlen;
size_t i;
switch (frame->hd.type) {
case NGHTTP2_HEADERS:
nva = frame->headers.nva;
nvlen = frame->headers.nvlen;
break;
case NGHTTP2_PUSH_PROMISE:
nva = frame->push_promise.nva;
nvlen = frame->push_promise.nvlen;
break;
default:
return;
}
/* TODO we should do this strictly. */
for (i = 0; i < nvlen; ++i) {
const nghttp2_nv *nv = &nva[i];
if (lookup_token(nv->name, nv->namelen) != NGHTTP2_TOKEN__METHOD) {
continue;
}
if (streq("CONNECT", nv->value, nv->valuelen)) {
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
return;
}
if (streq("HEAD", nv->value, nv->valuelen)) {
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
return;
}
return;
}
}

96
lib/nghttp2_http.h Normal file
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.
*/
#ifndef NGHTTP2_HTTP_H
#define NGHTTP2_HTTP_H
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
#include "nghttp2_session.h"
#include "nghttp2_stream.h"
/*
* This function is called when HTTP header field |nv| in |frame| is
* received for |stream|. This function will validate |nv| against
* the current state of stream.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_HTTP_HEADER
* Invalid HTTP header field was received.
* NGHTTP2_ERR_IGN_HTTP_HEADER
* Invalid HTTP header field was received but it can be treated as
* if it was not received because of compatibility reasons.
*/
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
nghttp2_frame *frame, nghttp2_nv *nv, int trailer);
/*
* This function is called when request header is received. This
* function performs validation and returns 0 if it succeeds, or -1.
*/
int nghttp2_http_on_request_headers(nghttp2_stream *stream,
nghttp2_frame *frame);
/*
* This function is called when response header is received. This
* function performs validation and returns 0 if it succeeds, or -1.
*/
int nghttp2_http_on_response_headers(nghttp2_stream *stream);
/*
* This function is called trailer header (for both request and
* response) is received. This function performs validation and
* returns 0 if it succeeds, or -1.
*/
int nghttp2_http_on_trailer_headers(nghttp2_stream *stream,
nghttp2_frame *frame);
/*
* This function is called when END_STREAM flag is seen in incoming
* frame. This function performs validation and returns 0 if it
* succeeds, or -1.
*/
int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream);
/*
* This function is called when chunk of data is received. This
* function performs validation and returns 0 if it succeeds, or -1.
*/
int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n);
/*
* This function inspects header field in |frame| and records its
* method in stream->http_flags. If frame->hd.type is neither
* NGHTTP2_HEADERS nor NGHTTP2_PUSH_PROMISE, this function does
* nothing.
*/
void nghttp2_http_record_request_method(nghttp2_stream *stream,
nghttp2_frame *frame);
#endif /* NGHTTP2_HTTP_H */

View File

@@ -46,7 +46,12 @@ typedef int (*nghttp2_compar)(const void *lhs, const void *rhs);
typedef enum {
NGHTTP2_ERR_CREDENTIAL_PENDING = -101,
NGHTTP2_ERR_IGN_HEADER_BLOCK = -103,
NGHTTP2_ERR_IGN_PAYLOAD = -104
NGHTTP2_ERR_IGN_PAYLOAD = -104,
/*
* Invalid HTTP header field was received but it can be treated as
* if it was not received because of compatibility reasons.
*/
NGHTTP2_ERR_IGN_HTTP_HEADER = -105,
} nghttp2_internal_error;
#endif /* NGHTTP2_INT_H */

View File

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

View File

@@ -26,29 +26,32 @@
#include <string.h>
int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen) {
int http_selected = 0;
unsigned int i = 0;
for (; i < inlen; i += in [i] + 1) {
if (in[i] == NGHTTP2_PROTO_VERSION_ID_LEN && i + 1 + in[i] <= inlen &&
memcmp(&in[i + 1], NGHTTP2_PROTO_VERSION_ID, in[i]) == 0) {
static int select_next_protocol(unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen,
const char *key, unsigned int keylen) {
unsigned int i;
for (i = 0; i + keylen <= inlen; i += in [i] + 1) {
if (memcmp(&in[i], key, keylen) == 0) {
*out = (unsigned char *)&in[i + 1];
*outlen = in[i];
return 1;
}
if (in[i] == 8 && i + 1 + in[i] <= inlen &&
memcmp(&in[i + 1], "http/1.1", in[i]) == 0) {
http_selected = 1;
*out = (unsigned char *)&in[i + 1];
*outlen = in[i];
/* Go through to the next iteration, because "HTTP/2" may be
there */
}
}
if (http_selected) {
return 0;
} else {
}
}
return -1;
}
#define NGHTTP2_HTTP_1_1_ALPN "\x8http/1.1"
#define NGHTTP2_HTTP_1_1_ALPN_LEN (sizeof(NGHTTP2_HTTP_1_1_ALPN) - 1)
int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen) {
if (select_next_protocol(out, outlen, in, inlen, NGHTTP2_PROTO_ALPN,
NGHTTP2_PROTO_ALPN_LEN) == 0) {
return 1;
}
if (select_next_protocol(out, outlen, in, inlen, NGHTTP2_HTTP_1_1_ALPN,
NGHTTP2_HTTP_1_1_ALPN_LEN) == 0) {
return 0;
}
return -1;
}

View File

@@ -51,3 +51,8 @@ void nghttp2_option_set_recv_client_preface(nghttp2_option *option, int val) {
option->opt_set_mask |= NGHTTP2_OPT_RECV_CLIENT_PREFACE;
option->recv_client_preface = val;
}
void nghttp2_option_set_no_http_messaging(nghttp2_option *option, int val) {
option->opt_set_mask |= NGHTTP2_OPT_NO_HTTP_MESSAGING;
option->no_http_messaging = val;
}

View File

@@ -58,6 +58,7 @@ typedef enum {
*/
NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1,
NGHTTP2_OPT_RECV_CLIENT_PREFACE = 1 << 2,
NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3,
} nghttp2_option_flag;
/**
@@ -81,6 +82,10 @@ struct nghttp2_option {
* NGHTTP2_OPT_RECV_CLIENT_PREFACE
*/
uint8_t recv_client_preface;
/**
* NGHTTP2_OPT_NO_HTTP_MESSAGING
*/
uint8_t no_http_messaging;
};
#endif /* NGHTTP2_OPTION_H */

View File

@@ -44,6 +44,12 @@
typedef struct {
nghttp2_data_provider data_prd;
void *stream_user_data;
/* error code when request HEADERS is canceled by RST_STREAM while
it is in queue. */
uint32_t error_code;
/* nonzero if request HEADERS is canceled. The error code is stored
in |error_code|. */
uint8_t canceled;
/* nonzero if this item should be attached to stream object to make
it under priority control */
uint8_t attach_stream;
@@ -70,11 +76,21 @@ typedef struct {
uint8_t eof;
} nghttp2_data_aux_data;
typedef enum {
NGHTTP2_GOAWAY_AUX_NONE = 0x0,
/* indicates that session should be terminated after the
transmission of this frame. */
NGHTTP2_GOAWAY_AUX_TERM_ON_SEND = 0x1,
/* indicates that this GOAWAY is just a notification for graceful
shutdown. No nghttp2_session.goaway_flags should be updated on
the reaction to this frame. */
NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE = 0x2,
} nghttp2_goaway_aux_flag;
/* struct used for GOAWAY frame */
typedef struct {
/* nonzero if session should be terminated after the transmission of
this frame. */
int terminate_on_send;
/* bitwise-OR of one or more of nghttp2_goaway_aux_flag. */
uint8_t flags;
} nghttp2_goaway_aux_data;
/* Additional data which cannot be stored in nghttp2_frame struct */

View File

@@ -130,3 +130,17 @@ void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg) {
}
}
}
int nghttp2_pq_each(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg) {
size_t i;
if (pq->length == 0) {
return 0;
}
for (i = 0; i < pq->length; ++i) {
if ((*fun)(pq->q[i], arg)) {
return 1;
}
}
return 0;
}

View File

@@ -109,4 +109,13 @@ typedef int (*nghttp2_pq_item_cb)(void *item, void *arg);
*/
void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg);
/*
* Applys |fun| to each item in |pq|. The |arg| is passed as arg
* parameter to callback function. This function must not change the
* ordering key. If the return value from callback is nonzero, this
* function returns 1 immediately without iterating remaining items.
* Otherwise this function returns 0.
*/
int nghttp2_pq_each(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg);
#endif /* NGHTTP2_PQ_H */

File diff suppressed because it is too large Load Diff

View File

@@ -47,6 +47,7 @@
typedef enum {
NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE = 1 << 0,
NGHTTP2_OPTMASK_RECV_CLIENT_PREFACE = 1 << 1,
NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2,
} nghttp2_optmask;
typedef enum {
@@ -81,7 +82,8 @@ typedef enum {
NGHTTP2_IB_IGN_CONTINUATION,
NGHTTP2_IB_READ_PAD_DATA,
NGHTTP2_IB_READ_DATA,
NGHTTP2_IB_IGN_DATA
NGHTTP2_IB_IGN_DATA,
NGHTTP2_IB_IGN_ALL,
} nghttp2_inbound_state;
#define NGHTTP2_INBOUND_NUM_IV 7
@@ -251,6 +253,9 @@ struct nghttp2_session {
/* Unacked local SETTINGS_MAX_CONCURRENT_STREAMS value. We use this
to refuse the incoming stream if it exceeds this value. */
uint32_t pending_local_max_concurrent_stream;
/* Unacked local ENABLE_PUSH value. We use this to refuse
PUSH_PROMISE before SETTINGS ACK is received. */
uint8_t pending_enable_push;
/* Nonzero if the session is server side. */
uint8_t server;
/* Flags indicating GOAWAY is sent and/or recieved. The flags are
@@ -347,7 +352,9 @@ int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags,
/*
* Adds GOAWAY frame with the last-stream-ID |last_stream_id| and the
* error code |error_code|. This is a convenient function built on top
* of nghttp2_session_add_frame() to add GOAWAY easily.
* of nghttp2_session_add_frame() to add GOAWAY easily. The
* |aux_flags| are bitwise-OR of one or more of
* nghttp2_goaway_aux_flag.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
@@ -359,7 +366,7 @@ int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags,
*/
int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id,
uint32_t error_code, const uint8_t *opaque_data,
size_t opaque_data_len, int terminate_on_send);
size_t opaque_data_len, uint8_t aux_flags);
/*
* Adds WINDOW_UPDATE frame with stream ID |stream_id| and
@@ -398,6 +405,9 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
* is a pointer to the arbitrary user supplied data to be associated
* to this stream.
*
* If |initial_state| is NGHTTP2_STREAM_RESERVED, this function sets
* NGHTTP2_STREAM_FLAG_PUSH flag set.
*
* This function returns a pointer to created new stream object, or
* NULL.
*/

View File

@@ -68,6 +68,11 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
stream->roots = roots;
stream->root_prev = NULL;
stream->root_next = NULL;
stream->http_flags = NGHTTP2_HTTP_FLAG_NONE;
stream->content_length = -1;
stream->recv_content_length = 0;
stream->status_code = -1;
}
void nghttp2_stream_free(nghttp2_stream *stream _U_) {
@@ -114,6 +119,7 @@ static int stream_push_item(nghttp2_stream *stream, nghttp2_session *session) {
}
break;
default:
rv = 0;
/* should not reach here */
assert(0);
}
@@ -515,6 +521,7 @@ int nghttp2_stream_update_local_initial_window_size(
void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream) {
stream->state = NGHTTP2_STREAM_OPENED;
stream->flags &= ~NGHTTP2_STREAM_FLAG_PUSH;
}
nghttp2_stream *nghttp2_stream_get_dep_root(nghttp2_stream *stream) {

View File

@@ -84,7 +84,8 @@ typedef enum {
typedef enum {
NGHTTP2_STREAM_FLAG_NONE = 0,
/* Indicates that this stream is pushed stream */
/* Indicates that this stream is pushed stream and not opened
yet. */
NGHTTP2_STREAM_FLAG_PUSH = 0x01,
/* Indicates that this stream was closed */
NGHTTP2_STREAM_FLAG_CLOSED = 0x02,
@@ -98,6 +99,33 @@ typedef enum {
} nghttp2_stream_flag;
/* HTTP related flags to enforce HTTP semantics */
typedef enum {
NGHTTP2_HTTP_FLAG_NONE = 0,
/* header field seen so far */
NGHTTP2_HTTP_FLAG__AUTHORITY = 1,
NGHTTP2_HTTP_FLAG__PATH = 1 << 1,
NGHTTP2_HTTP_FLAG__METHOD = 1 << 2,
NGHTTP2_HTTP_FLAG__SCHEME = 1 << 3,
/* host is not pseudo header, but we require either host or
:authority */
NGHTTP2_HTTP_FLAG_HOST = 1 << 4,
NGHTTP2_HTTP_FLAG__STATUS = 1 << 5,
/* required header fields for HTTP request except for CONNECT
method. */
NGHTTP2_HTTP_FLAG_REQ_HEADERS = NGHTTP2_HTTP_FLAG__METHOD |
NGHTTP2_HTTP_FLAG__PATH |
NGHTTP2_HTTP_FLAG__SCHEME,
NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED = 1 << 6,
/* HTTP method flags */
NGHTTP2_HTTP_FLAG_METH_CONNECT = 1 << 7,
NGHTTP2_HTTP_FLAG_METH_HEAD = 1 << 8,
NGHTTP2_HTTP_FLAG_METH_ALL =
NGHTTP2_HTTP_FLAG_METH_CONNECT | NGHTTP2_HTTP_FLAG_METH_HEAD,
/* set if final response is expected */
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 9,
} nghttp2_http_flag;
typedef enum {
NGHTTP2_STREAM_DPRI_NONE = 0,
NGHTTP2_STREAM_DPRI_NO_ITEM = 0x01,
@@ -183,6 +211,14 @@ struct nghttp2_stream {
uint8_t flags;
/* Bitwise OR of zero or more nghttp2_shut_flag values */
uint8_t shut_flags;
/* Content-Length of request/response body. -1 if unknown. */
int64_t content_length;
/* Received body so far */
int64_t recv_content_length;
/* status code from remote server */
int16_t status_code;
/* Bitwise OR of zero or more nghttp2_http_flag values */
uint16_t http_flags;
};
void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,

View File

@@ -155,6 +155,12 @@ static int32_t submit_headers_shared_nva(nghttp2_session *session,
attach_stream);
}
int32_t nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id,
const nghttp2_nv *nva, size_t nvlen) {
return submit_headers_shared_nva(session, NGHTTP2_FLAG_END_STREAM, stream_id,
NULL, nva, nvlen, NULL, NULL, 0);
}
int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
int32_t stream_id,
const nghttp2_priority_spec *pri_spec,
@@ -236,8 +242,24 @@ int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags _U_,
int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags _U_,
int32_t last_stream_id, uint32_t error_code,
const uint8_t *opaque_data, size_t opaque_data_len) {
if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) {
return 0;
}
return nghttp2_session_add_goaway(session, last_stream_id, error_code,
opaque_data, opaque_data_len, 0);
opaque_data, opaque_data_len,
NGHTTP2_GOAWAY_AUX_NONE);
}
int nghttp2_submit_shutdown_notice(nghttp2_session *session) {
if (!session->server) {
return NGHTTP2_ERR_INVALID_STATE;
}
if (session->goaway_flags) {
return 0;
}
return nghttp2_session_add_goaway(session, (1u << 31) - 1, NGHTTP2_NO_ERROR,
NULL, 0,
NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE);
}
int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags _U_,

188
m4/libxml2.m4 Normal file
View File

@@ -0,0 +1,188 @@
# Configure paths for LIBXML2
# Mike Hommey 2004-06-19
# use CPPFLAGS instead of CFLAGS
# Toshio Kuratomi 2001-04-21
# Adapted from:
# Configure paths for GLIB
# Owen Taylor 97-11-3
dnl AM_PATH_XML2([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
dnl Test for XML, and define XML_CPPFLAGS and XML_LIBS
dnl
AC_DEFUN([AM_PATH_XML2],[
AC_ARG_WITH(xml-prefix,
[ --with-xml-prefix=PFX Prefix where libxml is installed (optional)],
xml_config_prefix="$withval", xml_config_prefix="")
AC_ARG_WITH(xml-exec-prefix,
[ --with-xml-exec-prefix=PFX Exec prefix where libxml is installed (optional)],
xml_config_exec_prefix="$withval", xml_config_exec_prefix="")
AC_ARG_ENABLE(xmltest,
[ --disable-xmltest Do not try to compile and run a test LIBXML program],,
enable_xmltest=yes)
if test x$xml_config_exec_prefix != x ; then
xml_config_args="$xml_config_args"
if test x${XML2_CONFIG+set} != xset ; then
XML2_CONFIG=$xml_config_exec_prefix/bin/xml2-config
fi
fi
if test x$xml_config_prefix != x ; then
xml_config_args="$xml_config_args --prefix=$xml_config_prefix"
if test x${XML2_CONFIG+set} != xset ; then
XML2_CONFIG=$xml_config_prefix/bin/xml2-config
fi
fi
AC_PATH_PROG(XML2_CONFIG, xml2-config, no)
min_xml_version=ifelse([$1], ,2.0.0,[$1])
AC_MSG_CHECKING(for libxml - version >= $min_xml_version)
no_xml=""
if test "$XML2_CONFIG" = "no" ; then
no_xml=yes
else
XML_CPPFLAGS=`$XML2_CONFIG $xml_config_args --cflags`
XML_LIBS=`$XML2_CONFIG $xml_config_args --libs`
xml_config_major_version=`$XML2_CONFIG $xml_config_args --version | \
sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
xml_config_minor_version=`$XML2_CONFIG $xml_config_args --version | \
sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
xml_config_micro_version=`$XML2_CONFIG $xml_config_args --version | \
sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
if test "x$enable_xmltest" = "xyes" ; then
ac_save_CPPFLAGS="$CPPFLAGS"
ac_save_LIBS="$LIBS"
CPPFLAGS="$CPPFLAGS $XML_CPPFLAGS"
LIBS="$XML_LIBS $LIBS"
dnl
dnl Now check if the installed libxml is sufficiently new.
dnl (Also sanity checks the results of xml2-config to some extent)
dnl
rm -f conf.xmltest
AC_TRY_RUN([
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libxml/xmlversion.h>
int
main()
{
int xml_major_version, xml_minor_version, xml_micro_version;
int major, minor, micro;
char *tmp_version;
system("touch conf.xmltest");
/* Capture xml2-config output via autoconf/configure variables */
/* HP/UX 9 (%@#!) writes to sscanf strings */
tmp_version = (char *)strdup("$min_xml_version");
if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
printf("%s, bad version string from xml2-config\n", "$min_xml_version");
exit(1);
}
free(tmp_version);
/* Capture the version information from the header files */
tmp_version = (char *)strdup(LIBXML_DOTTED_VERSION);
if (sscanf(tmp_version, "%d.%d.%d", &xml_major_version, &xml_minor_version, &xml_micro_version) != 3) {
printf("%s, bad version string from libxml includes\n", "LIBXML_DOTTED_VERSION");
exit(1);
}
free(tmp_version);
/* Compare xml2-config output to the libxml headers */
if ((xml_major_version != $xml_config_major_version) ||
(xml_minor_version != $xml_config_minor_version) ||
(xml_micro_version != $xml_config_micro_version))
{
printf("*** libxml header files (version %d.%d.%d) do not match\n",
xml_major_version, xml_minor_version, xml_micro_version);
printf("*** xml2-config (version %d.%d.%d)\n",
$xml_config_major_version, $xml_config_minor_version, $xml_config_micro_version);
return 1;
}
/* Compare the headers to the library to make sure we match */
/* Less than ideal -- doesn't provide us with return value feedback,
* only exits if there's a serious mismatch between header and library.
*/
LIBXML_TEST_VERSION;
/* Test that the library is greater than our minimum version */
if ((xml_major_version > major) ||
((xml_major_version == major) && (xml_minor_version > minor)) ||
((xml_major_version == major) && (xml_minor_version == minor) &&
(xml_micro_version >= micro)))
{
return 0;
}
else
{
printf("\n*** An old version of libxml (%d.%d.%d) was found.\n",
xml_major_version, xml_minor_version, xml_micro_version);
printf("*** You need a version of libxml newer than %d.%d.%d. The latest version of\n",
major, minor, micro);
printf("*** libxml is always available from ftp://ftp.xmlsoft.org.\n");
printf("***\n");
printf("*** If you have already installed a sufficiently new version, this error\n");
printf("*** probably means that the wrong copy of the xml2-config shell script is\n");
printf("*** being found. The easiest way to fix this is to remove the old version\n");
printf("*** of LIBXML, but you can also set the XML2_CONFIG environment to point to the\n");
printf("*** correct copy of xml2-config. (In this case, you will have to\n");
printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
printf("*** so that the correct libraries are found at run-time))\n");
}
return 1;
}
],, no_xml=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
CPPFLAGS="$ac_save_CPPFLAGS"
LIBS="$ac_save_LIBS"
fi
fi
if test "x$no_xml" = x ; then
AC_MSG_RESULT(yes (version $xml_config_major_version.$xml_config_minor_version.$xml_config_micro_version))
ifelse([$2], , :, [$2])
else
AC_MSG_RESULT(no)
if test "$XML2_CONFIG" = "no" ; then
echo "*** The xml2-config script installed by LIBXML could not be found"
echo "*** If libxml was installed in PREFIX, make sure PREFIX/bin is in"
echo "*** your path, or set the XML2_CONFIG environment variable to the"
echo "*** full path to xml2-config."
else
if test -f conf.xmltest ; then
:
else
echo "*** Could not run libxml test program, checking why..."
CPPFLAGS="$CPPFLAGS $XML_CPPFLAGS"
LIBS="$LIBS $XML_LIBS"
AC_TRY_LINK([
#include <libxml/xmlversion.h>
#include <stdio.h>
], [ LIBXML_TEST_VERSION; return 0;],
[ echo "*** The test program compiled, but did not run. This usually means"
echo "*** that the run-time linker is not finding LIBXML or finding the wrong"
echo "*** version of LIBXML. If it is not finding LIBXML, you'll need to set your"
echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
echo "*** to the installed location Also, make sure you have run ldconfig if that"
echo "*** is required on your system"
echo "***"
echo "*** If you have an old version installed, it is best to remove it, although"
echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ],
[ echo "*** The test program failed to compile or link. See the file config.log for the"
echo "*** exact error that occured. This usually means LIBXML was incorrectly installed"
echo "*** or that you have moved LIBXML since it was installed. In the latter case, you"
echo "*** may want to edit the xml2-config script: $XML2_CONFIG" ])
CPPFLAGS="$ac_save_CPPFLAGS"
LIBS="$ac_save_LIBS"
fi
fi
XML_CPPFLAGS=""
XML_LIBS=""
ifelse([$3], , :, [$3])
fi
AC_SUBST(XML_CPPFLAGS)
AC_SUBST(XML_LIBS)
rm -f conf.xmltest
])

7
makebashcompletion Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/sh -e
BCPATH=doc/bash_completion
for prog in nghttp nghttpd nghttpx h2load; do
$BCPATH/make_bash_completion.py src/$prog > $BCPATH/$prog
done

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)
if frame.hd.type == cnghttp2.NGHTTP2_HEADERS:
if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_REQUEST:
return 0
handler = _get_stream_user_data(session, frame.hd.stream_id)
elif frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE:
handler = _get_stream_user_data(session, frame.push_promise.promised_stream_id)
@@ -400,12 +398,6 @@ cdef int server_on_frame_recv(cnghttp2.nghttp2_session *session,
handler = _get_stream_user_data(session, frame.hd.stream_id)
if not handler:
return 0
# Check required header fields. We expect that :authority
# or host header field.
if handler.scheme is None or handler.method is None or\
handler.host is None or handler.path is None:
return http2._rst_stream(frame.hd.stream_id,
cnghttp2.NGHTTP2_PROTOCOL_ERROR)
if handler.cookies:
handler.headers.append((b'cookie',
b'; '.join(handler.cookies)))
@@ -551,15 +543,12 @@ cdef int client_on_frame_recv(cnghttp2.nghttp2_session *session,
sys.stderr.write(traceback.format_exc())
return http2._rst_stream(frame.hd.stream_id)
elif frame.hd.type == cnghttp2.NGHTTP2_HEADERS:
if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_RESPONSE:
if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_RESPONSE or frame.headers.cat == cnghttp2.NGHTTP2_HCAT_PUSH_RESPONSE:
handler = _get_stream_user_data(session, frame.hd.stream_id)
if not handler:
return 0
# Check required header fields. We expect a status.
if handler.status is None:
return http2._rst_stream(frame.hd.stream_id,
cnghttp2.NGHTTP2_PROTOCOL_ERROR)
# TODO handle 1xx non-final response
if handler.cookies:
handler.headers.append((b'cookie',
b'; '.join(handler.cookies)))
@@ -587,10 +576,6 @@ cdef int client_on_frame_recv(cnghttp2.nghttp2_session *session,
cnghttp2.nghttp2_session_set_stream_user_data(session, frame.push_promise.promised_stream_id,
<void*>NULL)
if push_handler.scheme is None or push_handler.method is None or\
push_handler.host is None or push_handler.path is None:
return http2._rst_stream(frame.push_promise.promised_stream_id,
cnghttp2.NGHTTP2_PROTOCOL_ERROR)
try:
handler.on_push_promise(push_handler)
except:
@@ -983,7 +968,6 @@ cdef class _HTTP2ClientSessionCore(_HTTP2SessionCoreBase):
handler.method = push_promise.method
handler.host = push_promise.host
handler.path = push_promise.path
handler.headers = push_promise.headers
handler.cookies = push_promise.cookies
handler.stream_id = push_promise.stream_id
handler.http2 = self
@@ -1046,6 +1030,9 @@ if asyncio:
path
This is a value of :path header field.
headers
Request header fields
"""
def __init__(self, http2, stream_id):
@@ -1358,6 +1345,11 @@ if asyncio:
path
This is a value of :path header field.
headers
Response header fields. There is a special exception. If this
object is passed to push_promise(), this instance variable contains
pushed request header fields.
"""
def __init__(self, http2=None, stream_id=-1):

View File

@@ -31,6 +31,7 @@
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <cassert>
#include <set>
@@ -47,6 +48,7 @@
#include "http2.h"
#include "util.h"
#include "ssl.h"
#include "template.h"
#ifndef O_BINARY
#define O_BINARY (0)
@@ -76,7 +78,7 @@ void print_session_id(int64_t id) { std::cout << "[id=" << id << "] "; }
} // namespace
namespace {
void append_nv(Stream *stream, const std::vector<nghttp2_nv> &nva) {
template <typename Array> void append_nv(Stream *stream, const Array &nva) {
for (size_t i = 0; i < nva.size(); ++i) {
auto &nv = nva[i];
auto token = http2::lookup_token(nv.name, nv.namelen);
@@ -84,7 +86,7 @@ void append_nv(Stream *stream, const std::vector<nghttp2_nv> &nva) {
http2::index_header(stream->hdidx, token, i);
}
http2::add_header(stream->headers, nv.name, nv.namelen, nv.value,
nv.valuelen, nv.flags & NGHTTP2_NV_FLAG_NO_INDEX);
nv.valuelen, nv.flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
}
}
} // namespace
@@ -171,7 +173,8 @@ class Sessions {
public:
Sessions(struct ev_loop *loop, const Config *config, SSL_CTX *ssl_ctx)
: loop_(loop), config_(config), ssl_ctx_(ssl_ctx), callbacks_(nullptr),
next_session_id_(1) {
next_session_id_(1), tstamp_cached_(ev_now(loop)),
cached_date_(util::http_date(tstamp_cached_)) {
nghttp2_session_callbacks_new(&callbacks_);
fill_callback(callbacks_, config_);
@@ -223,7 +226,7 @@ public:
}
}
auto handler =
util::make_unique<Http2Handler>(this, fd, ssl, get_next_session_id());
make_unique<Http2Handler>(this, fd, ssl, get_next_session_id());
handler->setup_bev();
if (!ssl) {
if (handler->on_connect() != 0) {
@@ -232,17 +235,25 @@ public:
}
add_handler(handler.release());
}
void update_cached_date() { cached_date_ = util::http_date(time(nullptr)); }
const std::string &get_cached_date() const { return cached_date_; }
void update_cached_date() { cached_date_ = util::http_date(tstamp_cached_); }
const std::string &get_cached_date() {
auto t = ev_now(loop_);
if (t != tstamp_cached_) {
tstamp_cached_ = t;
update_cached_date();
}
return cached_date_;
}
private:
std::set<Http2Handler *> handlers_;
std::string cached_date_;
struct ev_loop *loop_;
const Config *config_;
SSL_CTX *ssl_ctx_;
nghttp2_session_callbacks *callbacks_;
int64_t next_session_id_;
ev_tstamp tstamp_cached_;
std::string cached_date_;
};
Stream::Stream(Http2Handler *handler, int32_t stream_id)
@@ -400,11 +411,11 @@ int Http2Handler::fill_wb() {
int Http2Handler::read_clear() {
int rv;
uint8_t buf[8192];
std::array<uint8_t, 8192> buf;
for (;;) {
ssize_t nread;
while ((nread = read(fd_, buf, sizeof(buf))) == -1 && errno == EINTR)
while ((nread = read(fd_, buf.data(), buf.size())) == -1 && errno == EINTR)
;
if (nread == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
@@ -415,10 +426,12 @@ int Http2Handler::read_clear() {
if (nread == 0) {
return -1;
}
rv = nghttp2_session_mem_recv(session_, buf, nread);
rv = nghttp2_session_mem_recv(session_, buf.data(), nread);
if (rv < 0) {
if (rv != NGHTTP2_ERR_BAD_PREFACE) {
std::cerr << "nghttp2_session_mem_recv() returned error: "
<< nghttp2_strerror(rv) << std::endl;
}
return -1;
}
}
@@ -430,11 +443,9 @@ int Http2Handler::write_clear() {
auto loop = sessions_->get_loop();
for (;;) {
if (wb_.rleft() > 0) {
struct iovec iov[2];
auto iovcnt = wb_.riovec(iov);
ssize_t nwrite;
while ((nwrite = writev(fd_, iov, iovcnt)) == -1 && errno == EINTR)
while ((nwrite = write(fd_, wb_.pos, wb_.rleft())) == -1 &&
errno == EINTR)
;
if (nwrite == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
@@ -512,12 +523,12 @@ int Http2Handler::tls_handshake() {
}
int Http2Handler::read_tls() {
uint8_t buf[8192];
std::array<uint8_t, 8192> buf;
ERR_clear_error();
for (;;) {
auto rv = SSL_read(ssl_, buf, sizeof(buf));
auto rv = SSL_read(ssl_, buf.data(), buf.size());
if (rv == 0) {
return -1;
@@ -537,10 +548,12 @@ int Http2Handler::read_tls() {
}
auto nread = rv;
rv = nghttp2_session_mem_recv(session_, buf, nread);
rv = nghttp2_session_mem_recv(session_, buf.data(), nread);
if (rv < 0) {
if (rv != NGHTTP2_ERR_BAD_PREFACE) {
std::cerr << "nghttp2_session_mem_recv() returned error: "
<< nghttp2_strerror(rv) << std::endl;
}
return -1;
}
}
@@ -556,11 +569,7 @@ int Http2Handler::write_tls() {
for (;;) {
if (wb_.rleft() > 0) {
const void *p;
size_t len;
std::tie(p, len) = wb_.get();
auto rv = SSL_write(ssl_, p, len);
auto rv = SSL_write(ssl_, wb_.pos, wb_.rleft());
if (rv == 0) {
return -1;
@@ -618,7 +627,7 @@ int Http2Handler::on_connect() {
if (r != 0) {
return r;
}
nghttp2_settings_entry entry[4];
std::array<nghttp2_settings_entry, 4> entry;
size_t niv = 1;
entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
@@ -629,7 +638,7 @@ int Http2Handler::on_connect() {
entry[niv].value = sessions_->get_config()->header_table_size;
++niv;
}
r = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry, niv);
r = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), niv);
if (r != 0) {
return r;
}
@@ -676,30 +685,39 @@ int Http2Handler::submit_file_response(const std::string &status,
nghttp2_data_provider *data_prd) {
std::string content_length = util::utos(file_length);
std::string last_modified_str;
nghttp2_nv nva[] = {
http2::make_nv_ls(":status", status),
auto nva = make_array(http2::make_nv_ls(":status", status),
http2::make_nv_ls("server", NGHTTPD_SERVER),
http2::make_nv_ls("content-length", content_length),
http2::make_nv_ll("cache-control", "max-age=3600"),
http2::make_nv_ls("date", sessions_->get_cached_date()),
http2::make_nv_ll("", ""),
};
http2::make_nv_ll("", ""), http2::make_nv_ll("", ""));
size_t nvlen = 5;
if (last_modified != 0) {
last_modified_str = util::http_date(last_modified);
nva[nvlen++] = http2::make_nv_ls("last-modified", last_modified_str);
}
return nghttp2_submit_response(session_, stream->stream_id, nva, nvlen,
auto &trailer = get_config()->trailer;
std::string trailer_names;
if (!trailer.empty()) {
trailer_names = trailer[0].name;
for (size_t i = 1; i < trailer.size(); ++i) {
trailer_names += ", ";
trailer_names += trailer[i].name;
}
nva[nvlen++] = http2::make_nv_ls("trailer", trailer_names);
}
return nghttp2_submit_response(session_, stream->stream_id, nva.data(), nvlen,
data_prd);
}
int Http2Handler::submit_response(const std::string &status, int32_t stream_id,
const Headers &headers,
nghttp2_data_provider *data_prd) {
auto nva = std::vector<nghttp2_nv>{
http2::make_nv_ls(":status", status),
http2::make_nv_ls("server", NGHTTPD_SERVER),
http2::make_nv_ls("date", sessions_->get_cached_date())};
auto nva = std::vector<nghttp2_nv>();
nva.reserve(3 + headers.size());
nva.push_back(http2::make_nv_ls(":status", status));
nva.push_back(http2::make_nv_ls("server", NGHTTPD_SERVER));
nva.push_back(http2::make_nv_ls("date", sessions_->get_cached_date()));
for (auto &nv : headers) {
nva.push_back(http2::make_nv(nv.name, nv.value, nv.no_index));
}
@@ -710,16 +728,15 @@ int Http2Handler::submit_response(const std::string &status, int32_t stream_id,
int Http2Handler::submit_response(const std::string &status, int32_t stream_id,
nghttp2_data_provider *data_prd) {
auto nva =
std::vector<nghttp2_nv>{http2::make_nv_ls(":status", status),
http2::make_nv_ls("server", NGHTTPD_SERVER)};
auto nva = make_array(http2::make_nv_ls(":status", status),
http2::make_nv_ls("server", NGHTTPD_SERVER));
return nghttp2_submit_response(session_, stream_id, nva.data(), nva.size(),
data_prd);
}
int Http2Handler::submit_non_final_response(const std::string &status,
int32_t stream_id) {
auto nva = std::vector<nghttp2_nv>{http2::make_nv_ls(":status", status)};
auto nva = make_array(http2::make_nv_ls(":status", status));
return nghttp2_submit_headers(session_, NGHTTP2_FLAG_NONE, stream_id, nullptr,
nva.data(), nva.size(), nullptr);
}
@@ -734,12 +751,12 @@ int Http2Handler::submit_push_promise(Stream *stream,
http2::get_header(stream->hdidx, http2::HD_HOST, stream->headers);
}
auto nva = std::vector<nghttp2_nv>{
http2::make_nv_ll(":method", "GET"),
auto nva =
make_array(http2::make_nv_ll(":method", "GET"),
http2::make_nv_ls(":path", push_path),
get_config()->no_tls ? http2::make_nv_ll(":scheme", "http")
: http2::make_nv_ll(":scheme", "https"),
http2::make_nv_ls(":authority", authority->value)};
http2::make_nv_ls(":authority", authority->value));
auto promised_stream_id = nghttp2_submit_push_promise(
session_, NGHTTP2_FLAG_END_HEADERS, stream->stream_id, nva.data(),
@@ -749,7 +766,7 @@ int Http2Handler::submit_push_promise(Stream *stream,
return promised_stream_id;
}
auto promised_stream = util::make_unique<Stream>(this, promised_stream_id);
auto promised_stream = make_unique<Stream>(this, promised_stream_id);
append_nv(promised_stream.get(), nva);
add_stream(promised_stream_id, std::move(promised_stream));
@@ -802,6 +819,7 @@ void Http2Handler::terminate_session(uint32_t error_code) {
ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
uint8_t *buf, size_t length, uint32_t *data_flags,
nghttp2_data_source *source, void *user_data) {
int rv;
auto hd = static_cast<Http2Handler *>(user_data);
auto stream = hd->get_stream(stream_id);
@@ -822,6 +840,23 @@ ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
if (nread == 0 || stream->body_left <= 0) {
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
auto config = hd->get_config();
if (!config->trailer.empty()) {
std::vector<nghttp2_nv> nva;
nva.reserve(config->trailer.size());
for (auto &kv : config->trailer) {
nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index));
}
rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size());
if (rv != 0) {
if (nghttp2_is_fatal(rv)) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
} else {
*data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
}
}
if (nghttp2_session_get_stream_remote_close(session, stream_id) == 0) {
remove_stream_read_timeout(stream);
remove_stream_write_timeout(stream);
@@ -1022,32 +1057,12 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
if (!stream) {
return 0;
}
if (!http2::check_nv(name, namelen, value, valuelen)) {
return 0;
}
auto token = http2::lookup_token(name, namelen);
if (name[0] == ':') {
if ((!stream->headers.empty() &&
stream->headers.back().name.c_str()[0] != ':') ||
!http2::check_http2_request_pseudo_header(stream->hdidx, token)) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
}
if (!http2::http2_header_allowed(token)) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
http2::index_header(stream->hdidx, token, stream->headers.size());
http2::add_header(stream->headers, name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX);
flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
return 0;
}
} // namespace
@@ -1062,7 +1077,7 @@ int on_begin_headers_callback(nghttp2_session *session,
return 0;
}
auto stream = util::make_unique<Stream>(hd, frame->hd.stream_id);
auto stream = make_unique<Stream>(hd, frame->hd.stream_id);
add_stream_read_timeout(stream.get());
@@ -1107,15 +1122,10 @@ int hd_on_frame_recv_callback(nghttp2_session *session,
if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
if (!http2::http2_mandatory_request_headers_presence(stream->hdidx)) {
hd->submit_rst_stream(stream, NGHTTP2_PROTOCOL_ERROR);
return 0;
}
auto expect100 =
http2::get_header(stream->hdidx, http2::HD_EXPECT, stream->headers);
if (expect100 && util::strieq("100-continue", expect100->value.c_str())) {
if (expect100 && util::strieq_l("100-continue", expect100->value)) {
hd->submit_non_final_response("100", frame->hd.stream_id);
}
@@ -1140,11 +1150,6 @@ int hd_on_frame_recv_callback(nghttp2_session *session,
hd->remove_settings_timer();
}
break;
case NGHTTP2_PUSH_PROMISE:
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
frame->push_promise.promised_stream_id,
NGHTTP2_REFUSED_STREAM);
break;
default:
break;
}
@@ -1309,21 +1314,9 @@ void worker_acceptcb(struct ev_loop *loop, ev_async *w, int revents) {
}
} // namespace
namespace {
void refresh_cb(struct ev_loop *loop, ev_timer *w, int revents) {
auto sessions = static_cast<Sessions *>(w->data);
sessions->update_cached_date();
}
} // namespace
namespace {
void run_worker(Worker *worker) {
auto loop = worker->sessions->get_loop();
ev_timer w;
ev_timer_init(&w, refresh_cb, 0., 1.);
w.data = worker->sessions.get();
ev_timer_again(loop, &w);
worker->sessions->update_cached_date();
ev_run(loop, 0);
}
@@ -1340,10 +1333,10 @@ public:
if (config_->verbose) {
std::cerr << "spawning thread #" << i << std::endl;
}
auto worker = util::make_unique<Worker>();
auto worker = make_unique<Worker>();
auto loop = ev_loop_new(0);
worker->sessions =
util::make_unique<Sessions>(loop, config_, sessions_->get_ssl_ctx());
make_unique<Sessions>(loop, config_, sessions_->get_ssl_ctx());
ev_async_init(&worker->w, worker_acceptcb);
worker->w.data = worker.get();
ev_async_start(loop, &worker->w);
@@ -1404,11 +1397,8 @@ public:
auto fd = accept(fd_, nullptr, nullptr);
#endif // !HAVE_ACCEPT4
if (fd == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
break;
}
continue;
}
#ifndef HAVE_ACCEPT4
util::make_socket_nonblocking(fd);
#endif // !HAVE_ACCEPT4
@@ -1456,6 +1446,7 @@ int start_listen(struct ev_loop *loop, Sessions *sessions,
addrinfo hints;
int r;
bool ok = false;
const char *addr = nullptr;
auto acceptor = std::make_shared<AcceptHandler>(sessions, config);
auto service = util::utos(config->port);
@@ -1468,12 +1459,17 @@ int start_listen(struct ev_loop *loop, Sessions *sessions,
hints.ai_flags |= AI_ADDRCONFIG;
#endif // AI_ADDRCONFIG
if (!config->address.empty()) {
addr = config->address.c_str();
}
addrinfo *res, *rp;
r = getaddrinfo(nullptr, service.c_str(), &hints, &res);
r = getaddrinfo(addr, service.c_str(), &hints, &res);
if (r != 0) {
std::cerr << "getaddrinfo() failed: " << gai_strerror(r) << std::endl;
return -1;
}
for (rp = res; rp; rp = rp->ai_next) {
int fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (fd == -1) {
@@ -1499,8 +1495,9 @@ int start_listen(struct ev_loop *loop, Sessions *sessions,
new ListenEventHandler(sessions, fd, acceptor);
if (config->verbose) {
std::cout << (rp->ai_family == AF_INET ? "IPv4" : "IPv6")
<< ": listen on port " << config->port << std::endl;
std::string s = util::numeric_name(rp->ai_addr, rp->ai_addrlen);
std::cout << (rp->ai_family == AF_INET ? "IPv4" : "IPv6") << ": listen "
<< s << ":" << config->port << std::endl;
}
ok = true;
continue;
@@ -1652,14 +1649,6 @@ int HttpServer::run() {
return -1;
}
ev_timer refresh_timer;
ev_timer_init(&refresh_timer, refresh_cb, 0., 1.);
refresh_timer.data = &sessions;
if (config_->num_worker == 1) {
ev_timer_again(loop, &refresh_timer);
sessions.update_cached_date();
}
ev_run(loop, 0);
return 0;
}

View File

@@ -44,17 +44,19 @@
#include <nghttp2/nghttp2.h>
#include "http2.h"
#include "ringbuf.h"
#include "buffer.h"
namespace nghttp2 {
struct Config {
std::map<std::string, std::vector<std::string>> push;
Headers trailer;
std::string htdocs;
std::string host;
std::string private_key_file;
std::string cert_file;
std::string dh_param_file;
std::string address;
ev_tstamp stream_read_timeout;
ev_tstamp stream_write_timeout;
nghttp2_option *session_option;
@@ -83,7 +85,7 @@ struct Stream {
int64_t body_left;
int32_t stream_id;
int file;
int hdidx[http2::HD_MAXIDX];
http2::HeaderIndex hdidx;
Stream(Http2Handler *handler, int32_t stream_id);
~Stream();
};
@@ -142,7 +144,7 @@ private:
ev_io rev_;
ev_timer settings_timerev_;
std::map<int32_t, std::unique_ptr<Stream>> id2stream_;
RingBuf<65536> wb_;
Buffer<65536> wb_;
std::function<int(Http2Handler &)> read_, write_;
int64_t session_id_;
nghttp2_session *session_;

View File

@@ -41,7 +41,9 @@ AM_CPPFLAGS = \
@JANSSON_CFLAGS@ \
@ZLIB_CFLAGS@ \
@DEFS@
AM_LDFLAGS = \
LDADD = $(top_builddir)/lib/libnghttp2.la \
$(top_builddir)/third-party/libhttp-parser.la \
@JEMALLOC_LIBS@ \
@LIBSPDYLAY_LIBS@ \
@XML_LIBS@ \
@@ -51,10 +53,6 @@ AM_LDFLAGS = \
@ZLIB_LIBS@ \
@APPLDFLAGS@
LDADD = \
$(top_builddir)/lib/libnghttp2.la \
$(top_builddir)/third-party/libhttp-parser.la
if ENABLE_APP
bin_PROGRAMS += nghttp nghttpd nghttpx
@@ -78,8 +76,7 @@ nghttp_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttp.cc nghttp.h \
nghttpd_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttpd.cc \
ssl.cc ssl.h \
HttpServer.cc HttpServer.h \
ringbuf.h
HttpServer.cc HttpServer.h
bin_PROGRAMS += h2load
@@ -117,11 +114,12 @@ NGHTTPX_SRCS = \
shrpx_io_control.cc shrpx_io_control.h \
shrpx_ssl.cc shrpx_ssl.h \
shrpx_worker.cc shrpx_worker.h \
shrpx_worker_config.cc shrpx_worker_config.h \
shrpx_log_config.cc shrpx_log_config.h \
shrpx_connect_blocker.cc shrpx_connect_blocker.h \
shrpx_downstream_connection_pool.cc shrpx_downstream_connection_pool.h \
shrpx_rate_limit.cc shrpx_rate_limit.h \
ringbuf.h memchunk.h
shrpx_connection.cc shrpx_connection.h \
buffer.h memchunk.h template.h
if HAVE_SPDYLAY
NGHTTPX_SRCS += shrpx_spdy_upstream.cc shrpx_spdy_upstream.h
@@ -131,8 +129,7 @@ noinst_LIBRARIES = libnghttpx.a
libnghttpx_a_SOURCES = ${NGHTTPX_SRCS}
nghttpx_SOURCES = shrpx.cc shrpx.h
nghttpx_LDFLAGS =
nghttpx_LDADD = libnghttpx.a ${LDADD} ${AM_LDFLAGS}
nghttpx_LDADD = libnghttpx.a ${LDADD}
if HAVE_CUNIT
check_PROGRAMS += nghttpx-unittest
@@ -144,13 +141,11 @@ nghttpx_unittest_SOURCES = shrpx-unittest.cc \
util_test.cc util_test.h \
nghttp2_gzip_test.c nghttp2_gzip_test.h \
nghttp2_gzip.c nghttp2_gzip.h \
ringbuf_test.cc ringbuf_test.h \
buffer_test.cc buffer_test.h \
memchunk_test.cc memchunk_test.h
nghttpx_unittest_CPPFLAGS = ${AM_CPPFLAGS}\
-DNGHTTP2_TESTS_DIR=\"$(top_srcdir)/tests\"
nghttpx_unittest_LDFLAGS = ${AM_LDFLAGS} \
@CUNIT_LIBS@ @TESTLDFLAGS@
nghttpx_unittest_LDADD = libnghttpx.a ${LDADD}
nghttpx_unittest_LDADD = libnghttpx.a ${LDADD} @CUNIT_LIBS@ @TESTLDADD@
TESTS += nghttpx-unittest
endif # HAVE_CUNIT
@@ -178,23 +173,43 @@ DISTCLEANFILES = $(pkgconfig_DATA)
lib_LTLIBRARIES = libnghttp2_asio.la
libnghttp2_asio_la_SOURCES = \
asio_connection.h \
asio_server.cc asio_server.h \
asio_io_service_pool.cc asio_io_service_pool.h \
asio_http2_handler.cc asio_http2_handler.h \
asio_http2_impl.cc asio_http2_impl.h \
util.cc util.h http2.cc http2.h \
ssl.cc ssl.h
ssl.cc ssl.h \
asio_common.cc asio_common.h \
asio_io_service_pool.cc asio_io_service_pool.h \
asio_server_http2.cc \
asio_server_http2_impl.cc asio_server_http2_impl.h \
asio_server.cc asio_server.h \
asio_server_http2_handler.cc asio_server_http2_handler.h \
asio_server_connection.h \
asio_server_request.cc \
asio_server_request_impl.cc asio_server_request_impl.h \
asio_server_response.cc \
asio_server_response_impl.cc asio_server_response_impl.h \
asio_server_stream.cc asio_server_stream.h \
asio_server_serve_mux.cc asio_server_serve_mux.h \
asio_server_request_handler.cc asio_server_request_handler.h \
asio_server_tls_context.cc asio_server_tls_context.h \
asio_client_session.cc \
asio_client_session_impl.cc asio_client_session_impl.h \
asio_client_session_tcp_impl.cc asio_client_session_tcp_impl.h \
asio_client_session_tls_impl.cc asio_client_session_tls_impl.h \
asio_client_response.cc \
asio_client_response_impl.cc asio_client_response_impl.h \
asio_client_request.cc \
asio_client_request_impl.cc asio_client_request_impl.h \
asio_client_stream.cc asio_client_stream.h \
asio_client_tls_context.cc asio_client_tls_context.h
libnghttp2_asio_la_CPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS}
libnghttp2_asio_la_LDFLAGS = \
libnghttp2_asio_la_LDFLAGS = -no-undefined -version-info 1:0:0
libnghttp2_asio_la_LIBADD = \
$(top_builddir)/lib/libnghttp2.la \
$(top_builddir)/third-party/libhttp-parser.la \
${BOOST_LDFLAGS} \
${BOOST_ASIO_LIB} \
${BOOST_THREAD_LIB} \
${BOOST_SYSTEM_LIB} \
@OPENSSL_LIBS@ \
-no-undefined \
-version-info 0:0:0
libnghttp2_asio_la_LIBADD = $(top_builddir)/lib/libnghttp2.la
@OPENSSL_LIBS@
endif # ENABLE_ASIO_LIB

View File

@@ -408,8 +408,11 @@ int verbose_on_header_callback(nghttp2_session *session,
namelen, valuelen};
print_timer();
fprintf(outfile, " recv (stream_id=%d, noind=%d) ", frame->hd.stream_id,
(flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0);
fprintf(outfile, " recv (stream_id=%d", frame->hd.stream_id);
if (flags & NGHTTP2_NV_FLAG_NO_INDEX) {
fprintf(outfile, ", sensitive");
}
fprintf(outfile, ") ");
print_nv(&nv);
fflush(outfile);

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

@@ -1,7 +1,7 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2014 Tatsuhiro Tsujikawa
* Copyright (c) 2015 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -22,17 +22,11 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "shrpx_worker_config.h"
#ifndef ASIO_CLIENT_TLS_CONTEXT_H
#define ASIO_CLIENT_TLS_CONTEXT_H
namespace shrpx {
#include "nghttp2_config.h"
WorkerConfig::WorkerConfig()
: accesslog_fd(-1), errorlog_fd(-1), errorlog_tty(false),
graceful_shutdown(false) {}
#include <nghttp2/asio_http2_client.h>
#ifndef NOTHREADS
thread_local
#endif // NOTHREADS
WorkerConfig *worker_config = new WorkerConfig();
} // namespace shrpx
#endif // ASIO_CLIENT_TLS_CONTEXT_H

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

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(buf_, buf_ + buflen_, std::begin(buffer));
len += buflen_;
buf_ = nullptr;
buflen_ = 0;
}
for (;;) {
const uint8_t *data;
auto nread = nghttp2_session_mem_send(session_, &data);
if (nread < 0) {
return -1;
}
if (nread == 0) {
break;
}
if (len + nread > buffer.size()) {
buf_ = data;
buflen_ = nread;
break;
}
std::copy(data, data + nread, std::begin(buffer) + len);
len += nread;
}
return 0;
}
private:
std::map<int32_t, std::shared_ptr<http2_stream>> streams_;
connection_write writefun_;
request_cb request_cb_;
boost::asio::io_service &io_service_;
boost::asio::io_service &task_io_service_;
std::shared_ptr<boost::asio::io_service::strand> strand_;
nghttp2_session *session_;
const uint8_t *buf_;
std::size_t buflen_;
bool inside_callback_;
};
} // namespace server
} // namespace asio_http2
} // namespace nghttp
#endif // HTTP2_HANDLER_H

View File

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

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