summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Cotter <matt.cotter@mongodb.com>2016-07-13 14:39:35 -0400
committerMatt Cotter <matt.cotter@mongodb.com>2016-08-18 14:13:01 -0400
commit88b540f0c14c7ab8af708aecc4cd6df83081b32e (patch)
treea6f01563ac2b8db7d34f98e5fab3d17f5416e315
parentceeb17e4265c4c48765acbf9ad6a60b5f1889332 (diff)
downloadmongo-88b540f0c14c7ab8af708aecc4cd6df83081b32e.tar.gz
SERVER-22382 allow mongo --host to take uri
-rw-r--r--jstests/core/connection_string_validation.js79
-rw-r--r--jstests/noPassthroughWithMongod/host_connection_string_validation.js125
-rw-r--r--jstests/noPassthroughWithMongod/ipv6_connection_string_validation.js75
-rw-r--r--src/mongo/client/mongo_uri.cpp2
-rw-r--r--src/mongo/scripting/SConscript5
-rw-r--r--src/mongo/scripting/mozjs/implscope.cpp10
-rw-r--r--src/mongo/scripting/mozjs/implscope.h13
-rw-r--r--src/mongo/scripting/mozjs/internedstring.defs8
-rw-r--r--src/mongo/scripting/mozjs/mongo.cpp8
-rw-r--r--src/mongo/scripting/mozjs/objectwrapper.cpp7
-rw-r--r--src/mongo/scripting/mozjs/objectwrapper.h1
-rw-r--r--src/mongo/scripting/mozjs/uri.cpp105
-rw-r--r--src/mongo/scripting/mozjs/uri.h52
-rw-r--r--src/mongo/scripting/mozjs/valuereader.cpp34
-rw-r--r--src/mongo/scripting/mozjs/valuereader.h1
-rw-r--r--src/mongo/shell/dbshell.cpp61
-rw-r--r--src/mongo/shell/mongo.js49
-rw-r--r--src/mongo/util/net/hostandport.cpp8
18 files changed, 504 insertions, 139 deletions
diff --git a/jstests/core/connection_string_validation.js b/jstests/core/connection_string_validation.js
index 232650f230b..4f878593e6e 100644
--- a/jstests/core/connection_string_validation.js
+++ b/jstests/core/connection_string_validation.js
@@ -8,30 +8,42 @@ if (db.getMongo().host.indexOf(":") >= 0) {
port = db.getMongo().host.substring(idx + 1);
}
-var goodStrings = ["localhost:" + port + "/test", "127.0.0.1:" + port + "/test"];
+var goodStrings = [
+ "localhost:" + port + "/test",
+ "127.0.0.1:" + port + "/test",
+ "127.0.0.1:" + port + "/",
+];
+var missingConnString = /^Missing connection string$/;
+var incorrectType = /^Incorrect type/;
+var emptyConnString = /^Empty connection string$/;
+var badHost = /^Failed to parse mongodb/;
+var emptyHost = /^Empty host component/;
+var noPort = /^No digits/;
+var badPort = /^Bad digit/;
+var invalidPort = /^Port number \d+ out of range/;
+var multipleColon = /^More than one ':' detected./;
var badStrings = [
- {s: undefined, r: /^Missing connection string$/},
- {s: 7, r: /^Incorrect type/},
- {s: null, r: /^Incorrect type/},
- {s: "", r: /^Empty connection string$/},
- {s: " ", r: /^Empty connection string$/},
- {s: ":", r: /^Missing host name/},
- {s: "/", r: /^Missing host name/},
- {s: ":/", r: /^Missing host name/},
- {s: ":/test", r: /^Missing host name/},
- {s: ":" + port + "/", r: /^Missing host name/},
- {s: ":" + port + "/test", r: /^Missing host name/},
- {s: "/test", r: /^Missing host name/},
- {s: "localhost:/test", r: /^Missing port number/},
- {s: "127.0.0.1:/test", r: /^Missing port number/},
- {s: "127.0.0.1:cat/test", r: /^Invalid port number/},
- {s: "127.0.0.1:1cat/test", r: /^Invalid port number/},
- {s: "127.0.0.1:123456/test", r: /^Invalid port number/},
- {s: "127.0.0.1:65536/test", r: /^Invalid port number/},
- {s: "::1:65536/test", r: /^Invalid port number/},
- {s: "127.0.0.1:" + port + "/", r: /^Missing database name/},
- {s: "::1:" + port + "/", r: /^Missing database name/}
+ {s: undefined, r: missingConnString},
+ {s: 7, r: incorrectType},
+ {s: null, r: incorrectType},
+ {s: "", r: emptyConnString},
+ {s: " ", r: emptyConnString},
+ {s: ":", r: emptyHost},
+ {s: "/", r: badHost},
+ {s: "/test", r: badHost},
+ {s: ":/", r: emptyHost},
+ {s: ":/test", r: emptyHost},
+ {s: ":" + port + "/", r: emptyHost},
+ {s: ":" + port + "/test", r: emptyHost},
+ {s: "localhost:/test", r: noPort},
+ {s: "127.0.0.1:/test", r: noPort},
+ {s: "127.0.0.1:cat/test", r: badPort},
+ {s: "127.0.0.1:1cat/test", r: badPort},
+ {s: "127.0.0.1:123456/test", r: invalidPort},
+ {s: "127.0.0.1:65536/test", r: invalidPort},
+ {s: "::1:65536/test", r: multipleColon},
+ {s: "::1:" + port + "/", r: multipleColon}
];
function testGood(i, connectionString) {
@@ -55,6 +67,28 @@ function testGood(i, connectionString) {
doassert(message);
}
+function testGoodAsURI(i, uri) {
+ uri = "mongodb://" + uri;
+ print("\nTesting good uri " + i + " (\"" + uri + "\") ...");
+ var gotException = false;
+ var exception;
+ try {
+ var m_uri = MongoURI(uri);
+ var connectDB = connect(uri);
+ connectDB = null;
+ } catch (e) {
+ gotException = true;
+ exception = e;
+ }
+ if (!gotException) {
+ print("Good uri " + i + " (\"" + uri + "\") correctly validated");
+ return;
+ }
+ var message = "FAILED to correctly validate goodString " + i + " (\"" + uri +
+ "\"): exception was \"" + tojson(exception) + "\"";
+ doassert(message);
+}
+
function testBad(i, connectionString, errorRegex) {
print("\nTesting bad connection string " + i + " (\"" + connectionString + "\") ...");
var gotException = false;
@@ -90,6 +124,7 @@ var i;
jsTest.log("TESTING " + goodStrings.length + " good connection strings");
for (i = 0; i < goodStrings.length; ++i) {
testGood(i, goodStrings[i]);
+ testGoodAsURI(i, goodStrings[i]);
}
jsTest.log("TESTING " + badStrings.length + " bad connection strings");
diff --git a/jstests/noPassthroughWithMongod/host_connection_string_validation.js b/jstests/noPassthroughWithMongod/host_connection_string_validation.js
new file mode 100644
index 00000000000..89d77e707f1
--- /dev/null
+++ b/jstests/noPassthroughWithMongod/host_connection_string_validation.js
@@ -0,0 +1,125 @@
+// Test --host.
+
+// This "inner_mode" method of spawning a mongod and re-running was copied from
+// ipv6_connection_string_validation.js
+if ("undefined" == typeof inner_mode) {
+ // Start a mongod with --ipv6
+ jsTest.log("Outer mode test starting mongod with --ipv6");
+ // NOTE: bind_ip arg is present to test if it can parse ipv6 addresses (::1 in this case).
+ // Unfortunately, having bind_ip = ::1 won't work in the test framework (But does work when
+ // tested manually), so 127.0.0.1 is also present so the test mongo shell can connect
+ // with that address.
+ var mongod = MongoRunner.runMongod({ipv6: "", bind_ip: "::1,127.0.0.1"});
+ var args = [
+ "mongo",
+ "--nodb",
+ "--ipv6",
+ "--host",
+ "::1",
+ "--port",
+ mongod.port,
+ "--eval",
+ "inner_mode=true;port=" + mongod.port + ";",
+ "jstests/noPassthroughWithMongod/host_connection_string_validation.js"
+ ];
+ var exitCode = _runMongoProgram.apply(null, args);
+ jsTest.log("Inner mode test finished, exit code was " + exitCode);
+
+ // Stop the server we started
+ jsTest.log("Outer mode test stopping server");
+ MongoRunner.stopMongod(mongod.port, 15);
+
+ // Pass the inner test's exit code back as the outer test's exit code
+ quit(exitCode);
+}
+
+var testHost = function(host, shouldSucceed) {
+ var exitCode = runMongoProgram('mongo', '--ipv6', '--eval', ';', '--host', host);
+ if (shouldSucceed) {
+ if (exitCode !== 0) {
+ doassert("failed to connect with `--host " + host +
+ "`, but expected success. Exit code: " + exitCode);
+ }
+ } else {
+ if (exitCode === 0) {
+ doassert("successfully connected with `--host " + host + "`, but expected to fail.");
+ }
+ }
+};
+
+var goodStrings = [
+ "[::1]:27999",
+ "[::1]:27999/test",
+ "localhost:27999",
+ "localhost:27999/test",
+ "127.0.0.1:27999",
+ "127.0.0.1:27999/test",
+ "[0:0:0:0:0:0:0:1]:27999",
+ "[0:0:0:0:0:0:0:1]:27999/test",
+ "[0000:0000:0000:0000:0000:0000:0000:0001]:27999",
+ "[0000:0000:0000:0000:0000:0000:0000:0001]:27999/test",
+];
+
+var goodSocketStrings = [
+ "/tmp/mongodb-27999.sock",
+ "/tmp/mongodb-27999.sock/test",
+];
+
+var badStrings = [
+ "/",
+ ":",
+ ":/",
+ "/test",
+ ":/test",
+ ":27999/",
+ ":27999/test",
+ "::1]:27999/",
+ "[::1:27999/",
+ "[::1]:/test",
+ "[::1:]27999/",
+ "a[::1:]27999/",
+ "::1:27999/test",
+ "::1:65536/test",
+ "[::1]:cat/test",
+ "[::1]:1cat/test",
+ "localhost:/test",
+ "127.0.0.1:/test",
+ "[::1]:65536/test",
+ "[::1]:123456/test",
+ "127.0.0.1:cat/test",
+ "a[127.0.0.1]:27999/",
+ "127.0.0.1:1cat/test",
+ "127.0.0.1:65536/test",
+ "0:0::0:0:1:27999/test",
+ "127.0.0.1:123456/test",
+ "0000:0000:0000:0000:0000:0000:0000:0001:27999/test",
+];
+
+function runUriTestFor(i, connectionString, isGood) {
+ connectionString = connectionString.replace("27999", "" + port);
+ print("Testing " + (isGood ? "good" : "bad") + " connection string " + i + "...");
+ print(" * testing " + connectionString);
+ testHost(connectionString, isGood);
+ print(" * testing mongodb://" + connectionString);
+ testHost("mongodb://" + connectionString, isGood);
+}
+
+var i;
+jsTest.log("TESTING " + goodStrings.length + " good uri strings");
+for (i = 0; i < goodStrings.length; ++i) {
+ runUriTestFor(i, goodStrings[i], true);
+}
+
+if (!_isWindows()) {
+ jsTest.log("TESTING " + goodSocketStrings.length + " good uri socket strings");
+ for (i = 0; i < goodSocketStrings.length; ++i) {
+ runUriTestFor(i, goodSocketStrings[i], true);
+ }
+}
+
+jsTest.log("TESTING " + badStrings.length + " bad uri strings");
+for (i = 0; i < badStrings.length; ++i) {
+ runUriTestFor(i, badStrings[i], false);
+}
+
+jsTest.log("SUCCESSFUL test completion");
diff --git a/jstests/noPassthroughWithMongod/ipv6_connection_string_validation.js b/jstests/noPassthroughWithMongod/ipv6_connection_string_validation.js
index e0a83397ee9..881c77c96d9 100644
--- a/jstests/noPassthroughWithMongod/ipv6_connection_string_validation.js
+++ b/jstests/noPassthroughWithMongod/ipv6_connection_string_validation.js
@@ -40,39 +40,54 @@ var goodStrings = [
"localhost:27999/test",
"[::1]:27999/test",
"[0:0:0:0:0:0:0:1]:27999/test",
- "[0000:0000:0000:0000:0000:0000:0000:0001]:27999/test"
+ "[0000:0000:0000:0000:0000:0000:0000:0001]:27999/test",
+ "localhost:27999",
+ "[::1]:27999",
+ "[0:0:0:0:0:0:0:1]:27999",
+ "[0000:0000:0000:0000:0000:0000:0000:0001]:27999",
];
+var missingConnString = /^Missing connection string$/;
+var incorrectType = /^Incorrect type/;
+var emptyConnString = /^Empty connection string$/;
+var badHost = /^Failed to parse mongodb/;
+var emptyHost = /^Empty host component/;
+var noPort = /^No digits/;
+var badPort = /^Bad digit/;
+var invalidPort = /^Port number \d+ out of range/;
+var moreThanOneColon = /^More than one ':' detected/;
+var charBeforeSquareBracket = /^'\[' present, but not first character/;
+var noCloseBracket = /^ipv6 address is missing closing '\]'/;
+var noOpenBracket = /^'\]' present without '\['/;
+var noColonPrePort = /^missing colon after '\]' before the port/;
var badStrings = [
- {s: undefined, r: /^Missing connection string$/},
- {s: 7, r: /^Incorrect type/},
- {s: null, r: /^Incorrect type/},
- {s: "", r: /^Empty connection string$/},
- {s: " ", r: /^Empty connection string$/},
- {s: ":", r: /^Missing host name/},
- {s: "/", r: /^Missing host name/},
- {s: ":/", r: /^Missing host name/},
- {s: ":/test", r: /^Missing host name/},
- {s: ":27999/", r: /^Missing host name/},
- {s: ":27999/test", r: /^Missing host name/},
- {s: "/test", r: /^Missing host name/},
- {s: "localhost:/test", r: /^Missing port number/},
- {s: "::1:/test", r: /^Missing port number/},
- {s: "::1:cat/test", r: /^Invalid port number/},
- {s: "::1:1cat/test", r: /^Invalid port number/},
- {s: "::1:123456/test", r: /^Invalid port number/},
- {s: "::1:65536/test", r: /^Invalid port number/},
- {s: "127.0.0.1:65536/test", r: /^Invalid port number/},
- {s: "::1:27999/", r: /^Missing database name/},
- {s: "127.0.0.1:27999/", r: /^Missing database name/},
- {s: "::1:27999/test", r: /^More than one ':'/},
- {s: "0:0::0:0:1:27999/test", r: /^More than one ':'/},
- {s: "0000:0000:0000:0000:0000:0000:0000:0001:27999/test", r: /^More than one ':'/},
- {s: "a[127.0.0.1]:27999/", r: /^Missing database name/},
- {s: "a[::1:]27999/", r: /^Invalid port number/},
- {s: "[::1:27999/", r: /^Missing database name/},
- {s: "[::1:]27999/", r: /^Invalid port number/},
- {s: "::1]:27999/", r: /^Missing database name/}
+ {s: undefined, r: missingConnString},
+ {s: 7, r: incorrectType},
+ {s: null, r: incorrectType},
+ {s: "", r: emptyConnString},
+ {s: " ", r: emptyConnString},
+ {s: ":", r: emptyHost},
+ {s: "/", r: badHost},
+ {s: ":/", r: emptyHost},
+ {s: ":/test", r: emptyHost},
+ {s: ":27999/", r: emptyHost},
+ {s: ":27999/test", r: emptyHost},
+ {s: "/test", r: badHost},
+ {s: "localhost:/test", r: noPort},
+ {s: "[::1]:/test", r: noPort},
+ {s: "[::1]:cat/test", r: badPort},
+ {s: "[::1]:1cat/test", r: badPort},
+ {s: "[::1]:123456/test", r: invalidPort},
+ {s: "[::1]:65536/test", r: invalidPort},
+ {s: "127.0.0.1:65536/test", r: invalidPort},
+ {s: "::1:27999/test", r: moreThanOneColon},
+ {s: "0:0::0:0:1:27999/test", r: moreThanOneColon},
+ {s: "0000:0000:0000:0000:0000:0000:0000:0001:27999/test", r: moreThanOneColon},
+ {s: "a[127.0.0.1]:27999/", r: charBeforeSquareBracket},
+ {s: "a[::1:]27999/", r: charBeforeSquareBracket},
+ {s: "[::1:27999/", r: noCloseBracket},
+ {s: "[::1:]27999/", r: noColonPrePort},
+ {s: "::1]:27999/", r: noOpenBracket},
];
var substitutePort = function(connectionString) {
diff --git a/src/mongo/client/mongo_uri.cpp b/src/mongo/client/mongo_uri.cpp
index ab16452b56b..e737479d05b 100644
--- a/src/mongo/client/mongo_uri.cpp
+++ b/src/mongo/client/mongo_uri.cpp
@@ -56,7 +56,7 @@ const char kMongoDBURL[] =
"(?:([^:]+)(?::([^@]+))?@)?"
// servers: grabs all host:port or UNIX socket names
- "((?:[^\\/]+|/.+\\.sock)(?:(?:[^\\/]+|/.+\\.sock),)*)"
+ "((?:[^\\/]+|/.+\\.sock)(?:,(?:[^\\/]+|/.+\\.sock))*)"
// database and options are grouped together
"(?:/"
diff --git a/src/mongo/scripting/SConscript b/src/mongo/scripting/SConscript
index fc32d46d01a..8c2604c76b6 100644
--- a/src/mongo/scripting/SConscript
+++ b/src/mongo/scripting/SConscript
@@ -107,8 +107,8 @@ if usemozjs:
'mozjs/countdownlatch.cpp',
'mozjs/cursor.cpp',
'mozjs/cursor_handle.cpp',
- 'mozjs/dbcollection.cpp',
'mozjs/db.cpp',
+ 'mozjs/dbcollection.cpp',
'mozjs/dbpointer.cpp',
'mozjs/dbquery.cpp',
'mozjs/dbref.cpp',
@@ -128,9 +128,9 @@ if usemozjs:
'mozjs/mongohelpers.cpp',
'mozjs/mongohelpers_js.cpp',
'mozjs/nativefunction.cpp',
+ 'mozjs/numberdecimal.cpp',
'mozjs/numberint.cpp',
'mozjs/numberlong.cpp',
- 'mozjs/numberdecimal.cpp',
'mozjs/object.cpp',
'mozjs/objectwrapper.cpp',
'mozjs/oid.cpp',
@@ -138,6 +138,7 @@ if usemozjs:
'mozjs/proxyscope.cpp',
'mozjs/regexp.cpp',
'mozjs/timestamp.cpp',
+ 'mozjs/uri.cpp',
'mozjs/valuereader.cpp',
'mozjs/valuewriter.cpp',
],
diff --git a/src/mongo/scripting/mozjs/implscope.cpp b/src/mongo/scripting/mozjs/implscope.cpp
index 2b01e99454b..fff5f233ac2 100644
--- a/src/mongo/scripting/mozjs/implscope.cpp
+++ b/src/mongo/scripting/mozjs/implscope.cpp
@@ -336,12 +336,12 @@ MozJSImplScope::MozJSImplScope(MozJSScriptEngine* engine)
_bsonProto(_context),
_codeProto(_context),
_countDownLatchProto(_context),
- _cursorProto(_context),
_cursorHandleProto(_context),
+ _cursorProto(_context),
_dbCollectionProto(_context),
+ _dbProto(_context),
_dbPointerProto(_context),
_dbQueryProto(_context),
- _dbProto(_context),
_dbRefProto(_context),
_errorProto(_context),
_jsThreadProto(_context),
@@ -351,13 +351,14 @@ MozJSImplScope::MozJSImplScope(MozJSScriptEngine* engine)
_mongoHelpersProto(_context),
_mongoLocalProto(_context),
_nativeFunctionProto(_context),
+ _numberDecimalProto(_context),
_numberIntProto(_context),
_numberLongProto(_context),
- _numberDecimalProto(_context),
_objectProto(_context),
_oidProto(_context),
_regExpProto(_context),
- _timestampProto(_context) {
+ _timestampProto(_context),
+ _uriProto(_context) {
kCurrentScope = this;
// The default is quite low and doesn't seem to directly correlate with
@@ -790,6 +791,7 @@ void MozJSImplScope::installBSONTypes() {
_oidProto.install(_global);
_regExpProto.install(_global);
_timestampProto.install(_global);
+ _uriProto.install(_global);
// This builtin map is a javascript 6 thing. We want our version. so
// take theirs out
diff --git a/src/mongo/scripting/mozjs/implscope.h b/src/mongo/scripting/mozjs/implscope.h
index d8225bffdde..080716428ac 100644
--- a/src/mongo/scripting/mozjs/implscope.h
+++ b/src/mongo/scripting/mozjs/implscope.h
@@ -60,6 +60,7 @@
#include "mongo/scripting/mozjs/oid.h"
#include "mongo/scripting/mozjs/regexp.h"
#include "mongo/scripting/mozjs/timestamp.h"
+#include "mongo/scripting/mozjs/uri.h"
namespace mongo {
namespace mozjs {
@@ -289,6 +290,11 @@ public:
return _timestampProto;
}
+ template <typename T>
+ typename std::enable_if<std::is_same<T, URIInfo>::value, WrapType<T>&>::type getProto() {
+ return _uriProto;
+ }
+
void setQuickExit(int exitCode);
bool getQuickExit(int* exitCode);
@@ -380,12 +386,12 @@ private:
WrapType<BSONInfo> _bsonProto;
WrapType<CodeInfo> _codeProto;
WrapType<CountDownLatchInfo> _countDownLatchProto;
- WrapType<CursorInfo> _cursorProto;
WrapType<CursorHandleInfo> _cursorHandleProto;
+ WrapType<CursorInfo> _cursorProto;
WrapType<DBCollectionInfo> _dbCollectionProto;
+ WrapType<DBInfo> _dbProto;
WrapType<DBPointerInfo> _dbPointerProto;
WrapType<DBQueryInfo> _dbQueryProto;
- WrapType<DBInfo> _dbProto;
WrapType<DBRefInfo> _dbRefProto;
WrapType<ErrorInfo> _errorProto;
WrapType<JSThreadInfo> _jsThreadProto;
@@ -395,13 +401,14 @@ private:
WrapType<MongoHelpersInfo> _mongoHelpersProto;
WrapType<MongoLocalInfo> _mongoLocalProto;
WrapType<NativeFunctionInfo> _nativeFunctionProto;
+ WrapType<NumberDecimalInfo> _numberDecimalProto;
WrapType<NumberIntInfo> _numberIntProto;
WrapType<NumberLongInfo> _numberLongProto;
- WrapType<NumberDecimalInfo> _numberDecimalProto;
WrapType<ObjectInfo> _objectProto;
WrapType<OIDInfo> _oidProto;
WrapType<RegExpInfo> _regExpProto;
WrapType<TimestampInfo> _timestampProto;
+ WrapType<URIInfo> _uriProto;
};
inline MozJSImplScope* getScope(JSContext* cx) {
diff --git a/src/mongo/scripting/mozjs/internedstring.defs b/src/mongo/scripting/mozjs/internedstring.defs
index 792ec0bbcb5..fe2394edb27 100644
--- a/src/mongo/scripting/mozjs/internedstring.defs
+++ b/src/mongo/scripting/mozjs/internedstring.defs
@@ -53,3 +53,11 @@ MONGO_MOZJS_INTERNED_STRING(str, "str")
MONGO_MOZJS_INTERNED_STRING(top, "top")
MONGO_MOZJS_INTERNED_STRING(t, "t")
MONGO_MOZJS_INTERNED_STRING(type, "type")
+MONGO_MOZJS_INTERNED_STRING(uri, "uri")
+MONGO_MOZJS_INTERNED_STRING(user, "user")
+MONGO_MOZJS_INTERNED_STRING(password, "password")
+MONGO_MOZJS_INTERNED_STRING(options, "options")
+MONGO_MOZJS_INTERNED_STRING(database, "database")
+MONGO_MOZJS_INTERNED_STRING(isValid, "isValid")
+MONGO_MOZJS_INTERNED_STRING(setName, "setName")
+MONGO_MOZJS_INTERNED_STRING(servers, "servers")
diff --git a/src/mongo/scripting/mozjs/mongo.cpp b/src/mongo/scripting/mozjs/mongo.cpp
index b7d9677fd0a..da62b092f34 100644
--- a/src/mongo/scripting/mozjs/mongo.cpp
+++ b/src/mongo/scripting/mozjs/mongo.cpp
@@ -638,8 +638,7 @@ void MongoExternalInfo::construct(JSContext* cx, JS::CallArgs args) {
}
auto statusWithHost = MongoURI::parse(host);
- uassertStatusOK(statusWithHost);
- auto cs = statusWithHost.getValue();
+ auto cs = uassertStatusOK(statusWithHost);
std::string errmsg;
std::unique_ptr<DBClientBase> conn(cs.connect("MongoDB Shell", errmsg));
@@ -657,8 +656,9 @@ void MongoExternalInfo::construct(JSContext* cx, JS::CallArgs args) {
JS_SetPrivate(thisv, new std::shared_ptr<DBClientBase>(conn.release()));
o.setBoolean(InternedString::slaveOk, false);
- o.setString(InternedString::host, host);
- o.setString(InternedString::defaultDB, cs.getDatabase());
+ o.setString(InternedString::host, cs.toString());
+ auto defaultDB = cs.getDatabase() == "" ? "test" : cs.getDatabase();
+ o.setString(InternedString::defaultDB, defaultDB);
args.rval().setObjectOrNull(thisv);
}
diff --git a/src/mongo/scripting/mozjs/objectwrapper.cpp b/src/mongo/scripting/mozjs/objectwrapper.cpp
index 5c921c1ebec..98f8094dfcc 100644
--- a/src/mongo/scripting/mozjs/objectwrapper.cpp
+++ b/src/mongo/scripting/mozjs/objectwrapper.cpp
@@ -361,6 +361,13 @@ void ObjectWrapper::setBSON(Key key, const BSONObj& obj, bool readOnly) {
setValue(key, value);
}
+void ObjectWrapper::setBSONArray(Key key, const BSONObj& obj, bool readOnly) {
+ JS::RootedValue value(_context);
+ ValueReader(_context, &value).fromBSONArray(obj, nullptr, readOnly);
+
+ setValue(key, value);
+}
+
void ObjectWrapper::setValue(Key key, JS::HandleValue val) {
key.set(_context, _object, val);
}
diff --git a/src/mongo/scripting/mozjs/objectwrapper.h b/src/mongo/scripting/mozjs/objectwrapper.h
index 8c85a5b42d1..cf6c61d39e1 100644
--- a/src/mongo/scripting/mozjs/objectwrapper.h
+++ b/src/mongo/scripting/mozjs/objectwrapper.h
@@ -115,6 +115,7 @@ public:
void setBoolean(Key key, bool val);
void setBSONElement(Key key, const BSONElement& elem, const BSONObj& obj, bool readOnly);
void setBSON(Key key, const BSONObj& obj, bool readOnly);
+ void setBSONArray(Key key, const BSONObj& obj, bool readOnly);
void setValue(Key key, JS::HandleValue value);
void setObject(Key key, JS::HandleObject value);
diff --git a/src/mongo/scripting/mozjs/uri.cpp b/src/mongo/scripting/mozjs/uri.cpp
new file mode 100644
index 00000000000..f943bb8a171
--- /dev/null
+++ b/src/mongo/scripting/mozjs/uri.cpp
@@ -0,0 +1,105 @@
+/**
+ * Copyright (C) 2016 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/uri.h"
+
+#include <algorithm>
+#include <iterator>
+
+#include "mongo/client/mongo_uri.h"
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/scripting/mozjs/valuereader.h"
+#include "mongo/scripting/mozjs/valuewriter.h"
+#include "mongo/scripting/mozjs/wrapconstrainedmethod.h"
+#include "mongo/stdx/memory.h"
+#include "mongo/util/mongoutils/str.h"
+
+namespace mongo {
+namespace mozjs {
+
+const JSFunctionSpec URIInfo::methods[2] = {
+ MONGO_ATTACH_JS_CONSTRAINED_METHOD(toString, URIInfo), JS_FS_END,
+};
+
+const char* const URIInfo::className = "MongoURI";
+
+void URIInfo::Functions::toString::call(JSContext* cx, JS::CallArgs args) {
+ ObjectWrapper o(cx, args.thisv());
+ ValueReader(cx, args.rval()).fromStringData(o.getString(InternedString::uri));
+}
+
+void URIInfo::construct(JSContext* cx, JS::CallArgs args) {
+ uassert(ErrorCodes::BadValue, "MongoURI needs 1 argument", args.length() == 1);
+
+ JS::HandleValue uriArg = args.get(0);
+ if (!uriArg.isString())
+ uasserted(ErrorCodes::BadValue, "uri must be a string");
+
+ std::string uri = ValueWriter(cx, args.get(0)).toString();
+
+ auto sw = MongoURI::parse(uri);
+ auto parsed = uassertStatusOK(sw);
+
+ BSONArrayBuilder serversBuilder;
+ for (const auto hp : parsed.getServers()) {
+ BSONObjBuilder b;
+ b.append("server", hp.toString());
+ b.append("host", hp.host());
+ if (hp.hasPort()) {
+ b.append("port", hp.port());
+ }
+ serversBuilder.append(b.obj());
+ }
+
+ BSONObjBuilder optsBuilder;
+ for (const auto& kvpair : parsed.getOptions()) {
+ optsBuilder.append(kvpair.first, kvpair.second);
+ }
+
+ JS::RootedObject thisv(cx);
+ auto scope = getScope(cx);
+ scope->getProto<URIInfo>().newObject(&thisv);
+ ObjectWrapper o(cx, thisv);
+
+ o.setValue(InternedString::uri, uriArg);
+ o.setString(InternedString::user, parsed.getUser());
+ o.setString(InternedString::password, parsed.getPassword());
+ o.setBSON(InternedString::options, optsBuilder.obj(), true);
+ o.setString(InternedString::database, parsed.getDatabase());
+ o.setBoolean(InternedString::isValid, parsed.isValid());
+ o.setString(InternedString::setName, parsed.getSetName());
+ o.setBSONArray(InternedString::servers, serversBuilder.arr(), true);
+
+ args.rval().setObjectOrNull(thisv);
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/uri.h b/src/mongo/scripting/mozjs/uri.h
new file mode 100644
index 00000000000..d1b487ce2ab
--- /dev/null
+++ b/src/mongo/scripting/mozjs/uri.h
@@ -0,0 +1,52 @@
+/**
+ * Copyright (C) 2016 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/scripting/mozjs/wraptype.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * A MongoURI object.
+ */
+struct URIInfo : public BaseInfo {
+ static void construct(JSContext* cx, JS::CallArgs args);
+
+ struct Functions {
+ MONGO_DECLARE_JS_FUNCTION(toString);
+ };
+
+ static const JSFunctionSpec methods[2];
+
+ static const char* const className;
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/valuereader.cpp b/src/mongo/scripting/mozjs/valuereader.cpp
index af26238763d..1eedef58e86 100644
--- a/src/mongo/scripting/mozjs/valuereader.cpp
+++ b/src/mongo/scripting/mozjs/valuereader.cpp
@@ -97,21 +97,7 @@ void ValueReader::fromBSONElement(const BSONElement& elem, const BSONObj& parent
_value.setInt32(elem.Int());
return;
case mongo::Array: {
- JS::AutoValueVector avv(_context);
-
- BSONForEach(subElem, elem.embeddedObject()) {
- JS::RootedValue member(_context);
-
- ValueReader(_context, &member).fromBSONElement(subElem, parent, readOnly);
- if (!avv.append(member)) {
- uasserted(ErrorCodes::JSInterpreterFailure, "Failed to append to JS array");
- }
- }
- JS::RootedObject array(_context, JS_NewArrayObject(_context, avv));
- if (!array) {
- uasserted(ErrorCodes::JSInterpreterFailure, "Failed to JS_NewArrayObject");
- }
- _value.setObjectOrNull(array);
+ fromBSONArray(elem.embeddedObject(), &parent, readOnly);
return;
}
case mongo::Object:
@@ -254,6 +240,24 @@ void ValueReader::fromBSON(const BSONObj& obj, const BSONObj* parent, bool readO
_value.setObjectOrNull(child);
}
+void ValueReader::fromBSONArray(const BSONObj& obj, const BSONObj* parent, bool readOnly) {
+ JS::AutoValueVector avv(_context);
+
+ BSONForEach(elem, obj) {
+ JS::RootedValue member(_context);
+
+ ValueReader(_context, &member).fromBSONElement(elem, parent ? *parent : obj, readOnly);
+ if (!avv.append(member)) {
+ uasserted(ErrorCodes::JSInterpreterFailure, "Failed to append to JS array");
+ }
+ }
+ JS::RootedObject array(_context, JS_NewArrayObject(_context, avv));
+ if (!array) {
+ uasserted(ErrorCodes::JSInterpreterFailure, "Failed to JS_NewArrayObject");
+ }
+ _value.setObjectOrNull(array);
+}
+
/**
* SpiderMonkey doesn't have a direct entry point to create a jsstring from
* utf8, so we have to flow through some slightly less public interfaces.
diff --git a/src/mongo/scripting/mozjs/valuereader.h b/src/mongo/scripting/mozjs/valuereader.h
index 6b03d30086c..58abed86c45 100644
--- a/src/mongo/scripting/mozjs/valuereader.h
+++ b/src/mongo/scripting/mozjs/valuereader.h
@@ -49,6 +49,7 @@ public:
void fromBSONElement(const BSONElement& elem, const BSONObj& parent, bool readOnly);
void fromBSON(const BSONObj& obj, const BSONObj* parent, bool readOnly);
+ void fromBSONArray(const BSONObj& obj, const BSONObj* parent, bool readOnly);
void fromDouble(double d);
void fromStringData(StringData sd);
void fromDecimal128(Decimal128 decimal);
diff --git a/src/mongo/shell/dbshell.cpp b/src/mongo/shell/dbshell.cpp
index 243bdd187ef..9fc6b6de5df 100644
--- a/src/mongo/shell/dbshell.cpp
+++ b/src/mongo/shell/dbshell.cpp
@@ -85,6 +85,10 @@ bool gotInterrupted = false;
bool inMultiLine = false;
static volatile bool atPrompt = false; // can eval before getting to prompt
+namespace {
+const auto kDefaultMongoURL = "mongodb://127.0.0.1:27017"_sd;
+}
+
namespace mongo {
Scope* shellMainScope;
@@ -200,35 +204,49 @@ void setupSignals() {
signal(SIGINT, quitNicely);
}
-string fixHost(const std::string& url, const std::string& host, const std::string& port) {
+string getURIFromArgs(const std::string& url, const std::string& host, const std::string& port) {
if (host.size() == 0 && port.size() == 0) {
- if (url.find("/") == string::npos) {
- // check for ips
- if (url.find(".") != string::npos)
- return url + "/test";
-
- if (url.rfind(":") != string::npos && isdigit(url[url.rfind(":") + 1]))
- return url + "/test";
- }
- return url;
+ return url.size() == 0 ? kDefaultMongoURL.toString() : url;
}
+ // The name URL is misleading; really it's just a positional argument that wasn't a file. The
+ // check for "/" means "this 'URL' is probably a real URL and not the db name (e.g.)".
if (url.find("/") != string::npos) {
- cerr << "url can't have host or port if you specify them individually" << endl;
+ cerr << "if a full URI is provided, you cannot also specify host or port" << endl;
quickExit(-1);
}
- string newurl((host.size() == 0) ? "127.0.0.1" : host);
- if (port.size() > 0)
- newurl += ":" + port;
- else if (host.find(':') == string::npos) {
- // need to add port with IPv6 addresses
- newurl += ":27017";
+ bool hostEndsInSock = str::endsWith(host, ".sock");
+
+ // If host looks like a full URI (i.e. has a slash and isn't a unix socket) and the other fields
+ // are empty, then just return host.
+ if (url.size() == 0 && port.size() == 0 &&
+ (!hostEndsInSock && host.find("/") != string::npos)) {
+ return host;
}
- newurl += "/" + url;
+ stringstream ss;
+ if (host.size() == 0) {
+ ss << "mongodb://127.0.0.1";
+ } else {
+ if (!str::startsWith(host, "mongodb://")) {
+ ss << "mongodb://";
+ }
+ ss << host;
+ }
+
+ if (!hostEndsInSock) {
+ if (port.size() > 0) {
+ ss << ":" << port;
+ } else if (host.find(':') == string::npos || str::endsWith(host, "]")) {
+ // Default the port to 27017 if the host did not provide one (i.e. the host has no
+ // colons or ends in ']' like an IPv6 address).
+ ss << ":27017";
+ }
+ }
- return newurl;
+ ss << "/" << url;
+ return ss.str();
}
static string OpSymbols = "~!%^&*-+=|:,<>/?.";
@@ -585,8 +603,6 @@ int _main(int argc, char* argv[], char** envp) {
mongo::shell_utils::RecordMyLocation(argv[0]);
- shellGlobalParams.url = "test";
-
mongo::runGlobalInitializersOrDie(argc, argv, envp);
// hide password from ps output
@@ -615,7 +631,8 @@ int _main(int argc, char* argv[], char** envp) {
if (mongo::serverGlobalParams.quiet)
ss << "__quiet = true;";
ss << "db = connect( \""
- << fixHost(shellGlobalParams.url, shellGlobalParams.dbhost, shellGlobalParams.port)
+ << getURIFromArgs(
+ shellGlobalParams.url, shellGlobalParams.dbhost, shellGlobalParams.port)
<< "\")";
mongo::shell_utils::_dbConnect = ss.str();
diff --git a/src/mongo/shell/mongo.js b/src/mongo/shell/mongo.js
index c7ba018358a..2cadfcb77ed 100644
--- a/src/mongo/shell/mongo.js
+++ b/src/mongo/shell/mongo.js
@@ -194,6 +194,11 @@ Mongo.prototype.getReadConcern = function() {
};
connect = function(url, user, pass) {
+ if (url instanceof MongoURI) {
+ user = url.user;
+ pass = url.password;
+ url = url.uri;
+ }
if (user && !pass)
throw Error("you specified a user and not a password. " +
"either you need a password, or you're using the old connect api");
@@ -201,8 +206,7 @@ connect = function(url, user, pass) {
// Validate connection string "url" as "hostName:portNumber/databaseName"
// or "hostName/databaseName"
// or "databaseName"
- // hostName may be an IPv6 address (with colons), in which case ":portNumber" is required
- //
+ // or full mongo uri.
var urlType = typeof url;
if (urlType == "undefined") {
throw Error("Missing connection string");
@@ -215,45 +219,20 @@ connect = function(url, user, pass) {
if (0 == url.length) {
throw Error("Empty connection string");
}
+
if (!url.startsWith("mongodb://")) {
var colon = url.lastIndexOf(":");
var slash = url.lastIndexOf("/");
- if (0 == colon || 0 == slash) {
- throw Error("Missing host name in connection string \"" + url + "\"");
- }
- if (colon == slash - 1 || colon == url.length - 1) {
- throw Error("Missing port number in connection string \"" + url + "\"");
- }
- if (colon != -1 && colon < slash) {
- var portNumber = url.substring(colon + 1, slash);
- if (portNumber.length > 5 || !/^\d*$/.test(portNumber) ||
- parseInt(portNumber) > 65535) {
- throw Error("Invalid port number \"" + portNumber + "\" in connection string \"" +
- url + "\"");
- }
- }
- if (slash == url.length - 1) {
- throw Error("Missing database name in connection string \"" + url + "\"");
+ if (slash == -1 && colon == -1) {
+ url = "mongodb://127.0.0.1:27017/" + url;
+ } else {
+ url = "mongodb://" + url;
}
}
- var db;
- if (url.startsWith("mongodb://")) {
- chatty("connecting to: " + url);
- db = new Mongo(url);
- if (db.defaultDB.length == 0) {
- db.defaultDB = "test";
- }
- db = db.getDB(db.defaultDB);
- } else if (slash == -1) {
- chatty("connecting to: 127.0.0.1:27017/" + url);
- db = new Mongo().getDB(url);
- } else {
- var hostPart = url.substring(0, slash);
- var dbPart = url.substring(slash + 1);
- chatty("connecting to: " + hostPart + "/" + dbPart);
- db = new Mongo(hostPart).getDB(dbPart);
- }
+ chatty("connecting to: " + url);
+ var m = new Mongo(url);
+ db = m.getDB(m.defaultDB);
if (user && pass) {
if (!db.auth(user, pass)) {
diff --git a/src/mongo/util/net/hostandport.cpp b/src/mongo/util/net/hostandport.cpp
index 36106ac83fa..04be4a97414 100644
--- a/src/mongo/util/net/hostandport.cpp
+++ b/src/mongo/util/net/hostandport.cpp
@@ -123,6 +123,12 @@ Status HostAndPort::initialize(StringData s) {
hostPart = s.substr(openBracketPos + 1, closeBracketPos - openBracketPos - 1);
// prevent accidental assignment of port to the value of the final portion of hostPart
if (colonPos < closeBracketPos) {
+ // If the last colon is inside the brackets, then there must not be a port.
+ if (s.size() != closeBracketPos + 1) {
+ return Status(ErrorCodes::FailedToParse,
+ str::stream() << "missing colon after ']' before the port in "
+ << s.toString());
+ }
colonPos = std::string::npos;
} else if (colonPos != closeBracketPos + 1) {
return Status(ErrorCodes::FailedToParse,
@@ -154,7 +160,7 @@ Status HostAndPort::initialize(StringData s) {
if (!status.isOK()) {
return status;
}
- if (port <= 0) {
+ if (port <= 0 || port > 65535) {
return Status(ErrorCodes::FailedToParse,
str::stream() << "Port number " << port
<< " out of range parsing HostAndPort from \""