LCOV - code coverage report
Current view: top level - eventdispatcher - dispatcher.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 26 94 27.7 %
Date: 2021-09-19 09:06:58 Functions: 28 66 42.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2012-2021  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             : #pragma once
      20             : 
      21             : /** \file
      22             :  * \brief Event dispatch class.
      23             :  *
      24             :  * Class used to handle events.
      25             :  */
      26             : 
      27             : 
      28             : // self
      29             : //
      30             : #include    "eventdispatcher/dispatcher_base.h"
      31             : #include    "eventdispatcher/communicator.h"
      32             : #include    "eventdispatcher/utils.h"
      33             : 
      34             : 
      35             : // snaplogger lib
      36             : //
      37             : #include    <snaplogger/message.h>
      38             : 
      39             : 
      40             : 
      41             : namespace ed
      42             : {
      43             : 
      44             : 
      45             : 
      46             : /** \brief A template to create a list of messages to dispatch on receival.
      47             :  *
      48             :  * Whenever you receive messages, they can automatically get dispatched to
      49             :  * various functions using the dispatcher.
      50             :  *
      51             :  * You define a dispatcher_match array and then add a dispatcher to
      52             :  * your connection object.
      53             :  *
      54             :  * \code
      55             :  *      ed::dispatcher<my_connection>::dispatcher_match const my_messages[] =
      56             :  *      {
      57             :  *          {
      58             :  *              "HELP"
      59             :  *            , &my_connection::msg_help
      60             :  *            //, &dispatcher<my_connection>::dispatcher_match::one_to_one_match -- use default
      61             :  *          },
      62             :  *          {
      63             :  *              "STATUS"
      64             :  *            , &my_connection::msg_status
      65             :  *            //, &dispatcher<my_connection>::dispatcher_match::one_to_one_match -- use default
      66             :  *          },
      67             :  *          ... // other messages
      68             :  *
      69             :  *          // if you'd like you can end your list with a catch all which
      70             :  *          // generate the UNKNOWN message with the following (not required)
      71             :  *          // if you have that entry, your own process_message() function
      72             :  *          // will not get called
      73             :  *          {
      74             :  *              nullptr
      75             :  *            , &dispatcher<my_connection>::dispatcher_match::msg_reply_with_unknown
      76             :  *            , &dispatcher<my_connection>::dispatcher_match::always_match
      77             :  *          },
      78             :  *      };
      79             :  * \endcode
      80             :  *
      81             :  * In most cases you do not need to specify the matching function. It will
      82             :  * use the default which is a one to one match. So in the example above,
      83             :  * for "HELP", only a message with the command set to "HELP" will match.
      84             :  * When a match is found, the corresponding function (msg_help() here)
      85             :  * gets called.
      86             :  *
      87             :  * Note that "functions" are actually offsets. You will get `this` defined
      88             :  * as expected when your function gets called. The one drawback is that
      89             :  * only a function of the connection you attach the dispatcher to can
      90             :  * be called from the dispatcher. This is because we want to have a
      91             :  * static table instead of a dynamic one created each time we start
      92             :  * a process (in a website server, many processes get started again and
      93             :  * again.)
      94             :  *
      95             :  * \note
      96             :  * The T parameter of this template is often referenced as a "connection"
      97             :  * because it is expected to be a connection. There is actually no such
      98             :  * constraint on that object. It just needs to understand the dispatcher
      99             :  * usage which is to call the dispatch() function whenever a message is
     100             :  * received. Also it needs to implement any f_execute() function as
     101             :  * defined in its dispatch_match vector.
     102             :  *
     103             :  * \note
     104             :  * This is documented here because it is a template and we cannot do
     105             :  * that in the .cpp (at least older versions of doxygen could not.)
     106             :  *
     107             :  * \todo
     108             :  * Transform the dispatcher_match with classes so we can build the
     109             :  * array safely.
     110             :  */
     111             : template<typename T>
     112           8 : class dispatcher
     113             :     : public dispatcher_base
     114             : {
     115             : public:
     116             :     /** \brief A smart pointer of the dispatcher.
     117             :      *
     118             :      * Although we expect the array of `dispatcher_match` to be
     119             :      * statically defined, the `dispatcher`, on the other hand,
     120             :      * is quite dynamic and needs to be allocated in a smart
     121             :      * pointer then added to your connection.
     122             :      */
     123             :     typedef std::shared_ptr<dispatcher> pointer_t;
     124             : 
     125             :     /** \brief This structure is used to define the list of supported messages.
     126             :      *
     127             :      * Whenever you create an array of messages, you use this structure.
     128             :      *
     129             :      * The structure takes a few parameters as follow:
     130             :      *
     131             :      * \li f_expr -- the "expression" to be matched to the command name
     132             :      *               for example "HELP".
     133             :      * \li f_execute -- the function offset to execute on a match.
     134             :      * \li f_match -- the function to check whether the expression is a match;
     135             :      *                it has a default of one_to_one_match() which means the
     136             :      *                f_expr string is viewed as a plain string defining the
     137             :      *                message name as is.
     138             :      *
     139             :      * The command name is called "f_expr" but some matching functions may
     140             :      * make use of the "f_expr" parameter as an expression such as a
     141             :      * regular expression. Such functions will be added here with time, you
     142             :      * may also have your own, of course. The match function is expected to
     143             :      * be a static or standalone function.
     144             :      */
     145             :     struct dispatcher_match
     146             :     {
     147             :         /** \brief Define a vector of dispatcher_match objects.
     148             :          *
     149             :          * This function includes an array of dispatcher_match objects.
     150             :          * Whenever you define dispatcher_match objects, you want to
     151             :          * use the C++11 syntax to create a vector.
     152             :          *
     153             :          * \attention
     154             :          * We are NOT using a match because the matching may make use
     155             :          * of complex functions that support things such as complex as
     156             :          * regular expressions. In other words, the name of the message
     157             :          * may not just be a simple string.
     158             :          */
     159             :         typedef std::vector<dispatcher_match>       vector_t;
     160             : 
     161             :         /** \brief The execution function.
     162             :          *
     163             :          * This type defines the execution function. We give it the message
     164             :          * on a match. If the command name is not a match, it is ignored.
     165             :          */
     166             :         typedef void (T::*execute_func_t)(message & msg);
     167             : 
     168             :         /** \brief The match function return types.
     169             :          *
     170             :          * Whenever a match function is called, it may return one of:
     171             :          *
     172             :          * \li MATCH_FALSE
     173             :          *
     174             :          * The function did not match anything. Ignore the corresponding
     175             :          * function.
     176             :          *
     177             :          * \li MATCH_TRUE
     178             :          *
     179             :          * This is a match, execute the function. We are done with this list
     180             :          * of matches.
     181             :          *
     182             :          * \li MATCH_CALLBACK
     183             :          *
     184             :          * The function is a callback, it gets called and the process
     185             :          * continues. Since the message parameter is read/write, it is
     186             :          * a way to tweak the message before other functions get it.
     187             :          */
     188             :         enum class match_t
     189             :         {
     190             :             MATCH_FALSE,
     191             :             MATCH_TRUE,
     192             :             MATCH_CALLBACK
     193             :         };
     194             : 
     195             :         /** \brief The match function.
     196             :          *
     197             :          * This type defines the match function. We give it the message
     198             :          * which has the command name, although specialized matching
     199             :          * function could test other parameters from the message such
     200             :          * as the origination of the message.
     201             :          */
     202             :         typedef match_t (*match_func_t)(std::string const & expr, message & msg);
     203             : 
     204             :         /** \brief The default matching function.
     205             :          *
     206             :          * This function checks the command one to one to the expression.
     207             :          * The word in the expression is compared as is to the command
     208             :          * name:
     209             :          *
     210             :          * \code
     211             :          *          return expr == msg.get_command();
     212             :          * \endcode
     213             :          *
     214             :          * We will add other matching functions with time
     215             :          * (start_with_match(), regex_match(), etc.)
     216             :          *
     217             :          * \note
     218             :          * It is permissible to use a match function to modify the
     219             :          * message in some way, however, it is not recommended.
     220             :          *
     221             :          * \param[in] expr  The expression to compare the command against.
     222             :          * \param[in] msg  The message to match against this expression.
     223             :          *
     224             :          * \return MATCH_TRUE if it is a match, MATCH_FALSE otherwise.
     225             :          */
     226           8 :         static match_t one_to_one_match(std::string const & expr, message & msg)
     227             :         {
     228           8 :             return expr == msg.get_command()
     229           8 :                             ? match_t::MATCH_TRUE
     230           8 :                             : match_t::MATCH_FALSE;
     231             :         }
     232             : 
     233             :         /** \brief Always returns true.
     234             :          *
     235             :          * This function always returns true. This is practical to close
     236             :          * your list of messages and return a specific message. In most
     237             :          * cases this is used to reply with the UNKNOWN message.
     238             :          *
     239             :          * \param[in] expr  The expression to compare the command against.
     240             :          * \param[in] msg  The message to match against this expression.
     241             :          *
     242             :          * \return Always returns MATCH_TRUE.
     243             :          */
     244           0 :         static match_t always_match(std::string const & expr, message & msg)
     245             :         {
     246           0 :             snap::NOT_USED(expr, msg);
     247           0 :             return match_t::MATCH_TRUE;
     248             :         }
     249             : 
     250             :         /** \brief Always returns true.
     251             :          *
     252             :          * This function always returns true. This is practical to close
     253             :          * your list of messages and return a specific message. In most
     254             :          * cases this is used to reply with the UNKNOWN message.
     255             :          *
     256             :          * \param[in] expr  The expression to compare the command against.
     257             :          * \param[in] msg  The message to match against this expression.
     258             :          *
     259             :          * \return Always returns MATCH_CALLBACK.
     260             :          */
     261           0 :         static match_t callback_match(std::string const & expr, message & msg)
     262             :         {
     263           0 :             snap::NOT_USED(expr, msg);
     264           0 :             return match_t::MATCH_CALLBACK;
     265             :         }
     266             : 
     267             :         /** \brief The expression to compare against.
     268             :          *
     269             :          * The expression is most often going to be the exact command name
     270             :          * which will be matched with the one_to_one_match() function.
     271             :          *
     272             :          * For other match functions, this would be whatever type of
     273             :          * expression supported by those other functions.
     274             :          *
     275             :          * \note
     276             :          * Effective C++ doesn't like bare pointers, but there is no real
     277             :          * reason for us to waste time and memory by having an std::string
     278             :          * here. It's going to always be a constant pointer anyway.
     279             :          */
     280             :         char const *        f_expr = nullptr;
     281             : 
     282             :         /** \brief The execute function.
     283             :          *
     284             :          * This is an offset in your connection class. We do not allow
     285             :          * std::bind() because we do not want the array of messages to be
     286             :          * dynamic (that way it is created at compile time and loaded as
     287             :          * ready/prepared data on load.)
     288             :          *
     289             :          * The functions called have `this` defined so you can access
     290             :          * your connection data and other functions. It requires the
     291             :          * `&` and the class name to define the pointer, like this:
     292             :          *
     293             :          * \code
     294             :          *      &MyClass::my_message_function
     295             :          * \endcode
     296             :          *
     297             :          * The execution is started by calling the run() function.
     298             :          */
     299             :         execute_func_t      f_execute = nullptr;
     300             : 
     301             :         /** \brief The match function.
     302             :          *
     303             :          * The match function is used to know whether that command
     304             :          * dispatch function was found.
     305             :          *
     306             :          * By default this parameter is set to one_to_one_match().
     307             :          * This means the command has to be one to one equal to
     308             :          * the f_expr string.
     309             :          *
     310             :          * The matching is done in the match() function.
     311             :          */
     312             :         match_func_t        f_match = &::ed::dispatcher<T>::dispatcher_match::one_to_one_match;
     313             : 
     314             :         /** \brief Run the execution function if this is a match.
     315             :          *
     316             :          * First this function checks whether the command of the message
     317             :          * in \p msg matches this `dispatcher_match` expression. In
     318             :          * most cases the match function is going to be
     319             :          * one_on_one_match() which means it has to be exactly equal.
     320             :          *
     321             :          * If it is a match, this function runs your \p connection execution
     322             :          * function (i.e. the message gets dispatched) and then it returns
     323             :          * true.
     324             :          *
     325             :          * If the message is not a match, then the function returns false
     326             :          * and only the matching function was called. In this case the
     327             :          * \p connection does not get used.
     328             :          *
     329             :          * When this function returns true, you should not call the
     330             :          * process_message() function since that was already taken care
     331             :          * of. The process_message() function should only be called
     332             :          * if the message was not yet dispatched. When the list of
     333             :          * matches includes a catch all at the end, the process_message()
     334             :          * will never be called.
     335             :          *
     336             :          * \note
     337             :          * Note that we do not offer two functions, one to run the match
     338             :          * function and one to execute the match because you could otherwise
     339             :          * end up calling the execute function (dispatch) on any
     340             :          * `dispatcher_match` entry and we did not want that to happen.
     341             :          *
     342             :          * \param[in] connection  The connection attached to that
     343             :          *                        `dispatcher_match`.
     344             :          * \param[in] msg  The message that matched.
     345             :          *
     346             :          * \return true if the connection execute function was called.
     347             :          */
     348           8 :         bool execute(T * connection, ::ed::message & msg) const
     349             :         {
     350           8 :             match_t m(f_match(f_expr == nullptr ? std::string() : f_expr, msg));
     351           8 :             if(m == match_t::MATCH_TRUE
     352           2 :             || m == match_t::MATCH_CALLBACK)
     353             :             {
     354           6 :                 (connection->*f_execute)(msg);
     355           6 :                 if(m == match_t::MATCH_TRUE)
     356             :                 {
     357           6 :                     return true;
     358             :                 }
     359             :             }
     360             : 
     361           2 :             return false;
     362             :         }
     363             : 
     364             :         /** \brief Check whether f_match is one_to_one_match().
     365             :          *
     366             :          * This function checks whether the f_match function was defined
     367             :          * to one_to_one_match()--the default--and if so returns true.
     368             :          *
     369             :          * \return true if f_match is the one_to_one_match() function.
     370             :          */
     371           0 :         bool match_is_one_to_one_match() const
     372             :         {
     373           0 :             return f_match == &ed::dispatcher<T>::dispatcher_match::one_to_one_match;
     374             :         }
     375             : 
     376             :         /** \brief Check whether f_match is always_match().
     377             :          *
     378             :          * This function checks whether the f_match function was defined
     379             :          * to always_match() and if so returns true.
     380             :          *
     381             :          * \return true if f_match is the always_match() function.
     382             :          */
     383           0 :         bool match_is_always_match() const
     384             :         {
     385           0 :             return f_match == &ed::dispatcher<T>::dispatcher_match::always_match;
     386             :         }
     387             : 
     388             :         /** \brief Check whether f_match is callback_match().
     389             :          *
     390             :          * This function checks whether the f_match function was defined
     391             :          * to callback_match() and if so returns true.
     392             :          *
     393             :          * \return true if f_match is the callback_match() function.
     394             :          */
     395           0 :         bool match_is_callback_match() const
     396             :         {
     397           0 :             return f_match == &ed::dispatcher<T>::dispatcher_match::callback_match;
     398             :         }
     399             :     };
     400             : 
     401             : private:
     402             :     /** \brief The connection pointer.
     403             :      *
     404             :      * This parameter is set by the constructor. It represents the
     405             :      * connection this dispatcher was added to (a form of parent of
     406             :      * this dispatcher object.)
     407             :      */
     408             :     T *                         f_connection = nullptr;
     409             : 
     410             :     /** \brief The array of possible matches.
     411             :      *
     412             :      * This is the vector of your messages with the corresponding
     413             :      * match and execute functions. This is used to go through
     414             :      * the matches and execute (dispatch) as required.
     415             :      */
     416             :     typename ed::dispatcher<T>::dispatcher_match::vector_t  f_matches = {};
     417             : 
     418             :     /** \brief Tell whether messages should be traced or not.
     419             :      *
     420             :      * Because your service may accept and send many messages a full
     421             :      * trace on all of them can really be resource intensive. By default
     422             :      * the system will not trace anything. By setting this parameter to
     423             :      * true (call set_trace() for that) you request the SNAP_LOG_TRACE()
     424             :      * to run on each message received by this dispatcher. This is done
     425             :      * on entry so whether the message is processed by the dispatcher
     426             :      * or your own send_message() function, it will trace that message.
     427             :      */
     428             :     bool                        f_trace = false;
     429             : 
     430             : public:
     431             : 
     432             :     /** \brief Initialize the dispatcher with your connection and messages.
     433             :      *
     434             :      * This function takes a pointer to your connection and an array
     435             :      * of matches.
     436             :      *
     437             :      * Whenever a message is received by one of your connections, the
     438             :      * dispatch() function gets called which checks the message against
     439             :      * each entry in this array of \p matches.
     440             :      *
     441             :      * \param[in] connection  The connection for which this dispatcher is
     442             :      *                        created.
     443             :      * \param[in] matches  The array of dispatch keywords and functions.
     444             :      * \param[in] matches_size  The sizeof() of the matches array.
     445             :      */
     446           4 :     dispatcher<T>(T * connection, typename ed::dispatcher<T>::dispatcher_match::vector_t matches)
     447             :         : f_connection(connection)
     448           4 :         , f_matches(matches)
     449             :     {
     450           4 :     }
     451             : 
     452             :     // prevent copies
     453             :     dispatcher<T>(dispatcher<T> const & rhs) = delete;
     454             :     dispatcher<T> & operator = (dispatcher<T> const & rhs) = delete;
     455             : 
     456             :     /** \brief Add a default array of possible matches.
     457             :      *
     458             :      * In Snap! a certain number of messages are always exactly the same
     459             :      * and these can be implemented internally so each daemon doesn't have
     460             :      * to duplicate that work over and over again. These are there in part
     461             :      * because the snapcommunicator expects those messages there.
     462             :      *
     463             :      * IMPORTANT NOTE: If you add your own version in your dispatcher_match
     464             :      * vector, then these will be ignored since your version will match first
     465             :      * and the dispatcher uses the first function only.
     466             :      *
     467             :      * This array currently includes:
     468             :      *
     469             :      * \li HELP -- msg_help() -- returns the list of all the messages
     470             :      * \li LOG -- msg_log() -- reconfigure() the logger
     471             :      * \li QUITTING -- msg_quitting() -- calls stop(true);
     472             :      * \li READY -- msg_ready() -- calls ready() -- snapcommunicator always
     473             :      *              sends that message so it has to be supported
     474             :      * \li RESTART -- msg_restart() -- calls restart() -- it is triggered
     475             :      *                when a restart is required (i.e. the library was
     476             :      *                upgraded, a configuration file was updated, etc.)
     477             :      * \li STOP -- msg_stop() -- calls stop(false);
     478             :      * \li UNKNOWN -- msg_log_unknown() -- in case we receive a message we
     479             :      *                don't understand
     480             :      * \li * -- msg_reply_with_unknown() -- the last entry will be a grab
     481             :      *          all pattern which returns the UNKNOWN message automatically
     482             :      *          for you
     483             :      *
     484             :      * The msg_...() functions must be declared in your class T. If you
     485             :      * use the system connection_with_send_message class then they're
     486             :      * already defined there.
     487             :      *
     488             :      * The HELP response is automatically built from the f_matches.f_expr
     489             :      * strings. However, if the function used to match the expression is
     490             :      * not one_to_one_match(), then that string doesn't get used.
     491             :      *
     492             :      * If any message can't be determine (i.e. the function is not the
     493             :      * one_to_one_match()) then the user help() function gets called and
     494             :      * we expect that function to add any dynamic message the daemon
     495             :      * understands.
     496             :      *
     497             :      * The LOG message reconfigures the logger if the is_configure() function
     498             :      * says it is configured. In any other circumstances, nothing happens.
     499             :      *
     500             :      * Note that the UNKNOWN message is understood and just logs the message
     501             :      * received. This allows us to see that WE sent a message that the receiver
     502             :      * (not us) does not understand and adjust our code accordingly (i.e. add
     503             :      * support for that message in that receiver or maybe fix the spelling.)
     504             :      */
     505           0 :     void add_communicator_commands()
     506             :     {
     507             :         // avoid more than one realloc()
     508             :         //
     509           0 :         f_matches.reserve(f_matches.size() + 10);
     510             : 
     511             :         {
     512           0 :             typename ed::dispatcher<T>::dispatcher_match m;
     513           0 :             m.f_expr = "ALIVE";
     514           0 :             m.f_execute = &T::msg_alive;
     515             :             //m.f_match = <default>;
     516           0 :             f_matches.push_back(m);
     517             :         }
     518             :         {
     519           0 :             typename ed::dispatcher<T>::dispatcher_match m;
     520           0 :             m.f_expr = "HELP";
     521           0 :             m.f_execute = &T::msg_help;
     522             :             //m.f_match = <default>;
     523           0 :             f_matches.push_back(m);
     524             :         }
     525             :         {
     526           0 :             typename ed::dispatcher<T>::dispatcher_match m;
     527           0 :             m.f_expr = "LEAK";
     528           0 :             m.f_execute = &T::msg_leak;
     529             :             //m.f_match = <default>;
     530           0 :             f_matches.push_back(m);
     531             :         }
     532             :         {
     533           0 :             typename ed::dispatcher<T>::dispatcher_match m;
     534           0 :             m.f_expr = "LOG";
     535           0 :             m.f_execute = &T::msg_log;
     536             :             //m.f_match = <default>;
     537           0 :             f_matches.push_back(m);
     538             :         }
     539             :         {
     540           0 :             typename ed::dispatcher<T>::dispatcher_match m;
     541           0 :             m.f_expr = "QUITTING";
     542           0 :             m.f_execute = &T::msg_quitting;
     543             :             //m.f_match = <default>;
     544           0 :             f_matches.push_back(m);
     545             :         }
     546             :         {
     547           0 :             typename ed::dispatcher<T>::dispatcher_match m;
     548           0 :             m.f_expr = "READY";
     549           0 :             m.f_execute = &T::msg_ready;
     550             :             //m.f_match = <default>;
     551           0 :             f_matches.push_back(m);
     552             :         }
     553             :         {
     554           0 :             typename ed::dispatcher<T>::dispatcher_match m;
     555           0 :             m.f_expr = "RESTART";
     556           0 :             m.f_execute = &T::msg_restart;
     557             :             //m.f_match = <default>;
     558           0 :             f_matches.push_back(m);
     559             :         }
     560             :         {
     561           0 :             typename ed::dispatcher<T>::dispatcher_match m;
     562           0 :             m.f_expr = "STOP";
     563           0 :             m.f_execute = &T::msg_stop;
     564             :             //m.f_match = <default>;
     565           0 :             f_matches.push_back(m);
     566             :         }
     567             :         {
     568           0 :             typename ed::dispatcher<T>::dispatcher_match m;
     569           0 :             m.f_expr = "UNKNOWN";
     570           0 :             m.f_execute = &T::msg_log_unknown;
     571             :             //m.f_match = <default>;
     572           0 :             f_matches.push_back(m);
     573             :         }
     574             :         {
     575           0 :             typename ed::dispatcher<T>::dispatcher_match m;
     576           0 :             m.f_expr = nullptr; // any other message
     577           0 :             m.f_execute = &T::msg_reply_with_unknown;
     578           0 :             m.f_match = &ed::dispatcher<T>::dispatcher_match::always_match;
     579           0 :             f_matches.push_back(m);
     580             :         }
     581           0 :     }
     582             : 
     583             :     typename ed::dispatcher<T>::dispatcher_match::vector_t const & get_matches() const
     584             :     {
     585             :         return f_matches;
     586             :     }
     587             : 
     588             :     /** \brief The dispatch function.
     589             :      *
     590             :      * This is the function your message system will call whenever
     591             :      * the system receives a message.
     592             :      *
     593             :      * The function returns true if the message was dispatched.
     594             :      * When that happen, the process_message() function of the
     595             :      * connection should not be called.
     596             :      *
     597             :      * You may not include a message in the array of `dispatch_match`
     598             :      * if it is too complicated to match or too many variables are
     599             :      * necessary then you will probably want to use your
     600             :      * process_message().
     601             :      *
     602             :      * By adding a catch-all at the end of your list of matches, you
     603             :      * can easily have one function called for any message. By default
     604             :      * the dispatcher environment offers such a match function and
     605             :      * it also includes a function that sends the UNKNOWN message as
     606             :      * an immediate reply to a received message.
     607             :      *
     608             :      * \param[in] msg  The message to be dispatched.
     609             :      *
     610             :      * \return true if the message was dispatched, false otherwise.
     611             :      */
     612           6 :     virtual bool dispatch(::ed::message & msg) override
     613             :     {
     614           6 :         if(f_trace)
     615             :         {
     616          12 :             SNAP_LOG_TRACE
     617          12 :                 << "dispatch message \""
     618             :                 << msg.to_message()
     619             :                 << "\"."
     620             :                 << SNAP_LOG_SEND;
     621             :         }
     622             : 
     623             :         // go in order to execute matches
     624             :         //
     625             :         // remember that a dispatcher with just a set of well defined command
     626             :         // names is a special case (albeit frequent) and we can't process
     627             :         // using a map (a.k.a. fast binary search) as a consequence
     628             :         //
     629           8 :         for(auto const & m : f_matches)
     630             :         {
     631           8 :             if(m.execute(f_connection, msg))
     632             :             {
     633           6 :                 return true;
     634             :             }
     635             :         }
     636             : 
     637           0 :         return false;
     638             :     }
     639             : 
     640             :     /** \brief Set whether the dispatcher should trace your messages or not.
     641             :      *
     642             :      * By default, the f_trace flag is set to false. You can change it to
     643             :      * true while debugging. You should remember to turn it back off once
     644             :      * you make an official version of your service to avoid the possibly
     645             :      * huge overhead of sending all those log messages. One way to do so
     646             :      * is to place the code within #ifdef/#endif as in:
     647             :      *
     648             :      * \code
     649             :      *     #ifdef _DEBUG
     650             :      *         my_dispatcher->set_trace();
     651             :      *     #endif
     652             :      * \endcode
     653             :      *
     654             :      * \param[in] trace  Set to true to get SNAP_LOG_TRACE() of each message.
     655             :      */
     656           4 :     void set_trace(bool trace = true)
     657             :     {
     658           4 :         f_trace = trace;
     659           4 :     }
     660             : 
     661             :     /** \brief Retrieve the list of commands.
     662             :      *
     663             :      * This function transforms the vector of f_matches in a list of
     664             :      * commands in a string_list_t.
     665             :      *
     666             :      * \param[in,out] commands  The place where the list of commands is saved.
     667             :      *
     668             :      * \return false if the commands were all determined, true if some need
     669             :      *         help from the user of this dispatcher.
     670             :      */
     671           0 :     virtual bool get_commands(string_list_t & commands) override
     672             :     {
     673           0 :         bool need_user_help(false);
     674           0 :         for(auto const & m : f_matches)
     675             :         {
     676           0 :             if(m.f_expr == nullptr)
     677             :             {
     678           0 :                 if(!m.match_is_always_match()
     679           0 :                 && !m.match_is_callback_match())
     680             :                 {
     681             :                     // this is a "special case" where the user has
     682             :                     // a magical function which does not require an
     683             :                     // expression at all (i.e. "hard coded" in a
     684             :                     // function)
     685             :                     //
     686           0 :                     need_user_help = true;
     687             :                 }
     688             :                 //else -- always match is the last entry and that just
     689             :                 //        means we can return UNKNOWN on an unknown message
     690             :             }
     691           0 :             else if(m.match_is_one_to_one_match())
     692             :             {
     693             :                 // add the f_expr as is since it represents a command
     694             :                 // as is
     695             :                 //
     696           0 :                 commands.push_back(m.f_expr);
     697             :             }
     698             :             else
     699             :             {
     700             :                 // this is not a one to one match, so possibly a
     701             :                 // full regex or similar
     702             :                 //
     703           0 :                 need_user_help = true;
     704             :             }
     705             :         }
     706           0 :         return need_user_help;
     707             :     }
     708             : };
     709             : 
     710             : 
     711             : 
     712             : } // namespace ed
     713             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13