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