70 for (iterator it = begin(); it != end(); ++it) {
71 SGSharedPtr<URIHandler> handler = *it;
73 if (0 == uri.find(handler->getUri()))
return handler;
75 return SGSharedPtr<URIHandler>();
94 static inline string NotNull(
const char * cp,
size_t len = string::npos)
96 if ( NULL == cp || 0 == len)
return string(
"");
97 if (string::npos == len)
return string(cp);
98 return string(cp, len);
110 Method = NotNull(connection->request_method);
120 using namespace simgear::strutils;
122 for (string_list::iterator it = pairs.begin(); it != pairs.end(); ++it) {
124 if (nvp.size() != 2)
continue;
128 for (
int i = 0;
i < connection->num_headers;
i++)
129 HeaderVariables[connection->http_headers[
i].name] = connection->http_headers[
i].value;
131 Content = NotNull(connection->content, connection->content_len);
146 int max = s.length();
148 for (
int i = 0;
i < max;
i++) {
151 }
else if (s[
i] ==
'%' &&
i + 2 < max && isxdigit(s[
i + 1]) && isxdigit(s[
i + 2])) {
153 a = isdigit(s[
i]) ? s[
i] -
'0' : toupper(s[
i]) -
'A' + 10;
155 b = isdigit(s[
i]) ? s[
i] -
'0' : toupper(s[
i]) -
'A' + 10;
156 r += (char) (a * 16 + b);
185 void bind()
override;
186 void init()
override;
188 void update(
double dt)
override;
199 return _uriHandler.findHandler(uri);
205 int poll(
struct mg_connection * connection);
206 int auth(
struct mg_connection * connection);
207 int request(
struct mg_connection * connection);
208 int onConnect(
struct mg_connection * connection);
209 void close(
struct mg_connection * connection);
211 static int staticRequestHandler(
struct mg_connection *, mg_event event);
213 struct mg_server *_server;
214 SGPropertyNode_ptr _configNode;
216 typedef int (
MongooseHttpd::*handler_t)(
struct mg_connection *);
230 virtual void close(
struct mg_connection * connection) = 0;
231 virtual int poll(
struct mg_connection * connection) = 0;
232 virtual int request(
struct mg_connection * connection) = 0;
233 virtual int onConnect(
struct mg_connection * connection) {
return 0;}
234 virtual void write(
const char * data,
size_t len)
265 virtual void close(
struct mg_connection * connection);
266 virtual int poll(
struct mg_connection * connection);
267 virtual int request(
struct mg_connection * connection);
270 SGSharedPtr<URIHandler> _handler;
283 virtual void close(
struct mg_connection * connection);
284 virtual int poll(
struct mg_connection * connection);
285 virtual int request(
struct mg_connection * connection);
286 virtual int onConnect(
struct mg_connection * connection);
291 MongooseWebsocketWriter(
struct mg_connection * connection)
292 : _connection(connection)
296 virtual int writeToWebsocket(
int opcode,
const char * data,
size_t len)
298 return mg_websocket_write(_connection, opcode, data, len);
301 struct mg_connection * _connection;
303 Websocket * _websocket;
308 if (connection->connection_param)
return static_cast<MongooseConnection*
>(connection->connection_param);
313 connection->connection_param = c;
321 SG_LOG(SG_NETWORK, SG_INFO,
"RegularConnection::request for " <<
request.Uri);
325 if (!_handler.valid()) {
332 response.
Header[
"Server"] =
"FlightGear/" FLIGHTGEAR_VERSION
" Mongoose/" MONGOOSE_VERSION;
333 response.
Header[
"Connection"] =
"keep-alive";
334 response.
Header[
"Cache-Control"] =
"no-cache";
337 time_t now = time(NULL);
338 strftime(buf,
sizeof(buf),
"%a, %d %b %Y %H:%M:%S GMT", gmtime(&now));
339 response.
Header[
"Date"] = buf;
344 bool done = _handler->handleRequest(
request, response,
this);
346 mg_send_status(connection, response.
StatusCode);
347 for (HTTPResponse::Header_t::const_iterator it = response.
Header.begin(); it != response.
Header.end(); ++it) {
348 const string name = it->first;
349 const string value = it->second;
350 if (
name.empty() || value.empty())
continue;
351 mg_send_header(connection,
name.c_str(), value.c_str());
353 if (done || !response.
Content.empty()) {
354 SG_LOG(SG_NETWORK, SG_INFO,
355 "RegularConnection::request() responding " << response.
Content.length() <<
" Bytes, done=" << done);
356 mg_send_data(connection, response.
Content.c_str(), response.
Content.length());
358 return done ? MG_TRUE : MG_MORE;
364 if (!_handler.valid())
return MG_FALSE;
366 return _handler->poll(
this) ? MG_TRUE : MG_MORE;
378 if ( NULL != _websocket) _websocket->close();
389 if ( NULL != _websocket) {
390 MongooseWebsocketWriter writer(connection);
391 _websocket->poll(writer);
400 SG_LOG(SG_NETWORK, SG_INFO,
"WebsocketConnection::connect for " <<
request.Uri);
401 if ( NULL == _websocket) _websocket =
_httpd->newWebsocket(
request.Uri);
402 if ( NULL == _websocket) {
403 SG_LOG(SG_NETWORK, SG_WARN,
"httpd: unhandled websocket uri: " <<
request.Uri);
413 if ((connection->wsbits & 0x0f) >= 0x8) {
415 if ((connection->wsbits & 0x0f) == WEBSOCKET_OPCODE_PING) {
416 mg_websocket_write(connection, WEBSOCKET_OPCODE_PONG, NULL, 0);
422 SG_LOG(SG_NETWORK, SG_DEBUG,
"WebsocketConnection::request for " <<
request.Uri);
424 if ( NULL == _websocket) {
425 SG_LOG(SG_NETWORK, SG_ALERT,
"httpd: unhandled websocket uri: " <<
request.Uri);
429 MongooseWebsocketWriter writer(connection);
430 _websocket->handleRequest(
request, writer);
435 : _server(NULL), _configNode(configNode)
441 mg_destroy_server(&_server);
446 SGPropertyNode_ptr n = _configNode->getNode(
"uri-handler");
450 if (!(uri = n->getStringValue(
"screenshot")).empty()) {
451 SG_LOG(SG_NETWORK, SG_INFO,
"httpd: adding screenshot uri handler at " << uri);
455 if (!(uri = n->getStringValue(
"property")).empty()) {
456 SG_LOG(SG_NETWORK, SG_INFO,
"httpd: adding property uri handler at " << uri);
460 if (!(uri = n->getStringValue(
"json")).empty()) {
461 SG_LOG(SG_NETWORK, SG_INFO,
"httpd: adding json uri handler at " << uri);
465 if (!(uri = n->getStringValue(
"pkg")).empty()) {
466 SG_LOG(SG_NETWORK, SG_INFO,
"httpd: adding pkg uri handler at " << uri);
470 if (!(uri = n->getStringValue(
"flighthistory")).empty()) {
471 SG_LOG(SG_NETWORK, SG_INFO,
"httpd: adding flighthistory uri handler at " << uri);
475 if (!(uri = n->getStringValue(
"run")).empty()) {
476 SG_LOG(SG_NETWORK, SG_INFO,
"httpd: adding run uri handler at " << uri);
480 if (!(uri = n->getStringValue(
"navdb")).empty()) {
481 SG_LOG(SG_NETWORK, SG_INFO,
"httpd: adding navdb uri handler at " << uri);
486 _server = mg_create_server(
this, MongooseHttpd::staticRequestHandler);
488 n = _configNode->getNode(
"options");
492 string docRoot = n->getStringValue(
"document-root", fgRoot.c_str());
493 if (docRoot[0] !=
'/') docRoot.insert(0,
"/").insert(0, fgRoot);
495 mg_set_option(_server,
"document_root", docRoot.c_str());
497 mg_set_option(_server,
"listening_port", n->getStringValue(
"listening-port",
"8080").c_str());
500 string rewrites = n->getStringValue(
"url-rewrites",
"");
501 string_list rwl = simgear::strutils::split(rewrites,
",");
502 rwl.push_back(
string(
"/aircraft-dir/=") +
fgGetString(
"/sim/aircraft-dir") +
"/" );
503 rwl.push_back(
string(
"/fg-home/=") +
fgGetString(
"/sim/fg-home") +
"/" );
504 rwl.push_back(
string(
"/fg-root/=") +
fgGetString(
"/sim/fg-root") +
"/" );
506 for (string_list::iterator it = rwl.begin(); it != rwl.end(); ++it) {
507 string_list rw_entries = simgear::strutils::split(*it,
"=");
508 if (rw_entries.size() != 2) {
509 SG_LOG(SG_NETWORK, SG_WARN,
"invalid entry '" << *it <<
"' in url-rewrites ignored.");
512 string & lhs = rw_entries[0];
513 string & rhs = rw_entries[1];
514 if (!rewrites.empty()) rewrites.append(1,
',');
515 rewrites.append(lhs).append(1,
'=');
516 SGPath targetPath(rhs);
517 if (targetPath.isAbsolute() ) {
518 rewrites.append(rhs);
521 rewrites.append(fgRoot).append(1,
'/').append(rhs);
524 if (!rewrites.empty()) mg_set_option(_server,
"url_rewrites", rewrites.c_str());
526 mg_set_option(_server,
"enable_directory_listing", n->getStringValue(
"enable-directory-listing",
"yes").c_str());
527 mg_set_option(_server,
"idle_timeout_ms", n->getStringValue(
"idle-timeout-ms",
"30000").c_str());
528 mg_set_option(_server,
"index_files", n->getStringValue(
"index-files",
"index.html").c_str());
529 mg_set_option(_server,
"extra_mime_types", n->getStringValue(
"extra-mime-types",
"").c_str());
530 mg_set_option(_server,
"access_log_file", n->getStringValue(
"access-log-file",
"").c_str());
532 SG_LOG(SG_NETWORK,SG_INFO,
"starting mongoose with these options: ");
533 const char ** optionNames = mg_get_valid_option_names();
534 for(
int i = 0; optionNames[
i] != NULL;
i+= 2 ) {
535 SG_LOG(SG_NETWORK,SG_INFO,
" > " << optionNames[
i] <<
": '" << mg_get_option(_server, optionNames[
i]) <<
"'" );
537 SG_LOG(SG_NETWORK,SG_INFO,
"end of mongoose options.");
540 _configNode->setBoolValue(
"running",
true);
550 _configNode->setBoolValue(
"running",
false);
551 mg_destroy_server(&_server);
553 _propertyChangeObserver.clear();
558 _propertyChangeObserver.check();
559 mg_poll_server(_server, 0);
560 _propertyChangeObserver.uncheck();
563int MongooseHttpd::poll(
struct mg_connection * connection)
565 if ( NULL == connection->connection_param)
return MG_FALSE;
570int MongooseHttpd::auth(
struct mg_connection * connection)
580int MongooseHttpd::request(
struct mg_connection * connection)
585int MongooseHttpd::onConnect(
struct mg_connection * connection)
590void MongooseHttpd::close(
struct mg_connection * connection)
593 c->close(connection);
598 if (uri.find(
"/PropertyListener") == 0) {
599 SG_LOG(SG_NETWORK, SG_INFO,
"new PropertyChangeWebsocket for: " << uri);
601 }
else if (uri.find(
"/PropertyTreeMirror/") == 0) {
602 const auto path = uri.substr(20);
603 SG_LOG(SG_NETWORK, SG_INFO,
"new MirrorPropertyTreeWebsocket for: " << path);
609int MongooseHttpd::staticRequestHandler(
struct mg_connection * connection, mg_event event)
613 return static_cast<MongooseHttpd*
>(connection->server_param)->poll(connection);
616 return static_cast<MongooseHttpd*
>(connection->server_param)->auth(connection);
619 return static_cast<MongooseHttpd*
>(connection->server_param)->request(connection);
622 static_cast<MongooseHttpd*
>(connection->server_param)->close(connection);
634 return static_cast<MongooseHttpd*
>(connection->server_param)->onConnect(connection);
644 if (!configNode.valid())
return NULL;
645 string port = configNode->getStringValue(
"options/listening-port",
"");
646 if (port.empty())
return NULL;
653SGSubsystemMgr::Registrant<MongooseHttpd> registrantMongooseHttpd;
static FGHttpd * createInstance(SGPropertyNode_ptr configNode)
StringMap HeaderVariables
StringMap RequestVariables
std::string remoteAddress
static MongooseConnection * getConnection(MongooseHttpd *httpd, struct mg_connection *connection)
virtual ~MongooseConnection()
virtual void write(const char *data, size_t len)
virtual int request(struct mg_connection *connection)=0
virtual int poll(struct mg_connection *connection)=0
virtual int onConnect(struct mg_connection *connection)
struct mg_connection * _connection
MongooseConnection(MongooseHttpd *httpd)
virtual void close(struct mg_connection *connection)=0
void setConnection(struct mg_connection *connection)
A Helper class to create a HTTPRequest from a mongoose connection struct.
MongooseHTTPRequest(struct mg_connection *connection)
Constructs a HTTPRequest from a mongoose connection struct Copies all fields into STL compatible loca...
static string urlDecode(const string &s)
Decodes a URL encoded string replaces '+' by ' ' replaces nn hexdigits.
A FGHttpd implementation based on mongoose httpd.
static const char * staticSubsystemClassId()
void update(double dt) override
MongooseHttpd(SGPropertyNode_ptr)
Construct a MongooseHttpd object from options in a PropertyNode.
SGSharedPtr< URIHandler > findHandler(const std::string &uri)
Returns a URIHandler for the given uri.
Websocket * newWebsocket(const string &uri)
~MongooseHttpd()
Cleanup et.al.
virtual int request(struct mg_connection *connection)
virtual ~RegularConnection()
RegularConnection(MongooseHttpd *httpd)
virtual void close(struct mg_connection *connection)
virtual int poll(struct mg_connection *connection)
A Helper class for URI Handlers.
SGSharedPtr< URIHandler > findHandler(const std::string &uri)
Find a URI Handler for a given URI.
virtual int request(struct mg_connection *connection)
WebsocketConnection(MongooseHttpd *httpd)
virtual int poll(struct mg_connection *connection)
virtual int onConnect(struct mg_connection *connection)
virtual ~WebsocketConnection()
virtual void close(struct mg_connection *connection)
std::string fgGetString(const char *name, const char *defaultValue)
Get a string value for a property.
std::vector< std::string > string_list
const char * PROPERTY_ROOT
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
std::vector< std::string > split(std::string str, char d)