summaryrefslogtreecommitdiff
path: root/src/mongo/client
diff options
context:
space:
mode:
authorSara Golemon <sara.golemon@mongodb.com>2017-09-14 15:24:18 -0400
committerSara Golemon <sara.golemon@mongodb.com>2017-09-21 11:19:26 -0400
commitf3bf7e7920a51df7100238a55c304ed7cd3aed1f (patch)
treea43a89c9a0f960cea7a2f9ce31c5ecc3243d5131 /src/mongo/client
parent6c4c5f47b2e3fbf841683448a5e117e86f72c4ef (diff)
downloadmongo-f3bf7e7920a51df7100238a55c304ed7cd3aed1f.tar.gz
SERVER-29921 rewrite URI parser
Diffstat (limited to 'src/mongo/client')
-rw-r--r--src/mongo/client/connection_string.h17
-rw-r--r--src/mongo/client/mongo_uri.cpp338
-rw-r--r--src/mongo/client/mongo_uri.h39
-rw-r--r--src/mongo/client/mongo_uri_test.cpp288
-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
11 files changed, 2267 insertions, 96 deletions
diff --git a/src/mongo/client/connection_string.h b/src/mongo/client/connection_string.h
index e8fc8eb2637..2e888b56dad 100644
--- a/src/mongo/client/connection_string.h
+++ b/src/mongo/client/connection_string.h
@@ -28,11 +28,13 @@
#pragma once
+#include <sstream>
#include <string>
#include <vector>
#include "mongo/base/status_with.h"
#include "mongo/base/string_data.h"
+#include "mongo/bson/util/builder.h"
#include "mongo/stdx/mutex.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/net/hostandport.h"
@@ -157,6 +159,10 @@ public:
return _string < other._string;
}
+
+ friend std::ostream& operator<<(std::ostream&, const ConnectionString&);
+ friend StringBuilder& operator<<(StringBuilder&, const ConnectionString&);
+
private:
/**
* Creates a SET connection string with the specified set name and servers.
@@ -179,4 +185,15 @@ private:
static stdx::mutex _connectHookMutex;
static ConnectionHook* _connectHook;
};
+
+inline std::ostream& operator<<(std::ostream& ss, const ConnectionString& cs) {
+ ss << cs._string;
+ return ss;
+}
+
+inline StringBuilder& operator<<(StringBuilder& sb, const ConnectionString& cs) {
+ sb << cs._string;
+ return sb;
+}
+
} // namespace mongo
diff --git a/src/mongo/client/mongo_uri.cpp b/src/mongo/client/mongo_uri.cpp
index 6e6a7825b2d..1db9c10d2c0 100644
--- a/src/mongo/client/mongo_uri.cpp
+++ b/src/mongo/client/mongo_uri.cpp
@@ -32,130 +32,322 @@
#include "mongo/client/mongo_uri.h"
-#include <regex>
+#include <utility>
#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 {
+constexpr std::array<char, 16> hexits{
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+const mongo::StringData kURIPrefix{"mongodb://"};
+}
+
+/**
+ * RFC 3986 Section 2.1 - Percent Encoding
+ *
+ * Encode data elements in a way which will allow them to be embedded
+ * into a mongodb:// URI safely.
+ */
+void mongo::uriEncode(std::ostream& ss, StringData toEncode) {
+ for (const auto& c : toEncode) {
+ if ((c == '-') || (c == '_') || (c == '.') || (c == '~') || isalnum(c)) {
+ ss << c;
+ } else {
+ // Encoding anything not included in section 2.3 "Unreserved characters"
+ ss << '%' << hexits[(c >> 4) & 0xF] << hexits[c & 0xF];
+ }
+ }
+}
+
+mongo::StatusWith<std::string> mongo::uriDecode(StringData toDecode) {
+ StringBuilder out;
+ for (size_t i = 0; i < toDecode.size(); ++i) {
+ const auto c = toDecode[i];
+ if (c == '%') {
+ if (i + 2 > toDecode.size()) {
+ return Status(ErrorCodes::FailedToParse,
+ "Encountered partial escape sequence at end of string");
+ }
+ out << fromHex(toDecode.substr(i + 1, 2));
+ i += 2;
+ } else {
+ out << c;
+ }
+ }
+ return out.str();
+}
namespace mongo {
namespace {
-const char kMongoDBURL[] =
- // scheme: non-capturing
- "mongodb://"
- // credentials: two inner captures for user and password
- "(?:([^:]+)(?::([^@]+))?@)?"
+/**
+ * Helper Method for MongoURI::parse() to split a string into exactly 2 pieces by a char delimeter
+ */
+std::pair<StringData, StringData> partitionForward(StringData str, const char c) {
+ const auto delim = str.find(c);
+ if (delim == std::string::npos) {
+ return {str, StringData()};
+ }
+ return {str.substr(0, delim), str.substr(delim + 1)};
+}
+
+/**
+ * Helper method for MongoURI::parse() to split a string into exactly 2 pieces by a char delimiter
+ * searching backward from the end of the string.
+ */
+std::pair<StringData, StringData> partitionBackward(StringData str, const char c) {
+ const auto delim = str.rfind(c);
+ if (delim == std::string::npos) {
+ return {StringData(), str};
+ }
+ return {str.substr(0, delim), str.substr(delim + 1)};
+}
- // servers: grabs all host:port or UNIX socket names
- "((?:[^\\/]+|/.+\\.sock)(?:,(?:[^\\/]+|/.+\\.sock))*)"
+/**
+ * Breakout method for parsing application/x-www-form-urlencoded option pairs
+ *
+ * foo=bar&baz=qux&...
+ */
+StatusWith<MongoURI::OptionsMap> parseOptions(StringData options, StringData url) {
+ MongoURI::OptionsMap ret;
+ if (options.empty()) {
+ return ret;
+ }
- // database and options are grouped together
- "(?:/"
+ if (options.find('?') != std::string::npos) {
+ return Status(ErrorCodes::FailedToParse,
+ str::stream()
+ << "URI Cannot Contain multiple questions marks for mongodb:// URL: "
+ << url);
+ }
- // 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.
- "([^/\\\\\\.\\ \"\\$*<>:\\|\\?]*)?"
+ const auto optionsStr = options.toString();
+ for (auto i =
+ boost::make_split_iterator(optionsStr, boost::first_finder("&", boost::is_iequal()));
+ i != std::remove_reference<decltype((i))>::type{};
+ ++i) {
+ const auto 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);
+ }
+
+ const auto kvPair = partitionForward(opt, '=');
+ const auto keyRaw = kvPair.first;
+ if (keyRaw.empty()) {
+ return Status(
+ ErrorCodes::FailedToParse,
+ str::stream()
+ << "Missing a key for key/value pair in the options for mongodb:// URL: "
+ << url);
+ }
+ const auto key = uriDecode(keyRaw);
+ if (!key.isOK()) {
+ return Status(
+ ErrorCodes::FailedToParse,
+ str::stream() << "Key '" << keyRaw
+ << "' in options cannot properly be URL decoded for mongodb:// URL: "
+ << url);
+ }
+ const auto valRaw = kvPair.second;
+ if (valRaw.empty()) {
+ return Status(ErrorCodes::FailedToParse,
+ str::stream() << "Missing value for key '" << keyRaw
+ << "' in the options for mongodb:// URL: "
+ << url);
+ }
+ const auto val = uriDecode(valRaw);
+ if (!val.isOK()) {
+ return Status(
+ ErrorCodes::FailedToParse,
+ str::stream() << "Value '" << valRaw << "' for key '" << keyRaw
+ << "' in options cannot properly be URL decoded for mongodb:// URL: "
+ << url);
+ }
- // options
- "(?:\\?([^&=?]+=[^&=?]+(?:&[^&=?]+=[^&=?]+)*))?"
+ ret[key.getValue()] = val.getValue();
+ }
- // close db/options group
- ")?";
+ return ret;
+}
} // namespace
-
StatusWith<MongoURI> MongoURI::parse(const std::string& url) {
- if (!boost::algorithm::starts_with(url, "mongodb://")) {
- auto cs_status = ConnectionString::parse(url);
+ const StringData urlSD(url);
+
+ // 1. Validate and remove the scheme prefix mongodb://
+ if (!urlSD.startsWith(kURIPrefix)) {
+ const 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);
+ const auto uriWithoutPrefix = urlSD.substr(kURIPrefix.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
+ const auto userAndDb = partitionForward(uriWithoutPrefix, '/');
+ const auto userAndHostInfo = userAndDb.first;
+ const auto databaseAndOptions = userAndDb.second;
+
+ // 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.find('?') != 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);
-
- if (!matches[3].matched) {
- return Status(ErrorCodes::FailedToParse, "No server(s) specified");
+ // 3. Split the user information and host identifiers string by the last, unescaped @, yielding:
+ // split[0]: User information
+ // split[1]: Host identifiers;
+ const auto userAndHost = partitionBackward(userAndHostInfo, '@');
+ const auto userInfo = userAndHost.first;
+ const auto hostIdentifiers = userAndHost.second;
+
+ // 4. Validate, split (if applicable), and URL decode the user information, yielding:
+ // split[0] = username
+ // split[1] = password
+ const auto userAndPass = partitionForward(userInfo, ':');
+ const auto usernameSD = userAndPass.first;
+ const auto passwordSD = userAndPass.second;
+
+ const auto containsColonOrAt = [](StringData str) {
+ return (str.find(':') != std::string::npos) || (str.find('@') != std::string::npos);
+ };
+
+ if (containsColonOrAt(usernameSD)) {
+ return Status(ErrorCodes::FailedToParse,
+ str::stream() << "Username must be URL Encoded for mongodb:// URL: " << url);
+ }
+ if (containsColonOrAt(passwordSD)) {
+ return Status(ErrorCodes::FailedToParse,
+ str::stream() << "Password must be URL Encoded for mongodb:// URL: " << url);
}
- std::map<std::string, std::string> options;
+ // Get the username and make sure it did not fail to decode
+ const auto usernameWithStatus = uriDecode(usernameSD);
+ if (!usernameWithStatus.isOK()) {
+ return Status(
+ ErrorCodes::FailedToParse,
+ str::stream() << "Username cannot properly be URL decoded for mongodb:// URL: " << url);
+ }
+ const auto username = usernameWithStatus.getValue();
+
+ // Get the password and make sure it did not fail to decode
+ const auto passwordWithStatus = uriDecode(passwordSD);
+ if (!passwordWithStatus.isOK())
+ return Status(
+ ErrorCodes::FailedToParse,
+ str::stream() << "Password cannot properly be URL decoded for mongodb:// URL: " << url);
+ const auto password = passwordWithStatus.getValue();
+
+ // 5. Validate, split, and URL decode the host identifiers.
+ const auto 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) {
+ const auto hostWithStatus = uriDecode(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);
+ }
- if (matches[5].matched) {
- const std::string optionsMatch = matches[5].str();
+ const auto host = hostWithStatus.getValue();
+ if (host.empty()) {
+ continue;
+ }
- std::vector<boost::iterator_range<std::string::const_iterator>> optionsTokens;
- boost::algorithm::split(optionsTokens, optionsMatch, boost::algorithm::is_any_of("=&"));
+ if ((host.find('/') != std::string::npos) && !StringData(host).endsWith(".sock")) {
+ return Status(
+ ErrorCodes::FailedToParse,
+ str::stream() << "'" << host << "' in '" << url
+ << "' appears to be a unix socket, but does not end in '.sock'");
+ }
- if (optionsTokens.size() % 2 != 0) {
- return Status(ErrorCodes::FailedToParse,
- str::stream()
- << "Missing a key or value in the options for mongodb:// URL: "
- << url);
- ;
+ const auto statusHostAndPort = HostAndPort::parse(host);
+ if (!statusHostAndPort.isOK()) {
+ return statusHostAndPort.getStatus();
}
+ servers.push_back(statusHostAndPort.getValue());
+ }
+ if (servers.empty()) {
+ return Status(ErrorCodes::FailedToParse, "No server(s) specified");
+ }
- 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());
+ // 6. Split the auth database and connection options string by the first, unescaped ?, yielding:
+ // split[0] = auth database
+ // split[1] = connection options
+ const auto dbAndOpts = partitionForward(databaseAndOptions, '?');
+ const auto databaseSD = dbAndOpts.first;
+ const auto connectionOptions = dbAndOpts.second;
+ const auto databaseWithStatus = uriDecode(databaseSD);
+ if (!databaseWithStatus.isOK()) {
+ return Status(ErrorCodes::FailedToParse,
+ str::stream()
+ << "Database name cannot properly be URL decoded for mongodb:// URL: "
+ << url);
+ }
+ const auto 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);
}
- OptionsMap::const_iterator optIter;
+ // 8. Validate, split, and URL decode the connection options
+ const auto optsWith = parseOptions(connectionOptions, url);
+ if (!optsWith.isOK()) {
+ return optsWith.getStatus();
+ }
+ const auto options = optsWith.getValue();
// If a replica set option was specified, store it in the 'setName' field.
- bool haveSetName;
+ const auto optIter = options.find("replicaSet");
std::string setName;
- if ((haveSetName = ((optIter = options.find("replicaSet")) != options.end()))) {
+ if (optIter != options.end()) {
setName = optIter->second;
+ invariant(!setName.empty());
}
- 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()) {
+ if ((servers.size() > 1) && 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));
+ setName.empty() ? ConnectionString::MASTER : ConnectionString::SET, servers, setName);
+ return MongoURI(std::move(cs), username, password, database, std::move(options));
}
} // namespace mongo
diff --git a/src/mongo/client/mongo_uri.h b/src/mongo/client/mongo_uri.h
index 7b6364761d5..d42d5d33d5d 100644
--- a/src/mongo/client/mongo_uri.h
+++ b/src/mongo/client/mongo_uri.h
@@ -29,12 +29,14 @@
#pragma once
#include <map>
+#include <sstream>
#include <string>
#include <vector>
#include "mongo/base/status_with.h"
#include "mongo/base/string_data.h"
#include "mongo/bson/bsonobj.h"
+#include "mongo/bson/util/builder.h"
#include "mongo/client/connection_string.h"
#include "mongo/stdx/mutex.h"
#include "mongo/util/assert_util.h"
@@ -43,12 +45,36 @@
namespace mongo {
/**
+ * Encode a string for embedding in a URI.
+ * Replaces reserved bytes with %xx sequences.
+ */
+void uriEncode(std::ostream& ss, StringData str);
+inline std::string uriEncode(StringData str) {
+ std::ostringstream ss;
+ uriEncode(ss, str);
+ return ss.str();
+}
+
+/**
+ * Decode a URI encoded string.
+ * Replaces + and %xx sequences with their original byte.
+ */
+StatusWith<std::string> uriDecode(StringData str);
+
+/**
* MongoURI handles parsing of URIs for mongodb, and falls back to old-style
* ConnectionString parsing. It's used primarily by the shell.
* It parses URIs with the following format:
*
* mongodb://[usr:pwd@]host1[:port1]...[,hostN[:portN]]][/[db][?options]]
*
+ * While this format is generally RFC 3986 compliant, some exceptions do exist:
+ * 1. The 'host' field, as defined by section 3.2.2 is expanded in the following ways:
+ * a. Multiple hosts may be specified as a comma separated list.
+ * b. Hosts may take the form of absolute paths for unix domain sockets.
+ * i. Sockets must end in the suffix '.sock'
+ * 2. The 'fragment' field, as defined by section 3.5 is not permitted.
+ *
* For a complete list of URI string options, see
* https://wiki.mongodb.com/display/DH/Connection+String+Format
*
@@ -125,6 +151,9 @@ public:
MongoURI() = default;
+ friend std::ostream& operator<<(std::ostream&, const MongoURI&);
+ friend StringBuilder& operator<<(StringBuilder&, const MongoURI&);
+
private:
MongoURI(ConnectionString connectString,
const std::string& user,
@@ -146,4 +175,14 @@ private:
OptionsMap _options;
};
+inline std::ostream& operator<<(std::ostream& ss, const MongoURI& uri) {
+ ss << uri._connectString;
+ return ss;
+}
+
+inline StringBuilder& operator<<(StringBuilder& sb, const MongoURI& uri) {
+ sb << uri._connectString;
+ return sb;
+}
+
} // namespace mongo
diff --git a/src/mongo/client/mongo_uri_test.cpp b/src/mongo/client/mongo_uri_test.cpp
index 6a5f6c9882a..00edcb110f4 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,16 +66,77 @@ 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://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://127.0.0.1/dbName?foo=a&c=b", "", "", kMaster, "", 1, 2, "dbName"},
+
+ {"mongodb://user:pwd@127.0.0.1,/dbName?foo=a&c=b", "user", "pwd", 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 +326,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"},
@@ -282,32 +355,105 @@ const InvalidURITestCase invalidCases[] = {
{"mongodb://localhost:27017localhost:27018"},
// Domain sockets have to end in ".sock".
- {"mongodb:///notareal/domainsock"},
+ {"mongodb://%2Fnotareal%2Fdomainsock"},
+
+ // 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"},
+
+ // Missing a hostname, or unparsable hostname(s)
+ {"mongodb://,/dbName"},
+ {"mongodb://user:pwd@,/dbName"},
+ {"mongodb://localhost:1234:5678/dbName"},
// 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_TRUE(obj.valid(mongo::BSONVersion::kLatest));
+ ASSERT_TRUE(obj.hasField("tests"));
+ mongo::BSONObj arr = obj.getField("tests").embeddedObject().getOwned();
+ ASSERT_TRUE(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_TRUE(!element.eoo());
+ if (element.type() == mongo::jstNULL) {
+ return std::string();
+ }
+ ASSERT_EQ(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;
+ const auto cs_status = MongoURI::parse(testCase.URI);
+ ASSERT_OK(cs_status);
+ 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, 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);
}
}
@@ -318,7 +464,7 @@ TEST(MongoURI, InvalidURIs) {
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());
+ ASSERT_NOT_OK(cs_status);
}
}
@@ -357,4 +503,100 @@ 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, specTests) {
+ const std::string files[] = {
+ "mongo-uri-valid-auth.json",
+ "mongo-uri-options.json",
+ "mongo-uri-unix-sockets-absolute.json",
+ "mongo-uri-unix-sockets-relative.json",
+ "mongo-uri-warnings.json",
+ "mongo-uri-host-identifiers.json",
+ "mongo-uri-invalid.json",
+ };
+
+ for (const auto& file : files) {
+ const auto testBson = getBsonFromJsonFile(file);
+
+ for (const auto& testElement : testBson) {
+ ASSERT_EQ(testElement.type(), mongo::Object);
+ const auto test = testElement.Obj();
+
+ // First extract the valid field and the uri field
+ const auto validDoc = test.getField("valid");
+ ASSERT_FALSE(validDoc.eoo());
+ ASSERT_TRUE(validDoc.isBoolean());
+ const auto valid = validDoc.Bool();
+
+ const auto uriDoc = test.getField("uri");
+ ASSERT_FALSE(uriDoc.eoo());
+ ASSERT_EQ(uriDoc.type(), mongo::String);
+ const auto 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_NOT_OK(cs_status);
+ } else {
+ // This uri is valid -- > parse the remaining necessary fields
+
+ // parse the auth options
+ std::string database, username, password;
+
+ const auto auth = test.getField("auth");
+ ASSERT_FALSE(auth.eoo());
+ if (auth.type() != mongo::jstNULL) {
+ ASSERT_EQ(auth.type(), mongo::Object);
+ const auto authObj = auth.embeddedObject();
+ database = returnStringFromElementOrNull(authObj.getField("db"));
+ username = returnStringFromElementOrNull(authObj.getField("username"));
+ password = returnStringFromElementOrNull(authObj.getField("password"));
+ }
+
+ // parse the hosts
+ const auto hosts = test.getField("hosts");
+ ASSERT_FALSE(hosts.eoo());
+ ASSERT_EQ(hosts.type(), mongo::Array);
+ const auto numHosts = static_cast<size_t>(hosts.Obj().nFields());
+
+ // parse the options
+ mongo::ConnectionString::ConnectionType connectionType = kMaster;
+ size_t numOptions = 0;
+ std::string setName;
+ const auto optionsElement = test.getField("options");
+ ASSERT_FALSE(optionsElement.eoo());
+ if (optionsElement.type() != mongo::jstNULL) {
+ ASSERT_EQ(optionsElement.type(), mongo::Object);
+ const auto optionsObj = optionsElement.Obj();
+ numOptions = optionsObj.nFields();
+ const auto replsetElement = optionsObj.getField("replicaSet");
+ if (!replsetElement.eoo()) {
+ ASSERT_EQ(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);
+ }
+ }
+ }
+}
+
} // 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