Skip to content

Commit

Permalink
merged connection string URI support from 1.8
Browse files Browse the repository at this point in the history
  • Loading branch information
obiltschnig committed Nov 8, 2017
1 parent 70217aa commit ad7c166
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 4 deletions.
49 changes: 48 additions & 1 deletion MongoDB/include/Poco/MongoDB/Connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,24 @@ class MongoDB_API Connection
public:
typedef Poco::SharedPtr<Connection> Ptr;

class MongoDB_API SocketFactory
{
public:
SocketFactory();
/// Creates the SocketFactory.

virtual ~SocketFactory();
/// Destroys the SocketFactory.

virtual Poco::Net::StreamSocket createSocket(const std::string& host, int port, Poco::Timespan connectTimeout, bool secure);
/// Creates a Poco::Net::StreamSocket (if secure is false), or a
/// Poco::Net::SecureStreamSocket (if secure is true) connected to the
/// given host and port number.
///
/// The default implementation will throw a Poco::NotImplementedException
/// if secure is true.
};

Connection();
/// Creates an unconnected Connection.
///
Expand All @@ -49,6 +67,12 @@ class MongoDB_API Connection
///
/// The host and port must be separated with a colon.

Connection(const std::string& uri, SocketFactory& socketFactory);
/// Creates a Connection connected to the given MongoDB instance at the
/// given URI.
///
/// See the corresponding connect() method for more information.

Connection(const std::string& host, int port);
/// Creates a Connection connected to the given MongoDB instance at host and port.

Expand All @@ -70,12 +94,35 @@ class MongoDB_API Connection
///
/// The host and port must be separated with a colon.

void connect(const std::string& uri, SocketFactory& socketFactory);
/// Connects to the given MongoDB instance at the given URI.
///
/// The URI must be in standard MongoDB connection string URI format:
///
/// mongodb://<user>:<password>@hostname.com:<port>/database-name?options
///
/// The following options are supported:
///
/// - ssl: If ssl=true is specified, a custom SocketFactory subclass creating
/// a SecureStreamSocket must be supplied.
/// - connectTimeoutMS: Socket connection timeout in milliseconds.
/// - socketTimeoutMS: Socket send/receive timeout in milliseconds.
/// - authMechanism: Authentication mechanism. Only "SCRAM-SHA-1" (default)
/// and "MONGODB-CR" are supported.
///
/// Unknown options are silently ignored.
///
/// Will also attempt to authenticate using the specified credentials,
/// using Database::authenticate().
///
/// Throws a Poco::NoPermissionException if authentication fails.

void connect(const std::string& host, int port);
/// Connects to the given MongoDB server.

void connect(const Poco::Net::SocketAddress& addrs);
/// Connects to the given MongoDB server.

void connect(const Poco::Net::StreamSocket& socket);
/// Connects using an already connected socket.

Expand Down
22 changes: 19 additions & 3 deletions MongoDB/include/Poco/MongoDB/PoolableConnectionFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,35 @@ namespace Poco {
template<>
class PoolableObjectFactory<MongoDB::Connection, MongoDB::Connection::Ptr>
/// PoolableObjectFactory specialisation for Connection. New connections
/// are created with the given address.
/// are created with the given address or URI.
///
/// If a Connection::SocketFactory is given, it must live for the entire
/// lifetime of the PoolableObjectFactory.
{
public:
PoolableObjectFactory(Net::SocketAddress& address):
_address(address)
_address(address),
_pSocketFactory(0)
{
}

PoolableObjectFactory(const std::string& address):
_address(address)
_address(address),
_pSocketFactory(0)
{
}

PoolableObjectFactory(const std::string& uri, MongoDB::Connection::SocketFactory& socketFactory):
_uri(uri),
_pSocketFactory(&socketFactory)
{
}

MongoDB::Connection::Ptr createObject()
{
if (_pSocketFactory)
return new MongoDB::Connection(_uri, *_pSocketFactory);
else
return new MongoDB::Connection(_address);
}

Expand All @@ -65,6 +79,8 @@ class PoolableObjectFactory<MongoDB::Connection, MongoDB::Connection::Ptr>

private:
Net::SocketAddress _address;
std::string _uri;
MongoDB::Connection::SocketFactory* _pSocketFactory;
};


Expand Down
2 changes: 2 additions & 0 deletions MongoDB/include/Poco/MongoDB/ReplicaSet.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ class MongoDB_API ReplicaSet
std::vector<Net::SocketAddress> _addresses;
};


} } // namespace Poco::MongoDB


#endif // MongoDB_ReplicaSet_INCLUDED
106 changes: 106 additions & 0 deletions MongoDB/src/Connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,43 @@

#include "Poco/Net/SocketStream.h"
#include "Poco/MongoDB/Connection.h"
#include "Poco/MongoDB/Database.h"
#include "Poco/URI.h"
#include "Poco/Format.h"
#include "Poco/NumberParser.h"
#include "Poco/Exception.h"


namespace Poco {
namespace MongoDB {


Connection::SocketFactory::SocketFactory()
{
}


Connection::SocketFactory::~SocketFactory()
{
}


Poco::Net::StreamSocket Connection::SocketFactory::createSocket(const std::string& host, int port, Poco::Timespan connectTimeout, bool secure)
{
if (!secure)
{
Poco::Net::SocketAddress addr(host, port);
Poco::Net::StreamSocket socket;
if (connectTimeout > 0)
socket.connect(addr, connectTimeout);
else
socket.connect(addr);
return socket;
}
else throw Poco::NotImplementedException("Default SocketFactory implementation does not support SecureStreamSocket");
}


Connection::Connection():
_address(),
_socket()
Expand All @@ -35,6 +66,14 @@ Connection::Connection(const std::string& hostAndPort):
}


Connection::Connection(const std::string& uri, SocketFactory& socketFactory):
_address(),
_socket()
{
connect(uri, socketFactory);
}


Connection::Connection(const std::string& host, int port):
_address(host, port),
_socket()
Expand Down Expand Up @@ -104,6 +143,73 @@ void Connection::connect(const Poco::Net::StreamSocket& socket)
}


void Connection::connect(const std::string& uri, SocketFactory& socketFactory)
{
Poco::URI theURI(uri);
if (theURI.getScheme() != "mongodb") throw Poco::UnknownURISchemeException(uri);

std::string userInfo = theURI.getUserInfo();
std::string host = theURI.getHost();
Poco::UInt16 port = theURI.getPort();
if (port == 0) port = 27017;

std::string databaseName = theURI.getPath();
if (!databaseName.empty() && databaseName[0] == '/') databaseName.erase(0, 1);
if (databaseName.empty()) databaseName = "admin";

bool ssl = false;
Poco::Timespan connectTimeout;
Poco::Timespan socketTimeout;
std::string authMechanism = Database::AUTH_SCRAM_SHA1;

Poco::URI::QueryParameters params = theURI.getQueryParameters();
for (Poco::URI::QueryParameters::const_iterator it = params.begin(); it != params.end(); ++it)
{
if (it->first == "ssl")
{
ssl = (it->second == "true");
}
else if (it->first == "connectTimeoutMS")
{
connectTimeout = static_cast<Poco::Timespan::TimeDiff>(1000)*Poco::NumberParser::parse(it->second);
}
else if (it->first == "socketTimeoutMS")
{
socketTimeout = static_cast<Poco::Timespan::TimeDiff>(1000)*Poco::NumberParser::parse(it->second);
}
else if (it->first == "authMechanism")
{
authMechanism = it->second;
}
}

connect(socketFactory.createSocket(host, port, connectTimeout, ssl));

if (socketTimeout > 0)
{
_socket.setSendTimeout(socketTimeout);
_socket.setReceiveTimeout(socketTimeout);
}

if (!userInfo.empty())
{
std::string username;
std::string password;
std::string::size_type pos = userInfo.find(':');
if (pos != std::string::npos)
{
username.assign(userInfo, 0, pos++);
password.assign(userInfo, pos, userInfo.size() - pos);
}
else username = userInfo;

Database database(databaseName);
if (!database.authenticate(*this, username, password, authMechanism))
throw Poco::NoPermissionException(Poco::format("Access to MongoDB database %s denied for user %s", databaseName, username));
}
}


void Connection::disconnect()
{
_socket.close();
Expand Down
46 changes: 46 additions & 0 deletions MongoDB/testsuite/src/MongoDBTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,51 @@ void MongoDBTest::testUUID()
}


void MongoDBTest::testConnectURI()
{
Poco::MongoDB::Connection conn;
Poco::MongoDB::Connection::SocketFactory sf;

conn.connect("mongodb://127.0.0.1", sf);
conn.disconnect();

try
{
conn.connect("http://127.0.0.1", sf);
fail("invalid URI scheme - must throw");
}
catch (Poco::UnknownURISchemeException&)
{
}

try
{
conn.connect("mongodb://127.0.0.1?ssl=true", sf);
fail("SSL not supported, must throw");
}
catch (Poco::NotImplementedException&)
{
}

conn.connect("mongodb://127.0.0.1/admin?ssl=false&connectTimeoutMS=10000&socketTimeoutMS=10000", sf);
conn.disconnect();

try
{
conn.connect("mongodb://127.0.0.1/admin?connectTimeoutMS=foo", sf);
fail("invalid parameter - must throw");
}
catch (Poco::Exception&)
{
}

#ifdef MONGODB_TEST_AUTH
conn.connect("mongodb://admin:admin@127.0.0.1/admin", sf);
conn.disconnect();
#endif
}


CppUnit::Test* MongoDBTest::suite()
{
try
Expand Down Expand Up @@ -423,6 +468,7 @@ CppUnit::Test* MongoDBTest::suite()
CppUnit_addTest(pSuite, MongoDBTest, testObjectID);
CppUnit_addTest(pSuite, MongoDBTest, testCommand);
CppUnit_addTest(pSuite, MongoDBTest, testUUID);
CppUnit_addTest(pSuite, MongoDBTest, testConnectURI);

return pSuite;
}
1 change: 1 addition & 0 deletions MongoDB/testsuite/src/MongoDBTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class MongoDBTest: public CppUnit::TestCase
void testObjectID();
void testCommand();
void testUUID();
void testConnectURI();
void setUp();
void tearDown();

Expand Down

0 comments on commit ad7c166

Please sign in to comment.