LCOV - code coverage report
Current view: top level - eventdispatcher - dispatcher.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 25 93 26.9 %
Date: 2022-02-12 12:27:47 Functions: 28 66 42.4 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13