JayBeams  0.1
Another project to have fun coding.
request_dispatcher.cpp
Go to the documentation of this file.
2 #include <jb/log.hpp>
3 
4 #include <sstream>
5 
6 namespace jb {
7 namespace ehs {
8 
9 request_dispatcher::request_dispatcher(std::string const& server_name)
10  : mu_()
11  , handlers_()
12  , server_name_(server_name)
13  , open_connection_(0)
14  , close_connection_(0)
15  , read_ok_(0)
16  , read_error_(0)
17  , write_invalid_(0)
18  , write_100_(0)
19  , write_200_(0)
20  , write_300_(0)
21  , write_400_(0)
22  , write_500_(0)
23  , write_ok_(0)
24  , write_error_(0)
25  , accept_ok_(0)
26  , accept_error_(0)
27  , accept_closed_(0) {
28 #ifndef ATOMIC_LONG_LOCK_FREE
29 #error "Missing ATOMIC_LONG_LOCK_FREE required by C++11 standard"
30 #endif // ATOMIC_LONG_LOCK_FREE
31  static_assert(
32  ATOMIC_LONG_LOCK_FREE == 2, "Class requires lock-free std::atomic<long>");
33 }
34 
36  std::string const& path, request_handler&& handler) {
37  std::lock_guard<std::mutex> guard(mu_);
38  auto inserted = handlers_.emplace(path, std::move(handler));
39  if (not inserted.second) {
40  throw std::runtime_error(std::string("duplicate handler path: ") + path);
41  }
42 }
43 
45  auto found = find_handler(req.target());
46  if (not found.second) {
47  return not_found(req);
48  }
49  response_type res;
50  res.result(beast::http::status::ok);
51  res.version = req.version;
52  res.insert("server", server_name_);
53  found.first(req, res);
55  return res;
56 } catch (std::exception const& e) {
57  // ... if there is an exception preparing the response we try to
58  // send back at least a 500 error ...
59  JB_LOG(info) << "std::exception raised while sending response: " << e.what();
60  return internal_error(req);
61 }
62 
64  std::ostringstream os;
65  os << "# HELP open_connection The number of HTTP connections opened\n"
66  << "# TYPE open_connection counter\n"
67  << "open_connection " << get_open_connection() << "\n"
68  << "\n"
69  << "# HELP close_connection The number of HTTP connections closed\n"
70  << "# TYPE close_connection counter\n"
71  << "close_connection " << get_close_connection() << "\n"
72  << "\n"
73  << "# HELP read_ok The number of HTTP request received successfully\n"
74  << "# TYPE read_ok counter\n"
75  << "read_ok " << get_read_ok() << "\n"
76  << "\n"
77  << "# HELP read_error The number of errors reading HTTP requests\n"
78  << "# TYPE read_error counter\n"
79  << "read_error " << get_read_error() << "\n"
80  << "\n"
81  << "# HELP response_by_code_range "
82  << "The number of HTTP responses within each response code range\n"
83  << "# TYPE response_by_code_range\n"
84  << "response_by_code_range{range=\"invalid\"} " << get_write_invalid()
85  << "\n"
86  << "response_by_code_range{range=\"100\"} " << get_write_100() << "\n"
87  << "response_by_code_range{range=\"200\"} " << get_write_200() << "\n"
88  << "response_by_code_range{range=\"300\"} " << get_write_300() << "\n"
89  << "response_by_code_range{range=\"400\"} " << get_write_400() << "\n"
90  << "response_by_code_range{range=\"500\"} " << get_write_500() << "\n"
91  << "\n"
92  << "# HELP write_ok The number of HTTP responses received successfully\n"
93  << "# TYPE write_ok counter\n"
94  << "write_ok " << get_write_ok() << "\n\n"
95  << "# HELP write_error The number of errors writing HTTP responses\n"
96  << "# TYPE write_error counter\n"
97  << "write_error " << get_write_error() << "\n"
98  << "\n"
99  << "# HELP accept_ok The number of HTTP connections accepted\n"
100  << "# TYPE accept_ok counter\n"
101  << "accept_ok " << get_accept_ok() << "\n\n"
102  << "# HELP accept_error The number of errors accepting HTTP connections\n"
103  << "# TYPE accept_error counter\n"
104  << "accept_error " << get_accept_error() << "\n"
105  << "# HELP accept_closed The number accept() attempts on a closed "
106  "acceptor\n"
107  << "# TYPE accept_closed counter\n"
108  << "accept_closed " << get_accept_closed() << "\n"
109  << "\n";
110  res.body += os.str();
111 }
112 
114  response_type res;
115  res.result(beast::http::status::internal_server_error);
116  res.version = req.version;
117  res.insert("server", server_name_);
118  res.insert("content-type", "text/plain");
119  res.body = std::string{"An internal error occurred"};
121  return res;
122 }
123 
125  response_type res;
126  res.result(beast::http::status::not_found);
127  res.version = req.version;
128  res.insert("server", server_name_);
129  res.insert("content-type", "text/plain");
130  res.body =
131  std::string("path: ") + std::string(req.target()) + " not found\r\n";
133  return res;
134 }
135 
136 std::pair<request_handler, bool>
137 request_dispatcher::find_handler(beast::string_view path) const {
138  std::lock_guard<std::mutex> guard(mu_);
139  auto location = handlers_.find(std::string(path));
140  if (location == handlers_.end()) {
141  return std::make_pair(request_handler(), false);
142  }
143  return std::make_pair(location->second, true);
144 }
145 
147  // TODO(coryan) - this sound become a map of counter (or is that a
148  // counter map?) when we create counter classes for prometheus.io
149  using sc = beast::http::status_class;
150  switch (beast::http::to_status_class(res.result())) {
151  case sc::unknown:
153  break;
154  case sc::informational:
155  count_write_100();
156  break;
157  case sc::successful:
158  count_write_200();
159  break;
160  case sc::redirection:
161  count_write_300();
162  break;
163  case sc::client_error:
164  count_write_400();
165  break;
166  case sc::server_error:
167  count_write_500();
168  break;
169  }
170 }
171 
172 } // namespace ehs
173 } // namespace jb
long get_open_connection() const
Returns the count of open connections.
response_type not_found(request_type const &request)
Create a 404 response.
std::map< std::string, request_handler > handlers_
The collection of handlers.
beast::http::request< beast::http::string_body > request_type
The request type used for JayBeams Embedded HTTP Servers.
Definition: base_types.hpp:17
long get_read_ok() const
Get the count of successful reads.
void count_write_100()
Count a write in the 100 range.
long get_close_connection() const
Get the count of close connections.
void append_metrics(response_type &res) const
Append the internal metrics to the body of res.
void count_write_invalid()
Internally updated event counters.
long get_write_400() const
Get the count write in the 400 range.
beast::http::response< beast::http::string_body > response_type
The response type used for JayBeams Embedded HTTP Servers.
Definition: base_types.hpp:20
request_dispatcher(std::string const &server_name)
Create a new empty dispatcher.
void count_write_500()
Count a write in the 500 range.
response_type internal_error(request_type const &request)
Create a 500 response.
std::mutex mu_
Protect the critical sections.
long get_write_invalid() const
Get count of responses with invalid codes (outside the [100,600) range).
std::function< void(request_type const &, response_type &)> request_handler
Define the interface for request handlers.
long get_write_100() const
Get the count write in the 100 range.
std::string server_name_
The name of the server returned in all HTTP responses.
void count_write_200()
Count a write in the 200 range.
long get_write_300() const
Get the count write in the 300 range.
long get_write_500() const
Get the count write in the 500 range.
long get_read_error() const
Get the count write errors.
void count_write_400()
Count a write in the 400 range.
long get_write_200() const
Get the count write in the 200 range.
void update_response_counter(response_type const &res)
Update the response code counters based on res.
long get_write_error() const
Get the count write errors.
#define JB_LOG(lvl)
Definition: log.hpp:70
response_type process(request_type const &request)
Process a new request using the right handler.
std::pair< request_handler, bool > find_handler(beast::string_view path) const
Find the request handler for the given path.
long get_write_ok() const
Get the count write successes.
void count_write_300()
Count a write in the 300 range.
The top-level namespace for the JayBeams library.
Definition: as_hhmmss.hpp:7
void add_handler(std::string const &path, request_handler &&handler)
Add a new handler.