Event driven http(s) API server library -in C++
Table of Contents
Background
Abstract
is2 is a reasonably performant event driven http(s) API server library written in C++ with support for url routing, tls (with openssl), serving static files, proxying, and subrequests.
Supports
- threading
- r3 like url routing with support for slugs
- URL handler registration inspired by flask blueprints
- tls with openssl
- handler support for files, proxies, and subrequests.
Architecture Overview
is2starts with aserverobject.listenerobjects -listening on a particular port with a given scheme (TCP/TLS) can be registered with aserverobject.handlerobjects can be routed to thelistenerobjects at specified urls.
A basic example of instantation/registration:
// create a TCP listener on port 12345 ns_is2::lsnr *lsnr = new ns_is2::lsnr(12345, ns_is2::SCHEME_TCP); // create request handler --based on ns_is2::default_rqst_h ns_is2::rqst_h *rqst_h = new request_handler(); // specify route to request handler on the listener lsnr->add_route("/path1/path2", rqst_h); // create a server object ns_is2::srvr *srvr = new ns_is2::srvr(); // register listener with server object srvr->register_lsnr(lsnr);
The Reactor
is2 has an event system similar to libevent or libuv. The reactor is a std::priority_queue of timer events with inverted ordering, for dequeue'ing the next nearest timeout. The "next nearest timeout" is used for the subsequent call to wait for events (epoll_wait, select, kqueue).
Reactor loop psuedo code:
while true: // dequeue / handle timer events until timer > now do last_timer = timer_queue.dequeue while(last_timer < now) // wait for an event -up to the next timer event events = wait_event(last_timer) // handle events if any for each event handle (readable/writeable/etc)
Install
Build requirements
Packages
- openssl
- pthread
Building example
g++ ./test.cc -lis2 -lpthread -o testUsage
Basic example
Code
#include <is2/srvr/srvr.h> #include <is2/srvr/lsnr.h> #include <is2/srvr/default_rqst_h.h> #include <is2/srvr/api_resp.h> // define handler class base_handler: public ns_is2::default_rqst_h { public: // GET ns_is2::h_resp_t do_get(ns_is2::session &a_session, ns_is2::rqst &a_rqst, const ns_is2::url_pmap_t &a_url_pmap) { // send json response return send_json_resp(a_session, true, ns_is2::HTTP_STATUS_OK, "{\"msg\": \"Hello World\"}"); } }; int main(void) { // create server ns_is2::srvr *l_srvr = new ns_is2::srvr(); // set server name -for server response l_srvr->set_server_name("hello world server"); // create listener ns_is2::lsnr *l_lsnr = new ns_is2::lsnr(12345, ns_is2::SCHEME_TCP); // create handler ns_is2::rqst_h *l_rqst_h = new base_handler(); // add route to listener l_lsnr->add_route("/hello", l_rqst_h); // register listener with server l_srvr->register_lsnr(l_lsnr); // num_threads == 0 means run single thread in foreground l_srvr->set_num_threads(0); l_srvr->run(); // cleanup if(l_srvr) {delete l_srvr; l_srvr = NULL;} if(l_rqst_h) {delete l_rqst_h; l_rqst_h = NULL;} return 0; }
Running
Testing
>curl 'http://127.0.0.1:12345/hello' -v * Trying 127.0.0.1... * Connected to 127.0.0.1 (127.0.0.1) port 12345 (#0) > GET /hello HTTP/1.1 > Host: 127.0.0.1:12345 > User-Agent: curl/7.47.0 > Accept: */* > < HTTP/1.1 200 OK < Connection: keep-alive < Content-Length: 22 < Content-type: application/json < Date: Thu, 15 Feb 2018 00:42:40 GMT < Server: hello world server < * Connection #0 to host 127.0.0.1 left intact {"msg": "Hello World"}
Performance with hurl
>hurl 'http://127.0.0.1:12345/hello' -t1 -p100 -l5 Running 1 threads 100 parallel connections per thread with infinite requests per connection +-----------/-----------+-----------+-----------+--------------+-----------+-------------+-----------+ | Completed / Requested | IdlKil | Errors | kBytes Recvd | Elapsed | Req/s | MB/s | +-----------/-----------+-----------+-----------+--------------+-----------+-------------+-----------+ | 58456 / 58556 | 0 | 0 | 11064.94 | 1.00s | 62255.49s | 10.81s | | 89685 / 89785 | 0 | 0 | 11100.93 | 1.50s | 62458.00s | 10.84s | | 120803 / 120903 | 0 | 0 | 11061.48 | 2.00s | 62236.00s | 10.80s | | 151836 / 151936 | 0 | 0 | 11031.26 | 2.50s | 62066.00s | 10.77s | | 182631 / 182731 | 0 | 0 | 10946.66 | 3.00s | 61590.00s | 10.69s | | 213663 / 213763 | 0 | 0 | 11030.91 | 3.50s | 62064.00s | 10.77s | | 244744 / 244844 | 0 | 0 | 11048.32 | 4.00s | 62162.00s | 10.79s | | 275814 / 275914 | 0 | 0 | 11044.41 | 4.50s | 62140.00s | 10.79s | | 306890 / 306990 | 0 | 0 | 11046.55 | 5.00s | 62152.00s | 10.79s | | RESULTS: ALL | fetches: 306890 | max parallel: 100 | bytes: 7.949221e+07 | seconds: 5.001000 | mean bytes/conn: 259.025090 | fetches/sec: 61365.726855 | bytes/sec: 1.589526e+07 | HTTP response codes: | 200 -- 306890
Contribute
- We welcome issues, questions and pull requests.
License
This project is licensed under the terms of the Apache 2.0 open source license. Please refer to the LICENSE-2.0.txt file for the full terms.