summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTyler Kaye <tyler.kaye@mongodb.com>2017-07-05 17:45:41 -0400
committerTyler Kaye <tkaye@princeton.edu>2017-08-10 13:55:46 -0400
commit880e3c102363611ef09b451737276c0ad9400d11 (patch)
treecc6cefb3e115c1f78cd392ad09408ba503fa2123
parent1dc5e4ba4e684c20a250238584f3b3d674d4c337 (diff)
downloadmongo-880e3c102363611ef09b451737276c0ad9400d11.tar.gz
SERVER-29923 Re-write the Mongo Server's URI parser and Testing Suite
-rw-r--r--src/mongo/client/mongo_uri.cpp279
-rw-r--r--src/mongo/client/mongo_uri_test.cpp321
-rw-r--r--src/mongo/client/mongo_uri_tests/mongo-uri-host-identifiers.json203
-rw-r--r--src/mongo/client/mongo_uri_tests/mongo-uri-invalid.json247
-rw-r--r--src/mongo/client/mongo_uri_tests/mongo-uri-options.json25
-rw-r--r--src/mongo/client/mongo_uri_tests/mongo-uri-unix-sockets-absolute.json373
-rw-r--r--src/mongo/client/mongo_uri_tests/mongo-uri-unix-sockets-relative.json466
-rw-r--r--src/mongo/client/mongo_uri_tests/mongo-uri-valid-auth.json311
-rw-r--r--src/mongo/client/mongo_uri_tests/mongo-uri-warnings.json56
9 files changed, 2198 insertions, 83 deletions
diff --git a/src/mongo/client/mongo_uri.cpp b/src/mongo/client/mongo_uri.cpp
index 6e6a7825b2d..0d5a9c5bd80 100644
--- a/src/mongo/client/mongo_uri.cpp
+++ b/src/mongo/client/mongo_uri.cpp
@@ -32,92 +32,265 @@
#include "mongo/client/mongo_uri.h"
-#include <regex>
+#include <tuple>
#include "mongo/base/status_with.h"
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/client/dbclientinterface.h"
#include "mongo/client/sasl_client_authenticate.h"
+#include "mongo/db/namespace_string.h"
+#include "mongo/util/hex.h"
#include "mongo/util/mongoutils/str.h"
-#include "mongo/util/password_digest.h"
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string/classification.hpp>
+#include <boost/algorithm/string/find_iterator.hpp>
#include <boost/algorithm/string/predicate.hpp>
-#include <boost/algorithm/string/split.hpp>
namespace mongo {
namespace {
-const char kMongoDBURL[] =
- // scheme: non-capturing
- "mongodb://"
- // credentials: two inner captures for user and password
- "(?:([^:]+)(?::([^@]+))?@)?"
+// Helper Method for MongoURI::parse() to urlDecode the components of the URI
+StatusWith<std::string> urlDecode(StringData toDecode) {
+ std::ostringstream out;
+ for (size_t i = 0; i < toDecode.size(); ++i) {
+ if (toDecode.substr(i, 1) == "%") {
+ if (i + 2 > toDecode.size()) {
+ return Status(ErrorCodes::FailedToParse, "");
+ }
+ out << mongo::fromHex(toDecode.substr(i + 1, 2));
+ i += 2;
+ } else {
+ out << toDecode.substr(i, 1);
+ }
+ }
+ return out.str();
+}
+
+/* Helper Method for MongoURI::parse() to split a string into exactly 2 pieces by a char delimeter
+ */
- // servers: grabs all host:port or UNIX socket names
- "((?:[^\\/]+|/.+\\.sock)(?:,(?:[^\\/]+|/.+\\.sock))*)"
+/* partitionForward() splits a string by the first occurance of the character delimeter
- // database and options are grouped together
- "(?:/"
+ Params:
+ str: The string to be split
+ c: The char delimeter
- // database: matches anything but the chars that cannot be part of a MongoDB database name which
- // are (in order) - forward slash, back slash, dot, space, double-quote, dollar sign, asterisk,
- // less than, greater than, colon, pipe, question mark.
- "([^/\\\\\\.\\ \"\\$*<>:\\|\\?]*)?"
+ Returns:
+ std::tuple<StringData, StringData>
+*/
+std::tuple<StringData, StringData> partitionForward(const StringData str, const char c) {
+ std::size_t delim = str.find(c);
+ if (delim == std::string::npos) {
+ return std::make_tuple(str, StringData());
+ }
+ return std::make_tuple(str.substr(0, delim), str.substr(delim + 1));
+}
- // options
- "(?:\\?([^&=?]+=[^&=?]+(?:&[^&=?]+=[^&=?]+)*))?"
+/* partitionBackward() splits a string by the last occurance of the character delimeter
- // close db/options group
- ")?";
+ Params:
+ str: The string to be split
+ c: The char delimeter
-} // namespace
+ Returns:
+ std::tuple<StringData, StringData>
+*/
+std::tuple<StringData, StringData> partitionBackward(const StringData str, const char c) {
+ std::size_t delim = str.rfind(c);
+ if (delim == std::string::npos) {
+ return std::make_tuple(StringData(), str);
+ }
+ return std::make_tuple(str.substr(0, delim), str.substr(delim + 1));
+}
+} // namespace
+
StatusWith<MongoURI> MongoURI::parse(const std::string& url) {
- if (!boost::algorithm::starts_with(url, "mongodb://")) {
+ StringData urlSD = StringData(url);
+ StringData prefix("mongodb://");
+ if (!urlSD.startsWith(prefix)) {
auto cs_status = ConnectionString::parse(url);
if (!cs_status.isOK()) {
return cs_status.getStatus();
}
-
return MongoURI(cs_status.getValue());
}
- const std::regex mongoUrlRe(kMongoDBURL);
- std::smatch matches;
- if (!std::regex_match(url, matches, mongoUrlRe)) {
- return Status(ErrorCodes::FailedToParse,
- str::stream() << "Failed to parse mongodb:// URL: " << url);
+
+ // 1. Validate and remove the scheme prefix mongodb://
+ StringData uriWithoutPrefix = urlSD.substr(prefix.size());
+
+ // 2. Split the string by the first, unescaped / (if any), yielding:
+ // split[0]: User information and host identifers
+ // split[1]: Auth database and connection options
+ auto t1 = partitionForward(uriWithoutPrefix, '/');
+ StringData userAndHostInfo = std::get<0>(t1);
+ StringData databaseAndOptions = std::get<1>(t1);
+
+ // 2.b Make sure that there are no question marks in the left side of the /
+ // as any options after the ? must still have the / delimeter
+ if (databaseAndOptions.empty() &&
+ userAndHostInfo.toString().find_first_of("?") != std::string::npos) {
+ return Status(
+ ErrorCodes::FailedToParse,
+ str::stream()
+ << "URI must contain slash delimeter between hosts and options for mongodb:// URL: "
+ << url);
}
- // We have the whole input plus 5 top level captures (user, password, host, db, options).
- invariant(matches.size() == 6);
+ // 3. Split the user information and host identifiers string by the last, unescaped @, yielding:
+ // split[0]: User information
+ // split[1]: Host identifiers;
+ auto t2 = partitionBackward(userAndHostInfo, '@');
+ StringData userInfo = std::get<0>(t2);
+ StringData hostIdentifiers = std::get<1>(t2);
+
+ // 4. Validate, split (if applicable), and URL decode the user information, yielding:
+ // split[0] = username
+ // split[1] = password
+ auto t3 = partitionForward(userInfo, ':');
+ StringData usernameSD = std::get<0>(t3);
+ StringData passwordSD = std::get<1>(t3);
+ std::size_t found = usernameSD.toString().find_first_of(":@");
+ if (found != std::string::npos) {
+ return Status(ErrorCodes::FailedToParse,
+ str::stream() << "Username must be URL Encoded for mongodb:// URL: " << url);
+ }
+ found = passwordSD.toString().find_first_of(":@");
+ if (found != std::string::npos) {
+ return Status(ErrorCodes::FailedToParse,
+ str::stream() << "Password must be URL Encoded for mongodb:// URL: " << url);
+ }
- if (!matches[3].matched) {
+ // Get the username and make sure it did not fail to decode
+ auto usernameWithStatus = urlDecode(usernameSD);
+ if (!usernameWithStatus.isOK())
+ return Status(
+ ErrorCodes::FailedToParse,
+ str::stream() << "Username cannot properly be URL decoded for mongodb:// URL: " << url);
+ std::string username = usernameWithStatus.getValue();
+
+ // Get the password and make sure it did not fail to decode
+ auto passwordWithStatus = urlDecode(passwordSD);
+ if (!passwordWithStatus.isOK())
+ return Status(
+ ErrorCodes::FailedToParse,
+ str::stream() << "Password cannot properly be URL decoded for mongodb:// URL: " << url);
+ std::string password = passwordWithStatus.getValue();
+
+ // 5. Validate, split, and URL decode the host identifiers.
+ if (hostIdentifiers.empty()) {
return Status(ErrorCodes::FailedToParse, "No server(s) specified");
}
+ std::string hostIdentifiersStr = hostIdentifiers.toString();
+ std::vector<HostAndPort> servers;
+ for (auto i = boost::make_split_iterator(hostIdentifiersStr,
+ boost::first_finder(",", boost::is_iequal()));
+ i != std::remove_reference<decltype((i))>::type{};
+ ++i) {
+ auto hostWithStatus = urlDecode(boost::copy_range<std::string>(*i));
+ if (!hostWithStatus.isOK()) {
+ return Status(
+ ErrorCodes::FailedToParse,
+ str::stream() << "Host cannot properly be URL decoded for mongodb:// URL: " << url);
+ }
- std::map<std::string, std::string> options;
-
- if (matches[5].matched) {
- const std::string optionsMatch = matches[5].str();
+ auto statusHostAndPort = HostAndPort::parse(hostWithStatus.getValue());
+ if (!statusHostAndPort.isOK()) {
+ return statusHostAndPort.getStatus();
+ }
+ servers.push_back(statusHostAndPort.getValue());
+ }
- std::vector<boost::iterator_range<std::string::const_iterator>> optionsTokens;
- boost::algorithm::split(optionsTokens, optionsMatch, boost::algorithm::is_any_of("=&"));
+ // 6. Split the auth database and connection options string by the first, unescaped ?, yielding:
+ // split[0] = auth database
+ // split[1] = connection options
+ auto t4 = partitionForward(databaseAndOptions, '?');
+ StringData databaseSD = std::get<0>(t4);
+ StringData connectionOptions = std::get<1>(t4);
+ auto databaseWithStatus = urlDecode(databaseSD);
+ if (!databaseWithStatus.isOK()) {
+ return Status(ErrorCodes::FailedToParse,
+ str::stream()
+ << "Database name cannot properly be URL decoded for mongodb:// URL: "
+ << url);
+ }
+ std::string database = databaseWithStatus.getValue();
+
+ // 7. Validate the database contains no prohibited characters
+ // Prohibited characters:
+ // slash ("/"), backslash ("\"), space (" "), double-quote ("""), or dollar sign ("$")
+ // period (".") is also prohibited, but drivers MAY allow periods
+ if (!database.empty() &&
+ !NamespaceString::validDBName(database,
+ NamespaceString::DollarInDbNameBehavior::Disallow)) {
+ return Status(ErrorCodes::FailedToParse,
+ str::stream()
+ << "Database name cannot have reserved characters for mongodb:// URL: "
+ << url);
+ }
- if (optionsTokens.size() % 2 != 0) {
+ // 8. Validate, split, and URL decode the connection options
+ std::map<std::string, std::string> options;
+ if (!connectionOptions.empty()) {
+ std::string connectionOptionsStr = connectionOptions.toString();
+ std::size_t foundQ = connectionOptionsStr.find_first_of("?");
+ if (foundQ != std::string::npos) {
return Status(ErrorCodes::FailedToParse,
str::stream()
- << "Missing a key or value in the options for mongodb:// URL: "
+ << "URI Cannot Contain multiple questions marks for mongodb:// URL: "
<< url);
- ;
}
+ for (auto i = boost::make_split_iterator(connectionOptionsStr,
+ boost::first_finder("&", boost::is_iequal()));
+ i != std::remove_reference<decltype((i))>::type{};
+ ++i) {
+ std::string opt = boost::copy_range<std::string>(*i);
+ if (opt.empty()) {
+ return Status(ErrorCodes::FailedToParse,
+ str::stream()
+ << "Missing a key/value pair in the options for mongodb:// URL: "
+ << url);
+ }
+ auto t5 = partitionForward(opt, '=');
+ StringData key = std::get<0>(t5);
+ StringData value = std::get<1>(t5);
+ if (key.empty()) {
+ return Status(
+ ErrorCodes::FailedToParse,
+ str::stream()
+ << "Missing a key for key/value pair in the options for mongodb:// URL: "
+ << url);
+ }
+ if (value.empty()) {
+ return Status(
+ ErrorCodes::FailedToParse,
+ str::stream()
+ << "Missing a value for key/value pair in the options for mongodb:// URL: "
+ << url);
+ }
+ auto keyWithStatus = urlDecode(key);
+ if (!keyWithStatus.isOK()) {
+ return Status(
+ ErrorCodes::FailedToParse,
+ str::stream()
+ << "Key in options cannot properly be URL decoded for mongodb:// URL: "
+ << url);
+ }
+ auto valueWithStatus = urlDecode(value);
+ if (!valueWithStatus.isOK()) {
+ return Status(
+ ErrorCodes::FailedToParse,
+ str::stream()
+ << "Value in options cannot properly be URL decoded for mongodb:// URL: "
+ << url);
+ }
+
- for (size_t i = 0; i != optionsTokens.size(); i = i + 2)
- options[std::string(optionsTokens[i].begin(), optionsTokens[i].end())] =
- std::string(optionsTokens[i + 1].begin(), optionsTokens[i + 1].end());
+ options[keyWithStatus.getValue()] = valueWithStatus.getValue();
+ }
}
OptionsMap::const_iterator optIter;
@@ -129,33 +302,15 @@ StatusWith<MongoURI> MongoURI::parse(const std::string& url) {
setName = optIter->second;
}
- std::vector<HostAndPort> servers;
-
- {
- std::vector<std::string> servers_split;
- const std::string serversStr = matches[3].str();
- boost::algorithm::split(servers_split, serversStr, boost::is_any_of(","));
- for (auto&& s : servers_split) {
- auto statusHostAndPort = HostAndPort::parse(s);
- if (!statusHostAndPort.isOK()) {
- return statusHostAndPort.getStatus();
- }
-
- servers.push_back(statusHostAndPort.getValue());
- }
- }
-
const bool direct = !haveSetName && (servers.size() == 1);
if (!direct && setName.empty()) {
return Status(ErrorCodes::FailedToParse,
"Cannot list multiple servers in URL without 'replicaSet' option");
}
-
ConnectionString cs(
direct ? ConnectionString::MASTER : ConnectionString::SET, servers, setName);
- return MongoURI(
- std::move(cs), matches[1].str(), matches[2].str(), matches[4].str(), std::move(options));
+ return MongoURI(std::move(cs), username, password, database, std::move(options));
}
} // namespace mongo
diff --git a/src/mongo/client/mongo_uri_test.cpp b/src/mongo/client/mongo_uri_test.cpp
index 6a5f6c9882a..5302ad14a9d 100644
--- a/src/mongo/client/mongo_uri_test.cpp
+++ b/src/mongo/client/mongo_uri_test.cpp
@@ -28,11 +28,17 @@
#include "mongo/platform/basic.h"
-#include "mongo/client/mongo_uri.h"
+#include <fstream>
#include "mongo/base/string_data.h"
+#include "mongo/bson/bsonobj.h"
+#include "mongo/bson/bsontypes.h"
+#include "mongo/bson/json.h"
+#include "mongo/client/mongo_uri.h"
#include "mongo/unittest/unittest.h"
+#include <boost/filesystem/operations.hpp>
+
namespace {
using mongo::MongoURI;
@@ -60,8 +66,6 @@ const URITestCase validCases[] = {
{"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, ""},
@@ -70,6 +74,65 @@ const URITestCase validCases[] = {
{"mongodb://127.0.0.1:1234/dbName?foo=a&c=b", "", "", kMaster, "", 1, 2, "dbName"},
+ {"mongodb://127.0.0.1/dbName?foo=a&c=b", "", "", kMaster, "", 1, 2, "dbName"},
+
+ {"mongodb://user:pwd@127.0.0.1,127.0.0.2/dbname?a=b&replicaSet=replName",
+ "user",
+ "pwd",
+ kSet,
+ "replName",
+ 2,
+ 2,
+ "dbname"},
+
+ {"mongodb://needs%20encoding%25%23!%3C%3E:pwd@127.0.0.1,127.0.0.2/"
+ "dbname?a=b&replicaSet=replName",
+ "needs encoding%#!<>",
+ "pwd",
+ kSet,
+ "replName",
+ 2,
+ 2,
+ "dbname"},
+
+ {"mongodb://needs%20encoding%25%23!%3C%3E:pwd@127.0.0.1,127.0.0.2/"
+ "db@name?a=b&replicaSet=replName",
+ "needs encoding%#!<>",
+ "pwd",
+ kSet,
+ "replName",
+ 2,
+ 2,
+ "db@name"},
+
+ {"mongodb://user:needs%20encoding%25%23!%3C%3E@127.0.0.1,127.0.0.2/"
+ "dbname?a=b&replicaSet=replName",
+ "user",
+ "needs encoding%#!<>",
+ kSet,
+ "replName",
+ 2,
+ 2,
+ "dbname"},
+
+ {"mongodb://user:pwd@127.0.0.1,127.0.0.2/dbname?a=b&replicaSet=needs%20encoding%25%23!%3C%3E",
+ "user",
+ "pwd",
+ kSet,
+ "needs encoding%#!<>",
+ 2,
+ 2,
+ "dbname"},
+
+ {"mongodb://user:pwd@127.0.0.1,127.0.0.2/needsencoding%40hello?a=b&replicaSet=replName",
+ "user",
+ "pwd",
+ kSet,
+ "replName",
+ 2,
+ 2,
+ "needsencoding@hello"},
+
{"mongodb://user:pwd@127.0.0.1,127.0.0.2/?replicaSet=replName",
"user",
"pwd",
@@ -259,21 +322,27 @@ const URITestCase validCases[] = {
1,
2,
""},
- {"mongodb:///tmp/mongodb-27017.sock", "", "", kMaster, "", 1, 0, ""},
- {"mongodb:///tmp/mongodb-27017.sock,/tmp/mongodb-27018.sock/?replicaSet=replName",
+ {"mongodb://%2Ftmp%2Fmongodb-27017.sock", "", "", kMaster, "", 1, 0, ""},
+
+ {"mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/?replicaSet=replName",
"",
"",
kSet,
"replName",
2,
1,
- ""}};
+ ""},
+};
const InvalidURITestCase invalidCases[] = {
// No host.
{"mongodb://"},
+ {"mongodb://usr:pwd@/dbname?a=b"},
+
+ // Username and password must be encoded (cannot have ':' or '@')
+ {"mongodb://usr:pwd:@127.0.0.1/dbName?foo=a&c=b"},
// Needs a "/" after the hosts and before the options.
{"mongodb://localhost:27017,localhost:27018?replicaSet=missingSlash"},
@@ -284,30 +353,193 @@ const InvalidURITestCase invalidCases[] = {
// Domain sockets have to end in ".sock".
{"mongodb:///notareal/domainsock"},
+ // Database name cannot contain slash ("/"), backslash ("\"), space (" "), double-quote ("""),
+ // or dollar sign ("$")
+ {"mongodb://usr:pwd@localhost:27017/db$name?a=b"},
+ {"mongodb://usr:pwd@localhost:27017/db/name?a=b"},
+ {"mongodb://usr:pwd@localhost:27017/db\\name?a=b"},
+ {"mongodb://usr:pwd@localhost:27017/db name?a=b"},
+ {"mongodb://usr:pwd@localhost:27017/db\"name?a=b"},
+
+ // Options must have a key
+ {"mongodb://usr:pwd@localhost:27017/dbname?=b"},
+
+ // Cannot skip a key value pair
+ {"mongodb://usr:pwd@localhost:27017/dbname?a=b&&b=c"},
+
+ // Multiple Unix domain sockets and auth DB resembling a socket (relative path)
+ {"mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin.sock?replicaSet=replName"},
+
+ // Multiple Unix domain sockets with auth DB resembling a path (relative path)
+ {"mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin.shoe?replicaSet=replName"},
+
+ // Multiple Unix domain sockets and auth DB resembling a socket (absolute path)
+ {"mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/"
+ "admin.sock?replicaSet=replName"},
+
+ // Multiple Unix domain sockets with auth DB resembling a path (absolute path)
+ {"mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/"
+ "admin.shoe?replicaSet=replName"},
+
+ // Missing value in key value pair for options
+ {"mongodb://127.0.0.1:1234/dbName?foo=a&c=b&d"},
+ {"mongodb://127.0.0.1:1234/dbName?foo=a&c=b&d="},
+ {"mongodb://127.0.0.1:1234/dbName?foo=a&h=&c=b&d=6"},
+ {"mongodb://127.0.0.1:1234/dbName?foo=a&h&c=b&d=6"},
+
// Options can't have multiple question marks. Only one.
{"mongodb://localhost:27017/?foo=a?c=b&d=e?asdf=foo"},
+
+ // Missing a key in key value pair for options
+ {"mongodb://127.0.0.1:1234/dbName?foo=a&=d&c=b"},
+
+ // Missing an entire key-value pair
+ {"mongodb://127.0.0.1:1234/dbName?foo=a&&c=b"},
};
+// Helper Method to take a filename for a json file and return the array of tests inside of it
+mongo::BSONObj getBsonFromJsonFile(std::string fileName) {
+ boost::filesystem::path directoryPath = boost::filesystem::current_path();
+ boost::filesystem::path filePath(directoryPath / "src" / "mongo" / "client" /
+ "mongo_uri_tests" / fileName);
+ std::string filename(filePath.string());
+ std::ifstream infile(filename.c_str());
+ std::string data((std::istreambuf_iterator<char>(infile)), std::istreambuf_iterator<char>());
+ mongo::BSONObj obj = mongo::fromjson(data);
+ ASSERT(obj.valid(mongo::BSONVersion::kLatest));
+ ASSERT(obj.hasField("tests"));
+ mongo::BSONObj arr = obj.getField("tests").embeddedObject().getOwned();
+ ASSERT(arr.couldBeArray());
+ return arr;
+}
+
+// Helper method to take a BSONElement and either extract its string or return an empty string
+std::string returnStringFromElementOrNull(mongo::BSONElement element) {
+ ASSERT(!element.eoo());
+ if (element.type() == mongo::jstNULL) {
+ return std::string();
+ }
+ ASSERT(element.type() == mongo::String);
+ return element.String();
+}
+
+// Helper method to take a valid test case, parse() it, and assure the output is correct
+void testValidURIFormat(URITestCase testCase) {
+ mongo::unittest::log() << "Testing URI: " << testCase.URI << '\n';
+ std::string errMsg;
+ auto cs_status = MongoURI::parse(testCase.URI);
+ if (!cs_status.getStatus().toString().empty()) {
+ if (!cs_status.getStatus().isOK())
+ mongo::unittest::log() << "ERROR: 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());
+}
+
+// Helper method to parse a BSON array/object and extract the individual tests
+// Method creates a URITestCase from every element in the array and then verifies that parse() has
+// the proper output
+void runTests(mongo::BSONObj tests) {
+ mongo::BSONObjIterator testsIter(tests);
+ while (testsIter.more()) {
+ mongo::BSONElement testElement = testsIter.next();
+ if (testElement.eoo())
+ break;
+ mongo::BSONObj test = testElement.embeddedObject();
+
+ // First extract the valid field and the uri field
+ mongo::BSONElement validDoc = test.getField("valid");
+ ASSERT(!validDoc.eoo());
+ ASSERT(validDoc.isBoolean());
+ bool valid = validDoc.Bool();
+
+ mongo::BSONElement uriDoc = test.getField("uri");
+ ASSERT(!uriDoc.eoo());
+ ASSERT(uriDoc.type() == mongo::String);
+ std::string uri = uriDoc.String();
+
+ if (!valid) {
+ // This uri string is invalid --> parse the uri and ensure it fails
+ const InvalidURITestCase testCase = {uri};
+ mongo::unittest::log() << "Testing URI: " << testCase.URI << '\n';
+ auto cs_status = MongoURI::parse(testCase.URI);
+ ASSERT_FALSE(cs_status.isOK());
+ } else {
+ // This uri is valid -- > parse the remaining necessary fields
+
+ // parse the auth options
+ std::string database = std::string();
+ std::string username = std::string();
+ std::string password = std::string();
+
+ mongo::BSONElement auth = test.getField("auth");
+ ASSERT(!auth.eoo());
+ if (auth.type() != mongo::jstNULL) {
+ ASSERT(auth.type() == mongo::Object);
+ mongo::BSONObj authObj = auth.embeddedObject();
+
+ mongo::BSONElement dbObj = authObj.getField("db");
+ database = returnStringFromElementOrNull(dbObj);
+
+ mongo::BSONElement usernameObj = authObj.getField("username");
+ username = returnStringFromElementOrNull(usernameObj);
+
+ mongo::BSONElement passwordObj = authObj.getField("password");
+ password = returnStringFromElementOrNull(passwordObj);
+ }
+
+ // parse the hosts
+ size_t numHosts = 0;
+ mongo::BSONElement hosts = test.getField("hosts");
+ ASSERT(!hosts.eoo());
+ ASSERT(hosts.type() == mongo::Array);
+ mongo::BSONObjIterator hostsIter(hosts.embeddedObject());
+ while (hostsIter.more()) {
+ mongo::BSONElement cHost = hostsIter.next();
+ if (cHost.eoo())
+ break;
+ numHosts++;
+ }
+
+ // parse the options
+ mongo::ConnectionString::ConnectionType connectionType = kMaster;
+ size_t numOptions = 0;
+ std::string setName = std::string();
+ mongo::BSONElement optionsElement = test.getField("options");
+ ASSERT(!optionsElement.eoo());
+ if (optionsElement.type() != mongo::jstNULL) {
+ ASSERT(optionsElement.type() == mongo::Object);
+ mongo::BSONObj optionsObj = optionsElement.embeddedObject();
+ numOptions = optionsObj.nFields();
+ mongo::BSONElement replsetElement = optionsObj.getField("replicaSet");
+ if (!replsetElement.eoo()) {
+ ASSERT(replsetElement.type() == mongo::String);
+ setName = replsetElement.String();
+ connectionType = kSet;
+ }
+ }
+
+ // Create the URITestCase abnd
+ const URITestCase testCase = {
+ uri, username, password, connectionType, setName, numHosts, numOptions, database};
+ testValidURIFormat(testCase);
+ }
+ }
+}
+
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());
+ testValidURIFormat(testCase);
}
}
@@ -357,4 +589,51 @@ TEST(MongoURI, CloneURIForServer) {
ASSERT_EQ(clonedURIOptions.at("ssl"), "true");
}
+/* These tests come from the Mongo Uri Specifications for the drivers found at:
+ https://github.com/mongodb/specifications/tree/master/source/connection-string/tests
+ They have been slighly altered as the Drivers specification is slighly different from the server
+ specification
+*/
+TEST(MongoURI, ValidAuth) {
+ std::string fileName = "mongo-uri-valid-auth.json";
+ mongo::BSONObj tests = getBsonFromJsonFile(fileName);
+ runTests(tests);
+}
+
+TEST(MongoURI, Options) {
+ std::string fileName = "mongo-uri-options.json";
+ mongo::BSONObj tests = getBsonFromJsonFile(fileName);
+ runTests(tests);
+}
+
+TEST(MongoURI, UnixSocketsAbsolute) {
+ std::string fileName = "mongo-uri-unix-sockets-absolute.json";
+ mongo::BSONObj tests = getBsonFromJsonFile(fileName);
+ runTests(tests);
+}
+
+TEST(MongoURI, UnixSocketsRelative) {
+ std::string fileName = "mongo-uri-unix-sockets-relative.json";
+ mongo::BSONObj tests = getBsonFromJsonFile(fileName);
+ runTests(tests);
+}
+
+TEST(MongoURI, Warnings) {
+ std::string fileName = "mongo-uri-warnings.json";
+ mongo::BSONObj tests = getBsonFromJsonFile(fileName);
+ runTests(tests);
+}
+
+TEST(MongoURI, HostIdentifiers) {
+ std::string fileName = "mongo-uri-host-identifiers.json";
+ mongo::BSONObj tests = getBsonFromJsonFile(fileName);
+ runTests(tests);
+}
+
+TEST(MongoURI, Invalid) {
+ std::string fileName = "mongo-uri-invalid.json";
+ mongo::BSONObj tests = getBsonFromJsonFile(fileName);
+ runTests(tests);
+}
+
} // namespace
diff --git a/src/mongo/client/mongo_uri_tests/mongo-uri-host-identifiers.json b/src/mongo/client/mongo_uri_tests/mongo-uri-host-identifiers.json
new file mode 100644
index 00000000000..8a1fda580a8
--- /dev/null
+++ b/src/mongo/client/mongo_uri_tests/mongo-uri-host-identifiers.json
@@ -0,0 +1,203 @@
+{
+ "tests": [
+ {
+ "description": "Single IPv4 host without port",
+ "uri": "mongodb://127.0.0.1",
+ "valid": true,
+ "warning": false,
+ "hosts": [
+ {
+ "type": "ipv4",
+ "host": "127.0.0.1",
+ "port": null
+ }
+ ],
+ "auth": null,
+ "options": null
+ },
+ {
+ "description": "Single IPv4 host with port",
+ "uri": "mongodb://127.0.0.1:27018",
+ "valid": true,
+ "warning": false,
+ "hosts": [
+ {
+ "type": "ipv4",
+ "host": "127.0.0.1",
+ "port": 27018
+ }
+ ],
+ "auth": null,
+ "options": null
+ },
+ {
+ "description": "Single IP literal host without port",
+ "uri": "mongodb://[::1]",
+ "valid": true,
+ "warning": false,
+ "hosts": [
+ {
+ "type": "ip_literal",
+ "host": "::1",
+ "port": null
+ }
+ ],
+ "auth": null,
+ "options": null
+ },
+ {
+ "description": "Single IP literal host with port",
+ "uri": "mongodb://[::1]:27019",
+ "valid": true,
+ "warning": false,
+ "hosts": [
+ {
+ "type": "ip_literal",
+ "host": "::1",
+ "port": 27019
+ }
+ ],
+ "auth": null,
+ "options": null
+ },
+ {
+ "description": "Single hostname without port",
+ "uri": "mongodb://example.com",
+ "valid": true,
+ "warning": false,
+ "hosts": [
+ {
+ "type": "hostname",
+ "host": "example.com",
+ "port": null
+ }
+ ],
+ "auth": null,
+ "options": null
+ },
+ {
+ "description": "Single hostname with port",
+ "uri": "mongodb://example.com:27020",
+ "valid": true,
+ "warning": false,
+ "hosts": [
+ {
+ "type": "hostname",
+ "host": "example.com",
+ "port": 27020
+ }
+ ],
+ "auth": null,
+ "options": null
+ },
+ {
+ "description": "Single hostname (resembling IPv4) without port",
+ "uri": "mongodb://256.0.0.1",
+ "valid": true,
+ "warning": false,
+ "hosts": [
+ {
+ "type": "hostname",
+ "host": "256.0.0.1",
+ "port": null
+ }
+ ],
+ "auth": null,
+ "options": null
+ },
+ {
+ "description": "Multiple hosts (mixed formats)",
+ "uri": "mongodb://127.0.0.1,[::1]:27018,example.com:27019",
+ "valid": false,
+ "warning": false,
+ "hosts": [
+ {
+ "type": "ipv4",
+ "host": "127.0.0.1",
+ "port": null
+ },
+ {
+ "type": "ip_literal",
+ "host": "::1",
+ "port": 27018
+ },
+ {
+ "type": "hostname",
+ "host": "example.com",
+ "port": 27019
+ }
+ ],
+ "auth": null,
+ "options": null
+ },
+ {
+ "description": "Multiple hosts (mixed formats)",
+ "uri": "mongodb://127.0.0.1,[::1]:27018,example.com:27019/?replicaSet=replset",
+ "valid": true,
+ "warning": false,
+ "hosts": [
+ {
+ "type": "ipv4",
+ "host": "127.0.0.1",
+ "port": null
+ },
+ {
+ "type": "ip_literal",
+ "host": "::1",
+ "port": 27018
+ },
+ {
+ "type": "hostname",
+ "host": "example.com",
+ "port": 27019
+ }
+ ],
+ "auth": null,
+ "options": {
+ "replicaSet": "replset"
+ }
+ },
+ {
+ "description": "UTF-8 hosts",
+ "uri": "mongodb://bücher.example.com,umläut.example.com/",
+ "valid": false,
+ "warning": false,
+ "hosts": [
+ {
+ "type": "hostname",
+ "host": "bücher.example.com",
+ "port": null
+ },
+ {
+ "type": "hostname",
+ "host": "umläut.example.com",
+ "port": null
+ }
+ ],
+ "auth": null,
+ "options": null
+ },
+ {
+ "description": "UTF-8 hosts",
+ "uri": "mongodb://bücher.example.com,umläut.example.com/?replicaSet=replset",
+ "valid": true,
+ "warning": false,
+ "hosts": [
+ {
+ "type": "hostname",
+ "host": "bücher.example.com",
+ "port": null
+ },
+ {
+ "type": "hostname",
+ "host": "umläut.example.com",
+ "port": null
+ }
+ ],
+ "auth": null,
+ "options": {
+ "replicaSet": "replset"
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/mongo/client/mongo_uri_tests/mongo-uri-invalid.json b/src/mongo/client/mongo_uri_tests/mongo-uri-invalid.json
new file mode 100644
index 00000000000..3ee82bd535e
--- /dev/null
+++ b/src/mongo/client/mongo_uri_tests/mongo-uri-invalid.json
@@ -0,0 +1,247 @@
+{
+ "tests": [
+ {
+ "auth": null,
+ "description": "Empty string",
+ "hosts": null,
+ "options": null,
+ "uri": "",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Missing host",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Double colon in host identifier",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://localhost::27017",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Double colon in host identifier and trailing slash",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://localhost::27017/",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Double colon in host identifier with missing host and port",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://::",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Double colon in host identifier with missing port",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://localhost,localhost::",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Double colon in host identifier and second host",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://localhost::27017,abc",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Invalid port (negative number) with hostname",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://localhost:-1",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Invalid port (zero) with hostname",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://localhost:0/",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Invalid port (positive number) with hostname",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://localhost:65536",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Invalid port (positive number) with hostname and trailing slash",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://localhost:65536/",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Invalid port (non-numeric string) with hostname",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://localhost:foo",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Invalid port (negative number) with IP literal",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://[::1]:-1",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Invalid port (zero) with IP literal",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://[::1]:0/",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Invalid port (positive number) with IP literal",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://[::1]:65536",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Invalid port (positive number) with IP literal and trailing slash",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://[::1]:65536/",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Invalid port (non-numeric string) with IP literal",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://[::1]:foo",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Missing delimiting slash between hosts and options",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://example.com?w=1",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Incomplete key value pair for option",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://example.com/?w",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Username with password containing an unescaped colon",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://alice:foo:bar@127.0.0.1",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Username with password containing an unescaped colon",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://alice:foo:bar@127.0.0.1",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Username containing an unescaped at-sign",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://alice@@127.0.0.1",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Username with password containing an unescaped at-sign",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://alice@foo:bar@127.0.0.1",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Username containing an unescaped slash",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://alice/@localhost/db",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Username containing unescaped slash with password",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://alice/bob:foo@localhost/db",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Username with password containing an unescaped slash",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb://alice:foo/bar@localhost/db",
+ "valid": false,
+ "warning": null
+ },
+ {
+ "auth": null,
+ "description": "Host with unescaped slash",
+ "hosts": null,
+ "options": null,
+ "uri": "mongodb:///tmp/mongodb-27017.sock/",
+ "valid": false,
+ "warning": null
+ }
+ ]
+}
diff --git a/src/mongo/client/mongo_uri_tests/mongo-uri-options.json b/src/mongo/client/mongo_uri_tests/mongo-uri-options.json
new file mode 100644
index 00000000000..4c2bded9e72
--- /dev/null
+++ b/src/mongo/client/mongo_uri_tests/mongo-uri-options.json
@@ -0,0 +1,25 @@
+{
+ "tests": [
+ {
+ "description": "Option names are normalized to lowercase",
+ "uri": "mongodb://alice:secret@example.com/admin?AUTHMechanism=MONGODB-CR",
+ "valid": true,
+ "warning": false,
+ "hosts": [
+ {
+ "type": "hostname",
+ "host": "example.com",
+ "port": null
+ }
+ ],
+ "auth": {
+ "username": "alice",
+ "password": "secret",
+ "db": "admin"
+ },
+ "options": {
+ "authmechanism": "MONGODB-CR"
+ }
+ }
+ ]
+}
diff --git a/src/mongo/client/mongo_uri_tests/mongo-uri-unix-sockets-absolute.json b/src/mongo/client/mongo_uri_tests/mongo-uri-unix-sockets-absolute.json
new file mode 100644
index 00000000000..d167c315bbc
--- /dev/null
+++ b/src/mongo/client/mongo_uri_tests/mongo-uri-unix-sockets-absolute.json
@@ -0,0 +1,373 @@
+{
+ "tests": [
+ {
+ "auth": null,
+ "description": "Unix domain socket (absolute path with trailing slash)",
+ "hosts": [
+ {
+ "host": "/tmp/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://%2Ftmp%2Fmongodb-27017.sock/",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": null,
+ "description": "Unix domain socket (absolute path without trailing slash)",
+ "hosts": [
+ {
+ "host": "/tmp/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://%2Ftmp%2Fmongodb-27017.sock",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": null,
+ "description": "Unix domain socket (absolute path with spaces in path)",
+ "hosts": [
+ {
+ "host": "/tmp/ /mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://%2Ftmp%2F %2Fmongodb-27017.sock",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": null,
+ "description": "Multiple Unix domain sockets (absolute paths)",
+ "hosts": [
+ {
+ "host": "/tmp/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ },
+ {
+ "host": "/tmp/mongodb-27018.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock",
+ "valid": false,
+ "warning": false
+ },
+ {
+ "auth": null,
+ "description": "Multiple hosts (absolute path and ipv4)",
+ "hosts": [
+ {
+ "host": "127.0.0.1",
+ "port": 27017,
+ "type": "ipv4"
+ },
+ {
+ "host": "/tmp/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://127.0.0.1:27017,%2Ftmp%2Fmongodb-27017.sock",
+ "valid": false,
+ "warning": false
+ },
+ {
+ "auth": null,
+ "description": "Multiple hosts (absolute path and hostname resembling relative path)",
+ "hosts": [
+ {
+ "host": "mongodb-27017.sock",
+ "port": null,
+ "type": "hostname"
+ },
+ {
+ "host": "/tmp/mongodb-27018.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://mongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock",
+ "valid": false,
+ "warning": false
+ },
+ {
+ "auth": null,
+ "description": "Multiple Unix domain sockets (absolute paths)",
+ "hosts": [
+ {
+ "host": "/tmp/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ },
+ {
+ "host": "/tmp/mongodb-27018.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": {
+ "replicaSet": "replset"
+ },
+ "uri": "mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/?replicaSet=replset",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": null,
+ "description": "Multiple hosts (absolute path and ipv4)",
+ "hosts": [
+ {
+ "host": "127.0.0.1",
+ "port": 27017,
+ "type": "ipv4"
+ },
+ {
+ "host": "/tmp/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": {
+ "replicaSet": "replset"
+ },
+ "uri": "mongodb://127.0.0.1:27017,%2Ftmp%2Fmongodb-27017.sock/?replicaSet=replset",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": null,
+ "description": "Multiple hosts (absolute path and hostname resembling relative path)",
+ "hosts": [
+ {
+ "host": "mongodb-27017.sock",
+ "port": null,
+ "type": "hostname"
+ },
+ {
+ "host": "/tmp/mongodb-27018.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": {
+ "replicaSet": "replset"
+ },
+ "uri": "mongodb://mongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/?replicaSet=replset",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": "admin",
+ "password": "foo",
+ "username": "alice"
+ },
+ "description": "Unix domain socket with auth database (absolute path)",
+ "hosts": [
+ {
+ "host": "/tmp/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://alice:foo@%2Ftmp%2Fmongodb-27017.sock/admin",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": null,
+ "description": "Unix domain socket with path resembling socket file (absolute path with trailing slash)",
+ "hosts": [
+ {
+ "host": "/tmp/path.to.sock/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://%2Ftmp%2Fpath.to.sock%2Fmongodb-27017.sock/",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": null,
+ "description": "Unix domain socket with path resembling socket file (absolute path without trailing slash)",
+ "hosts": [
+ {
+ "host": "/tmp/path.to.sock/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://%2Ftmp%2Fpath.to.sock%2Fmongodb-27017.sock",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": "admin",
+ "password": "bar",
+ "username": "bob"
+ },
+ "description": "Unix domain socket with path resembling socket file and auth (absolute path)",
+ "hosts": [
+ {
+ "host": "/tmp/path.to.sock/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://bob:bar@%2Ftmp%2Fpath.to.sock%2Fmongodb-27017.sock/admin",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": "admin",
+ "password": null,
+ "username": null
+ },
+ "description": "Multiple Unix domain sockets and auth DB (absolute path)",
+ "hosts": [
+ {
+ "host": "/tmp/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ },
+ {
+ "host": "/tmp/mongodb-27018.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/admin",
+ "valid": false,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": "admin",
+ "password": null,
+ "username": null
+ },
+ "description": "Multiple Unix domain sockets and auth DB (absolute path)",
+ "hosts": [
+ {
+ "host": "/tmp/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ },
+ {
+ "host": "/tmp/mongodb-27018.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": {
+ "replicaSet": "replset"
+ },
+ "uri": "mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/admin?replicaSet=replset",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": "admin",
+ "password": "bar",
+ "username": "bob"
+ },
+ "description": "Multiple Unix domain sockets with auth and query string (absolute path)",
+ "hosts": [
+ {
+ "host": "/tmp/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ },
+ {
+ "host": "/tmp/mongodb-27018.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": {
+ "w": 1
+ },
+ "uri": "mongodb://bob:bar@%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/admin?w=1",
+ "valid": false,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": "admin",
+ "password": "bar",
+ "username": "bob"
+ },
+ "description": "Multiple Unix domain sockets with auth and query string (absolute path)",
+ "hosts": [
+ {
+ "host": "/tmp/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ },
+ {
+ "host": "/tmp/mongodb-27018.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": {
+ "w": 1,
+ "replicaSet":"replset"
+ },
+ "uri": "mongodb://bob:bar@%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/admin?w=1&replicaSet=replset",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": "admin",
+ "password": "bar",
+ "username": "bob"
+ },
+ "description": "Multiple Unix domain sockets with auth and query string (absolute path)",
+ "hosts": [
+ {
+ "host": "/tmp/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ },
+ {
+ "host": "/tmp/mongodb-27018.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": {
+ "w": 1,
+ "replicaSet":"replset"
+ },
+ "uri": "mongodb://bob:bar@%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/admin?replicaSet=replset&w=1",
+ "valid": true,
+ "warning": false
+ }
+ ]
+}
diff --git a/src/mongo/client/mongo_uri_tests/mongo-uri-unix-sockets-relative.json b/src/mongo/client/mongo_uri_tests/mongo-uri-unix-sockets-relative.json
new file mode 100644
index 00000000000..8da45c4e1b9
--- /dev/null
+++ b/src/mongo/client/mongo_uri_tests/mongo-uri-unix-sockets-relative.json
@@ -0,0 +1,466 @@
+{
+ "tests": [
+ {
+ "auth": null,
+ "description": "Unix domain socket (relative path with trailing slash)",
+ "hosts": [
+ {
+ "host": "rel/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://rel%2Fmongodb-27017.sock/",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": null,
+ "description": "Unix domain socket (relative path without trailing slash)",
+ "hosts": [
+ {
+ "host": "rel/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://rel%2Fmongodb-27017.sock",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": null,
+ "description": "Unix domain socket (relative path with spaces)",
+ "hosts": [
+ {
+ "host": "rel/ /mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://rel%2F %2Fmongodb-27017.sock",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": null,
+ "description": "Multiple Unix domain sockets (relative paths)",
+ "hosts": [
+ {
+ "host": "rel/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ },
+ {
+ "host": "rel/mongodb-27018.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock",
+ "valid": false,
+ "warning": false
+ },
+ {
+ "auth": null,
+ "description": "Multiple Unix domain sockets (relative paths)",
+ "hosts": [
+ {
+ "host": "rel/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ },
+ {
+ "host": "rel/mongodb-27018.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": {
+ "replicaSet": "replset"
+ },
+ "uri": "mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/?replicaSet=replset",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": null,
+ "description": "Multiple Unix domain sockets (relative and absolute paths)",
+ "hosts": [
+ {
+ "host": "rel/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ },
+ {
+ "host": "/tmp/mongodb-27018.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://rel%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock",
+ "valid": false,
+ "warning": false
+ },
+ {
+ "auth": null,
+ "description": "Multiple Unix domain sockets (relative and absolute paths)",
+ "hosts": [
+ {
+ "host": "rel/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ },
+ {
+ "host": "/tmp/mongodb-27018.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": {
+ "replicaSet": "replset"
+ },
+ "uri": "mongodb://rel%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/?replicaSet=replset",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": null,
+ "description": "Multiple hosts (relative path and ipv4)",
+ "hosts": [
+ {
+ "host": "127.0.0.1",
+ "port": 27017,
+ "type": "ipv4"
+ },
+ {
+ "host": "rel/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://127.0.0.1:27017,rel%2Fmongodb-27017.sock",
+ "valid": false,
+ "warning": false
+ },
+ {
+ "auth": null,
+ "description": "Multiple hosts (relative path and ipv4)",
+ "hosts": [
+ {
+ "host": "127.0.0.1",
+ "port": 27017,
+ "type": "ipv4"
+ },
+ {
+ "host": "rel/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": {
+ "replicaSet": "replset"
+ },
+ "uri": "mongodb://127.0.0.1:27017,rel%2Fmongodb-27017.sock/?replicaSet=replset",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": null,
+ "description": "Multiple hosts (relative path and hostname resembling relative path)",
+ "hosts": [
+ {
+ "host": "mongodb-27017.sock",
+ "port": null,
+ "type": "hostname"
+ },
+ {
+ "host": "rel/mongodb-27018.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://mongodb-27017.sock,rel%2Fmongodb-27018.sock",
+ "valid": false,
+ "warning": false
+ },
+ {
+ "auth": null,
+ "description": "Multiple hosts (relative path and hostname resembling relative path)",
+ "hosts": [
+ {
+ "host": "mongodb-27017.sock",
+ "port": null,
+ "type": "hostname"
+ },
+ {
+ "host": "rel/mongodb-27018.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": {
+ "replicaSet": "replset"
+ },
+ "uri": "mongodb://mongodb-27017.sock,rel%2Fmongodb-27018.sock/?replicaSet=replset",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": "admin",
+ "password": "foo",
+ "username": "alice"
+ },
+ "description": "Unix domain socket with auth database (relative path)",
+ "hosts": [
+ {
+ "host": "rel/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://alice:foo@rel%2Fmongodb-27017.sock/admin",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": null,
+ "description": "Unix domain socket with path resembling socket file (relative path with trailing slash)",
+ "hosts": [
+ {
+ "host": "rel/path.to.sock/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://rel%2Fpath.to.sock%2Fmongodb-27017.sock/",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": null,
+ "description": "Unix domain socket with path resembling socket file (relative path without trailing slash)",
+ "hosts": [
+ {
+ "host": "rel/path.to.sock/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://rel%2Fpath.to.sock%2Fmongodb-27017.sock",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": "admin",
+ "password": "bar",
+ "username": "bob"
+ },
+ "description": "Unix domain socket with path resembling socket file and auth (relative path)",
+ "hosts": [
+ {
+ "host": "rel/path.to.sock/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://bob:bar@rel%2Fpath.to.sock%2Fmongodb-27017.sock/admin",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": "admin",
+ "password": null,
+ "username": null
+ },
+ "description": "Multiple Unix domain sockets and auth DB resembling a socket (relative path)",
+ "hosts": [
+ {
+ "host": "rel/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ },
+ {
+ "host": "rel/mongodb-27018.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin",
+ "valid": false,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": "admin",
+ "password": null,
+ "username": null
+ },
+ "description": "Multiple Unix domain sockets and auth DB resembling a socket (relative path)",
+ "hosts": [
+ {
+ "host": "rel/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ },
+ {
+ "host": "rel/mongodb-27018.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": {
+ "replicaSet": "replset"
+ },
+ "uri": "mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin?replicaSet=replset",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": "admin",
+ "password": null,
+ "username": null
+ },
+ "description": "Multiple Unix domain sockets with auth DB resembling a path (relative path)",
+ "hosts": [
+ {
+ "host": "rel/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ },
+ {
+ "host": "rel/mongodb-27018.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin",
+ "valid": false,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": "admin",
+ "password": null,
+ "username": null
+ },
+ "description": "Multiple Unix domain sockets with auth DB resembling a path (relative path)",
+ "hosts": [
+ {
+ "host": "rel/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ },
+ {
+ "host": "rel/mongodb-27018.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": {
+ "replicaSet": "replset"
+ },
+ "uri": "mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin?replicaSet=replset",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": "admin",
+ "password": "bar",
+ "username": "bob"
+ },
+ "description": "Multiple Unix domain sockets with auth and query string (relative path)",
+ "hosts": [
+ {
+ "host": "rel/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ },
+ {
+ "host": "rel/mongodb-27018.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": {
+ "w": 1
+ },
+ "uri": "mongodb://bob:bar@rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin?w=1",
+ "valid": false,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": "admin",
+ "password": "bar",
+ "username": "bob"
+ },
+ "description": "Multiple Unix domain sockets with auth and query string (relative path)",
+ "hosts": [
+ {
+ "host": "rel/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ },
+ {
+ "host": "rel/mongodb-27018.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": {
+ "w": 1,
+ "replicaSet": "replset"
+ },
+ "uri": "mongodb://bob:bar@rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin?w=1&replicaSet=replset",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": "admin",
+ "password": "bar",
+ "username": "bob"
+ },
+ "description": "Multiple Unix domain sockets with auth and query string (relative path)",
+ "hosts": [
+ {
+ "host": "rel/mongodb-27017.sock",
+ "port": null,
+ "type": "unix"
+ },
+ {
+ "host": "rel/mongodb-27018.sock",
+ "port": null,
+ "type": "unix"
+ }
+ ],
+ "options": {
+ "w": 1,
+ "replicaSet": "replset",
+ "b": 4
+ },
+ "uri": "mongodb://bob:bar@rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin?w=1&replicaSet=replset&b=4",
+ "valid": true,
+ "warning": false
+ }
+ ]
+}
diff --git a/src/mongo/client/mongo_uri_tests/mongo-uri-valid-auth.json b/src/mongo/client/mongo_uri_tests/mongo-uri-valid-auth.json
new file mode 100644
index 00000000000..a44236e7b62
--- /dev/null
+++ b/src/mongo/client/mongo_uri_tests/mongo-uri-valid-auth.json
@@ -0,0 +1,311 @@
+{
+ "tests": [
+ {
+ "auth": {
+ "db": null,
+ "password": "foo",
+ "username": "alice"
+ },
+ "description": "User info for single IPv4 host without database",
+ "hosts": [
+ {
+ "host": "127.0.0.1",
+ "port": null,
+ "type": "ipv4"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://alice:foo@127.0.0.1",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": "test",
+ "password": "foo",
+ "username": "alice"
+ },
+ "description": "User info for single IPv4 host with database",
+ "hosts": [
+ {
+ "host": "127.0.0.1",
+ "port": null,
+ "type": "ipv4"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://alice:foo@127.0.0.1/test",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": null,
+ "password": "bar",
+ "username": "bob"
+ },
+ "description": "User info for single IP literal host without database",
+ "hosts": [
+ {
+ "host": "::1",
+ "port": 27018,
+ "type": "ip_literal"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://bob:bar@[::1]:27018",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": "admin",
+ "password": "bar",
+ "username": "bob"
+ },
+ "description": "User info for single IP literal host with database",
+ "hosts": [
+ {
+ "host": "::1",
+ "port": 27018,
+ "type": "ip_literal"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://bob:bar@[::1]:27018/admin",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": null,
+ "password": "baz",
+ "username": "eve"
+ },
+ "description": "User info for single hostname without database",
+ "hosts": [
+ {
+ "host": "example.com",
+ "port": null,
+ "type": "hostname"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://eve:baz@example.com",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": "db2",
+ "password": "baz",
+ "username": "eve"
+ },
+ "description": "User info for single hostname with database",
+ "hosts": [
+ {
+ "host": "example.com",
+ "port": null,
+ "type": "hostname"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://eve:baz@example.com/db2",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": null,
+ "password": "secret",
+ "username": "alice"
+ },
+ "description": "User info for multiple hosts without database",
+ "hosts": [
+ {
+ "host": "127.0.0.1",
+ "port": null,
+ "type": "ipv4"
+ },
+ {
+ "host": "example.com",
+ "port": 27018,
+ "type": "hostname"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://alice:secret@127.0.0.1,example.com:27018",
+ "valid": false,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": "admin",
+ "password": "secret",
+ "username": "alice"
+ },
+ "description": "User info for multiple hosts with database",
+ "hosts": [
+ {
+ "host": "example.com",
+ "port": null,
+ "type": "hostname"
+ },
+ {
+ "host": "::1",
+ "port": 27019,
+ "type": "ip_literal"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://alice:secret@example.com,[::1]:27019/admin",
+ "valid": false,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": null,
+ "password": null,
+ "username": "alice"
+ },
+ "description": "Username without password",
+ "hosts": [
+ {
+ "host": "127.0.0.1",
+ "port": null,
+ "type": "ipv4"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://alice@127.0.0.1",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": null,
+ "password": "",
+ "username": "alice"
+ },
+ "description": "Username with empty password",
+ "hosts": [
+ {
+ "host": "127.0.0.1",
+ "port": null,
+ "type": "ipv4"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://alice:@127.0.0.1",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": "my=db",
+ "password": null,
+ "username": "@l:ce/="
+ },
+ "description": "Escaped username and database without password",
+ "hosts": [
+ {
+ "host": "example.com",
+ "port": null,
+ "type": "hostname"
+ }
+ ],
+ "options": null,
+ "uri": "mongodb://%40l%3Ace%2F%3D@example.com/my%3Ddb",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": "admin=",
+ "password": "fizzb@zz=",
+ "username": "$am"
+ },
+ "description": "Escaped user info and database (MONGODB-CR)",
+ "hosts": [
+ {
+ "host": "127.0.0.1",
+ "port": null,
+ "type": "ipv4"
+ }
+ ],
+ "options": {
+ "authmechanism": "MONGODB-CR"
+ },
+ "uri": "mongodb://%24am:fizzb%40zz%3D@127.0.0.1/admin%3D?authMechanism=MONGODB-CR",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": null,
+ "password": null,
+ "username": "CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry"
+ },
+ "description": "Escaped username (MONGODB-X509)",
+ "hosts": [
+ {
+ "host": "localhost",
+ "port": null,
+ "type": "hostname"
+ }
+ ],
+ "options": {
+ "authmechanism": "MONGODB-X509"
+ },
+ "uri": "mongodb://CN%3DmyName%2COU%3DmyOrgUnit%2CO%3DmyOrg%2CL%3DmyLocality%2CST%3DmyState%2CC%3DmyCountry@localhost/?authMechanism=MONGODB-X509",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": null,
+ "password": "secret",
+ "username": "user@EXAMPLE.COM"
+ },
+ "description": "Escaped username (GSSAPI)",
+ "hosts": [
+ {
+ "host": "localhost",
+ "port": null,
+ "type": "hostname"
+ }
+ ],
+ "options": {
+ "authmechanism": "GSSAPI",
+ "authmechanismproperties": {
+ "CANONICALIZE_HOST_NAME": true,
+ "SERVICE_NAME": "other"
+ }
+ },
+ "uri": "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true&authMechanism=GSSAPI",
+ "valid": true,
+ "warning": false
+ },
+ {
+ "auth": {
+ "db": "admin",
+ "password": "secret",
+ "username": "alice"
+ },
+ "description": "At-signs in options aren't part of the userinfo",
+ "hosts": [
+ {
+ "host": "example.com",
+ "port": null,
+ "type": "hostname"
+ }
+ ],
+ "options": {
+ "replicaSet": "my@replicaset"
+ },
+ "uri": "mongodb://alice:secret@example.com/admin?replicaSet=my@replicaset",
+ "valid": true,
+ "warning": false
+ }
+ ]
+}
diff --git a/src/mongo/client/mongo_uri_tests/mongo-uri-warnings.json b/src/mongo/client/mongo_uri_tests/mongo-uri-warnings.json
new file mode 100644
index 00000000000..a04246847ab
--- /dev/null
+++ b/src/mongo/client/mongo_uri_tests/mongo-uri-warnings.json
@@ -0,0 +1,56 @@
+{
+ "tests": [
+ {
+ "description": "Unrecognized option keys are ignored",
+ "uri": "mongodb://example.com/?foo=bar",
+ "valid": true,
+ "warning": true,
+ "hosts": [
+ {
+ "type": "hostname",
+ "host": "example.com",
+ "port": null
+ }
+ ],
+ "auth": null,
+ "options": {
+ "foo": "bar"
+ }
+ },
+ {
+ "description": "Unsupported option values are ignored",
+ "uri": "mongodb://example.com/?fsync=ifPossible",
+ "valid": true,
+ "warning": true,
+ "hosts": [
+ {
+ "type": "hostname",
+ "host": "example.com",
+ "port": null
+ }
+ ],
+ "auth": null,
+ "options": {
+ "fsync": "ifPossible"
+ }
+ },
+ {
+ "description": "Deprecated (or unknown) options are ignored if replacement exists",
+ "uri": "mongodb://example.com/?wtimeout=5&wtimeoutMS=10",
+ "valid": true,
+ "warning": true,
+ "hosts": [
+ {
+ "type": "hostname",
+ "host": "example.com",
+ "port": null
+ }
+ ],
+ "auth": null,
+ "options": {
+ "wtimeoutMS": 10,
+ "wtimeout": 5
+ }
+ }
+ ]
+} \ No newline at end of file