• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1libnghttp2_asio: High level HTTP/2 C++ library
2==============================================
3
4libnghttp2_asio is C++ library built on top of libnghttp2 and provides
5high level abstraction API to build HTTP/2 applications.  It depends
6on Boost::ASIO library and OpenSSL.  Currently libnghttp2_asio
7provides server and client side API.
8
9libnghttp2_asio is not built by default.  Use ``--enable-asio-lib``
10configure flag to build libnghttp2_asio.  The required Boost libraries
11are:
12
13* Boost::Asio
14* Boost::System
15* Boost::Thread
16
17We have 3 header files for this library:
18
19* :doc:`asio_http2_server.h`
20* :doc:`asio_http2_client.h`
21* :doc:`asio_http2.h`
22
23asio_http2.h is included from the other two files.
24
25To build a program with libnghttp2_asio, link to the following
26libraries::
27
28    -lnghttp2_asio -lboost_system
29
30If ``boost::asio::ssl`` is used in application code, OpenSSL is also
31required in link line::
32
33    -lnghttp2_asio -lboost_system -lssl -lcrypto
34
35Server API
36----------
37
38To use server API, first include following header file:
39
40.. code-block:: cpp
41
42    #include <nghttp2/asio_http2_server.h>
43
44Also take a look at that header file :doc:`asio_http2_server.h`.
45
46Server API is designed to build HTTP/2 server very easily to utilize
47C++11 anonymous function and closure.  The bare minimum example of
48HTTP/2 server looks like this:
49
50.. code-block:: cpp
51
52    using namespace nghttp2::asio_http2;
53    using namespace nghttp2::asio_http2::server;
54
55    int main(int argc, char *argv[]) {
56      boost::system::error_code ec;
57      http2 server;
58
59      server.handle("/", [](const request &req, const response &res) {
60        res.write_head(200);
61        res.end("hello, world\n");
62      });
63
64      if (server.listen_and_serve(ec, "localhost", "3000")) {
65        std::cerr << "error: " << ec.message() << std::endl;
66      }
67    }
68
69First we instantiate ``nghttp2::asio_http2::server::http2`` object.
70``nghttp2::asio_http2::server::http2::handle`` function registers
71pattern and its handler function.  In this example, we register "/" as
72pattern, which matches all requests.  Then call
73``nghttp2::asio_http2::server::http2::listen_and_serve`` function with
74address and port to listen to.
75
76The ``req`` and ``res`` represent HTTP request and response
77respectively.  ``nghttp2::asio_http2_::server::response::write_head``
78constructs HTTP response header fields.  The first argument is HTTP
79status code, in the above example, which is 200.  The second argument,
80which is omitted in the above example, is additional header fields to
81send.
82
83``nghttp2::asio_http2::server::response::end`` sends response body.
84In the above example, we send string "hello, world".
85
86The life time of req and res object ends after the callback set by
87``nghttp2::asio_http2::server::response::on_close`` function.
88Application must not use those objects after this call.
89
90Serving static files and enabling SSL/TLS
91+++++++++++++++++++++++++++++++++++++++++
92
93In this example, we serve a couple of static files and also enable
94SSL/TLS.
95
96.. code-block:: cpp
97
98    #include <nghttp2/asio_http2_server.h>
99
100    using namespace nghttp2::asio_http2;
101    using namespace nghttp2::asio_http2::server;
102
103    int main(int argc, char *argv[]) {
104      boost::system::error_code ec;
105      boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
106
107      tls.use_private_key_file("server.key", boost::asio::ssl::context::pem);
108      tls.use_certificate_chain_file("server.crt");
109
110      configure_tls_context_easy(ec, tls);
111
112      http2 server;
113
114      server.handle("/index.html", [](const request &req, const response &res) {
115        res.write_head(200);
116        res.end(file_generator("index.html"));
117      });
118
119      if (server.listen_and_serve(ec, tls, "localhost", "3000")) {
120        std::cerr << "error: " << ec.message() << std::endl;
121      }
122    }
123
124We first create ``boost::asio::ssl::context`` object and set path to
125private key file and certificate file.
126``nghttp2::asio_http2::server::configure_tls_context_easy`` function
127configures SSL/TLS context object for HTTP/2 server use, including NPN
128callbacks.
129
130In the above example, if request path is "/index.html", we serve
131index.html file in the current working directory.
132``nghttp2::asio_http2::server::response::end`` has overload to take
133function of type ``nghttp2::asio_http2::generator_cb`` and application
134pass its implementation to generate response body.  For the
135convenience, libnghttp2_asio library provides
136``nghttp2::asio_http2::file_generator`` function to generate function
137to server static file.  If other resource is requested, server
138automatically responds with 404 status code.
139
140Server push
141+++++++++++
142
143Server push is also supported.
144
145.. code-block:: cpp
146
147    #include <nghttp2/asio_http2_server.h>
148
149    using namespace nghttp2::asio_http2;
150    using namespace nghttp2::asio_http2::server;
151
152    int main(int argc, char *argv[]) {
153      boost::system::error_code ec;
154      boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
155
156      tls.use_private_key_file("server.key", boost::asio::ssl::context::pem);
157      tls.use_certificate_chain_file("server.crt");
158
159      configure_tls_context_easy(ec, tls);
160
161      http2 server;
162
163      std::string style_css = "h1 { color: green; }";
164
165      server.handle("/", [&style_css](const request &req, const response &res) {
166        boost::system::error_code ec;
167        auto push = res.push(ec, "GET", "/style.css");
168        push->write_head(200);
169        push->end(style_css);
170
171        res.write_head(200);
172        res.end(R"(
173    <!DOCTYPE html><html lang="en">
174    <title>HTTP/2 FTW</title><body>
175    <link href="/style.css" rel="stylesheet" type="text/css">
176    <h1>This should be green</h1>
177    </body></html>
178    )");
179      });
180
181      server.handle("/style.css",
182                    [&style_css](const request &req, const response &res) {
183        res.write_head(200);
184        res.end(style_css);
185      });
186
187      if (server.listen_and_serve(ec, tls, "localhost", "3000")) {
188        std::cerr << "error: " << ec.message() << std::endl;
189      }
190    }
191
192When client requested any resource other than "/style.css", we push
193"/style.css".  To push resource, call
194``nghttp2::asio_http2::server::response::push`` function with desired
195method and path.  It returns another response object and use its
196functions to send push response.
197
198Enable multi-threading
199++++++++++++++++++++++
200
201Enabling multi-threading is very easy.  Just call
202``nghttp2::asio_http2::server::http2::num_threads`` function with the
203desired number of threads:
204
205.. code-block:: cpp
206
207    http2 server;
208
209    // Use 4 native threads
210    server.num_threads(4);
211
212Client API
213----------
214
215To use client API, first include following header file:
216
217.. code-block:: cpp
218
219    #include <nghttp2/asio_http2_client.h>
220
221Also take a look at that header file :doc:`asio_http2_client.h`.
222
223Here is the sample client code to access HTTP/2 server and print out
224response header fields and response body to the console screen:
225
226.. code-block:: cpp
227
228    #include <iostream>
229
230    #include <nghttp2/asio_http2_client.h>
231
232    using boost::asio::ip::tcp;
233
234    using namespace nghttp2::asio_http2;
235    using namespace nghttp2::asio_http2::client;
236
237    int main(int argc, char *argv[]) {
238      boost::system::error_code ec;
239      boost::asio::io_service io_service;
240
241      // connect to localhost:3000
242      session sess(io_service, "localhost", "3000");
243
244      sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
245	boost::system::error_code ec;
246
247	auto req = sess.submit(ec, "GET", "http://localhost:3000/");
248
249	req->on_response([](const response &res) {
250	  // print status code and response header fields.
251	  std::cerr << "HTTP/2 " << res.status_code() << std::endl;
252	  for (auto &kv : res.header()) {
253	    std::cerr << kv.first << ": " << kv.second.value << "\n";
254	  }
255	  std::cerr << std::endl;
256
257	  res.on_data([](const uint8_t *data, std::size_t len) {
258	    std::cerr.write(reinterpret_cast<const char *>(data), len);
259	    std::cerr << std::endl;
260	  });
261	});
262
263	req->on_close([&sess](uint32_t error_code) {
264	  // shutdown session after first request was done.
265	  sess.shutdown();
266	});
267      });
268
269      sess.on_error([](const boost::system::error_code &ec) {
270	std::cerr << "error: " << ec.message() << std::endl;
271      });
272
273      io_service.run();
274    }
275
276``nghttp2::asio_http2::client::session`` object takes
277``boost::asio::io_service`` object and remote server address.  When
278connection is made, the callback function passed to
279``nghttp2::asio_http2::client::on_connect`` is invoked with connected
280address as its parameter.  After this callback call, use
281``nghttp2::asio_http2::session::submit`` to send request to the
282server.  You can submit multiple requests at once without waiting for
283the completion of previous request.
284
285The life time of req and res object ends after the callback set by
286``nghttp2::asio_http2::server::request::on_close`` function.
287Application must not use those objects after this call.
288
289Normally, client does not stop even after all requests are done unless
290connection is lost.  To stop client, call
291``nghttp2::asio_http2::server::session::shutdown()``.
292
293Receive server push and enable SSL/TLS
294++++++++++++++++++++++++++++++++++++++
295
296.. code-block:: cpp
297
298    #include <iostream>
299
300    #include <nghttp2/asio_http2_client.h>
301
302    using boost::asio::ip::tcp;
303
304    using namespace nghttp2::asio_http2;
305    using namespace nghttp2::asio_http2::client;
306
307    int main(int argc, char *argv[]) {
308      boost::system::error_code ec;
309      boost::asio::io_service io_service;
310
311      boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
312      tls.set_default_verify_paths();
313      // disabled to make development easier...
314      // tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer);
315      configure_tls_context(ec, tls);
316
317      // connect to localhost:3000
318      session sess(io_service, tls, "localhost", "3000");
319
320      sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
321	boost::system::error_code ec;
322
323	auto req = sess.submit(ec, "GET", "http://localhost:3000/");
324
325	req->on_response([&sess](const response &res) {
326	  std::cerr << "response received!" << std::endl;
327	  res.on_data([&sess](const uint8_t *data, std::size_t len) {
328	    std::cerr.write(reinterpret_cast<const char *>(data), len);
329	    std::cerr << std::endl;
330	  });
331	});
332
333	req->on_push([](const request &push) {
334	  std::cerr << "push request received!" << std::endl;
335	  push.on_response([](const response &res) {
336	    std::cerr << "push response received!" << std::endl;
337	    res.on_data([](const uint8_t *data, std::size_t len) {
338	      std::cerr.write(reinterpret_cast<const char *>(data), len);
339	      std::cerr << std::endl;
340	    });
341	  });
342	});
343      });
344
345      sess.on_error([](const boost::system::error_code &ec) {
346	std::cerr << "error: " << ec.message() << std::endl;
347      });
348
349      io_service.run();
350    }
351
352The above sample code demonstrates how to enable SSL/TLS and receive
353server push.  Currently,
354``nghttp2::asio_http2::client::configure_tls_context`` function setups
355NPN callbacks for SSL/TLS context for HTTP/2 use.
356
357To receive server push, use
358``nghttp2::asio_http2::client::request::on_push`` function to set
359callback function which is invoked when server push request is
360arrived.  The callback function takes
361``nghttp2::asio_http2::client::request`` object, which contains the
362pushed request.  To get server push response, set callback using
363``nghttp2::asio_http2::client::request::on_response``.
364
365As stated in the previous section, client does not stop automatically
366as long as HTTP/2 session is fine and connection is alive.  We don't
367call ``nghttp2::asio_http2::client::session::shutdown`` in this
368example, so the program does not terminate after all responses are
369received.  Hit Ctrl-C to terminate the program.
370
371Multiple concurrent requests
372++++++++++++++++++++++++++++
373
374.. code-block:: cpp
375
376    #include <iostream>
377
378    #include <nghttp2/asio_http2_client.h>
379
380    using boost::asio::ip::tcp;
381
382    using namespace nghttp2::asio_http2;
383    using namespace nghttp2::asio_http2::client;
384
385    int main(int argc, char *argv[]) {
386      boost::system::error_code ec;
387      boost::asio::io_service io_service;
388
389      // connect to localhost:3000
390      session sess(io_service, "localhost", "3000");
391
392      sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
393	boost::system::error_code ec;
394
395	auto printer = [](const response &res) {
396	  res.on_data([](const uint8_t *data, std::size_t len) {
397	    std::cerr.write(reinterpret_cast<const char *>(data), len);
398	    std::cerr << std::endl;
399	  });
400	};
401
402	std::size_t num = 3;
403	auto count = std::make_shared<int>(num);
404
405	for (std::size_t i = 0; i < num; ++i) {
406	  auto req = sess.submit(ec, "GET",
407				 "http://localhost:3000/" + std::to_string(i + 1));
408
409	  req->on_response(printer);
410	  req->on_close([&sess, count](uint32_t error_code) {
411	    if (--*count == 0) {
412	      // shutdown session after |num| requests were done.
413	      sess.shutdown();
414	    }
415	  });
416	}
417      });
418
419      sess.on_error([](const boost::system::error_code &ec) {
420	std::cerr << "error: " << ec.message() << std::endl;
421      });
422
423      io_service.run();
424    }
425
426Here is the sample to send 3 requests at once.  Depending on the
427server settings, these requests are processed out-of-order.  In this
428example, we have a trick to shutdown session after all requests were
429done.  We made ``count`` object which is shared pointer to int and is
430initialized to 3.  On each request closure (the invocation of the
431callback set by ``nghttp2::asio_http2::client::request::on_close``),
432we decrement the count.  If count becomes 0, we are sure that all
433requests have been done and initiate shutdown.
434