mirror of
https://github.com/nghttp2/nghttp2.git
synced 2025-12-08 02:58:53 +08:00
Compare commits
180 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7fdcbb3d6 | ||
|
|
de2c2ad65c | ||
|
|
862a0ee66b | ||
|
|
c2510a01a5 | ||
|
|
dc85623060 | ||
|
|
8afbb6ca26 | ||
|
|
ed79637737 | ||
|
|
faa2c4467a | ||
|
|
c87fae463e | ||
|
|
29b4b11e78 | ||
|
|
3b24be3bcd | ||
|
|
ece8289aaf | ||
|
|
4042ff0fc4 | ||
|
|
d3d6c5e314 | ||
|
|
125e32eb56 | ||
|
|
94bf8dcd4e | ||
|
|
89b8039466 | ||
|
|
72843b33d0 | ||
|
|
03e699e013 | ||
|
|
fcf99fa8fc | ||
|
|
661fb2eb0e | ||
|
|
42496d638b | ||
|
|
c0a6a0a6d1 | ||
|
|
b7bda783c5 | ||
|
|
6893608ae2 | ||
|
|
ef913bc929 | ||
|
|
a5ed70bcfe | ||
|
|
9bf2ca6916 | ||
|
|
57729d0a23 | ||
|
|
e86b81ec10 | ||
|
|
076eefbed6 | ||
|
|
58a735dc68 | ||
|
|
948d4d43d5 | ||
|
|
ef581e94bb | ||
|
|
ad84af2b2b | ||
|
|
0e65e1254d | ||
|
|
08ec5b3fc0 | ||
|
|
8c491d5917 | ||
|
|
4219fe7822 | ||
|
|
d4eb2b2c75 | ||
|
|
8ea26fddfd | ||
|
|
98add63cdf | ||
|
|
7b90404072 | ||
|
|
de0543f684 | ||
|
|
46e3be7b5b | ||
|
|
7c675b1505 | ||
|
|
d287ea986f | ||
|
|
399328cb49 | ||
|
|
d46e50b112 | ||
|
|
0f87cedc2d | ||
|
|
d34095cf49 | ||
|
|
6039bacb1b | ||
|
|
4877f72a75 | ||
|
|
274b3a2296 | ||
|
|
93013f4205 | ||
|
|
5789987ca3 | ||
|
|
a0524ef05d | ||
|
|
0e3ae63965 | ||
|
|
3e14261ebf | ||
|
|
446de923f3 | ||
|
|
c5860fc6f4 | ||
|
|
6b714030dd | ||
|
|
8483225839 | ||
|
|
585af93828 | ||
|
|
41e266181e | ||
|
|
6b7e6166f9 | ||
|
|
2a4f347dbc | ||
|
|
bff5a28d12 | ||
|
|
5c31c130bd | ||
|
|
b9d6fff962 | ||
|
|
9ffbc45ba6 | ||
|
|
961f41d7ce | ||
|
|
928d3e5f3f | ||
|
|
42eeebc7f6 | ||
|
|
991baf9e69 | ||
|
|
6ad63a06b0 | ||
|
|
60c2fe5a2e | ||
|
|
41b5077330 | ||
|
|
d10dc4bb9b | ||
|
|
c4b487be05 | ||
|
|
c976a0fcd1 | ||
|
|
5b5034f19c | ||
|
|
dd9e829ee1 | ||
|
|
80a7523b49 | ||
|
|
4c55a2340b | ||
|
|
2f2a535113 | ||
|
|
505a300d93 | ||
|
|
4236fa603f | ||
|
|
196d7da53f | ||
|
|
6824319fe6 | ||
|
|
66d00954a5 | ||
|
|
76eb3193ab | ||
|
|
66f5438dc9 | ||
|
|
45164b6761 | ||
|
|
ceefddd332 | ||
|
|
a043230371 | ||
|
|
c2f49fa478 | ||
|
|
fe17a20f84 | ||
|
|
773b108eeb | ||
|
|
36a9cbf41f | ||
|
|
f8182333b4 | ||
|
|
c3265de625 | ||
|
|
b24bd3d8cb | ||
|
|
fa21b95274 | ||
|
|
5dccc88a7c | ||
|
|
2cadd38b6b | ||
|
|
d7cfe464a2 | ||
|
|
018e9eaf6d | ||
|
|
94ca9705ef | ||
|
|
d6f810d91a | ||
|
|
d8cf29c202 | ||
|
|
bf46539d10 | ||
|
|
2ed47cdd19 | ||
|
|
a3f79232c6 | ||
|
|
421bf85808 | ||
|
|
05a847b6b8 | ||
|
|
1e3afe0646 | ||
|
|
38788d707b | ||
|
|
44c0d32a1b | ||
|
|
36aae8c310 | ||
|
|
34ac90443f | ||
|
|
992c14e354 | ||
|
|
529fc937dc | ||
|
|
42c174e803 | ||
|
|
05b8d8c5b7 | ||
|
|
a1c937a007 | ||
|
|
8baec366f0 | ||
|
|
c64bb62ffe | ||
|
|
6020b727d8 | ||
|
|
7f04968950 | ||
|
|
ca1f43dfca | ||
|
|
9efb62f40b | ||
|
|
f1c7d3edfd | ||
|
|
7ff38eeb2e | ||
|
|
e4ce595ebb | ||
|
|
8accf3898a | ||
|
|
690a1622aa | ||
|
|
aaef798566 | ||
|
|
0753fcd6e5 | ||
|
|
a14029744c | ||
|
|
7e46d0b142 | ||
|
|
726e6c087d | ||
|
|
ff0eaf8399 | ||
|
|
7fb7575f78 | ||
|
|
44642af227 | ||
|
|
e7d052100c | ||
|
|
fc2170e488 | ||
|
|
0cda2282dd | ||
|
|
566baab307 | ||
|
|
6753b6d61e | ||
|
|
35817876fe | ||
|
|
7d753d779e | ||
|
|
f6f908a541 | ||
|
|
b0c1986a46 | ||
|
|
9671eaa850 | ||
|
|
062b42918c | ||
|
|
2fa28e790d | ||
|
|
76eab3faa0 | ||
|
|
70ea774f23 | ||
|
|
e15d302985 | ||
|
|
b2196f215a | ||
|
|
8afd75ca47 | ||
|
|
0676535caf | ||
|
|
dd741a58ae | ||
|
|
26304546c4 | ||
|
|
838fb33892 | ||
|
|
7f802b623d | ||
|
|
c2426bc732 | ||
|
|
08d0cdfd31 | ||
|
|
1fd44b1567 | ||
|
|
a2a9f15307 | ||
|
|
291c27c940 | ||
|
|
f86c11f07d | ||
|
|
dbd0f032ce | ||
|
|
6a2e6b744f | ||
|
|
4de8db523f | ||
|
|
cf5ac0f363 | ||
|
|
185ebd7b79 | ||
|
|
38153e0f6e | ||
|
|
f36f3ae1fa |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -42,6 +42,8 @@ doc/tutorial-hpack.rst
|
|||||||
doc/python-apiref.rst
|
doc/python-apiref.rst
|
||||||
doc/building-android-binary.rst
|
doc/building-android-binary.rst
|
||||||
doc/asio_http2.h.rst
|
doc/asio_http2.h.rst
|
||||||
|
doc/asio_http2_server.h.rst
|
||||||
|
doc/asio_http2_client.h.rst
|
||||||
doc/libnghttp2_asio.rst
|
doc/libnghttp2_asio.rst
|
||||||
doc/contribute.rst
|
doc/contribute.rst
|
||||||
python/setup.py
|
python/setup.py
|
||||||
|
|||||||
316
README.rst
316
README.rst
@@ -1,30 +1,30 @@
|
|||||||
nghttp2 - HTTP/2 C Library
|
nghttp2 - HTTP/2 C Library
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
This is an implementation of Hypertext Transfer Protocol version 2
|
This is an implementation of the Hypertext Transfer Protocol version 2
|
||||||
in C.
|
in C.
|
||||||
|
|
||||||
The framing layer of HTTP/2 is implemented as a form of reusable C
|
The framing layer of HTTP/2 is implemented as a reusable C
|
||||||
library. On top of that, we have implemented HTTP/2 client, server
|
library. On top of that, we have implemented an HTTP/2 client, server
|
||||||
and proxy. We have also developed load test and benchmarking tool for
|
and proxy. We have also developed load test and benchmarking tools for
|
||||||
HTTP/2 and SPDY.
|
HTTP/2 and SPDY.
|
||||||
|
|
||||||
HPACK encoder and decoder are available as public API.
|
An HPACK encoder and decoder are available as a public API.
|
||||||
|
|
||||||
The experimental high level C++ library is also available.
|
An experimental high level C++ library is also available.
|
||||||
|
|
||||||
We have Python binding of this libary, but we have not covered
|
We have Python bindings of this libary, but we do not have full
|
||||||
everything yet.
|
code coverage yet.
|
||||||
|
|
||||||
Development Status
|
Development Status
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
We started to implement h2-14
|
We started to implement h2-14
|
||||||
(http://tools.ietf.org/html/draft-ietf-httpbis-http2-14), the header
|
(http://tools.ietf.org/html/draft-ietf-httpbis-http2-14), and header
|
||||||
compression
|
compression
|
||||||
(http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09).
|
(http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09).
|
||||||
|
|
||||||
The nghttp2 code base was forked from spdylay project.
|
The nghttp2 code base was forked from the spdylay project.
|
||||||
|
|
||||||
=========================== =======
|
=========================== =======
|
||||||
HTTP/2 Features Support
|
HTTP/2 Features Support
|
||||||
@@ -37,14 +37,14 @@ Large header (CONTINUATION) Yes
|
|||||||
Public Test Server
|
Public Test Server
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
The following endpoints are available to try out nghttp2
|
The following endpoints are available to try out our nghttp2
|
||||||
implementation.
|
implementation.
|
||||||
|
|
||||||
* https://nghttp2.org/ (TLS + ALPN/NPN)
|
* https://nghttp2.org/ (TLS + ALPN/NPN)
|
||||||
|
|
||||||
NPN offer ``h2-14``, ``spdy/3.1`` and ``http/1.1``.
|
This endpoint supports ``h2``, ``h2-16``, ``h2-14``, ``spdy/3.1``
|
||||||
|
and ``http/1.1`` via ALPN/NPN and requires TLSv1.2 for HTTP/2
|
||||||
This endpoint requires TLSv1.2 for HTTP/2 connection.
|
connection.
|
||||||
|
|
||||||
* http://nghttp2.org/ (Upgrade / Direct)
|
* http://nghttp2.org/ (Upgrade / Direct)
|
||||||
|
|
||||||
@@ -67,16 +67,16 @@ To build the documentation, you need to install:
|
|||||||
* sphinx (http://sphinx-doc.org/)
|
* sphinx (http://sphinx-doc.org/)
|
||||||
|
|
||||||
To build and run the application programs (``nghttp``, ``nghttpd`` and
|
To build and run the application programs (``nghttp``, ``nghttpd`` and
|
||||||
``nghttpx``) in ``src`` directory, the following packages are
|
``nghttpx``) in the ``src`` directory, the following packages are
|
||||||
required:
|
required:
|
||||||
|
|
||||||
* OpenSSL >= 1.0.1
|
* OpenSSL >= 1.0.1
|
||||||
* libev >= 4.15
|
* libev >= 4.15
|
||||||
* zlib >= 1.2.3
|
* zlib >= 1.2.3
|
||||||
|
|
||||||
ALPN support requires unreleased version OpenSSL >= 1.0.2.
|
ALPN support requires OpenSSL >= 1.0.2 (released 22 January 2015).
|
||||||
|
|
||||||
To enable SPDY protocol in the application program ``nghttpx`` and
|
To enable the SPDY protocol in the application program ``nghttpx`` and
|
||||||
``h2load``, the following package is required:
|
``h2load``, the following package is required:
|
||||||
|
|
||||||
* spdylay >= 1.3.0
|
* spdylay >= 1.3.0
|
||||||
@@ -90,7 +90,7 @@ The HPACK tools require the following package:
|
|||||||
|
|
||||||
* jansson >= 2.5
|
* jansson >= 2.5
|
||||||
|
|
||||||
To build sources under examples directory, libevent is required:
|
To build sources under the examples directory, libevent is required:
|
||||||
|
|
||||||
* libevent-openssl >= 2.0.8
|
* libevent-openssl >= 2.0.8
|
||||||
|
|
||||||
@@ -109,16 +109,17 @@ The Python bindings require the following packages:
|
|||||||
* cython >= 0.19
|
* cython >= 0.19
|
||||||
* python >= 2.7
|
* python >= 2.7
|
||||||
|
|
||||||
If you are using Ubuntu 14.04 LTS, you need the following packages
|
If you are using Ubuntu 14.04 LTS (trusty), run the following to install the needed packages::
|
||||||
installed::
|
|
||||||
|
|
||||||
apt-get install make binutils autoconf automake autotools-dev libtool pkg-config zlib1g-dev libcunit1-dev libssl-dev libxml2-dev libev-dev libevent-dev libjansson-dev libjemalloc-dev cython python3.4-dev
|
sudo apt-get install make binutils autoconf automake autotools-dev libtool pkg-config \
|
||||||
|
zlib1g-dev libcunit1-dev libssl-dev libxml2-dev libev-dev libevent-dev libjansson-dev \
|
||||||
|
libjemalloc-dev cython python3.4-dev
|
||||||
|
|
||||||
spdylay is not packaged in Ubuntu, so you need to build it yourself:
|
spdylay is not packaged in Ubuntu, so you need to build it yourself:
|
||||||
http://tatsuhiro-t.github.io/spdylay/
|
http://tatsuhiro-t.github.io/spdylay/
|
||||||
|
|
||||||
Build from git
|
Building from git
|
||||||
--------------
|
-----------------
|
||||||
|
|
||||||
Building from git is easy, but please be sure that at least autoconf 2.68 is
|
Building from git is easy, but please be sure that at least autoconf 2.68 is
|
||||||
used::
|
used::
|
||||||
@@ -129,23 +130,23 @@ used::
|
|||||||
$ ./configure
|
$ ./configure
|
||||||
$ make
|
$ make
|
||||||
|
|
||||||
To compile source code, gcc >= 4.8.3 or clang >= 3.4 is required.
|
To compile the source code, gcc >= 4.8.3 or clang >= 3.4 is required.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
Mac OS X users may need ``--disable-threads`` configure option to
|
Mac OS X users may need the ``--disable-threads`` configure option to
|
||||||
disable multi threading in nghttpd, nghttpx and h2load to prevent
|
disable multi-threading in nghttpd, nghttpx and h2load to prevent
|
||||||
them from crashing. Patch is welcome to make multi threading work
|
them from crashing. A patch is welcome to make multi threading work
|
||||||
on Mac OS X platform.
|
on Mac OS X platform.
|
||||||
|
|
||||||
Building documentation
|
Building the documentation
|
||||||
----------------------
|
--------------------------
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
Documentation is still incomplete.
|
Documentation is still incomplete.
|
||||||
|
|
||||||
To build documentation, run::
|
To build the documentation, run::
|
||||||
|
|
||||||
$ make html
|
$ make html
|
||||||
|
|
||||||
@@ -164,8 +165,8 @@ Unit tests are done by simply running `make check`.
|
|||||||
Integration tests
|
Integration tests
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
We have the integration tests for nghttpx proxy server. The tests are
|
We have the integration tests for the nghttpx proxy server. The tests are
|
||||||
written in `Go programming language <http://golang.org/>`_ and uses
|
written in the `Go programming language <http://golang.org/>`_ and uses
|
||||||
its testing framework. We depend on the following libraries:
|
its testing framework. We depend on the following libraries:
|
||||||
|
|
||||||
* https://github.com/bradfitz/http2
|
* https://github.com/bradfitz/http2
|
||||||
@@ -182,12 +183,12 @@ To run the tests, run the following command under
|
|||||||
|
|
||||||
$ make it
|
$ make it
|
||||||
|
|
||||||
Inside the tests, we use port 3009 to run test subject server.
|
Inside the tests, we use port 3009 to run the test subject server.
|
||||||
|
|
||||||
Client, Server and Proxy programs
|
Client, Server and Proxy programs
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
The src directory contains HTTP/2 client, server and proxy programs.
|
The ``src`` directory contains the HTTP/2 client, server and proxy programs.
|
||||||
|
|
||||||
nghttp - client
|
nghttp - client
|
||||||
+++++++++++++++
|
+++++++++++++++
|
||||||
@@ -334,7 +335,7 @@ The HTTP Upgrade is performed like this::
|
|||||||
[ 0.038] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
|
[ 0.038] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
|
||||||
(last_stream_id=0, error_code=NO_ERROR(0), opaque_data(0)=[])
|
(last_stream_id=0, error_code=NO_ERROR(0), opaque_data(0)=[])
|
||||||
|
|
||||||
With ``-s`` option, ``nghttp`` prints out some timing information for
|
Using the ``-s`` option, ``nghttp`` prints out some timing information for
|
||||||
requests, sorted by completion time::
|
requests, sorted by completion time::
|
||||||
|
|
||||||
$ nghttp -nas https://nghttp2.org/
|
$ nghttp -nas https://nghttp2.org/
|
||||||
@@ -360,8 +361,8 @@ requests, sorted by completion time::
|
|||||||
+76.14ms +11.17ms 64.97ms 200 171K /images/posts/with-pri-blog.png
|
+76.14ms +11.17ms 64.97ms 200 171K /images/posts/with-pri-blog.png
|
||||||
+88.52ms +11.17ms 77.36ms 200 174K /images/posts/without-pri-blog.png
|
+88.52ms +11.17ms 77.36ms 200 174K /images/posts/without-pri-blog.png
|
||||||
|
|
||||||
With ``-r`` option, ``nghttp`` writes more detailed timing data to
|
Using the ``-r`` option, ``nghttp`` writes more detailed timing data to
|
||||||
given file in HAR format.
|
the given file in HAR format.
|
||||||
|
|
||||||
nghttpd - server
|
nghttpd - server
|
||||||
++++++++++++++++
|
++++++++++++++++
|
||||||
@@ -371,13 +372,13 @@ nghttpd - server
|
|||||||
By default, it uses SSL/TLS connection. Use ``--no-tls`` option to
|
By default, it uses SSL/TLS connection. Use ``--no-tls`` option to
|
||||||
disable it.
|
disable it.
|
||||||
|
|
||||||
``nghttpd`` only accepts the HTTP/2 connection via NPN/ALPN or direct
|
``nghttpd`` only accepts HTTP/2 connections via NPN/ALPN or direct
|
||||||
HTTP/2 connection. No HTTP Upgrade is supported.
|
HTTP/2 connections. No HTTP Upgrade is supported.
|
||||||
|
|
||||||
``-p`` option allows users to configure server push.
|
The ``-p`` option allows users to configure server push.
|
||||||
|
|
||||||
Just like ``nghttp``, it has verbose output mode for framing
|
Just like ``nghttp``, it has a verbose output mode for framing
|
||||||
information. Here is sample output from ``nghttpd`` server::
|
information. Here is sample output from ``nghttpd``::
|
||||||
|
|
||||||
$ nghttpd --no-tls -v 8080
|
$ nghttpd --no-tls -v 8080
|
||||||
IPv4: listen on port 8080
|
IPv4: listen on port 8080
|
||||||
@@ -431,8 +432,8 @@ nghttpx - proxy
|
|||||||
+++++++++++++++
|
+++++++++++++++
|
||||||
|
|
||||||
``nghttpx`` is a multi-threaded reverse proxy for ``h2-14``, SPDY and
|
``nghttpx`` is a multi-threaded reverse proxy for ``h2-14``, SPDY and
|
||||||
HTTP/1.1 and powers nghttp2.org site and supports HTTP/2 server push.
|
HTTP/1.1, and powers http://nghttp2.org and supports HTTP/2 server push.
|
||||||
It has several operation modes:
|
It has several operational modes:
|
||||||
|
|
||||||
================== ============================ ============== =============
|
================== ============================ ============== =============
|
||||||
Mode option Frontend Backend Note
|
Mode option Frontend Backend Note
|
||||||
@@ -446,23 +447,21 @@ default mode HTTP/2, SPDY, HTTP/1.1 (TLS) HTTP/1.1 Reverse proxy
|
|||||||
|
|
||||||
The interesting mode at the moment is the default mode. It works like
|
The interesting mode at the moment is the default mode. It works like
|
||||||
a reverse proxy and listens for ``h2-14``, SPDY and HTTP/1.1 and can
|
a reverse proxy and listens for ``h2-14``, SPDY and HTTP/1.1 and can
|
||||||
be deployed SSL/TLS terminator for existing web server.
|
be deployed as a SSL/TLS terminator for existing web server.
|
||||||
|
|
||||||
The default mode, ``--http2-proxy`` and ``--http2-bridge`` modes use
|
The default mode, ``--http2-proxy`` and ``--http2-bridge`` modes use
|
||||||
SSL/TLS in the frontend connection by default. To disable SSL/TLS,
|
SSL/TLS in the frontend connection by default. To disable SSL/TLS,
|
||||||
use ``--frontend-no-tls`` option. If that option is used, SPDY is
|
use the ``--frontend-no-tls`` option. If that option is used, SPDY is
|
||||||
disabled in the frontend and incoming HTTP/1.1 connection can be
|
disabled in the frontend and incoming HTTP/1.1 connections can be
|
||||||
upgraded to HTTP/2 through HTTP Upgrade.
|
upgraded to HTTP/2 through HTTP Upgrade.
|
||||||
|
|
||||||
The ``--http2-bridge``, ``--client`` and ``--client-proxy`` modes use
|
The ``--http2-bridge``, ``--client`` and ``--client-proxy`` modes use
|
||||||
SSL/TLS in the backend connection by deafult. To disable SSL/TLS, use
|
SSL/TLS in the backend connection by deafult. To disable SSL/TLS, use
|
||||||
``--backend-no-tls`` option.
|
the ``--backend-no-tls`` option.
|
||||||
|
|
||||||
``nghttpx`` supports configuration file. See ``--conf`` option and
|
``nghttpx`` supports a configuration file. See the ``--conf`` option and
|
||||||
sample configuration file ``nghttpx.conf.sample``.
|
sample configuration file ``nghttpx.conf.sample``.
|
||||||
|
|
||||||
``nghttpx`` does not support server push.
|
|
||||||
|
|
||||||
In the default mode, (without any of ``--http2-proxy``,
|
In the default mode, (without any of ``--http2-proxy``,
|
||||||
``--http2-bridge``, ``--client-proxy`` and ``--client`` options),
|
``--http2-bridge``, ``--client-proxy`` and ``--client`` options),
|
||||||
``nghttpx`` works as reverse proxy to the backend server::
|
``nghttpx`` works as reverse proxy to the backend server::
|
||||||
@@ -470,18 +469,18 @@ In the default mode, (without any of ``--http2-proxy``,
|
|||||||
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Web Server
|
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Web Server
|
||||||
[reverse proxy]
|
[reverse proxy]
|
||||||
|
|
||||||
With ``--http2-proxy`` option, it works as so called secure proxy (aka
|
With the ``--http2-proxy`` option, it works as a so called secure proxy (aka
|
||||||
SPDY proxy)::
|
SPDY proxy)::
|
||||||
|
|
||||||
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy
|
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy
|
||||||
[secure proxy] (e.g., Squid, ATS)
|
[secure proxy] (e.g., Squid, ATS)
|
||||||
|
|
||||||
The ``Client`` in the above needs to be configured to use
|
The ``Client`` in the above example needs to be configured to use
|
||||||
``nghttpx`` as secure proxy.
|
``nghttpx`` as secure proxy.
|
||||||
|
|
||||||
At the time of this writing, Chrome is the only browser which supports
|
At the time of this writing, Chrome is the only browser which supports
|
||||||
secure proxy. The one way to configure Chrome to use secure proxy is
|
secure proxy. One way to configure Chrome to use a secure proxy is
|
||||||
create proxy.pac script like this:
|
to create a proxy.pac script like this:
|
||||||
|
|
||||||
.. code-block:: javascript
|
.. code-block:: javascript
|
||||||
|
|
||||||
@@ -490,7 +489,7 @@ create proxy.pac script like this:
|
|||||||
}
|
}
|
||||||
|
|
||||||
``SERVERADDR`` and ``PORT`` is the hostname/address and port of the
|
``SERVERADDR`` and ``PORT`` is the hostname/address and port of the
|
||||||
machine nghttpx is running. Please note that Chrome requires valid
|
machine nghttpx is running on. Please note that Chrome requires a valid
|
||||||
certificate for secure proxy.
|
certificate for secure proxy.
|
||||||
|
|
||||||
Then run Chrome with the following arguments::
|
Then run Chrome with the following arguments::
|
||||||
@@ -498,24 +497,24 @@ Then run Chrome with the following arguments::
|
|||||||
$ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn
|
$ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn
|
||||||
|
|
||||||
With ``--http2-bridge``, it accepts HTTP/2, SPDY and HTTP/1.1
|
With ``--http2-bridge``, it accepts HTTP/2, SPDY and HTTP/1.1
|
||||||
connections and communicates with backend in HTTP/2::
|
connections and communicates with the backend in HTTP/2::
|
||||||
|
|
||||||
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/2) --> Web or HTTP/2 Proxy etc
|
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/2) --> Web or HTTP/2 Proxy etc
|
||||||
(e.g., nghttpx -s)
|
(e.g., nghttpx -s)
|
||||||
|
|
||||||
With ``--client-proxy`` option, it works as forward proxy and expects
|
With ``--client-proxy``, it works as a forward proxy and expects
|
||||||
that the backend is HTTP/2 proxy::
|
that the backend is an HTTP/2 proxy::
|
||||||
|
|
||||||
Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) --> HTTP/2 Proxy
|
Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) --> HTTP/2 Proxy
|
||||||
[forward proxy] (e.g., nghttpx -s)
|
[forward proxy] (e.g., nghttpx -s)
|
||||||
|
|
||||||
The ``Client`` needs to be configured to use nghttpx as forward
|
The ``Client`` needs to be configured to use nghttpx as a forward
|
||||||
proxy. The frontend HTTP/1.1 connection can be upgraded to HTTP/2
|
proxy. The frontend HTTP/1.1 connection can be upgraded to HTTP/2
|
||||||
through HTTP Upgrade. With the above configuration, one can use
|
through HTTP Upgrade. With the above configuration, one can use
|
||||||
HTTP/1.1 client to access and test their HTTP/2 servers.
|
HTTP/1.1 client to access and test their HTTP/2 servers.
|
||||||
|
|
||||||
With ``--client`` option, it works as reverse proxy and expects that
|
With ``--client``, it works as a reverse proxy and expects that
|
||||||
the backend is HTTP/2 Web server::
|
the backend is an HTTP/2 Web server::
|
||||||
|
|
||||||
Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) --> Web Server
|
Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) --> Web Server
|
||||||
[reverse proxy]
|
[reverse proxy]
|
||||||
@@ -524,11 +523,11 @@ The frontend HTTP/1.1 connection can be upgraded to HTTP/2
|
|||||||
through HTTP Upgrade.
|
through HTTP Upgrade.
|
||||||
|
|
||||||
For the operation modes which talk to the backend in HTTP/2 over
|
For the operation modes which talk to the backend in HTTP/2 over
|
||||||
SSL/TLS, the backend connections can be tunneled through HTTP proxy.
|
SSL/TLS, the backend connections can be tunneled through an HTTP proxy.
|
||||||
The proxy is specified using ``--backend-http-proxy-uri`` option. The
|
The proxy is specified using ``--backend-http-proxy-uri``. The
|
||||||
following figure illustrates the example of ``--http2-bridge`` and
|
following figure illustrates the example of the ``--http2-bridge`` and
|
||||||
``--backend-http-proxy-uri`` options to talk to the outside HTTP/2
|
``--backend-http-proxy-uri`` options to talk to the outside HTTP/2
|
||||||
proxy through HTTP proxy::
|
proxy through an HTTP proxy::
|
||||||
|
|
||||||
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/2) --
|
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/2) --
|
||||||
|
|
||||||
@@ -539,7 +538,7 @@ Benchmarking tool
|
|||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
The ``h2load`` program is a benchmarking tool for HTTP/2 and SPDY.
|
The ``h2load`` program is a benchmarking tool for HTTP/2 and SPDY.
|
||||||
The SPDY support is enabled if the program was built with spdylay
|
The SPDY support is enabled if the program was built with the spdylay
|
||||||
library. The UI of ``h2load`` is heavily inspired by ``weighttp``
|
library. The UI of ``h2load`` is heavily inspired by ``weighttp``
|
||||||
(https://github.com/lighttpd/weighttp). The typical usage is as
|
(https://github.com/lighttpd/weighttp). The typical usage is as
|
||||||
follows::
|
follows::
|
||||||
@@ -567,38 +566,38 @@ follows::
|
|||||||
min max mean sd +/- sd
|
min max mean sd +/- sd
|
||||||
time for request: 283.86ms 1.46s 659.70ms 150.87ms 84.68%
|
time for request: 283.86ms 1.46s 659.70ms 150.87ms 84.68%
|
||||||
|
|
||||||
The above example issued total 100000 requests, using 100 concurrent
|
The above example issued total 100,000 requests, using 100 concurrent
|
||||||
clients (in other words, 100 HTTP/2 sessions), and maximum 100 streams
|
clients (in other words, 100 HTTP/2 sessions), and a maximum of 100 streams
|
||||||
per client. With ``-t`` option, ``h2load`` will use multiple native
|
per client. With the ``-t`` option, ``h2load`` will use multiple native
|
||||||
threads to avoid saturating single core on client side.
|
threads to avoid saturating a single core on client side.
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
**Don't use this tool against publicly available servers.** That is
|
**Don't use this tool against publicly available servers.** That is
|
||||||
considered a DOS attack. Please only use against your private
|
considered a DOS attack. Please only use it against your private
|
||||||
servers.
|
servers.
|
||||||
|
|
||||||
HPACK tools
|
HPACK tools
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
The ``src`` directory contains HPACK tools. The ``deflatehd`` is a
|
The ``src`` directory contains the HPACK tools. The ``deflatehd`` program is a
|
||||||
command-line header compression tool. The ``inflatehd`` is
|
command-line header compression tool. The ``inflatehd`` program is a
|
||||||
command-line header decompression tool. Both tools read input from
|
command-line header decompression tool. Both tools read input from
|
||||||
stdin and write output to stdout. The errors are written to stderr.
|
stdin and write output to stdout. Errors are written to stderr.
|
||||||
They take JSON as input and output. We use (mostly) same JSON data
|
They take JSON as input and output. We (mostly) use the same JSON data
|
||||||
format described at https://github.com/http2jp/hpack-test-case
|
format described at https://github.com/http2jp/hpack-test-case.
|
||||||
|
|
||||||
deflatehd - header compressor
|
deflatehd - header compressor
|
||||||
+++++++++++++++++++++++++++++
|
+++++++++++++++++++++++++++++
|
||||||
|
|
||||||
The ``deflatehd`` reads JSON data or HTTP/1-style header fields from
|
The ``deflatehd`` program reads JSON data or HTTP/1-style header fields from
|
||||||
stdin and outputs compressed header block in JSON.
|
stdin and outputs compressed header block in JSON.
|
||||||
|
|
||||||
For the JSON input, the root JSON object must include ``cases`` key.
|
For the JSON input, the root JSON object must include a ``cases`` key.
|
||||||
Its value has to include the sequence of input header set. They share
|
Its value has to include the sequence of input header set. They share
|
||||||
the same compression context and are processed in the order they
|
the same compression context and are processed in the order they
|
||||||
appear. Each item in the sequence is a JSON object and it must
|
appear. Each item in the sequence is a JSON object and it must
|
||||||
include ``headers`` key. Its value is an array of a JSON object,
|
include a ``headers`` key. Its value is an array of JSON objects,
|
||||||
which includes exactly one name/value pair.
|
which includes exactly one name/value pair.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
@@ -624,8 +623,8 @@ Example:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
With ``-t`` option, the program can accept more familiar HTTP/1 style
|
With the ``-t`` option, the program can accept more familiar HTTP/1 style
|
||||||
header field block. Each header set is delimited by empty line:
|
header field blocks. Each header set is delimited by an empty line:
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
@@ -636,29 +635,29 @@ Example::
|
|||||||
:method: POST
|
:method: POST
|
||||||
user-agent: nghttp2
|
user-agent: nghttp2
|
||||||
|
|
||||||
The output is JSON object. It should include ``cases`` key and its
|
The output is in JSON object. It should include a ``cases`` key and its
|
||||||
value is an array of JSON object, which has at least following keys:
|
value is an array of JSON objects, which has at least the following keys:
|
||||||
|
|
||||||
seq
|
seq
|
||||||
The index of header set in the input.
|
The index of header set in the input.
|
||||||
|
|
||||||
input_length
|
input_length
|
||||||
The sum of length of name/value pair in the input.
|
The sum of the length of the name/value pairs in the input.
|
||||||
|
|
||||||
output_length
|
output_length
|
||||||
The length of compressed header block.
|
The length of the compressed header block.
|
||||||
|
|
||||||
percentage_of_original_size
|
percentage_of_original_size
|
||||||
``input_length`` / ``output_length`` * 100
|
``input_length`` / ``output_length`` * 100
|
||||||
|
|
||||||
wire
|
wire
|
||||||
The compressed header block in hex string.
|
The compressed header block as a hex string.
|
||||||
|
|
||||||
headers
|
headers
|
||||||
The input header set.
|
The input header set.
|
||||||
|
|
||||||
header_table_size
|
header_table_size
|
||||||
The header table size adjusted before deflating header set.
|
The header table size adjusted before deflating the header set.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
@@ -725,7 +724,7 @@ Examples:
|
|||||||
The output can be used as the input for ``inflatehd`` and
|
The output can be used as the input for ``inflatehd`` and
|
||||||
``deflatehd``.
|
``deflatehd``.
|
||||||
|
|
||||||
With ``-d`` option, the extra ``header_table`` key is added and its
|
With the ``-d`` option, the extra ``header_table`` key is added and its
|
||||||
associated value includes the state of dynamic header table after the
|
associated value includes the state of dynamic header table after the
|
||||||
corresponding header set was processed. The value includes at least
|
corresponding header set was processed. The value includes at least
|
||||||
the following keys:
|
the following keys:
|
||||||
@@ -749,8 +748,8 @@ deflate_size
|
|||||||
``max_deflate_size``.
|
``max_deflate_size``.
|
||||||
|
|
||||||
max_deflate_size
|
max_deflate_size
|
||||||
The maximum header table size encoder uses. This can be smaller
|
The maximum header table size the encoder uses. This can be smaller
|
||||||
than ``max_size``. In this case, encoder only uses up to first
|
than ``max_size``. In this case, the encoder only uses up to first
|
||||||
``max_deflate_size`` buffer. Since the header table size is still
|
``max_deflate_size`` buffer. Since the header table size is still
|
||||||
``max_size``, the encoder has to keep track of entries ouside the
|
``max_size``, the encoder has to keep track of entries ouside the
|
||||||
``max_deflate_size`` but inside the ``max_size`` and make sure
|
``max_deflate_size`` but inside the ``max_size`` and make sure
|
||||||
@@ -913,14 +912,14 @@ Example:
|
|||||||
inflatehd - header decompressor
|
inflatehd - header decompressor
|
||||||
+++++++++++++++++++++++++++++++
|
+++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
The ``inflatehd`` reads JSON data from stdin and outputs decompressed
|
The ``inflatehd`` program reads JSON data from stdin and outputs decompressed
|
||||||
name/value pairs in JSON.
|
name/value pairs in JSON.
|
||||||
|
|
||||||
The root JSON object must include ``cases`` key. Its value has to
|
The root JSON object must include the ``cases`` key. Its value has to
|
||||||
include the sequence of compressed header block. They share the same
|
include the sequence of compressed header blocks. They share the same
|
||||||
compression context and are processed in the order they appear. Each
|
compression context and are processed in the order they appear. Each
|
||||||
item in the sequence is a JSON object and it must have at least
|
item in the sequence is a JSON object and it must have at least a
|
||||||
``wire`` key. Its value is a compressed header block in hex string.
|
``wire`` key. Its value is a compressed header block as a hex string.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@@ -934,17 +933,17 @@ Example:
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
The output is JSON object. It should include ``cases`` key and its
|
The output is a JSON object. It should include a ``cases`` key and its
|
||||||
value is an array of JSON object, which has at least following keys:
|
value is an array of JSON objects, which has at least following keys:
|
||||||
|
|
||||||
seq
|
seq
|
||||||
The index of header set in the input.
|
The index of the header set in the input.
|
||||||
|
|
||||||
headers
|
headers
|
||||||
The JSON array includes decompressed name/value pairs.
|
A JSON array that includes decompressed name/value pairs.
|
||||||
|
|
||||||
wire
|
wire
|
||||||
The compressed header block in hex string.
|
The compressed header block as a hex string.
|
||||||
|
|
||||||
header_table_size
|
header_table_size
|
||||||
The header table size adjusted before inflating compressed header
|
The header table size adjusted before inflating compressed header
|
||||||
@@ -1008,8 +1007,8 @@ Example:
|
|||||||
The output can be used as the input for ``deflatehd`` and
|
The output can be used as the input for ``deflatehd`` and
|
||||||
``inflatehd``.
|
``inflatehd``.
|
||||||
|
|
||||||
With ``-d`` option, the extra ``header_table`` key is added and its
|
With the ``-d`` option, the extra ``header_table`` key is added and its
|
||||||
associated value includes the state of dynamic header table after the
|
associated value includes the state of the dynamic header table after the
|
||||||
corresponding header set was processed. The format is the same as
|
corresponding header set was processed. The format is the same as
|
||||||
``deflatehd``.
|
``deflatehd``.
|
||||||
|
|
||||||
@@ -1018,10 +1017,10 @@ libnghttp2_asio: High level HTTP/2 C++ library
|
|||||||
|
|
||||||
libnghttp2_asio is C++ library built on top of libnghttp2 and provides
|
libnghttp2_asio is C++ library built on top of libnghttp2 and provides
|
||||||
high level abstraction API to build HTTP/2 applications. It depends
|
high level abstraction API to build HTTP/2 applications. It depends
|
||||||
on Boost::ASIO library and OpenSSL. Currently libnghttp2_asio
|
on the Boost::ASIO library and OpenSSL. Currently libnghttp2_asio
|
||||||
provides server side API.
|
provides both client and server APIs.
|
||||||
|
|
||||||
libnghttp2_asio is not built by default. Use ``--enable-asio-lib``
|
libnghttp2_asio is not built by default. Use the ``--enable-asio-lib``
|
||||||
configure flag to build libnghttp2_asio. The required Boost libraries
|
configure flag to build libnghttp2_asio. The required Boost libraries
|
||||||
are:
|
are:
|
||||||
|
|
||||||
@@ -1029,25 +1028,81 @@ are:
|
|||||||
* Boost::System
|
* Boost::System
|
||||||
* Boost::Thread
|
* Boost::Thread
|
||||||
|
|
||||||
Server API is designed to build HTTP/2 server very easily to utilize
|
The server API is designed to build an HTTP/2 server very easily to utilize
|
||||||
C++11 anonymous function and closure. The bare minimum example of
|
C++11 anonymous functions and closures. The bare minimum example of
|
||||||
HTTP/2 server looks like this:
|
an HTTP/2 server looks like this:
|
||||||
|
|
||||||
.. code-block:: cpp
|
.. code-block:: cpp
|
||||||
|
|
||||||
#include <nghttp2/asio_http2.h>
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
using namespace nghttp2::asio_http2;
|
using namespace nghttp2::asio_http2;
|
||||||
using namespace nghttp2::asio_http2::server;
|
using namespace nghttp2::asio_http2::server;
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
boost::system::error_code ec;
|
||||||
http2 server;
|
http2 server;
|
||||||
|
|
||||||
server.listen("*", 3000, [](const std::shared_ptr<request> &req,
|
server.handle("/", [](const request &req, const response &res) {
|
||||||
const std::shared_ptr<response> &res) {
|
res.write_head(200);
|
||||||
res->write_head(200);
|
res.end("hello, world\n");
|
||||||
res->end("hello, world");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (server.listen_and_serve(ec, "localhost", "3000")) {
|
||||||
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Here is sample code to use the client API:
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2_client.h>
|
||||||
|
|
||||||
|
using boost::asio::ip::tcp;
|
||||||
|
|
||||||
|
using namespace nghttp2::asio_http2;
|
||||||
|
using namespace nghttp2::asio_http2::client;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
boost::system::error_code ec;
|
||||||
|
boost::asio::io_service io_service;
|
||||||
|
|
||||||
|
// connect to localhost:3000
|
||||||
|
session sess(io_service, "localhost", "3000");
|
||||||
|
|
||||||
|
sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
|
||||||
|
boost::system::error_code ec;
|
||||||
|
|
||||||
|
auto req = sess.submit(ec, "GET", "http://localhost:3000/");
|
||||||
|
|
||||||
|
req->on_response([](const response &res) {
|
||||||
|
// print status code and response header fields.
|
||||||
|
std::cerr << "HTTP/2 " << res.status_code() << std::endl;
|
||||||
|
for (auto &kv : res.header()) {
|
||||||
|
std::cerr << kv.first << ": " << kv.second.value << "\n";
|
||||||
|
}
|
||||||
|
std::cerr << std::endl;
|
||||||
|
|
||||||
|
res.on_data([](const uint8_t *data, std::size_t len) {
|
||||||
|
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||||
|
std::cerr << std::endl;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
req->on_close([&sess](uint32_t error_code) {
|
||||||
|
// shutdown session after first request was done.
|
||||||
|
sess.shutdown();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
sess.on_error([](const boost::system::error_code &ec) {
|
||||||
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
|
});
|
||||||
|
|
||||||
|
io_service.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
For more details, see the documentation of libnghttp2_asio.
|
For more details, see the documentation of libnghttp2_asio.
|
||||||
@@ -1055,19 +1110,19 @@ For more details, see the documentation of libnghttp2_asio.
|
|||||||
Python bindings
|
Python bindings
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
This ``python`` directory contains nghttp2 Python bindings. The
|
The ``python`` directory contains nghttp2 Python bindings. The
|
||||||
bindings currently provide HPACK compressor and decompressor classes
|
bindings currently provide HPACK compressor and decompressor classes
|
||||||
and HTTP/2 server.
|
and an HTTP/2 server.
|
||||||
|
|
||||||
The extension module is called ``nghttp2``.
|
The extension module is called ``nghttp2``.
|
||||||
|
|
||||||
``make`` will build the bindings and target Python version is
|
``make`` will build the bindings and target Python version is
|
||||||
determined by configure script. If the detected Python version is not
|
determined by the ``configure`` script. If the detected Python version is not
|
||||||
what you expect, specify a path to Python executable in ``PYTHON``
|
what you expect, specify a path to Python executable in a ``PYTHON``
|
||||||
variable as an argument to configure script (e.g., ``./configure
|
variable as an argument to configure script (e.g., ``./configure
|
||||||
PYTHON=/usr/bin/python3.4``).
|
PYTHON=/usr/bin/python3.4``).
|
||||||
|
|
||||||
The following example code illustrates basic usage of HPACK compressor
|
The following example code illustrates basic usage of the HPACK compressor
|
||||||
and decompressor in Python:
|
and decompressor in Python:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
@@ -1094,21 +1149,21 @@ By default, it does nothing. It must be subclassed to handle each
|
|||||||
event callback method.
|
event callback method.
|
||||||
|
|
||||||
The first callback method invoked is ``on_headers()``. It is called
|
The first callback method invoked is ``on_headers()``. It is called
|
||||||
when HEADERS frame, which includes request header fields, has arrived.
|
when HEADERS frame, which includes the request header fields, has arrived.
|
||||||
|
|
||||||
If request has request body, ``on_data(data)`` is invoked for each
|
If the request has a request body, ``on_data(data)`` is invoked for each
|
||||||
chunk of received data.
|
chunk of received data.
|
||||||
|
|
||||||
When whole request is received, ``on_request_done()`` is invoked.
|
Once the entire request is received, ``on_request_done()`` is invoked.
|
||||||
|
|
||||||
When stream is closed, ``on_close(error_code)`` is called.
|
When the stream is closed, ``on_close(error_code)`` is called.
|
||||||
|
|
||||||
The application can send response using ``send_response()`` method.
|
The application can send a response using ``send_response()`` method.
|
||||||
It can be used in ``on_headers()``, ``on_data()`` or
|
It can be used in ``on_headers()``, ``on_data()`` or
|
||||||
``on_request_done()``.
|
``on_request_done()``.
|
||||||
|
|
||||||
The application can push resource using ``push()`` method. It must be
|
The application can push resources using the ``push()`` method. It must be
|
||||||
used before ``send_response()`` call.
|
used before the ``send_response()`` call.
|
||||||
|
|
||||||
The following instance variables are available:
|
The following instance variables are available:
|
||||||
|
|
||||||
@@ -1178,14 +1233,15 @@ When contributing with code, you agree to put your changes and new
|
|||||||
code under the same license nghttp2 is already using unless stated and
|
code under the same license nghttp2 is already using unless stated and
|
||||||
agreed otherwise.
|
agreed otherwise.
|
||||||
|
|
||||||
When changing existing source code, you do not alter the copyright of
|
When changing existing source code, do not alter the copyright of
|
||||||
the original file(s). The copyright will still be owned by the
|
the original file(s). The copyright will still be owned by the
|
||||||
original creator(s) or those who have been assigned copyright by the
|
original creator(s) or those who have been assigned copyright by the
|
||||||
original author(s).
|
original author(s).
|
||||||
|
|
||||||
By submitting a patch to the nghttp2 project, you are assumed to have
|
By submitting a patch to the nghttp2 project, you (or your employer, as
|
||||||
the right to the code and to be allowed by your employer or whatever
|
the case may be) agree to assign the copyright of your submission to us.
|
||||||
to hand over that patch/code to us. We will credit you for your
|
.. the above really needs to be reworded to pass legal muster.
|
||||||
|
We will credit you for your
|
||||||
changes as far as possible, to give credit but also to keep a trace
|
changes as far as possible, to give credit but also to keep a trace
|
||||||
back to who made what changes. Please always provide us with your
|
back to who made what changes. Please always provide us with your
|
||||||
full real name when contributing!
|
full real name when contributing!
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ PATH=$TOOLCHAIN/bin:$PATH
|
|||||||
--without-libxml2 \
|
--without-libxml2 \
|
||||||
--disable-python-bindings \
|
--disable-python-bindings \
|
||||||
--disable-examples \
|
--disable-examples \
|
||||||
|
--enable-werror \
|
||||||
CC=clang \
|
CC=clang \
|
||||||
CXX=clang++ \
|
CXX=clang++ \
|
||||||
CPPFLAGS="-I$PREFIX/include" \
|
CPPFLAGS="-I$PREFIX/include" \
|
||||||
|
|||||||
38
configure.ac
38
configure.ac
@@ -25,14 +25,14 @@ dnl Do not change user variables!
|
|||||||
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
|
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
|
||||||
|
|
||||||
AC_PREREQ(2.61)
|
AC_PREREQ(2.61)
|
||||||
AC_INIT([nghttp2], [0.7.5], [t-tujikawa@users.sourceforge.net])
|
AC_INIT([nghttp2], [0.7.9], [t-tujikawa@users.sourceforge.net])
|
||||||
LT_PREREQ([2.2.6])
|
LT_PREREQ([2.2.6])
|
||||||
LT_INIT()
|
LT_INIT()
|
||||||
dnl See versioning rule:
|
dnl See versioning rule:
|
||||||
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||||
AC_SUBST(LT_CURRENT, 11)
|
AC_SUBST(LT_CURRENT, 12)
|
||||||
AC_SUBST(LT_REVISION, 0)
|
AC_SUBST(LT_REVISION, 2)
|
||||||
AC_SUBST(LT_AGE, 6)
|
AC_SUBST(LT_AGE, 7)
|
||||||
|
|
||||||
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
|
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
|
||||||
minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"`
|
minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"`
|
||||||
@@ -200,21 +200,6 @@ std::vector<std::future<int>> v;
|
|||||||
[have_std_future=no
|
[have_std_future=no
|
||||||
AC_MSG_RESULT([no])])
|
AC_MSG_RESULT([no])])
|
||||||
|
|
||||||
# Check that thread_local is available.
|
|
||||||
AC_MSG_CHECKING([whether thread_local is available])
|
|
||||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
|
||||||
[[
|
|
||||||
]],
|
|
||||||
[[
|
|
||||||
thread_local int n;
|
|
||||||
]])],
|
|
||||||
[AC_DEFINE([HAVE_THREAD_LOCAL], [1],
|
|
||||||
[Define to 1 if you have the `thread_local`.])
|
|
||||||
have_thread_local=yes
|
|
||||||
AC_MSG_RESULT([yes])],
|
|
||||||
[have_thread_local=no
|
|
||||||
AC_MSG_RESULT([no])])
|
|
||||||
|
|
||||||
# Check that std::map::emplace is available for g++-4.7.
|
# Check that std::map::emplace is available for g++-4.7.
|
||||||
AC_MSG_CHECKING([whether std::map::emplace is available])
|
AC_MSG_CHECKING([whether std::map::emplace is available])
|
||||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||||
@@ -388,7 +373,7 @@ fi
|
|||||||
# spdylay (for src/nghttpx and src/h2load)
|
# spdylay (for src/nghttpx and src/h2load)
|
||||||
have_spdylay=no
|
have_spdylay=no
|
||||||
if test "x${request_spdylay}" != "xno"; then
|
if test "x${request_spdylay}" != "xno"; then
|
||||||
PKG_CHECK_MODULES([LIBSPDYLAY], [libspdylay >= 1.3.0],
|
PKG_CHECK_MODULES([LIBSPDYLAY], [libspdylay >= 1.3.2],
|
||||||
[have_spdylay=yes], [have_spdylay=no])
|
[have_spdylay=yes], [have_spdylay=no])
|
||||||
if test "x${have_spdylay}" = "xyes"; then
|
if test "x${have_spdylay}" = "xyes"; then
|
||||||
AC_DEFINE([HAVE_SPDYLAY], [1], [Define to 1 if you have `spdylay` library.])
|
AC_DEFINE([HAVE_SPDYLAY], [1], [Define to 1 if you have `spdylay` library.])
|
||||||
@@ -549,6 +534,10 @@ if test "x$have_struct_tm_tm_gmtoff" = "xyes"; then
|
|||||||
[Define to 1 if you have `struct tm.tm_gmtoff` member.])
|
[Define to 1 if you have `struct tm.tm_gmtoff` member.])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check size of pointer to decide we need 8 bytes alignment
|
||||||
|
# adjustment.
|
||||||
|
AC_CHECK_SIZEOF([int *])
|
||||||
|
|
||||||
# Checks for library functions.
|
# Checks for library functions.
|
||||||
if test "x$cross_compiling" != "xyes"; then
|
if test "x$cross_compiling" != "xyes"; then
|
||||||
AC_FUNC_MALLOC
|
AC_FUNC_MALLOC
|
||||||
@@ -627,11 +616,10 @@ if test "x$debug" != "xno"; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
enable_threads=yes
|
enable_threads=yes
|
||||||
# Some platform does not have working std::future or thread_local. We
|
# Some platform does not have working std::future. We disable
|
||||||
# disable threading for those platforms.
|
# threading for those platforms.
|
||||||
if test "x$threads" != "xyes" ||
|
if test "x$threads" != "xyes" ||
|
||||||
test "x$have_std_future" != "xyes" ||
|
test "x$have_std_future" != "xyes"; then
|
||||||
test "x$have_thread_local" != "xyes"; then
|
|
||||||
enable_threads=no
|
enable_threads=no
|
||||||
AC_DEFINE([NOTHREADS], [1], [Define to 1 if you want to disable threads.])
|
AC_DEFINE([NOTHREADS], [1], [Define to 1 if you want to disable threads.])
|
||||||
fi
|
fi
|
||||||
@@ -672,6 +660,8 @@ AC_CONFIG_FILES([
|
|||||||
doc/nghttp2.h.rst
|
doc/nghttp2.h.rst
|
||||||
doc/nghttp2ver.h.rst
|
doc/nghttp2ver.h.rst
|
||||||
doc/asio_http2.h.rst
|
doc/asio_http2.h.rst
|
||||||
|
doc/asio_http2_server.h.rst
|
||||||
|
doc/asio_http2_client.h.rst
|
||||||
doc/contribute.rst
|
doc/contribute.rst
|
||||||
contrib/Makefile
|
contrib/Makefile
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -84,6 +84,11 @@ are floating around in existing internet and resetting stream just
|
|||||||
because of this may break many web sites. This is especially true if
|
because of this may break many web sites. This is especially true if
|
||||||
we forward to or translate from HTTP/1 traffic.
|
we forward to or translate from HTTP/1 traffic.
|
||||||
|
|
||||||
|
For "http" or "https" URIs, ":path" pseudo header fields must start
|
||||||
|
with "/". The only exception is OPTIONS request, in that case, "*" is
|
||||||
|
allowed in ":path" pseudo header field to represent system-wide
|
||||||
|
OPTIONS request.
|
||||||
|
|
||||||
With the above validations, nghttp2 library guarantees that header
|
With the above validations, nghttp2 library guarantees that header
|
||||||
field name passed to `nghttp2_on_header_callback()` is not empty.
|
field name passed to `nghttp2_on_header_callback()` is not empty.
|
||||||
Also required pseudo headers are all present and not empty.
|
Also required pseudo headers are all present and not empty.
|
||||||
|
|||||||
5
doc/asio_http2_client.h.rst.in
Normal file
5
doc/asio_http2_client.h.rst.in
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
asio_http2_client.h
|
||||||
|
===================
|
||||||
|
|
||||||
|
.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2_client.h
|
||||||
|
:language: cpp
|
||||||
5
doc/asio_http2_server.h.rst.in
Normal file
5
doc/asio_http2_server.h.rst.in
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
asio_http2_server.h
|
||||||
|
===================
|
||||||
|
|
||||||
|
.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2_server.h
|
||||||
|
:language: cpp
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
.\" Man page generated from reStructuredText.
|
.\" Man page generated from reStructuredText.
|
||||||
.
|
.
|
||||||
.TH "H2LOAD" "1" "February 27, 2015" "0.7.5" "nghttp2"
|
.TH "H2LOAD" "1" "March 27, 2015" "0.7.9" "nghttp2"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
h2load \- HTTP/2 benchmarking tool
|
h2load \- HTTP/2 benchmarking tool
|
||||||
.
|
.
|
||||||
@@ -118,6 +118,12 @@ Default: \fBh2c\-14\fP
|
|||||||
.UNINDENT
|
.UNINDENT
|
||||||
.INDENT 0.0
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
|
.B \-d, \-\-data=<FILE>
|
||||||
|
Post FILE to server. The request method is changed to
|
||||||
|
POST.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
.B \-v, \-\-verbose
|
.B \-v, \-\-verbose
|
||||||
Output debug information.
|
Output debug information.
|
||||||
.UNINDENT
|
.UNINDENT
|
||||||
|
|||||||
@@ -84,6 +84,11 @@ OPTIONS
|
|||||||
|
|
||||||
Default: ``h2c-14``
|
Default: ``h2c-14``
|
||||||
|
|
||||||
|
.. option:: -d, --data=<FILE>
|
||||||
|
|
||||||
|
Post FILE to server. The request method is changed to
|
||||||
|
POST.
|
||||||
|
|
||||||
.. option:: -v, --verbose
|
.. option:: -v, --verbose
|
||||||
|
|
||||||
Output debug information.
|
Output debug information.
|
||||||
|
|||||||
17
doc/nghttp.1
17
doc/nghttp.1
@@ -1,6 +1,6 @@
|
|||||||
.\" Man page generated from reStructuredText.
|
.\" Man page generated from reStructuredText.
|
||||||
.
|
.
|
||||||
.TH "NGHTTP" "1" "February 27, 2015" "0.7.5" "nghttp2"
|
.TH "NGHTTP" "1" "March 27, 2015" "0.7.9" "nghttp2"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
nghttp \- HTTP/2 experimental client
|
nghttp \- HTTP/2 experimental client
|
||||||
.
|
.
|
||||||
@@ -101,6 +101,14 @@ Add a header to the requests. Example: \fI\%\-H\fP\(aq:method: PUT\(aq
|
|||||||
.UNINDENT
|
.UNINDENT
|
||||||
.INDENT 0.0
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
|
.B \-\-trailer=<HEADER>
|
||||||
|
Add a trailer header to the requests. <HEADER> must not
|
||||||
|
include pseudo header field (header field name starting
|
||||||
|
with \(aq:\(aq). To send trailer, one must use \fI\-d\fP option to
|
||||||
|
send request body. Example: \fI\-\-trailer\fP \(aqfoo: bar\(aq.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
.B \-\-cert=<CERT>
|
.B \-\-cert=<CERT>
|
||||||
Use the specified client certificate file. The file
|
Use the specified client certificate file. The file
|
||||||
must be in PEM format.
|
must be in PEM format.
|
||||||
@@ -189,6 +197,13 @@ Use idle streams as anchor nodes to express priority.
|
|||||||
.UNINDENT
|
.UNINDENT
|
||||||
.INDENT 0.0
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
|
.B \-\-hexdump
|
||||||
|
Display the incoming traffic in hexadecimal (Canonical
|
||||||
|
hex+ASCII display). If SSL/TLS is used, decrypted data
|
||||||
|
are used.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
.B \-\-version
|
.B \-\-version
|
||||||
Display version information and exit.
|
Display version information and exit.
|
||||||
.UNINDENT
|
.UNINDENT
|
||||||
|
|||||||
@@ -67,6 +67,13 @@ OPTIONS
|
|||||||
|
|
||||||
Add a header to the requests. Example: :option:`-H`\':method: PUT'
|
Add a header to the requests. Example: :option:`-H`\':method: PUT'
|
||||||
|
|
||||||
|
.. option:: --trailer=<HEADER>
|
||||||
|
|
||||||
|
Add a trailer header to the requests. <HEADER> must not
|
||||||
|
include pseudo header field (header field name starting
|
||||||
|
with ':'). To send trailer, one must use :option:`-d` option to
|
||||||
|
send request body. Example: :option:`--trailer` 'foo: bar'.
|
||||||
|
|
||||||
.. option:: --cert=<CERT>
|
.. option:: --cert=<CERT>
|
||||||
|
|
||||||
Use the specified client certificate file. The file
|
Use the specified client certificate file. The file
|
||||||
@@ -140,6 +147,12 @@ OPTIONS
|
|||||||
|
|
||||||
Use idle streams as anchor nodes to express priority.
|
Use idle streams as anchor nodes to express priority.
|
||||||
|
|
||||||
|
.. option:: --hexdump
|
||||||
|
|
||||||
|
Display the incoming traffic in hexadecimal (Canonical
|
||||||
|
hex+ASCII display). If SSL/TLS is used, decrypted data
|
||||||
|
are used.
|
||||||
|
|
||||||
.. option:: --version
|
.. option:: --version
|
||||||
|
|
||||||
Display version information and exit.
|
Display version information and exit.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.\" Man page generated from reStructuredText.
|
.\" Man page generated from reStructuredText.
|
||||||
.
|
.
|
||||||
.TH "NGHTTPD" "1" "February 27, 2015" "0.7.5" "nghttp2"
|
.TH "NGHTTPD" "1" "March 27, 2015" "0.7.9" "nghttp2"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
nghttpd \- HTTP/2 experimental server
|
nghttpd \- HTTP/2 experimental server
|
||||||
.
|
.
|
||||||
@@ -144,6 +144,21 @@ rather than complete request is received.
|
|||||||
.UNINDENT
|
.UNINDENT
|
||||||
.INDENT 0.0
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
|
.B \-\-trailer=<HEADER>
|
||||||
|
Add a trailer header to a response. <HEADER> must not
|
||||||
|
include pseudo header field (header field name starting
|
||||||
|
with \(aq:\(aq). The trailer is sent only if a response has
|
||||||
|
body part. Example: \fI\%\-\-trailer\fP \(aqfoo: bar\(aq.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
|
.B \-\-hexdump
|
||||||
|
Display the incoming traffic in hexadecimal (Canonical
|
||||||
|
hex+ASCII display). If SSL/TLS is used, decrypted data
|
||||||
|
are used.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
.B \-\-version
|
.B \-\-version
|
||||||
Display version information and exit.
|
Display version information and exit.
|
||||||
.UNINDENT
|
.UNINDENT
|
||||||
|
|||||||
@@ -104,6 +104,19 @@ OPTIONS
|
|||||||
Start sending response when request HEADERS is received,
|
Start sending response when request HEADERS is received,
|
||||||
rather than complete request is received.
|
rather than complete request is received.
|
||||||
|
|
||||||
|
.. option:: --trailer=<HEADER>
|
||||||
|
|
||||||
|
Add a trailer header to a response. <HEADER> must not
|
||||||
|
include pseudo header field (header field name starting
|
||||||
|
with ':'). The trailer is sent only if a response has
|
||||||
|
body part. Example: :option:`--trailer` 'foo: bar'.
|
||||||
|
|
||||||
|
.. option:: --hexdump
|
||||||
|
|
||||||
|
Display the incoming traffic in hexadecimal (Canonical
|
||||||
|
hex+ASCII display). If SSL/TLS is used, decrypted data
|
||||||
|
are used.
|
||||||
|
|
||||||
.. option:: --version
|
.. option:: --version
|
||||||
|
|
||||||
Display version information and exit.
|
Display version information and exit.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.\" Man page generated from reStructuredText.
|
.\" Man page generated from reStructuredText.
|
||||||
.
|
.
|
||||||
.TH "NGHTTPX" "1" "February 27, 2015" "0.7.5" "nghttp2"
|
.TH "NGHTTPX" "1" "March 27, 2015" "0.7.9" "nghttp2"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
nghttpx \- HTTP/2 experimental proxy
|
nghttpx \- HTTP/2 experimental proxy
|
||||||
.
|
.
|
||||||
@@ -55,13 +55,10 @@ The options are categorized into several groups.
|
|||||||
.INDENT 0.0
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
.B \-b, \-\-backend=<HOST,PORT>
|
.B \-b, \-\-backend=<HOST,PORT>
|
||||||
Set backend host and port. For HTTP/1 backend, multiple
|
Set backend host and port. The multiple backend
|
||||||
backend addresses are accepted by repeating this option.
|
addresses are accepted by repeating this option. UNIX
|
||||||
HTTP/2 backend does not support multiple backend
|
domain socket can be specified by prefixing path name
|
||||||
addresses and the first occurrence of this option is
|
with "unix:" (e.g., unix:/var/run/backend.sock)
|
||||||
used. UNIX domain socket can be specified by prefixing
|
|
||||||
path name with "unix:" (e.g.,
|
|
||||||
unix:/var/run/backend.sock)
|
|
||||||
.sp
|
.sp
|
||||||
Default: \fB127.0.0.1,80\fP
|
Default: \fB127.0.0.1,80\fP
|
||||||
.UNINDENT
|
.UNINDENT
|
||||||
@@ -196,6 +193,13 @@ Default: \fB0\fP
|
|||||||
.UNINDENT
|
.UNINDENT
|
||||||
.INDENT 0.0
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
|
.B \-\-backend\-http2\-connections\-per\-worker=<N>
|
||||||
|
Set maximum number of HTTP/2 connections per worker.
|
||||||
|
The default value is 0, which means the number of
|
||||||
|
backend addresses specified by \fI\%\-b\fP option.
|
||||||
|
.UNINDENT
|
||||||
|
.INDENT 0.0
|
||||||
|
.TP
|
||||||
.B \-\-backend\-http1\-connections\-per\-host=<N>
|
.B \-\-backend\-http1\-connections\-per\-host=<N>
|
||||||
Set maximum number of backend concurrent HTTP/1
|
Set maximum number of backend concurrent HTTP/1
|
||||||
connections per host. This option is meaningful when \fI\%\-s\fP
|
connections per host. This option is meaningful when \fI\%\-s\fP
|
||||||
|
|||||||
@@ -34,13 +34,10 @@ Connections
|
|||||||
|
|
||||||
.. option:: -b, --backend=<HOST,PORT>
|
.. option:: -b, --backend=<HOST,PORT>
|
||||||
|
|
||||||
Set backend host and port. For HTTP/1 backend, multiple
|
Set backend host and port. The multiple backend
|
||||||
backend addresses are accepted by repeating this option.
|
addresses are accepted by repeating this option. UNIX
|
||||||
HTTP/2 backend does not support multiple backend
|
domain socket can be specified by prefixing path name
|
||||||
addresses and the first occurrence of this option is
|
with "unix:" (e.g., unix:/var/run/backend.sock)
|
||||||
used. UNIX domain socket can be specified by prefixing
|
|
||||||
path name with "unix:" (e.g.,
|
|
||||||
unix:/var/run/backend.sock)
|
|
||||||
|
|
||||||
Default: ``127.0.0.1,80``
|
Default: ``127.0.0.1,80``
|
||||||
|
|
||||||
@@ -161,6 +158,12 @@ Performance
|
|||||||
|
|
||||||
Default: ``0``
|
Default: ``0``
|
||||||
|
|
||||||
|
.. option:: --backend-http2-connections-per-worker=<N>
|
||||||
|
|
||||||
|
Set maximum number of HTTP/2 connections per worker.
|
||||||
|
The default value is 0, which means the number of
|
||||||
|
backend addresses specified by :option:`-b` option.
|
||||||
|
|
||||||
.. option:: --backend-http1-connections-per-host=<N>
|
.. option:: --backend-http1-connections-per-host=<N>
|
||||||
|
|
||||||
Set maximum number of backend concurrent HTTP/1
|
Set maximum number of backend concurrent HTTP/1
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ Contents:
|
|||||||
python-apiref
|
python-apiref
|
||||||
nghttp2.h
|
nghttp2.h
|
||||||
nghttp2ver.h
|
nghttp2ver.h
|
||||||
|
asio_http2_server.h
|
||||||
|
asio_http2_client.h
|
||||||
asio_http2.h
|
asio_http2.h
|
||||||
Source <https://github.com/tatsuhiro-t/nghttp2>
|
Source <https://github.com/tatsuhiro-t/nghttp2>
|
||||||
Issues <https://github.com/tatsuhiro-t/nghttp2/issues>
|
Issues <https://github.com/tatsuhiro-t/nghttp2/issues>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ libnghttp2_asio: High level HTTP/2 C++ library
|
|||||||
libnghttp2_asio is C++ library built on top of libnghttp2 and provides
|
libnghttp2_asio is C++ library built on top of libnghttp2 and provides
|
||||||
high level abstraction API to build HTTP/2 applications. It depends
|
high level abstraction API to build HTTP/2 applications. It depends
|
||||||
on Boost::ASIO library and OpenSSL. Currently libnghttp2_asio
|
on Boost::ASIO library and OpenSSL. Currently libnghttp2_asio
|
||||||
provides server side API.
|
provides server and client side API.
|
||||||
|
|
||||||
libnghttp2_asio is not built by default. Use ``--enable-asio-lib``
|
libnghttp2_asio is not built by default. Use ``--enable-asio-lib``
|
||||||
configure flag to build libnghttp2_asio. The required Boost libraries
|
configure flag to build libnghttp2_asio. The required Boost libraries
|
||||||
@@ -14,42 +14,64 @@ are:
|
|||||||
* Boost::System
|
* Boost::System
|
||||||
* Boost::Thread
|
* Boost::Thread
|
||||||
|
|
||||||
To use libnghttp2_asio, first include following header file:
|
We have 3 header files for this library:
|
||||||
|
|
||||||
.. code-block:: cpp
|
* :doc:`asio_http2_server.h`
|
||||||
|
* :doc:`asio_http2_client.h`
|
||||||
|
* :doc:`asio_http2.h`
|
||||||
|
|
||||||
#include <nghttp2/asio_http2.h>
|
asio_http2.h is included from the other two files.
|
||||||
|
|
||||||
Also take a look at that header file :doc:`asio_http2.h`.
|
To build a program with libnghttp2_asio, link to the following
|
||||||
|
libraries::
|
||||||
|
|
||||||
|
-lnghttp2_asio -lboost_system
|
||||||
|
|
||||||
|
If ``boost::asio::ssl`` is used in application code, OpenSSL is also
|
||||||
|
required in link line::
|
||||||
|
|
||||||
|
-lnghttp2_asio -lboost_system -lssl -lcrypto
|
||||||
|
|
||||||
Server API
|
Server API
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
To use server API, first include following header file:
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
|
Also take a look at that header file :doc:`asio_http2_server.h`.
|
||||||
|
|
||||||
Server API is designed to build HTTP/2 server very easily to utilize
|
Server API is designed to build HTTP/2 server very easily to utilize
|
||||||
C++11 anonymous function and closure. The bare minimum example of
|
C++11 anonymous function and closure. The bare minimum example of
|
||||||
HTTP/2 server looks like this:
|
HTTP/2 server looks like this:
|
||||||
|
|
||||||
.. code-block:: cpp
|
.. code-block:: cpp
|
||||||
|
|
||||||
#include <nghttp2/asio_http2.h>
|
|
||||||
|
|
||||||
using namespace nghttp2::asio_http2;
|
using namespace nghttp2::asio_http2;
|
||||||
using namespace nghttp2::asio_http2::server;
|
using namespace nghttp2::asio_http2::server;
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
boost::system::error_code ec;
|
||||||
http2 server;
|
http2 server;
|
||||||
|
|
||||||
server.listen("*", 3000, [](const std::shared_ptr<request> &req,
|
server.handle("/", [](const request &req, const response &res) {
|
||||||
const std::shared_ptr<response> &res) {
|
res.write_head(200);
|
||||||
res->write_head(200);
|
res.end("hello, world\n");
|
||||||
res->end("hello, world");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (server.listen_and_serve(ec, "localhost", "3000")) {
|
||||||
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
First we instantiate ``nghttp2::asio_http2::server::http2`` object.
|
First we instantiate ``nghttp2::asio_http2::server::http2`` object.
|
||||||
Then call ``nghttp2::asio_http2::server::http2::listen`` function with
|
``nghttp2::asio_http2::server::http2::handle`` function registers
|
||||||
address and port to listen to and callback function, namely "request
|
pattern and its handler function. In this example, we register "/" as
|
||||||
callback", invoked when request arrives.
|
pattern, which matches all requests. Then call
|
||||||
|
``nghttp2::asio_http2::server::http2::listen_and_serve`` function with
|
||||||
|
address and port to listen to.
|
||||||
|
|
||||||
The ``req`` and ``res`` represent HTTP request and response
|
The ``req`` and ``res`` represent HTTP request and response
|
||||||
respectively. ``nghttp2::asio_http2_::server::response::write_head``
|
respectively. ``nghttp2::asio_http2_::server::response::write_head``
|
||||||
@@ -61,6 +83,10 @@ send.
|
|||||||
``nghttp2::asio_http2::server::response::end`` sends responde body.
|
``nghttp2::asio_http2::server::response::end`` sends responde body.
|
||||||
In the above example, we send string "hello, world".
|
In the above example, we send string "hello, world".
|
||||||
|
|
||||||
|
The life time of req and res object ends after the callback set by
|
||||||
|
``nghttp2::asio_http2::server::response::on_close`` function.
|
||||||
|
Application must not use those objects after this call.
|
||||||
|
|
||||||
Serving static files and enabling SSL/TLS
|
Serving static files and enabling SSL/TLS
|
||||||
+++++++++++++++++++++++++++++++++++++++++
|
+++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
@@ -69,40 +95,47 @@ SSL/TLS.
|
|||||||
|
|
||||||
.. code-block:: cpp
|
.. code-block:: cpp
|
||||||
|
|
||||||
#include <nghttp2/asio_http2.h>
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
using namespace nghttp2::asio_http2;
|
using namespace nghttp2::asio_http2;
|
||||||
using namespace nghttp2::asio_http2::server;
|
using namespace nghttp2::asio_http2::server;
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
boost::system::error_code ec;
|
||||||
|
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
|
||||||
|
|
||||||
|
tls.use_private_key_file("server.key", boost::asio::ssl::context::pem);
|
||||||
|
tls.use_certificate_chain_file("server.crt");
|
||||||
|
|
||||||
|
configure_tls_context_easy(ec, tls);
|
||||||
|
|
||||||
http2 server;
|
http2 server;
|
||||||
|
|
||||||
server.tls("server.key", "server.crt");
|
server.handle("/index.html", [](const request &req, const response &res) {
|
||||||
|
res.write_head(200);
|
||||||
server.listen("*", 3000, [](const std::shared_ptr<request> &req,
|
res.end(file_generator("index.html"));
|
||||||
const std::shared_ptr<response> &res) {
|
|
||||||
if (req->path() == "/" || req->path() == "/index.html") {
|
|
||||||
res->write_head(200);
|
|
||||||
res->end(file_reader("index.html"));
|
|
||||||
} else {
|
|
||||||
res->write_head(404);
|
|
||||||
res->end("<html><head><title>404</title></head>"
|
|
||||||
"<body>404 Not Found</body></html>");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (server.listen_and_serve(ec, tls, "localhost", "3000")) {
|
||||||
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Specifying path to private key file and certificate file in
|
We first create ``boost::asio::ssl::context`` object and set path to
|
||||||
``nghttp2::asio_http2::server::http2::tls`` will enable SSL/TLS. Both
|
private key file and certificate file.
|
||||||
files must be in PEM format.
|
``nghttp2::asio_http2::server::configure_tls_context_easy`` function
|
||||||
|
configures SSL/TLS context object for HTTP/2 server use, including NPN
|
||||||
|
callbacks.
|
||||||
|
|
||||||
In the above example, if request path is either "/" or "/index.html",
|
In the above example, if request path is "/index.html", we serve
|
||||||
we serve index.html file in the current working directory.
|
index.html file in the current working directory.
|
||||||
``nghttp2::asio_http2::server::response::end`` has overload to take
|
``nghttp2::asio_http2::server::response::end`` has overload to take
|
||||||
function of type ``nghttp2::asio_http2::read_cb`` and application pass
|
function of type ``nghttp2::asio_http2::generator_cb`` and application
|
||||||
its implementation to generate response body. For the convenience,
|
pass its implementation to generate response body. For the
|
||||||
libnghttp2_asio library provides ``nghttp2::asio_http2::file_reader``
|
convenience, libnghttp2_asio library provides
|
||||||
function to generate function to server static file.
|
``nghttp2::asio_http2::file_generator`` function to generate function
|
||||||
|
to server static file. If other resource is requested, server
|
||||||
|
automatically responds with 404 status code.
|
||||||
|
|
||||||
Server push
|
Server push
|
||||||
+++++++++++
|
+++++++++++
|
||||||
@@ -111,44 +144,56 @@ Server push is also supported.
|
|||||||
|
|
||||||
.. code-block:: cpp
|
.. code-block:: cpp
|
||||||
|
|
||||||
#include <nghttp2/asio_http2.h>
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
using namespace nghttp2::asio_http2;
|
using namespace nghttp2::asio_http2;
|
||||||
using namespace nghttp2::asio_http2::server;
|
using namespace nghttp2::asio_http2::server;
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
boost::system::error_code ec;
|
||||||
|
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
|
||||||
|
|
||||||
|
tls.use_private_key_file("server.key", boost::asio::ssl::context::pem);
|
||||||
|
tls.use_certificate_chain_file("server.crt");
|
||||||
|
|
||||||
|
configure_tls_context_easy(ec, tls);
|
||||||
|
|
||||||
http2 server;
|
http2 server;
|
||||||
|
|
||||||
server.tls("server.key", "server.crt");
|
std::string style_css = "h1 { color: green; }";
|
||||||
|
|
||||||
server.listen("*", 3000, [](const std::shared_ptr<request> &req,
|
server.handle("/", [&style_css](const request &req, const response &res) {
|
||||||
const std::shared_ptr<response> &res) {
|
boost::system::error_code ec;
|
||||||
if (req->path() == "/") {
|
auto push = res.push(ec, "GET", "/style.css");
|
||||||
req->push("GET", "/my.css");
|
push->write_head(200);
|
||||||
|
push->end(style_css);
|
||||||
|
|
||||||
res->write_head(200);
|
res.write_head(200);
|
||||||
res->end(file_reader("index.html"));
|
res.end(R"(
|
||||||
|
<!DOCTYPE html><html lang="en">
|
||||||
return;
|
<title>HTTP/2 FTW</title><body>
|
||||||
}
|
<link href="/style.css" rel="stylesheet" type="text/css">
|
||||||
|
<h1>This should be green</h1>
|
||||||
if (req->path() == "/my.css") {
|
</body></html>
|
||||||
res->write_head(200);
|
)");
|
||||||
res->end(file_reader("my.css"));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
res->write_head(404);
|
|
||||||
res->end("<html><head><title>404</title></head>"
|
|
||||||
"<body>404 Not Found</body></html>");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.handle("/style.css",
|
||||||
|
[&style_css](const request &req, const response &res) {
|
||||||
|
res.write_head(200);
|
||||||
|
res.end(style_css);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (server.listen_and_serve(ec, tls, "localhost", "3000")) {
|
||||||
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
When client requested "/", we push "/my.css". To push resource, call
|
When client requested any resource other than "/style.css", we push
|
||||||
``nghttp2::asio_http2::server::request::push`` function with desired
|
"/style.css". To push resource, call
|
||||||
method and path. Later, the callback will be called with the pushed
|
``nghttp2::asio_http2::server::response::push`` function with desired
|
||||||
resource "/my.css".
|
method and path. It returns another response object and use its
|
||||||
|
functions to send push response.
|
||||||
|
|
||||||
Enable multi-threading
|
Enable multi-threading
|
||||||
++++++++++++++++++++++
|
++++++++++++++++++++++
|
||||||
@@ -164,65 +209,225 @@ desired number of threads:
|
|||||||
// Use 4 native threads
|
// Use 4 native threads
|
||||||
server.num_threads(4);
|
server.num_threads(4);
|
||||||
|
|
||||||
Run blocking tasks in background thread
|
Client API
|
||||||
+++++++++++++++++++++++++++++++++++++++
|
----------
|
||||||
|
|
||||||
The request callback is called in the same thread where HTTP request
|
To use client API, first include following header file:
|
||||||
is handled. And many connections shares the same thread, we cannot
|
|
||||||
directly run blocking tasks in request callback.
|
|
||||||
|
|
||||||
To run blocking tasks, use
|
|
||||||
``nghttp2::asio_http2::server::request::run_task``. The passed
|
|
||||||
callback will be executed in the different thread from the thread
|
|
||||||
where request callback was executed. So application can perform
|
|
||||||
blocking task there. The example follows:
|
|
||||||
|
|
||||||
.. code-block:: cpp
|
.. code-block:: cpp
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <nghttp2/asio_http2_client.h>
|
||||||
#include <nghttp2/asio_http2.h>
|
|
||||||
|
Also take a look at that header file :doc:`asio_http2_client.h`.
|
||||||
|
|
||||||
|
Here is the sample client code to access HTTP/2 server and print out
|
||||||
|
response header fields and response body to the console screen:
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2_client.h>
|
||||||
|
|
||||||
|
using boost::asio::ip::tcp;
|
||||||
|
|
||||||
using namespace nghttp2::asio_http2;
|
using namespace nghttp2::asio_http2;
|
||||||
using namespace nghttp2::asio_http2::server;
|
using namespace nghttp2::asio_http2::client;
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
http2 server;
|
boost::system::error_code ec;
|
||||||
|
boost::asio::io_service io_service;
|
||||||
|
|
||||||
server.num_concurrent_tasks(16);
|
// connect to localhost:3000
|
||||||
|
session sess(io_service, "localhost", "3000");
|
||||||
|
|
||||||
server.listen("*", 3000, [](const std::shared_ptr<request> &req,
|
sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
|
||||||
const std::shared_ptr<response> &res) {
|
boost::system::error_code ec;
|
||||||
req->run_task([res](channel &channel) {
|
|
||||||
// executed in different thread than the thread where
|
|
||||||
// request callback was executed.
|
|
||||||
|
|
||||||
// using res directly here is not safe. Capturing it by
|
auto req = sess.submit(ec, "GET", "http://localhost:3000/");
|
||||||
// value is safe because it is std::shared_ptr.
|
|
||||||
|
|
||||||
sleep(1);
|
req->on_response([](const response &res) {
|
||||||
|
// print status code and response header fields.
|
||||||
|
std::cerr << "HTTP/2 " << res.status_code() << std::endl;
|
||||||
|
for (auto &kv : res.header()) {
|
||||||
|
std::cerr << kv.first << ": " << kv.second.value << "\n";
|
||||||
|
}
|
||||||
|
std::cerr << std::endl;
|
||||||
|
|
||||||
channel.post([res]() {
|
res.on_data([](const uint8_t *data, std::size_t len) {
|
||||||
// executed in the same thread where request callback
|
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||||
// was executed.
|
std::cerr << std::endl;
|
||||||
res->write_head(200);
|
});
|
||||||
res->end("hello, world");
|
});
|
||||||
});
|
|
||||||
});
|
req->on_close([&sess](uint32_t error_code) {
|
||||||
|
// shutdown session after first request was done.
|
||||||
|
sess.shutdown();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
sess.on_error([](const boost::system::error_code &ec) {
|
||||||
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
|
});
|
||||||
|
|
||||||
|
io_service.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
First we set the number of background threads which run tasks. By
|
``nghttp2::asio_http2::client::session`` object takes
|
||||||
default it is set to 1. In this example, we set it to 16, so at most
|
``boost::asio::io_service`` object and remote server address. When
|
||||||
16 tasks can be executed concurrently without blocking handling new
|
connection is made, the callback function passed to
|
||||||
requests.
|
``nghttp2::asio_http2::client::on_connect`` is invoked with connected
|
||||||
|
address as its paramter. After this callback call, use
|
||||||
|
``nghttp2::asio_http2::session::submit`` to send request to the
|
||||||
|
server. You can submit multiple requests at once without waiting for
|
||||||
|
the completion of previous request.
|
||||||
|
|
||||||
We call ``req->run_task()`` to execute task in background thread. In
|
The life time of req and res object ends after the callback set by
|
||||||
the passed callback, we just simply sleeps 1 second. After sleep is
|
``nghttp2::asio_http2::server::request::on_close`` function.
|
||||||
over, we schedule another callback to send response to the client.
|
Application must not use those objects after this call.
|
||||||
Since the callback passed to ``req->run_task()`` is executed in the
|
|
||||||
different thread from the thread where request callback is called,
|
Normally, client does not stop even after all requests are done unless
|
||||||
using ``req`` or ``res`` object directly there may cause undefined
|
connection is lost. To stop client, call
|
||||||
behaviour. To avoid this issue, we can use
|
``nghttp2::asio_http2::server::session::shutdown()``.
|
||||||
``nghttp2::asio_http2::channel::post`` by supplying a callback which
|
|
||||||
in turn get called in the same thread where request callback was
|
Recieve server push and enable SSL/TLS
|
||||||
called.
|
++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2_client.h>
|
||||||
|
|
||||||
|
using boost::asio::ip::tcp;
|
||||||
|
|
||||||
|
using namespace nghttp2::asio_http2;
|
||||||
|
using namespace nghttp2::asio_http2::client;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
boost::system::error_code ec;
|
||||||
|
boost::asio::io_service io_service;
|
||||||
|
|
||||||
|
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
|
||||||
|
tls.set_default_verify_paths();
|
||||||
|
// disabled to make development easier...
|
||||||
|
// tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer);
|
||||||
|
configure_tls_context(ec, tls);
|
||||||
|
|
||||||
|
// connect to localhost:3000
|
||||||
|
session sess(io_service, tls, "localhost", "3000");
|
||||||
|
|
||||||
|
sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
|
||||||
|
boost::system::error_code ec;
|
||||||
|
|
||||||
|
auto req = sess.submit(ec, "GET", "http://localhost:3000/");
|
||||||
|
|
||||||
|
req->on_response([&sess](const response &res) {
|
||||||
|
std::cerr << "response received!" << std::endl;
|
||||||
|
res.on_data([&sess](const uint8_t *data, std::size_t len) {
|
||||||
|
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||||
|
std::cerr << std::endl;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
req->on_push([](const request &push) {
|
||||||
|
std::cerr << "push request received!" << std::endl;
|
||||||
|
push.on_response([](const response &res) {
|
||||||
|
std::cerr << "push response received!" << std::endl;
|
||||||
|
res.on_data([](const uint8_t *data, std::size_t len) {
|
||||||
|
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||||
|
std::cerr << std::endl;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
sess.on_error([](const boost::system::error_code &ec) {
|
||||||
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
|
});
|
||||||
|
|
||||||
|
io_service.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
The above sample code demonstrates how to enable SSL/TLS and receive
|
||||||
|
server push. Currently,
|
||||||
|
``nghttp2::asio_http2::client::configure_tls_context`` function setups
|
||||||
|
NPN callbacks for SSL/TLS context for HTTP/2 use.
|
||||||
|
|
||||||
|
To receive server push, use
|
||||||
|
``nghttp2::asio_http2::client::request::on_push`` function to set
|
||||||
|
callback function which is invoked when server push request is
|
||||||
|
arrived. The callback function takes
|
||||||
|
``nghttp2::asio_http2::client::request`` object, which contains the
|
||||||
|
pushed request. To get server push response, set callback using
|
||||||
|
``nghttp2::asio_http2::client::request::on_response``.
|
||||||
|
|
||||||
|
As stated in the previous section, client does not stop automatically
|
||||||
|
as long as HTTP/2 session is fine and connection is alive. We don't
|
||||||
|
call ``nghttp2::asio_http2::client::session::shutdown`` in this
|
||||||
|
example, so the program does not terminate after all responses are
|
||||||
|
received. Hit Ctrl-C to terminate the program.
|
||||||
|
|
||||||
|
Multiple concurrent requests
|
||||||
|
++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2_client.h>
|
||||||
|
|
||||||
|
using boost::asio::ip::tcp;
|
||||||
|
|
||||||
|
using namespace nghttp2::asio_http2;
|
||||||
|
using namespace nghttp2::asio_http2::client;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
boost::system::error_code ec;
|
||||||
|
boost::asio::io_service io_service;
|
||||||
|
|
||||||
|
// connect to localhost:3000
|
||||||
|
session sess(io_service, "localhost", "3000");
|
||||||
|
|
||||||
|
sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
|
||||||
|
boost::system::error_code ec;
|
||||||
|
|
||||||
|
auto printer = [](const response &res) {
|
||||||
|
res.on_data([](const uint8_t *data, std::size_t len) {
|
||||||
|
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||||
|
std::cerr << std::endl;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
std::size_t num = 3;
|
||||||
|
auto count = std::make_shared<int>(num);
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < num; ++i) {
|
||||||
|
auto req = sess.submit(ec, "GET",
|
||||||
|
"http://localhost:3000/" + std::to_string(i + 1));
|
||||||
|
|
||||||
|
req->on_response(printer);
|
||||||
|
req->on_close([&sess, count](uint32_t error_code) {
|
||||||
|
if (--*count == 0) {
|
||||||
|
// shutdown session after |num| requests were done.
|
||||||
|
sess.shutdown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sess.on_error([](const boost::system::error_code &ec) {
|
||||||
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
|
});
|
||||||
|
|
||||||
|
io_service.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
Here is the sample to send 3 requests at once. Depending on the
|
||||||
|
server settings, these requests are processed out-of-order. In this
|
||||||
|
example, we have a trick to shutdown session after all requests were
|
||||||
|
done. We made ``count`` object which is shared pointer to int and is
|
||||||
|
initialized to 3. On each request closure (the invocation of the
|
||||||
|
callback set by ``nghttp2::asio_http2::client::request::on_close``),
|
||||||
|
we decrement the count. If count becomes 0, we are sure that all
|
||||||
|
requests have been done and initiate shutdown.
|
||||||
|
|||||||
@@ -59,11 +59,11 @@ SPDY protocols and it works as so called SPDY proxy.
|
|||||||
With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend
|
With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend
|
||||||
connection, so the connection gets insecure.
|
connection, so the connection gets insecure.
|
||||||
|
|
||||||
The backend must be HTTP/1 proxy server. nghttpx only supports
|
The backend must be HTTP/1 proxy server. nghttpx supports multiple
|
||||||
multiple backend server addresses. It translates incoming requests to
|
backend server addresses. It translates incoming requests to HTTP/1
|
||||||
HTTP/1 request to backend server. The backend server performs real
|
request to backend server. The backend server performs real proxy
|
||||||
proxy work for each request, for example, dispatching requests to the
|
work for each request, for example, dispatching requests to the origin
|
||||||
origin server and caching contents.
|
server and caching contents.
|
||||||
|
|
||||||
For example, to make nghttpx listen to encrypted HTTP/2 requests at
|
For example, to make nghttpx listen to encrypted HTTP/2 requests at
|
||||||
port 8443, and a backend HTTP/1 proxy server is configured to listen
|
port 8443, and a backend HTTP/1 proxy server is configured to listen
|
||||||
@@ -124,7 +124,9 @@ HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP
|
|||||||
Upgrade. To disable SSL/TLS in backend connection, use
|
Upgrade. To disable SSL/TLS in backend connection, use
|
||||||
``--backend-no-tls`` option.
|
``--backend-no-tls`` option.
|
||||||
|
|
||||||
The backend connection is created one per worker (thread).
|
By default, the number of backend HTTP/2 connections per worker
|
||||||
|
(thread) is determined by number of ``-b`` option. To adjust this
|
||||||
|
value, use ``--backend-http2-connections-per-worker`` option.
|
||||||
|
|
||||||
The backend server is supporsed to be a HTTP/2 web server (e.g.,
|
The backend server is supporsed to be a HTTP/2 web server (e.g.,
|
||||||
nghttpd). The one use-case of this mode is utilize existing HTTP/1
|
nghttpd). The one use-case of this mode is utilize existing HTTP/1
|
||||||
@@ -156,7 +158,9 @@ HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP
|
|||||||
Upgrade. To disable SSL/TLS in backend connection, use
|
Upgrade. To disable SSL/TLS in backend connection, use
|
||||||
``--backend-no-tls`` option.
|
``--backend-no-tls`` option.
|
||||||
|
|
||||||
The backend connection is created one per worker (thread).
|
By default, the number of backend HTTP/2 connections per worker
|
||||||
|
(thread) is determined by number of ``-b`` option. To adjust this
|
||||||
|
value, use ``--backend-http2-connections-per-worker`` option.
|
||||||
|
|
||||||
The backend server must be a HTTP/2 proxy. You can use nghttpx in
|
The backend server must be a HTTP/2 proxy. You can use nghttpx in
|
||||||
`HTTP/2 proxy mode`_ as backend server. The one use-case of this mode
|
`HTTP/2 proxy mode`_ as backend server. The one use-case of this mode
|
||||||
@@ -196,6 +200,10 @@ With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend
|
|||||||
connection, so the connection gets insecure. To disable SSL/TLS in
|
connection, so the connection gets insecure. To disable SSL/TLS in
|
||||||
backend connection, use ``--backend-no-tls`` option.
|
backend connection, use ``--backend-no-tls`` option.
|
||||||
|
|
||||||
|
By default, the number of backend HTTP/2 connections per worker
|
||||||
|
(thread) is determined by number of ``-b`` option. To adjust this
|
||||||
|
value, use ``--backend-http2-connections-per-worker`` option.
|
||||||
|
|
||||||
The backend server is supporsed to be a HTTP/2 web server or HTTP/2
|
The backend server is supporsed to be a HTTP/2 web server or HTTP/2
|
||||||
proxy. If backend server is HTTP/2 proxy, use
|
proxy. If backend server is HTTP/2 proxy, use
|
||||||
``--no-location-rewrite`` and ``--no-host-rewrite`` options to disable
|
``--no-location-rewrite`` and ``--no-host-rewrite`` options to disable
|
||||||
@@ -285,11 +293,11 @@ re-open log files, send USR1 signal to nghttpx process. It will
|
|||||||
re-open files specified by ``--accesslog-file`` and
|
re-open files specified by ``--accesslog-file`` and
|
||||||
``--errorlog-file`` options.
|
``--errorlog-file`` options.
|
||||||
|
|
||||||
Multiple HTTP/1 backend addresses
|
Multiple backend addresses
|
||||||
---------------------------------
|
--------------------------
|
||||||
|
|
||||||
nghttpx supports multiple HTTP/1 backend addresses. To specify them,
|
nghttpx supports multiple backend addresses. To specify them, just
|
||||||
just use ``-b`` option repeatedly. For example, to use backend1:8080
|
use ``-b`` option repeatedly. For example, to use backend1:8080 and
|
||||||
and backend2:8080, use command-line like this: ``-bbackend1,8080
|
backend2:8080, use command-line like this: ``-bbackend1,8080
|
||||||
-bbackend2,8080``. Please note that HTTP/2 backend only supports 1
|
-bbackend2,8080``. For HTTP/2 backend, see also
|
||||||
backend address.
|
``--backend-http2-connections-per-worker`` option.
|
||||||
|
|||||||
5
examples/.gitignore
vendored
5
examples/.gitignore
vendored
@@ -2,7 +2,8 @@ client
|
|||||||
libevent-client
|
libevent-client
|
||||||
libevent-server
|
libevent-server
|
||||||
deflate
|
deflate
|
||||||
asio-sv
|
|
||||||
tiny-nghttpd
|
tiny-nghttpd
|
||||||
|
asio-sv
|
||||||
asio-sv2
|
asio-sv2
|
||||||
asio-sv3
|
asio-cl
|
||||||
|
asio-cl2
|
||||||
|
|||||||
@@ -58,10 +58,21 @@ endif # ENABLE_TINY_NGHTTPD
|
|||||||
|
|
||||||
if ENABLE_ASIO_LIB
|
if ENABLE_ASIO_LIB
|
||||||
|
|
||||||
noinst_PROGRAMS += asio-sv asio-sv2 asio-sv3
|
noinst_PROGRAMS += asio-sv asio-sv2 asio-cl asio-cl2
|
||||||
|
|
||||||
ASIOCPPFLAGS = ${BOOST_CPPFLAGS} ${AM_CPPFLAGS}
|
# AM_CPPFLAGS must be placed first, so that header file (e.g.,
|
||||||
ASIOLDADD = $(top_builddir)/src/libnghttp2_asio.la @JEMALLOC_LIBS@
|
# nghttp2/nghttp2.h) in this package is used rather than installed
|
||||||
|
# one.
|
||||||
|
ASIOCPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS}
|
||||||
|
ASIOLDADD = $(top_builddir)/lib/libnghttp2.la \
|
||||||
|
$(top_builddir)/src/libnghttp2_asio.la @JEMALLOC_LIBS@ \
|
||||||
|
$(top_builddir)/third-party/libhttp-parser.la \
|
||||||
|
${BOOST_LDFLAGS} \
|
||||||
|
${BOOST_ASIO_LIB} \
|
||||||
|
${BOOST_THREAD_LIB} \
|
||||||
|
${BOOST_SYSTEM_LIB} \
|
||||||
|
@OPENSSL_LIBS@ \
|
||||||
|
@APPLDFLAGS@
|
||||||
|
|
||||||
asio_sv_SOURCES = asio-sv.cc
|
asio_sv_SOURCES = asio-sv.cc
|
||||||
asio_sv_CPPFLAGS = ${ASIOCPPFLAGS}
|
asio_sv_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||||
@@ -71,9 +82,13 @@ asio_sv2_SOURCES = asio-sv2.cc
|
|||||||
asio_sv2_CPPFLAGS = ${ASIOCPPFLAGS}
|
asio_sv2_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||||
asio_sv2_LDADD = ${ASIOLDADD}
|
asio_sv2_LDADD = ${ASIOLDADD}
|
||||||
|
|
||||||
asio_sv3_SOURCES = asio-sv3.cc
|
asio_cl_SOURCES = asio-cl.cc
|
||||||
asio_sv3_CPPFLAGS = ${ASIOCPPFLAGS}
|
asio_cl_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||||
asio_sv3_LDADD = ${ASIOLDADD}
|
asio_cl_LDADD = ${ASIOLDADD}
|
||||||
|
|
||||||
|
asio_cl2_SOURCES = asio-cl2.cc
|
||||||
|
asio_cl2_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||||
|
asio_cl2_LDADD = ${ASIOLDADD}
|
||||||
|
|
||||||
endif # ENABLE_ASIO_LIB
|
endif # ENABLE_ASIO_LIB
|
||||||
|
|
||||||
|
|||||||
96
examples/asio-cl.cc
Normal file
96
examples/asio-cl.cc
Normal 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
134
examples/asio-cl2.cc
Normal 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;
|
||||||
|
}
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <nghttp2/asio_http2.h>
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
using namespace nghttp2::asio_http2;
|
using namespace nghttp2::asio_http2;
|
||||||
using namespace nghttp2::asio_http2::server;
|
using namespace nghttp2::asio_http2::server;
|
||||||
@@ -45,28 +45,102 @@ using namespace nghttp2::asio_http2::server;
|
|||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
try {
|
try {
|
||||||
// Check command line arguments.
|
// Check command line arguments.
|
||||||
if (argc < 3) {
|
if (argc < 4) {
|
||||||
std::cerr << "Usage: asio-sv <port> <threads> <private-key-file> "
|
std::cerr
|
||||||
<< "<cert-file>\n";
|
<< "Usage: asio-sv <address> <port> <threads> [<private-key-file> "
|
||||||
|
<< "<cert-file>]\n";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t port = std::stoi(argv[1]);
|
boost::system::error_code ec;
|
||||||
std::size_t num_threads = std::stoi(argv[2]);
|
|
||||||
|
std::string addr = argv[1];
|
||||||
|
std::string port = argv[2];
|
||||||
|
std::size_t num_threads = std::stoi(argv[3]);
|
||||||
|
|
||||||
http2 server;
|
http2 server;
|
||||||
|
|
||||||
server.num_threads(num_threads);
|
server.num_threads(num_threads);
|
||||||
|
|
||||||
if (argc >= 5) {
|
server.handle("/", [](const request &req, const response &res) {
|
||||||
server.tls(argv[3], argv[4]);
|
res.write_head(200, {{"foo", {"bar"}}});
|
||||||
}
|
res.end("hello, world\n");
|
||||||
|
|
||||||
server.listen("*", port, [](const std::shared_ptr<request> &req,
|
|
||||||
const std::shared_ptr<response> &res) {
|
|
||||||
res->write_head(200, {header{"foo", "bar"}});
|
|
||||||
res->end("hello, world");
|
|
||||||
});
|
});
|
||||||
|
server.handle("/secret/", [](const request &req, const response &res) {
|
||||||
|
res.write_head(200);
|
||||||
|
res.end("under construction!\n");
|
||||||
|
});
|
||||||
|
server.handle("/push", [](const request &req, const response &res) {
|
||||||
|
boost::system::error_code ec;
|
||||||
|
auto push = res.push(ec, "GET", "/push/1");
|
||||||
|
if (!ec) {
|
||||||
|
push->write_head(200);
|
||||||
|
push->end("server push FTW!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
res.write_head(200);
|
||||||
|
res.end("you'll receive server push!\n");
|
||||||
|
});
|
||||||
|
server.handle("/delay", [](const request &req, const response &res) {
|
||||||
|
res.write_head(200);
|
||||||
|
|
||||||
|
auto timer = std::make_shared<boost::asio::deadline_timer>(
|
||||||
|
res.io_service(), boost::posix_time::seconds(3));
|
||||||
|
auto closed = std::make_shared<bool>();
|
||||||
|
|
||||||
|
res.on_close([timer, closed](uint32_t error_code) {
|
||||||
|
timer->cancel();
|
||||||
|
*closed = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
timer->async_wait([&res, closed](const boost::system::error_code &ec) {
|
||||||
|
if (ec || *closed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.end("finally!\n");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
server.handle("/trailer", [](const request &req, const response &res) {
|
||||||
|
// send trailer part.
|
||||||
|
res.write_head(200, {{"trailers", {"digest"}}});
|
||||||
|
|
||||||
|
std::string body = "nghttp2 FTW!\n";
|
||||||
|
auto left = std::make_shared<size_t>(body.size());
|
||||||
|
|
||||||
|
res.end([&res, body, left](uint8_t *dst, std::size_t len,
|
||||||
|
uint32_t *data_flags) {
|
||||||
|
auto n = std::min(len, *left);
|
||||||
|
std::copy_n(body.c_str() + (body.size() - *left), n, dst);
|
||||||
|
*left -= n;
|
||||||
|
if (*left == 0) {
|
||||||
|
*data_flags |=
|
||||||
|
NGHTTP2_DATA_FLAG_EOF | NGHTTP2_DATA_FLAG_NO_END_STREAM;
|
||||||
|
// RFC 3230 Instance Digests in HTTP. The digest value is
|
||||||
|
// SHA-256 message digest of body.
|
||||||
|
res.write_trailer(
|
||||||
|
{{"digest",
|
||||||
|
{"SHA-256=qqXqskW7F3ueBSvmZRCiSwl2ym4HRO0M/pvQCBlSDis="}}});
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (argc >= 6) {
|
||||||
|
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
|
||||||
|
tls.use_private_key_file(argv[4], boost::asio::ssl::context::pem);
|
||||||
|
tls.use_certificate_chain_file(argv[5]);
|
||||||
|
|
||||||
|
configure_tls_context_easy(ec, tls);
|
||||||
|
|
||||||
|
if (server.listen_and_serve(ec, tls, addr, port)) {
|
||||||
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (server.listen_and_serve(ec, addr, port)) {
|
||||||
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (std::exception &e) {
|
} catch (std::exception &e) {
|
||||||
std::cerr << "exception: " << e.what() << "\n";
|
std::cerr << "exception: " << e.what() << "\n";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <nghttp2/asio_http2.h>
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
using namespace nghttp2::asio_http2;
|
using namespace nghttp2::asio_http2;
|
||||||
using namespace nghttp2::asio_http2::server;
|
using namespace nghttp2::asio_http2::server;
|
||||||
@@ -48,30 +48,28 @@ using namespace nghttp2::asio_http2::server;
|
|||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
try {
|
try {
|
||||||
// Check command line arguments.
|
// Check command line arguments.
|
||||||
if (argc < 4) {
|
if (argc < 5) {
|
||||||
std::cerr << "Usage: asio-sv2 <port> <threads> <doc-root> "
|
std::cerr << "Usage: asio-sv2 <address> <port> <threads> <doc-root> "
|
||||||
<< "<private-key-file> <cert-file>\n";
|
<< "[<private-key-file> <cert-file>]\n";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t port = std::stoi(argv[1]);
|
boost::system::error_code ec;
|
||||||
std::size_t num_threads = std::stoi(argv[2]);
|
|
||||||
std::string docroot = argv[3];
|
std::string addr = argv[1];
|
||||||
|
std::string port = argv[2];
|
||||||
|
std::size_t num_threads = std::stoi(argv[3]);
|
||||||
|
std::string docroot = argv[4];
|
||||||
|
|
||||||
http2 server;
|
http2 server;
|
||||||
|
|
||||||
server.num_threads(num_threads);
|
server.num_threads(num_threads);
|
||||||
|
|
||||||
if (argc >= 6) {
|
server.handle("/", [&docroot](const request &req, const response &res) {
|
||||||
server.tls(argv[4], argv[5]);
|
auto path = percent_decode(req.uri().path);
|
||||||
}
|
|
||||||
|
|
||||||
server.listen("*", port, [&docroot](const std::shared_ptr<request> &req,
|
|
||||||
const std::shared_ptr<response> &res) {
|
|
||||||
auto path = percent_decode(req->path());
|
|
||||||
if (!check_path(path)) {
|
if (!check_path(path)) {
|
||||||
res->write_head(404);
|
res.write_head(404);
|
||||||
res->end();
|
res.end();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,22 +80,39 @@ int main(int argc, char *argv[]) {
|
|||||||
path = docroot + path;
|
path = docroot + path;
|
||||||
auto fd = open(path.c_str(), O_RDONLY);
|
auto fd = open(path.c_str(), O_RDONLY);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
res->write_head(404);
|
res.write_head(404);
|
||||||
res->end();
|
res.end();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto headers = std::vector<header>();
|
auto header = header_map();
|
||||||
|
|
||||||
struct stat stbuf;
|
struct stat stbuf;
|
||||||
if (stat(path.c_str(), &stbuf) == 0) {
|
if (stat(path.c_str(), &stbuf) == 0) {
|
||||||
headers.push_back(
|
header.emplace("content-length",
|
||||||
header{"content-length", std::to_string(stbuf.st_size)});
|
header_value{std::to_string(stbuf.st_size)});
|
||||||
headers.push_back(header{"last-modified", http_date(stbuf.st_mtime)});
|
header.emplace("last-modified",
|
||||||
|
header_value{http_date(stbuf.st_mtime)});
|
||||||
}
|
}
|
||||||
res->write_head(200, std::move(headers));
|
res.write_head(200, std::move(header));
|
||||||
res->end(file_reader_from_fd(fd));
|
res.end(file_generator_from_fd(fd));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (argc >= 7) {
|
||||||
|
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
|
||||||
|
tls.use_private_key_file(argv[5], boost::asio::ssl::context::pem);
|
||||||
|
tls.use_certificate_chain_file(argv[6]);
|
||||||
|
|
||||||
|
configure_tls_context_easy(ec, tls);
|
||||||
|
|
||||||
|
if (server.listen_and_serve(ec, tls, addr, port)) {
|
||||||
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (server.listen_and_serve(ec, addr, port)) {
|
||||||
|
std::cerr << "error: " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (std::exception &e) {
|
} catch (std::exception &e) {
|
||||||
std::cerr << "exception: " << e.what() << "\n";
|
std::cerr << "exception: " << e.what() << "\n";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -77,7 +77,7 @@ int lookup_token(const uint8_t *name, size_t namelen) {
|
|||||||
print '''\
|
print '''\
|
||||||
case {}:'''.format(size)
|
case {}:'''.format(size)
|
||||||
print '''\
|
print '''\
|
||||||
switch (name[namelen - 1]) {'''
|
switch (name[{}]) {{'''.format(size - 1)
|
||||||
for c in sorted(ents.keys()):
|
for c in sorted(ents.keys()):
|
||||||
headers = sorted(ents[c])
|
headers = sorted(ents[c])
|
||||||
print '''\
|
print '''\
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
|
|||||||
print '''\
|
print '''\
|
||||||
case {}:'''.format(size)
|
case {}:'''.format(size)
|
||||||
print '''\
|
print '''\
|
||||||
switch (name[namelen - 1]) {'''
|
switch (name[{}]) {{'''.format(size - 1)
|
||||||
for c in sorted(ents.keys()):
|
for c in sorted(ents.keys()):
|
||||||
headers = sorted(ents[c])
|
headers = sorted(ents[c])
|
||||||
print '''\
|
print '''\
|
||||||
|
|||||||
@@ -211,6 +211,41 @@ func TestH1H1HTTP10NoHostRewrite(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestH1H1RequestTrailer tests request trailer part is forwarded to
|
||||||
|
// backend.
|
||||||
|
func TestH1H1RequestTrailer(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
buf := make([]byte, 4096)
|
||||||
|
for {
|
||||||
|
_, err := r.Body.Read(buf)
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("r.Body.Read() = %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if got, want := r.Trailer.Get("foo"), "bar"; got != want {
|
||||||
|
t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http1(requestParam{
|
||||||
|
name: "TestH1H1RequestTrailer",
|
||||||
|
body: []byte("1"),
|
||||||
|
trailer: []hpack.HeaderField{
|
||||||
|
pair("foo", "bar"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http1() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("res.status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestH1H2ConnectFailure tests that server handles the situation that
|
// TestH1H2ConnectFailure tests that server handles the situation that
|
||||||
// connection attempt to HTTP/2 backend failed.
|
// connection attempt to HTTP/2 backend failed.
|
||||||
func TestH1H2ConnectFailure(t *testing.T) {
|
func TestH1H2ConnectFailure(t *testing.T) {
|
||||||
|
|||||||
@@ -520,6 +520,41 @@ func TestH2H1ServerPush(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestH2H1RequestTrailer tests request trailer part is forwarded to
|
||||||
|
// backend.
|
||||||
|
func TestH2H1RequestTrailer(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
buf := make([]byte, 4096)
|
||||||
|
for {
|
||||||
|
_, err := r.Body.Read(buf)
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("r.Body.Read() = %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if got, want := r.Trailer.Get("foo"), "bar"; got != want {
|
||||||
|
t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1RequestTrailer",
|
||||||
|
body: []byte("1"),
|
||||||
|
trailer: []hpack.HeaderField{
|
||||||
|
pair("foo", "bar"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("res.status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestH2H1GracefulShutdown tests graceful shutdown.
|
// TestH2H1GracefulShutdown tests graceful shutdown.
|
||||||
func TestH2H1GracefulShutdown(t *testing.T) {
|
func TestH2H1GracefulShutdown(t *testing.T) {
|
||||||
st := newServerTester(nil, t, noopHandler)
|
st := newServerTester(nil, t, noopHandler)
|
||||||
|
|||||||
@@ -255,6 +255,27 @@ type requestParam struct {
|
|||||||
path string // path, defaults to /
|
path string // path, defaults to /
|
||||||
header []hpack.HeaderField // additional request header fields
|
header []hpack.HeaderField // additional request header fields
|
||||||
body []byte // request body
|
body []byte // request body
|
||||||
|
trailer []hpack.HeaderField // trailer part
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapper for request body to set trailer part
|
||||||
|
type chunkedBodyReader struct {
|
||||||
|
trailer []hpack.HeaderField
|
||||||
|
trailerWritten bool
|
||||||
|
body io.Reader
|
||||||
|
req *http.Request
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cbr *chunkedBodyReader) Read(p []byte) (n int, err error) {
|
||||||
|
// document says that we have to set http.Request.Trailer
|
||||||
|
// after request was sent and before body returns EOF.
|
||||||
|
if !cbr.trailerWritten {
|
||||||
|
cbr.trailerWritten = true
|
||||||
|
for _, h := range cbr.trailer {
|
||||||
|
cbr.req.Trailer.Set(h.Name, h.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cbr.body.Read(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
|
func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
|
||||||
@@ -264,8 +285,16 @@ func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body io.Reader
|
var body io.Reader
|
||||||
|
var cbr *chunkedBodyReader
|
||||||
if rp.body != nil {
|
if rp.body != nil {
|
||||||
body = bytes.NewBuffer(rp.body)
|
body = bytes.NewBuffer(rp.body)
|
||||||
|
if len(rp.trailer) != 0 {
|
||||||
|
cbr = &chunkedBodyReader{
|
||||||
|
trailer: rp.trailer,
|
||||||
|
body: body,
|
||||||
|
}
|
||||||
|
body = cbr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
req, err := http.NewRequest(method, st.url, body)
|
req, err := http.NewRequest(method, st.url, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -275,7 +304,15 @@ func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
|
|||||||
req.Header.Add(h.Name, h.Value)
|
req.Header.Add(h.Name, h.Value)
|
||||||
}
|
}
|
||||||
req.Header.Add("Test-Case", rp.name)
|
req.Header.Add("Test-Case", rp.name)
|
||||||
|
if cbr != nil {
|
||||||
|
cbr.req = req
|
||||||
|
// this makes request use chunked encoding
|
||||||
|
req.ContentLength = -1
|
||||||
|
req.Trailer = make(http.Header)
|
||||||
|
for _, h := range cbr.trailer {
|
||||||
|
req.Trailer.Set(h.Name, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
if err := req.Write(st.conn); err != nil {
|
if err := req.Write(st.conn); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -473,7 +510,7 @@ func (st *serverTester) http2(rp requestParam) (*serverResponse, error) {
|
|||||||
|
|
||||||
err := st.fr.WriteHeaders(http2.HeadersFrameParam{
|
err := st.fr.WriteHeaders(http2.HeadersFrameParam{
|
||||||
StreamID: id,
|
StreamID: id,
|
||||||
EndStream: len(rp.body) == 0,
|
EndStream: len(rp.body) == 0 && len(rp.trailer) == 0,
|
||||||
EndHeaders: true,
|
EndHeaders: true,
|
||||||
BlockFragment: st.headerBlkBuf.Bytes(),
|
BlockFragment: st.headerBlkBuf.Bytes(),
|
||||||
})
|
})
|
||||||
@@ -483,7 +520,23 @@ func (st *serverTester) http2(rp requestParam) (*serverResponse, error) {
|
|||||||
|
|
||||||
if len(rp.body) != 0 {
|
if len(rp.body) != 0 {
|
||||||
// TODO we assume rp.body fits in 1 frame
|
// TODO we assume rp.body fits in 1 frame
|
||||||
if err := st.fr.WriteData(id, true, rp.body); err != nil {
|
if err := st.fr.WriteData(id, len(rp.trailer) == 0, rp.body); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(rp.trailer) != 0 {
|
||||||
|
st.headerBlkBuf.Reset()
|
||||||
|
for _, h := range rp.trailer {
|
||||||
|
_ = st.enc.WriteField(h)
|
||||||
|
}
|
||||||
|
err := st.fr.WriteHeaders(http2.HeadersFrameParam{
|
||||||
|
StreamID: id,
|
||||||
|
EndStream: true,
|
||||||
|
EndHeaders: true,
|
||||||
|
BlockFragment: st.headerBlkBuf.Bytes(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -402,21 +402,27 @@ typedef enum {
|
|||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/**
|
/**
|
||||||
* The |name| byte string, which is not necessarily ``NULL``
|
* The |name| byte string. If this struct is presented from library
|
||||||
* terminated.
|
* (e.g., :type:`nghttp2_on_frame_recv_callback`), |name| is
|
||||||
|
* guaranteed to be NULL-terminated. When application is
|
||||||
|
* constructing this struct, |name| is not required to be
|
||||||
|
* NULL-terminated.
|
||||||
*/
|
*/
|
||||||
uint8_t *name;
|
uint8_t *name;
|
||||||
/**
|
/**
|
||||||
* The |value| byte string, which is not necessarily ``NULL``
|
* The |value| byte string. If this struct is presented from
|
||||||
* terminated.
|
* library (e.g., :type:`nghttp2_on_frame_recv_callback`), |value|
|
||||||
|
* is guaranteed to be NULL-terminated. When application is
|
||||||
|
* constructing this struct, |value| is not required to be
|
||||||
|
* NULL-terminated.
|
||||||
*/
|
*/
|
||||||
uint8_t *value;
|
uint8_t *value;
|
||||||
/**
|
/**
|
||||||
* The length of the |name|.
|
* The length of the |name|, excluding terminating NULL.
|
||||||
*/
|
*/
|
||||||
size_t namelen;
|
size_t namelen;
|
||||||
/**
|
/**
|
||||||
* The length of the |value|.
|
* The length of the |value|, excluding terminating NULL.
|
||||||
*/
|
*/
|
||||||
size_t valuelen;
|
size_t valuelen;
|
||||||
/**
|
/**
|
||||||
@@ -468,7 +474,9 @@ typedef enum {
|
|||||||
*/
|
*/
|
||||||
NGHTTP2_WINDOW_UPDATE = 0x08,
|
NGHTTP2_WINDOW_UPDATE = 0x08,
|
||||||
/**
|
/**
|
||||||
* The CONTINUATION frame.
|
* The CONTINUATION frame. This frame type won't be passed to any
|
||||||
|
* callbacks because the library processes this frame type and its
|
||||||
|
* preceding HEADERS/PUSH_PROMISE as a single frame.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_CONTINUATION = 0x09
|
NGHTTP2_CONTINUATION = 0x09
|
||||||
} nghttp2_frame_type;
|
} nghttp2_frame_type;
|
||||||
@@ -682,7 +690,14 @@ typedef enum {
|
|||||||
/**
|
/**
|
||||||
* Indicates EOF was sensed.
|
* Indicates EOF was sensed.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_DATA_FLAG_EOF = 0x01
|
NGHTTP2_DATA_FLAG_EOF = 0x01,
|
||||||
|
/**
|
||||||
|
* Indicates that END_STREAM flag must not be set even if
|
||||||
|
* NGHTTP2_DATA_FLAG_EOF is set. Usually this flag is used to send
|
||||||
|
* trailer header fields with `nghttp2_submit_request()` or
|
||||||
|
* `nghttp2_submit_response()`.
|
||||||
|
*/
|
||||||
|
NGHTTP2_DATA_FLAG_NO_END_STREAM = 0x02
|
||||||
} nghttp2_data_flag;
|
} nghttp2_data_flag;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -695,6 +710,21 @@ typedef enum {
|
|||||||
* them in |buf| and return number of data stored in |buf|. If EOF is
|
* them in |buf| and return number of data stored in |buf|. If EOF is
|
||||||
* reached, set :enum:`NGHTTP2_DATA_FLAG_EOF` flag in |*data_flags|.
|
* reached, set :enum:`NGHTTP2_DATA_FLAG_EOF` flag in |*data_flags|.
|
||||||
*
|
*
|
||||||
|
* If this callback is set by `nghttp2_submit_request()`,
|
||||||
|
* `nghttp2_submit_response()` or `nghttp2_submit_headers()` and
|
||||||
|
* `nghttp2_submit_data()` with flag parameter
|
||||||
|
* :enum:`NGHTTP2_FLAG_END_STREAM` set, and
|
||||||
|
* :enum:`NGHTTP2_DATA_FLAG_EOF` flag is set to |*data_flags|, DATA
|
||||||
|
* frame will have END_STREAM flag set. Usually, this is expected
|
||||||
|
* behaviour and all are fine. One exception is send trailer header
|
||||||
|
* fields. You cannot send trailers after sending frame with
|
||||||
|
* END_STREAM set. To avoid this problem, one can set
|
||||||
|
* :enum:`NGHTTP2_DATA_FLAG_NO_END_STREAM` along with
|
||||||
|
* :enum:`NGHTTP2_DATA_FLAG_EOF` to signal the library not to set
|
||||||
|
* END_STREAM in DATA frame. Then application can use
|
||||||
|
* `nghttp2_submit_trailer()` to send trailers.
|
||||||
|
* `nghttp2_submit_trailer()` can be called inside this callback.
|
||||||
|
*
|
||||||
* If the application wants to postpone DATA frames (e.g.,
|
* If the application wants to postpone DATA frames (e.g.,
|
||||||
* asynchronous I/O, or reading data blocks for long time), it is
|
* asynchronous I/O, or reading data blocks for long time), it is
|
||||||
* achieved by returning :enum:`NGHTTP2_ERR_DEFERRED` without reading
|
* achieved by returning :enum:`NGHTTP2_ERR_DEFERRED` without reading
|
||||||
@@ -1437,7 +1467,11 @@ typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session,
|
|||||||
* :type:`nghttp2_on_frame_recv_callback` for the |frame| will not be
|
* :type:`nghttp2_on_frame_recv_callback` for the |frame| will not be
|
||||||
* invoked.
|
* invoked.
|
||||||
*
|
*
|
||||||
* The |value| may be ``NULL`` if the |valuelen| is 0.
|
* Both |name| and |value| are guaranteed to be NULL-terminated. The
|
||||||
|
* |namelen| and |valuelen| do not include terminal NULL. If
|
||||||
|
* `nghttp2_option_set_no_http_messaging()` is used with nonzero
|
||||||
|
* value, NULL character may be included in |name| or |value| before
|
||||||
|
* terminating NULL.
|
||||||
*
|
*
|
||||||
* Please note that unless `nghttp2_option_set_no_http_messaging()` is
|
* Please note that unless `nghttp2_option_set_no_http_messaging()` is
|
||||||
* used, nghttp2 library does perform validation against the |name|
|
* used, nghttp2 library does perform validation against the |name|
|
||||||
@@ -2834,6 +2868,53 @@ int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,
|
|||||||
const nghttp2_nv *nva, size_t nvlen,
|
const nghttp2_nv *nva, size_t nvlen,
|
||||||
const nghttp2_data_provider *data_prd);
|
const nghttp2_data_provider *data_prd);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @function
|
||||||
|
*
|
||||||
|
* Submits trailer HEADERS against the stream |stream_id|.
|
||||||
|
*
|
||||||
|
* The |nva| is an array of name/value pair :type:`nghttp2_nv` with
|
||||||
|
* |nvlen| elements. The application is responsible not to include
|
||||||
|
* required pseudo-header fields (header field whose name starts with
|
||||||
|
* ":") in |nva|.
|
||||||
|
*
|
||||||
|
* This function creates copies of all name/value pairs in |nva|. It
|
||||||
|
* also lower-cases all names in |nva|. The order of elements in
|
||||||
|
* |nva| is preserved.
|
||||||
|
*
|
||||||
|
* For server, trailer must be followed by response HEADERS or
|
||||||
|
* response DATA. The library does not check that response HEADERS
|
||||||
|
* has already sent and if `nghttp2_submit_trailer()` is called before
|
||||||
|
* any response HEADERS submission (usually by
|
||||||
|
* `nghttp2_submit_response()`), the content of |nva| will be sent as
|
||||||
|
* reponse headers, which will result in error.
|
||||||
|
*
|
||||||
|
* This function has the same effect with `nghttp2_submit_headers()`,
|
||||||
|
* with flags = :enum:`NGHTTP2_FLAG_END_HEADERS` and both pri_spec and
|
||||||
|
* stream_user_data to NULL.
|
||||||
|
*
|
||||||
|
* To submit trailer after `nghttp2_submit_response()` is called, the
|
||||||
|
* application has to specify :type:`nghttp2_data_provider` to
|
||||||
|
* `nghttp2_submit_response()`. In side
|
||||||
|
* :type:`nghttp2_data_source_read_callback`, when setting
|
||||||
|
* :enum:`NGHTTP2_DATA_FLAG_EOF`, also set
|
||||||
|
* :enum:`NGHTTP2_DATA_FLAG_NO_END_STREAM`. After that, the
|
||||||
|
* application can send trailer using `nghttp2_submit_trailer()`.
|
||||||
|
* `nghttp2_submit_trailer()` can be used inside
|
||||||
|
* :type:`nghttp2_data_source_read_callback`.
|
||||||
|
*
|
||||||
|
* This function returns 0 if it succeeds and |stream_id| is -1.
|
||||||
|
* Otherwise, this function returns 0 if it succeeds, or one of the
|
||||||
|
* following negative error codes:
|
||||||
|
*
|
||||||
|
* :enum:`NGHTTP2_ERR_NOMEM`
|
||||||
|
* Out of memory.
|
||||||
|
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
|
||||||
|
* The |stream_id| is 0.
|
||||||
|
*/
|
||||||
|
int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id,
|
||||||
|
const nghttp2_nv *nva, size_t nvlen);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function
|
* @function
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -410,31 +410,23 @@ ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) {
|
|||||||
len += nghttp2_buf_len(&chain->buf);
|
len += nghttp2_buf_len(&chain->buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!len) {
|
if (len == 0) {
|
||||||
res = NULL;
|
res = NULL;
|
||||||
} else {
|
return 0;
|
||||||
res = nghttp2_mem_malloc(bufs->mem, len);
|
}
|
||||||
|
|
||||||
if (res == NULL) {
|
res = nghttp2_mem_malloc(bufs->mem, len);
|
||||||
return NGHTTP2_ERR_NOMEM;
|
if (res == NULL) {
|
||||||
}
|
return NGHTTP2_ERR_NOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
nghttp2_buf_wrap_init(&resbuf, res, len);
|
nghttp2_buf_wrap_init(&resbuf, res, len);
|
||||||
|
|
||||||
for (chain = bufs->head; chain; chain = chain->next) {
|
for (chain = bufs->head; chain; chain = chain->next) {
|
||||||
buf = &chain->buf;
|
buf = &chain->buf;
|
||||||
|
resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf));
|
||||||
if (resbuf.last) {
|
|
||||||
resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf));
|
|
||||||
}
|
|
||||||
|
|
||||||
nghttp2_buf_reset(buf);
|
|
||||||
nghttp2_buf_shift_right(&chain->buf, bufs->offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bufs->cur = bufs->head;
|
|
||||||
|
|
||||||
*out = res;
|
*out = res;
|
||||||
|
|
||||||
return (ssize_t)len;
|
return (ssize_t)len;
|
||||||
|
|||||||
@@ -313,9 +313,8 @@ int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b);
|
|||||||
* function allocates the contagious memory to store all data in
|
* function allocates the contagious memory to store all data in
|
||||||
* |bufs| and assigns it to |*out|.
|
* |bufs| and assigns it to |*out|.
|
||||||
*
|
*
|
||||||
* On successful return, nghttp2_bufs_len(bufs) returns 0, just like
|
* The contents of |bufs| is left unchanged.
|
||||||
* after calling nghttp2_bufs_reset().
|
*
|
||||||
|
|
||||||
* This function returns the length of copied data and assigns the
|
* This function returns the length of copied data and assigns the
|
||||||
* pointer to copied data to |*out| if it succeeds, or one of the
|
* pointer to copied data to |*out| if it succeeds, or one of the
|
||||||
* following negative error codes:
|
* following negative error codes:
|
||||||
|
|||||||
@@ -739,7 +739,8 @@ int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva,
|
|||||||
nghttp2_nv *p;
|
nghttp2_nv *p;
|
||||||
|
|
||||||
for (i = 0; i < nvlen; ++i) {
|
for (i = 0; i < nvlen; ++i) {
|
||||||
buflen += nva[i].namelen + nva[i].valuelen;
|
/* + 2 for null-termination */
|
||||||
|
buflen += nva[i].namelen + nva[i].valuelen + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nvlen == 0) {
|
if (nvlen == 0) {
|
||||||
@@ -765,12 +766,14 @@ int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva,
|
|||||||
memcpy(data, nva[i].name, nva[i].namelen);
|
memcpy(data, nva[i].name, nva[i].namelen);
|
||||||
p->name = data;
|
p->name = data;
|
||||||
p->namelen = nva[i].namelen;
|
p->namelen = nva[i].namelen;
|
||||||
|
data[p->namelen] = '\0';
|
||||||
nghttp2_downcase(p->name, p->namelen);
|
nghttp2_downcase(p->name, p->namelen);
|
||||||
data += nva[i].namelen;
|
data += nva[i].namelen + 1;
|
||||||
memcpy(data, nva[i].value, nva[i].valuelen);
|
memcpy(data, nva[i].value, nva[i].valuelen);
|
||||||
p->value = data;
|
p->value = data;
|
||||||
p->valuelen = nva[i].valuelen;
|
p->valuelen = nva[i].valuelen;
|
||||||
data += nva[i].valuelen;
|
data[p->valuelen] = '\0';
|
||||||
|
data += nva[i].valuelen + 1;
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -471,7 +471,9 @@ void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen);
|
|||||||
/*
|
/*
|
||||||
* Copies name/value pairs from |nva|, which contains |nvlen| pairs,
|
* Copies name/value pairs from |nva|, which contains |nvlen| pairs,
|
||||||
* to |*nva_ptr|, which is dynamically allocated so that all items can
|
* to |*nva_ptr|, which is dynamically allocated so that all items can
|
||||||
* be stored.
|
* be stored. The resultant name and value in nghttp2_nv are
|
||||||
|
* guaranteed to be NULL-terminated even if the input is not
|
||||||
|
* null-terminated.
|
||||||
*
|
*
|
||||||
* The |*nva_ptr| must be freed using nghttp2_nv_array_del().
|
* The |*nva_ptr| must be freed using nghttp2_nv_array_del().
|
||||||
*
|
*
|
||||||
|
|||||||
103
lib/nghttp2_hd.c
103
lib/nghttp2_hd.c
@@ -152,10 +152,11 @@ int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name,
|
|||||||
if ((flags & NGHTTP2_HD_FLAG_NAME_ALLOC) &&
|
if ((flags & NGHTTP2_HD_FLAG_NAME_ALLOC) &&
|
||||||
(flags & NGHTTP2_HD_FLAG_NAME_GIFT) == 0) {
|
(flags & NGHTTP2_HD_FLAG_NAME_GIFT) == 0) {
|
||||||
if (namelen == 0) {
|
if (namelen == 0) {
|
||||||
/* We should not allow empty header field name */
|
flags &= ~NGHTTP2_HD_FLAG_NAME_ALLOC;
|
||||||
ent->nv.name = NULL;
|
ent->nv.name = (uint8_t *)"";
|
||||||
} else {
|
} else {
|
||||||
ent->nv.name = nghttp2_memdup(name, namelen, mem);
|
/* copy including terminating NULL byte */
|
||||||
|
ent->nv.name = nghttp2_memdup(name, namelen + 1, mem);
|
||||||
if (ent->nv.name == NULL) {
|
if (ent->nv.name == NULL) {
|
||||||
rv = NGHTTP2_ERR_NOMEM;
|
rv = NGHTTP2_ERR_NOMEM;
|
||||||
goto fail;
|
goto fail;
|
||||||
@@ -167,9 +168,11 @@ int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name,
|
|||||||
if ((flags & NGHTTP2_HD_FLAG_VALUE_ALLOC) &&
|
if ((flags & NGHTTP2_HD_FLAG_VALUE_ALLOC) &&
|
||||||
(flags & NGHTTP2_HD_FLAG_VALUE_GIFT) == 0) {
|
(flags & NGHTTP2_HD_FLAG_VALUE_GIFT) == 0) {
|
||||||
if (valuelen == 0) {
|
if (valuelen == 0) {
|
||||||
ent->nv.value = NULL;
|
flags &= ~NGHTTP2_HD_FLAG_VALUE_ALLOC;
|
||||||
|
ent->nv.value = (uint8_t *)"";
|
||||||
} else {
|
} else {
|
||||||
ent->nv.value = nghttp2_memdup(value, valuelen, mem);
|
/* copy including terminating NULL byte */
|
||||||
|
ent->nv.value = nghttp2_memdup(value, valuelen + 1, mem);
|
||||||
if (ent->nv.value == NULL) {
|
if (ent->nv.value == NULL) {
|
||||||
rv = NGHTTP2_ERR_NOMEM;
|
rv = NGHTTP2_ERR_NOMEM;
|
||||||
goto fail2;
|
goto fail2;
|
||||||
@@ -404,11 +407,8 @@ static size_t entry_room(size_t namelen, size_t valuelen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int emit_indexed_header(nghttp2_nv *nv_out, nghttp2_hd_entry *ent) {
|
static int emit_indexed_header(nghttp2_nv *nv_out, nghttp2_hd_entry *ent) {
|
||||||
DEBUGF(fprintf(stderr, "inflatehd: header emission: "));
|
DEBUGF(fprintf(stderr, "inflatehd: header emission: %s: %s\n", ent->nv.name,
|
||||||
DEBUGF(fwrite(ent->nv.name, ent->nv.namelen, 1, stderr));
|
ent->nv.value));
|
||||||
DEBUGF(fprintf(stderr, ": "));
|
|
||||||
DEBUGF(fwrite(ent->nv.value, ent->nv.valuelen, 1, stderr));
|
|
||||||
DEBUGF(fprintf(stderr, "\n"));
|
|
||||||
/* ent->ref may be 0. This happens if the encoder emits literal
|
/* ent->ref may be 0. This happens if the encoder emits literal
|
||||||
block larger than header table capacity with indexing. */
|
block larger than header table capacity with indexing. */
|
||||||
*nv_out = ent->nv;
|
*nv_out = ent->nv;
|
||||||
@@ -416,11 +416,8 @@ static int emit_indexed_header(nghttp2_nv *nv_out, nghttp2_hd_entry *ent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int emit_literal_header(nghttp2_nv *nv_out, nghttp2_nv *nv) {
|
static int emit_literal_header(nghttp2_nv *nv_out, nghttp2_nv *nv) {
|
||||||
DEBUGF(fprintf(stderr, "inflatehd: header emission: "));
|
DEBUGF(fprintf(stderr, "inflatehd: header emission: %s: %s\n", nv->name,
|
||||||
DEBUGF(fwrite(nv->name, nv->namelen, 1, stderr));
|
nv->value));
|
||||||
DEBUGF(fprintf(stderr, ": "));
|
|
||||||
DEBUGF(fwrite(nv->value, nv->valuelen, 1, stderr));
|
|
||||||
DEBUGF(fprintf(stderr, "\n"));
|
|
||||||
*nv_out = *nv;
|
*nv_out = *nv;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -750,11 +747,9 @@ static nghttp2_hd_entry *add_hd_table_incremental(nghttp2_hd_context *context,
|
|||||||
|
|
||||||
context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen);
|
context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen);
|
||||||
|
|
||||||
DEBUGF(fprintf(stderr, "hpack: remove item from header table: "));
|
DEBUGF(fprintf(stderr, "hpack: remove item from header table: %s: %s\n",
|
||||||
DEBUGF(fwrite(ent->nv.name, ent->nv.namelen, 1, stderr));
|
ent->nv.name, ent->nv.value));
|
||||||
DEBUGF(fprintf(stderr, ": "));
|
|
||||||
DEBUGF(fwrite(ent->nv.value, ent->nv.valuelen, 1, stderr));
|
|
||||||
DEBUGF(fprintf(stderr, "\n"));
|
|
||||||
hd_ringbuf_pop_back(&context->hd_table);
|
hd_ringbuf_pop_back(&context->hd_table);
|
||||||
if (--ent->ref == 0) {
|
if (--ent->ref == 0) {
|
||||||
nghttp2_hd_entry_free(ent, mem);
|
nghttp2_hd_entry_free(ent, mem);
|
||||||
@@ -975,11 +970,7 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs,
|
|||||||
uint32_t value_hash = hash(nv->value, nv->valuelen);
|
uint32_t value_hash = hash(nv->value, nv->valuelen);
|
||||||
nghttp2_mem *mem;
|
nghttp2_mem *mem;
|
||||||
|
|
||||||
DEBUGF(fprintf(stderr, "deflatehd: deflating "));
|
DEBUGF(fprintf(stderr, "deflatehd: deflating %s: %s\n", nv->name, nv->value));
|
||||||
DEBUGF(fwrite(nv->name, nv->namelen, 1, stderr));
|
|
||||||
DEBUGF(fprintf(stderr, ": "));
|
|
||||||
DEBUGF(fwrite(nv->value, nv->valuelen, 1, stderr));
|
|
||||||
DEBUGF(fprintf(stderr, "\n"));
|
|
||||||
|
|
||||||
mem = deflater->ctx.mem;
|
mem = deflater->ctx.mem;
|
||||||
|
|
||||||
@@ -1337,18 +1328,24 @@ static int hd_inflate_remove_bufs(nghttp2_hd_inflater *inflater, nghttp2_nv *nv,
|
|||||||
return NGHTTP2_ERR_NOMEM;
|
return NGHTTP2_ERR_NOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nghttp2_bufs_reset(&inflater->nvbufs);
|
||||||
|
|
||||||
buflen = rv;
|
buflen = rv;
|
||||||
|
|
||||||
if (value_only) {
|
if (value_only) {
|
||||||
|
/* we don't use this value, so no need to NULL-terminate */
|
||||||
nv->name = NULL;
|
nv->name = NULL;
|
||||||
nv->namelen = 0;
|
nv->namelen = 0;
|
||||||
|
|
||||||
|
nv->value = buf;
|
||||||
|
nv->valuelen = buflen - 1;
|
||||||
} else {
|
} else {
|
||||||
nv->name = buf;
|
nv->name = buf;
|
||||||
nv->namelen = inflater->newnamelen;
|
nv->namelen = inflater->newnamelen;
|
||||||
}
|
|
||||||
|
|
||||||
nv->value = buf + nv->namelen;
|
nv->value = buf + nv->namelen + 1;
|
||||||
nv->valuelen = buflen - nv->namelen;
|
nv->valuelen = buflen - nv->namelen - 2;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1360,15 +1357,19 @@ static int hd_inflate_remove_bufs(nghttp2_hd_inflater *inflater, nghttp2_nv *nv,
|
|||||||
pbuf = &inflater->nvbufs.head->buf;
|
pbuf = &inflater->nvbufs.head->buf;
|
||||||
|
|
||||||
if (value_only) {
|
if (value_only) {
|
||||||
|
/* we don't use this value, so no need to NULL-terminate */
|
||||||
nv->name = NULL;
|
nv->name = NULL;
|
||||||
nv->namelen = 0;
|
nv->namelen = 0;
|
||||||
|
|
||||||
|
nv->value = pbuf->pos;
|
||||||
|
nv->valuelen = nghttp2_buf_len(pbuf) - 1;
|
||||||
} else {
|
} else {
|
||||||
nv->name = pbuf->pos;
|
nv->name = pbuf->pos;
|
||||||
nv->namelen = inflater->newnamelen;
|
nv->namelen = inflater->newnamelen;
|
||||||
}
|
|
||||||
|
|
||||||
nv->value = pbuf->pos + nv->namelen;
|
nv->value = pbuf->pos + nv->namelen + 1;
|
||||||
nv->valuelen = nghttp2_buf_len(pbuf) - nv->namelen;
|
nv->valuelen = nghttp2_buf_len(pbuf) - nv->namelen - 2;
|
||||||
|
}
|
||||||
|
|
||||||
/* Resetting does not change the content of first buffer */
|
/* Resetting does not change the content of first buffer */
|
||||||
nghttp2_bufs_reset(&inflater->nvbufs);
|
nghttp2_bufs_reset(&inflater->nvbufs);
|
||||||
@@ -1528,6 +1529,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out,
|
|||||||
uint8_t *first = in;
|
uint8_t *first = in;
|
||||||
uint8_t *last = in + inlen;
|
uint8_t *last = in + inlen;
|
||||||
int rfin = 0;
|
int rfin = 0;
|
||||||
|
int busy = 0;
|
||||||
|
|
||||||
if (inflater->ctx.bad) {
|
if (inflater->ctx.bad) {
|
||||||
return NGHTTP2_ERR_HEADER_COMP;
|
return NGHTTP2_ERR_HEADER_COMP;
|
||||||
@@ -1536,7 +1538,8 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out,
|
|||||||
DEBUGF(fprintf(stderr, "inflatehd: start state=%d\n", inflater->state));
|
DEBUGF(fprintf(stderr, "inflatehd: start state=%d\n", inflater->state));
|
||||||
hd_inflate_keep_free(inflater);
|
hd_inflate_keep_free(inflater);
|
||||||
*inflate_flags = NGHTTP2_HD_INFLATE_NONE;
|
*inflate_flags = NGHTTP2_HD_INFLATE_NONE;
|
||||||
for (; in != last;) {
|
for (; in != last || busy;) {
|
||||||
|
busy = 0;
|
||||||
switch (inflater->state) {
|
switch (inflater->state) {
|
||||||
case NGHTTP2_HD_STATE_OPCODE:
|
case NGHTTP2_HD_STATE_OPCODE:
|
||||||
if ((*in & 0xe0u) == 0x20u) {
|
if ((*in & 0xe0u) == 0x20u) {
|
||||||
@@ -1688,6 +1691,11 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out,
|
|||||||
|
|
||||||
inflater->newnamelen = nghttp2_bufs_len(&inflater->nvbufs);
|
inflater->newnamelen = nghttp2_bufs_len(&inflater->nvbufs);
|
||||||
|
|
||||||
|
rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0');
|
||||||
|
if (rv != 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN;
|
inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -1709,6 +1717,11 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out,
|
|||||||
|
|
||||||
inflater->newnamelen = nghttp2_bufs_len(&inflater->nvbufs);
|
inflater->newnamelen = nghttp2_bufs_len(&inflater->nvbufs);
|
||||||
|
|
||||||
|
rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0');
|
||||||
|
if (rv != 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN;
|
inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -1734,19 +1747,6 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out,
|
|||||||
}
|
}
|
||||||
|
|
||||||
DEBUGF(fprintf(stderr, "inflatehd: valuelen=%zu\n", inflater->left));
|
DEBUGF(fprintf(stderr, "inflatehd: valuelen=%zu\n", inflater->left));
|
||||||
if (inflater->left == 0) {
|
|
||||||
if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) {
|
|
||||||
rv = hd_inflate_commit_newname(inflater, nv_out);
|
|
||||||
} else {
|
|
||||||
rv = hd_inflate_commit_indname(inflater, nv_out);
|
|
||||||
}
|
|
||||||
if (rv != 0) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
inflater->state = NGHTTP2_HD_STATE_OPCODE;
|
|
||||||
*inflate_flags |= NGHTTP2_HD_INFLATE_EMIT;
|
|
||||||
return (ssize_t)(in - first);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inflater->huffman_encoded) {
|
if (inflater->huffman_encoded) {
|
||||||
nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx);
|
nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx);
|
||||||
@@ -1755,6 +1755,9 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out,
|
|||||||
} else {
|
} else {
|
||||||
inflater->state = NGHTTP2_HD_STATE_READ_VALUE;
|
inflater->state = NGHTTP2_HD_STATE_READ_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
busy = 1;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case NGHTTP2_HD_STATE_READ_VALUEHUFF:
|
case NGHTTP2_HD_STATE_READ_VALUEHUFF:
|
||||||
rv = hd_inflate_read_huff(inflater, &inflater->nvbufs, in, last);
|
rv = hd_inflate_read_huff(inflater, &inflater->nvbufs, in, last);
|
||||||
@@ -1773,6 +1776,11 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out,
|
|||||||
goto almost_ok;
|
goto almost_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0');
|
||||||
|
if (rv != 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) {
|
if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) {
|
||||||
rv = hd_inflate_commit_newname(inflater, nv_out);
|
rv = hd_inflate_commit_newname(inflater, nv_out);
|
||||||
} else {
|
} else {
|
||||||
@@ -1805,6 +1813,11 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out,
|
|||||||
goto almost_ok;
|
goto almost_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0');
|
||||||
|
if (rv != 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) {
|
if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) {
|
||||||
rv = hd_inflate_commit_newname(inflater, nv_out);
|
rv = hd_inflate_commit_newname(inflater, nv_out);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -324,8 +324,7 @@ void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx);
|
|||||||
* be initialized by nghttp2_hd_huff_decode_context_init(). The result
|
* be initialized by nghttp2_hd_huff_decode_context_init(). The result
|
||||||
* will be added to |dest|. This function may expand |dest| as
|
* will be added to |dest|. This function may expand |dest| as
|
||||||
* needed. The caller is responsible to release the memory of |dest|
|
* needed. The caller is responsible to release the memory of |dest|
|
||||||
* by calling nghttp2_bufs_free() or export its content using
|
* by calling nghttp2_bufs_free().
|
||||||
* nghttp2_bufs_remove().
|
|
||||||
*
|
*
|
||||||
* The caller must set the |final| to nonzero if the given input is
|
* The caller must set the |final| to nonzero if the given input is
|
||||||
* the final block.
|
* the final block.
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ typedef enum {
|
|||||||
static int lookup_token(const uint8_t *name, size_t namelen) {
|
static int lookup_token(const uint8_t *name, size_t namelen) {
|
||||||
switch (namelen) {
|
switch (namelen) {
|
||||||
case 2:
|
case 2:
|
||||||
switch (name[namelen - 1]) {
|
switch (name[1]) {
|
||||||
case 'e':
|
case 'e':
|
||||||
if (streq("t", name, 1)) {
|
if (streq("t", name, 1)) {
|
||||||
return NGHTTP2_TOKEN_TE;
|
return NGHTTP2_TOKEN_TE;
|
||||||
@@ -85,7 +85,7 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
switch (name[namelen - 1]) {
|
switch (name[3]) {
|
||||||
case 't':
|
case 't':
|
||||||
if (streq("hos", name, 3)) {
|
if (streq("hos", name, 3)) {
|
||||||
return NGHTTP2_TOKEN_HOST;
|
return NGHTTP2_TOKEN_HOST;
|
||||||
@@ -94,7 +94,7 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
switch (name[namelen - 1]) {
|
switch (name[4]) {
|
||||||
case 'h':
|
case 'h':
|
||||||
if (streq(":pat", name, 4)) {
|
if (streq(":pat", name, 4)) {
|
||||||
return NGHTTP2_TOKEN__PATH;
|
return NGHTTP2_TOKEN__PATH;
|
||||||
@@ -103,7 +103,7 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 7:
|
||||||
switch (name[namelen - 1]) {
|
switch (name[6]) {
|
||||||
case 'd':
|
case 'd':
|
||||||
if (streq(":metho", name, 6)) {
|
if (streq(":metho", name, 6)) {
|
||||||
return NGHTTP2_TOKEN__METHOD;
|
return NGHTTP2_TOKEN__METHOD;
|
||||||
@@ -125,7 +125,7 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 10:
|
case 10:
|
||||||
switch (name[namelen - 1]) {
|
switch (name[9]) {
|
||||||
case 'e':
|
case 'e':
|
||||||
if (streq("keep-aliv", name, 9)) {
|
if (streq("keep-aliv", name, 9)) {
|
||||||
return NGHTTP2_TOKEN_KEEP_ALIVE;
|
return NGHTTP2_TOKEN_KEEP_ALIVE;
|
||||||
@@ -144,7 +144,7 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 14:
|
case 14:
|
||||||
switch (name[namelen - 1]) {
|
switch (name[13]) {
|
||||||
case 'h':
|
case 'h':
|
||||||
if (streq("content-lengt", name, 13)) {
|
if (streq("content-lengt", name, 13)) {
|
||||||
return NGHTTP2_TOKEN_CONTENT_LENGTH;
|
return NGHTTP2_TOKEN_CONTENT_LENGTH;
|
||||||
@@ -153,7 +153,7 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 16:
|
case 16:
|
||||||
switch (name[namelen - 1]) {
|
switch (name[15]) {
|
||||||
case 'n':
|
case 'n':
|
||||||
if (streq("proxy-connectio", name, 15)) {
|
if (streq("proxy-connectio", name, 15)) {
|
||||||
return NGHTTP2_TOKEN_PROXY_CONNECTION;
|
return NGHTTP2_TOKEN_PROXY_CONNECTION;
|
||||||
@@ -162,7 +162,7 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 17:
|
case 17:
|
||||||
switch (name[namelen - 1]) {
|
switch (name[16]) {
|
||||||
case 'g':
|
case 'g':
|
||||||
if (streq("transfer-encodin", name, 16)) {
|
if (streq("transfer-encodin", name, 16)) {
|
||||||
return NGHTTP2_TOKEN_TRANSFER_ENCODING;
|
return NGHTTP2_TOKEN_TRANSFER_ENCODING;
|
||||||
@@ -225,6 +225,18 @@ static int expect_response_body(nghttp2_stream *stream) {
|
|||||||
stream->status_code != 204;
|
stream->status_code != 204;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* For "http" or "https" URIs, OPTIONS request may have "*" in :path
|
||||||
|
header field to represent system-wide OPTIONS request. Otherwise,
|
||||||
|
:path header field value must start with "/". This function must
|
||||||
|
be called after ":method" header field was received. This function
|
||||||
|
returns nonzero if path is valid.*/
|
||||||
|
static int check_path(nghttp2_stream *stream) {
|
||||||
|
return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 ||
|
||||||
|
((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) ||
|
||||||
|
((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) &&
|
||||||
|
(stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK)));
|
||||||
|
}
|
||||||
|
|
||||||
static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
|
static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
|
||||||
int trailer) {
|
int trailer) {
|
||||||
int token;
|
int token;
|
||||||
@@ -248,18 +260,34 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
|
|||||||
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
|
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
|
||||||
return NGHTTP2_ERR_HTTP_HEADER;
|
return NGHTTP2_ERR_HTTP_HEADER;
|
||||||
}
|
}
|
||||||
if (streq("HEAD", nv->value, nv->valuelen)) {
|
switch (nv->valuelen) {
|
||||||
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
|
case 4:
|
||||||
} else if (streq("CONNECT", nv->value, nv->valuelen)) {
|
if (streq("HEAD", nv->value, nv->valuelen)) {
|
||||||
if (stream->stream_id % 2 == 0) {
|
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
|
||||||
/* we won't allow CONNECT for push */
|
|
||||||
return NGHTTP2_ERR_HTTP_HEADER;
|
|
||||||
}
|
}
|
||||||
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
|
break;
|
||||||
if (stream->http_flags &
|
case 7:
|
||||||
(NGHTTP2_HTTP_FLAG__PATH | NGHTTP2_HTTP_FLAG__SCHEME)) {
|
switch (nv->value[6]) {
|
||||||
return NGHTTP2_ERR_HTTP_HEADER;
|
case 'T':
|
||||||
|
if (streq("CONNECT", nv->value, nv->valuelen)) {
|
||||||
|
if (stream->stream_id % 2 == 0) {
|
||||||
|
/* we won't allow CONNECT for push */
|
||||||
|
return NGHTTP2_ERR_HTTP_HEADER;
|
||||||
|
}
|
||||||
|
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
|
||||||
|
if (stream->http_flags &
|
||||||
|
(NGHTTP2_HTTP_FLAG__PATH | NGHTTP2_HTTP_FLAG__SCHEME)) {
|
||||||
|
return NGHTTP2_ERR_HTTP_HEADER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
if (streq("OPTIONS", nv->value, nv->valuelen)) {
|
||||||
|
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NGHTTP2_TOKEN__PATH:
|
case NGHTTP2_TOKEN__PATH:
|
||||||
@@ -269,6 +297,11 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
|
|||||||
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
|
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
|
||||||
return NGHTTP2_ERR_HTTP_HEADER;
|
return NGHTTP2_ERR_HTTP_HEADER;
|
||||||
}
|
}
|
||||||
|
if (nv->value[0] == '/') {
|
||||||
|
stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
|
||||||
|
} else if (nv->valuelen == 1 && nv->value[0] == '*') {
|
||||||
|
stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case NGHTTP2_TOKEN__SCHEME:
|
case NGHTTP2_TOKEN__SCHEME:
|
||||||
if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
|
if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
|
||||||
@@ -277,6 +310,10 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
|
|||||||
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
|
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
|
||||||
return NGHTTP2_ERR_HTTP_HEADER;
|
return NGHTTP2_ERR_HTTP_HEADER;
|
||||||
}
|
}
|
||||||
|
if ((nv->valuelen == 4 && memieq("http", nv->value, 4)) ||
|
||||||
|
(nv->valuelen == 5 && memieq("https", nv->value, 5))) {
|
||||||
|
stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case NGHTTP2_TOKEN_HOST:
|
case NGHTTP2_TOKEN_HOST:
|
||||||
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
|
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
|
||||||
@@ -434,11 +471,16 @@ int nghttp2_http_on_request_headers(nghttp2_stream *stream,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
stream->content_length = -1;
|
stream->content_length = -1;
|
||||||
} else if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
|
} else {
|
||||||
NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
|
if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) !=
|
||||||
(stream->http_flags &
|
NGHTTP2_HTTP_FLAG_REQ_HEADERS ||
|
||||||
(NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
|
(stream->http_flags &
|
||||||
return -1;
|
(NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!check_path(stream)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
|
if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ typedef uint32_t key_type;
|
|||||||
typedef struct nghttp2_map_entry {
|
typedef struct nghttp2_map_entry {
|
||||||
struct nghttp2_map_entry *next;
|
struct nghttp2_map_entry *next;
|
||||||
key_type key;
|
key_type key;
|
||||||
|
#if SIZEOF_INT_P == 4
|
||||||
|
/* we requires 8 bytes aligment */
|
||||||
|
int64_t pad;
|
||||||
|
#endif
|
||||||
} nghttp2_map_entry;
|
} nghttp2_map_entry;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
@@ -383,6 +383,7 @@ static int session_new(nghttp2_session **session_ptr,
|
|||||||
|
|
||||||
(*session_ptr)->pending_local_max_concurrent_stream =
|
(*session_ptr)->pending_local_max_concurrent_stream =
|
||||||
NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS;
|
NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS;
|
||||||
|
(*session_ptr)->pending_enable_push = 1;
|
||||||
|
|
||||||
if (server) {
|
if (server) {
|
||||||
(*session_ptr)->server = 1;
|
(*session_ptr)->server = 1;
|
||||||
@@ -3901,6 +3902,7 @@ int nghttp2_session_update_local_settings(nghttp2_session *session,
|
|||||||
|
|
||||||
session->pending_local_max_concurrent_stream =
|
session->pending_local_max_concurrent_stream =
|
||||||
NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS;
|
NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS;
|
||||||
|
session->pending_enable_push = 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -4129,7 +4131,8 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session,
|
|||||||
}
|
}
|
||||||
session->last_recv_stream_id = frame->push_promise.promised_stream_id;
|
session->last_recv_stream_id = frame->push_promise.promised_stream_id;
|
||||||
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
|
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
|
||||||
if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) {
|
if (!stream || stream->state == NGHTTP2_STREAM_CLOSING ||
|
||||||
|
!session->pending_enable_push) {
|
||||||
if (!stream) {
|
if (!stream) {
|
||||||
if (session_detect_idle_stream(session, frame->hd.stream_id)) {
|
if (session_detect_idle_stream(session, frame->hd.stream_id)) {
|
||||||
return session_inflate_handle_invalid_connection(
|
return session_inflate_handle_invalid_connection(
|
||||||
@@ -6105,8 +6108,10 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Extract NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS here and use
|
/* Extract NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS and ENABLE_PUSH
|
||||||
it to refuse the incoming streams with RST_STREAM. */
|
here. We use it to refuse the incoming stream and PUSH_PROMISE
|
||||||
|
with RST_STREAM. */
|
||||||
|
|
||||||
for (i = niv; i > 0; --i) {
|
for (i = niv; i > 0; --i) {
|
||||||
if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) {
|
if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) {
|
||||||
session->pending_local_max_concurrent_stream = iv[i - 1].value;
|
session->pending_local_max_concurrent_stream = iv[i - 1].value;
|
||||||
@@ -6114,6 +6119,13 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = niv; i > 0; --i) {
|
||||||
|
if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_ENABLE_PUSH) {
|
||||||
|
session->pending_enable_push = iv[i - 1].value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6210,7 +6222,10 @@ int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
|
|||||||
|
|
||||||
if (data_flags & NGHTTP2_DATA_FLAG_EOF) {
|
if (data_flags & NGHTTP2_DATA_FLAG_EOF) {
|
||||||
aux_data->eof = 1;
|
aux_data->eof = 1;
|
||||||
if (aux_data->flags & NGHTTP2_FLAG_END_STREAM) {
|
/* If NGHTTP2_DATA_FLAG_NO_END_STREAM is set, don't set
|
||||||
|
NGHTTP2_FLAG_END_STREAM */
|
||||||
|
if ((aux_data->flags & NGHTTP2_FLAG_END_STREAM) &&
|
||||||
|
(data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM) == 0) {
|
||||||
frame->hd.flags |= NGHTTP2_FLAG_END_STREAM;
|
frame->hd.flags |= NGHTTP2_FLAG_END_STREAM;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -253,6 +253,9 @@ struct nghttp2_session {
|
|||||||
/* Unacked local SETTINGS_MAX_CONCURRENT_STREAMS value. We use this
|
/* Unacked local SETTINGS_MAX_CONCURRENT_STREAMS value. We use this
|
||||||
to refuse the incoming stream if it exceeds this value. */
|
to refuse the incoming stream if it exceeds this value. */
|
||||||
uint32_t pending_local_max_concurrent_stream;
|
uint32_t pending_local_max_concurrent_stream;
|
||||||
|
/* Unacked local ENABLE_PUSH value. We use this to refuse
|
||||||
|
PUSH_PROMISE before SETTINGS ACK is received. */
|
||||||
|
uint8_t pending_enable_push;
|
||||||
/* Nonzero if the session is server side. */
|
/* Nonzero if the session is server side. */
|
||||||
uint8_t server;
|
uint8_t server;
|
||||||
/* Flags indicating GOAWAY is sent and/or recieved. The flags are
|
/* Flags indicating GOAWAY is sent and/or recieved. The flags are
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ static int stream_push_item(nghttp2_stream *stream, nghttp2_session *session) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
rv = 0;
|
||||||
/* should not reach here */
|
/* should not reach here */
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,10 +120,20 @@ typedef enum {
|
|||||||
/* HTTP method flags */
|
/* HTTP method flags */
|
||||||
NGHTTP2_HTTP_FLAG_METH_CONNECT = 1 << 7,
|
NGHTTP2_HTTP_FLAG_METH_CONNECT = 1 << 7,
|
||||||
NGHTTP2_HTTP_FLAG_METH_HEAD = 1 << 8,
|
NGHTTP2_HTTP_FLAG_METH_HEAD = 1 << 8,
|
||||||
NGHTTP2_HTTP_FLAG_METH_ALL =
|
NGHTTP2_HTTP_FLAG_METH_OPTIONS = 1 << 9,
|
||||||
NGHTTP2_HTTP_FLAG_METH_CONNECT | NGHTTP2_HTTP_FLAG_METH_HEAD,
|
NGHTTP2_HTTP_FLAG_METH_ALL = NGHTTP2_HTTP_FLAG_METH_CONNECT |
|
||||||
|
NGHTTP2_HTTP_FLAG_METH_HEAD |
|
||||||
|
NGHTTP2_HTTP_FLAG_METH_OPTIONS,
|
||||||
|
/* :path category */
|
||||||
|
/* path starts with "/" */
|
||||||
|
NGHTTP2_HTTP_FLAG_PATH_REGULAR = 1 << 10,
|
||||||
|
/* path "*" */
|
||||||
|
NGHTTP2_HTTP_FLAG_PATH_ASTERISK = 1 << 11,
|
||||||
|
/* scheme */
|
||||||
|
/* "http" or "https" scheme */
|
||||||
|
NGHTTP2_HTTP_FLAG_SCHEME_HTTP = 1 << 12,
|
||||||
/* set if final response is expected */
|
/* set if final response is expected */
|
||||||
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 9,
|
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 13,
|
||||||
} nghttp2_http_flag;
|
} nghttp2_http_flag;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|||||||
@@ -155,6 +155,12 @@ static int32_t submit_headers_shared_nva(nghttp2_session *session,
|
|||||||
attach_stream);
|
attach_stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32_t nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id,
|
||||||
|
const nghttp2_nv *nva, size_t nvlen) {
|
||||||
|
return submit_headers_shared_nva(session, NGHTTP2_FLAG_END_STREAM, stream_id,
|
||||||
|
NULL, nva, nvlen, NULL, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
|
int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
|
||||||
int32_t stream_id,
|
int32_t stream_id,
|
||||||
const nghttp2_priority_spec *pri_spec,
|
const nghttp2_priority_spec *pri_spec,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# * The options which do not take argument in the command-line *take*
|
# * The options which do not take argument in the command-line *take*
|
||||||
# argument in the configuration file. Specify 'yes' as argument
|
# argument in the configuration file. Specify 'yes' as argument
|
||||||
# (e.g., spdy-proxy=yes). If other string is given, it disables the
|
# (e.g., http2-proxy=yes). If other string is given, it disables the
|
||||||
# option.
|
# option.
|
||||||
#
|
#
|
||||||
# * To specify private key and certificate file, use private-key-file
|
# * To specify private key and certificate file, use private-key-file
|
||||||
@@ -25,5 +25,5 @@
|
|||||||
# backend=127.0.0.1,80
|
# backend=127.0.0.1,80
|
||||||
# private-key-file=/path/to/server.key
|
# private-key-file=/path/to/server.key
|
||||||
# certificate-file=/path/to/server.crt
|
# certificate-file=/path/to/server.crt
|
||||||
# spdy-proxy=no
|
# http2-proxy=no
|
||||||
# workers=1
|
# workers=1
|
||||||
|
|||||||
@@ -322,8 +322,6 @@ cdef int client_on_header(cnghttp2.nghttp2_session *session,
|
|||||||
logging.debug('client_on_header, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id)
|
logging.debug('client_on_header, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id)
|
||||||
|
|
||||||
if frame.hd.type == cnghttp2.NGHTTP2_HEADERS:
|
if frame.hd.type == cnghttp2.NGHTTP2_HEADERS:
|
||||||
if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_REQUEST:
|
|
||||||
return 0
|
|
||||||
handler = _get_stream_user_data(session, frame.hd.stream_id)
|
handler = _get_stream_user_data(session, frame.hd.stream_id)
|
||||||
elif frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE:
|
elif frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE:
|
||||||
handler = _get_stream_user_data(session, frame.push_promise.promised_stream_id)
|
handler = _get_stream_user_data(session, frame.push_promise.promised_stream_id)
|
||||||
@@ -545,7 +543,7 @@ cdef int client_on_frame_recv(cnghttp2.nghttp2_session *session,
|
|||||||
sys.stderr.write(traceback.format_exc())
|
sys.stderr.write(traceback.format_exc())
|
||||||
return http2._rst_stream(frame.hd.stream_id)
|
return http2._rst_stream(frame.hd.stream_id)
|
||||||
elif frame.hd.type == cnghttp2.NGHTTP2_HEADERS:
|
elif frame.hd.type == cnghttp2.NGHTTP2_HEADERS:
|
||||||
if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_RESPONSE:
|
if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_RESPONSE or frame.headers.cat == cnghttp2.NGHTTP2_HCAT_PUSH_RESPONSE:
|
||||||
handler = _get_stream_user_data(session, frame.hd.stream_id)
|
handler = _get_stream_user_data(session, frame.hd.stream_id)
|
||||||
|
|
||||||
if not handler:
|
if not handler:
|
||||||
@@ -970,7 +968,6 @@ cdef class _HTTP2ClientSessionCore(_HTTP2SessionCoreBase):
|
|||||||
handler.method = push_promise.method
|
handler.method = push_promise.method
|
||||||
handler.host = push_promise.host
|
handler.host = push_promise.host
|
||||||
handler.path = push_promise.path
|
handler.path = push_promise.path
|
||||||
handler.headers = push_promise.headers
|
|
||||||
handler.cookies = push_promise.cookies
|
handler.cookies = push_promise.cookies
|
||||||
handler.stream_id = push_promise.stream_id
|
handler.stream_id = push_promise.stream_id
|
||||||
handler.http2 = self
|
handler.http2 = self
|
||||||
@@ -1033,6 +1030,9 @@ if asyncio:
|
|||||||
path
|
path
|
||||||
This is a value of :path header field.
|
This is a value of :path header field.
|
||||||
|
|
||||||
|
headers
|
||||||
|
Request header fields
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, http2, stream_id):
|
def __init__(self, http2, stream_id):
|
||||||
@@ -1345,6 +1345,11 @@ if asyncio:
|
|||||||
path
|
path
|
||||||
This is a value of :path header field.
|
This is a value of :path header field.
|
||||||
|
|
||||||
|
headers
|
||||||
|
Response header fields. There is a special exception. If this
|
||||||
|
object is passed to push_promise(), this instance variable contains
|
||||||
|
pushed request header fields.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, http2=None, stream_id=-1):
|
def __init__(self, http2=None, stream_id=-1):
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ Config::Config()
|
|||||||
session_option(nullptr), data_ptr(nullptr), padding(0), num_worker(1),
|
session_option(nullptr), data_ptr(nullptr), padding(0), num_worker(1),
|
||||||
header_table_size(-1), port(0), verbose(false), daemon(false),
|
header_table_size(-1), port(0), verbose(false), daemon(false),
|
||||||
verify_client(false), no_tls(false), error_gzip(false),
|
verify_client(false), no_tls(false), error_gzip(false),
|
||||||
early_response(false) {
|
early_response(false), hexdump(false) {
|
||||||
nghttp2_option_new(&session_option);
|
nghttp2_option_new(&session_option);
|
||||||
nghttp2_option_set_recv_client_preface(session_option, 1);
|
nghttp2_option_set_recv_client_preface(session_option, 1);
|
||||||
}
|
}
|
||||||
@@ -173,7 +173,8 @@ class Sessions {
|
|||||||
public:
|
public:
|
||||||
Sessions(struct ev_loop *loop, const Config *config, SSL_CTX *ssl_ctx)
|
Sessions(struct ev_loop *loop, const Config *config, SSL_CTX *ssl_ctx)
|
||||||
: loop_(loop), config_(config), ssl_ctx_(ssl_ctx), callbacks_(nullptr),
|
: loop_(loop), config_(config), ssl_ctx_(ssl_ctx), callbacks_(nullptr),
|
||||||
next_session_id_(1) {
|
next_session_id_(1), tstamp_cached_(ev_now(loop)),
|
||||||
|
cached_date_(util::http_date(tstamp_cached_)) {
|
||||||
nghttp2_session_callbacks_new(&callbacks_);
|
nghttp2_session_callbacks_new(&callbacks_);
|
||||||
|
|
||||||
fill_callback(callbacks_, config_);
|
fill_callback(callbacks_, config_);
|
||||||
@@ -234,17 +235,25 @@ public:
|
|||||||
}
|
}
|
||||||
add_handler(handler.release());
|
add_handler(handler.release());
|
||||||
}
|
}
|
||||||
void update_cached_date() { cached_date_ = util::http_date(time(nullptr)); }
|
void update_cached_date() { cached_date_ = util::http_date(tstamp_cached_); }
|
||||||
const std::string &get_cached_date() const { return cached_date_; }
|
const std::string &get_cached_date() {
|
||||||
|
auto t = ev_now(loop_);
|
||||||
|
if (t != tstamp_cached_) {
|
||||||
|
tstamp_cached_ = t;
|
||||||
|
update_cached_date();
|
||||||
|
}
|
||||||
|
return cached_date_;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::set<Http2Handler *> handlers_;
|
std::set<Http2Handler *> handlers_;
|
||||||
std::string cached_date_;
|
|
||||||
struct ev_loop *loop_;
|
struct ev_loop *loop_;
|
||||||
const Config *config_;
|
const Config *config_;
|
||||||
SSL_CTX *ssl_ctx_;
|
SSL_CTX *ssl_ctx_;
|
||||||
nghttp2_session_callbacks *callbacks_;
|
nghttp2_session_callbacks *callbacks_;
|
||||||
int64_t next_session_id_;
|
int64_t next_session_id_;
|
||||||
|
ev_tstamp tstamp_cached_;
|
||||||
|
std::string cached_date_;
|
||||||
};
|
};
|
||||||
|
|
||||||
Stream::Stream(Http2Handler *handler, int32_t stream_id)
|
Stream::Stream(Http2Handler *handler, int32_t stream_id)
|
||||||
@@ -417,6 +426,11 @@ int Http2Handler::read_clear() {
|
|||||||
if (nread == 0) {
|
if (nread == 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (get_config()->hexdump) {
|
||||||
|
util::hexdump(stdout, buf.data(), nread);
|
||||||
|
}
|
||||||
|
|
||||||
rv = nghttp2_session_mem_recv(session_, buf.data(), nread);
|
rv = nghttp2_session_mem_recv(session_, buf.data(), nread);
|
||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
if (rv != NGHTTP2_ERR_BAD_PREFACE) {
|
if (rv != NGHTTP2_ERR_BAD_PREFACE) {
|
||||||
@@ -539,6 +553,11 @@ int Http2Handler::read_tls() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto nread = rv;
|
auto nread = rv;
|
||||||
|
|
||||||
|
if (get_config()->hexdump) {
|
||||||
|
util::hexdump(stdout, buf.data(), nread);
|
||||||
|
}
|
||||||
|
|
||||||
rv = nghttp2_session_mem_recv(session_, buf.data(), nread);
|
rv = nghttp2_session_mem_recv(session_, buf.data(), nread);
|
||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
if (rv != NGHTTP2_ERR_BAD_PREFACE) {
|
if (rv != NGHTTP2_ERR_BAD_PREFACE) {
|
||||||
@@ -681,12 +700,22 @@ int Http2Handler::submit_file_response(const std::string &status,
|
|||||||
http2::make_nv_ls("content-length", content_length),
|
http2::make_nv_ls("content-length", content_length),
|
||||||
http2::make_nv_ll("cache-control", "max-age=3600"),
|
http2::make_nv_ll("cache-control", "max-age=3600"),
|
||||||
http2::make_nv_ls("date", sessions_->get_cached_date()),
|
http2::make_nv_ls("date", sessions_->get_cached_date()),
|
||||||
http2::make_nv_ll("", ""));
|
http2::make_nv_ll("", ""), http2::make_nv_ll("", ""));
|
||||||
size_t nvlen = 5;
|
size_t nvlen = 5;
|
||||||
if (last_modified != 0) {
|
if (last_modified != 0) {
|
||||||
last_modified_str = util::http_date(last_modified);
|
last_modified_str = util::http_date(last_modified);
|
||||||
nva[nvlen++] = http2::make_nv_ls("last-modified", last_modified_str);
|
nva[nvlen++] = http2::make_nv_ls("last-modified", last_modified_str);
|
||||||
}
|
}
|
||||||
|
auto &trailer = get_config()->trailer;
|
||||||
|
std::string trailer_names;
|
||||||
|
if (!trailer.empty()) {
|
||||||
|
trailer_names = trailer[0].name;
|
||||||
|
for (size_t i = 1; i < trailer.size(); ++i) {
|
||||||
|
trailer_names += ", ";
|
||||||
|
trailer_names += trailer[i].name;
|
||||||
|
}
|
||||||
|
nva[nvlen++] = http2::make_nv_ls("trailer", trailer_names);
|
||||||
|
}
|
||||||
return nghttp2_submit_response(session_, stream->stream_id, nva.data(), nvlen,
|
return nghttp2_submit_response(session_, stream->stream_id, nva.data(), nvlen,
|
||||||
data_prd);
|
data_prd);
|
||||||
}
|
}
|
||||||
@@ -800,6 +829,7 @@ void Http2Handler::terminate_session(uint32_t error_code) {
|
|||||||
ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
|
ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
|
||||||
uint8_t *buf, size_t length, uint32_t *data_flags,
|
uint8_t *buf, size_t length, uint32_t *data_flags,
|
||||||
nghttp2_data_source *source, void *user_data) {
|
nghttp2_data_source *source, void *user_data) {
|
||||||
|
int rv;
|
||||||
auto hd = static_cast<Http2Handler *>(user_data);
|
auto hd = static_cast<Http2Handler *>(user_data);
|
||||||
auto stream = hd->get_stream(stream_id);
|
auto stream = hd->get_stream(stream_id);
|
||||||
|
|
||||||
@@ -820,6 +850,23 @@ ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
|
|||||||
if (nread == 0 || stream->body_left <= 0) {
|
if (nread == 0 || stream->body_left <= 0) {
|
||||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||||
|
|
||||||
|
auto config = hd->get_config();
|
||||||
|
if (!config->trailer.empty()) {
|
||||||
|
std::vector<nghttp2_nv> nva;
|
||||||
|
nva.reserve(config->trailer.size());
|
||||||
|
for (auto &kv : config->trailer) {
|
||||||
|
nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index));
|
||||||
|
}
|
||||||
|
rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size());
|
||||||
|
if (rv != 0) {
|
||||||
|
if (nghttp2_is_fatal(rv)) {
|
||||||
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (nghttp2_session_get_stream_remote_close(session, stream_id) == 0) {
|
if (nghttp2_session_get_stream_remote_close(session, stream_id) == 0) {
|
||||||
remove_stream_read_timeout(stream);
|
remove_stream_read_timeout(stream);
|
||||||
remove_stream_write_timeout(stream);
|
remove_stream_write_timeout(stream);
|
||||||
@@ -1280,7 +1327,6 @@ void worker_acceptcb(struct ev_loop *loop, ev_async *w, int revents) {
|
|||||||
namespace {
|
namespace {
|
||||||
void run_worker(Worker *worker) {
|
void run_worker(Worker *worker) {
|
||||||
auto loop = worker->sessions->get_loop();
|
auto loop = worker->sessions->get_loop();
|
||||||
worker->sessions->update_cached_date();
|
|
||||||
|
|
||||||
ev_run(loop, 0);
|
ev_run(loop, 0);
|
||||||
}
|
}
|
||||||
@@ -1361,10 +1407,7 @@ public:
|
|||||||
auto fd = accept(fd_, nullptr, nullptr);
|
auto fd = accept(fd_, nullptr, nullptr);
|
||||||
#endif // !HAVE_ACCEPT4
|
#endif // !HAVE_ACCEPT4
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
#ifndef HAVE_ACCEPT4
|
#ifndef HAVE_ACCEPT4
|
||||||
util::make_socket_nonblocking(fd);
|
util::make_socket_nonblocking(fd);
|
||||||
@@ -1616,10 +1659,6 @@ int HttpServer::run() {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config_->num_worker == 1) {
|
|
||||||
sessions.update_cached_date();
|
|
||||||
}
|
|
||||||
|
|
||||||
ev_run(loop, 0);
|
ev_run(loop, 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ namespace nghttp2 {
|
|||||||
|
|
||||||
struct Config {
|
struct Config {
|
||||||
std::map<std::string, std::vector<std::string>> push;
|
std::map<std::string, std::vector<std::string>> push;
|
||||||
|
Headers trailer;
|
||||||
std::string htdocs;
|
std::string htdocs;
|
||||||
std::string host;
|
std::string host;
|
||||||
std::string private_key_file;
|
std::string private_key_file;
|
||||||
@@ -70,6 +71,7 @@ struct Config {
|
|||||||
bool no_tls;
|
bool no_tls;
|
||||||
bool error_gzip;
|
bool error_gzip;
|
||||||
bool early_response;
|
bool early_response;
|
||||||
|
bool hexdump;
|
||||||
Config();
|
Config();
|
||||||
~Config();
|
~Config();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -173,18 +173,39 @@ DISTCLEANFILES = $(pkgconfig_DATA)
|
|||||||
lib_LTLIBRARIES = libnghttp2_asio.la
|
lib_LTLIBRARIES = libnghttp2_asio.la
|
||||||
|
|
||||||
libnghttp2_asio_la_SOURCES = \
|
libnghttp2_asio_la_SOURCES = \
|
||||||
asio_connection.h \
|
|
||||||
asio_server.cc asio_server.h \
|
|
||||||
asio_io_service_pool.cc asio_io_service_pool.h \
|
|
||||||
asio_http2_handler.cc asio_http2_handler.h \
|
|
||||||
asio_http2_impl.cc asio_http2_impl.h \
|
|
||||||
util.cc util.h http2.cc http2.h \
|
util.cc util.h http2.cc http2.h \
|
||||||
ssl.cc ssl.h
|
ssl.cc ssl.h \
|
||||||
|
asio_common.cc asio_common.h \
|
||||||
|
asio_io_service_pool.cc asio_io_service_pool.h \
|
||||||
|
asio_server_http2.cc \
|
||||||
|
asio_server_http2_impl.cc asio_server_http2_impl.h \
|
||||||
|
asio_server.cc asio_server.h \
|
||||||
|
asio_server_http2_handler.cc asio_server_http2_handler.h \
|
||||||
|
asio_server_connection.h \
|
||||||
|
asio_server_request.cc \
|
||||||
|
asio_server_request_impl.cc asio_server_request_impl.h \
|
||||||
|
asio_server_response.cc \
|
||||||
|
asio_server_response_impl.cc asio_server_response_impl.h \
|
||||||
|
asio_server_stream.cc asio_server_stream.h \
|
||||||
|
asio_server_serve_mux.cc asio_server_serve_mux.h \
|
||||||
|
asio_server_request_handler.cc asio_server_request_handler.h \
|
||||||
|
asio_server_tls_context.cc asio_server_tls_context.h \
|
||||||
|
asio_client_session.cc \
|
||||||
|
asio_client_session_impl.cc asio_client_session_impl.h \
|
||||||
|
asio_client_session_tcp_impl.cc asio_client_session_tcp_impl.h \
|
||||||
|
asio_client_session_tls_impl.cc asio_client_session_tls_impl.h \
|
||||||
|
asio_client_response.cc \
|
||||||
|
asio_client_response_impl.cc asio_client_response_impl.h \
|
||||||
|
asio_client_request.cc \
|
||||||
|
asio_client_request_impl.cc asio_client_request_impl.h \
|
||||||
|
asio_client_stream.cc asio_client_stream.h \
|
||||||
|
asio_client_tls_context.cc asio_client_tls_context.h
|
||||||
|
|
||||||
libnghttp2_asio_la_CPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS}
|
libnghttp2_asio_la_CPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS}
|
||||||
libnghttp2_asio_la_LDFLAGS = -no-undefined -version-info 0:0:0
|
libnghttp2_asio_la_LDFLAGS = -no-undefined -version-info 1:0:0
|
||||||
libnghttp2_asio_la_LIBADD = \
|
libnghttp2_asio_la_LIBADD = \
|
||||||
$(top_builddir)/lib/libnghttp2.la \
|
$(top_builddir)/lib/libnghttp2.la \
|
||||||
|
$(top_builddir)/third-party/libhttp-parser.la \
|
||||||
${BOOST_LDFLAGS} \
|
${BOOST_LDFLAGS} \
|
||||||
${BOOST_ASIO_LIB} \
|
${BOOST_ASIO_LIB} \
|
||||||
${BOOST_THREAD_LIB} \
|
${BOOST_THREAD_LIB} \
|
||||||
|
|||||||
@@ -164,11 +164,8 @@ const char *ansi_escend() { return color_output ? "\033[0m" : ""; }
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void print_nv(nghttp2_nv *nv) {
|
void print_nv(nghttp2_nv *nv) {
|
||||||
fprintf(outfile, "%s", ansi_esc("\033[1;34m"));
|
fprintf(outfile, "%s%s%s: %s\n", ansi_esc("\033[1;34m"), nv->name,
|
||||||
fwrite(nv->name, nv->namelen, 1, outfile);
|
ansi_escend(), nv->value);
|
||||||
fprintf(outfile, "%s: ", ansi_escend());
|
|
||||||
fwrite(nv->value, nv->valuelen, 1, outfile);
|
|
||||||
fprintf(outfile, "\n");
|
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
namespace {
|
namespace {
|
||||||
|
|||||||
67
src/asio_client_request.cc
Normal file
67
src/asio_client_request.cc
Normal 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
|
||||||
110
src/asio_client_request_impl.cc
Normal file
110
src/asio_client_request_impl.cc
Normal 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
|
||||||
93
src/asio_client_request_impl.h
Normal file
93
src/asio_client_request_impl.h
Normal 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
|
||||||
53
src/asio_client_response.cc
Normal file
53
src/asio_client_response.cc
Normal 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
|
||||||
57
src/asio_client_response_impl.cc
Normal file
57
src/asio_client_response_impl.cc
Normal 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
|
||||||
69
src/asio_client_response_impl.h
Normal file
69
src/asio_client_response_impl.h
Normal 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
|
||||||
98
src/asio_client_session.cc
Normal file
98
src/asio_client_session.cc
Normal 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
|
||||||
621
src/asio_client_session_impl.cc
Normal file
621
src/asio_client_session_impl.cc
Normal 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
|
||||||
122
src/asio_client_session_impl.h
Normal file
122
src/asio_client_session_impl.h
Normal 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
|
||||||
69
src/asio_client_session_tcp_impl.cc
Normal file
69
src/asio_client_session_tcp_impl.cc
Normal 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
|
||||||
60
src/asio_client_session_tcp_impl.h
Normal file
60
src/asio_client_session_tcp_impl.h
Normal 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
|
||||||
85
src/asio_client_session_tls_impl.cc
Normal file
85
src/asio_client_session_tls_impl.cc
Normal 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
|
||||||
63
src/asio_client_session_tls_impl.h
Normal file
63
src/asio_client_session_tls_impl.h
Normal 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
59
src/asio_client_stream.cc
Normal 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
68
src/asio_client_stream.h
Normal 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
|
||||||
64
src/asio_client_tls_context.cc
Normal file
64
src/asio_client_tls_context.cc
Normal 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
|
||||||
32
src/asio_client_tls_context.h
Normal file
32
src/asio_client_tls_context.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef ASIO_CLIENT_TLS_CONTEXT_H
|
||||||
|
#define ASIO_CLIENT_TLS_CONTEXT_H
|
||||||
|
|
||||||
|
#include "nghttp2_config.h"
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2_client.h>
|
||||||
|
|
||||||
|
#endif // ASIO_CLIENT_TLS_CONTEXT_H
|
||||||
148
src/asio_common.cc
Normal file
148
src/asio_common.cc
Normal 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
65
src/asio_common.h
Normal 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
|
||||||
@@ -1,711 +0,0 @@
|
|||||||
/*
|
|
||||||
* nghttp2 - HTTP/2 C Library
|
|
||||||
*
|
|
||||||
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
* a copy of this software and associated documentation files (the
|
|
||||||
* "Software"), to deal in the Software without restriction, including
|
|
||||||
* without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
* permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
* the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be
|
|
||||||
* included in all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#include "asio_http2_handler.h"
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include "http2.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "template.h"
|
|
||||||
|
|
||||||
namespace nghttp2 {
|
|
||||||
|
|
||||||
namespace asio_http2 {
|
|
||||||
|
|
||||||
channel::channel() : impl_(make_unique<channel_impl>()) {}
|
|
||||||
|
|
||||||
void channel::post(void_cb cb) { impl_->post(std::move(cb)); }
|
|
||||||
|
|
||||||
channel_impl &channel::impl() { return *impl_; }
|
|
||||||
|
|
||||||
channel_impl::channel_impl() : strand_(nullptr) {}
|
|
||||||
|
|
||||||
void channel_impl::post(void_cb cb) { strand_->post(std::move(cb)); }
|
|
||||||
|
|
||||||
void channel_impl::strand(boost::asio::io_service::strand *strand) {
|
|
||||||
strand_ = strand;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace server {
|
|
||||||
|
|
||||||
extern std::shared_ptr<std::string> cached_date;
|
|
||||||
|
|
||||||
request::request() : impl_(make_unique<request_impl>()) {}
|
|
||||||
|
|
||||||
const std::vector<header> &request::headers() const { return impl_->headers(); }
|
|
||||||
|
|
||||||
const std::string &request::method() const { return impl_->method(); }
|
|
||||||
|
|
||||||
const std::string &request::scheme() const { return impl_->scheme(); }
|
|
||||||
|
|
||||||
const std::string &request::authority() const { return impl_->authority(); }
|
|
||||||
|
|
||||||
const std::string &request::host() const { return impl_->host(); }
|
|
||||||
|
|
||||||
const std::string &request::path() const { return impl_->path(); }
|
|
||||||
|
|
||||||
bool request::push(std::string method, std::string path,
|
|
||||||
std::vector<header> headers) {
|
|
||||||
return impl_->push(std::move(method), std::move(path), std::move(headers));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool request::pushed() const { return impl_->pushed(); }
|
|
||||||
|
|
||||||
bool request::closed() const { return impl_->closed(); }
|
|
||||||
|
|
||||||
void request::on_data(data_cb cb) { return impl_->on_data(std::move(cb)); }
|
|
||||||
|
|
||||||
void request::on_end(void_cb cb) { return impl_->on_end(std::move(cb)); }
|
|
||||||
|
|
||||||
bool request::run_task(thread_cb start) {
|
|
||||||
return impl_->run_task(std::move(start));
|
|
||||||
}
|
|
||||||
|
|
||||||
request_impl &request::impl() { return *impl_; }
|
|
||||||
|
|
||||||
response::response() : impl_(make_unique<response_impl>()) {}
|
|
||||||
|
|
||||||
void response::write_head(unsigned int status_code,
|
|
||||||
std::vector<header> headers) {
|
|
||||||
impl_->write_head(status_code, std::move(headers));
|
|
||||||
}
|
|
||||||
|
|
||||||
void response::end(std::string data) { impl_->end(std::move(data)); }
|
|
||||||
|
|
||||||
void response::end(read_cb cb) { impl_->end(std::move(cb)); }
|
|
||||||
|
|
||||||
void response::resume() { impl_->resume(); }
|
|
||||||
|
|
||||||
unsigned int response::status_code() const { return impl_->status_code(); }
|
|
||||||
|
|
||||||
bool response::started() const { return impl_->started(); }
|
|
||||||
|
|
||||||
response_impl &response::impl() { return *impl_; }
|
|
||||||
|
|
||||||
request_impl::request_impl() : pushed_(false) {}
|
|
||||||
|
|
||||||
const std::vector<header> &request_impl::headers() const { return headers_; }
|
|
||||||
|
|
||||||
const std::string &request_impl::method() const { return method_; }
|
|
||||||
|
|
||||||
const std::string &request_impl::scheme() const { return scheme_; }
|
|
||||||
|
|
||||||
const std::string &request_impl::authority() const { return authority_; }
|
|
||||||
|
|
||||||
const std::string &request_impl::host() const { return host_; }
|
|
||||||
|
|
||||||
const std::string &request_impl::path() const { return path_; }
|
|
||||||
|
|
||||||
void request_impl::set_header(std::vector<header> headers) {
|
|
||||||
headers_ = std::move(headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
void request_impl::add_header(std::string name, std::string value) {
|
|
||||||
headers_.push_back(header{std::move(name), std::move(value)});
|
|
||||||
}
|
|
||||||
|
|
||||||
void request_impl::method(std::string arg) { method_ = std::move(arg); }
|
|
||||||
|
|
||||||
void request_impl::scheme(std::string arg) { scheme_ = std::move(arg); }
|
|
||||||
|
|
||||||
void request_impl::authority(std::string arg) { authority_ = std::move(arg); }
|
|
||||||
|
|
||||||
void request_impl::host(std::string arg) { host_ = std::move(arg); }
|
|
||||||
|
|
||||||
void request_impl::path(std::string arg) { path_ = std::move(arg); }
|
|
||||||
|
|
||||||
bool request_impl::push(std::string method, std::string path,
|
|
||||||
std::vector<header> headers) {
|
|
||||||
if (closed()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto handler = handler_.lock();
|
|
||||||
auto stream = stream_.lock();
|
|
||||||
auto rv = handler->push_promise(*stream, std::move(method), std::move(path),
|
|
||||||
std::move(headers));
|
|
||||||
return rv == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool request_impl::pushed() const { return pushed_; }
|
|
||||||
|
|
||||||
void request_impl::pushed(bool f) { pushed_ = f; }
|
|
||||||
|
|
||||||
bool request_impl::closed() const {
|
|
||||||
return handler_.expired() || stream_.expired();
|
|
||||||
}
|
|
||||||
|
|
||||||
void request_impl::on_data(data_cb cb) { on_data_cb_ = std::move(cb); }
|
|
||||||
|
|
||||||
void request_impl::on_end(void_cb cb) { on_end_cb_ = std::move(cb); }
|
|
||||||
|
|
||||||
bool request_impl::run_task(thread_cb start) {
|
|
||||||
if (closed()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto handler = handler_.lock();
|
|
||||||
|
|
||||||
return handler->run_task(std::move(start));
|
|
||||||
}
|
|
||||||
|
|
||||||
void request_impl::handler(std::weak_ptr<http2_handler> h) {
|
|
||||||
handler_ = std::move(h);
|
|
||||||
}
|
|
||||||
|
|
||||||
void request_impl::stream(std::weak_ptr<http2_stream> s) {
|
|
||||||
stream_ = std::move(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
void request_impl::call_on_data(const uint8_t *data, std::size_t len) {
|
|
||||||
if (on_data_cb_) {
|
|
||||||
on_data_cb_(data, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void request_impl::call_on_end() {
|
|
||||||
if (on_end_cb_) {
|
|
||||||
on_end_cb_();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
response_impl::response_impl() : status_code_(200), started_(false) {}
|
|
||||||
|
|
||||||
unsigned int response_impl::status_code() const { return status_code_; }
|
|
||||||
|
|
||||||
void response_impl::write_head(unsigned int status_code,
|
|
||||||
std::vector<header> headers) {
|
|
||||||
status_code_ = status_code;
|
|
||||||
headers_ = std::move(headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
void response_impl::end(std::string data) {
|
|
||||||
if (started_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto strio = std::make_shared<std::pair<std::string, size_t>>(std::move(data),
|
|
||||||
data.size());
|
|
||||||
auto read_cb = [strio](uint8_t *buf, size_t len) {
|
|
||||||
auto nread = std::min(len, strio->second);
|
|
||||||
memcpy(buf, strio->first.c_str(), nread);
|
|
||||||
strio->second -= nread;
|
|
||||||
if (strio->second == 0) {
|
|
||||||
return std::make_pair(nread, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::make_pair(nread, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
end(std::move(read_cb));
|
|
||||||
}
|
|
||||||
|
|
||||||
void response_impl::end(read_cb cb) {
|
|
||||||
if (started_ || closed()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
read_cb_ = std::move(cb);
|
|
||||||
started_ = true;
|
|
||||||
|
|
||||||
auto handler = handler_.lock();
|
|
||||||
auto stream = stream_.lock();
|
|
||||||
|
|
||||||
if (handler->start_response(*stream) != 0) {
|
|
||||||
handler->stream_error(stream->get_stream_id(), NGHTTP2_INTERNAL_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!handler->inside_callback()) {
|
|
||||||
handler->initiate_write();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool response_impl::closed() const {
|
|
||||||
return handler_.expired() || stream_.expired();
|
|
||||||
}
|
|
||||||
|
|
||||||
void response_impl::resume() {
|
|
||||||
if (closed()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto handler = handler_.lock();
|
|
||||||
auto stream = stream_.lock();
|
|
||||||
handler->resume(*stream);
|
|
||||||
|
|
||||||
if (!handler->inside_callback()) {
|
|
||||||
handler->initiate_write();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool response_impl::started() const { return started_; }
|
|
||||||
|
|
||||||
const std::vector<header> &response_impl::headers() const { return headers_; }
|
|
||||||
|
|
||||||
void response_impl::handler(std::weak_ptr<http2_handler> h) {
|
|
||||||
handler_ = std::move(h);
|
|
||||||
}
|
|
||||||
|
|
||||||
void response_impl::stream(std::weak_ptr<http2_stream> s) {
|
|
||||||
stream_ = std::move(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<ssize_t, bool> response_impl::call_read(uint8_t *data,
|
|
||||||
std::size_t len) {
|
|
||||||
if (read_cb_) {
|
|
||||||
return read_cb_(data, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::make_pair(0, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
http2_stream::http2_stream(int32_t stream_id)
|
|
||||||
: request_(std::make_shared<request>()),
|
|
||||||
response_(std::make_shared<response>()), stream_id_(stream_id) {}
|
|
||||||
|
|
||||||
int32_t http2_stream::get_stream_id() const { return stream_id_; }
|
|
||||||
|
|
||||||
const std::shared_ptr<request> &http2_stream::get_request() { return request_; }
|
|
||||||
|
|
||||||
const std::shared_ptr<response> &http2_stream::get_response() {
|
|
||||||
return response_;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
int stream_error(nghttp2_session *session, int32_t stream_id,
|
|
||||||
uint32_t error_code) {
|
|
||||||
return nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
|
|
||||||
error_code);
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
int on_begin_headers_callback(nghttp2_session *session,
|
|
||||||
const nghttp2_frame *frame, void *user_data) {
|
|
||||||
auto handler = static_cast<http2_handler *>(user_data);
|
|
||||||
|
|
||||||
if (frame->hd.type != NGHTTP2_HEADERS ||
|
|
||||||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
handler->create_stream(frame->hd.stream_id);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
|
||||||
const uint8_t *name, size_t namelen,
|
|
||||||
const uint8_t *value, size_t valuelen, uint8_t flags,
|
|
||||||
void *user_data) {
|
|
||||||
auto handler = static_cast<http2_handler *>(user_data);
|
|
||||||
auto stream_id = frame->hd.stream_id;
|
|
||||||
|
|
||||||
if (frame->hd.type != NGHTTP2_HEADERS ||
|
|
||||||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto stream = handler->find_stream(stream_id);
|
|
||||||
if (!stream) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto &req = stream->get_request()->impl();
|
|
||||||
|
|
||||||
switch (nghttp2::http2::lookup_token(name, namelen)) {
|
|
||||||
case nghttp2::http2::HD__METHOD:
|
|
||||||
req.method(std::string(value, value + valuelen));
|
|
||||||
break;
|
|
||||||
case nghttp2::http2::HD__SCHEME:
|
|
||||||
req.scheme(std::string(value, value + valuelen));
|
|
||||||
break;
|
|
||||||
case nghttp2::http2::HD__AUTHORITY:
|
|
||||||
req.authority(std::string(value, value + valuelen));
|
|
||||||
break;
|
|
||||||
case nghttp2::http2::HD__PATH:
|
|
||||||
req.path(std::string(value, value + valuelen));
|
|
||||||
break;
|
|
||||||
case nghttp2::http2::HD_HOST:
|
|
||||||
req.host(std::string(value, value + valuelen));
|
|
||||||
// fall through
|
|
||||||
default:
|
|
||||||
req.add_header(std::string(name, name + namelen),
|
|
||||||
std::string(value, value + valuelen));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
|
||||||
void *user_data) {
|
|
||||||
auto handler = static_cast<http2_handler *>(user_data);
|
|
||||||
auto stream = handler->find_stream(frame->hd.stream_id);
|
|
||||||
|
|
||||||
switch (frame->hd.type) {
|
|
||||||
case NGHTTP2_DATA:
|
|
||||||
if (!stream) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
|
||||||
stream->get_request()->impl().call_on_end();
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case NGHTTP2_HEADERS: {
|
|
||||||
if (!stream || frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto &req = stream->get_request()->impl();
|
|
||||||
|
|
||||||
if (req.host().empty()) {
|
|
||||||
req.host(req.authority());
|
|
||||||
}
|
|
||||||
|
|
||||||
handler->call_on_request(*stream);
|
|
||||||
|
|
||||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
|
||||||
stream->get_request()->impl().call_on_end();
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
|
||||||
int32_t stream_id, const uint8_t *data,
|
|
||||||
size_t len, void *user_data) {
|
|
||||||
auto handler = static_cast<http2_handler *>(user_data);
|
|
||||||
auto stream = handler->find_stream(stream_id);
|
|
||||||
|
|
||||||
if (!stream) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream->get_request()->impl().call_on_data(data, len);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
|
||||||
uint32_t error_code, void *user_data) {
|
|
||||||
auto handler = static_cast<http2_handler *>(user_data);
|
|
||||||
|
|
||||||
handler->close_stream(stream_id);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
|
||||||
void *user_data) {
|
|
||||||
auto handler = static_cast<http2_handler *>(user_data);
|
|
||||||
|
|
||||||
if (frame->hd.type != NGHTTP2_PUSH_PROMISE) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto stream = handler->find_stream(frame->push_promise.promised_stream_id);
|
|
||||||
|
|
||||||
if (!stream) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
handler->call_on_request(*stream);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
int on_frame_not_send_callback(nghttp2_session *session,
|
|
||||||
const nghttp2_frame *frame, int lib_error_code,
|
|
||||||
void *user_data) {
|
|
||||||
if (frame->hd.type != NGHTTP2_HEADERS) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Issue RST_STREAM so that stream does not hang around.
|
|
||||||
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
|
|
||||||
NGHTTP2_INTERNAL_ERROR);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
http2_handler::http2_handler(boost::asio::io_service &io_service,
|
|
||||||
boost::asio::io_service &task_io_service_,
|
|
||||||
connection_write writefun, request_cb cb)
|
|
||||||
: writefun_(writefun), request_cb_(std::move(cb)), io_service_(io_service),
|
|
||||||
task_io_service_(task_io_service_),
|
|
||||||
strand_(std::make_shared<boost::asio::io_service::strand>(io_service_)),
|
|
||||||
session_(nullptr), buf_(nullptr), buflen_(0), inside_callback_(false) {}
|
|
||||||
|
|
||||||
http2_handler::~http2_handler() { nghttp2_session_del(session_); }
|
|
||||||
|
|
||||||
int http2_handler::start() {
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
nghttp2_session_callbacks *callbacks;
|
|
||||||
rv = nghttp2_session_callbacks_new(&callbacks);
|
|
||||||
if (rv != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto cb_del = defer(nghttp2_session_callbacks_del, callbacks);
|
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
|
||||||
callbacks, on_begin_headers_callback);
|
|
||||||
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
|
||||||
on_header_callback);
|
|
||||||
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
|
||||||
on_frame_recv_callback);
|
|
||||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
|
||||||
callbacks, on_data_chunk_recv_callback);
|
|
||||||
nghttp2_session_callbacks_set_on_stream_close_callback(
|
|
||||||
callbacks, on_stream_close_callback);
|
|
||||||
nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
|
|
||||||
on_frame_send_callback);
|
|
||||||
nghttp2_session_callbacks_set_on_frame_not_send_callback(
|
|
||||||
callbacks, on_frame_not_send_callback);
|
|
||||||
|
|
||||||
nghttp2_option *option;
|
|
||||||
rv = nghttp2_option_new(&option);
|
|
||||||
if (rv != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto opt_del = defer(nghttp2_option_del, option);
|
|
||||||
|
|
||||||
nghttp2_option_set_recv_client_preface(option, 1);
|
|
||||||
|
|
||||||
rv = nghttp2_session_server_new2(&session_, callbacks, this, option);
|
|
||||||
if (rv != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
nghttp2_settings_entry ent{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100};
|
|
||||||
nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, &ent, 1);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<http2_stream> http2_handler::create_stream(int32_t stream_id) {
|
|
||||||
auto stream = std::make_shared<http2_stream>(stream_id);
|
|
||||||
streams_.emplace(stream_id, stream);
|
|
||||||
|
|
||||||
auto self = shared_from_this();
|
|
||||||
auto &req = stream->get_request()->impl();
|
|
||||||
auto &res = stream->get_response()->impl();
|
|
||||||
req.handler(self);
|
|
||||||
req.stream(stream);
|
|
||||||
res.handler(self);
|
|
||||||
res.stream(stream);
|
|
||||||
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
void http2_handler::close_stream(int32_t stream_id) {
|
|
||||||
streams_.erase(stream_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<http2_stream> http2_handler::find_stream(int32_t stream_id) {
|
|
||||||
auto i = streams_.find(stream_id);
|
|
||||||
if (i == std::end(streams_)) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (*i).second;
|
|
||||||
}
|
|
||||||
|
|
||||||
void http2_handler::call_on_request(http2_stream &stream) {
|
|
||||||
request_cb_(stream.get_request(), stream.get_response());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool http2_handler::should_stop() const {
|
|
||||||
return !nghttp2_session_want_read(session_) &&
|
|
||||||
!nghttp2_session_want_write(session_);
|
|
||||||
}
|
|
||||||
|
|
||||||
int http2_handler::start_response(http2_stream &stream) {
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
auto &res = stream.get_response()->impl();
|
|
||||||
auto &headers = res.headers();
|
|
||||||
auto nva = std::vector<nghttp2_nv>();
|
|
||||||
nva.reserve(2 + headers.size());
|
|
||||||
auto status = util::utos(res.status_code());
|
|
||||||
auto date = cached_date;
|
|
||||||
nva.push_back(nghttp2::http2::make_nv_ls(":status", status));
|
|
||||||
nva.push_back(nghttp2::http2::make_nv_ls("date", *date));
|
|
||||||
for (auto &hd : headers) {
|
|
||||||
nva.push_back(nghttp2::http2::make_nv(hd.name, hd.value));
|
|
||||||
}
|
|
||||||
|
|
||||||
nghttp2_data_provider prd;
|
|
||||||
prd.source.ptr = &stream;
|
|
||||||
prd.read_callback =
|
|
||||||
[](nghttp2_session *session, int32_t stream_id, uint8_t *buf,
|
|
||||||
size_t length, uint32_t *data_flags, nghttp2_data_source *source,
|
|
||||||
void *user_data) -> ssize_t {
|
|
||||||
auto &stream = *static_cast<http2_stream *>(source->ptr);
|
|
||||||
auto rv = stream.get_response()->impl().call_read(buf, length);
|
|
||||||
if (rv.first < 0) {
|
|
||||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rv.second) {
|
|
||||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
|
||||||
} else if (rv.first == 0) {
|
|
||||||
return NGHTTP2_ERR_DEFERRED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rv.first;
|
|
||||||
};
|
|
||||||
|
|
||||||
rv = nghttp2_submit_response(session_, stream.get_stream_id(), nva.data(),
|
|
||||||
nva.size(), &prd);
|
|
||||||
|
|
||||||
if (rv != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void http2_handler::enter_callback() {
|
|
||||||
assert(!inside_callback_);
|
|
||||||
inside_callback_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void http2_handler::leave_callback() {
|
|
||||||
assert(inside_callback_);
|
|
||||||
inside_callback_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool http2_handler::inside_callback() const { return inside_callback_; }
|
|
||||||
|
|
||||||
void http2_handler::stream_error(int32_t stream_id, uint32_t error_code) {
|
|
||||||
::nghttp2::asio_http2::server::stream_error(session_, stream_id, error_code);
|
|
||||||
}
|
|
||||||
|
|
||||||
void http2_handler::initiate_write() { writefun_(); }
|
|
||||||
|
|
||||||
void http2_handler::resume(http2_stream &stream) {
|
|
||||||
nghttp2_session_resume_data(session_, stream.get_stream_id());
|
|
||||||
}
|
|
||||||
|
|
||||||
int http2_handler::push_promise(http2_stream &stream, std::string method,
|
|
||||||
std::string path, std::vector<header> headers) {
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
auto &req = stream.get_request()->impl();
|
|
||||||
|
|
||||||
auto nva = std::vector<nghttp2_nv>();
|
|
||||||
nva.reserve(5 + headers.size());
|
|
||||||
nva.push_back(nghttp2::http2::make_nv_ls(":method", method));
|
|
||||||
nva.push_back(nghttp2::http2::make_nv_ls(":scheme", req.scheme()));
|
|
||||||
if (!req.authority().empty()) {
|
|
||||||
nva.push_back(nghttp2::http2::make_nv_ls(":authority", req.authority()));
|
|
||||||
}
|
|
||||||
nva.push_back(nghttp2::http2::make_nv_ls(":path", path));
|
|
||||||
if (!req.host().empty()) {
|
|
||||||
nva.push_back(nghttp2::http2::make_nv_ls("host", req.host()));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &hd : headers) {
|
|
||||||
nva.push_back(nghttp2::http2::make_nv(hd.name, hd.value));
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = nghttp2_submit_push_promise(session_, NGHTTP2_FLAG_NONE,
|
|
||||||
stream.get_stream_id(), nva.data(),
|
|
||||||
nva.size(), nullptr);
|
|
||||||
|
|
||||||
if (rv < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto promised_stream = create_stream(rv);
|
|
||||||
auto &promised_req = promised_stream->get_request()->impl();
|
|
||||||
promised_req.pushed(true);
|
|
||||||
promised_req.method(std::move(method));
|
|
||||||
promised_req.scheme(req.scheme());
|
|
||||||
promised_req.authority(req.authority());
|
|
||||||
promised_req.path(std::move(path));
|
|
||||||
promised_req.host(req.host());
|
|
||||||
promised_req.set_header(std::move(headers));
|
|
||||||
if (!req.host().empty()) {
|
|
||||||
promised_req.add_header("host", req.host());
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool http2_handler::run_task(thread_cb start) {
|
|
||||||
auto strand = strand_;
|
|
||||||
|
|
||||||
try {
|
|
||||||
task_io_service_.post([start, strand]() {
|
|
||||||
channel chan;
|
|
||||||
chan.impl().strand(strand.get());
|
|
||||||
|
|
||||||
start(chan);
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (std::exception &ex) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boost::asio::io_service &http2_handler::io_service() { return io_service_; }
|
|
||||||
|
|
||||||
callback_guard::callback_guard(http2_handler &h) : handler(h) {
|
|
||||||
handler.enter_callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
callback_guard::~callback_guard() { handler.leave_callback(); }
|
|
||||||
|
|
||||||
} // namespace server
|
|
||||||
|
|
||||||
} // namespace asio_http2
|
|
||||||
|
|
||||||
} // namespace nghttp2
|
|
||||||
@@ -1,265 +0,0 @@
|
|||||||
/*
|
|
||||||
* nghttp2 - HTTP/2 C Library
|
|
||||||
*
|
|
||||||
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
* a copy of this software and associated documentation files (the
|
|
||||||
* "Software"), to deal in the Software without restriction, including
|
|
||||||
* without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
* permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
* the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be
|
|
||||||
* included in all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#ifndef HTTP2_HANDLER_H
|
|
||||||
#define HTTP2_HANDLER_H
|
|
||||||
|
|
||||||
#include "nghttp2_config.h"
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
|
||||||
#include <boost/array.hpp>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
|
|
||||||
#include <nghttp2/nghttp2.h>
|
|
||||||
|
|
||||||
#include <nghttp2/asio_http2.h>
|
|
||||||
|
|
||||||
namespace nghttp2 {
|
|
||||||
namespace asio_http2 {
|
|
||||||
|
|
||||||
class channel_impl {
|
|
||||||
public:
|
|
||||||
channel_impl();
|
|
||||||
void post(void_cb cb);
|
|
||||||
void strand(boost::asio::io_service::strand *strand);
|
|
||||||
|
|
||||||
private:
|
|
||||||
boost::asio::io_service::strand *strand_;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace server {
|
|
||||||
|
|
||||||
class http2_handler;
|
|
||||||
class http2_stream;
|
|
||||||
|
|
||||||
class request_impl {
|
|
||||||
public:
|
|
||||||
request_impl();
|
|
||||||
|
|
||||||
const std::vector<header> &headers() const;
|
|
||||||
const std::string &method() const;
|
|
||||||
const std::string &scheme() const;
|
|
||||||
const std::string &authority() const;
|
|
||||||
const std::string &host() const;
|
|
||||||
const std::string &path() const;
|
|
||||||
|
|
||||||
bool push(std::string method, std::string path,
|
|
||||||
std::vector<header> headers = {});
|
|
||||||
|
|
||||||
bool pushed() const;
|
|
||||||
bool closed() const;
|
|
||||||
|
|
||||||
void on_data(data_cb cb);
|
|
||||||
void on_end(void_cb cb);
|
|
||||||
|
|
||||||
bool run_task(thread_cb start);
|
|
||||||
|
|
||||||
void set_header(std::vector<header> headers);
|
|
||||||
void add_header(std::string name, std::string value);
|
|
||||||
void method(std::string method);
|
|
||||||
void scheme(std::string scheme);
|
|
||||||
void authority(std::string authority);
|
|
||||||
void host(std::string host);
|
|
||||||
void path(std::string path);
|
|
||||||
void pushed(bool f);
|
|
||||||
void handler(std::weak_ptr<http2_handler> h);
|
|
||||||
void stream(std::weak_ptr<http2_stream> s);
|
|
||||||
void call_on_data(const uint8_t *data, std::size_t len);
|
|
||||||
void call_on_end();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<header> headers_;
|
|
||||||
std::string method_;
|
|
||||||
std::string scheme_;
|
|
||||||
std::string authority_;
|
|
||||||
std::string host_;
|
|
||||||
std::string path_;
|
|
||||||
data_cb on_data_cb_;
|
|
||||||
void_cb on_end_cb_;
|
|
||||||
std::weak_ptr<http2_handler> handler_;
|
|
||||||
std::weak_ptr<http2_stream> stream_;
|
|
||||||
bool pushed_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class response_impl {
|
|
||||||
public:
|
|
||||||
response_impl();
|
|
||||||
void write_head(unsigned int status_code, std::vector<header> headers = {});
|
|
||||||
void end(std::string data = "");
|
|
||||||
void end(read_cb cb);
|
|
||||||
void resume();
|
|
||||||
bool closed() const;
|
|
||||||
|
|
||||||
unsigned int status_code() const;
|
|
||||||
const std::vector<header> &headers() const;
|
|
||||||
bool started() const;
|
|
||||||
void handler(std::weak_ptr<http2_handler> h);
|
|
||||||
void stream(std::weak_ptr<http2_stream> s);
|
|
||||||
read_cb::result_type call_read(uint8_t *data, std::size_t len);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<header> headers_;
|
|
||||||
read_cb read_cb_;
|
|
||||||
std::weak_ptr<http2_handler> handler_;
|
|
||||||
std::weak_ptr<http2_stream> stream_;
|
|
||||||
unsigned int status_code_;
|
|
||||||
bool started_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class http2_stream {
|
|
||||||
public:
|
|
||||||
http2_stream(int32_t stream_id);
|
|
||||||
|
|
||||||
int32_t get_stream_id() const;
|
|
||||||
const std::shared_ptr<request> &get_request();
|
|
||||||
const std::shared_ptr<response> &get_response();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<request> request_;
|
|
||||||
std::shared_ptr<response> response_;
|
|
||||||
int32_t stream_id_;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct callback_guard {
|
|
||||||
callback_guard(http2_handler &h);
|
|
||||||
~callback_guard();
|
|
||||||
http2_handler &handler;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::function<void(void)> connection_write;
|
|
||||||
|
|
||||||
class http2_handler : public std::enable_shared_from_this<http2_handler> {
|
|
||||||
public:
|
|
||||||
http2_handler(boost::asio::io_service &io_service,
|
|
||||||
boost::asio::io_service &task_io_service,
|
|
||||||
connection_write writefun, request_cb cb);
|
|
||||||
|
|
||||||
~http2_handler();
|
|
||||||
|
|
||||||
int start();
|
|
||||||
|
|
||||||
std::shared_ptr<http2_stream> create_stream(int32_t stream_id);
|
|
||||||
void close_stream(int32_t stream_id);
|
|
||||||
std::shared_ptr<http2_stream> find_stream(int32_t stream_id);
|
|
||||||
|
|
||||||
void call_on_request(http2_stream &stream);
|
|
||||||
|
|
||||||
bool should_stop() const;
|
|
||||||
|
|
||||||
int start_response(http2_stream &stream);
|
|
||||||
|
|
||||||
void stream_error(int32_t stream_id, uint32_t error_code);
|
|
||||||
|
|
||||||
void initiate_write();
|
|
||||||
|
|
||||||
void enter_callback();
|
|
||||||
void leave_callback();
|
|
||||||
bool inside_callback() const;
|
|
||||||
|
|
||||||
void resume(http2_stream &stream);
|
|
||||||
|
|
||||||
int push_promise(http2_stream &stream, std::string method, std::string path,
|
|
||||||
std::vector<header> headers);
|
|
||||||
|
|
||||||
bool run_task(thread_cb start);
|
|
||||||
|
|
||||||
boost::asio::io_service &io_service();
|
|
||||||
|
|
||||||
template <size_t N>
|
|
||||||
int on_read(const boost::array<uint8_t, N> &buffer, std::size_t len) {
|
|
||||||
callback_guard cg(*this);
|
|
||||||
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
rv = nghttp2_session_mem_recv(session_, buffer.data(), len);
|
|
||||||
|
|
||||||
if (rv < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <size_t N>
|
|
||||||
int on_write(boost::array<uint8_t, N> &buffer, std::size_t &len) {
|
|
||||||
callback_guard cg(*this);
|
|
||||||
|
|
||||||
len = 0;
|
|
||||||
|
|
||||||
if (buf_) {
|
|
||||||
std::copy_n(buf_, buflen_, std::begin(buffer));
|
|
||||||
|
|
||||||
len += buflen_;
|
|
||||||
|
|
||||||
buf_ = nullptr;
|
|
||||||
buflen_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
const uint8_t *data;
|
|
||||||
auto nread = nghttp2_session_mem_send(session_, &data);
|
|
||||||
if (nread < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nread == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len + nread > buffer.size()) {
|
|
||||||
buf_ = data;
|
|
||||||
buflen_ = nread;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::copy_n(data, nread, std::begin(buffer) + len);
|
|
||||||
|
|
||||||
len += nread;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::map<int32_t, std::shared_ptr<http2_stream>> streams_;
|
|
||||||
connection_write writefun_;
|
|
||||||
request_cb request_cb_;
|
|
||||||
boost::asio::io_service &io_service_;
|
|
||||||
boost::asio::io_service &task_io_service_;
|
|
||||||
std::shared_ptr<boost::asio::io_service::strand> strand_;
|
|
||||||
nghttp2_session *session_;
|
|
||||||
const uint8_t *buf_;
|
|
||||||
std::size_t buflen_;
|
|
||||||
bool inside_callback_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace server
|
|
||||||
} // namespace asio_http2
|
|
||||||
} // namespace nghttp
|
|
||||||
|
|
||||||
#endif // HTTP2_HANDLER_H
|
|
||||||
@@ -1,183 +0,0 @@
|
|||||||
/*
|
|
||||||
* nghttp2 - HTTP/2 C Library
|
|
||||||
*
|
|
||||||
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
* a copy of this software and associated documentation files (the
|
|
||||||
* "Software"), to deal in the Software without restriction, including
|
|
||||||
* without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
* permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
* the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be
|
|
||||||
* included in all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#include "asio_http2_impl.h"
|
|
||||||
|
|
||||||
#include <boost/asio/ssl.hpp>
|
|
||||||
|
|
||||||
#include <openssl/ssl.h>
|
|
||||||
|
|
||||||
#include <nghttp2/nghttp2.h>
|
|
||||||
|
|
||||||
#include "asio_server.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "ssl.h"
|
|
||||||
#include "template.h"
|
|
||||||
|
|
||||||
namespace nghttp2 {
|
|
||||||
|
|
||||||
namespace asio_http2 {
|
|
||||||
|
|
||||||
namespace server {
|
|
||||||
|
|
||||||
http2::http2() : impl_(make_unique<http2_impl>()) {}
|
|
||||||
|
|
||||||
http2::~http2() {}
|
|
||||||
|
|
||||||
void http2::listen(const std::string &address, uint16_t port, request_cb cb) {
|
|
||||||
impl_->listen(address, port, std::move(cb));
|
|
||||||
}
|
|
||||||
|
|
||||||
void http2::num_threads(size_t num_threads) { impl_->num_threads(num_threads); }
|
|
||||||
|
|
||||||
void http2::tls(std::string private_key_file, std::string certificate_file) {
|
|
||||||
impl_->tls(std::move(private_key_file), std::move(certificate_file));
|
|
||||||
}
|
|
||||||
|
|
||||||
void http2::num_concurrent_tasks(size_t num_concurrent_tasks) {
|
|
||||||
impl_->num_concurrent_tasks(num_concurrent_tasks);
|
|
||||||
}
|
|
||||||
|
|
||||||
void http2::backlog(int backlog) { impl_->backlog(backlog); }
|
|
||||||
|
|
||||||
http2_impl::http2_impl()
|
|
||||||
: num_threads_(1), num_concurrent_tasks_(1), backlog_(-1) {}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
std::vector<unsigned char> &get_alpn_token() {
|
|
||||||
static auto alpn_token = util::get_default_alpn();
|
|
||||||
return alpn_token;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void http2_impl::listen(const std::string &address, uint16_t port,
|
|
||||||
request_cb cb) {
|
|
||||||
std::unique_ptr<boost::asio::ssl::context> ssl_ctx;
|
|
||||||
|
|
||||||
if (!private_key_file_.empty() && !certificate_file_.empty()) {
|
|
||||||
ssl_ctx = make_unique<boost::asio::ssl::context>(
|
|
||||||
boost::asio::ssl::context::sslv23);
|
|
||||||
|
|
||||||
ssl_ctx->use_private_key_file(private_key_file_,
|
|
||||||
boost::asio::ssl::context::pem);
|
|
||||||
ssl_ctx->use_certificate_chain_file(certificate_file_);
|
|
||||||
|
|
||||||
auto ctx = ssl_ctx->native_handle();
|
|
||||||
|
|
||||||
SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
|
||||||
SSL_OP_NO_COMPRESSION |
|
|
||||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |
|
|
||||||
SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_TICKET |
|
|
||||||
SSL_OP_CIPHER_SERVER_PREFERENCE);
|
|
||||||
SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
|
|
||||||
SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
|
|
||||||
SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
|
|
||||||
|
|
||||||
SSL_CTX_set_cipher_list(ctx, ssl::DEFAULT_CIPHER_LIST);
|
|
||||||
|
|
||||||
auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
|
||||||
if (ecdh) {
|
|
||||||
SSL_CTX_set_tmp_ecdh(ctx, ecdh);
|
|
||||||
EC_KEY_free(ecdh);
|
|
||||||
}
|
|
||||||
|
|
||||||
SSL_CTX_set_next_protos_advertised_cb(
|
|
||||||
ctx,
|
|
||||||
[](SSL *s, const unsigned char **data, unsigned int *len, void *arg) {
|
|
||||||
auto &token = get_alpn_token();
|
|
||||||
|
|
||||||
*data = token.data();
|
|
||||||
*len = token.size();
|
|
||||||
|
|
||||||
return SSL_TLSEXT_ERR_OK;
|
|
||||||
},
|
|
||||||
nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
server(address, port, num_threads_, num_concurrent_tasks_, std::move(cb),
|
|
||||||
std::move(ssl_ctx), backlog_).run();
|
|
||||||
}
|
|
||||||
|
|
||||||
void http2_impl::num_threads(size_t num_threads) { num_threads_ = num_threads; }
|
|
||||||
|
|
||||||
void http2_impl::tls(std::string private_key_file,
|
|
||||||
std::string certificate_file) {
|
|
||||||
private_key_file_ = std::move(private_key_file);
|
|
||||||
certificate_file_ = std::move(certificate_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
void http2_impl::num_concurrent_tasks(size_t num_concurrent_tasks) {
|
|
||||||
num_concurrent_tasks_ = num_concurrent_tasks;
|
|
||||||
}
|
|
||||||
|
|
||||||
void http2_impl::backlog(int backlog) { backlog_ = backlog; }
|
|
||||||
|
|
||||||
} // namespace server
|
|
||||||
|
|
||||||
template <typename F, typename... T>
|
|
||||||
std::shared_ptr<Defer<F, T...>> defer_shared(F &&f, T &&... t) {
|
|
||||||
return std::make_shared<Defer<F, T...>>(std::forward<F>(f),
|
|
||||||
std::forward<T>(t)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
read_cb file_reader(const std::string &path) {
|
|
||||||
auto fd = open(path.c_str(), O_RDONLY);
|
|
||||||
if (fd == -1) {
|
|
||||||
return read_cb();
|
|
||||||
}
|
|
||||||
|
|
||||||
return file_reader_from_fd(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
read_cb file_reader_from_fd(int fd) {
|
|
||||||
auto d = defer_shared(close, fd);
|
|
||||||
|
|
||||||
return [fd, d](uint8_t *buf, size_t len) -> read_cb::result_type {
|
|
||||||
int rv;
|
|
||||||
while ((rv = read(fd, buf, len)) == -1 && errno == EINTR)
|
|
||||||
;
|
|
||||||
|
|
||||||
if (rv == -1) {
|
|
||||||
return std::make_pair(-1, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rv == 0) {
|
|
||||||
return std::make_pair(rv, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::make_pair(rv, false);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool check_path(const std::string &path) { return util::check_path(path); }
|
|
||||||
|
|
||||||
std::string percent_decode(const std::string &s) {
|
|
||||||
return util::percentDecode(std::begin(s), std::end(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string http_date(int64_t t) { return util::http_date(t); }
|
|
||||||
|
|
||||||
} // namespace asio_http2
|
|
||||||
|
|
||||||
} // namespace nghttp2
|
|
||||||
@@ -33,22 +33,15 @@
|
|||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
//
|
//
|
||||||
|
#include "asio_io_service_pool.h"
|
||||||
|
|
||||||
#include "asio_server.h"
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <future>
|
#include <future>
|
||||||
#include <boost/thread/thread.hpp>
|
|
||||||
#include <boost/bind.hpp>
|
|
||||||
|
|
||||||
namespace nghttp2 {
|
namespace nghttp2 {
|
||||||
|
|
||||||
namespace asio_http2 {
|
namespace asio_http2 {
|
||||||
|
|
||||||
namespace server {
|
io_service_pool::io_service_pool(std::size_t pool_size) : next_io_service_(0) {
|
||||||
|
|
||||||
io_service_pool::io_service_pool(std::size_t pool_size,
|
|
||||||
std::size_t thread_pool_size)
|
|
||||||
: next_io_service_(0), thread_pool_size_(thread_pool_size) {
|
|
||||||
if (pool_size == 0) {
|
if (pool_size == 0) {
|
||||||
throw std::runtime_error("io_service_pool size is 0");
|
throw std::runtime_error("io_service_pool size is 0");
|
||||||
}
|
}
|
||||||
@@ -61,16 +54,9 @@ io_service_pool::io_service_pool(std::size_t pool_size,
|
|||||||
io_services_.push_back(io_service);
|
io_services_.push_back(io_service);
|
||||||
work_.push_back(work);
|
work_.push_back(work);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto work = std::make_shared<boost::asio::io_service::work>(task_io_service_);
|
|
||||||
work_.push_back(work);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void io_service_pool::run() {
|
void io_service_pool::run() {
|
||||||
for (std::size_t i = 0; i < thread_pool_size_; ++i) {
|
|
||||||
thread_pool_.create_thread([this]() { task_io_service_.run(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a pool of threads to run all of the io_services.
|
// Create a pool of threads to run all of the io_services.
|
||||||
auto futs = std::vector<std::future<std::size_t>>();
|
auto futs = std::vector<std::future<std::size_t>>();
|
||||||
|
|
||||||
@@ -85,8 +71,6 @@ void io_service_pool::run() {
|
|||||||
for (auto &fut : futs) {
|
for (auto &fut : futs) {
|
||||||
fut.get();
|
fut.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_pool_.join_all();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void io_service_pool::stop() {
|
void io_service_pool::stop() {
|
||||||
@@ -94,8 +78,6 @@ void io_service_pool::stop() {
|
|||||||
for (auto &iosv : io_services_) {
|
for (auto &iosv : io_services_) {
|
||||||
iosv->stop();
|
iosv->stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
task_io_service_.stop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::asio::io_service &io_service_pool::get_io_service() {
|
boost::asio::io_service &io_service_pool::get_io_service() {
|
||||||
@@ -108,12 +90,6 @@ boost::asio::io_service &io_service_pool::get_io_service() {
|
|||||||
return io_service;
|
return io_service;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::asio::io_service &io_service_pool::get_task_io_service() {
|
|
||||||
return task_io_service_;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace server
|
|
||||||
|
|
||||||
} // namespace asio_http2
|
} // namespace asio_http2
|
||||||
|
|
||||||
} // namespace nghttp2
|
} // namespace nghttp2
|
||||||
|
|||||||
@@ -34,14 +34,14 @@
|
|||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef HTTP_SERVER2_IO_SERVICE_POOL_HPP
|
#ifndef ASIO_IO_SERVICE_POOL_H
|
||||||
#define HTTP_SERVER2_IO_SERVICE_POOL_HPP
|
#define ASIO_IO_SERVICE_POOL_H
|
||||||
|
|
||||||
#include "nghttp2_config.h"
|
#include "nghttp2_config.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
#include <boost/thread.hpp>
|
#include <boost/thread.hpp>
|
||||||
|
|
||||||
@@ -51,13 +51,11 @@ namespace nghttp2 {
|
|||||||
|
|
||||||
namespace asio_http2 {
|
namespace asio_http2 {
|
||||||
|
|
||||||
namespace server {
|
|
||||||
|
|
||||||
/// A pool of io_service objects.
|
/// A pool of io_service objects.
|
||||||
class io_service_pool : private boost::noncopyable {
|
class io_service_pool : private boost::noncopyable {
|
||||||
public:
|
public:
|
||||||
/// Construct the io_service pool.
|
/// Construct the io_service pool.
|
||||||
explicit io_service_pool(std::size_t pool_size, std::size_t thread_pool_size);
|
explicit io_service_pool(std::size_t pool_size);
|
||||||
|
|
||||||
/// Run all io_service objects in the pool.
|
/// Run all io_service objects in the pool.
|
||||||
void run();
|
void run();
|
||||||
@@ -68,31 +66,19 @@ public:
|
|||||||
/// Get an io_service to use.
|
/// Get an io_service to use.
|
||||||
boost::asio::io_service &get_io_service();
|
boost::asio::io_service &get_io_service();
|
||||||
|
|
||||||
boost::asio::io_service &get_task_io_service();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::shared_ptr<boost::asio::io_service> io_service_ptr;
|
|
||||||
typedef std::shared_ptr<boost::asio::io_service::work> work_ptr;
|
|
||||||
|
|
||||||
/// The pool of io_services.
|
/// The pool of io_services.
|
||||||
std::vector<io_service_ptr> io_services_;
|
std::vector<std::shared_ptr<boost::asio::io_service>> io_services_;
|
||||||
|
|
||||||
boost::asio::io_service task_io_service_;
|
|
||||||
boost::thread_group thread_pool_;
|
|
||||||
|
|
||||||
/// The work that keeps the io_services running.
|
/// The work that keeps the io_services running.
|
||||||
std::vector<work_ptr> work_;
|
std::vector<std::shared_ptr<boost::asio::io_service::work>> work_;
|
||||||
|
|
||||||
/// The next io_service to use for a connection.
|
/// The next io_service to use for a connection.
|
||||||
std::size_t next_io_service_;
|
std::size_t next_io_service_;
|
||||||
|
|
||||||
std::size_t thread_pool_size_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace server
|
|
||||||
|
|
||||||
} // namespace asio_http2
|
} // namespace asio_http2
|
||||||
|
|
||||||
} // namespace nghttp2
|
} // namespace nghttp2
|
||||||
|
|
||||||
#endif // HTTP_SERVER2_IO_SERVICE_POOL_HPP
|
#endif // ASIO_IO_SERVICE_POOL_H
|
||||||
|
|||||||
@@ -36,125 +36,124 @@
|
|||||||
|
|
||||||
#include "asio_server.h"
|
#include "asio_server.h"
|
||||||
|
|
||||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
#include "asio_server_connection.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
namespace nghttp2 {
|
namespace nghttp2 {
|
||||||
namespace asio_http2 {
|
namespace asio_http2 {
|
||||||
namespace server {
|
namespace server {
|
||||||
|
|
||||||
server::server(const std::string &address, uint16_t port,
|
server::server(std::size_t io_service_pool_size)
|
||||||
std::size_t io_service_pool_size, std::size_t thread_pool_size,
|
: io_service_pool_(io_service_pool_size) {}
|
||||||
request_cb cb,
|
|
||||||
std::unique_ptr<boost::asio::ssl::context> ssl_ctx, int backlog)
|
|
||||||
: io_service_pool_(io_service_pool_size, thread_pool_size),
|
|
||||||
signals_(io_service_pool_.get_io_service()),
|
|
||||||
tick_timer_(io_service_pool_.get_io_service(),
|
|
||||||
boost::posix_time::seconds(1)),
|
|
||||||
ssl_ctx_(std::move(ssl_ctx)), request_cb_(std::move(cb)) {
|
|
||||||
// Register to handle the signals that indicate when the server should exit.
|
|
||||||
// It is safe to register for the same signal multiple times in a program,
|
|
||||||
// provided all registration for the specified signal is made through Asio.
|
|
||||||
signals_.add(SIGINT);
|
|
||||||
signals_.add(SIGTERM);
|
|
||||||
#if defined(SIGQUIT)
|
|
||||||
signals_.add(SIGQUIT);
|
|
||||||
#endif // defined(SIGQUIT)
|
|
||||||
signals_.async_wait([this](const boost::system::error_code &error,
|
|
||||||
int signal_number) { io_service_pool_.stop(); });
|
|
||||||
|
|
||||||
// Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
|
boost::system::error_code
|
||||||
boost::asio::ip::tcp::resolver resolver(io_service_pool_.get_io_service());
|
server::listen_and_serve(boost::system::error_code &ec,
|
||||||
boost::asio::ip::tcp::resolver::query query(address, std::to_string(port));
|
boost::asio::ssl::context *tls_context,
|
||||||
|
const std::string &address, const std::string &port,
|
||||||
|
int backlog, serve_mux &mux) {
|
||||||
|
ec.clear();
|
||||||
|
|
||||||
for (auto itr = resolver.resolve(query);
|
if (bind_and_listen(ec, address, port, backlog)) {
|
||||||
itr != boost::asio::ip::tcp::resolver::iterator(); ++itr) {
|
return ec;
|
||||||
boost::asio::ip::tcp::endpoint endpoint = *itr;
|
}
|
||||||
auto acceptor =
|
|
||||||
boost::asio::ip::tcp::acceptor(io_service_pool_.get_io_service());
|
|
||||||
|
|
||||||
acceptor.open(endpoint.protocol());
|
for (auto &acceptor : acceptors_) {
|
||||||
acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
|
if (tls_context) {
|
||||||
acceptor.bind(endpoint);
|
start_accept(*tls_context, acceptor, mux);
|
||||||
if (backlog == -1) {
|
|
||||||
acceptor.listen();
|
|
||||||
} else {
|
} else {
|
||||||
acceptor.listen(backlog);
|
start_accept(acceptor, mux);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
io_service_pool_.run();
|
||||||
|
|
||||||
|
return ec;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::system::error_code server::bind_and_listen(boost::system::error_code &ec,
|
||||||
|
const std::string &address,
|
||||||
|
const std::string &port,
|
||||||
|
int backlog) {
|
||||||
|
// Open the acceptor with the option to reuse the address (i.e.
|
||||||
|
// SO_REUSEADDR).
|
||||||
|
tcp::resolver resolver(io_service_pool_.get_io_service());
|
||||||
|
tcp::resolver::query query(address, port);
|
||||||
|
auto it = resolver.resolve(query, ec);
|
||||||
|
if (ec) {
|
||||||
|
return ec;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; it != tcp::resolver::iterator(); ++it) {
|
||||||
|
tcp::endpoint endpoint = *it;
|
||||||
|
auto acceptor = tcp::acceptor(io_service_pool_.get_io_service());
|
||||||
|
|
||||||
|
if (acceptor.open(endpoint.protocol(), ec)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
acceptor.set_option(tcp::acceptor::reuse_address(true));
|
||||||
|
|
||||||
|
if (acceptor.bind(endpoint, ec)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (acceptor.listen(
|
||||||
|
backlog == -1 ? boost::asio::socket_base::max_connections : backlog,
|
||||||
|
ec)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
acceptors_.push_back(std::move(acceptor));
|
acceptors_.push_back(std::move(acceptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
start_accept();
|
if (acceptors_.empty()) {
|
||||||
|
return ec;
|
||||||
|
}
|
||||||
|
|
||||||
start_timer();
|
// ec could have some errors since we may have failed to bind some
|
||||||
|
// interfaces.
|
||||||
|
ec.clear();
|
||||||
|
|
||||||
|
return ec;
|
||||||
}
|
}
|
||||||
|
|
||||||
void server::run() { io_service_pool_.run(); }
|
void server::start_accept(boost::asio::ssl::context &tls_context,
|
||||||
|
tcp::acceptor &acceptor, serve_mux &mux) {
|
||||||
|
auto new_connection = std::make_shared<connection<ssl_socket>>(
|
||||||
|
mux, io_service_pool_.get_io_service(), tls_context);
|
||||||
|
|
||||||
std::shared_ptr<std::string> cached_date;
|
acceptor.async_accept(new_connection->socket().lowest_layer(),
|
||||||
|
[this, &tls_context, &acceptor, &mux, new_connection](
|
||||||
|
const boost::system::error_code &e) {
|
||||||
|
if (!e) {
|
||||||
|
new_connection->socket().lowest_layer().set_option(tcp::no_delay(true));
|
||||||
|
new_connection->socket().async_handshake(
|
||||||
|
boost::asio::ssl::stream_base::server,
|
||||||
|
[new_connection](const boost::system::error_code &e) {
|
||||||
|
if (!e) {
|
||||||
|
new_connection->start();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
start_accept(tls_context, acceptor, mux);
|
||||||
void update_date() {
|
|
||||||
cached_date = std::make_shared<std::string>(util::http_date(time(nullptr)));
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void server::start_timer() {
|
|
||||||
update_date();
|
|
||||||
|
|
||||||
tick_timer_.async_wait([this](const boost::system::error_code &e) {
|
|
||||||
tick_timer_.expires_at(tick_timer_.expires_at() +
|
|
||||||
boost::posix_time::seconds(1));
|
|
||||||
start_timer();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket;
|
void server::start_accept(tcp::acceptor &acceptor, serve_mux &mux) {
|
||||||
|
auto new_connection = std::make_shared<connection<tcp::socket>>(
|
||||||
|
mux, io_service_pool_.get_io_service());
|
||||||
|
|
||||||
void server::start_accept() {
|
acceptor.async_accept(new_connection->socket(),
|
||||||
if (ssl_ctx_) {
|
[this, &acceptor, &mux, new_connection](
|
||||||
auto new_connection = std::make_shared<connection<ssl_socket>>(
|
const boost::system::error_code &e) {
|
||||||
request_cb_, io_service_pool_.get_task_io_service(),
|
if (!e) {
|
||||||
io_service_pool_.get_io_service(), *ssl_ctx_);
|
new_connection->socket().set_option(tcp::no_delay(true));
|
||||||
|
new_connection->start();
|
||||||
for (auto &acceptor : acceptors_) {
|
|
||||||
acceptor.async_accept(
|
|
||||||
new_connection->socket().lowest_layer(),
|
|
||||||
[this, new_connection](const boost::system::error_code &e) {
|
|
||||||
if (!e) {
|
|
||||||
new_connection->socket().lowest_layer().set_option(
|
|
||||||
boost::asio::ip::tcp::no_delay(true));
|
|
||||||
new_connection->socket().async_handshake(
|
|
||||||
boost::asio::ssl::stream_base::server,
|
|
||||||
[new_connection](const boost::system::error_code &e) {
|
|
||||||
if (!e) {
|
|
||||||
new_connection->start();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
start_accept();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
auto new_connection =
|
|
||||||
std::make_shared<connection<boost::asio::ip::tcp::socket>>(
|
|
||||||
request_cb_, io_service_pool_.get_task_io_service(),
|
|
||||||
io_service_pool_.get_io_service());
|
|
||||||
|
|
||||||
for (auto &acceptor : acceptors_) {
|
start_accept(acceptor, mux);
|
||||||
acceptor.async_accept(
|
});
|
||||||
new_connection->socket(),
|
|
||||||
[this, new_connection](const boost::system::error_code &e) {
|
|
||||||
if (!e) {
|
|
||||||
new_connection->socket().set_option(
|
|
||||||
boost::asio::ip::tcp::no_delay(true));
|
|
||||||
new_connection->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
start_accept();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace server
|
} // namespace server
|
||||||
|
|||||||
@@ -34,21 +34,19 @@
|
|||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef HTTP_SERVER2_SERVER_HPP
|
#ifndef ASIO_SERVER_H
|
||||||
#define HTTP_SERVER2_SERVER_HPP
|
#define ASIO_SERVER_H
|
||||||
|
|
||||||
#include "nghttp2_config.h"
|
#include "nghttp2_config.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include <boost/asio/ssl.hpp>
|
|
||||||
|
|
||||||
#include <nghttp2/asio_http2.h>
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
#include "asio_connection.h"
|
|
||||||
#include "asio_io_service_pool.h"
|
#include "asio_io_service_pool.h"
|
||||||
|
|
||||||
namespace nghttp2 {
|
namespace nghttp2 {
|
||||||
@@ -57,40 +55,43 @@ namespace asio_http2 {
|
|||||||
|
|
||||||
namespace server {
|
namespace server {
|
||||||
|
|
||||||
/// The top-level class of the HTTP server.
|
class serve_mux;
|
||||||
|
|
||||||
|
using boost::asio::ip::tcp;
|
||||||
|
|
||||||
|
using ssl_socket = boost::asio::ssl::stream<tcp::socket>;
|
||||||
|
|
||||||
class server : private boost::noncopyable {
|
class server : private boost::noncopyable {
|
||||||
public:
|
public:
|
||||||
/// Construct the server to listen on the specified TCP address and port, and
|
explicit server(std::size_t io_service_pool_size);
|
||||||
/// serve up files from the given directory.
|
|
||||||
explicit server(const std::string &address, uint16_t port,
|
|
||||||
std::size_t io_service_pool_size,
|
|
||||||
std::size_t thread_pool_size, request_cb cb,
|
|
||||||
std::unique_ptr<boost::asio::ssl::context> ssl_ctx,
|
|
||||||
int backlog = -1);
|
|
||||||
|
|
||||||
/// Run the server's io_service loop.
|
boost::system::error_code
|
||||||
void run();
|
listen_and_serve(boost::system::error_code &ec,
|
||||||
|
boost::asio::ssl::context *tls_context,
|
||||||
|
const std::string &address, const std::string &port,
|
||||||
|
int backlog, serve_mux &mux);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Initiate an asynchronous accept operation.
|
/// Initiate an asynchronous accept operation.
|
||||||
void start_accept();
|
void start_accept(tcp::acceptor &acceptor, serve_mux &mux);
|
||||||
|
/// Same as above but with tls_context
|
||||||
|
void start_accept(boost::asio::ssl::context &tls_context,
|
||||||
|
tcp::acceptor &acceptor, serve_mux &mux);
|
||||||
|
|
||||||
void start_timer();
|
/// Resolves address and bind socket to the resolved addresses.
|
||||||
|
boost::system::error_code bind_and_listen(boost::system::error_code &ec,
|
||||||
|
const std::string &address,
|
||||||
|
const std::string &port,
|
||||||
|
int backlog);
|
||||||
|
|
||||||
/// The pool of io_service objects used to perform asynchronous operations.
|
/// The pool of io_service objects used to perform asynchronous
|
||||||
|
/// operations.
|
||||||
io_service_pool io_service_pool_;
|
io_service_pool io_service_pool_;
|
||||||
|
|
||||||
/// The signal_set is used to register for process termination notifications.
|
|
||||||
boost::asio::signal_set signals_;
|
|
||||||
|
|
||||||
boost::asio::deadline_timer tick_timer_;
|
|
||||||
|
|
||||||
/// Acceptor used to listen for incoming connections.
|
/// Acceptor used to listen for incoming connections.
|
||||||
std::vector<boost::asio::ip::tcp::acceptor> acceptors_;
|
std::vector<tcp::acceptor> acceptors_;
|
||||||
|
|
||||||
std::unique_ptr<boost::asio::ssl::context> ssl_ctx_;
|
std::unique_ptr<boost::asio::ssl::context> ssl_ctx_;
|
||||||
|
|
||||||
request_cb request_cb_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace server
|
} // namespace server
|
||||||
@@ -99,4 +100,4 @@ private:
|
|||||||
|
|
||||||
} // namespace nghttp2
|
} // namespace nghttp2
|
||||||
|
|
||||||
#endif // HTTP_SERVER2_SERVER_HPP
|
#endif // ASIO_SERVER_H
|
||||||
|
|||||||
@@ -34,19 +34,20 @@
|
|||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef HTTP_SERVER2_CONNECTION_HPP
|
#ifndef ASIO_SERVER_CONNECTION_H
|
||||||
#define HTTP_SERVER2_CONNECTION_HPP
|
#define ASIO_SERVER_CONNECTION_H
|
||||||
|
|
||||||
#include "nghttp2_config.h"
|
#include "nghttp2_config.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
#include <boost/array.hpp>
|
#include <boost/array.hpp>
|
||||||
|
|
||||||
#include <nghttp2/asio_http2.h>
|
#include <nghttp2/asio_http2_server.h>
|
||||||
#include "asio_http2_handler.h"
|
|
||||||
|
#include "asio_server_http2_handler.h"
|
||||||
|
#include "asio_server_serve_mux.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
namespace nghttp2 {
|
namespace nghttp2 {
|
||||||
@@ -62,16 +63,14 @@ class connection : public std::enable_shared_from_this<connection<socket_type>>,
|
|||||||
public:
|
public:
|
||||||
/// Construct a connection with the given io_service.
|
/// Construct a connection with the given io_service.
|
||||||
template <typename... SocketArgs>
|
template <typename... SocketArgs>
|
||||||
explicit connection(request_cb cb, boost::asio::io_service &task_io_service,
|
explicit connection(serve_mux &mux, SocketArgs &&... args)
|
||||||
SocketArgs &&... args)
|
: socket_(std::forward<SocketArgs>(args)...), mux_(mux), writing_(false) {
|
||||||
: socket_(std::forward<SocketArgs>(args)...), request_cb_(std::move(cb)),
|
}
|
||||||
task_io_service_(task_io_service), writing_(false) {}
|
|
||||||
|
|
||||||
/// Start the first asynchronous operation for the connection.
|
/// Start the first asynchronous operation for the connection.
|
||||||
void start() {
|
void start() {
|
||||||
handler_ = std::make_shared<http2_handler>(
|
handler_ = std::make_shared<http2_handler>(socket_.get_io_service(),
|
||||||
socket_.get_io_service(), task_io_service_, [this]() { do_write(); },
|
[this]() { do_write(); }, mux_);
|
||||||
request_cb_);
|
|
||||||
if (handler_->start() != 0) {
|
if (handler_->start() != 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -149,9 +148,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
socket_type socket_;
|
socket_type socket_;
|
||||||
|
|
||||||
request_cb request_cb_;
|
serve_mux &mux_;
|
||||||
|
|
||||||
boost::asio::io_service &task_io_service_;
|
|
||||||
|
|
||||||
std::shared_ptr<http2_handler> handler_;
|
std::shared_ptr<http2_handler> handler_;
|
||||||
|
|
||||||
@@ -169,4 +166,4 @@ private:
|
|||||||
|
|
||||||
} // namespace nghttp2
|
} // namespace nghttp2
|
||||||
|
|
||||||
#endif // HTTP_SERVER2_CONNECTION_HPP
|
#endif // ASIO_SERVER_CONNECTION_H
|
||||||
80
src/asio_server_http2.cc
Normal file
80
src/asio_server_http2.cc
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "nghttp2_config.h"
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
|
#include "asio_server_http2_impl.h"
|
||||||
|
#include "asio_server.h"
|
||||||
|
#include "template.h"
|
||||||
|
|
||||||
|
namespace nghttp2 {
|
||||||
|
|
||||||
|
namespace asio_http2 {
|
||||||
|
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
http2::http2() : impl_(make_unique<http2_impl>()) {}
|
||||||
|
|
||||||
|
http2::~http2() {}
|
||||||
|
|
||||||
|
http2::http2(http2 &&other) noexcept : impl_(std::move(other.impl_)) {}
|
||||||
|
|
||||||
|
http2 &http2::operator=(http2 &&other) noexcept {
|
||||||
|
if (this == &other) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_ = std::move(other.impl_);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::system::error_code http2::listen_and_serve(boost::system::error_code &ec,
|
||||||
|
const std::string &address,
|
||||||
|
const std::string &port) {
|
||||||
|
return impl_->listen_and_serve(ec, nullptr, address, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::system::error_code
|
||||||
|
http2::listen_and_serve(boost::system::error_code &ec,
|
||||||
|
boost::asio::ssl::context &tls_context,
|
||||||
|
const std::string &address, const std::string &port) {
|
||||||
|
return impl_->listen_and_serve(ec, &tls_context, address, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
void http2::num_threads(size_t num_threads) { impl_->num_threads(num_threads); }
|
||||||
|
|
||||||
|
void http2::backlog(int backlog) { impl_->backlog(backlog); }
|
||||||
|
|
||||||
|
bool http2::handle(std::string pattern, request_cb cb) {
|
||||||
|
return impl_->handle(std::move(pattern), std::move(cb));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
|
||||||
|
} // namespace asio_http2
|
||||||
|
|
||||||
|
} // namespace nghttp2
|
||||||
472
src/asio_server_http2_handler.cc
Normal file
472
src/asio_server_http2_handler.cc
Normal file
@@ -0,0 +1,472 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "asio_server_http2_handler.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "asio_common.h"
|
||||||
|
#include "asio_server_serve_mux.h"
|
||||||
|
#include "asio_server_stream.h"
|
||||||
|
#include "asio_server_request_impl.h"
|
||||||
|
#include "asio_server_response_impl.h"
|
||||||
|
#include "http2.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "template.h"
|
||||||
|
|
||||||
|
namespace nghttp2 {
|
||||||
|
|
||||||
|
namespace asio_http2 {
|
||||||
|
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
int stream_error(nghttp2_session *session, int32_t stream_id,
|
||||||
|
uint32_t error_code) {
|
||||||
|
return nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
|
||||||
|
error_code);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
int on_begin_headers_callback(nghttp2_session *session,
|
||||||
|
const nghttp2_frame *frame, void *user_data) {
|
||||||
|
auto handler = static_cast<http2_handler *>(user_data);
|
||||||
|
|
||||||
|
if (frame->hd.type != NGHTTP2_HEADERS ||
|
||||||
|
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
handler->create_stream(frame->hd.stream_id);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
|
const uint8_t *name, size_t namelen,
|
||||||
|
const uint8_t *value, size_t valuelen, uint8_t flags,
|
||||||
|
void *user_data) {
|
||||||
|
auto handler = static_cast<http2_handler *>(user_data);
|
||||||
|
auto stream_id = frame->hd.stream_id;
|
||||||
|
|
||||||
|
if (frame->hd.type != NGHTTP2_HEADERS ||
|
||||||
|
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto strm = handler->find_stream(stream_id);
|
||||||
|
if (!strm) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &req = strm->request().impl();
|
||||||
|
auto &uref = req.uri();
|
||||||
|
|
||||||
|
switch (nghttp2::http2::lookup_token(name, namelen)) {
|
||||||
|
case nghttp2::http2::HD__METHOD:
|
||||||
|
req.method(std::string(value, value + valuelen));
|
||||||
|
break;
|
||||||
|
case nghttp2::http2::HD__SCHEME:
|
||||||
|
uref.scheme.assign(value, value + valuelen);
|
||||||
|
break;
|
||||||
|
case nghttp2::http2::HD__AUTHORITY:
|
||||||
|
uref.host.assign(value, value + valuelen);
|
||||||
|
break;
|
||||||
|
case nghttp2::http2::HD__PATH:
|
||||||
|
split_path(uref, value, value + valuelen);
|
||||||
|
break;
|
||||||
|
case nghttp2::http2::HD_HOST:
|
||||||
|
if (uref.host.empty()) {
|
||||||
|
uref.host.assign(value, value + valuelen);
|
||||||
|
}
|
||||||
|
// fall through
|
||||||
|
default:
|
||||||
|
req.header().emplace(std::string(name, name + namelen),
|
||||||
|
header_value{std::string(value, value + valuelen),
|
||||||
|
(flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0});
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
|
void *user_data) {
|
||||||
|
auto handler = static_cast<http2_handler *>(user_data);
|
||||||
|
auto strm = handler->find_stream(frame->hd.stream_id);
|
||||||
|
|
||||||
|
switch (frame->hd.type) {
|
||||||
|
case NGHTTP2_DATA:
|
||||||
|
if (!strm) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||||
|
strm->request().impl().call_on_data(nullptr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case NGHTTP2_HEADERS: {
|
||||||
|
if (!strm || frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
handler->call_on_request(*strm);
|
||||||
|
|
||||||
|
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||||
|
strm->request().impl().call_on_data(nullptr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||||
|
int32_t stream_id, const uint8_t *data,
|
||||||
|
size_t len, void *user_data) {
|
||||||
|
auto handler = static_cast<http2_handler *>(user_data);
|
||||||
|
auto strm = handler->find_stream(stream_id);
|
||||||
|
|
||||||
|
if (!strm) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
strm->request().impl().call_on_data(data, len);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||||
|
uint32_t error_code, void *user_data) {
|
||||||
|
auto handler = static_cast<http2_handler *>(user_data);
|
||||||
|
|
||||||
|
auto strm = handler->find_stream(stream_id);
|
||||||
|
if (!strm) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
strm->response().impl().call_on_close(error_code);
|
||||||
|
|
||||||
|
handler->close_stream(stream_id);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
|
void *user_data) {
|
||||||
|
auto handler = static_cast<http2_handler *>(user_data);
|
||||||
|
|
||||||
|
if (frame->hd.type != NGHTTP2_PUSH_PROMISE) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto strm = handler->find_stream(frame->push_promise.promised_stream_id);
|
||||||
|
|
||||||
|
if (!strm) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &res = strm->response().impl();
|
||||||
|
res.push_promise_sent();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
int on_frame_not_send_callback(nghttp2_session *session,
|
||||||
|
const nghttp2_frame *frame, int lib_error_code,
|
||||||
|
void *user_data) {
|
||||||
|
if (frame->hd.type != NGHTTP2_HEADERS) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issue RST_STREAM so that stream does not hang around.
|
||||||
|
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
|
||||||
|
NGHTTP2_INTERNAL_ERROR);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
http2_handler::http2_handler(boost::asio::io_service &io_service,
|
||||||
|
connection_write writefun, serve_mux &mux)
|
||||||
|
: writefun_(writefun), mux_(mux), io_service_(io_service),
|
||||||
|
session_(nullptr), buf_(nullptr), buflen_(0), inside_callback_(false),
|
||||||
|
tstamp_cached_(time(nullptr)),
|
||||||
|
formatted_date_(util::http_date(tstamp_cached_)) {}
|
||||||
|
|
||||||
|
http2_handler::~http2_handler() { nghttp2_session_del(session_); }
|
||||||
|
|
||||||
|
const std::string &http2_handler::http_date() {
|
||||||
|
auto t = time(nullptr);
|
||||||
|
if (t != tstamp_cached_) {
|
||||||
|
tstamp_cached_ = t;
|
||||||
|
formatted_date_ = util::http_date(t);
|
||||||
|
}
|
||||||
|
return formatted_date_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int http2_handler::start() {
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
nghttp2_session_callbacks *callbacks;
|
||||||
|
rv = nghttp2_session_callbacks_new(&callbacks);
|
||||||
|
if (rv != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cb_del = defer(nghttp2_session_callbacks_del, callbacks);
|
||||||
|
|
||||||
|
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||||
|
callbacks, on_begin_headers_callback);
|
||||||
|
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||||
|
on_header_callback);
|
||||||
|
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||||
|
on_frame_recv_callback);
|
||||||
|
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||||
|
callbacks, on_data_chunk_recv_callback);
|
||||||
|
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||||
|
callbacks, on_stream_close_callback);
|
||||||
|
nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
|
||||||
|
on_frame_send_callback);
|
||||||
|
nghttp2_session_callbacks_set_on_frame_not_send_callback(
|
||||||
|
callbacks, on_frame_not_send_callback);
|
||||||
|
|
||||||
|
nghttp2_option *option;
|
||||||
|
rv = nghttp2_option_new(&option);
|
||||||
|
if (rv != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto opt_del = defer(nghttp2_option_del, option);
|
||||||
|
|
||||||
|
nghttp2_option_set_recv_client_preface(option, 1);
|
||||||
|
|
||||||
|
rv = nghttp2_session_server_new2(&session_, callbacks, this, option);
|
||||||
|
if (rv != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nghttp2_settings_entry ent{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100};
|
||||||
|
nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, &ent, 1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream *http2_handler::create_stream(int32_t stream_id) {
|
||||||
|
auto p = streams_.emplace(stream_id, make_unique<stream>(this, stream_id));
|
||||||
|
assert(p.second);
|
||||||
|
return (*p.first).second.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void http2_handler::close_stream(int32_t stream_id) {
|
||||||
|
streams_.erase(stream_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream *http2_handler::find_stream(int32_t stream_id) {
|
||||||
|
auto i = streams_.find(stream_id);
|
||||||
|
if (i == std::end(streams_)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (*i).second.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void http2_handler::call_on_request(stream &strm) {
|
||||||
|
auto cb = mux_.handler(strm.request().impl());
|
||||||
|
cb(strm.request(), strm.response());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool http2_handler::should_stop() const {
|
||||||
|
return !nghttp2_session_want_read(session_) &&
|
||||||
|
!nghttp2_session_want_write(session_);
|
||||||
|
}
|
||||||
|
|
||||||
|
int http2_handler::start_response(stream &strm) {
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
auto &res = strm.response().impl();
|
||||||
|
auto &header = res.header();
|
||||||
|
auto nva = std::vector<nghttp2_nv>();
|
||||||
|
nva.reserve(2 + header.size());
|
||||||
|
auto status = util::utos(res.status_code());
|
||||||
|
auto date = http_date();
|
||||||
|
nva.push_back(nghttp2::http2::make_nv_ls(":status", status));
|
||||||
|
nva.push_back(nghttp2::http2::make_nv_ls("date", date));
|
||||||
|
for (auto &hd : header) {
|
||||||
|
nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
|
||||||
|
hd.second.sensitive));
|
||||||
|
}
|
||||||
|
|
||||||
|
nghttp2_data_provider *prd_ptr = nullptr, prd;
|
||||||
|
auto &req = strm.request().impl();
|
||||||
|
if (::nghttp2::http2::expect_response_body(req.method(), res.status_code())) {
|
||||||
|
prd.source.ptr = &strm;
|
||||||
|
prd.read_callback =
|
||||||
|
[](nghttp2_session *session, int32_t stream_id, uint8_t *buf,
|
||||||
|
size_t length, uint32_t *data_flags, nghttp2_data_source *source,
|
||||||
|
void *user_data) -> ssize_t {
|
||||||
|
auto &strm = *static_cast<stream *>(source->ptr);
|
||||||
|
return strm.response().impl().call_read(buf, length, data_flags);
|
||||||
|
};
|
||||||
|
prd_ptr = &prd;
|
||||||
|
}
|
||||||
|
rv = nghttp2_submit_response(session_, strm.get_stream_id(), nva.data(),
|
||||||
|
nva.size(), prd_ptr);
|
||||||
|
|
||||||
|
if (rv != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
signal_write();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int http2_handler::submit_trailer(stream &strm, header_map h) {
|
||||||
|
int rv;
|
||||||
|
auto nva = std::vector<nghttp2_nv>();
|
||||||
|
nva.reserve(h.size());
|
||||||
|
for (auto &hd : h) {
|
||||||
|
nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
|
||||||
|
hd.second.sensitive));
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = nghttp2_submit_trailer(session_, strm.get_stream_id(), nva.data(),
|
||||||
|
nva.size());
|
||||||
|
|
||||||
|
if (rv != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
signal_write();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void http2_handler::enter_callback() {
|
||||||
|
assert(!inside_callback_);
|
||||||
|
inside_callback_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void http2_handler::leave_callback() {
|
||||||
|
assert(inside_callback_);
|
||||||
|
inside_callback_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void http2_handler::stream_error(int32_t stream_id, uint32_t error_code) {
|
||||||
|
::nghttp2::asio_http2::server::stream_error(session_, stream_id, error_code);
|
||||||
|
signal_write();
|
||||||
|
}
|
||||||
|
|
||||||
|
void http2_handler::signal_write() {
|
||||||
|
if (!inside_callback_) {
|
||||||
|
initiate_write();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void http2_handler::initiate_write() { writefun_(); }
|
||||||
|
|
||||||
|
void http2_handler::resume(stream &strm) {
|
||||||
|
nghttp2_session_resume_data(session_, strm.get_stream_id());
|
||||||
|
signal_write();
|
||||||
|
}
|
||||||
|
|
||||||
|
response *http2_handler::push_promise(boost::system::error_code &ec,
|
||||||
|
stream &strm, std::string method,
|
||||||
|
std::string raw_path_query,
|
||||||
|
header_map h) {
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
ec.clear();
|
||||||
|
|
||||||
|
auto &req = strm.request().impl();
|
||||||
|
|
||||||
|
auto nva = std::vector<nghttp2_nv>();
|
||||||
|
nva.reserve(4 + h.size());
|
||||||
|
nva.push_back(nghttp2::http2::make_nv_ls(":method", method));
|
||||||
|
nva.push_back(nghttp2::http2::make_nv_ls(":scheme", req.uri().scheme));
|
||||||
|
nva.push_back(nghttp2::http2::make_nv_ls(":authority", req.uri().host));
|
||||||
|
nva.push_back(nghttp2::http2::make_nv_ls(":path", raw_path_query));
|
||||||
|
|
||||||
|
for (auto &hd : h) {
|
||||||
|
nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
|
||||||
|
hd.second.sensitive));
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = nghttp2_submit_push_promise(session_, NGHTTP2_FLAG_NONE,
|
||||||
|
strm.get_stream_id(), nva.data(), nva.size(),
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
if (rv < 0) {
|
||||||
|
ec = make_error_code(static_cast<nghttp2_error>(rv));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto promised_strm = create_stream(rv);
|
||||||
|
auto &promised_req = promised_strm->request().impl();
|
||||||
|
promised_req.header(std::move(h));
|
||||||
|
promised_req.method(std::move(method));
|
||||||
|
|
||||||
|
auto &uref = promised_req.uri();
|
||||||
|
uref.scheme = req.uri().scheme;
|
||||||
|
uref.host = req.uri().host;
|
||||||
|
split_path(uref, std::begin(raw_path_query), std::end(raw_path_query));
|
||||||
|
|
||||||
|
auto &promised_res = promised_strm->response().impl();
|
||||||
|
promised_res.pushed(true);
|
||||||
|
|
||||||
|
signal_write();
|
||||||
|
|
||||||
|
return &promised_strm->response();
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::asio::io_service &http2_handler::io_service() { return io_service_; }
|
||||||
|
|
||||||
|
callback_guard::callback_guard(http2_handler &h) : handler(h) {
|
||||||
|
handler.enter_callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
callback_guard::~callback_guard() { handler.leave_callback(); }
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
|
||||||
|
} // namespace asio_http2
|
||||||
|
|
||||||
|
} // namespace nghttp2
|
||||||
167
src/asio_server_http2_handler.h
Normal file
167
src/asio_server_http2_handler.h
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef ASIO_SERVER_HTTP2_HANDLER_H
|
||||||
|
#define ASIO_SERVER_HTTP2_HANDLER_H
|
||||||
|
|
||||||
|
#include "nghttp2_config.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <boost/array.hpp>
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
|
namespace nghttp2 {
|
||||||
|
namespace asio_http2 {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
class http2_handler;
|
||||||
|
class stream;
|
||||||
|
class serve_mux;
|
||||||
|
|
||||||
|
struct callback_guard {
|
||||||
|
callback_guard(http2_handler &h);
|
||||||
|
~callback_guard();
|
||||||
|
http2_handler &handler;
|
||||||
|
};
|
||||||
|
|
||||||
|
using connection_write = std::function<void(void)>;
|
||||||
|
|
||||||
|
class http2_handler : public std::enable_shared_from_this<http2_handler> {
|
||||||
|
public:
|
||||||
|
http2_handler(boost::asio::io_service &io_service, connection_write writefun,
|
||||||
|
serve_mux &mux);
|
||||||
|
|
||||||
|
~http2_handler();
|
||||||
|
|
||||||
|
int start();
|
||||||
|
|
||||||
|
stream *create_stream(int32_t stream_id);
|
||||||
|
void close_stream(int32_t stream_id);
|
||||||
|
stream *find_stream(int32_t stream_id);
|
||||||
|
|
||||||
|
void call_on_request(stream &s);
|
||||||
|
|
||||||
|
bool should_stop() const;
|
||||||
|
|
||||||
|
int start_response(stream &s);
|
||||||
|
|
||||||
|
int submit_trailer(stream &s, header_map h);
|
||||||
|
|
||||||
|
void stream_error(int32_t stream_id, uint32_t error_code);
|
||||||
|
|
||||||
|
void initiate_write();
|
||||||
|
|
||||||
|
void enter_callback();
|
||||||
|
void leave_callback();
|
||||||
|
|
||||||
|
void resume(stream &s);
|
||||||
|
|
||||||
|
response *push_promise(boost::system::error_code &ec, stream &s,
|
||||||
|
std::string method, std::string raw_path_query,
|
||||||
|
header_map h);
|
||||||
|
|
||||||
|
void signal_write();
|
||||||
|
|
||||||
|
boost::asio::io_service &io_service();
|
||||||
|
|
||||||
|
const std::string &http_date();
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
int on_read(const boost::array<uint8_t, N> &buffer, std::size_t len) {
|
||||||
|
callback_guard cg(*this);
|
||||||
|
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
rv = nghttp2_session_mem_recv(session_, buffer.data(), len);
|
||||||
|
|
||||||
|
if (rv < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
int on_write(boost::array<uint8_t, N> &buffer, std::size_t &len) {
|
||||||
|
callback_guard cg(*this);
|
||||||
|
|
||||||
|
len = 0;
|
||||||
|
|
||||||
|
if (buf_) {
|
||||||
|
std::copy_n(buf_, buflen_, std::begin(buffer));
|
||||||
|
|
||||||
|
len += buflen_;
|
||||||
|
|
||||||
|
buf_ = nullptr;
|
||||||
|
buflen_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
const uint8_t *data;
|
||||||
|
auto nread = nghttp2_session_mem_send(session_, &data);
|
||||||
|
if (nread < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nread == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len + nread > buffer.size()) {
|
||||||
|
buf_ = data;
|
||||||
|
buflen_ = nread;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::copy_n(data, nread, std::begin(buffer) + len);
|
||||||
|
|
||||||
|
len += nread;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::map<int32_t, std::shared_ptr<stream>> streams_;
|
||||||
|
connection_write writefun_;
|
||||||
|
serve_mux &mux_;
|
||||||
|
boost::asio::io_service &io_service_;
|
||||||
|
nghttp2_session *session_;
|
||||||
|
const uint8_t *buf_;
|
||||||
|
std::size_t buflen_;
|
||||||
|
bool inside_callback_;
|
||||||
|
time_t tstamp_cached_;
|
||||||
|
std::string formatted_date_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace asio_http2
|
||||||
|
} // namespace nghttp
|
||||||
|
|
||||||
|
#endif // ASIO_SERVER_HTTP2_HANDLER_H
|
||||||
61
src/asio_server_http2_impl.cc
Normal file
61
src/asio_server_http2_impl.cc
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "asio_server_http2_impl.h"
|
||||||
|
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
|
||||||
|
#include "asio_server.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "ssl.h"
|
||||||
|
#include "template.h"
|
||||||
|
|
||||||
|
namespace nghttp2 {
|
||||||
|
|
||||||
|
namespace asio_http2 {
|
||||||
|
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
http2_impl::http2_impl() : num_threads_(1), backlog_(-1) {}
|
||||||
|
|
||||||
|
boost::system::error_code http2_impl::listen_and_serve(
|
||||||
|
boost::system::error_code &ec, boost::asio::ssl::context *tls_context,
|
||||||
|
const std::string &address, const std::string &port) {
|
||||||
|
return server(num_threads_)
|
||||||
|
.listen_and_serve(ec, tls_context, address, port, backlog_, mux_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void http2_impl::num_threads(size_t num_threads) { num_threads_ = num_threads; }
|
||||||
|
|
||||||
|
void http2_impl::backlog(int backlog) { backlog_ = backlog; }
|
||||||
|
|
||||||
|
bool http2_impl::handle(std::string pattern, request_cb cb) {
|
||||||
|
return mux_.handle(std::move(pattern), std::move(cb));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
|
||||||
|
} // namespace asio_http2
|
||||||
|
|
||||||
|
} // namespace nghttp2
|
||||||
@@ -22,12 +22,14 @@
|
|||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#ifndef ASIO_HTTP2_IMPL_H
|
#ifndef ASIO_SERVER_HTTP2_IMPL_H
|
||||||
#define ASIO_HTTP2_IMPL_H
|
#define ASIO_SERVER_HTTP2_IMPL_H
|
||||||
|
|
||||||
#include "nghttp2_config.h"
|
#include "nghttp2_config.h"
|
||||||
|
|
||||||
#include <nghttp2/asio_http2.h>
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
|
#include "asio_server_serve_mux.h"
|
||||||
|
|
||||||
namespace nghttp2 {
|
namespace nghttp2 {
|
||||||
|
|
||||||
@@ -40,19 +42,19 @@ class server;
|
|||||||
class http2_impl {
|
class http2_impl {
|
||||||
public:
|
public:
|
||||||
http2_impl();
|
http2_impl();
|
||||||
void listen(const std::string &address, uint16_t port, request_cb cb);
|
boost::system::error_code
|
||||||
|
listen_and_serve(boost::system::error_code &ec,
|
||||||
|
boost::asio::ssl::context *tls_context,
|
||||||
|
const std::string &address, const std::string &port);
|
||||||
void num_threads(size_t num_threads);
|
void num_threads(size_t num_threads);
|
||||||
void tls(std::string private_key_file, std::string certificate_file);
|
|
||||||
void num_concurrent_tasks(size_t num_concurrent_tasks);
|
|
||||||
void backlog(int backlog);
|
void backlog(int backlog);
|
||||||
|
bool handle(std::string pattern, request_cb cb);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string private_key_file_;
|
|
||||||
std::string certificate_file_;
|
|
||||||
std::unique_ptr<server> server_;
|
std::unique_ptr<server> server_;
|
||||||
std::size_t num_threads_;
|
std::size_t num_threads_;
|
||||||
std::size_t num_concurrent_tasks_;
|
|
||||||
int backlog_;
|
int backlog_;
|
||||||
|
serve_mux mux_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace server
|
} // namespace server
|
||||||
@@ -61,4 +63,4 @@ private:
|
|||||||
|
|
||||||
} // namespace nghttp2
|
} // namespace nghttp2
|
||||||
|
|
||||||
#endif // ASIO_HTTP2_IMPL_H
|
#endif // ASIO_SERVER_HTTP2_IMPL_H
|
||||||
55
src/asio_server_request.cc
Normal file
55
src/asio_server_request.cc
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "nghttp2_config.h"
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
|
#include "asio_server_request_impl.h"
|
||||||
|
|
||||||
|
#include "template.h"
|
||||||
|
|
||||||
|
namespace nghttp2 {
|
||||||
|
namespace asio_http2 {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
request::request() : impl_(make_unique<request_impl>()) {}
|
||||||
|
|
||||||
|
request::~request() {}
|
||||||
|
|
||||||
|
const header_map &request::header() const { return impl_->header(); }
|
||||||
|
|
||||||
|
const std::string &request::method() const { return impl_->method(); }
|
||||||
|
|
||||||
|
const uri_ref &request::uri() const { return impl_->uri(); }
|
||||||
|
|
||||||
|
void request::on_data(data_cb cb) const {
|
||||||
|
return impl_->on_data(std::move(cb));
|
||||||
|
}
|
||||||
|
|
||||||
|
request_impl &request::impl() const { return *impl_; }
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace asio_http2
|
||||||
|
} // namespace nghttp2
|
||||||
84
src/asio_server_request_handler.cc
Normal file
84
src/asio_server_request_handler.cc
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "asio_server_request_handler.h"
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "http2.h"
|
||||||
|
|
||||||
|
namespace nghttp2 {
|
||||||
|
namespace asio_http2 {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::string create_html(int status_code) {
|
||||||
|
std::string res;
|
||||||
|
res.reserve(512);
|
||||||
|
auto status = ::nghttp2::http2::get_status_string(status_code);
|
||||||
|
res += R"(<!DOCTYPE html><html lang="en"><title>)";
|
||||||
|
res += status;
|
||||||
|
res += "</title><body><h1>";
|
||||||
|
res += status;
|
||||||
|
res += "</h1></body></html>";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
request_cb redirect_handler(int status_code, std::string uri) {
|
||||||
|
return [status_code, uri](const request &req, const response &res) {
|
||||||
|
header_map h;
|
||||||
|
h.emplace("location", header_value{std::move(uri)});
|
||||||
|
std::string html;
|
||||||
|
if (req.method() == "GET") {
|
||||||
|
html = create_html(status_code);
|
||||||
|
}
|
||||||
|
h.emplace("content-length", header_value{util::utos(html.size())});
|
||||||
|
|
||||||
|
res.write_head(status_code, std::move(h));
|
||||||
|
res.end(std::move(html));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
request_cb status_handler(int status_code) {
|
||||||
|
return [status_code](const request &req, const response &res) {
|
||||||
|
if (!::nghttp2::http2::expect_response_body(status_code)) {
|
||||||
|
res.write_head(status_code);
|
||||||
|
res.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// we supply content-length for HEAD request, but body will not be
|
||||||
|
// sent.
|
||||||
|
auto html = create_html(status_code);
|
||||||
|
header_map h;
|
||||||
|
h.emplace("content-length", header_value{util::utos(html.size())});
|
||||||
|
h.emplace("content-type", header_value{"text/html; charset=utf-8"});
|
||||||
|
|
||||||
|
res.write_head(status_code, std::move(h));
|
||||||
|
res.end(std::move(html));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace asio_http2
|
||||||
|
} // namespace nghttp2
|
||||||
32
src/asio_server_request_handler.h
Normal file
32
src/asio_server_request_handler.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef ASIO_SERVER_REQUEST_HANDLER_H
|
||||||
|
#define ASIO_SERVER_REQUEST_HANDLER_H
|
||||||
|
|
||||||
|
#include "nghttp2_config.h"
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
|
#endif // ASIO_SERVER_REQUEST_HANDLER_H
|
||||||
59
src/asio_server_request_impl.cc
Normal file
59
src/asio_server_request_impl.cc
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "asio_server_request_impl.h"
|
||||||
|
|
||||||
|
namespace nghttp2 {
|
||||||
|
namespace asio_http2 {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
request_impl::request_impl() : strm_(nullptr) {}
|
||||||
|
|
||||||
|
const header_map &request_impl::header() const { return header_; }
|
||||||
|
|
||||||
|
const std::string &request_impl::method() const { return method_; }
|
||||||
|
|
||||||
|
const uri_ref &request_impl::uri() const { return uri_; }
|
||||||
|
|
||||||
|
uri_ref &request_impl::uri() { return uri_; }
|
||||||
|
|
||||||
|
void request_impl::header(header_map h) { header_ = std::move(h); }
|
||||||
|
|
||||||
|
header_map &request_impl::header() { return header_; }
|
||||||
|
|
||||||
|
void request_impl::method(std::string arg) { method_ = std::move(arg); }
|
||||||
|
|
||||||
|
void request_impl::on_data(data_cb cb) { on_data_cb_ = std::move(cb); }
|
||||||
|
|
||||||
|
void request_impl::stream(class stream *s) { strm_ = s; }
|
||||||
|
|
||||||
|
void request_impl::call_on_data(const uint8_t *data, std::size_t len) {
|
||||||
|
if (on_data_cb_) {
|
||||||
|
on_data_cb_(data, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace asio_http2
|
||||||
|
} // namespace nghttp2
|
||||||
69
src/asio_server_request_impl.h
Normal file
69
src/asio_server_request_impl.h
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef ASIO_SERVER_REQUEST_IMPL_H
|
||||||
|
#define ASIO_SERVER_REQUEST_IMPL_H
|
||||||
|
|
||||||
|
#include "nghttp2_config.h"
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
|
namespace nghttp2 {
|
||||||
|
namespace asio_http2 {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
class stream;
|
||||||
|
|
||||||
|
class request_impl {
|
||||||
|
public:
|
||||||
|
request_impl();
|
||||||
|
|
||||||
|
void header(header_map h);
|
||||||
|
const header_map &header() const;
|
||||||
|
header_map &header();
|
||||||
|
|
||||||
|
void method(std::string method);
|
||||||
|
const std::string &method() const;
|
||||||
|
|
||||||
|
const uri_ref &uri() const;
|
||||||
|
uri_ref &uri();
|
||||||
|
|
||||||
|
void on_data(data_cb cb);
|
||||||
|
|
||||||
|
void stream(class stream *s);
|
||||||
|
void call_on_data(const uint8_t *data, std::size_t len);
|
||||||
|
|
||||||
|
private:
|
||||||
|
class stream *strm_;
|
||||||
|
header_map header_;
|
||||||
|
std::string method_;
|
||||||
|
uri_ref uri_;
|
||||||
|
data_cb on_data_cb_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace asio_http2
|
||||||
|
} // namespace nghttp2
|
||||||
|
|
||||||
|
#endif // ASIO_SERVER_REQUEST_IMPL_H
|
||||||
75
src/asio_server_response.cc
Normal file
75
src/asio_server_response.cc
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "nghttp2_config.h"
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
|
#include "asio_server_response_impl.h"
|
||||||
|
|
||||||
|
#include "template.h"
|
||||||
|
|
||||||
|
namespace nghttp2 {
|
||||||
|
namespace asio_http2 {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
response::response() : impl_(make_unique<response_impl>()) {}
|
||||||
|
|
||||||
|
response::~response() {}
|
||||||
|
|
||||||
|
void response::write_head(unsigned int status_code, header_map h) const {
|
||||||
|
impl_->write_head(status_code, std::move(h));
|
||||||
|
}
|
||||||
|
|
||||||
|
void response::end(std::string data) const { impl_->end(std::move(data)); }
|
||||||
|
|
||||||
|
void response::end(generator_cb cb) const { impl_->end(std::move(cb)); }
|
||||||
|
|
||||||
|
void response::write_trailer(header_map h) const {
|
||||||
|
impl_->write_trailer(std::move(h));
|
||||||
|
}
|
||||||
|
|
||||||
|
void response::on_close(close_cb cb) const { impl_->on_close(std::move(cb)); }
|
||||||
|
|
||||||
|
void response::cancel(uint32_t error_code) const { impl_->cancel(error_code); }
|
||||||
|
|
||||||
|
const response *response::push(boost::system::error_code &ec,
|
||||||
|
std::string method, std::string path,
|
||||||
|
header_map h) const {
|
||||||
|
return impl_->push(ec, std::move(method), std::move(path), std::move(h));
|
||||||
|
}
|
||||||
|
|
||||||
|
void response::resume() const { impl_->resume(); }
|
||||||
|
|
||||||
|
unsigned int response::status_code() const { return impl_->status_code(); }
|
||||||
|
|
||||||
|
boost::asio::io_service &response::io_service() const {
|
||||||
|
return impl_->io_service();
|
||||||
|
}
|
||||||
|
|
||||||
|
response_impl &response::impl() const { return *impl_; }
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace asio_http2
|
||||||
|
} // namespace nghttp2
|
||||||
163
src/asio_server_response_impl.cc
Normal file
163
src/asio_server_response_impl.cc
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "asio_server_response_impl.h"
|
||||||
|
|
||||||
|
#include "asio_server_stream.h"
|
||||||
|
#include "asio_server_request_impl.h"
|
||||||
|
#include "asio_server_http2_handler.h"
|
||||||
|
#include "asio_common.h"
|
||||||
|
|
||||||
|
#include "http2.h"
|
||||||
|
|
||||||
|
namespace nghttp2 {
|
||||||
|
namespace asio_http2 {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
response_impl::response_impl()
|
||||||
|
: strm_(nullptr), generator_cb_(deferred_generator()), status_code_(200),
|
||||||
|
state_(response_state::INITIAL), pushed_(false),
|
||||||
|
push_promise_sent_(false) {}
|
||||||
|
|
||||||
|
unsigned int response_impl::status_code() const { return status_code_; }
|
||||||
|
|
||||||
|
void response_impl::write_head(unsigned int status_code, header_map h) {
|
||||||
|
if (state_ != response_state::INITIAL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_code_ = status_code;
|
||||||
|
header_ = std::move(h);
|
||||||
|
|
||||||
|
state_ = response_state::HEADER_DONE;
|
||||||
|
|
||||||
|
if (pushed_ && !push_promise_sent_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
start_response();
|
||||||
|
}
|
||||||
|
|
||||||
|
void response_impl::end(std::string data) {
|
||||||
|
end(string_generator(std::move(data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void response_impl::end(generator_cb cb) {
|
||||||
|
if (state_ == response_state::BODY_STARTED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
generator_cb_ = std::move(cb);
|
||||||
|
|
||||||
|
if (state_ == response_state::INITIAL) {
|
||||||
|
write_head(status_code_);
|
||||||
|
} else {
|
||||||
|
// generator_cb is changed, start writing in case it is deferred.
|
||||||
|
auto handler = strm_->handler();
|
||||||
|
handler->resume(*strm_);
|
||||||
|
}
|
||||||
|
|
||||||
|
state_ = response_state::BODY_STARTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void response_impl::write_trailer(header_map h) {
|
||||||
|
auto handler = strm_->handler();
|
||||||
|
handler->submit_trailer(*strm_, std::move(h));
|
||||||
|
}
|
||||||
|
|
||||||
|
void response_impl::start_response() {
|
||||||
|
auto handler = strm_->handler();
|
||||||
|
|
||||||
|
auto &req = strm_->request().impl();
|
||||||
|
|
||||||
|
if (!::nghttp2::http2::expect_response_body(req.method(), status_code_)) {
|
||||||
|
state_ = response_state::BODY_STARTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handler->start_response(*strm_) != 0) {
|
||||||
|
handler->stream_error(strm_->get_stream_id(), NGHTTP2_INTERNAL_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void response_impl::on_close(close_cb cb) { close_cb_ = std::move(cb); }
|
||||||
|
|
||||||
|
void response_impl::call_on_close(uint32_t error_code) {
|
||||||
|
if (close_cb_) {
|
||||||
|
close_cb_(error_code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void response_impl::cancel(uint32_t error_code) {
|
||||||
|
auto handler = strm_->handler();
|
||||||
|
handler->stream_error(strm_->get_stream_id(), error_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
response *response_impl::push(boost::system::error_code &ec, std::string method,
|
||||||
|
std::string raw_path_query, header_map h) const {
|
||||||
|
auto handler = strm_->handler();
|
||||||
|
return handler->push_promise(ec, *strm_, std::move(method),
|
||||||
|
std::move(raw_path_query), std::move(h));
|
||||||
|
}
|
||||||
|
|
||||||
|
void response_impl::resume() {
|
||||||
|
auto handler = strm_->handler();
|
||||||
|
handler->resume(*strm_);
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::asio::io_service &response_impl::io_service() {
|
||||||
|
return strm_->handler()->io_service();
|
||||||
|
}
|
||||||
|
|
||||||
|
void response_impl::pushed(bool f) { pushed_ = f; }
|
||||||
|
|
||||||
|
void response_impl::push_promise_sent() {
|
||||||
|
if (push_promise_sent_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
push_promise_sent_ = true;
|
||||||
|
if (state_ == response_state::INITIAL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
start_response();
|
||||||
|
}
|
||||||
|
|
||||||
|
const header_map &response_impl::header() const { return header_; }
|
||||||
|
|
||||||
|
void response_impl::stream(class stream *s) { strm_ = s; }
|
||||||
|
|
||||||
|
generator_cb::result_type
|
||||||
|
response_impl::call_read(uint8_t *data, std::size_t len, uint32_t *data_flags) {
|
||||||
|
if (generator_cb_) {
|
||||||
|
return generator_cb_(data, len, data_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace asio_http2
|
||||||
|
} // namespace nghttp2
|
||||||
92
src/asio_server_response_impl.h
Normal file
92
src/asio_server_response_impl.h
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef ASIO_SERVER_RESPONSE_IMPL_H
|
||||||
|
#define ASIO_SERVER_RESPONSE_IMPL_H
|
||||||
|
|
||||||
|
#include "nghttp2_config.h"
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
|
namespace nghttp2 {
|
||||||
|
namespace asio_http2 {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
class stream;
|
||||||
|
|
||||||
|
enum class response_state {
|
||||||
|
INITIAL,
|
||||||
|
// response_impl::write_head() was called
|
||||||
|
HEADER_DONE,
|
||||||
|
// response_impl::end() was called
|
||||||
|
BODY_STARTED,
|
||||||
|
};
|
||||||
|
|
||||||
|
class response_impl {
|
||||||
|
public:
|
||||||
|
response_impl();
|
||||||
|
void write_head(unsigned int status_code, header_map h = header_map{});
|
||||||
|
void end(std::string data = "");
|
||||||
|
void end(generator_cb cb);
|
||||||
|
void write_trailer(header_map h);
|
||||||
|
void on_close(close_cb cb);
|
||||||
|
void resume();
|
||||||
|
|
||||||
|
void cancel(uint32_t error_code);
|
||||||
|
|
||||||
|
response *push(boost::system::error_code &ec, std::string method,
|
||||||
|
std::string raw_path_query, header_map) const;
|
||||||
|
|
||||||
|
boost::asio::io_service &io_service();
|
||||||
|
|
||||||
|
void start_response();
|
||||||
|
|
||||||
|
unsigned int status_code() const;
|
||||||
|
const header_map &header() const;
|
||||||
|
void pushed(bool f);
|
||||||
|
void push_promise_sent();
|
||||||
|
void stream(class stream *s);
|
||||||
|
generator_cb::result_type call_read(uint8_t *data, std::size_t len,
|
||||||
|
uint32_t *data_flags);
|
||||||
|
void call_on_close(uint32_t error_code);
|
||||||
|
|
||||||
|
private:
|
||||||
|
class stream *strm_;
|
||||||
|
header_map header_;
|
||||||
|
generator_cb generator_cb_;
|
||||||
|
close_cb close_cb_;
|
||||||
|
unsigned int status_code_;
|
||||||
|
response_state state_;
|
||||||
|
// true if this is pushed stream's response
|
||||||
|
bool pushed_;
|
||||||
|
// true if PUSH_PROMISE is sent if this is response of a pushed
|
||||||
|
// stream
|
||||||
|
bool push_promise_sent_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace asio_http2
|
||||||
|
} // namespace nghttp2
|
||||||
|
|
||||||
|
#endif // ASIO_SERVER_RESPONSE_IMPL_H
|
||||||
138
src/asio_server_serve_mux.cc
Normal file
138
src/asio_server_serve_mux.cc
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "asio_server_serve_mux.h"
|
||||||
|
|
||||||
|
#include "asio_server_request_impl.h"
|
||||||
|
#include "asio_server_request_handler.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "http2.h"
|
||||||
|
|
||||||
|
namespace nghttp2 {
|
||||||
|
|
||||||
|
namespace asio_http2 {
|
||||||
|
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
bool serve_mux::handle(std::string pattern, request_cb cb) {
|
||||||
|
if (pattern.empty() || !cb) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = mux_.find(pattern);
|
||||||
|
if (it != std::end(mux_) && (*it).second.user_defined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if pattern ends with '/' (e.g., /foo/), add implicit permanent
|
||||||
|
// redirect for '/foo'.
|
||||||
|
if (pattern.size() >= 2 && pattern.back() == '/') {
|
||||||
|
auto redirect_pattern = pattern.substr(0, pattern.size() - 1);
|
||||||
|
auto it = mux_.find(redirect_pattern);
|
||||||
|
if (it == std::end(mux_) || !(*it).second.user_defined) {
|
||||||
|
std::string path;
|
||||||
|
if (pattern[0] == '/') {
|
||||||
|
path = pattern;
|
||||||
|
} else {
|
||||||
|
// skip host part
|
||||||
|
path = pattern.substr(pattern.find('/'));
|
||||||
|
}
|
||||||
|
if (it == std::end(mux_)) {
|
||||||
|
mux_.emplace(std::move(redirect_pattern),
|
||||||
|
handler_entry{false,
|
||||||
|
redirect_handler(301, std::move(path)),
|
||||||
|
pattern});
|
||||||
|
} else {
|
||||||
|
(*it).second = handler_entry{
|
||||||
|
false, redirect_handler(301, std::move(path)), pattern};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mux_.emplace(pattern, handler_entry{true, std::move(cb), pattern});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
request_cb serve_mux::handler(request_impl &req) const {
|
||||||
|
auto &path = req.uri().path;
|
||||||
|
if (req.method() != "CONNECT") {
|
||||||
|
auto clean_path = ::nghttp2::http2::path_join(
|
||||||
|
nullptr, 0, nullptr, 0, path.c_str(), path.size(), nullptr, 0);
|
||||||
|
if (clean_path != path) {
|
||||||
|
auto new_uri = util::percent_encode_path(clean_path);
|
||||||
|
auto &uref = req.uri();
|
||||||
|
if (!uref.raw_query.empty()) {
|
||||||
|
new_uri += "?";
|
||||||
|
new_uri += uref.raw_query;
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect_handler(301, std::move(new_uri));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto &host = req.uri().host;
|
||||||
|
|
||||||
|
auto cb = match(host + path);
|
||||||
|
if (cb) {
|
||||||
|
return cb;
|
||||||
|
}
|
||||||
|
cb = match(path);
|
||||||
|
if (cb) {
|
||||||
|
return cb;
|
||||||
|
}
|
||||||
|
return status_handler(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
bool path_match(const std::string &pattern, const std::string &path) {
|
||||||
|
if (pattern.back() != '/') {
|
||||||
|
return pattern == path;
|
||||||
|
}
|
||||||
|
return util::startsWith(path, pattern);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
request_cb serve_mux::match(const std::string &path) const {
|
||||||
|
const handler_entry *ent = nullptr;
|
||||||
|
size_t best = 0;
|
||||||
|
for (auto &kv : mux_) {
|
||||||
|
auto &pattern = kv.first;
|
||||||
|
if (!path_match(pattern, path)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!ent || best < pattern.size()) {
|
||||||
|
best = pattern.size();
|
||||||
|
ent = &kv.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ent) {
|
||||||
|
return ent->cb;
|
||||||
|
}
|
||||||
|
return request_cb();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
|
||||||
|
} // namespace asio_http2
|
||||||
|
|
||||||
|
} // namespace nghttp2
|
||||||
64
src/asio_server_serve_mux.h
Normal file
64
src/asio_server_serve_mux.h
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef ASIO_SERVER_SERVE_MUX_H
|
||||||
|
#define ASIO_SERVER_SERVE_MUX_H
|
||||||
|
|
||||||
|
#include "nghttp2_config.h"
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
|
namespace nghttp2 {
|
||||||
|
|
||||||
|
namespace asio_http2 {
|
||||||
|
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
class request_impl;
|
||||||
|
|
||||||
|
// port from go's ServeMux
|
||||||
|
|
||||||
|
struct handler_entry {
|
||||||
|
bool user_defined;
|
||||||
|
request_cb cb;
|
||||||
|
std::string pattern;
|
||||||
|
};
|
||||||
|
|
||||||
|
class serve_mux {
|
||||||
|
public:
|
||||||
|
bool handle(std::string pattern, request_cb cb);
|
||||||
|
request_cb handler(request_impl &req) const;
|
||||||
|
request_cb match(const std::string &path) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::map<std::string, handler_entry> mux_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
|
||||||
|
} // namespace asio_http2
|
||||||
|
|
||||||
|
} // namespace nghttp2
|
||||||
|
|
||||||
|
#endif // ASIO_SERVER_SERVE_MUX_H
|
||||||
55
src/asio_server_stream.cc
Normal file
55
src/asio_server_stream.cc
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "asio_server_stream.h"
|
||||||
|
|
||||||
|
#include "asio_server_http2_handler.h"
|
||||||
|
#include "asio_server_request_impl.h"
|
||||||
|
#include "asio_server_response_impl.h"
|
||||||
|
|
||||||
|
namespace nghttp2 {
|
||||||
|
namespace asio_http2 {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
stream::stream(http2_handler *h, int32_t stream_id)
|
||||||
|
: handler_(h), stream_id_(stream_id) {
|
||||||
|
request_.impl().stream(this);
|
||||||
|
response_.impl().stream(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t stream::get_stream_id() const { return stream_id_; }
|
||||||
|
|
||||||
|
class request &stream::request() {
|
||||||
|
return request_;
|
||||||
|
}
|
||||||
|
|
||||||
|
class response &stream::response() {
|
||||||
|
return response_;
|
||||||
|
}
|
||||||
|
|
||||||
|
http2_handler *stream::handler() const { return handler_; }
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace asio_http2
|
||||||
|
} // namespace nghttp2
|
||||||
59
src/asio_server_stream.h
Normal file
59
src/asio_server_stream.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef ASIO_SERVER_STREAM_H
|
||||||
|
#define ASIO_SERVER_STREAM_H
|
||||||
|
|
||||||
|
#include "nghttp2_config.h"
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
|
namespace nghttp2 {
|
||||||
|
namespace asio_http2 {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
class http2_handler;
|
||||||
|
|
||||||
|
class stream {
|
||||||
|
public:
|
||||||
|
stream(http2_handler *h, int32_t stream_id);
|
||||||
|
|
||||||
|
int32_t get_stream_id() const;
|
||||||
|
class request &request();
|
||||||
|
class response &response();
|
||||||
|
|
||||||
|
http2_handler *handler() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
http2_handler *handler_;
|
||||||
|
class request request_;
|
||||||
|
class response response_;
|
||||||
|
int32_t stream_id_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace asio_http2
|
||||||
|
} // namespace nghttp2
|
||||||
|
|
||||||
|
#endif // ASIO_SERVER_STREAM_H
|
||||||
85
src/asio_server_tls_context.cc
Normal file
85
src/asio_server_tls_context.cc
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "asio_server_tls_context.h"
|
||||||
|
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
|
||||||
|
#include <boost/asio/ssl.hpp>
|
||||||
|
|
||||||
|
#include "ssl.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
namespace nghttp2 {
|
||||||
|
namespace asio_http2 {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::vector<unsigned char> &get_alpn_token() {
|
||||||
|
static auto alpn_token = util::get_default_alpn();
|
||||||
|
return alpn_token;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
boost::system::error_code
|
||||||
|
configure_tls_context_easy(boost::system::error_code &ec,
|
||||||
|
boost::asio::ssl::context &tls_context) {
|
||||||
|
ec.clear();
|
||||||
|
|
||||||
|
auto ctx = tls_context.native_handle();
|
||||||
|
|
||||||
|
SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
||||||
|
SSL_OP_NO_COMPRESSION |
|
||||||
|
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |
|
||||||
|
SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_TICKET |
|
||||||
|
SSL_OP_CIPHER_SERVER_PREFERENCE);
|
||||||
|
SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
|
||||||
|
SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
|
||||||
|
|
||||||
|
SSL_CTX_set_cipher_list(ctx, ssl::DEFAULT_CIPHER_LIST);
|
||||||
|
|
||||||
|
auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||||
|
if (ecdh) {
|
||||||
|
SSL_CTX_set_tmp_ecdh(ctx, ecdh);
|
||||||
|
EC_KEY_free(ecdh);
|
||||||
|
}
|
||||||
|
|
||||||
|
SSL_CTX_set_next_protos_advertised_cb(
|
||||||
|
ctx,
|
||||||
|
[](SSL *s, const unsigned char **data, unsigned int *len, void *arg) {
|
||||||
|
auto &token = get_alpn_token();
|
||||||
|
|
||||||
|
*data = token.data();
|
||||||
|
*len = token.size();
|
||||||
|
|
||||||
|
return SSL_TLSEXT_ERR_OK;
|
||||||
|
},
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
return ec;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace asio_http2
|
||||||
|
} // namespace nghttp2
|
||||||
32
src/asio_server_tls_context.h
Normal file
32
src/asio_server_tls_context.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef ASIO_SERVER_TLS_CONTEXT_H
|
||||||
|
#define ASIO_SERVER_TLS_CONTEXT_H
|
||||||
|
|
||||||
|
#include "nghttp2_config.h"
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2_server.h>
|
||||||
|
|
||||||
|
#endif // ASIO_SERVER_TLS_CONTEXT_H
|
||||||
@@ -28,6 +28,8 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
@@ -57,16 +59,27 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "template.h"
|
#include "template.h"
|
||||||
|
|
||||||
|
#ifndef O_BINARY
|
||||||
|
#define O_BINARY (0)
|
||||||
|
#endif // O_BINARY
|
||||||
|
|
||||||
using namespace nghttp2;
|
using namespace nghttp2;
|
||||||
|
|
||||||
namespace h2load {
|
namespace h2load {
|
||||||
|
|
||||||
Config::Config()
|
Config::Config()
|
||||||
: addrs(nullptr), nreqs(1), nclients(1), nthreads(1),
|
: data_length(-1), addrs(nullptr), nreqs(1), nclients(1), nthreads(1),
|
||||||
max_concurrent_streams(-1), window_bits(16), connection_window_bits(16),
|
max_concurrent_streams(-1), window_bits(16), connection_window_bits(16),
|
||||||
no_tls_proto(PROTO_HTTP2), port(0), default_port(0), verbose(false) {}
|
no_tls_proto(PROTO_HTTP2), data_fd(-1), port(0), default_port(0),
|
||||||
|
verbose(false) {}
|
||||||
|
|
||||||
Config::~Config() { freeaddrinfo(addrs); }
|
Config::~Config() {
|
||||||
|
freeaddrinfo(addrs);
|
||||||
|
|
||||||
|
if (data_fd != -1) {
|
||||||
|
close(data_fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Config config;
|
Config config;
|
||||||
|
|
||||||
@@ -95,7 +108,7 @@ void debug_nextproto_error() {
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
RequestStat::RequestStat() : completed(false) {}
|
RequestStat::RequestStat() : data_offset(0), completed(false) {}
|
||||||
|
|
||||||
Stats::Stats(size_t req_todo)
|
Stats::Stats(size_t req_todo)
|
||||||
: req_todo(0), req_started(0), req_done(0), req_success(0),
|
: req_todo(0), req_started(0), req_done(0), req_success(0),
|
||||||
@@ -987,6 +1000,9 @@ Options:
|
|||||||
#endif // !HAVE_SPDYLAY
|
#endif // !HAVE_SPDYLAY
|
||||||
out << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
|
out << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
|
||||||
Default: )" << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
|
Default: )" << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
|
||||||
|
-d, --data=<FILE>
|
||||||
|
Post FILE to server. The request method is changed to
|
||||||
|
POST.
|
||||||
-v, --verbose
|
-v, --verbose
|
||||||
Output debug information.
|
Output debug information.
|
||||||
--version Display version information and exit.
|
--version Display version information and exit.
|
||||||
@@ -995,11 +1011,13 @@ Options:
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
|
std::string datafile;
|
||||||
while (1) {
|
while (1) {
|
||||||
static int flag = 0;
|
static int flag = 0;
|
||||||
static option long_options[] = {
|
static option long_options[] = {
|
||||||
{"requests", required_argument, nullptr, 'n'},
|
{"requests", required_argument, nullptr, 'n'},
|
||||||
{"clients", required_argument, nullptr, 'c'},
|
{"clients", required_argument, nullptr, 'c'},
|
||||||
|
{"data", required_argument, nullptr, 'd'},
|
||||||
{"threads", required_argument, nullptr, 't'},
|
{"threads", required_argument, nullptr, 't'},
|
||||||
{"max-concurrent-streams", required_argument, nullptr, 'm'},
|
{"max-concurrent-streams", required_argument, nullptr, 'm'},
|
||||||
{"window-bits", required_argument, nullptr, 'w'},
|
{"window-bits", required_argument, nullptr, 'w'},
|
||||||
@@ -1012,7 +1030,7 @@ int main(int argc, char **argv) {
|
|||||||
{"version", no_argument, &flag, 1},
|
{"version", no_argument, &flag, 1},
|
||||||
{nullptr, 0, nullptr, 0}};
|
{nullptr, 0, nullptr, 0}};
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
auto c = getopt_long(argc, argv, "hvW:c:m:n:p:t:w:H:i:", long_options,
|
auto c = getopt_long(argc, argv, "hvW:c:d:m:n:p:t:w:H:i:", long_options,
|
||||||
&option_index);
|
&option_index);
|
||||||
if (c == -1) {
|
if (c == -1) {
|
||||||
break;
|
break;
|
||||||
@@ -1024,6 +1042,9 @@ int main(int argc, char **argv) {
|
|||||||
case 'c':
|
case 'c':
|
||||||
config.nclients = strtoul(optarg, nullptr, 10);
|
config.nclients = strtoul(optarg, nullptr, 10);
|
||||||
break;
|
break;
|
||||||
|
case 'd':
|
||||||
|
datafile = optarg;
|
||||||
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
#ifdef NOTHREADS
|
#ifdef NOTHREADS
|
||||||
std::cerr << "-t: WARNING: Threading disabled at build time, "
|
std::cerr << "-t: WARNING: Threading disabled at build time, "
|
||||||
@@ -1157,11 +1178,31 @@ int main(int argc, char **argv) {
|
|||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.nclients < config.nthreads) {
|
||||||
|
std::cerr << "-c, -t: the number of client must be greater than or equal "
|
||||||
|
"to the number of threads." << std::endl;
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
if (config.nthreads > std::thread::hardware_concurrency()) {
|
if (config.nthreads > std::thread::hardware_concurrency()) {
|
||||||
std::cerr << "-t: warning: the number of threads is greater than hardware "
|
std::cerr << "-t: warning: the number of threads is greater than hardware "
|
||||||
<< "cores." << std::endl;
|
<< "cores." << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!datafile.empty()) {
|
||||||
|
config.data_fd = open(datafile.c_str(), O_RDONLY | O_BINARY);
|
||||||
|
if (config.data_fd == -1) {
|
||||||
|
std::cerr << "-d: Could not open file " << datafile << std::endl;
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
struct stat data_stat;
|
||||||
|
if (fstat(config.data_fd, &data_stat) == -1) {
|
||||||
|
std::cerr << "-d: Could not stat file " << datafile << std::endl;
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
config.data_length = data_stat.st_size;
|
||||||
|
}
|
||||||
|
|
||||||
struct sigaction act;
|
struct sigaction act;
|
||||||
memset(&act, 0, sizeof(struct sigaction));
|
memset(&act, 0, sizeof(struct sigaction));
|
||||||
act.sa_handler = SIG_IGN;
|
act.sa_handler = SIG_IGN;
|
||||||
@@ -1243,7 +1284,7 @@ int main(int argc, char **argv) {
|
|||||||
} else {
|
} else {
|
||||||
shared_nva.emplace_back(":authority", config.host);
|
shared_nva.emplace_back(":authority", config.host);
|
||||||
}
|
}
|
||||||
shared_nva.emplace_back(":method", "GET");
|
shared_nva.emplace_back(":method", config.data_fd == -1 ? "GET" : "POST");
|
||||||
|
|
||||||
// list overridalbe headers
|
// list overridalbe headers
|
||||||
auto override_hdrs =
|
auto override_hdrs =
|
||||||
@@ -1315,7 +1356,7 @@ int main(int argc, char **argv) {
|
|||||||
auto start = std::chrono::steady_clock::now();
|
auto start = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
std::vector<std::unique_ptr<Worker>> workers;
|
std::vector<std::unique_ptr<Worker>> workers;
|
||||||
workers.reserve(config.nthreads - 1);
|
workers.reserve(config.nthreads);
|
||||||
|
|
||||||
#ifndef NOTHREADS
|
#ifndef NOTHREADS
|
||||||
std::vector<std::future<void>> futures;
|
std::vector<std::future<void>> futures;
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ struct Config {
|
|||||||
std::string scheme;
|
std::string scheme;
|
||||||
std::string host;
|
std::string host;
|
||||||
std::string ifile;
|
std::string ifile;
|
||||||
|
// length of upload data
|
||||||
|
int64_t data_length;
|
||||||
addrinfo *addrs;
|
addrinfo *addrs;
|
||||||
size_t nreqs;
|
size_t nreqs;
|
||||||
size_t nclients;
|
size_t nclients;
|
||||||
@@ -69,6 +71,8 @@ struct Config {
|
|||||||
size_t window_bits;
|
size_t window_bits;
|
||||||
size_t connection_window_bits;
|
size_t connection_window_bits;
|
||||||
enum { PROTO_HTTP2, PROTO_SPDY2, PROTO_SPDY3, PROTO_SPDY3_1 } no_tls_proto;
|
enum { PROTO_HTTP2, PROTO_SPDY2, PROTO_SPDY3, PROTO_SPDY3_1 } no_tls_proto;
|
||||||
|
// file descriptor for upload data
|
||||||
|
int data_fd;
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
uint16_t default_port;
|
uint16_t default_port;
|
||||||
bool verbose;
|
bool verbose;
|
||||||
@@ -83,6 +87,8 @@ struct RequestStat {
|
|||||||
std::chrono::steady_clock::time_point request_time;
|
std::chrono::steady_clock::time_point request_time;
|
||||||
// time point when stream was closed
|
// time point when stream was closed
|
||||||
std::chrono::steady_clock::time_point stream_close_time;
|
std::chrono::steady_clock::time_point stream_close_time;
|
||||||
|
// upload data length sent so far
|
||||||
|
int64_t data_offset;
|
||||||
// true if stream was successfully closed. This means stream was
|
// true if stream was successfully closed. This means stream was
|
||||||
// not reset, but it does not mean HTTP level error (e.g., 404).
|
// not reset, but it does not mean HTTP level error (e.g., 404).
|
||||||
bool completed;
|
bool completed;
|
||||||
|
|||||||
@@ -110,6 +110,36 @@ int before_frame_send_callback(nghttp2_session *session,
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
|
||||||
|
uint8_t *buf, size_t length, uint32_t *data_flags,
|
||||||
|
nghttp2_data_source *source, void *user_data) {
|
||||||
|
auto client = static_cast<Client *>(user_data);
|
||||||
|
auto config = client->worker->config;
|
||||||
|
auto req_stat = static_cast<RequestStat *>(
|
||||||
|
nghttp2_session_get_stream_user_data(session, stream_id));
|
||||||
|
|
||||||
|
ssize_t nread;
|
||||||
|
while ((nread = pread(config->data_fd, buf, length, req_stat->data_offset)) ==
|
||||||
|
-1 &&
|
||||||
|
errno == EINTR)
|
||||||
|
;
|
||||||
|
|
||||||
|
if (nread == -1) {
|
||||||
|
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
req_stat->data_offset += nread;
|
||||||
|
|
||||||
|
if (nread == 0 || req_stat->data_offset == config->data_length) {
|
||||||
|
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nread;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
ssize_t send_callback(nghttp2_session *session, const uint8_t *data,
|
ssize_t send_callback(nghttp2_session *session, const uint8_t *data,
|
||||||
size_t length, int flags, void *user_data) {
|
size_t length, int flags, void *user_data) {
|
||||||
@@ -188,8 +218,11 @@ void Http2Session::submit_request(RequestStat *req_stat) {
|
|||||||
client_->reqidx = 0;
|
client_->reqidx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto stream_id = nghttp2_submit_request(session_, nullptr, nva.data(),
|
nghttp2_data_provider prd{{0}, file_read_callback};
|
||||||
nva.size(), nullptr, req_stat);
|
|
||||||
|
auto stream_id =
|
||||||
|
nghttp2_submit_request(session_, nullptr, nva.data(), nva.size(),
|
||||||
|
config->data_fd == -1 ? nullptr : &prd, req_stat);
|
||||||
assert(stream_id > 0);
|
assert(stream_id > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -110,6 +110,35 @@ ssize_t send_callback(spdylay_session *session, const uint8_t *data,
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
ssize_t file_read_callback(spdylay_session *session, int32_t stream_id,
|
||||||
|
uint8_t *buf, size_t length, int *eof,
|
||||||
|
spdylay_data_source *source, void *user_data) {
|
||||||
|
auto client = static_cast<Client *>(user_data);
|
||||||
|
auto config = client->worker->config;
|
||||||
|
auto req_stat = static_cast<RequestStat *>(
|
||||||
|
spdylay_session_get_stream_user_data(session, stream_id));
|
||||||
|
|
||||||
|
ssize_t nread;
|
||||||
|
while ((nread = pread(config->data_fd, buf, length, req_stat->data_offset)) ==
|
||||||
|
-1 &&
|
||||||
|
errno == EINTR)
|
||||||
|
;
|
||||||
|
|
||||||
|
if (nread == -1) {
|
||||||
|
return SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
req_stat->data_offset += nread;
|
||||||
|
|
||||||
|
if (nread == 0 || req_stat->data_offset == config->data_length) {
|
||||||
|
*eof = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nread;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void SpdySession::on_connect() {
|
void SpdySession::on_connect() {
|
||||||
spdylay_session_callbacks callbacks = {0};
|
spdylay_session_callbacks callbacks = {0};
|
||||||
callbacks.send_callback = send_callback;
|
callbacks.send_callback = send_callback;
|
||||||
@@ -150,7 +179,10 @@ void SpdySession::submit_request(RequestStat *req_stat) {
|
|||||||
client_->reqidx = 0;
|
client_->reqidx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
spdylay_submit_request(session_, 0, nv.data(), nullptr, req_stat);
|
spdylay_data_provider prd{{0}, file_read_callback};
|
||||||
|
|
||||||
|
spdylay_submit_request(session_, 0, nv.data(),
|
||||||
|
config->data_fd == -1 ? nullptr : &prd, req_stat);
|
||||||
}
|
}
|
||||||
|
|
||||||
int SpdySession::on_read(const uint8_t *data, size_t len) {
|
int SpdySession::on_read(const uint8_t *data, size_t len) {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user