1 /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2  *
3  * This library is open source and may be redistributed and/or modified under
4  * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5  * (at your option) any later version.  The full license is in LICENSE file
6  * included with this distribution, and on the openscenegraph.org website.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * OpenSceneGraph Public License for more details.
12 */
13 
14 #include "RestHttpDevice.hpp"
15 #include <OpenThreads/Thread>
16 #include <osg/ValueObject>
17 #include <osgDB/FileUtils>
18 #include "request_handler.hpp"
19 
20 namespace RestHttp {
21 
22 
23 class UserEventRequestHandler : public RestHttpDevice::RequestHandler {
24 public:
UserEventRequestHandler()25     UserEventRequestHandler() : RestHttpDevice::RequestHandler("/user-event") {}
operator ()(const std::string & request_path,const std::string & full_request_path,const Arguments & arguments,http::server::reply & reply)26     virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const Arguments& arguments, http::server::reply& reply)
27     {
28         OSG_INFO << "RestHttpDevice :: handling request " << full_request_path << " as user-event" << std::endl;
29 
30         osg::ref_ptr<osgGA::Event> event = new osgGA::Event();
31         event->setName(full_request_path);
32         event->setTime(getDevice()->getEventQueue()->getTime());
33 
34         for(Arguments::const_iterator i = arguments.begin(); i != arguments.end(); ++i)
35         {
36             event->setUserValue(i->first,i->second);
37         }
38         getDevice()->getEventQueue()->addEvent(event.get());
39 
40         return sendOkReply(reply);
41     }
42 
describeTo(std::ostream & out) const43     virtual void describeTo(std::ostream& out) const
44     {
45         out << getRequestPath() << ": fall-through request-handler, catches all requests w/o registered handler and report them to the console" << std::dec;
46     }
47 };
48 
49 class HomeRequestHandler : public RestHttpDevice::RequestHandler {
50 public:
HomeRequestHandler()51     HomeRequestHandler()
52         : RestHttpDevice::RequestHandler("/home")
53     {
54     }
55 
operator ()(const std::string & request_path,const std::string & full_request_path,const Arguments & arguments,http::server::reply & reply)56     virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const Arguments& arguments, http::server::reply& reply)
57     {
58         double time = getLocalTime(arguments, reply);
59         getDevice()->getEventQueue()->keyPress(' ', time);
60         getDevice()->getEventQueue()->keyRelease(' ', time);
61 
62         return sendOkReply(reply);
63     }
64 
describeTo(std::ostream & out) const65     virtual void describeTo(std::ostream& out) const
66     {
67         out << getRequestPath() << ": sets the mouse-input-range arguments: 'x_min','y_min', 'x_max' and 'y_max'" << std::dec;
68     }
69 };
70 
71 class SetMouseInputRangeRequestHandler : public RestHttpDevice::RequestHandler {
72 public:
SetMouseInputRangeRequestHandler()73     SetMouseInputRangeRequestHandler()
74         : RestHttpDevice::RequestHandler("/mouse/set_input_range")
75     {
76     }
77 
operator ()(const std::string & request_path,const std::string & full_request_path,const Arguments & arguments,http::server::reply & reply)78     virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const Arguments& arguments, http::server::reply& reply)
79     {
80         int x_min(0), y_min(0), x_max(0), y_max(0);
81 
82         if (   getIntArgument(arguments, "x_min", reply, x_min)
83             && getIntArgument(arguments, "y_min", reply, y_min)
84             && getIntArgument(arguments, "x_max", reply, x_max)
85             && getIntArgument(arguments, "y_max", reply, y_max))
86         {
87             getDevice()->getEventQueue()->setMouseInputRange(x_min, y_min, x_max, y_max);
88         }
89 
90         return sendOkReply(reply);
91     }
92 
describeTo(std::ostream & out) const93     virtual void describeTo(std::ostream& out) const
94     {
95         out << getRequestPath() << ": sets the mouse-input-range arguments: 'x_min','y_min', 'x_max' and 'y_max'" << std::dec;
96     }
97 };
98 
99 
100 
101 
102 
103 class KeyCodeRequestHandler : public RestHttpDevice::RequestHandler {
104 public:
KeyCodeRequestHandler(bool handle_key_press)105     KeyCodeRequestHandler(bool handle_key_press)
106         : RestHttpDevice::RequestHandler(std::string("/key/") + ((handle_key_press) ? "press" : "release"))
107         , _handleKeyPress(handle_key_press)
108     {
109     }
110 
operator ()(const std::string & request_path,const std::string & full_request_path,const Arguments & arguments,http::server::reply & reply)111     virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const Arguments& arguments, http::server::reply& reply)
112     {
113         int keycode(0);
114 
115         if (getHexArgument(arguments, "code", reply, keycode))
116         {
117             if (_handleKeyPress)
118                 getDevice()->getEventQueue()->keyPress(keycode, getLocalTime(arguments, reply));
119             else
120                 getDevice()->getEventQueue()->keyRelease(keycode, getLocalTime(arguments, reply));
121         }
122 
123         return sendOkReply(reply);
124     }
125 
describeTo(std::ostream & out) const126     virtual void describeTo(std::ostream& out) const
127     {
128         out << getRequestPath() << ": send KEY_" << (_handleKeyPress ? "DOWN" : "UP") <<", using hex-argument 'code' as keycode" << std::dec;
129     }
130 private:
131     bool _handleKeyPress;
132 };
133 
134 
135 class MouseMotionRequestHandler : public RestHttpDevice::RequestHandler {
136 public:
MouseMotionRequestHandler()137     MouseMotionRequestHandler()
138         : RestHttpDevice::RequestHandler("/mouse/motion")
139     {
140     }
141 
operator ()(const std::string & request_path,const std::string & full_request_path,const Arguments & arguments,http::server::reply & reply)142     virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const Arguments& arguments, http::server::reply& reply)
143     {
144         int x(0),y(0);
145         if (getIntArgument(arguments, "x", reply, x) && getIntArgument(arguments, "y", reply, y))
146         {
147             double time_stamp = getTimeStamp(arguments, reply);
148 
149             if (getDevice()->isNewer(time_stamp))
150             {
151                 //getDevice()->getEventQueue()->mouseMotion(x,y, getLocalTime(time_stamp));
152                 getDevice()->setTargetMousePosition(x,y);
153             }
154         }
155 
156         return sendOkReply(reply);
157     }
158 
describeTo(std::ostream & out) const159     virtual void describeTo(std::ostream& out) const
160     {
161         out << getRequestPath() << ": send mouse motion using arguments 'x' and 'y' as coordinates" << std::dec;
162     }
163 private:
164 
165 };
166 
167 
168 class MouseButtonRequestHandler : public RestHttpDevice::RequestHandler {
169 public:
170     enum Mode { PRESS, RELEASE, DOUBLE_PRESS};
171 
MouseButtonRequestHandler(Mode mode)172     MouseButtonRequestHandler(Mode mode)
173         : RestHttpDevice::RequestHandler("")
174         , _mode(mode)
175     {
176         switch(mode) {
177             case PRESS:
178                 setRequestPath("/mouse/press");
179                 break;
180             case RELEASE:
181                 setRequestPath("/mouse/release");
182                 break;
183             case DOUBLE_PRESS:
184                 setRequestPath("/mouse/doublepress");
185                 break;
186         }
187     }
188 
operator ()(const std::string & request_path,const std::string & full_request_path,const Arguments & arguments,http::server::reply & reply)189     virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const Arguments& arguments, http::server::reply& reply)
190     {
191         int x(0),y(0), button(0);
192 
193         if (getIntArgument(arguments, "x", reply, x)
194             && getIntArgument(arguments, "y", reply, y)
195             && getIntArgument(arguments, "button", reply, button))
196         {
197             getDevice()->setTargetMousePosition(x,y, true);
198             switch (_mode) {
199                 case PRESS:
200                     getDevice()->getEventQueue()->mouseButtonPress(x,y, button, getLocalTime(arguments, reply));
201                     break;
202                 case RELEASE:
203                     getDevice()->getEventQueue()->mouseButtonRelease(x,y, button, getLocalTime(arguments, reply));
204                     break;
205                 case DOUBLE_PRESS:
206                     getDevice()->getEventQueue()->mouseDoubleButtonPress(x,y, button, getLocalTime(arguments, reply));
207                     break;
208             }
209         }
210 
211         return sendOkReply(reply);
212     }
213 
describeTo(std::ostream & out) const214     virtual void describeTo(std::ostream& out) const
215     {
216         out << getRequestPath() << ": send mouse ";
217         switch (_mode) {
218             case PRESS:
219                 out << "press"; break;
220             case RELEASE:
221                 out << "release"; break;
222             case DOUBLE_PRESS:
223                 out << "double press"; break;
224         }
225         out << " using arguments 'x', 'y' and 'button' as coordinates" << std::dec;
226     }
227 private:
228     Mode _mode;
229 };
230 
231 
232 
233 
234 
235 
236 class RequestHandlerDispatcherCallback: public http::server::request_handler::Callback {
237 public:
RequestHandlerDispatcherCallback(RestHttpDevice * parent)238     RequestHandlerDispatcherCallback(RestHttpDevice* parent)
239         : http::server::request_handler::Callback()
240         , _parent(parent)
241     {
242     }
243 
244     virtual bool operator()(const std::string& request_path, http::server::reply& rep);
245 
applyTemplateVars(const std::string & txt)246     virtual std::string applyTemplateVars(const std::string& txt) { return txt; }
247 private:
248     RestHttpDevice* _parent;
249 };
250 
251 
operator ()(const std::string & request_path,http::server::reply & reply)252 bool RequestHandlerDispatcherCallback::operator()(const std::string& request_path, http::server::reply& reply)
253 {
254     return _parent->handleRequest(request_path, reply);
255 }
256 
257 }
258 
RestHttpDevice(const std::string & listening_address,const std::string & listening_port,const std::string & doc_root)259 RestHttpDevice::RestHttpDevice(const std::string& listening_address, const std::string& listening_port, const std::string& doc_root)
260     : osgGA::Device()
261     , OpenThreads::Thread()
262     , _server(listening_address, listening_port, osgDB::findDataFile(doc_root), std::max(OpenThreads::GetNumberOfProcessors() - 1, 1))
263     , _serverAddress(listening_address)
264     , _serverPort(listening_port)
265     , _documentRoot(doc_root)
266     , _firstEventLocalTimeStamp()
267     , _firstEventRemoteTimeStamp(-1)
268     , _lastEventRemoteTimeStamp(0)
269     , _currentMouseX(0.0f)
270     , _currentMouseY(0.0f)
271     , _targetMouseX(0.0f)
272     , _targetMouseY(0.0f)
273     , _targetMouseChanged(false)
274 {
275     setCapabilities(RECEIVE_EVENTS);
276 
277     OSG_NOTICE << "RestHttpDevice :: listening on " << listening_address << ":" << listening_port << ", document root: " << doc_root << std::endl;
278 
279     if (osgDB::findDataFile(doc_root).empty())
280     {
281         OSG_WARN << "RestHttpDevice :: warning, can't locate document-root '" << doc_root << "'for the http-server, starting anyway" << std::endl;
282     }
283     _server.setCallback(new RestHttp::RequestHandlerDispatcherCallback(this));
284 
285     addRequestHandler(new RestHttp::KeyCodeRequestHandler(false));
286     addRequestHandler(new RestHttp::KeyCodeRequestHandler(true));
287 
288     addRequestHandler(new RestHttp::SetMouseInputRangeRequestHandler());
289     addRequestHandler(new RestHttp::MouseMotionRequestHandler());
290     addRequestHandler(new RestHttp::MouseButtonRequestHandler(RestHttp::MouseButtonRequestHandler::PRESS));
291     addRequestHandler(new RestHttp::MouseButtonRequestHandler(RestHttp::MouseButtonRequestHandler::RELEASE));
292     addRequestHandler(new RestHttp::MouseButtonRequestHandler(RestHttp::MouseButtonRequestHandler::DOUBLE_PRESS));
293 
294     addRequestHandler(new RestHttp::HomeRequestHandler());
295 
296     addRequestHandler(new RestHttp::UserEventRequestHandler());
297 
298     // start the thread
299     start();
300 }
301 
302 
run()303 void RestHttpDevice::run()
304 {
305     _server.run();
306 }
307 
308 
~RestHttpDevice()309 RestHttpDevice::~RestHttpDevice()
310 {
311     _server.stop();
312     join();
313 }
314 
315 
addRequestHandler(RequestHandler * handler)316 void RestHttpDevice::addRequestHandler(RequestHandler* handler)
317 {
318     if (handler)
319     {
320         _map.insert(std::make_pair(handler->getRequestPath(), handler));
321         handler->setDevice(this);
322     }
323 }
324 
325 
parseArguments(const std::string request_path,RequestHandler::Arguments & arguments)326 void RestHttpDevice::parseArguments(const std::string request_path, RequestHandler::Arguments& arguments)
327 {
328     std::size_t pos = request_path.find('?');
329     if (pos == std::string::npos)
330         return;
331 
332     std::vector<std::string> list;
333     osgDB::split(request_path.substr(pos+1, std::string::npos), list, '&');
334     for(std::vector<std::string>::iterator i = list.begin(); i != list.end(); ++i)
335     {
336         std::vector<std::string> sub_list;
337         osgDB::split(*i, sub_list, '=');
338         if (sub_list.size() == 2)
339             arguments[sub_list[0]] = sub_list[1];
340         else if (sub_list.size() == 1)
341             arguments[sub_list[0]] = "";
342     }
343 }
344 
345 
handleRequest(const std::string & in_request_path,http::server::reply & reply)346 bool RestHttpDevice::handleRequest(const std::string& in_request_path,  http::server::reply& reply)
347 {
348     std::string request_path = in_request_path.substr(0, in_request_path.find('?'));
349     request_path += "/";
350     RequestHandler::Arguments arguments;
351     bool arguments_parsed(false);
352 
353     std::size_t pos(std::string::npos);
354     bool handled(false);
355     do {
356         pos = request_path.find_last_of('/', pos-1);
357         if (pos != std::string::npos)
358         {
359             std::string mangled_path = request_path.substr(0, pos);
360 
361             std::pair<RequestHandlerMap::iterator,RequestHandlerMap::iterator> range = _map.equal_range(mangled_path);
362             if (!arguments_parsed && (range.first != range.second))
363             {
364                 // parse arguments
365                 parseArguments(in_request_path, arguments);
366                 arguments_parsed = true;
367             }
368             for(RequestHandlerMap::iterator i = range.first; i != range.second; ++i)
369             {
370                 if (i->second->operator()(mangled_path, in_request_path, arguments, reply) && !handled)
371                     handled = true;
372             }
373 
374         }
375     } while ((pos != std::string::npos) && (pos > 0) && !handled);
376 
377     return handled;
378 }
379 
380 
describeTo(std::ostream & out) const381 void RestHttpDevice::describeTo(std::ostream& out) const
382 {
383     out << "RestHttpDevice :: Server:        " << _serverAddress << std::endl;
384     out << "RestHttpDevice :: Port:          " << _serverPort << std::endl;
385     out << "RestHttpDevice :: Document-Root: " << _documentRoot << std::endl;
386     out << std::endl;
387 
388     for(RequestHandlerMap::const_iterator i = _map.begin(); i != _map.end(); ++i)
389     {
390         const RequestHandler* handler(i->second.get());
391         out << "RestHttpDevice :: ";
392         handler->describeTo(out);
393         out << std::endl;
394     }
395 
396 }
397 
398