Web Wispers 1.2.2 C++ Unit Test Coverage
Current view: top level - ssrc/wispers/ws_lua - service.cc (source / functions) Hit Total Coverage
Test: Web Wispers 1.2.2 C++ Unit Tests Lines: 135 171 78.9 %
Date: 2012-04-09 Functions: 15 16 93.8 %
Branches: 154 405 38.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright 2006-2009 Savarese Software Research Corporation
       3                 :            :  *
       4                 :            :  * Licensed under the Apache License, Version 2.0 (the "License");
       5                 :            :  * you may not use this file except in compliance with the License.
       6                 :            :  * You may obtain a copy of the License at
       7                 :            :  *
       8                 :            :  *     https://www.savarese.com/software/ApacheLicense-2.0
       9                 :            :  *
      10                 :            :  * Unless required by applicable law or agreed to in writing, software
      11                 :            :  * distributed under the License is distributed on an "AS IS" BASIS,
      12                 :            :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      13                 :            :  * See the License for the specific language governing permissions and
      14                 :            :  * limitations under the License.
      15                 :            :  */
      16                 :            : 
      17                 :            : #include <ssrc/wispers/ws_lua/service.h>
      18                 :            : 
      19                 :            : #ifdef WSPR_DEBUG
      20                 :            : #include <ssrc/wispers/utility/PropertiesToString.h>
      21                 :            : #endif
      22                 :            : 
      23                 :            : #include <boost/filesystem/path.hpp>
      24                 :            : 
      25                 :            : __BEGIN_NS_SSRC_WSPR_WS_LUA
      26                 :            : 
      27                 :            : using namespace Lua;
      28                 :            : using namespace NS_SSRC_WSPR_LUA;
      29                 :            : 
      30                 :            : namespace {
      31                 :          2 :   inline void get_parameters(lua_State* state,
      32                 :            :                              const int index,
      33                 :            :                              parameter_sequence & parameters)
      34                 :            :   {
      35                 :          2 :     lua_getfield(state, index, "parameters");
      36                 :            : 
      37         [ +  - ]:          2 :     if(lua_istable(state, -1)) {
      38                 :          2 :       int count_parameters = 0;
      39                 :            : 
      40                 :          2 :       lua_pushnil(state);
      41         [ +  + ]:          6 :       while(lua_next(state, -2)) {
      42                 :          2 :         ++count_parameters;
      43                 :          2 :         lua_pop(state, 1);
      44                 :            :       }
      45                 :            : 
      46         [ +  - ]:          2 :       if(count_parameters > 0) {
      47                 :          4 :         SSRC_UNIQUE_PTR<CallParameter> param;
      48                 :            : 
      49         [ +  - ]:          2 :         parameters.reserve(count_parameters);
      50                 :            : 
      51         [ +  - ]:          2 :         lua_pushnil(state);
      52   [ +  -  +  + ]:          6 :         while(lua_next(state, -2)) {
      53                 :            :           const unsigned int min_size =
      54         [ +  - ]:          2 :             get_field<int>(1, state, -1, "min_size");
      55                 :            :           const unsigned int max_size =
      56         [ +  - ]:          2 :             get_field<int>(128, state, -1, "max_size");
      57                 :            :           const bool multi_value =
      58         [ +  - ]:          2 :             get_field<bool>(false, state, -1, "multi_value");
      59         [ +  - ]:          2 :           const bool optional = get_field<bool>(false, state, -1, "optional");
      60                 :            :           const string && pattern =
      61   [ +  -  +  - ]:          4 :             get_field<string>(state, -1, "matches", "");
      62                 :            : 
      63   [ +  -  +  - ]:          2 :           if(pattern.empty()) {
      64                 :            :             param.reset(new CallParameter(lua_tostring(state, -2),
      65                 :            :                                           min_size, max_size, multi_value,
      66   [ +  -  +  -  :          2 :                                           optional));
          +  -  +  -  +  
                -  +  - ]
      67                 :            :           } else {
      68                 :            :             param.reset(new PatternParameter(lua_tostring(state, -2), pattern,
      69                 :            :                                              min_size, max_size, multi_value,
      70   [ #  #  #  #  :          0 :                                              optional));
          #  #  #  #  #  
                #  #  # ]
      71                 :            :           }
      72                 :            : 
      73   [ +  -  +  - ]:          2 :           parameters.push_back(param.release());
      74                 :            : 
      75         [ +  - ]:          2 :           lua_pop(state, 1);
      76                 :            :         }
      77                 :            :       }
      78                 :            :     }
      79                 :            : 
      80                 :          2 :     lua_pop(state, 1);
      81                 :          2 :   }
      82                 :            : }
      83                 :            : 
      84                 :          2 : void Service::transition(State state) {
      85      [ +  +  - ]:          2 :   switch(state) {
      86                 :            :   case Starting:
      87                 :          1 :     state = Started;
      88                 :          1 :     break;
      89                 :            :   case Stopping:
      90                 :          1 :     state = Stopped;
      91                 :          1 :     break;
      92                 :            :   default:
      93                 :          0 :     break;
      94                 :            :   }
      95                 :            : 
      96                 :          2 :   super::transition(state);
      97                 :          2 : }
      98                 :            : 
      99                 :          1 : inline void Service::do_call(const WebServiceCall & wc,
     100                 :            :                              const MessageInfo & msginfo)
     101                 :            : {
     102                 :            :   using NS_SSRC_WSPR_FCGI::parameter_map;
     103                 :          1 :   call_map_type::const_iterator call_it = _call_map.find(wc.call);
     104                 :            : 
     105         [ +  - ]:          1 :   if(call_it != _call_map.end()) {
     106                 :          1 :     const CallEntry & call = *(call_it->second);
     107                 :          1 :     lua_rawgeti(_lua_state, LUA_REGISTRYINDEX, call.ref);
     108                 :            : 
     109   [ -  +  #  #  :          1 :     if(!call.requires_session || wc.session) {
                   +  - ]
     110                 :          1 :       int nargs = 0;
     111                 :            : 
     112         [ +  - ]:          1 :       if(!call.parameters.empty()) {
     113                 :          1 :         lua_newtable(_lua_state);
     114                 :            :         typedef
     115                 :            :           std::pair<parameter_map::const_iterator,parameter_map::const_iterator>
     116                 :            :           parameter_range;
     117                 :          1 :         parameter_range p;
     118                 :            : 
     119                 :            :         // Set parameters
     120         [ +  + ]:          2 :          for(parameter_sequence::const_iterator param =
     121                 :          1 :               call.parameters.begin(), end = call.parameters.end();
     122                 :            :             param != end; ++param)
     123                 :            :         {
     124         [ +  - ]:          1 :           if(param->multi_value) {
     125                 :          1 :             p = wc.parameters.equal_range(param->name);
     126                 :            :           } else {
     127                 :          0 :             p.first = wc.parameters.find(param->name);
     128                 :          0 :             p.second = wc.parameters.end();
     129                 :            :           }
     130                 :            : 
     131         [ +  - ]:          1 :           if(p.first != p.second) {
     132         [ +  - ]:          1 :             if(param->multi_value) {
     133                 :          1 :               int pos = 0;
     134                 :          1 :               lua_newtable(_lua_state);
     135                 :            : 
     136         [ +  + ]:          1 :               while(p.first != p.second) {
     137         [ +  - ]:          1 :                 if(param->validate(p.first->second)) {
     138                 :          1 :                   lua_pushstring(_lua_state, p.first->second.c_str());
     139                 :          1 :                   lua_rawseti(_lua_state, -2, ++pos);
     140                 :            :                 } else {
     141         [ #  # ]:          0 :                   string error("invalid parameter value: ");
     142   [ #  #  #  #  :          0 :                   reply_error(error.append(param->name).append(" = ").append(p.first->second), msginfo);
          #  #  #  #  #  
                #  #  # ]
     143                 :            : #ifdef WSPR_DEBUG
     144   [ #  #  #  # ]:          0 :                   std::cerr << error << std::endl;
     145                 :            : #endif
     146         [ #  # ]:          0 :                   lua_pop(_lua_state, 2);
     147                 :            :                   return;
     148                 :            :                 }
     149                 :          1 :                 ++p.first;
     150                 :            :               }
     151                 :            :             } else {
     152         [ #  # ]:          0 :               if(param->validate(p.first->second)) {
     153                 :          0 :                  lua_pushstring(_lua_state, p.first->second.c_str());
     154                 :            :               } else {
     155         [ #  # ]:          0 :                 string error("invalid parameter value: ");
     156   [ #  #  #  #  :          0 :                 reply_error(error.append(param->name).append(" = ").append(p.first->second), msginfo);
          #  #  #  #  #  
                #  #  # ]
     157                 :            : #ifdef WSPR_DEBUG
     158   [ #  #  #  # ]:          0 :                 std::cerr << error << std::endl;
     159                 :            : #endif
     160         [ #  # ]:          0 :                 lua_pop(_lua_state, 2);
     161                 :            :                 return;
     162                 :            :               }
     163                 :            :             }
     164                 :            : 
     165                 :          1 :             lua_setfield(_lua_state, -2, param->name.c_str());
     166         [ #  # ]:          0 :           } else if(!param->optional) {
     167                 :          0 :             lua_pop(_lua_state, 2);
     168         [ #  # ]:          0 :             if(!call.one_way) {
     169         [ #  # ]:          0 :               string error("missing parameter: ");
     170   [ #  #  #  #  :          0 :               reply_error(error.append(param->name), msginfo);
                   #  # ]
     171                 :            : #ifdef WSPR_DEBUG
     172   [ #  #  #  # ]:          0 :               std::cerr << error << std::endl;
     173                 :            : #endif
     174                 :            :             }
     175                 :          0 :             return;
     176                 :            :           }
     177                 :            :         }
     178                 :            : 
     179                 :          1 :         nargs = 1;
     180                 :            :       }
     181                 :            : 
     182                 :            :       /*
     183                 :            :         TODO: Populate module context (use separate function).  If
     184                 :            :         modules can perform asynchronous calls completed via
     185                 :            :         continuations, then we'll need a new module context instance
     186                 :            :         for each module call, allowing the module call to hold onto
     187                 :            :         the context for its continuation.
     188                 :            :        */
     189         [ +  - ]:          2 :       MessageResponse response(wc.session, new NS_SSRC_WSPR_UTILITY::Properties);
     190                 :          1 :       _context.start_call(&wc, &msginfo, &response);
     191                 :            : 
     192   [ +  -  -  + ]:          1 :       if(lua_pcall(_lua_state, nargs, 0, 0) != 0) {
     193                 :            : #ifdef WSPR_DEBUG
     194   [ #  #  #  #  :          0 :         std::cerr << "ERROR!  " << lua_tostring(_lua_state, -1) << std::endl;
             #  #  #  # ]
     195                 :            : #endif
     196         [ #  # ]:          0 :         lua_pop(_lua_state, 1);
     197                 :            :         // TODO: handle error somehow.
     198                 :            :       }
     199                 :            : 
     200                 :          1 :       _context.end_call();
     201                 :            : 
     202         [ +  - ]:          1 :       if(!call.one_way) {
     203                 :            : #ifdef WSPR_DEBUG
     204         [ +  - ]:          1 :         std::cerr << "RESPONSE:\n"
     205   [ +  -  +  -  :          3 :                   << NS_SSRC_WSPR_UTILITY::to_string(*response.template_data)
                   +  - ]
     206         [ +  - ]:          1 :                   << std::endl;
     207                 :            : #endif
     208         [ +  - ]:          1 :         _caller.reply<CallResponse>(msginfo.sender(), msginfo.token(), response, Message::FIFOSelfDiscard);
     209                 :            :       }
     210                 :            : 
     211         [ #  # ]:          0 :     } else if(!call.one_way) {
     212         [ #  # ]:          0 :       string error("valid session required");
     213         [ #  # ]:          0 :       reply_error(error, msginfo);
     214                 :            : #ifdef WSPR_DEBUG
     215   [ #  #  #  # ]:          0 :       std::cerr << error << std::endl;
     216                 :            : #endif
     217                 :            :     }
     218                 :            :   }
     219                 :            : }
     220                 :            : 
     221                 :          1 : void Service::load_module(const string & module_dir, const string & module)
     222                 :            :   SSRC_DECL_THROW(LoadError)
     223                 :            : {
     224                 :          2 :   boost::filesystem::path module_path(module_dir);
     225   [ +  -  +  - ]:          2 :   string module_file(module);
     226                 :          1 :   ModuleEntry module_entry;
     227                 :            : 
     228         [ +  - ]:          1 :   module_file.append(".lua");
     229         [ +  - ]:          1 :   module_path /= module_file;
     230                 :            : 
     231                 :            :   // We don't pop the error string because error is fatal.
     232   [ +  -  +  -  :          1 :   if(luaL_dofile(_lua_state, module_path.string().c_str()))
          +  -  +  -  -  
                +  -  + ]
     233   [ #  #  #  #  :          0 :     throw LoadError(lua_tostring(_lua_state, -1));
                   #  # ]
     234                 :            : 
     235   [ +  -  +  - ]:          1 :   lua_getglobal(_lua_state, module.c_str());
     236         [ +  - ]:          1 :   lua_getfield(_lua_state, -1, "load");
     237                 :            : 
     238         [ +  - ]:          1 :   lua_pushvalue(_lua_state, -2);
     239         [ +  - ]:          1 :   lua_pushlightuserdata(_lua_state, &_context);
     240                 :            : 
     241   [ +  -  -  + ]:          1 :   if(lua_pcall(_lua_state, 2, 0, 0) != 0)
     242   [ #  #  #  #  :          0 :     throw LoadError(lua_tostring(_lua_state, -1));
                   #  # ]
     243                 :            : 
     244         [ +  - ]:          1 :   lua_getfield(_lua_state, -1, "unload");
     245   [ +  -  -  + ]:          1 :   if(!lua_isfunction(_lua_state, -1))
     246   [ #  #  #  # ]:          0 :     throw LoadError("unload() is undefined");
     247         [ +  - ]:          1 :   module_entry.unload_ref = luaL_ref(_lua_state, LUA_REGISTRYINDEX);
     248                 :            : 
     249         [ +  - ]:          1 :   lua_getfield(_lua_state, -1, "ws_methods");
     250                 :            : 
     251   [ +  -  +  - ]:          1 :   if(lua_istable(_lua_state, -1)) {
     252         [ +  - ]:          1 :     lua_pushnil(_lua_state);
     253   [ +  -  +  + ]:          4 :     while(lua_next(_lua_state, -2)) {
     254   [ +  -  +  - ]:          2 :       if(lua_istable(_lua_state, -1)) {
     255                 :            : 
     256         [ +  - ]:          2 :         lua_getfield(_lua_state, -1, "call");
     257                 :            : 
     258   [ +  -  +  - ]:          2 :         if(lua_isfunction(_lua_state, -1)) {
     259   [ +  -  +  -  :          4 :           string call_name = lua_tostring(_lua_state, -3);
                   +  - ]
     260   [ +  -  +  -  :          4 :           SSRC_UNIQUE_PTR<CallEntry> call_entry(new CallEntry(luaL_ref(_lua_state, LUA_REGISTRYINDEX)));
          +  -  +  -  +  
                      - ]
     261                 :            : 
     262   [ +  -  +  - ]:          2 :           call_entry->one_way = get_field<bool>(false, _lua_state, -1, "one_way");
     263                 :            : 
     264   [ +  -  +  + ]:          2 :           if(call_entry->one_way) {
     265   [ +  -  +  -  :          1 :             WS_ONE_WAY_CALL(call_name, &Service::do_call);
             +  -  +  - ]
     266                 :            :           } else {
     267   [ +  -  +  -  :          1 :             WS_TWO_WAY_CALL(call_name, &Service::do_call);
             +  -  +  - ]
     268                 :            :           }
     269                 :            : 
     270         [ +  - ]:          2 :           call_entry->requires_session =
     271         [ +  - ]:          2 :             get_field<bool>(true, _lua_state, -1, "requires_session");
     272                 :            : 
     273   [ +  -  +  - ]:          2 :           get_parameters(_lua_state, -1, call_entry->parameters);
     274                 :            : 
     275   [ +  -  +  -  :          2 :           if(!_call_map.insert(call_name, call_entry.release()).second) {
                   -  + ]
     276         [ #  # ]:          0 :             string msg("duplicate method name: ");
     277   [ #  #  #  # ]:          0 :             throw LoadError(msg.append(call_name));
     278                 :            :           }
     279                 :            :         } else {
     280         [ #  # ]:          0 :           lua_pop(_lua_state, 1);
     281                 :            :         }
     282                 :            :       }
     283                 :            : 
     284         [ +  - ]:          2 :       lua_pop(_lua_state, 1);
     285                 :            :     }
     286                 :            :   }
     287                 :            : 
     288         [ +  - ]:          1 :   lua_pop(_lua_state, 1);
     289         [ +  - ]:          1 :   module_entry.module_ref = luaL_ref(_lua_state, LUA_REGISTRYINDEX);
     290         [ +  - ]:          1 :   _modules.push_back(module_entry);
     291                 :          1 : }
     292                 :            : 
     293                 :          1 : void Service::load_modules(const string & module_dir,
     294                 :            :                            const module_list & modules)
     295                 :            :   SSRC_DECL_THROW(LoadError)
     296                 :            : {
     297                 :          2 :   boost::filesystem::path lua_path(module_dir);
     298                 :            : 
     299         [ +  - ]:          1 :   lua_path /= "?.lua;";
     300                 :            : 
     301         [ +  - ]:          1 :   luaL_openlibs(_lua_state);
     302                 :            : 
     303                 :            :   /* Set package.path to module_dir/?.lua. */
     304         [ +  - ]:          1 :   lua_getglobal(_lua_state, "package");
     305         [ +  - ]:          1 :   lua_getfield(_lua_state, -1, "path");
     306                 :            : 
     307   [ +  -  +  - ]:          2 :   string lua_path_str = lua_path.string();
     308   [ +  -  +  - ]:          1 :   lua_path_str.append(lua_tostring(_lua_state, -1));
     309   [ +  -  +  - ]:          1 :   lua_pushstring(_lua_state, lua_path_str.c_str());
     310         [ +  - ]:          1 :   lua_setfield(_lua_state, -3, "path");
     311         [ +  - ]:          1 :   lua_pop(_lua_state, 2);
     312                 :            : 
     313   [ +  -  +  -  :          2 :   for(module_list::const_iterator it = modules.begin(), end = modules.end();
          +  -  +  -  +  
                      + ]
     314                 :            :       it != end; ++it)
     315                 :            :   {
     316   [ +  -  +  - ]:          1 :     load_module(module_dir, *it);
     317                 :            :   }
     318                 :          1 : }
     319                 :            : 
     320                 :          1 : void Service::unload_module(const ModuleEntry & module) {
     321                 :          1 :   lua_rawgeti(_lua_state, LUA_REGISTRYINDEX, module.unload_ref);
     322                 :          1 :   lua_rawgeti(_lua_state, LUA_REGISTRYINDEX, module.module_ref);
     323                 :            : 
     324         [ -  + ]:          1 :   if(lua_pcall(_lua_state, 1, 0, 0) != 0) {
     325                 :            : #ifdef WSPR_DEBUG
     326                 :          0 :     std::cerr << "unload error: " << lua_tostring(_lua_state, -1)
     327                 :          0 :               << std::endl;
     328                 :            : #endif
     329                 :          0 :     lua_pop(_lua_state, 1);
     330                 :            :   }
     331                 :            : 
     332                 :          1 :   luaL_unref(_lua_state, LUA_REGISTRYINDEX, module.unload_ref);
     333                 :          1 :   luaL_unref(_lua_state, LUA_REGISTRYINDEX, module.module_ref);
     334                 :          1 : }
     335                 :            : 
     336                 :          1 : void Service::unload_modules() {
     337                 :            :   // Unload modules in reverse order from which they were loaded.
     338         [ +  + ]:          3 :   for(module_entry_list::const_reverse_iterator it = _modules.rbegin(),
     339                 :          1 :         end = _modules.rend(); it != end; ++it)
     340                 :            :   {
     341                 :          1 :     unload_module(*it);
     342                 :            :   }
     343                 :            : 
     344                 :            :   // If we're going to support unloading a single module, then
     345                 :            :   // we have to store the module reference in CallEntry so we
     346                 :            :   // can unreference all calls that belong to the module.
     347                 :            :   // For now, however, we simply unload everything at once,
     348                 :            :   // unreferencing the modules first and then the methods.
     349         [ +  + ]:          4 :   for(call_map_type::const_iterator it = _call_map.begin(),
     350                 :          1 :         end = _call_map.end(); it != end; ++it)
     351                 :            :   {
     352                 :          2 :     luaL_unref(_lua_state, LUA_REGISTRYINDEX, it->second->ref);
     353                 :            :   }
     354                 :            : 
     355                 :          1 :   _modules.clear();
     356                 :          1 :   _call_map.clear();
     357                 :          1 : }
     358                 :            : 
     359   [ +  -  +  -  :         15 : __END_NS_SSRC_WSPR_WS_LUA
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
             +  -  +  - ]