Skip to content

Commit d01a859

Browse files
manarhusriehdhmlau
authored andcommitted
fix: connection string seed list parsing
closes: #663 Signed-off-by: Manar Husrieh <[email protected]>
1 parent 48ee112 commit d01a859

File tree

3 files changed

+352
-19
lines changed

3 files changed

+352
-19
lines changed

lib/mongodb.js

+104-8
Original file line numberDiff line numberDiff line change
@@ -332,15 +332,12 @@ MongoDB.prototype.connect = function(callback) {
332332
if (callback) callback(err);
333333
}
334334

335-
const urlObj = new URL(self.settings.url);
336-
337-
if ((urlObj.pathname === '' ||
338-
urlObj.pathname.split('/')[1] === '') &&
339-
typeof self.settings.database === 'string') {
340-
urlObj.pathname = self.settings.database;
341-
self.settings.url = urlObj.toString();
335+
// This is special processing if database is not part of url, but is in settings
336+
if (self.settings.url && self.settings.database) {
337+
if (self.settings.url.indexOf('/' + self.settings.database) === -1) {
338+
self.settings.url = processMongoDBURL(self.settings.database, self.settings.url);
339+
}
342340
}
343-
344341
new mongodb.MongoClient(self.settings.url, validOptions).connect(function(
345342
err,
346343
client,
@@ -2120,6 +2117,105 @@ MongoDB.prototype.rollback = function(tx, cb) {
21202117
});
21212118
};
21222119

2120+
exports.processMongoDBURL = processMongoDBURL;
2121+
/**
2122+
* This method parses a Mongo connection url string and refers the formats
2123+
* specified at: https://www.mongodb.com/docs/manual/reference/connection-string/.
2124+
* Since there are cases where database is not specified in the url, but as a settings property,
2125+
* the code has to reflect that in the url otherwise the MongoDB driver defaults to 'admin' db.
2126+
* @param {string} settingsDatabase - the database that will be added if url doesn't have a db specified
2127+
* @param {string} mongoUrl - the url to be processed for database manipulation
2128+
*/
2129+
function processMongoDBURL(settingsDatabase, mongoUrl) {
2130+
// Reference: https://www.mongodb.com/docs/manual/reference/connection-string/
2131+
// Standard format::: mongodb://[username:password@]host1[:port1][,...hostN[:portN]][/[defaultauthdb][?options]]
2132+
// DNS SeedList format::: mongodb+srv://server.example.com/?connectTimeoutMS=300000&authSource=aDifferentAuthDB
2133+
// Actual replicaset example::: mongodb://mongodb1.example.com:27317,mongodb2.example.com:27017/?connectTimeoutMS=300000&replicaSet=mySet&authSource=aDifferentAuthDB
2134+
2135+
if (mongoUrl) {
2136+
// 1. Know the protocol
2137+
let baseUrl = '';
2138+
if (mongoUrl.startsWith('mongodb:'))
2139+
baseUrl = 'mongodb://';
2140+
else if (mongoUrl.startsWith('mongodb+srv:'))
2141+
baseUrl = 'mongodb+srv://';
2142+
else if (mongoUrl.startsWith('loopback-connector-mongodb:'))
2143+
baseUrl = 'loopback-connector-mongodb://';
2144+
else if (mongoUrl.startsWith('loopback-connector-mongodb+srv:'))
2145+
baseUrl = 'loopback-connector-mongodb+srv://';
2146+
else
2147+
return mongoUrl; // Not a MongoURL that we can process
2148+
2149+
let remainderUrl = mongoUrl.substring(baseUrl.length);
2150+
// 2. Check if userId/password is present
2151+
let uidPassword = '';
2152+
if (remainderUrl.indexOf('@') !== -1) {
2153+
const parts = remainderUrl.split('@');
2154+
uidPassword = parts[0];
2155+
if (parts.length === 2)
2156+
remainderUrl = parts[1];
2157+
else
2158+
remainderUrl = '';
2159+
}
2160+
let hosts = '';
2161+
let dbName = '';
2162+
let options = '';
2163+
let hostsArray = [];
2164+
// 3. Check if comma separated replicas are specified
2165+
if (remainderUrl.indexOf(',') !== -1) {
2166+
hostsArray = remainderUrl.split(',');
2167+
remainderUrl = hostsArray[hostsArray.length - 1];
2168+
}
2169+
2170+
// 4. Check if authDB is specified in the URL
2171+
const slashIndex = remainderUrl.indexOf('/');
2172+
if ((slashIndex !== -1)) {
2173+
if (slashIndex !== (remainderUrl.length - 1)) {
2174+
const optionsIndex = remainderUrl.indexOf('?');
2175+
if (optionsIndex !== -1) {
2176+
options = remainderUrl.substring(optionsIndex + 1);
2177+
dbName = remainderUrl.substring(slashIndex + 1, optionsIndex);
2178+
} else {
2179+
// No DB options specified
2180+
dbName = remainderUrl.substring(slashIndex + 1);
2181+
}
2182+
}
2183+
2184+
if (hostsArray.length > 1) {
2185+
const newHosts = hostsArray;
2186+
newHosts.pop();
2187+
newHosts.push(remainderUrl.substring(0, slashIndex));
2188+
hosts = newHosts.join(',');
2189+
} else {
2190+
hosts = remainderUrl.substring(0, slashIndex);
2191+
}
2192+
} else {
2193+
// No database specified
2194+
if (hostsArray.length > 1)
2195+
hosts = hostsArray.join(',');
2196+
else
2197+
hosts = remainderUrl;
2198+
}
2199+
2200+
// 5. Reconstruct url, but this time add database from settings if URL didn't have it
2201+
// The below code has an overlap with generateMongoDBURL()
2202+
let modifiedUrl = baseUrl;
2203+
2204+
if (uidPassword)
2205+
modifiedUrl += uidPassword + '@';
2206+
if (hosts)
2207+
modifiedUrl += hosts;
2208+
2209+
modifiedUrl += '/' + (dbName ? dbName : settingsDatabase);
2210+
2211+
if (options)
2212+
modifiedUrl += '?' + options;
2213+
2214+
return modifiedUrl;
2215+
}
2216+
return mongoUrl;
2217+
}
2218+
21232219
function isInTransation(options) {
21242220
const ops = {};
21252221
if (options && options.transaction && options.transaction.isInTransation)

package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)