mirror of
https://github.com/mongodb/mongo.git
synced 2024-12-01 09:32:32 +01:00
311 lines
10 KiB
C++
311 lines
10 KiB
C++
/** @file resetapi.cpp
|
|
web rest api
|
|
*/
|
|
/**
|
|
* Copyright (C) 2008 10gen Inc.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License, version 3,
|
|
* as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "pch.h"
|
|
#include "../util/miniwebserver.h"
|
|
#include "../util/mongoutils/html.h"
|
|
#include "../util/md5.hpp"
|
|
#include "instance.h"
|
|
#include "dbwebserver.h"
|
|
#include "dbhelpers.h"
|
|
#include "repl.h"
|
|
#include "replpair.h"
|
|
#include "clientcursor.h"
|
|
#include "background.h"
|
|
|
|
namespace mongo {
|
|
|
|
extern const char *replInfo;
|
|
bool getInitialSyncCompleted();
|
|
|
|
using namespace bson;
|
|
using namespace mongoutils::html;
|
|
|
|
class RESTHandler : public DbWebHandler {
|
|
public:
|
|
RESTHandler() : DbWebHandler( "DUMMY REST" , 1000 , true ){}
|
|
|
|
virtual bool handles( const string& url ) const {
|
|
return
|
|
url[0] == '/' &&
|
|
url.find_last_of( '/' ) > 0;
|
|
}
|
|
|
|
virtual void handle( const char *rq, string url,
|
|
string& responseMsg, int& responseCode,
|
|
vector<string>& headers, const SockAddr &from ){
|
|
|
|
string::size_type first = url.find( "/" , 1 );
|
|
if ( first == string::npos ) {
|
|
responseCode = 400;
|
|
return;
|
|
}
|
|
|
|
string method = MiniWebServer::parseMethod( rq );
|
|
string dbname = url.substr( 1 , first - 1 );
|
|
string coll = url.substr( first + 1 );
|
|
string action = "";
|
|
|
|
BSONObj params;
|
|
if ( coll.find( "?" ) != string::npos ) {
|
|
MiniWebServer::parseParams( params , coll.substr( coll.find( "?" ) + 1 ) );
|
|
coll = coll.substr( 0 , coll.find( "?" ) );
|
|
}
|
|
|
|
string::size_type last = coll.find_last_of( "/" );
|
|
if ( last == string::npos ) {
|
|
action = coll;
|
|
coll = "_defaultCollection";
|
|
}
|
|
else {
|
|
action = coll.substr( last + 1 );
|
|
coll = coll.substr( 0 , last );
|
|
}
|
|
|
|
for ( string::size_type i=0; i<coll.size(); i++ )
|
|
if ( coll[i] == '/' )
|
|
coll[i] = '.';
|
|
|
|
string fullns = MiniWebServer::urlDecode(dbname + "." + coll);
|
|
|
|
headers.push_back( (string)"x-action: " + action );
|
|
headers.push_back( (string)"x-ns: " + fullns );
|
|
|
|
bool html = false;
|
|
|
|
|
|
stringstream ss;
|
|
|
|
if ( method == "GET" ) {
|
|
responseCode = 200;
|
|
html = handleRESTQuery( fullns , action , params , responseCode , ss );
|
|
}
|
|
else if ( method == "POST" ) {
|
|
responseCode = 201;
|
|
handlePost( fullns , MiniWebServer::body( rq ) , params , responseCode , ss );
|
|
}
|
|
else {
|
|
responseCode = 400;
|
|
headers.push_back( "X_err: bad request" );
|
|
ss << "don't know how to handle a [" << method << "]";
|
|
out() << "don't know how to handle a [" << method << "]" << endl;
|
|
}
|
|
|
|
if( html )
|
|
headers.push_back("Content-Type: text/html;charset=utf-8");
|
|
else
|
|
headers.push_back("Content-Type: text/plain;charset=utf-8");
|
|
|
|
responseMsg = ss.str();
|
|
}
|
|
|
|
bool handleRESTQuery( string ns , string action , BSONObj & params , int & responseCode , stringstream & out ) {
|
|
Timer t;
|
|
|
|
int html = _getOption( params["html"] , 0 );
|
|
int skip = _getOption( params["skip"] , 0 );
|
|
int num = _getOption( params["limit"] , _getOption( params["count" ] , 1000 ) ); // count is old, limit is new
|
|
|
|
int one = 0;
|
|
if ( params["one"].type() == String && tolower( params["one"].valuestr()[0] ) == 't' ) {
|
|
num = 1;
|
|
one = 1;
|
|
}
|
|
|
|
BSONObjBuilder queryBuilder;
|
|
|
|
BSONObjIterator i(params);
|
|
while ( i.more() ){
|
|
BSONElement e = i.next();
|
|
string name = e.fieldName();
|
|
if ( ! name.find( "filter_" ) == 0 )
|
|
continue;
|
|
|
|
string field = name.substr(7);
|
|
const char * val = e.valuestr();
|
|
|
|
char * temp;
|
|
|
|
// TODO: this is how i guess if something is a number. pretty lame right now
|
|
double number = strtod( val , &temp );
|
|
if ( temp != val )
|
|
queryBuilder.append( field , number );
|
|
else
|
|
queryBuilder.append( field , val );
|
|
}
|
|
|
|
BSONObj query = queryBuilder.obj();
|
|
auto_ptr<DBClientCursor> cursor = db.query( ns.c_str() , query, num , skip );
|
|
uassert( 13085 , "query failed for dbwebserver" , cursor.get() );
|
|
|
|
if ( one ) {
|
|
if ( cursor->more() ) {
|
|
BSONObj obj = cursor->next();
|
|
out << obj.jsonString(Strict,html?1:0) << '\n';
|
|
}
|
|
else {
|
|
responseCode = 404;
|
|
}
|
|
return html != 0;
|
|
}
|
|
|
|
if( html ) {
|
|
string title = string("query ") + ns;
|
|
out << start(title)
|
|
<< p(title)
|
|
<< "<pre>";
|
|
} else {
|
|
out << "{\n";
|
|
out << " \"offset\" : " << skip << ",\n";
|
|
out << " \"rows\": [\n";
|
|
}
|
|
|
|
int howMany = 0;
|
|
while ( cursor->more() ) {
|
|
if ( howMany++ && html == 0 )
|
|
out << " ,\n";
|
|
BSONObj obj = cursor->next();
|
|
if( html ) {
|
|
if( out.tellp() > 4 * 1024 * 1024 ) {
|
|
out << "Stopping output: more than 4MB returned and in html mode\n";
|
|
break;
|
|
}
|
|
out << obj.jsonString(Strict, html?1:0) << "\n\n";
|
|
}
|
|
else {
|
|
if( out.tellp() > 50 * 1024 * 1024 ) // 50MB limit - we are using ram
|
|
break;
|
|
out << " " << obj.jsonString();
|
|
}
|
|
}
|
|
|
|
if( html ) {
|
|
out << "</pre>\n";
|
|
if( howMany == 0 ) out << p("Collection is empty");
|
|
out << _end();
|
|
}
|
|
else {
|
|
out << "\n ],\n\n";
|
|
out << " \"total_rows\" : " << howMany << " ,\n";
|
|
out << " \"query\" : " << query.jsonString() << " ,\n";
|
|
out << " \"millis\" : " << t.millis() << '\n';
|
|
out << "}\n";
|
|
}
|
|
|
|
return html != 0;
|
|
}
|
|
|
|
// TODO Generate id and revision per couch POST spec
|
|
void handlePost( string ns, const char *body, BSONObj& params, int & responseCode, stringstream & out ) {
|
|
try {
|
|
BSONObj obj = fromjson( body );
|
|
db.insert( ns.c_str(), obj );
|
|
} catch ( ... ) {
|
|
responseCode = 400; // Bad Request. Seems reasonable for now.
|
|
out << "{ \"ok\" : false }";
|
|
return;
|
|
}
|
|
|
|
responseCode = 201;
|
|
out << "{ \"ok\" : true }";
|
|
}
|
|
|
|
int _getOption( BSONElement e , int def ) {
|
|
if ( e.isNumber() )
|
|
return e.numberInt();
|
|
if ( e.type() == String )
|
|
return atoi( e.valuestr() );
|
|
return def;
|
|
}
|
|
|
|
DBDirectClient db;
|
|
|
|
} restHandler;
|
|
|
|
bool webHaveAdminUsers(){
|
|
readlocktryassert rl("admin.system.users", 10000);
|
|
Client::Context cx( "admin.system.users" );
|
|
return ! Helpers::isEmpty("admin.system.users");
|
|
}
|
|
|
|
BSONObj webGetAdminUser( const string& username ){
|
|
Client::GodScope gs;
|
|
readlocktryassert rl("admin.system.users", 10000);
|
|
Client::Context cx( "admin.system.users" );
|
|
BSONObj user;
|
|
if ( Helpers::findOne( "admin.system.users" , BSON( "user" << username ) , user ) )
|
|
return user.copy();
|
|
return BSONObj();
|
|
}
|
|
|
|
class LowLevelMongodStatus : public WebStatusPlugin {
|
|
public:
|
|
LowLevelMongodStatus() : WebStatusPlugin( "low level" , 5 , "requires read lock" ){}
|
|
|
|
virtual void init(){}
|
|
|
|
void _gotLock( int millis , stringstream& ss ){
|
|
ss << "<pre>\n";
|
|
ss << "time to get readlock: " << millis << "ms\n";
|
|
|
|
ss << "# databases: " << dbHolder.size() << '\n';
|
|
|
|
if( ClientCursor::numCursors()>500 )
|
|
ss << "# Cursors: " << ClientCursor::numCursors() << '\n';
|
|
|
|
ss << "\nreplication: ";
|
|
if( *replInfo )
|
|
ss << "\nreplInfo: " << replInfo << "\n\n";
|
|
if( replSet ) {
|
|
ss << a("", "see replSetGetStatus link top of page") << "--replSet </a>" << cmdLine._replSet << '\n';
|
|
}
|
|
if ( replAllDead )
|
|
ss << "<b>replication replAllDead=" << replAllDead << "</b>\n";
|
|
|
|
else {
|
|
ss << "\nmaster: " << replSettings.master << '\n';
|
|
ss << "slave: " << replSettings.slave << '\n';
|
|
if ( replPair ) {
|
|
ss << "replpair:\n";
|
|
ss << replPair->getInfo();
|
|
}
|
|
bool seemCaughtUp = getInitialSyncCompleted();
|
|
if ( !seemCaughtUp ) ss << "<b>";
|
|
ss << "initialSyncCompleted: " << seemCaughtUp;
|
|
if ( !seemCaughtUp ) ss << "</b>";
|
|
ss << '\n';
|
|
}
|
|
|
|
BackgroundOperation::dump(ss);
|
|
ss << "</pre>\n";
|
|
}
|
|
|
|
virtual void run( stringstream& ss ){
|
|
Timer t;
|
|
readlocktry lk( "" , 300 );
|
|
if ( lk.got() ){
|
|
_gotLock( t.millis() , ss );
|
|
}
|
|
else {
|
|
ss << "\n<b>timed out getting lock</b>\n";
|
|
}
|
|
}
|
|
} lowLevelMongodStatus;
|
|
}
|