Update documents using updated libnghttp2_asio API, including client API

This commit is contained in:
Tatsuhiro Tsujikawa 2015-03-07 03:12:13 +09:00
parent 66f5438dc9
commit 76eb3193ab
7 changed files with 371 additions and 108 deletions

2
.gitignore vendored
View File

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

View File

@ -1054,6 +1054,58 @@ HTTP/2 server looks like this:
} }
} }
Here is the sample code for client API use:
.. code-block:: cpp
#include <iostream>
#include <nghttp2/asio_http2_client.h>
using boost::asio::ip::tcp;
using namespace nghttp2::asio_http2;
using namespace nghttp2::asio_http2::client;
int main(int argc, char *argv[]) {
boost::system::error_code ec;
boost::asio::io_service io_service;
// connect to localhost:3000
session sess(io_service, "localhost", "3000");
sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
boost::system::error_code ec;
auto req = sess.submit(ec, "GET", "http://localhost:3000/");
req->on_response([](const response &res) {
// print status code and response header fields.
std::cerr << "HTTP/2 " << res.status_code() << std::endl;
for (auto &kv : res.header()) {
std::cerr << kv.first << ": " << kv.second.value << "\n";
}
std::cerr << std::endl;
res.on_data([](const uint8_t *data, std::size_t len) {
std::cerr.write(reinterpret_cast<const char *>(data), len);
std::cerr << std::endl;
});
});
req->on_close([&sess](uint32_t error_code) {
// shutdown session after first request was done.
sess.shutdown();
});
});
sess.on_error([](const boost::system::error_code &ec) {
std::cerr << "error: " << ec.message() << std::endl;
});
io_service.run();
}
For more details, see the documentation of libnghttp2_asio. For more details, see the documentation of libnghttp2_asio.
Python bindings Python bindings

View File

@ -660,6 +660,8 @@ AC_CONFIG_FILES([
doc/nghttp2.h.rst doc/nghttp2.h.rst
doc/nghttp2ver.h.rst doc/nghttp2ver.h.rst
doc/asio_http2.h.rst doc/asio_http2.h.rst
doc/asio_http2_server.h.rst
doc/asio_http2_client.h.rst
doc/contribute.rst doc/contribute.rst
contrib/Makefile contrib/Makefile
]) ])

View File

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

View File

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

View File

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

View File

@ -4,7 +4,7 @@ libnghttp2_asio: High level HTTP/2 C++ library
libnghttp2_asio is C++ library built on top of libnghttp2 and provides libnghttp2_asio is C++ library built on top of libnghttp2 and provides
high level abstraction API to build HTTP/2 applications. It depends high level abstraction API to build HTTP/2 applications. It depends
on Boost::ASIO library and OpenSSL. Currently libnghttp2_asio on Boost::ASIO library and OpenSSL. Currently libnghttp2_asio
provides server side API. provides server and client side API.
libnghttp2_asio is not built by default. Use ``--enable-asio-lib`` libnghttp2_asio is not built by default. Use ``--enable-asio-lib``
configure flag to build libnghttp2_asio. The required Boost libraries configure flag to build libnghttp2_asio. The required Boost libraries
@ -14,42 +14,54 @@ 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`.
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 +73,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 +85,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 +134,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 +199,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.