Line data Source code
1 : // Copyright (c) 2012-2019 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/eventdispatcher
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 2 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, write to the Free Software
18 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 :
20 : /** \file
21 : * \brief Implementation of the Snap Communicator class.
22 : *
23 : * This class wraps the C poll() interface in a C++ object with many types
24 : * of objects:
25 : *
26 : * \li Server Connections; for software that want to offer a port to
27 : * which clients can connect to; the server will call accept()
28 : * once a new client connection is ready; this results in a
29 : * Server/Client connection object
30 : * \li Client Connections; for software that want to connect to
31 : * a server; these expect the IP address and port to connect to
32 : * \li Server/Client Connections; for the server when it accepts a new
33 : * connection; in this case the server gets a socket from accept()
34 : * and creates one of these objects to handle the connection
35 : *
36 : * Using the poll() function is the easiest and allows us to listen
37 : * on pretty much any number of sockets (on my server it is limited
38 : * at 16,768 and frankly over 1,000 we probably will start to have
39 : * real slowness issues on small VPN servers.)
40 : */
41 :
42 :
43 : // self
44 : //
45 : #include "eventdispatcher/message.h"
46 :
47 : #include "eventdispatcher/exception.h"
48 :
49 :
50 : // snaplogger lib
51 : //
52 : #include <snaplogger/message.h>
53 :
54 :
55 : // snapdev lib
56 : //
57 : #include <snapdev/string_replace_many.h>
58 :
59 :
60 : // boost lib
61 : //
62 : #include <boost/algorithm/string.hpp>
63 :
64 :
65 : // last include
66 : //
67 : #include <snapdev/poison.h>
68 :
69 :
70 :
71 : namespace ed
72 : {
73 :
74 :
75 :
76 : /** \brief Parse a message from the specified paremeter.
77 : *
78 : * This function transformed the input string in a set of message
79 : * fields.
80 : *
81 : * The message format supported is:
82 : *
83 : * \code
84 : * ( '<' sent-from-server ':' sent-from-service ' ')? ( ( server ':' )? service '/' )? command ' ' ( parameter_name '=' value ';' )*
85 : * \endcode
86 : *
87 : * The sender "<sent-from-server:sent-from-service" names are added by
88 : * snapcommunicator when it receives a message which is destined for
89 : * another service (i.e. not itself). This can be used by the receiver
90 : * to reply back to the exact same process if it is a requirement for that
91 : * message (i.e. a process that sends a LOCK message, for example,
92 : * expects to receive the LOCKED message back as an answer.) Note that
93 : * it is assumed that there cannot be more than one service named
94 : * 'service' per server. This is enforced by the snapcommunicator
95 : * REGISTER function.
96 : *
97 : * \code
98 : * // to reply to the exact message sender, one can use the
99 : * // following two lines of code:
100 : * //
101 : * reply.set_server(message.get_sent_from_server());
102 : * reply.set_service(message.get_sent_from_service());
103 : *
104 : * // or use the reply_to() helper function
105 : * //
106 : * reply.reply_to(message);
107 : * \endcode
108 : *
109 : * The space after the command cannot be there unless parameters follow.
110 : * Parameters must be separated by semi-colons.
111 : *
112 : * The value of a parameter gets quoted when it includes a ';'. Within
113 : * the quotes, a Double Quote can be escaped inside by adding a backslash
114 : * in front of it (\"). Newline characters (as well as return carriage)
115 : * are also escaped using \n and \r respectively. Finally, we have to
116 : * escape backslashes themselves by doubling them, so \ becomes \\.
117 : *
118 : * Note that only parameter values support absolutely any character.
119 : * All the other parameters are limited to the latin alphabet, digits,
120 : * and underscores ([A-Za-z0-9_]+). Also all commands are limited
121 : * to uppercase letters only.
122 : *
123 : * \note
124 : * The input message is not saved as a cached version of the message
125 : * because we assume it may not be 100% optimized (canonicalized.)
126 : *
127 : * \param[in] message The string to convert into fields in this
128 : * message object.
129 : *
130 : * \return true if the message was succesfully parsed; false when an
131 : * error occurs and in that case no fields get modified.
132 : *
133 : * \sa to_message()
134 : * \sa get_sent_from_server()
135 : * \sa get_sent_from_service()
136 : * \sa reply_to()
137 : */
138 0 : bool message::from_message(std::string const & original_message)
139 : {
140 0 : std::string sent_from_server;
141 0 : std::string sent_from_service;
142 0 : std::string server;
143 0 : std::string service;
144 0 : std::string command;
145 0 : parameters_t parameters;
146 :
147 : // someone using telnet to test sending messages will include a '\r'
148 : // so run a trim on the message in case it is there
149 : //
150 0 : std::string msg(original_message);
151 0 : boost::trim(msg);
152 0 : char const * m(msg.c_str());
153 :
154 : // sent-from indicated?
155 : //
156 0 : if(*m != '\0' && *m == '<')
157 : {
158 : // the name of the server and server sending this message
159 : //
160 : // First ++m to skip the '<'
161 : //
162 0 : for(++m; *m != '\0' && *m != ':'; ++m)
163 : {
164 0 : if(*m == ' ')
165 : {
166 : // invalid syntax from input message
167 : //
168 : SNAP_LOG_ERROR
169 0 : << "a message with sent_from_server must not include a space in the server name ("
170 0 : << original_message
171 0 : << ").";
172 0 : return false;
173 : }
174 :
175 0 : sent_from_server += *m;
176 : }
177 0 : if(*m != '\0')
178 : {
179 : // First ++m to skip the ':'
180 0 : for(++m; *m != '\0' && *m != ' '; ++m)
181 : {
182 0 : sent_from_service += *m;
183 : }
184 : }
185 0 : if(*m == '\0')
186 : {
187 : // invalid syntax from input message
188 : //
189 : SNAP_LOG_ERROR
190 0 : << "a message cannot only include a 'sent from service' definition.";
191 0 : return false;
192 : }
193 : // Skip the ' '
194 0 : ++m;
195 : }
196 :
197 0 : bool has_server(false);
198 0 : bool has_service(false);
199 0 : for(; *m != '\0' && *m != ' '; ++m)
200 : {
201 0 : if(*m == ':')
202 : {
203 0 : if(has_server
204 0 : || has_service
205 0 : || command.empty())
206 : {
207 : // we cannot have more than one ':'
208 : // and the name cannot be empty if ':' is used
209 : // we also cannot have a ':' after the '/'
210 : //
211 : SNAP_LOG_ERROR
212 0 : << "a server name cannot be empty when specified, also it cannot include two server names and a server name after a service name was specified.";
213 0 : return false;
214 : }
215 0 : has_server = true;
216 0 : server = command;
217 0 : command.clear();
218 : }
219 0 : else if(*m == '/')
220 : {
221 0 : if(has_service
222 0 : || command.empty())
223 : {
224 : // we cannot have more than one '/'
225 : // and the name cannot be empty if '/' is used
226 : //
227 : SNAP_LOG_ERROR
228 0 : << "a service name is mandatory when the message includes a slash (/), also it cannot include two service names.";
229 0 : return false;
230 : }
231 0 : has_service = true;
232 0 : service = command;
233 0 : command.clear();
234 : }
235 : else
236 : {
237 0 : command += *m;
238 : }
239 : }
240 :
241 0 : if(command.empty())
242 : {
243 : // command is mandatory
244 : //
245 : SNAP_LOG_ERROR
246 0 : << "a command is mandatory in a message.";
247 0 : return false;
248 : }
249 :
250 : // if we have a space, we expect one or more parameters
251 : //
252 0 : if(*m == ' ')
253 : {
254 0 : for(++m; *m != '\0';)
255 : {
256 : // first we have to read the parameter name (up to the '=')
257 : //
258 0 : std::string param_name;
259 0 : for(; *m != '\0' && *m != '='; ++m)
260 : {
261 0 : param_name += *m;
262 : }
263 0 : if(param_name.empty())
264 : {
265 : // parameters must have a name
266 : //
267 : SNAP_LOG_ERROR
268 0 : << "could not accept message because an empty parameter name is not valid.";
269 0 : return false;
270 : }
271 : try
272 : {
273 0 : verify_message_name(param_name);
274 : }
275 0 : catch(event_dispatcher_invalid_message const & e)
276 : {
277 : // name is not empty, but it has invalid characters in it
278 : //
279 : SNAP_LOG_ERROR
280 0 : << "could not accept message because parameter name \""
281 0 : << param_name
282 0 : << "\" is not considered valid: "
283 0 : << e.what();
284 0 : return false;
285 : }
286 :
287 0 : if(*m == '\0'
288 0 : || *m != '=')
289 : {
290 : // ?!?
291 : //
292 : SNAP_LOG_ERROR
293 0 : << "message parameters must be followed by an equal (=) character.";
294 0 : return false;
295 : }
296 0 : ++m; // skip '='
297 :
298 : // retrieve the parameter name at first
299 : //
300 0 : std::string param_value;
301 0 : if(*m == '"')
302 : {
303 : // quoted parameter
304 : //
305 0 : for(++m; *m != '"'; ++m)
306 : {
307 0 : if(*m == '\0')
308 : {
309 : // closing quote (") is missing
310 : //
311 : SNAP_LOG_ERROR
312 0 : << "a quoted message parameter must end with a quote (\").";
313 0 : return false;
314 : }
315 :
316 : // restored escaped double quotes
317 : // (note that we do not yet restore other backslashed
318 : // characters, that's done below)
319 : //
320 0 : if(*m == '\\' && m[1] != '\0' && m[1] == '"')
321 : {
322 0 : ++m;
323 : }
324 : // here the character may be ';'
325 : //
326 0 : param_value += *m;
327 : }
328 :
329 : // skip the quote
330 : //
331 0 : ++m;
332 : }
333 : else
334 : {
335 : // parameter value is found as is
336 : //
337 0 : for(; *m != '\0' && *m != ';'; ++m)
338 : {
339 0 : param_value += *m;
340 : }
341 : }
342 :
343 0 : if(*m != '\0')
344 : {
345 0 : if(*m != ';')
346 : {
347 : // this should never happend
348 : //
349 : SNAP_LOG_ERROR
350 0 : << "two parameters must be separated by a semicolon (;).";
351 0 : return false;
352 : }
353 :
354 : // skip the ';'
355 : //
356 0 : ++m;
357 : }
358 :
359 : // also restore new lines and blackslashes if any
360 : //
361 : std::string const unsafe_value(snap::string_replace_many(
362 : param_value,
363 : {
364 : { "\\\\", "\\" },
365 : { "\\n", "\n" },
366 : { "\\r", "\r" }
367 0 : }));
368 :
369 : // we got a valid parameter, add it
370 : //
371 0 : parameters[param_name] = unsafe_value;
372 : }
373 : }
374 :
375 0 : f_sent_from_server = sent_from_server;
376 0 : f_sent_from_service = sent_from_service;
377 0 : f_server = server;
378 0 : f_service = service;
379 0 : f_command = command;
380 0 : f_parameters.swap(parameters);
381 0 : f_cached_message.clear();
382 :
383 0 : return true;
384 : }
385 :
386 :
387 : /** \brief Transform all the message parameters in a string.
388 : *
389 : * This function transforms all the message parameters in a string
390 : * and returns the result. The string is a message we can send over
391 : * TCP/IP (if you make sure to add a "\n", note that the
392 : * send_message() does that automatically) or over UDP/IP.
393 : *
394 : * \note
395 : * The function caches the result so calling the function many times
396 : * will return the same string and thus the function is very fast
397 : * after the first call (assuming you do not modify the message on
398 : * each call to to_message().)
399 : *
400 : * \note
401 : * The sent-from information gets saved in the message only if both,
402 : * the server name and service name it was sent from are defined.
403 : *
404 : * \exception snap_communicator_invalid_message
405 : * This function raises an exception if the message command was not
406 : * defined since a command is always mandatory.
407 : *
408 : * \return The converted message as a string.
409 : *
410 : * \sa get_sent_from_server()
411 : * \sa get_sent_from_service()
412 : * \sa set_reply_to_server()
413 : * \sa set_reply_to_service()
414 : */
415 0 : std::string message::to_message() const
416 : {
417 0 : if(f_cached_message.empty())
418 : {
419 0 : if(f_command.empty())
420 : {
421 0 : throw event_dispatcher_invalid_message("message::to_message(): cannot build a valid message without at least a command.");
422 : }
423 :
424 : // add info about the sender
425 : //
426 : // ['<' <sent-from-server> ':' <sent-from-service> ' ']
427 : //
428 0 : if(!f_sent_from_server.empty()
429 0 : || !f_sent_from_service.empty())
430 : {
431 0 : f_cached_message += '<';
432 0 : f_cached_message += f_sent_from_server;
433 0 : f_cached_message += ':';
434 0 : f_cached_message += f_sent_from_service;
435 0 : f_cached_message += ' ';
436 : }
437 :
438 : // add server and optionally the destination server name if both are defined
439 : //
440 : // ['<' <sent-from-server> ':' <sent-from-service> ' ']
441 : // [[<server> ':'] <name> '/']
442 : //
443 0 : if(!f_service.empty())
444 : {
445 0 : if(!f_server.empty())
446 : {
447 0 : f_cached_message += f_server;
448 0 : f_cached_message += ':';
449 : }
450 0 : f_cached_message += f_service;
451 0 : f_cached_message += '/';
452 : }
453 :
454 : // ['<' <sent-from-server> ':' <sent-from-service> ' ']
455 : // [[<server> ':'] <name> '/'] <command>
456 : //
457 0 : f_cached_message += f_command;
458 :
459 : // add parameters if any
460 : //
461 : // ['<' <sent-from-server> ':' <sent-from-service> ' ']
462 : // [[<server> ':'] <name> '/'] <command>
463 : // [' ' <param1> '=' <value1>][';' <param2> '=' <value2>]...
464 : //
465 0 : char sep(' ');
466 0 : for(auto p : f_parameters)
467 : {
468 0 : f_cached_message += sep;
469 0 : f_cached_message += p.first;
470 :
471 : std::string safe_value(snap::string_replace_many(
472 : p.second,
473 : {
474 : { "\\", "\\\\" },
475 : { "\n", "\\n" },
476 : { "\r", "\\r" }
477 0 : }));
478 :
479 0 : if(safe_value.find(';') != std::string::npos
480 0 : || (!safe_value.empty() && safe_value[0] == '\"'))
481 : {
482 : // escape the double quotes
483 : //
484 0 : boost::algorithm::replace_all(safe_value, "\"", "\\\"");
485 :
486 : // quote the resulting parameter and save in f_cached_message
487 : //
488 0 : f_cached_message += '"';
489 0 : f_cached_message += safe_value;
490 0 : f_cached_message += '"';
491 : }
492 : else
493 : {
494 : // no special handling necessary
495 : //
496 0 : f_cached_message += safe_value;
497 : }
498 :
499 0 : sep = ';';
500 : }
501 : }
502 :
503 0 : return f_cached_message;
504 : }
505 :
506 :
507 : /** \brief Where this message came from.
508 : *
509 : * Some services send a message expecting an answer directly sent back
510 : * to them. Yet, those services may have multiple instances in your cluster
511 : * (i.e. snapcommunicator runs on all computers, snapwatchdog, snapfirewall,
512 : * snaplock, snapdbproxy are likely to run on most computers, etc.)
513 : * This parameter defines which computer the message came from. Thus,
514 : * you can use that information to send the message back to that
515 : * specific computer. The snapcommunicator on that computer will
516 : * then forward the message to the specified service instance.
517 : *
518 : * If empty (the default,) then the normal snapcommunicator behavior is
519 : * used (i.e. send to any instance of the service that is available.)
520 : *
521 : * \return The address and port of the specific service this message has to
522 : * be sent to.
523 : *
524 : * \sa set_sent_from_server()
525 : * \sa set_sent_from_service()
526 : * \sa get_sent_from_service()
527 : */
528 5 : std::string const & message::get_sent_from_server() const
529 : {
530 5 : return f_sent_from_server;
531 : }
532 :
533 :
534 : /** \brief Set the name of the server that sent this message.
535 : *
536 : * This function saves the name of the server that was used to
537 : * generate the message. This can be used later to send a reply
538 : * to the service that sent this message.
539 : *
540 : * The snapcommunicator tool is actually in charge of setting this
541 : * parameter and you should never have to do so from your tool.
542 : * The set happens whenever the snapcommunicator receives a
543 : * message from a client. If you are not using the snapcommunicator
544 : * then you are welcome to use this function for your own needs.
545 : *
546 : * \param[in] sent_from_server The name of the source server.
547 : *
548 : * \sa get_sent_from_server()
549 : * \sa get_sent_from_service()
550 : * \sa set_sent_from_service()
551 : */
552 1 : void message::set_sent_from_server(std::string const & sent_from_server)
553 : {
554 1 : if(f_sent_from_server != sent_from_server)
555 : {
556 : // this name can be empty and it supports lowercase
557 : //
558 1 : verify_message_name(sent_from_server, true);
559 :
560 1 : f_sent_from_server = sent_from_server;
561 1 : f_cached_message.clear();
562 : }
563 1 : }
564 :
565 :
566 : /** \brief Who sent this message.
567 : *
568 : * Some services send messages expecting an answer sent right back to
569 : * them. For example, the snaplock tool sends the message LOCKENTERING
570 : * and expects the LOCKENTERED as a reply. The reply has to be sent
571 : * to the exact same instance t hat sent the LOCKENTERING message.
572 : *
573 : * In order to do so, the system makes use of the server and service
574 : * name the data was sent from. Since the name of each service
575 : * registering with snapcommunicator must be unique, it 100% defines
576 : * the sender of the that message.
577 : *
578 : * If empty (the default,) then the normal snapcommunicator behavior is
579 : * used (i.e. send to any instance of the service that is available locally,
580 : * if not available locally, try to send it to another snapcommunicator
581 : * that knows about it.)
582 : *
583 : * \return The address and port of the specific service this message has to
584 : * be sent to.
585 : *
586 : * \sa get_sent_from_server()
587 : * \sa set_sent_from_server()
588 : * \sa set_sent_from_service()
589 : */
590 5 : std::string const & message::get_sent_from_service() const
591 : {
592 5 : return f_sent_from_service;
593 : }
594 :
595 :
596 : /** \brief Set the name of the server that sent this message.
597 : *
598 : * This function saves the name of the service that sent this message
599 : * to snapcommuncator. It is set by snapcommunicator whenever it receives
600 : * a message from a service it manages so you do not have to specify this
601 : * parameter yourselves.
602 : *
603 : * This can be used to provide the name of the service to reply to. This
604 : * is useful when the receiver does not already know exactly who sends it
605 : * certain messages.
606 : *
607 : * \param[in] sent_from_service The name of the service that sent this message.
608 : *
609 : * \sa get_sent_from_server()
610 : * \sa set_sent_from_server()
611 : * \sa get_sent_from_service()
612 : */
613 1 : void message::set_sent_from_service(std::string const & sent_from_service)
614 : {
615 1 : if(f_sent_from_service != sent_from_service)
616 : {
617 : // this name can be empty and it supports lowercase
618 : //
619 1 : verify_message_name(sent_from_service, true);
620 :
621 1 : f_sent_from_service = sent_from_service;
622 1 : f_cached_message.clear();
623 : }
624 1 : }
625 :
626 :
627 : /** \brief The server where this message has to be delivered.
628 : *
629 : * Some services need their messages to be delivered to a service
630 : * running on a specific computer. This function returns the name
631 : * of that server.
632 : *
633 : * If the function returns an empty string, then snapcommunicator is
634 : * free to send the message to any server.
635 : *
636 : * \return The name of the server to send this message to or an empty string.
637 : *
638 : * \sa set_server()
639 : * \sa get_service()
640 : * \sa set_service()
641 : */
642 4 : std::string const & message::get_server() const
643 : {
644 4 : return f_server;
645 : }
646 :
647 :
648 : /** \brief Set the name of a specific server where to send this message.
649 : *
650 : * In some cases you may want to send a message to a service running
651 : * on a specific server. This function can be used to specify the exact
652 : * server where the message has to be delivered.
653 : *
654 : * This is particularly useful when you need to send a reply to a
655 : * specific daemon that sent you a message.
656 : *
657 : * The name can be set to ".", which means send to a local service
658 : * only, whether it is available or not. This option can be used
659 : * to avoid/prevent sending a message to other computers.
660 : *
661 : * The name can be set to "*", which is useful to broadcast the message
662 : * to all servers even if the destination service name is
663 : * "snapcommunicator".
664 : *
665 : * \param[in] server The name of the server to send this message to.
666 : *
667 : * \sa get_server()
668 : * \sa get_service()
669 : * \sa set_service()
670 : */
671 2 : void message::set_server(std::string const & server)
672 : {
673 2 : if(f_server != server)
674 : {
675 : // this name can be empty and it supports lowercase
676 : //
677 4 : if(server != "."
678 2 : && server != "*")
679 : {
680 2 : verify_message_name(server, true);
681 : }
682 :
683 2 : f_server = server;
684 2 : f_cached_message.clear();
685 : }
686 2 : }
687 :
688 :
689 : /** \brief Retrieve the name of the service the message is for.
690 : *
691 : * This function returns the name of the service this message is being
692 : * sent to.
693 : *
694 : * \return Destination service.
695 : *
696 : * \sa get_server()
697 : * \sa set_server()
698 : * \sa set_service()
699 : */
700 4 : std::string const & message::get_service() const
701 : {
702 4 : return f_service;
703 : }
704 :
705 :
706 : /** \brief Set the name of the service this message is being sent to.
707 : *
708 : * This function specifies the name of the server this message is expected
709 : * to be sent to.
710 : *
711 : * When a service wants to send a message to snapcommunicator, no service
712 : * name is required.
713 : *
714 : * \param[in] service The name of the destination service.
715 : *
716 : * \sa get_server()
717 : * \sa set_server()
718 : * \sa get_service()
719 : */
720 2 : void message::set_service(std::string const & service)
721 : {
722 2 : if(f_service != service)
723 : {
724 : // broadcast is a special case that the verify_message_name() does not
725 : // support
726 : //
727 4 : if(service != "*"
728 2 : && service != "?"
729 4 : && service != ".")
730 : {
731 : // this name can be empty and it supports lowercase
732 : //
733 2 : verify_message_name(service, true);
734 : }
735 :
736 2 : f_service = service;
737 2 : f_cached_message.clear();
738 : }
739 2 : }
740 :
741 :
742 : /** \brief Copy sent information to this message.
743 : *
744 : * This function copies the sent information found in message
745 : * to this message server and service names.
746 : *
747 : * This is an equivalent to the following two lines of code:
748 : *
749 : * \code
750 : * reply.set_server(message.get_sent_from_server());
751 : * reply.set_service(message.get_sent_from_service());
752 : * \endcode
753 : *
754 : * \param[in] original_message The source message you want to reply to.
755 : */
756 1 : void message::reply_to(message const & original_message)
757 : {
758 1 : set_server(original_message.get_sent_from_server());
759 1 : set_service(original_message.get_sent_from_service());
760 1 : }
761 :
762 :
763 : /** \brief Get the command being sent.
764 : *
765 : * Each message is an equivalent to an RPC command being send between
766 : * services.
767 : *
768 : * The command is a string of text, generally one or more words
769 : * concatenated (no space allowed) such as STOP and LOCKENTERING.
770 : *
771 : * \note
772 : * The command string may still be empty if it was not yet assigned.
773 : *
774 : * \return The command of this message.
775 : */
776 3 : std::string const & message::get_command() const
777 : {
778 3 : return f_command;
779 : }
780 :
781 :
782 : /** \brief Set the message command.
783 : *
784 : * This function is used to define the RPC-like command of this message.
785 : *
786 : * The name of the command gets verified using the verify_message_name() function.
787 : * It cannot be empty and all letters have to be uppercase.
788 : *
789 : * \param[in] command The command to send to a connection.
790 : *
791 : * \sa verify_message_name()
792 : */
793 1 : void message::set_command(std::string const & command)
794 : {
795 : // this name cannot be empty and it does not support lowercase
796 : // characters either
797 : //
798 1 : verify_message_name(command, false, false);
799 :
800 1 : if(f_command != command)
801 : {
802 1 : f_command = command;
803 1 : f_cached_message.clear();
804 : }
805 1 : }
806 :
807 :
808 : /** \brief Retrieve the message version this library was compiled with.
809 : *
810 : * This function returns the MESSAGE_VERSION that this library was
811 : * compiled with. Since we offer a shared object (.so) library, it
812 : * could be different from the version your application was compiled
813 : * with. If that's the case, your application may want to at least
814 : * warn the user about the dicrepancy.
815 : *
816 : * \return The MESSAGE_VERSION at the time this library was compiled.
817 : */
818 2 : message_version_t message::get_message_version() const
819 : {
820 2 : return MESSAGE_VERSION;
821 : }
822 :
823 :
824 : /** \brief Check the version parameter.
825 : *
826 : * This function retrieves the version parameter which has to exist and
827 : * be an integer. If this is not the case, then an exception is raised.
828 : *
829 : * If the version is defined, it gets checked against this library's
830 : * compile time MESSAGE_VERSION variable. If equal, then the function
831 : * returns true. Otherwise, it returns false.
832 : *
833 : * In most cases, the very first message that you send with your service
834 : * should be such that it includes the version the libeventdispatcher
835 : * your program is linked against. In other words, you want to call
836 : * the add_version_parameter() in your sender and call this function
837 : * in your receiver.
838 : *
839 : * Make sure to design a way to disconnect cleanly so the other
840 : * party knows that the communication is interrupted because the
841 : * versions do not match. This allows your application to prevent
842 : * re-connecting over and over again when it knows it will fail
843 : * each time.
844 : *
845 : * \exception event_dispatcher_invalid_message
846 : * If you call this function and no version parameter was added to
847 : * the message, then this exception is raised.
848 : *
849 : * \return true if the version is present and valid.
850 : */
851 1 : bool message::check_version_parameter() const
852 : {
853 1 : return get_integer_parameter(MESSAGE_VERSION_NAME) == MESSAGE_VERSION;
854 : }
855 :
856 :
857 : /** \brief Add version parameter.
858 : *
859 : * Add a parameter named `"version"` with the current version of the
860 : * message protocol.
861 : *
862 : * In the snapcommunicator tool, this is sent over with the CONNECT
863 : * message. It allows the snapcommunicators to make sure they will
864 : * properly understand each others.
865 : */
866 1 : void message::add_version_parameter()
867 : {
868 1 : add_parameter(MESSAGE_VERSION_NAME, MESSAGE_VERSION);
869 1 : }
870 :
871 :
872 : /** \brief Add a string parameter to the message.
873 : *
874 : * Messages can include parameters (variables) such as a URI or a word.
875 : *
876 : * The value is not limited, although it probably should be limited to
877 : * standard text as these messages are sent as text. Especially, we
878 : * manage the '\0' character as the end of the message.
879 : *
880 : * The parameter name is verified by the verify_message_name() function.
881 : *
882 : * \param[in] name The name of the parameter.
883 : * \param[in] value The value of this parameter.
884 : *
885 : * \sa verify_message_name()
886 : */
887 1 : void message::add_parameter(std::string const & name, std::string const & value)
888 : {
889 1 : verify_message_name(name);
890 :
891 1 : f_parameters[name] = value;
892 1 : f_cached_message.clear();
893 1 : }
894 :
895 :
896 : /** \brief Add an integer parameter to the message.
897 : *
898 : * Messages can include parameters (variables) such as a URI or a word.
899 : *
900 : * The value is not limited, although it probably should be limited to
901 : * standard text as these messages are sent as text.
902 : *
903 : * The parameter name is verified by the verify_message_name() function.
904 : *
905 : * \param[in] name The name of the parameter.
906 : * \param[in] value The value of this parameter.
907 : *
908 : * \sa verify_message_name()
909 : */
910 2 : void message::add_parameter(std::string const & name, int32_t value)
911 : {
912 2 : verify_message_name(name);
913 :
914 2 : f_parameters[name] = std::to_string(value);
915 2 : f_cached_message.clear();
916 2 : }
917 :
918 :
919 : /** \brief Add an integer parameter to the message.
920 : *
921 : * Messages can include parameters (variables) such as a URI or a word.
922 : *
923 : * The value is not limited, although it probably should be limited to
924 : * standard text as these messages are sent as text.
925 : *
926 : * The parameter name is verified by the verify_message_name() function.
927 : *
928 : * \param[in] name The name of the parameter.
929 : * \param[in] value The value of this parameter.
930 : *
931 : * \sa verify_message_name()
932 : */
933 1 : void message::add_parameter(std::string const & name, uint32_t value)
934 : {
935 1 : verify_message_name(name);
936 :
937 1 : f_parameters[name] = std::to_string(value);
938 1 : f_cached_message.clear();
939 1 : }
940 :
941 :
942 : /** \brief Add an integer parameter to the message.
943 : *
944 : * Messages can include parameters (variables) such as a URI or a word.
945 : *
946 : * The value is not limited, although it probably should be limited to
947 : * standard text as these messages are sent as text.
948 : *
949 : * The parameter name is verified by the verify_message_name() function.
950 : *
951 : * \param[in] name The name of the parameter.
952 : * \param[in] value The value of this parameter.
953 : *
954 : * \sa verify_message_name()
955 : */
956 1 : void message::add_parameter(std::string const & name, long long value)
957 : {
958 1 : verify_message_name(name);
959 :
960 1 : f_parameters[name] = std::to_string(value);
961 1 : f_cached_message.clear();
962 1 : }
963 :
964 :
965 : /** \brief Add an integer parameter to the message.
966 : *
967 : * Messages can include parameters (variables) such as a URI or a word.
968 : *
969 : * The value is not limited, although it probably should be limited to
970 : * standard text as these messages are sent as text.
971 : *
972 : * The parameter name is verified by the verify_message_name() function.
973 : *
974 : * \param[in] name The name of the parameter.
975 : * \param[in] value The value of this parameter.
976 : *
977 : * \sa verify_message_name()
978 : */
979 1 : void message::add_parameter(std::string const & name, unsigned long long value)
980 : {
981 1 : verify_message_name(name);
982 :
983 1 : f_parameters[name] = std::to_string(value);
984 1 : f_cached_message.clear();
985 1 : }
986 :
987 :
988 : /** \brief Add an integer parameter to the message.
989 : *
990 : * Messages can include parameters (variables) such as a URI or a word.
991 : *
992 : * The value is not limited, although it probably should be limited to
993 : * standard text as these messages are sent as text.
994 : *
995 : * The parameter name is verified by the verify_message_name() function.
996 : *
997 : * \param[in] name The name of the parameter.
998 : * \param[in] value The value of this parameter.
999 : *
1000 : * \sa verify_message_name()
1001 : */
1002 1 : void message::add_parameter(std::string const & name, int64_t value)
1003 : {
1004 1 : verify_message_name(name);
1005 :
1006 1 : f_parameters[name] = std::to_string(value);
1007 1 : f_cached_message.clear();
1008 1 : }
1009 :
1010 :
1011 : /** \brief Add an integer parameter to the message.
1012 : *
1013 : * Messages can include parameters (variables) such as a URI or a word.
1014 : *
1015 : * The value is not limited, although it probably should be limited to
1016 : * standard text as these messages are sent as text.
1017 : *
1018 : * The parameter name is verified by the verify_message_name() function.
1019 : *
1020 : * \param[in] name The name of the parameter.
1021 : * \param[in] value The value of this parameter.
1022 : *
1023 : * \sa verify_message_name()
1024 : */
1025 1 : void message::add_parameter(std::string const & name, uint64_t value)
1026 : {
1027 1 : verify_message_name(name);
1028 :
1029 1 : f_parameters[name] = std::to_string(value);
1030 1 : f_cached_message.clear();
1031 1 : }
1032 :
1033 :
1034 : /** \brief Check whether a parameter is defined in this message.
1035 : *
1036 : * This function checks whether a parameter is defined in a message. If
1037 : * so it returns true. This is important because the get_parameter()
1038 : * functions throw if the parameter is not available (i.e. which is
1039 : * what is used for mandatory parameters.)
1040 : *
1041 : * The parameter name is verified by the verify_message_name() function.
1042 : *
1043 : * \param[in] name The name of the parameter.
1044 : *
1045 : * \return true if that parameter exists.
1046 : *
1047 : * \sa verify_message_name()
1048 : */
1049 21 : bool message::has_parameter(std::string const & name) const
1050 : {
1051 21 : verify_message_name(name);
1052 :
1053 21 : return f_parameters.find(name) != f_parameters.end();
1054 : }
1055 :
1056 :
1057 : /** \brief Retrieve a parameter as a string from this message.
1058 : *
1059 : * This function retrieves the named parameter from this message as a string,
1060 : * which is the default.
1061 : *
1062 : * The name must be valid as defined by the verify_message_name() function.
1063 : *
1064 : * \note
1065 : * This function returns a copy of the parameter so if you later change
1066 : * the value of that parameter, what has been returned does not change
1067 : * under your feet.
1068 : *
1069 : * \exception snap_communicator_invalid_message
1070 : * This exception is raised whenever the parameter is not defined or
1071 : * if the parameter \p name is not considered valid.
1072 : *
1073 : * \param[in] name The name of the parameter.
1074 : *
1075 : * \return A copy of the parameter value.
1076 : *
1077 : * \sa verify_message_name()
1078 : */
1079 7 : std::string message::get_parameter(std::string const & name) const
1080 : {
1081 7 : verify_message_name(name);
1082 :
1083 7 : auto const it(f_parameters.find(name));
1084 7 : if(it != f_parameters.end())
1085 : {
1086 14 : return it->second;
1087 : }
1088 :
1089 : throw event_dispatcher_invalid_message(
1090 : "message::get_parameter(): parameter \""
1091 0 : + name
1092 0 : + "\" of command \""
1093 0 : + f_command
1094 0 : + "\" not defined, try has_parameter() before calling"
1095 0 : " the get_parameter() function.");
1096 : }
1097 :
1098 :
1099 : /** \brief Retrieve a parameter as an integer from this message.
1100 : *
1101 : * This function retrieves the named parameter from this message as a string,
1102 : * which is the default.
1103 : *
1104 : * The name must be valid as defined by the verify_message_name() function.
1105 : *
1106 : * \exception snap_communicator_invalid_message
1107 : * This exception is raised whenever the parameter is not a valid integer,
1108 : * it is not set, or the parameter name is not considered valid.
1109 : *
1110 : * \param[in] name The name of the parameter.
1111 : *
1112 : * \return The parameter converted to an integer.
1113 : *
1114 : * \sa verify_message_name()
1115 : */
1116 7 : std::int64_t message::get_integer_parameter(std::string const & name) const
1117 : {
1118 7 : verify_message_name(name);
1119 :
1120 7 : auto const it(f_parameters.find(name));
1121 7 : if(it != f_parameters.end())
1122 : {
1123 : std::int64_t r;
1124 7 : if(!advgetopt::validator_integer::convert_string(it->second, r))
1125 : {
1126 : throw event_dispatcher_invalid_message(
1127 : "message::get_integer_parameter(): command \""
1128 0 : + f_command
1129 0 : + "\" expected integer for \""
1130 0 : + name
1131 0 : + "\" but \""
1132 0 : + it->second
1133 0 : + "\" could not be converted.");
1134 : }
1135 14 : return r;
1136 : }
1137 :
1138 : throw event_dispatcher_invalid_message(
1139 : "message::get_integer_parameter(): parameter \""
1140 0 : + name
1141 0 : + "\" of command \""
1142 0 : + f_command
1143 0 : + "\" not defined, try has_parameter() before calling"
1144 0 : " the get_integer_parameter() function.");
1145 : }
1146 :
1147 :
1148 : /** \brief Retrieve the list of parameters from this message.
1149 : *
1150 : * This function returns a constant reference to the list of parameters
1151 : * defined in this message.
1152 : *
1153 : * This can be useful if you allow for variable lists of parameters, but
1154 : * generally the get_parameter() and get_integer_parameter() are prefered.
1155 : *
1156 : * \warning
1157 : * This is a direct reference to the list of parameter. If you call the
1158 : * add_parameter() function, the new parameter will be visible in that
1159 : * new list and an iterator is likely not going to be valid on return
1160 : * from that call.
1161 : *
1162 : * \return A constant reference to the list of message parameters.
1163 : *
1164 : * \sa get_parameter()
1165 : * \sa get_integer_parameter()
1166 : */
1167 2 : message::parameters_t const & message::get_all_parameters() const
1168 : {
1169 2 : return f_parameters;
1170 : }
1171 :
1172 :
1173 : /** \brief Verify various names used with messages.
1174 : *
1175 : * The messages use names for:
1176 : *
1177 : * \li commands
1178 : * \li services
1179 : * \li parameters
1180 : *
1181 : * All those names must be valid as per this function. They are checked
1182 : * on read and on write (i.e. add_parameter() and get_paramter() both
1183 : * check the parameter name to make sure you did not mistype it.)
1184 : *
1185 : * A valid name must start with a letter or an underscore (although
1186 : * we suggest you do not start names with underscores; we want to
1187 : * have those reserved for low level system like messages,) and
1188 : * it can only include letters, digits, and underscores.
1189 : *
1190 : * The letters are limited to uppercase for commands. Also certain
1191 : * names may be empty (See concerned functions for details on that one.)
1192 : *
1193 : * \note
1194 : * The allowed letters are 'a' to 'z' and 'A' to 'Z' only. The allowed
1195 : * digits are '0' to '9' only. The underscore is '_' only.
1196 : *
1197 : * A few valid names:
1198 : *
1199 : * \li commands: PING, STOP, LOCK, LOCKED, QUITTING, UNKNOWN, LOCKEXITING
1200 : * \li services: snapcommunicator, snapserver, snaplock, MyOwnService
1201 : * \li parameters: URI, name, IP, TimeOut
1202 : *
1203 : * At this point all our services use lowercase, but this is not enforced.
1204 : * Actually, mixed case or uppercase service names are allowed.
1205 : *
1206 : * \exception event_distpatcher_invalid_message
1207 : * This exception is raised if the name includes characters considered
1208 : * invalid.
1209 : *
1210 : * \param[in] name The name of the parameter.
1211 : * \param[in] can_be_empty Whether the name can be empty.
1212 : * \param[in] can_be_lowercase Whether the name can include lowercase letters.
1213 : */
1214 50 : void verify_message_name(std::string const & name, bool can_be_empty, bool can_be_lowercase)
1215 : {
1216 100 : if(!can_be_empty
1217 50 : && name.empty())
1218 : {
1219 0 : std::string err("a message name cannot be empty.");
1220 0 : SNAP_LOG_FATAL << err;
1221 0 : throw event_dispatcher_invalid_message(err);
1222 : }
1223 :
1224 325 : for(auto const & c : name)
1225 : {
1226 275 : if((c < 'a' || c > 'z' || !can_be_lowercase)
1227 37 : && (c < 'A' || c > 'Z')
1228 30 : && (c < '0' || c > '9')
1229 0 : && c != '_')
1230 : {
1231 : std::string err("a message name must be composed of ASCII"
1232 : " 'a'..'z', 'A'..'Z', '0'..'9', or '_'"
1233 0 : " only (also a command must be uppercase only,) \"");
1234 0 : err += name;
1235 0 : err += "\" is not valid.";
1236 0 : SNAP_LOG_FATAL << err;
1237 0 : throw event_dispatcher_invalid_message(err);
1238 : }
1239 : }
1240 :
1241 100 : if(!name.empty()
1242 50 : && name[0] >= '0' && name[0] <= '9')
1243 : {
1244 0 : std::string err("parameter name cannot start with a digit, \"");
1245 0 : err += name;
1246 0 : err += "\" is not valid.";
1247 0 : SNAP_LOG_FATAL << err;
1248 0 : throw event_dispatcher_invalid_message(err);
1249 : }
1250 50 : }
1251 :
1252 :
1253 :
1254 :
1255 6 : } // namespace ed
1256 : // vim: ts=4 sw=4 et
|