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
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - ]
|