Line data Source code
1 : // Copyright (c) 2011-2022 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/edhttp
4 : // contact@m2osw.com
5 : //
6 : // This program is free software: you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation, either version 3 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License
17 : // along with this program. If not, see <https://www.gnu.org/licenses/>.
18 :
19 :
20 : // self
21 : //
22 : #include "edhttp/health.h"
23 :
24 : #include "edhttp/http_client_server.h"
25 :
26 :
27 : // eventdispatcher
28 : //
29 : #include <eventdispatcher/communicator.h>
30 : #include <eventdispatcher/tcp_server_connection.h>
31 : #include <eventdispatcher/tcp_server_client_message_connection.h>
32 :
33 :
34 : // snaplogger
35 : //
36 : #include <snaplogger/map_diagnostic.h>
37 :
38 :
39 : // libaddr
40 : //
41 : #include <libaddr/addr_parser.h>
42 :
43 :
44 : // last include
45 : //
46 : #include <snapdev/poison.h>
47 :
48 :
49 :
50 : namespace edhttp
51 : {
52 :
53 :
54 : namespace
55 : {
56 :
57 :
58 :
59 : /** \brief The command line options that the health object supports.
60 : *
61 : * This variable holds the list of options that the health object can be
62 : * given. If not specified, then no health object is created (there is
63 : * not default). [TBD: we could let the programmer set defaults.]
64 : *
65 : * The options are:
66 : *
67 : * * `--health-certificate` -- the certificate (optional)
68 : * * `--health-listen` -- the IP address to listen
69 : * * `--health-private-key` -- the private key (optional)
70 : *
71 : * Note that the `--health-listen` option is also optional. If not specified,
72 : * then no health server is created and other processes will not have access
73 : * to the health of the service.
74 : */
75 : advgetopt::option const g_options[] =
76 : {
77 : // HEALTH LISTEN
78 : //
79 : advgetopt::define_option(
80 : advgetopt::Name("health-listen")
81 : , advgetopt::Flags(advgetopt::all_flags<
82 : advgetopt::GETOPT_FLAG_GROUP_OPTIONS
83 : , advgetopt::GETOPT_FLAG_REQUIRED>())
84 : , advgetopt::Help("the IP and port to listen on for health messages.")
85 : ),
86 : advgetopt::define_option(
87 : advgetopt::Name("health-certificate")
88 : , advgetopt::Flags(advgetopt::all_flags<
89 : advgetopt::GETOPT_FLAG_GROUP_OPTIONS
90 : , advgetopt::GETOPT_FLAG_REQUIRED>())
91 : , advgetopt::Help("certificate for --health-listen connection.")
92 : ),
93 : advgetopt::define_option(
94 : advgetopt::Name("health-private-key")
95 : , advgetopt::Flags(advgetopt::all_flags<
96 : advgetopt::GETOPT_FLAG_GROUP_OPTIONS
97 : , advgetopt::GETOPT_FLAG_REQUIRED>())
98 : , advgetopt::Help("private key for --health-listen connection.")
99 : ),
100 :
101 : // END
102 : //
103 : advgetopt::end_options()
104 : };
105 :
106 :
107 :
108 : // TODO: this needs to be a the HTTP server (which we do not quite have yet)
109 0 : class health_server_connection
110 : : public ed::tcp_server_connection
111 : {
112 : public:
113 : typedef std::shared_ptr<health_server_connection> pointer_t;
114 :
115 : health_server_connection(
116 : addr::addr const & addr
117 : , std::string const & certificate
118 : , std::string const & private_key
119 : , ed::mode_t mode = ed::mode_t::MODE_PLAIN
120 : , int max_connections = -1
121 : , bool reuse_addr = false);
122 :
123 : void set_status(std::string const & status);
124 : std::string get_status() const;
125 :
126 : // tcp_server_connection implementation
127 : virtual void process_accept();
128 :
129 : private:
130 : std::string f_status = std::string();
131 : };
132 :
133 :
134 0 : class health_client_connection
135 : : public ed::tcp_server_client_message_connection
136 : {
137 : public:
138 : health_client_connection(
139 : health_server_connection * server
140 : , ed::tcp_bio_client::pointer_t client);
141 :
142 : health_client_connection(health_client_connection const &) = delete;
143 : health_client_connection & operator = (health_client_connection const &) = delete;
144 :
145 : private:
146 : health_server_connection * f_server = nullptr;
147 : };
148 :
149 :
150 0 : health_server_connection::health_server_connection(
151 : addr::addr const & addr
152 : , std::string const & certificate
153 : , std::string const & private_key
154 : , ed::mode_t mode
155 : , int max_connections
156 0 : , bool reuse_addr)
157 : : tcp_server_connection(
158 : addr
159 : , certificate
160 : , private_key
161 : , mode
162 : , max_connections
163 0 : , reuse_addr)
164 : {
165 : // do a set_status() so that way we get the set_diagnostic() for "free"
166 : //
167 0 : set_status(HEALTH_STARTING);
168 0 : }
169 :
170 :
171 0 : void health_server_connection::set_status(std::string const & status)
172 : {
173 0 : f_status = status;
174 :
175 0 : snaplogger::set_diagnostic(DIAG_KEY_HEALTH, status);
176 0 : }
177 :
178 :
179 0 : std::string health_server_connection::get_status() const
180 : {
181 0 : return f_status;
182 : }
183 :
184 :
185 0 : void health_server_connection::process_accept()
186 : {
187 0 : ed::tcp_bio_client::pointer_t const new_client(accept());
188 0 : if(new_client == nullptr)
189 : {
190 : // an error occurred, report in the logs
191 0 : int const e(errno);
192 0 : SNAP_LOG_ERROR
193 0 : << "somehow accept() failed with errno: "
194 : << e
195 : << " -- "
196 0 : << strerror(e)
197 : << SNAP_LOG_SEND;
198 0 : return;
199 : }
200 :
201 0 : health_client_connection::pointer_t client(std::make_shared<health_client_connection>(this, new_client));
202 0 : if(!ed::communicator::instance()->add_connection(client))
203 : {
204 0 : SNAP_LOG_ERROR
205 : << "adding a health client connection to the list of connections failed."
206 : << SNAP_LOG_SEND;
207 0 : return;
208 : }
209 : }
210 :
211 :
212 :
213 :
214 :
215 0 : health_client_connection::health_client_connection(
216 : health_server_connection * server
217 0 : , ed::tcp_bio_client::pointer_t client)
218 : : tcp_server_client_message_connection(client)
219 0 : , f_server(server)
220 : {
221 0 : }
222 :
223 :
224 :
225 2 : health_server_connection::pointer_t g_health_connection;
226 :
227 :
228 :
229 : } // no name namespace
230 :
231 :
232 0 : void add_health_options(advgetopt::getopt & opts)
233 : {
234 0 : opts.parse_options_info(g_options, true);
235 0 : }
236 :
237 :
238 0 : bool process_health_options(advgetopt::getopt & opts)
239 : {
240 0 : if(!opts.is_defined("health-listen"))
241 : {
242 0 : return true;
243 : }
244 :
245 0 : std::string const address(opts.get_string("health-listen"));
246 :
247 0 : std::string certificate;
248 0 : std::string private_key;
249 :
250 0 : ed::mode_t mode(ed::mode_t::MODE_PLAIN);
251 0 : if(opts.is_defined("health-certificate")
252 0 : && opts.is_defined("health-private-key"))
253 : {
254 0 : certificate = opts.get_string("health-certificate");
255 0 : private_key = opts.get_string("health-private-key");
256 :
257 0 : if(certificate.empty()
258 0 : || private_key.empty())
259 : {
260 0 : SNAP_LOG_ERROR
261 : << "--health-certificate and --health-private-key must both be defined."
262 : << SNAP_LOG_SEND;
263 0 : return false;
264 : }
265 :
266 0 : mode = ed::mode_t::MODE_ALWAYS_SECURE;
267 : }
268 :
269 0 : addr::addr_parser p;
270 0 : p.set_protocol(IPPROTO_TCP);
271 0 : addr::addr_range::vector_t const vec(p.parse(address));
272 0 : if(p.has_errors())
273 : {
274 0 : SNAP_LOG_ERROR
275 : << "--health-listen was passed an invalid address ("
276 : << address
277 : << "): "
278 0 : << p.error_messages()
279 : << SNAP_LOG_SEND;
280 0 : return false;
281 : }
282 :
283 0 : if(vec.size() != 1
284 0 : || !vec[0].has_from()
285 0 : || vec[0].has_to())
286 : {
287 0 : SNAP_LOG_ERROR
288 : << "--health-listen was somehow passed multiple addresses ("
289 : << vec
290 : << "): "
291 0 : << p.error_messages()
292 : << SNAP_LOG_SEND;
293 0 : return false;
294 : }
295 :
296 0 : g_health_connection = std::make_shared<health_server_connection>(
297 0 : vec[0].get_from()
298 : , certificate
299 : , private_key
300 : , mode
301 : , 5 // max. connections
302 : , true); // reuse_addr
303 :
304 0 : if(!ed::communicator::instance()->add_connection(g_health_connection))
305 : {
306 0 : SNAP_LOG_ERROR
307 : << "adding the health connection to the list of connections failed."
308 : << SNAP_LOG_SEND;
309 0 : return false;
310 : }
311 :
312 0 : return true;
313 : }
314 :
315 :
316 0 : void set_status(std::string const & status)
317 : {
318 0 : if(g_health_connection != nullptr)
319 : {
320 0 : g_health_connection->set_status(status);
321 : }
322 0 : }
323 :
324 :
325 0 : std::string get_status()
326 : {
327 0 : if(g_health_connection != nullptr)
328 : {
329 0 : return g_health_connection->get_status();
330 : }
331 :
332 0 : return std::string();
333 : }
334 :
335 :
336 :
337 6 : } // namespace edhttp
338 : // vim: ts=4 sw=4 et
|