merged changes from develop; added authentication support

This commit is contained in:
Guenter Obiltschnig 2017-02-12 23:13:51 +01:00
parent 155c12e2e0
commit 9f8146ccaa
12 changed files with 547 additions and 142 deletions

View File

@ -62,7 +62,7 @@ public:
/// An empty element will be returned when the element is not found.
template<typename T>
bool isType(int pos)
bool isType(int pos) const
/// Returns true when the type of the element equals the TypeId of ElementTrait
{
return Document::isType<T>(Poco::NumberFormatter::format(pos));

View File

@ -26,6 +26,7 @@
#include "Poco/Buffer.h"
#include "Poco/StreamCopier.h"
#include "Poco/MemoryStream.h"
#include "Poco/UUID.h"
#include <sstream>
@ -45,6 +46,15 @@ public:
Binary(Poco::Int32 size, unsigned char subtype);
/// Constructor
Binary(const UUID& uuid);
/// Constructor for setting a UUID in a binary element
Binary(const std::string& data, unsigned char subtype = 0);
/// Constructor for getting binary data from a string.
Binary(const void* data, Poco::Int32 size, unsigned char subtype = 0);
/// Constructor for getting binary data from a buffer.
virtual ~Binary();
/// Destructor
@ -59,6 +69,13 @@ public:
std::string toString(int indent = 0) const;
/// Returns the binary encoded in Base64
std::string toRawString() const;
/// Returns the raw content as a string.
UUID uuid() const;
/// Returns the UUID when the binary subtype is 0x04.
/// Otherwise BadCastException will be thrown
private:
Buffer<unsigned char> _buffer;
@ -84,6 +101,12 @@ inline Buffer<unsigned char>& Binary::buffer()
}
inline std::string Binary::toRawString() const
{
return std::string(reinterpret_cast<const char*>(_buffer.begin()), _buffer.size());
}
// BSON Embedded Document
// spec: binary
template<>

View File

@ -44,8 +44,18 @@ public:
virtual ~Database();
/// Destructor
bool authenticate(Connection& connection, const std::string& username, const std::string& password, const std::string& method = AUTH_MONGODB_CR);
/// Authenticates against the database using the given connection,
/// username and password, as well as authentication method.
///
/// "MONGODB-CR" (default prior to MongoDB 3.0) and
/// "SCRAM-SHA-1" (default starting in 3.0) are the only supported
/// authentication methods.
///
/// Returns true if authentication was successful, otherwise false.
double count(Connection& connection, const std::string& collectionName) const;
Int64 count(Connection& connection, const std::string& collectionName) const;
/// Sends a count request for the given collection to MongoDB. When
/// the command fails, -1 is returned.
@ -88,6 +98,16 @@ public:
/// Sends the getLastError command to the database and returns the err element
/// from the error document. When err is null, an empty string is returned.
static const std::string AUTH_MONGODB_CR;
/// Default authentication mechanism prior to MongoDB 3.0.
static const std::string AUTH_SCRAM_SHA1;
/// Default authentication mechanism for MongoDB 3.0.
protected:
bool authCR(Connection& connection, const std::string& username, const std::string& password);
bool authSCRAM(Connection& connection, const std::string& username, const std::string& password);
private:
std::string _dbname;
};
@ -128,6 +148,7 @@ Database::createUpdateRequest(const std::string& collectionName) const
return new Poco::MongoDB::UpdateRequest(_dbname + '.' + collectionName);
}
} } // namespace Poco::MongoDB

View File

@ -68,7 +68,7 @@ public:
template<typename T>
Document& add(const std::string& name, T value)
/// Creates an element with the given name and value and
// adds it to the document.
/// adds it to the document.
/// The active document is returned to allow chaining of the add methods.
{
return addElement(new ConcreteElement<T>(name, value));
@ -76,7 +76,7 @@ public:
Document& add(const std::string& name, const char* value)
/// Creates an element with the given name and value and
// adds it to the document.
/// adds it to the document.
/// The active document is returned to allow chaining of the add methods.
{
return addElement(new ConcreteElement<std::string>(name, std::string(value)));
@ -153,8 +153,14 @@ public:
/// Returns the element with the given name.
/// An empty element will be returned when the element is not found.
Int64 getInteger(const std::string& name) const;
/// Returns an integer. Useful when MongoDB returns int32, int64
/// or double for a number (count for example). This method will always
/// return an Int64. When the element is not found, a
/// NotFoundException will be thrown.
template<typename T>
bool isType(const std::string& name)
bool isType(const std::string& name) const
/// Returns true when the type of the element equals the TypeId of ElementTrait
{
Element::Ptr element = get(name);
@ -185,7 +191,7 @@ protected:
inline Document& Document::addElement(Element::Ptr element)
{
_elements.insert(element);
_elements.push_back(element);
return *this;
}

View File

@ -27,13 +27,14 @@
#include "Poco/Nullable.h"
#include "Poco/NumberFormatter.h"
#include "Poco/DateTimeFormatter.h"
#include "Poco/UTF8String.h"
#include "Poco/MongoDB/MongoDB.h"
#include "Poco/MongoDB/BSONReader.h"
#include "Poco/MongoDB/BSONWriter.h"
#include <string>
#include <sstream>
#include <iomanip>
#include <set>
#include <list>
namespace Poco {
@ -76,17 +77,7 @@ inline std::string Element::name() const
}
class ElementComparator
{
public:
bool operator()(const Element::Ptr& s1, const Element::Ptr& s2)
{
return s1->name() < s2->name();
}
};
typedef std::set<Element::Ptr, ElementComparator> ElementSet;
typedef std::list<Element::Ptr> ElementSet;
template<typename T>
@ -241,7 +232,11 @@ struct ElementTraits<Timestamp>
static std::string toString(const Timestamp& value, int indent = 0)
{
return DateTimeFormatter::format(value, "%Y-%m-%dT%H:%M:%s%z");
std::string result;
result.append(1, '"');
result.append(DateTimeFormatter::format(value, "%Y-%m-%dT%H:%M:%s%z"));
result.append(1, '"');
return result;
}
};

View File

@ -58,7 +58,6 @@ protected:
void buildRequest(BinaryWriter& writer);
private:
Int32 _flags;
std::string _fullCollectionName;
Int32 _numberToReturn;
Int64 _cursorID;

View File

@ -59,7 +59,7 @@ std::string Array::toString(int indent) const
for(int i = 0; i < indent; ++i) oss << ' ';
oss << (*it)->toString();
oss << (*it)->toString(indent > 0 ? indent + 2 : 0);
}
if ( indent > 0 )

View File

@ -23,12 +23,41 @@ namespace Poco {
namespace MongoDB {
Binary::Binary() : _buffer(0)
Binary::Binary():
_buffer(0),
_subtype(0)
{
}
Binary::Binary(Poco::Int32 size, unsigned char subtype) : _buffer(size), _subtype(subtype)
Binary::Binary(Poco::Int32 size, unsigned char subtype):
_buffer(size),
_subtype(subtype)
{
}
Binary::Binary(const UUID& uuid):
_buffer(128 / 8),
_subtype(0x04)
{
unsigned char szUUID[16];
uuid.copyTo((char*) szUUID);
_buffer.assign(szUUID, 16);
}
Binary::Binary(const std::string& data, unsigned char subtype):
_buffer(reinterpret_cast<const unsigned char*>(data.data()), data.size()),
_subtype(subtype)
{
}
Binary::Binary(const void* data, Poco::Int32 size, unsigned char subtype):
_buffer(reinterpret_cast<const unsigned char*>(data), size),
_subtype(subtype)
{
}
@ -47,5 +76,15 @@ std::string Binary::toString(int indent) const
return oss.str();
}
UUID Binary::uuid() const
{
if ( _subtype == 0x04 && _buffer.size() == 16 )
{
UUID uuid;
uuid.copyFrom((const char*) _buffer.begin());
return uuid;
}
throw BadCastException("Invalid subtype");
}
} } // namespace Poco::MongoDB

View File

@ -17,22 +17,323 @@
#include "Poco/MongoDB/Database.h"
#include "Poco/MongoDB/Binary.h"
#include "Poco/MD5Engine.h"
#include "Poco/SHA1Engine.h"
#include "Poco/PBKDF2Engine.h"
#include "Poco/HMACEngine.h"
#include "Poco/Base64Decoder.h"
#include "Poco/MemoryStream.h"
#include "Poco/StreamCopier.h"
#include "Poco/Exception.h"
#include "Poco/RandomStream.h"
#include "Poco/Random.h"
#include "Poco/Format.h"
#include "Poco/NumberParser.h"
#include <sstream>
#include <map>
namespace Poco {
namespace MongoDB {
Database::Database( const std::string& db) : _dbname(db)
const std::string Database::AUTH_MONGODB_CR("MONGODB-CR");
const std::string Database::AUTH_SCRAM_SHA1("SCRAM-SHA-1");
namespace
{
std::map<std::string, std::string> parseKeyValueList(const std::string& str)
{
std::map<std::string, std::string> kvm;
std::string::const_iterator it = str.begin();
std::string::const_iterator end = str.end();
while (it != end)
{
std::string k;
std::string v;
while (it != end && *it != '=') k += *it++;
if (it != end) ++it;
while (it != end && *it != ',') v += *it++;
if (it != end) ++it;
kvm[k] = v;
}
return kvm;
}
std::string hashCredentials(const std::string& username, const std::string& password)
{
Poco::MD5Engine md5;
md5.update(username);
md5.update(std::string(":mongo:"));
md5.update(password);
return Poco::DigestEngine::digestToHex(md5.digest());
}
std::string decodeBase64(const std::string& base64)
{
Poco::MemoryInputStream istr(base64.data(), base64.size());
Poco::Base64Decoder decoder(istr);
std::string result;
Poco::StreamCopier::copyToString(decoder, result);
return result;
}
std::string encodeBase64(const std::string& data)
{
std::ostringstream ostr;
Poco::Base64Encoder encoder(ostr);
encoder.rdbuf()->setLineLength(0);
encoder << data;
encoder.close();
return ostr.str();
}
std::string digestToBinaryString(Poco::DigestEngine& engine)
{
Poco::DigestEngine::Digest d = engine.digest();
return std::string(reinterpret_cast<const char*>(&d[0]), d.size());
}
std::string digestToHexString(Poco::DigestEngine& engine)
{
Poco::DigestEngine::Digest d = engine.digest();
return Poco::DigestEngine::digestToHex(d);
}
std::string digestToBase64(Poco::DigestEngine& engine)
{
return encodeBase64(digestToBinaryString(engine));
}
std::string createNonce()
{
Poco::MD5Engine md5;
Poco::RandomInputStream randomStream;
Poco::Random random;
for (int i = 0; i < 4; i++)
{
md5.update(randomStream.get());
md5.update(random.nextChar());
}
return digestToHexString(md5);
}
}
Database::Database(const std::string& db):
_dbname(db)
{
}
Database::~Database()
{
}
double Database::count(Connection& connection, const std::string& collectionName) const
bool Database::authenticate(Connection& connection, const std::string& username, const std::string& password, const std::string& method)
{
if (username.empty()) throw Poco::InvalidArgumentException("empty username");
if (password.empty()) throw Poco::InvalidArgumentException("empty password");
if (method == AUTH_MONGODB_CR)
return authCR(connection, username, password);
else if (method == AUTH_SCRAM_SHA1)
return authSCRAM(connection, username, password);
else
throw Poco::InvalidArgumentException("authentication method", method);
}
bool Database::authCR(Connection& connection, const std::string& username, const std::string& password)
{
std::string nonce;
Poco::SharedPtr<QueryRequest> pCommand = createCommand();
pCommand->selector().add<Poco::Int32>("getnonce", 1);
ResponseMessage response;
connection.sendRequest(*pCommand, response);
if (response.documents().size() > 0)
{
Document::Ptr pDoc = response.documents()[0];
try
{
double ok = pDoc->get<double>("ok");
if (ok != 1) return false;
nonce = pDoc->get<std::string>("nonce", "");
if (nonce.empty()) return false;
}
catch (Poco::NotFoundException&)
{
return false;
}
}
else return false;
std::string credsDigest = hashCredentials(username, password);
Poco::MD5Engine md5;
md5.update(nonce);
md5.update(username);
md5.update(credsDigest);
std::string key = digestToHexString(md5);
pCommand = createCommand();
pCommand->selector()
.add<Poco::Int32>("authenticate", 1)
.add<std::string>("user", username)
.add<std::string>("nonce", nonce)
.add<std::string>("key", key);
connection.sendRequest(*pCommand, response);
if (response.documents().size() > 0)
{
Document::Ptr pDoc = response.documents()[0];
try
{
return pDoc->get<double>("ok") == 1;
}
catch (Poco::NotFoundException&)
{
}
}
return false;
}
bool Database::authSCRAM(Connection& connection, const std::string& username, const std::string& password)
{
std::string clientNonce(createNonce());
std::string clientFirstMsg = Poco::format("n=%s,r=%s", username, clientNonce);
Poco::SharedPtr<QueryRequest> pCommand = createCommand();
pCommand->selector()
.add<Poco::Int32>("saslStart", 1)
.add<std::string>("mechanism", AUTH_SCRAM_SHA1)
.add<Binary::Ptr>("payload", new Binary(Poco::format("n,,%s", clientFirstMsg)))
.add<bool>("authAuthorize", true);
ResponseMessage response;
connection.sendRequest(*pCommand, response);
Int32 conversationId = 0;
std::string serverFirstMsg;
if (response.documents().size() > 0)
{
Document::Ptr pDoc = response.documents()[0];
try
{
if (pDoc->get<double>("ok") == 1)
{
Binary::Ptr pPayload = pDoc->get<Binary::Ptr>("payload");
serverFirstMsg = pPayload->toRawString();
conversationId = pDoc->get<Int32>("conversationId");
}
else return false;
}
catch (Poco::NotFoundException&)
{
return false;
}
}
else return false;
std::map<std::string, std::string> kvm = parseKeyValueList(serverFirstMsg);
const std::string serverNonce = kvm["r"];
const std::string salt = decodeBase64(kvm["s"]);
const unsigned iterations = Poco::NumberParser::parseUnsigned(kvm["i"]);
const Poco::UInt32 dkLen = 20;
std::string hashedPassword = hashCredentials(username, password);
Poco::PBKDF2Engine<Poco::HMACEngine<Poco::SHA1Engine> > pbkdf2(salt, iterations, dkLen);
pbkdf2.update(hashedPassword);
std::string saltedPassword = digestToBinaryString(pbkdf2);
std::string clientFinalNoProof = Poco::format("c=biws,r=%s", serverNonce);
std::string authMessage = Poco::format("%s,%s,%s", clientFirstMsg, serverFirstMsg, clientFinalNoProof);
Poco::HMACEngine<Poco::SHA1Engine> hmacKey(saltedPassword);
hmacKey.update(std::string("Client Key"));
std::string clientKey = digestToBinaryString(hmacKey);
Poco::SHA1Engine sha1;
sha1.update(clientKey);
std::string storedKey = digestToBinaryString(sha1);
Poco::HMACEngine<Poco::SHA1Engine> hmacSig(storedKey);
hmacSig.update(authMessage);
std::string clientSignature = digestToBinaryString(hmacSig);
std::string clientProof(clientKey);
for (std::size_t i = 0; i < clientProof.size(); i++)
{
clientProof[i] ^= clientSignature[i];
}
std::string clientFinal = Poco::format("%s,p=%s", clientFinalNoProof, encodeBase64(clientProof));
pCommand = createCommand();
pCommand->selector()
.add<Poco::Int32>("saslContinue", 1)
.add<Poco::Int32>("conversationId", conversationId)
.add<Binary::Ptr>("payload", new Binary(clientFinal));
std::string serverSecondMsg;
connection.sendRequest(*pCommand, response);
if (response.documents().size() > 0)
{
Document::Ptr pDoc = response.documents()[0];
try
{
if (pDoc->get<double>("ok") == 1)
{
Binary::Ptr pPayload = pDoc->get<Binary::Ptr>("payload");
serverSecondMsg.assign(reinterpret_cast<const char*>(pPayload->buffer().begin()), pPayload->buffer().size());
}
else return false;
}
catch (Poco::NotFoundException&)
{
return false;
}
}
else return false;
Poco::HMACEngine<Poco::SHA1Engine> hmacSKey(saltedPassword);
hmacSKey.update(std::string("Server Key"));
std::string serverKey = digestToBinaryString(hmacSKey);
Poco::HMACEngine<Poco::SHA1Engine> hmacSSig(serverKey);
hmacSSig.update(authMessage);
std::string serverSignature = digestToBase64(hmacSSig);
kvm = parseKeyValueList(serverSecondMsg);
std::string serverSignatureReceived = kvm["v"];
if (serverSignature != serverSignatureReceived) return false;
pCommand = createCommand();
pCommand->selector()
.add<Poco::Int32>("saslContinue", 1)
.add<Poco::Int32>("conversationId", conversationId)
.add<Binary::Ptr>("payload", new Binary);
connection.sendRequest(*pCommand, response);
if (response.documents().size() > 0)
{
Document::Ptr pDoc = response.documents()[0];
return pDoc->get<double>("ok") == 1;
}
return false;
}
Int64 Database::count(Connection& connection, const std::string& collectionName) const
{
Poco::SharedPtr<Poco::MongoDB::QueryRequest> countRequest = createCountRequest(collectionName);
@ -42,7 +343,7 @@ double Database::count(Connection& connection, const std::string& collectionName
if ( response.documents().size() > 0 )
{
Poco::MongoDB::Document::Ptr doc = response.documents()[0];
return doc->get<double>("n");
return doc->getInteger("n");
}
return -1;

View File

@ -52,6 +52,28 @@ Element::Ptr Document::get(const std::string& name) const
return element;
}
Int64 Document::getInteger(const std::string& name) const
{
Element::Ptr element = get(name);
if ( element.isNull() ) throw NotFoundException(name);
if ( ElementTraits<double>::TypeId == element->type() )
{
ConcreteElement<double>* concrete = dynamic_cast<ConcreteElement<double>* >(element.get());
if ( concrete != NULL ) return concrete->value();
}
else if ( ElementTraits<Int32>::TypeId == element->type() )
{
ConcreteElement<Int32>* concrete = dynamic_cast<ConcreteElement<Int32>* >(element.get());
if ( concrete != NULL ) return concrete->value();
}
else if ( ElementTraits<Int64>::TypeId == element->type() )
{
ConcreteElement<Int64>* concrete = dynamic_cast<ConcreteElement<Int64>* >(element.get());
if ( concrete != NULL ) return concrete->value();
}
throw BadCastException("Invalid type mismatch!");
}
void Document::read(BinaryReader& reader)
{
@ -123,7 +145,7 @@ void Document::read(BinaryReader& reader)
}
element->read(reader);
_elements.insert(element);
_elements.push_back(element);
reader >> type;
}

View File

@ -21,8 +21,10 @@
#include "Poco/MongoDB/Database.h"
#include "Poco/MongoDB/Cursor.h"
#include "Poco/MongoDB/ObjectId.h"
#include "Poco/MongoDB/Binary.h"
#include "Poco/Net/NetException.h"
#include "Poco/UUIDGenerator.h"
#include "MongoDBTest.h"
#include "CppUnit/TestCaller.h"
@ -30,46 +32,22 @@
using namespace Poco::MongoDB;
bool MongoDBTest::_connected = false;
Poco::MongoDB::Connection MongoDBTest::_mongo;
Poco::MongoDB::Connection::Ptr MongoDBTest::_mongo;
MongoDBTest::MongoDBTest(const std::string& name):
CppUnit::TestCase("MongoDB"),
_host("localhost"),
_port(27017)
MongoDBTest::MongoDBTest(const std::string& name):
CppUnit::TestCase("MongoDB")
{
if (!_connected)
{
try
{
_mongo.connect(_host, _port);
_connected = true;
std::cout << "Connected to [" << _host << ':' << _port << ']' << std::endl;
}
catch (Poco::Net::ConnectionRefusedException& e)
{
std::cout << "Couldn't connect to " << e.message() << ". " << std::endl;
}
}
}
MongoDBTest::~MongoDBTest()
{
if (_connected)
{
_mongo.disconnect();
_connected = false;
std::cout << "Disconnected from [" << _host << ':' << _port << ']' << std::endl;
}
}
void MongoDBTest::setUp()
{
}
@ -80,12 +58,6 @@ void MongoDBTest::tearDown()
void MongoDBTest::testInsertRequest()
{
if (!_connected)
{
std::cout << "Not connected, test skipped." << std::endl;
return;
}
Poco::MongoDB::Document::Ptr player = new Poco::MongoDB::Document();
player->add("lastname", std::string("Braem"));
player->add("firstname", std::string("Franky"));
@ -98,31 +70,24 @@ void MongoDBTest::testInsertRequest()
player->add("active", false);
Poco::DateTime now;
std::cout << now.day() << " " << now.hour() << ":" << now.minute() << ":" << now.second() << std::endl;
player->add("lastupdated", now.timestamp());
player->add("unknown", NullValue());
Poco::MongoDB::InsertRequest request("team.players");
request.documents().push_back(player);
_mongo.sendRequest(request);
_mongo->sendRequest(request);
}
void MongoDBTest::testQueryRequest()
{
if (!_connected)
{
std::cout << "Not connected, test skipped." << std::endl;
return;
}
Poco::MongoDB::QueryRequest request("team.players");
request.selector().add("lastname" , std::string("Braem"));
request.setNumberToReturn(1);
Poco::MongoDB::ResponseMessage response;
_mongo.sendRequest(request, response);
_mongo->sendRequest(request, response);
if ( response.documents().size() > 0 )
{
@ -143,7 +108,6 @@ void MongoDBTest::testQueryRequest()
assert(!active);
std::string id = doc->get("_id")->toString();
std::cout << id << std::endl;
}
catch(Poco::NotFoundException& nfe)
{
@ -158,18 +122,12 @@ void MongoDBTest::testQueryRequest()
void MongoDBTest::testDBQueryRequest()
{
if (!_connected)
{
std::cout << "Not connected, test skipped." << std::endl;
return;
}
Database db("team");
Poco::SharedPtr<Poco::MongoDB::QueryRequest> request = db.createQueryRequest("players");
request->selector().add("lastname" , std::string("Braem"));
Poco::MongoDB::ResponseMessage response;
_mongo.sendRequest(*request, response);
_mongo->sendRequest(*request, response);
if ( response.documents().size() > 0 )
{
@ -188,7 +146,6 @@ void MongoDBTest::testDBQueryRequest()
assert(doc->isType<NullValue>("unknown"));
std::string id = doc->get("_id")->toString();
std::cout << id << std::endl;
}
catch(Poco::NotFoundException& nfe)
{
@ -204,25 +161,18 @@ void MongoDBTest::testDBQueryRequest()
void MongoDBTest::testCountCommand()
{
if (!_connected)
{
std::cout << "Not connected, test skipped." << std::endl;
return;
}
Poco::MongoDB::QueryRequest request("team.$cmd");
request.setNumberToReturn(1);
request.selector().add("count", std::string("players"));
Poco::MongoDB::ResponseMessage response;
_mongo.sendRequest(request, response);
_mongo->sendRequest(request, response);
if ( response.documents().size() > 0 )
{
Poco::MongoDB::Document::Ptr doc = response.documents()[0];
double count = doc->get<double>("n");
assert(count == 1);
assert(doc->getInteger("n") == 1);
}
else
{
@ -233,23 +183,16 @@ void MongoDBTest::testCountCommand()
void MongoDBTest::testDBCountCommand()
{
if (!_connected)
{
std::cout << "Not connected, test skipped." << std::endl;
return;
}
Poco::MongoDB::Database db("team");
Poco::SharedPtr<Poco::MongoDB::QueryRequest> request = db.createCountRequest("players");
Poco::MongoDB::ResponseMessage response;
_mongo.sendRequest(*request, response);
_mongo->sendRequest(*request, response);
if ( response.documents().size() > 0 )
{
Poco::MongoDB::Document::Ptr doc = response.documents()[0];
double count = doc->get<double>("n");
assert(count == 1);
assert(doc->getInteger("n") == 1);
}
else
{
@ -260,42 +203,28 @@ void MongoDBTest::testDBCountCommand()
void MongoDBTest::testDBCount2Command()
{
if (!_connected)
{
std::cout << "Not connected, test skipped." << std::endl;
return;
}
Poco::MongoDB::Database db("team");
double count = db.count(_mongo, "players");
Poco::Int64 count = db.count(*_mongo, "players");
assert(count == 1);
}
void MongoDBTest::testDeleteRequest()
{
if (!_connected)
{
std::cout << "Not connected, test skipped." << std::endl;
return;
}
Poco::MongoDB::DeleteRequest request("team.players");
request.selector().add("lastname", std::string("Braem"));
_mongo.sendRequest(request);
_mongo->sendRequest(request);
}
void MongoDBTest::testCursorRequest()
{
if (!_connected)
{
std::cout << "Not connected, test skipped." << std::endl;
return;
}
Poco::MongoDB::Database db("team");
Poco::SharedPtr<Poco::MongoDB::DeleteRequest> deleteRequest = db.createDeleteRequest("numbers");
_mongo->sendRequest(*deleteRequest);
Poco::SharedPtr<Poco::MongoDB::InsertRequest> insertRequest = db.createInsertRequest("numbers");
for(int i = 0; i < 10000; ++i)
{
@ -303,23 +232,23 @@ void MongoDBTest::testCursorRequest()
doc->add("number", i);
insertRequest->documents().push_back(doc);
}
_mongo.sendRequest(*insertRequest);
_mongo->sendRequest(*insertRequest);
Poco::Int64 count = db.count(*_mongo, "numbers");
double count = db.count(_mongo, "numbers");
assert(count == 10000);
Poco::MongoDB::Cursor cursor("team", "numbers");
int n = 0;
Poco::MongoDB::ResponseMessage& response = cursor.next(_mongo);
Poco::MongoDB::ResponseMessage& response = cursor.next(*_mongo);
while(1)
{
n += response.documents().size();
if ( response.cursorID() == 0 )
break;
response = cursor.next(_mongo);
response = cursor.next(*_mongo);
}
std::cout << "n= " << n << std::endl;
assert(n == 10000);
Poco::MongoDB::QueryRequest drop("team.$cmd");
@ -327,23 +256,12 @@ void MongoDBTest::testCursorRequest()
drop.selector().add("drop", std::string("numbers"));
Poco::MongoDB::ResponseMessage responseDrop;
_mongo.sendRequest(drop, responseDrop);
if ( responseDrop.documents().size() > 0 )
{
std::cout << responseDrop.documents()[0]->toString(2) << std::endl;
}
_mongo->sendRequest(drop, responseDrop);
}
void MongoDBTest::testBuildInfo()
{
if (!_connected)
{
std::cout << "Not connected, test skipped." << std::endl;
return;
}
Poco::MongoDB::QueryRequest request("team.$cmd");
request.setNumberToReturn(1);
request.selector().add("buildInfo", 1);
@ -352,7 +270,7 @@ void MongoDBTest::testBuildInfo()
try
{
_mongo.sendRequest(request, response);
_mongo->sendRequest(request, response);
}
catch(Poco::NotImplementedException& nie)
{
@ -374,7 +292,7 @@ void MongoDBTest::testBuildInfo()
void MongoDBTest::testConnectionPool()
{
Poco::Net::SocketAddress sa(_host, _port);
Poco::Net::SocketAddress sa("127.0.0.1", 27017);
Poco::PoolableObjectFactory<Poco::MongoDB::Connection, Poco::MongoDB::Connection::Ptr> factory(sa);
Poco::ObjectPool<Poco::MongoDB::Connection, Poco::MongoDB::Connection::Ptr> pool(factory, 10, 15);
@ -390,8 +308,7 @@ void MongoDBTest::testConnectionPool()
if ( response.documents().size() > 0 )
{
Poco::MongoDB::Document::Ptr doc = response.documents()[0];
double count = doc->get<double>("n");
assert(count == 1);
assert(doc->getInteger("n") == 1);
}
else
{
@ -408,10 +325,92 @@ void MongoDBTest::testObjectID()
}
void MongoDBTest::testCommand() {
Poco::MongoDB::Database db("team");
Poco::SharedPtr<Poco::MongoDB::QueryRequest> command = db.createCommand();
command->selector().add("create", "fixCol")
.add("capped", true)
.add("max", 1024*1024)
.add("size", 1024);
Poco::MongoDB::ResponseMessage response;
_mongo->sendRequest(*command, response);
if ( response.documents().size() > 0 )
{
Poco::MongoDB::Document::Ptr doc = response.documents()[0];
}
else
{
Poco::MongoDB::Document::Ptr lastError = db.getLastErrorDoc(*_mongo);
fail(lastError->toString(2));
}
}
void MongoDBTest::testUUID()
{
Poco::MongoDB::Document::Ptr club = new Poco::MongoDB::Document();
club->add("name", std::string("Barcelona"));
Poco::UUIDGenerator generator;
Poco::UUID uuid = generator.create();
Poco::MongoDB::Binary::Ptr uuidBinary = new Poco::MongoDB::Binary(uuid);
club->add("uuid", uuidBinary);
Poco::MongoDB::InsertRequest request("team.club");
request.documents().push_back(club);
_mongo->sendRequest(request);
Poco::MongoDB::QueryRequest queryReq("team.club");
queryReq.selector().add("name" , std::string("Barcelona"));
Poco::MongoDB::ResponseMessage response;
_mongo->sendRequest(queryReq, response);
if ( response.documents().size() > 0 )
{
Poco::MongoDB::Document::Ptr doc = response.documents()[0];
try
{
std::string name = doc->get<std::string>("name");
assert(name.compare("Barcelona") == 0);
Poco::MongoDB::Binary::Ptr uuidBinary = doc->get<Binary::Ptr>("uuid");
assert(uuid == uuidBinary->uuid());
}
catch(Poco::NotFoundException& nfe)
{
fail(nfe.message() + " not found.");
}
}
else
{
fail("No document returned");
}
Poco::MongoDB::DeleteRequest delRequest("team.club");
delRequest.selector().add("name", std::string("Barcelona"));
_mongo->sendRequest(delRequest);
}
CppUnit::Test* MongoDBTest::suite()
{
try
{
_mongo = new Poco::MongoDB::Connection("127.0.0.1", 27017);
std::cout << "Connected to [127.0.0.1:27017]" << std::endl;
}
catch (Poco::Net::ConnectionRefusedException& e)
{
std::cout << "Couldn't connect to " << e.message() << ". " << std::endl;
return 0;
}
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("MongoDBTest");
CppUnit_addTest(pSuite, MongoDBTest, testBuildInfo);
CppUnit_addTest(pSuite, MongoDBTest, testInsertRequest);
CppUnit_addTest(pSuite, MongoDBTest, testQueryRequest);
CppUnit_addTest(pSuite, MongoDBTest, testDBQueryRequest);
@ -420,9 +419,10 @@ CppUnit::Test* MongoDBTest::suite()
CppUnit_addTest(pSuite, MongoDBTest, testDBCount2Command);
CppUnit_addTest(pSuite, MongoDBTest, testConnectionPool);
CppUnit_addTest(pSuite, MongoDBTest, testDeleteRequest);
CppUnit_addTest(pSuite, MongoDBTest, testBuildInfo);
CppUnit_addTest(pSuite, MongoDBTest, testCursorRequest);
CppUnit_addTest(pSuite, MongoDBTest, testObjectID);
CppUnit_addTest(pSuite, MongoDBTest, testCommand);
CppUnit_addTest(pSuite, MongoDBTest, testUUID);
return pSuite;
}

View File

@ -41,6 +41,8 @@ public:
void testConnectionPool();
void testCursorRequest();
void testObjectID();
void testCommand();
void testUUID();
void setUp();
void tearDown();
@ -48,10 +50,7 @@ public:
private:
std::string _host;
unsigned _port;
static bool _connected;
static Poco::MongoDB::Connection _mongo;
static Poco::MongoDB::Connection::Ptr _mongo;
};