diff options
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/db.cpp | 12 | ||||
-rw-r--r-- | src/mongo/db/dbwebserver.cpp | 764 | ||||
-rw-r--r-- | src/mongo/db/dbwebserver.h | 42 |
3 files changed, 433 insertions, 385 deletions
diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp index ae5ca3128ee..dcdc03b2537 100644 --- a/src/mongo/db/db.cpp +++ b/src/mongo/db/db.cpp @@ -34,6 +34,7 @@ #include <boost/thread/thread.hpp> #include <boost/filesystem/operations.hpp> +#include <boost/shared_ptr.hpp> #include <fstream> #include <iostream> #include <limits> @@ -453,6 +454,14 @@ namespace mongo { // do not want connections to just hang if recovery takes a very long time. server->setupSockets(); + boost::shared_ptr<DbWebServer> dbWebServer; + if (serverGlobalParams.isHttpInterfaceEnabled) { + dbWebServer.reset(new DbWebServer(serverGlobalParams.bind_ip, + serverGlobalParams.port + 1000, + new RestAdminAccess())); + dbWebServer->setupSockets(); + } + // Warn if we detect configurations for multiple registered storage engines in // the same configuration file/environment. if (serverGlobalParams.parsedOpts.hasField("storage")) { @@ -560,7 +569,8 @@ namespace mongo { if (serverGlobalParams.isHttpInterfaceEnabled) { snapshotThread.go(); - boost::thread web(stdx::bind(&webServerThread, new RestAdminAccess())); + invariant(dbWebServer); + boost::thread web(stdx::bind(&webServerListenThread, dbWebServer)); web.detach(); } diff --git a/src/mongo/db/dbwebserver.cpp b/src/mongo/db/dbwebserver.cpp index 68c8ee3dd65..66e9ec6e53d 100644 --- a/src/mongo/db/dbwebserver.cpp +++ b/src/mongo/db/dbwebserver.cpp @@ -50,11 +50,11 @@ #include "mongo/db/db.h" #include "mongo/db/global_environment_experiment.h" #include "mongo/db/instance.h" +#include "mongo/db/operation_context.h" #include "mongo/db/stats/snapshots.h" #include "mongo/util/admin_access.h" #include "mongo/util/md5.hpp" #include "mongo/util/mongoutils/html.h" -#include "mongo/util/net/miniwebserver.h" #include "mongo/util/ramlog.h" #include "mongo/util/version.h" #include "mongo/util/version_reporting.h" @@ -68,6 +68,25 @@ namespace mongo { using namespace html; +namespace { + + void doUnlockedStuff(stringstream& ss) { + // This is in the header already ss << "port: " << port << '\n' + ss << "<pre>"; + ss << mongodVersion() << '\n'; + ss << "git hash: " << gitVersion() << '\n'; + ss << openSSLVersion("OpenSSL version: ", "\n"); + ss << "sys info: " << sysInfo() << '\n'; + ss << "uptime: " << time(0) - serverGlobalParams.started << " seconds\n"; + ss << "</pre>"; + } + + + bool prisort(const Prioritizable* a, const Prioritizable* b) { + return a->priority() < b->priority(); + } + + struct Timing { Timing() { start = timeLocked = 0; @@ -75,254 +94,423 @@ namespace mongo { unsigned long long start, timeLocked; }; - class DbWebServer : public MiniWebServer { - public: - DbWebServer(const string& ip, - int port, - const AdminAccess* webUsers) - : MiniWebServer("admin web console", ip, port), - _webUsers(webUsers) { - WebStatusPlugin::initAll(); + class LogPlugin : public WebStatusPlugin { + public: + LogPlugin() : WebStatusPlugin("Log", 100), _log(0) { + _log = RamLog::get("global"); } - private: - const AdminAccess* _webUsers; // not owned here - - void doUnlockedStuff(stringstream& ss) { - /* this is in the header already ss << "port: " << port << '\n'; */ - ss << "<pre>"; - ss << mongodVersion() << '\n'; - ss << "git hash: " << gitVersion() << '\n'; - ss << openSSLVersion("OpenSSL version: ", "\n"); - ss << "sys info: " << sysInfo() << '\n'; - ss << "uptime: " << time(0)-serverGlobalParams.started << " seconds\n"; - ss << "</pre>"; + virtual void init() {} + + virtual void run(OperationContext* txn, stringstream& ss) { + _log->toHTML(ss); } + RamLog * _log; + }; - void _authorizePrincipal(OperationContext* txn, const UserName& userName) { - Status status = cc().getAuthorizationSession()->addAndAuthorizeUser(txn, userName); - uassertStatusOK(status); + + class FavIconHandler : public DbWebHandler { + public: + FavIconHandler() : DbWebHandler("favicon.ico", 0, false) {} + + virtual void handle(OperationContext* txn, + const char *rq, const std::string& url, BSONObj params, + string& responseMsg, int& responseCode, + vector<string>& headers, const SockAddr &from) { + responseCode = 404; + headers.push_back("Content-Type: text/plain;charset=utf-8"); + responseMsg = "no favicon\n"; } - bool allowed(OperationContext* txn, - const char * rq, - vector<string>& headers, - const SockAddr &from) { + } faviconHandler; - if ( !cc().getAuthorizationSession()->getAuthorizationManager().isAuthEnabled() ) { - return true; - } - if ( from.isLocalHost() && !_webUsers->haveAdminUsers(txn) ) { - cc().getAuthorizationSession()->grantInternalAuthorization(); - return true; - } + class StatusHandler : public DbWebHandler { + public: + StatusHandler() : DbWebHandler("_status", 1, false) {} + + virtual void handle(OperationContext* txn, + const char *rq, const std::string& url, BSONObj params, + string& responseMsg, int& responseCode, + vector<string>& headers, const SockAddr &from) { + headers.push_back("Content-Type: application/json;charset=utf-8"); + responseCode = 200; - string auth = getHeader( rq , "Authorization" ); + static vector<string> commands; + if (commands.size() == 0) { + commands.push_back("serverStatus"); + commands.push_back("buildinfo"); + } - if ( auth.size() > 0 && auth.find( "Digest " ) == 0 ) { - auth = auth.substr( 7 ) + ", "; + BSONObjBuilder buf(1024); - map<string,string> parms; - pcrecpp::StringPiece input( auth ); + for (unsigned i = 0; i<commands.size(); i++) { + string cmd = commands[i]; - string name, val; - pcrecpp::RE re("(\\w+)=\"?(.*?)\"?,\\s*"); - while ( re.Consume( &input, &name, &val) ) { - parms[name] = val; - } + Command * c = Command::findCommand(cmd); + verify(c); - // Only users in the admin DB are visible by the webserver - UserName userName(parms["username"], "admin"); - User* user; - AuthorizationManager& authzManager = - cc().getAuthorizationSession()->getAuthorizationManager(); - Status status = authzManager.acquireUser(txn, userName, &user); - if (!status.isOK()) { - if (status.code() != ErrorCodes::UserNotFound) { - uasserted(17051, status.reason()); - } - } else { - uassert(17090, - "External users don't have a password", - !user->getCredentials().isExternal); - string ha1 = user->getCredentials().password; - authzManager.releaseUser(user); - if (ha1.empty()) { - return false; - } + BSONObj co; + { + BSONObjBuilder b; + b.append(cmd, 1); - string ha2 = md5simpledigest( (string)"GET" + ":" + parms["uri"] ); - - stringstream r; - r << ha1 << ':' << parms["nonce"]; - if ( parms["nc"].size() && parms["cnonce"].size() && parms["qop"].size() ) { - r << ':'; - r << parms["nc"]; - r << ':'; - r << parms["cnonce"]; - r << ':'; - r << parms["qop"]; + if (cmd == "serverStatus" && params["repl"].type()) { + b.append("repl", atoi(params["repl"].valuestr())); } - r << ':'; - r << ha2; - string r1 = md5simpledigest( r.str() ); - if ( r1 == parms["response"] ) { - _authorizePrincipal(txn, userName); - return true; - } + co = b.obj(); } + + string errmsg; + + BSONObjBuilder sub; + if (!c->run(txn, "admin.$cmd", co, 0, errmsg, sub, false)) + buf.append(cmd, errmsg); + else + buf.append(cmd, sub.obj()); } - stringstream authHeader; - authHeader - << "WWW-Authenticate: " - << "Digest realm=\"mongo\", " - << "nonce=\"abc\", " - << "algorithm=MD5, qop=\"auth\" " - ; + responseMsg = buf.obj().jsonString(); + } - headers.push_back( authHeader.str() ); - return 0; + } statusHandler; + + + class CommandListHandler : public DbWebHandler { + public: + CommandListHandler() : DbWebHandler("_commands", 1, true) {} + + virtual void handle(OperationContext* txn, + const char *rq, const std::string& url, BSONObj params, + string& responseMsg, int& responseCode, + vector<string>& headers, const SockAddr &from) { + headers.push_back("Content-Type: text/html;charset=utf-8"); + responseCode = 200; + + stringstream ss; + ss << start("Commands List"); + ss << p(a("/", "back", "Home")); + ss << p("<b>MongoDB List of " + "<a href=\"http://dochub.mongodb.org/core/commands\">Commands</a>" + "</b>\n"); + + const Command::CommandMap* m = Command::commandsByBestName(); + ss << "S:slave-ok R:read-lock W:write-lock A:admin-only<br>\n"; + ss << table(); + ss << "<tr><th>Command</th><th>Attributes</th><th>Help</th></tr>\n"; + for (Command::CommandMap::const_iterator i = m->begin(); i != m->end(); ++i) { + i->second->htmlHelp(ss); + } + ss << _table() << _end(); + + responseMsg = ss.str(); } + } commandListHandler; - virtual void doRequest( - const char *rq, // the full request - string url, - // set these and return them: - string& responseMsg, - int& responseCode, - vector<string>& headers, // if completely empty, content-type: text/html will be added - const SockAddr &from - ) { - boost::scoped_ptr<OperationContext> txn(getGlobalEnvironment()->newOpCtx()); + class CommandsHandler : public DbWebHandler { + public: + CommandsHandler() : DbWebHandler("DUMMY COMMANDS", 2, true) {} - if ( url.size() > 1 ) { + bool _cmd(const string& url, string& cmd, bool& text, bo params) const { + cmd = str::after(url, '/'); + text = params["text"].boolean(); + return true; + } - if (!allowed(txn.get(), rq, headers, from)) { - responseCode = 401; - headers.push_back( "Content-Type: text/plain;charset=utf-8" ); - responseMsg = "not allowed\n"; - return; - } + Command * _cmd(const string& cmd) const { + const Command::CommandMap* m = Command::webCommands(); + if (!m) + return 0; - { - BSONObj params; - const size_t pos = url.find( "?" ); - if ( pos != string::npos ) { - MiniWebServer::parseParams( params , url.substr( pos + 1 ) ); - url = url.substr(0, pos); - } + Command::CommandMap::const_iterator i = m->find(cmd); + if (i == m->end()) + return 0; - DbWebHandler * handler = DbWebHandler::findHandler( url ); - if ( handler ) { - if (handler->requiresREST(url) && !serverGlobalParams.rest) { - _rejectREST( responseMsg , responseCode , headers ); - } - else { - string callback = params.getStringField("jsonp"); - uassert(13453, "server not started with --jsonp", - callback.empty() || serverGlobalParams.jsonp); + return i->second; + } - handler->handle( txn.get(), rq , url , params , responseMsg , responseCode , headers , from ); + virtual bool handles(const string& url) const { + string cmd; + bool text; + if (!_cmd(url, cmd, text, bo())) + return false; + return _cmd(cmd) != 0; + } - if (responseCode == 200 && !callback.empty()) { - responseMsg = callback + '(' + responseMsg + ')'; - } - } - return; - } - } + virtual void handle(OperationContext* txn, + const char *rq, const std::string& url, BSONObj params, + string& responseMsg, int& responseCode, + vector<string>& headers, const SockAddr &from) { + string cmd; + bool text = false; + verify(_cmd(url, cmd, text, params)); + Command * c = _cmd(cmd); + verify(c); + BSONObj cmdObj = BSON(cmd << 1); - if (!serverGlobalParams.rest) { - _rejectREST( responseMsg , responseCode , headers ); - return; - } + BSONObjBuilder result; + Command::execCommand(txn, c, 0, "admin.", cmdObj, result, false); - responseCode = 404; - headers.push_back( "Content-Type: text/html;charset=utf-8" ); - responseMsg = "<html><body>unknown url</body></html>\n"; - return; + responseCode = 200; + + string j = result.done().jsonString(Strict, text); + responseMsg = j; + + if (text) { + headers.push_back("Content-Type: text/plain;charset=utf-8"); + responseMsg += '\n'; + } + else { + headers.push_back("Content-Type: application/json;charset=utf-8"); } - // generate home page + } + + } commandsHandler; + + + MONGO_INITIALIZER(WebStatusLogPlugin)(InitializerContext*) { + if (serverGlobalParams.isHttpInterfaceEnabled) { + new LogPlugin; + } + return Status::OK(); + } + +} // namespace - if (!allowed(txn.get(), rq, headers, from)) { + + DbWebServer::DbWebServer(const string& ip, int port, const AdminAccess* webUsers) + : MiniWebServer("admin web console", ip, port), + _webUsers(webUsers) { + + WebStatusPlugin::initAll(); + } + + void DbWebServer::doRequest(const char *rq, + string url, + string& responseMsg, + int& responseCode, + vector<string>& headers, + const SockAddr &from) { + + boost::scoped_ptr<OperationContext> txn(getGlobalEnvironment()->newOpCtx()); + + if (url.size() > 1) { + + if (!_allowed(txn.get(), rq, headers, from)) { responseCode = 401; - headers.push_back( "Content-Type: text/plain;charset=utf-8" ); + headers.push_back("Content-Type: text/plain;charset=utf-8"); responseMsg = "not allowed\n"; return; } - responseCode = 200; - stringstream ss; - string dbname; { - stringstream z; - z << serverGlobalParams.binaryName << ' ' << prettyHostName(); - dbname = z.str(); + BSONObj params; + const size_t pos = url.find("?"); + if (pos != string::npos) { + MiniWebServer::parseParams(params, url.substr(pos + 1)); + url = url.substr(0, pos); + } + + DbWebHandler * handler = DbWebHandler::findHandler(url); + if (handler) { + if (handler->requiresREST(url) && !serverGlobalParams.rest) { + _rejectREST(responseMsg, responseCode, headers); + } + else { + const string callback = params.getStringField("jsonp"); + + uassert(13453, + "server not started with --jsonp", + callback.empty() || serverGlobalParams.jsonp); + + handler->handle(txn.get(), + rq, + url, + params, + responseMsg, + responseCode, + headers, + from); + + if (responseCode == 200 && !callback.empty()) { + responseMsg = callback + '(' + responseMsg + ')'; + } + } + + return; + } } - ss << start(dbname) << h2(dbname); - ss << "<p><a href=\"/_commands\">List all commands</a></p>\n"; - { - const Command::CommandMap* m = Command::webCommands(); - if( m ) { - ss << - a("", - "These read-only context-less commands can be executed from the web interface. " - "Results are json format, unless ?text=1 is appended in which case the result is output as text " - "for easier human viewing", - "Commands") - << ": "; - for( Command::CommandMap::const_iterator i = m->begin(); i != m->end(); ++i ) { - stringstream h; - i->second->help(h); - string help = h.str(); - ss << "<a href=\"/" << i->first << "?text=1\""; - if( help != "no help defined" ) - ss << " title=\"" << help << '"'; - ss << ">" << i->first << "</a> "; + if (!serverGlobalParams.rest) { + _rejectREST(responseMsg, responseCode, headers); + return; + } + + responseCode = 404; + headers.push_back("Content-Type: text/html;charset=utf-8"); + responseMsg = "<html><body>unknown url</body></html>\n"; + return; + } + + // generate home page + + if (!_allowed(txn.get(), rq, headers, from)) { + responseCode = 401; + headers.push_back("Content-Type: text/plain;charset=utf-8"); + responseMsg = "not allowed\n"; + return; + } + + responseCode = 200; + stringstream ss; + string dbname; + { + stringstream z; + z << serverGlobalParams.binaryName << ' ' << prettyHostName(); + dbname = z.str(); + } + + ss << start(dbname) << h2(dbname); + ss << "<p><a href=\"/_commands\">List all commands</a></p>\n"; + + { + const Command::CommandMap* m = Command::webCommands(); + if (m) { + ss << a("", + "These read-only context-less commands can be executed from the web " + "interface. Results are json format, unless ?text=1 is appended in which " + "case the result is output as text for easier human viewing", + "Commands") + << ": "; + + for (Command::CommandMap::const_iterator i = m->begin(); i != m->end(); ++i) { + stringstream h; + i->second->help(h); + + const string help = h.str(); + ss << "<a href=\"/" << i->first << "?text=1\""; + if (help != "no help defined") { + ss << " title=\"" << help << '"'; } - ss << '\n'; + + ss << ">" << i->first << "</a> "; } + ss << '\n'; } - ss << '\n'; - /* - ss << "HTTP <a " - "title=\"click for documentation on this http interface\"" - "href=\"http://dochub.mongodb.org/core/httpinterface\">admin port</a>:" << _port << "<p>\n"; - */ + } - doUnlockedStuff(ss); + ss << '\n'; - WebStatusPlugin::runAll(txn.get(), ss); + doUnlockedStuff(ss); - ss << "</body></html>\n"; - responseMsg = ss.str(); - headers.push_back( "Content-Type: text/html;charset=utf-8" ); + WebStatusPlugin::runAll(txn.get(), ss); + + ss << "</body></html>\n"; + responseMsg = ss.str(); + headers.push_back("Content-Type: text/html;charset=utf-8"); + } + + bool DbWebServer::_allowed(OperationContext* txn, + const char * rq, + vector<string>& headers, + const SockAddr &from) { + + AuthorizationSession* authSess = cc().getAuthorizationSession(); + if (!authSess->getAuthorizationManager().isAuthEnabled()) { + return true; } - void _rejectREST( string& responseMsg , int& responseCode, vector<string>& headers ) { - responseCode = 403; - stringstream ss; - ss << "REST is not enabled. use --rest to turn on.\n"; - ss << "check that port " << _port << " is secured for the network too.\n"; - responseMsg = ss.str(); - headers.push_back( "Content-Type: text/plain;charset=utf-8" ); + if (from.isLocalHost() && !_webUsers->haveAdminUsers(txn)) { + authSess->grantInternalAuthorization(); + return true; } - }; - // --- + string auth = getHeader(rq, "Authorization"); - bool prisort( const Prioritizable * a , const Prioritizable * b ) { - return a->priority() < b->priority(); + if (auth.size() > 0 && auth.find("Digest ") == 0) { + auth = auth.substr(7) + ", "; + + map<string, string> parms; + pcrecpp::StringPiece input(auth); + + string name, val; + pcrecpp::RE re("(\\w+)=\"?(.*?)\"?,\\s*"); + while (re.Consume(&input, &name, &val)) { + parms[name] = val; + } + + // Only users in the admin DB are visible by the webserver + UserName userName(parms["username"], "admin"); + User* user; + AuthorizationManager& authzManager = + cc().getAuthorizationSession()->getAuthorizationManager(); + Status status = authzManager.acquireUser(txn, userName, &user); + if (!status.isOK()) { + if (status.code() != ErrorCodes::UserNotFound) { + uasserted(17051, status.reason()); + } + } + else { + uassert(17090, + "External users don't have a password", + !user->getCredentials().isExternal); + + string ha1 = user->getCredentials().password; + authzManager.releaseUser(user); + if (ha1.empty()) { + return false; + } + + const string ha2 = md5simpledigest((string)"GET" + ":" + parms["uri"]); + + stringstream r; + r << ha1 << ':' << parms["nonce"]; + if (parms["nc"].size() && parms["cnonce"].size() && parms["qop"].size()) { + r << ':'; + r << parms["nc"]; + r << ':'; + r << parms["cnonce"]; + r << ':'; + r << parms["qop"]; + } + r << ':'; + r << ha2; + + const string r1 = md5simpledigest(r.str()); + + if (r1 == parms["response"]) { + Status status = authSess->addAndAuthorizeUser(txn, userName); + uassertStatusOK(status); + return true; + } + } + } + + stringstream authHeader; + authHeader << "WWW-Authenticate: " + << "Digest realm=\"mongo\", " + << "nonce=\"abc\", " + << "algorithm=MD5, qop=\"auth\" "; + + headers.push_back(authHeader.str()); + return 0; } + void DbWebServer::_rejectREST(string& responseMsg, int& responseCode, vector<string>& headers) { + responseCode = 403; + stringstream ss; + ss << "REST is not enabled. use --rest to turn on.\n"; + ss << "check that port " << _port << " is secured for the network too.\n"; + responseMsg = ss.str(); + headers.push_back("Content-Type: text/plain;charset=utf-8"); + } + + // -- status framework --- WebStatusPlugin::WebStatusPlugin( const string& secionName , double priority , const string& subheader ) : Prioritizable(priority), _name( secionName ) , _subHeading( subheader ) { @@ -361,30 +549,6 @@ namespace mongo { vector<WebStatusPlugin*> * WebStatusPlugin::_plugins = 0; - // -- basic status plugins -- - - class LogPlugin : public WebStatusPlugin { - public: - LogPlugin() : WebStatusPlugin( "Log" , 100 ), _log(0) { - _log = RamLog::get( "global" ); - } - - virtual void init() {} - - virtual void run(OperationContext* txn, stringstream& ss ) { - _log->toHTML( ss ); - } - RamLog * _log; - }; - - MONGO_INITIALIZER(WebStatusLogPlugin)(InitializerContext*) { - if (serverGlobalParams.isHttpInterfaceEnabled) { - new LogPlugin; - } - return Status::OK(); - } - - // -- handler framework --- DbWebHandler::DbWebHandler( const string& name , double priority , bool requiresREST ) : Prioritizable(priority), _name(name) , _requiresREST(requiresREST) { @@ -423,173 +587,11 @@ namespace mongo { vector<DbWebHandler*> * DbWebHandler::_handlers = 0; - // --- basic handlers --- - - class FavIconHandler : public DbWebHandler { - public: - FavIconHandler() : DbWebHandler( "favicon.ico" , 0 , false ) {} - - virtual void handle( OperationContext* txn, - const char *rq, const std::string& url, BSONObj params, - string& responseMsg, int& responseCode, - vector<string>& headers, const SockAddr &from ) { - responseCode = 404; - headers.push_back( "Content-Type: text/plain;charset=utf-8" ); - responseMsg = "no favicon\n"; - } - - } faviconHandler; - - class StatusHandler : public DbWebHandler { - public: - StatusHandler() : DbWebHandler( "_status" , 1 , false ) {} - - virtual void handle( OperationContext* txn, - const char *rq, const std::string& url, BSONObj params, - string& responseMsg, int& responseCode, - vector<string>& headers, const SockAddr &from ) { - headers.push_back( "Content-Type: application/json;charset=utf-8" ); - responseCode = 200; - - static vector<string> commands; - if ( commands.size() == 0 ) { - commands.push_back( "serverStatus" ); - commands.push_back( "buildinfo" ); - } - - BSONObjBuilder buf(1024); - - for ( unsigned i=0; i<commands.size(); i++ ) { - string cmd = commands[i]; - - Command * c = Command::findCommand( cmd ); - verify( c ); - - BSONObj co; - { - BSONObjBuilder b; - b.append( cmd , 1 ); - - if ( cmd == "serverStatus" && params["repl"].type() ) { - b.append( "repl" , atoi( params["repl"].valuestr() ) ); - } - - co = b.obj(); - } - - string errmsg; - - BSONObjBuilder sub; - if ( ! c->run( txn, "admin.$cmd" , co , 0, errmsg , sub , false ) ) - buf.append( cmd , errmsg ); - else - buf.append( cmd , sub.obj() ); - } - - responseMsg = buf.obj().jsonString(); - - } - - } statusHandler; - - class CommandListHandler : public DbWebHandler { - public: - CommandListHandler() : DbWebHandler( "_commands" , 1 , true ) {} - - virtual void handle( OperationContext* txn, - const char *rq, const std::string& url, BSONObj params, - string& responseMsg, int& responseCode, - vector<string>& headers, const SockAddr &from ) { - headers.push_back( "Content-Type: text/html;charset=utf-8" ); - responseCode = 200; - - stringstream ss; - ss << start("Commands List"); - ss << p( a("/", "back", "Home") ); - ss << p( "<b>MongoDB List of <a href=\"http://dochub.mongodb.org/core/commands\">Commands</a></b>\n" ); - const Command::CommandMap* m = Command::commandsByBestName(); - ss << "S:slave-ok R:read-lock W:write-lock A:admin-only<br>\n"; - ss << table(); - ss << "<tr><th>Command</th><th>Attributes</th><th>Help</th></tr>\n"; - for( Command::CommandMap::const_iterator i = m->begin(); i != m->end(); ++i ) - i->second->htmlHelp(ss); - ss << _table() << _end(); - - responseMsg = ss.str(); - } - } commandListHandler; - - class CommandsHandler : public DbWebHandler { - public: - CommandsHandler() : DbWebHandler( "DUMMY COMMANDS" , 2 , true ) {} - - bool _cmd( const string& url , string& cmd , bool& text, bo params ) const { - cmd = str::after(url, '/'); - text = params["text"].boolean(); - return true; - } - - Command * _cmd( const string& cmd ) const { - const Command::CommandMap* m = Command::webCommands(); - if( ! m ) - return 0; - - Command::CommandMap::const_iterator i = m->find(cmd); - if ( i == m->end() ) - return 0; - - return i->second; - } - - virtual bool handles( const string& url ) const { - string cmd; - bool text; - if ( ! _cmd( url , cmd , text, bo() ) ) - return false; - return _cmd(cmd) != 0; - } - - virtual void handle( OperationContext* txn, - const char *rq, const std::string& url, BSONObj params, - string& responseMsg, int& responseCode, - vector<string>& headers, const SockAddr &from ) { - string cmd; - bool text = false; - verify( _cmd( url , cmd , text, params ) ); - Command * c = _cmd( cmd ); - verify( c ); - - BSONObj cmdObj = BSON( cmd << 1 ); - - BSONObjBuilder result; - Command::execCommand(txn, c, 0, "admin.", cmdObj , result, false); - - responseCode = 200; - - string j = result.done().jsonString(Strict, text ); - responseMsg = j; - - if( text ) { - headers.push_back( "Content-Type: text/plain;charset=utf-8" ); - responseMsg += '\n'; - } - else { - headers.push_back( "Content-Type: application/json;charset=utf-8" ); - } - - } - - } commandsHandler; + void webServerListenThread(boost::shared_ptr<DbWebServer> dbWebServer) { + Client::initThread("websvr"); - // --- external ---- + dbWebServer->initAndListen(); - void webServerThread(const AdminAccess* adminAccess) { - boost::scoped_ptr<const AdminAccess> adminAccessPtr(adminAccess); // adminAccess is owned here - Client::initThread("websvr"); - const int p = serverGlobalParams.port + 1000; - DbWebServer mini(serverGlobalParams.bind_ip, p, adminAccessPtr.get()); - mini.setupSockets(); - mini.initAndListen(); cc().shutdown(); } diff --git a/src/mongo/db/dbwebserver.h b/src/mongo/db/dbwebserver.h index 8a47dde547e..b1c8529024a 100644 --- a/src/mongo/db/dbwebserver.h +++ b/src/mongo/db/dbwebserver.h @@ -29,15 +29,22 @@ * it in the license file. */ +#pragma once + +#include <boost/shared_ptr.hpp> #include <string> #include <vector> -#include "mongo/util/admin_access.h" +#include "mongo/util/net/miniwebserver.h" #include "mongo/util/net/sock.h" -#include "mongo/db/operation_context.h" namespace mongo { + class AdminAccess; + class DbWebServer; + class OperationContext; + + class Prioritizable { public: Prioritizable( double p ) : _priority(p) {} @@ -79,6 +86,7 @@ namespace mongo { static std::vector<DbWebHandler*> * _handlers; }; + class WebStatusPlugin : public Prioritizable { public: WebStatusPlugin( const std::string& secionName , double priority , const std::string& subheader = "" ); @@ -96,7 +104,35 @@ namespace mongo { static std::vector<WebStatusPlugin*> * _plugins; }; - void webServerThread(const AdminAccess* admins); + + class DbWebServer : public MiniWebServer { + public: + DbWebServer(const std::string& ip, int port, const AdminAccess* webUsers); + + private: + virtual void doRequest(const char *rq, + std::string url, + std::string& responseMsg, + int& responseCode, + std::vector<std::string>& headers, + const SockAddr &from); + + bool _allowed(OperationContext* txn, + const char* rq, + std::vector<std::string>& headers, + const SockAddr& from); + + void _rejectREST(std::string& responseMsg, + int& responseCode, + std::vector<std::string>& headers); + + + // not owned here + const AdminAccess* _webUsers; + }; + + void webServerListenThread(boost::shared_ptr<DbWebServer> dbWebServer); + std::string prettyHostName(); }; |