/** * Copyright (C) 2009-2015 MongoDB 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 . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #include "mongo/platform/basic.h" #include "mongo/client/mongo_uri.h" #include "mongo/base/string_data.h" #include "mongo/unittest/unittest.h" namespace { using mongo::MongoURI; struct URITestCase { std::string URI; std::string uname; std::string password; mongo::ConnectionString::ConnectionType type; std::string setname; size_t numservers; size_t numOptions; std::string database; }; struct InvalidURITestCase { std::string URI; }; const mongo::ConnectionString::ConnectionType kMaster = mongo::ConnectionString::MASTER; const mongo::ConnectionString::ConnectionType kSet = mongo::ConnectionString::SET; const URITestCase validCases[] = { {"mongodb://user:pwd@127.0.0.1", "user", "pwd", kMaster, "", 1, 0, ""}, {"mongodb://user@127.0.0.1", "user", "", kMaster, "", 1, 0, ""}, {"mongodb://127.0.0.1/dbName?foo=a&c=b", "", "", kMaster, "", 1, 2, "dbName"}, {"mongodb://localhost/?foo=bar", "", "", kMaster, "", 1, 1, ""}, {"mongodb://user:pwd@127.0.0.1:1234", "user", "pwd", kMaster, "", 1, 0, ""}, {"mongodb://user@127.0.0.1:1234", "user", "", kMaster, "", 1, 0, ""}, {"mongodb://127.0.0.1:1234/dbName?foo=a&c=b", "", "", kMaster, "", 1, 2, "dbName"}, {"mongodb://user:pwd@127.0.0.1,127.0.0.2/?replicaSet=replName", "user", "pwd", kSet, "replName", 2, 1, ""}, {"mongodb://user@127.0.0.1,127.0.0.2/?replicaSet=replName", "user", "", kSet, "replName", 2, 1, ""}, {"mongodb://127.0.0.1,127.0.0.2/dbName?foo=a&c=b&replicaSet=replName", "", "", kSet, "replName", 2, 3, "dbName"}, {"mongodb://user:pwd@127.0.0.1:1234,127.0.0.2:1234/?replicaSet=replName", "user", "pwd", kSet, "replName", 2, 1, ""}, {"mongodb://user@127.0.0.1:1234,127.0.0.2:1234/?replicaSet=replName", "user", "", kSet, "replName", 2, 1, ""}, {"mongodb://127.0.0.1:1234,127.0.0.1:1234/dbName?foo=a&c=b&replicaSet=replName", "", "", kSet, "replName", 2, 3, "dbName"}, {"mongodb://user:pwd@[::1]", "user", "pwd", kMaster, "", 1, 0, ""}, {"mongodb://user@[::1]", "user", "", kMaster, "", 1, 0, ""}, {"mongodb://[::1]/dbName?foo=a&c=b", "", "", kMaster, "", 1, 2, "dbName"}, {"mongodb://user:pwd@[::1]:1234", "user", "pwd", kMaster, "", 1, 0, ""}, {"mongodb://user@[::1]:1234", "user", "", kMaster, "", 1, 0, ""}, {"mongodb://[::1]:1234/dbName?foo=a&c=b", "", "", kMaster, "", 1, 2, "dbName"}, {"mongodb://user:pwd@[::1],127.0.0.2/?replicaSet=replName", "user", "pwd", kSet, "replName", 2, 1, ""}, {"mongodb://user@[::1],127.0.0.2/?replicaSet=replName", "user", "", kSet, "replName", 2, 1, ""}, {"mongodb://[::1],127.0.0.2/dbName?foo=a&c=b&replicaSet=replName", "", "", kSet, "replName", 2, 3, "dbName"}, {"mongodb://user:pwd@[::1]:1234,127.0.0.2:1234/?replicaSet=replName", "user", "pwd", kSet, "replName", 2, 1, ""}, {"mongodb://user@[::1]:1234,127.0.0.2:1234/?replicaSet=replName", "user", "", kSet, "replName", 2, 1, ""}, {"mongodb://[::1]:1234,[::1]:1234/dbName?foo=a&c=b&replicaSet=replName", "", "", kSet, "replName", 2, 3, "dbName"}, {"mongodb://user:pwd@[::1]", "user", "pwd", kMaster, "", 1, 0, ""}, {"mongodb://user@[::1]", "user", "", kMaster, "", 1, 0, ""}, {"mongodb://[::1]/dbName?foo=a&c=b", "", "", kMaster, "", 1, 2, "dbName"}, {"mongodb://user:pwd@[::1]:1234", "user", "pwd", kMaster, "", 1, 0, ""}, {"mongodb://user@[::1]:1234", "user", "", kMaster, "", 1, 0, ""}, {"mongodb://[::1]:1234/dbName?foo=a&c=b", "", "", kMaster, "", 1, 2, "dbName"}, {"mongodb://user:pwd@[::1],127.0.0.2/?replicaSet=replName", "user", "pwd", kSet, "replName", 2, 1, ""}, {"mongodb://user@[::1],127.0.0.2/?replicaSet=replName", "user", "", kSet, "replName", 2, 1, ""}, {"mongodb://[::1],127.0.0.2/dbName?foo=a&c=b&replicaSet=replName", "", "", kSet, "replName", 2, 3, "dbName"}, {"mongodb://user:pwd@[::1]:1234,127.0.0.2:1234/?replicaSet=replName", "user", "pwd", kSet, "replName", 2, 1, ""}, {"mongodb://user@[::1]:1234,127.0.0.2:1234/?replicaSet=replName", "user", "", kSet, "replName", 2, 1, ""}, {"mongodb://[::1]:1234,[::1]:1234/dbName?foo=a&c=b&replicaSet=replName", "", "", kSet, "replName", 2, 3, "dbName"}, {"mongodb://user:pwd@[::1]/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:foobar", "user", "pwd", kMaster, "", 1, 2, ""}, {"mongodb://user:pwd@[::1]/?authMechanism=GSSAPI&gssapiServiceName=foobar", "user", "pwd", kMaster, "", 1, 2, ""}, {"mongodb:///tmp/mongodb-27017.sock", "", "", kMaster, "", 1, 0, ""}, {"mongodb:///tmp/mongodb-27017.sock,/tmp/mongodb-27018.sock/?replicaSet=replName", "", "", kSet, "replName", 2, 1, ""}}; const InvalidURITestCase invalidCases[] = { // No host. {"mongodb://"}, // Needs a "/" after the hosts and before the options. {"mongodb://localhost:27017,localhost:27018?replicaSet=missingSlash"}, // Host list must actually be comma separated. {"mongodb://localhost:27017localhost:27018"}, // Domain sockets have to end in ".sock". {"mongodb:///notareal/domainsock"}, // Options can't have multiple question marks. Only one. {"mongodb://localhost:27017/?foo=a?c=b&d=e?asdf=foo"}, }; TEST(MongoURI, GoodTrickyURIs) { const size_t numCases = sizeof(validCases) / sizeof(validCases[0]); for (size_t i = 0; i != numCases; ++i) { const URITestCase testCase = validCases[i]; mongo::unittest::log() << "Testing URI: " << testCase.URI << '\n'; std::string errMsg; auto cs_status = MongoURI::parse(testCase.URI); if (!cs_status.getStatus().toString().empty()) { mongo::unittest::log() << "error with uri: " << cs_status.getStatus().toString(); } ASSERT_TRUE(cs_status.isOK()); auto result = cs_status.getValue(); ASSERT_EQ(testCase.uname, result.getUser()); ASSERT_EQ(testCase.password, result.getPassword()); ASSERT_EQ(testCase.type, result.type()); ASSERT_EQ(testCase.setname, result.getSetName()); ASSERT_EQ(testCase.numservers, result.getServers().size()); ASSERT_EQ(testCase.numOptions, result.getOptions().size()); ASSERT_EQ(testCase.database, result.getDatabase()); } } TEST(MongoURI, InvalidURIs) { const size_t numCases = sizeof(invalidCases) / sizeof(invalidCases[0]); for (size_t i = 0; i != numCases; ++i) { const InvalidURITestCase testCase = invalidCases[i]; mongo::unittest::log() << "Testing URI: " << testCase.URI << '\n'; auto cs_status = MongoURI::parse(testCase.URI); ASSERT_FALSE(cs_status.isOK()); } } TEST(MongoURI, ValidButBadURIsFailToConnect) { // "invalid" is a TLD that cannot exit on the public internet (see rfc2606). It should always // parse as a valid URI, but connecting should always fail. auto sw_uri = MongoURI::parse("mongodb://user:pass@hostname.invalid:12345"); ASSERT_OK(sw_uri.getStatus()); auto uri = sw_uri.getValue(); ASSERT_TRUE(uri.isValid()); std::string errmsg; auto dbclient = uri.connect(mongo::StringData(), errmsg); ASSERT_EQ(dbclient, static_cast(nullptr)); } TEST(MongoURI, CloneURIForServer) { auto sw_uri = MongoURI::parse( "mongodb://localhost:27017,localhost:27018,localhost:27019/admin?replicaSet=rs1&ssl=true"); ASSERT_OK(sw_uri.getStatus()); auto uri = sw_uri.getValue(); ASSERT_EQ(uri.type(), kSet); ASSERT_EQ(uri.getSetName(), "rs1"); ASSERT_EQ(uri.getServers().size(), static_cast(3)); auto& uriOptions = uri.getOptions(); ASSERT_EQ(uriOptions.at("ssl"), "true"); auto clonedURI = uri.cloneURIForServer(mongo::HostAndPort{"localhost:27020"}); ASSERT_EQ(clonedURI.type(), kMaster); ASSERT_TRUE(clonedURI.getSetName().empty()); ASSERT_EQ(clonedURI.getServers().size(), static_cast(1)); auto& clonedURIOptions = clonedURI.getOptions(); ASSERT_EQ(clonedURIOptions.at("ssl"), "true"); } } // namespace