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