You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					223 lines
				
				6.4 KiB
			
		
		
			
		
	
	
					223 lines
				
				6.4 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * Copyright (c) 2013 ObjectLabs Corporation
							 | 
						||
| 
								 | 
							
								 * Distributed under the MIT license - http://opensource.org/licenses/MIT
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Creates a parser.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {Object=} options
							 | 
						||
| 
								 | 
							
								 * @constructor
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function MongodbUriParser(options) {
							 | 
						||
| 
								 | 
							
								    if (options && options.scheme) {
							 | 
						||
| 
								 | 
							
								        this.scheme = options.scheme;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Takes a URI of the form:
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 *   mongodb://[username[:password]@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database]][?options]
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * and returns an object of the form:
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 *   {
							 | 
						||
| 
								 | 
							
								 *     scheme: !String,
							 | 
						||
| 
								 | 
							
								 *     username: String=,
							 | 
						||
| 
								 | 
							
								 *     password: String=,
							 | 
						||
| 
								 | 
							
								 *     hosts: [ { host: !String, port: Number= }, ... ],
							 | 
						||
| 
								 | 
							
								 *     database: String=,
							 | 
						||
| 
								 | 
							
								 *     options: Object=
							 | 
						||
| 
								 | 
							
								 *   }
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * scheme and hosts will always be present. Other fields will only be present in the result if they were
							 | 
						||
| 
								 | 
							
								 * present in the input.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {!String} uri
							 | 
						||
| 
								 | 
							
								 * @return {Object}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								MongodbUriParser.prototype.parse = function parse(uri) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var uriObject = {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var i = uri.indexOf('://');
							 | 
						||
| 
								 | 
							
								    if (i < 0) {
							 | 
						||
| 
								 | 
							
								        throw new Error('No scheme found in URI ' + uri);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    uriObject.scheme = uri.substring(0, i);
							 | 
						||
| 
								 | 
							
								    if (this.scheme && this.scheme !== uriObject.scheme) {
							 | 
						||
| 
								 | 
							
								        throw new Error('URI must begin with ' + this.scheme + '://');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    var rest = uri.substring(i + 3);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    i = rest.indexOf('@');
							 | 
						||
| 
								 | 
							
								    if (i >= 0) {
							 | 
						||
| 
								 | 
							
								        var credentials = rest.substring(0, i);
							 | 
						||
| 
								 | 
							
								        rest = rest.substring(i + 1);
							 | 
						||
| 
								 | 
							
								        i = credentials.indexOf(':');
							 | 
						||
| 
								 | 
							
								        if (i >= 0) {
							 | 
						||
| 
								 | 
							
								            uriObject.username = decodeURIComponent(credentials.substring(0, i));
							 | 
						||
| 
								 | 
							
								            uriObject.password = decodeURIComponent(credentials.substring(i + 1));
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            uriObject.username = decodeURIComponent(credentials);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    i = rest.indexOf('?');
							 | 
						||
| 
								 | 
							
								    if (i >= 0) {
							 | 
						||
| 
								 | 
							
								        var options = rest.substring(i + 1);
							 | 
						||
| 
								 | 
							
								        rest = rest.substring(0, i);
							 | 
						||
| 
								 | 
							
								        uriObject.options = {};
							 | 
						||
| 
								 | 
							
								        options.split('&').forEach(function (o) {
							 | 
						||
| 
								 | 
							
								            var iEquals = o.indexOf('=');
							 | 
						||
| 
								 | 
							
								            uriObject.options[decodeURIComponent(o.substring(0, iEquals))] = decodeURIComponent(o.substring(iEquals + 1));
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    i = rest.indexOf('/');
							 | 
						||
| 
								 | 
							
								    if (i >= 0) {
							 | 
						||
| 
								 | 
							
								        // Make sure the database name isn't the empty string
							 | 
						||
| 
								 | 
							
								        if (i < rest.length - 1) {
							 | 
						||
| 
								 | 
							
								            uriObject.database = decodeURIComponent(rest.substring(i + 1));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        rest = rest.substring(0, i);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this._parseAddress(rest, uriObject);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return uriObject;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Parses the address into the uriObject, mutating it.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {!String} address
							 | 
						||
| 
								 | 
							
								 * @param {!Object} uriObject
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								MongodbUriParser.prototype._parseAddress = function _parseAddress(address, uriObject) {
							 | 
						||
| 
								 | 
							
								    uriObject.hosts = [];
							 | 
						||
| 
								 | 
							
								    address.split(',').forEach(function (h) {
							 | 
						||
| 
								 | 
							
								        var i = h.indexOf(':');
							 | 
						||
| 
								 | 
							
								        if (i >= 0) {
							 | 
						||
| 
								 | 
							
								            uriObject.hosts.push(
							 | 
						||
| 
								 | 
							
								                    {
							 | 
						||
| 
								 | 
							
								                        host: decodeURIComponent(h.substring(0, i)),
							 | 
						||
| 
								 | 
							
								                        port: parseInt(h.substring(i + 1))
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								            );
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            uriObject.hosts.push({ host: decodeURIComponent(h) });
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Takes a URI object and returns a URI string of the form:
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 *   mongodb://[username[:password]@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database]][?options]
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {Object=} uriObject
							 | 
						||
| 
								 | 
							
								 * @return {String}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								MongodbUriParser.prototype.format = function format(uriObject) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!uriObject) {
							 | 
						||
| 
								 | 
							
								        return (this.scheme || 'mongodb') + '://localhost';
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (this.scheme && uriObject.scheme && this.scheme !== uriObject.scheme) {
							 | 
						||
| 
								 | 
							
								        throw new Error('Scheme not supported: ' + uriObject.scheme);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    var uri = (this.scheme || uriObject.scheme || 'mongodb') + '://';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (uriObject.username) {
							 | 
						||
| 
								 | 
							
								        uri += encodeURIComponent(uriObject.username);
							 | 
						||
| 
								 | 
							
								        // While it's not to the official spec, we allow empty passwords
							 | 
						||
| 
								 | 
							
								        if (uriObject.password) {
							 | 
						||
| 
								 | 
							
								            uri += ':' + encodeURIComponent(uriObject.password);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        uri += '@';
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    uri += this._formatAddress(uriObject);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // While it's not to the official spec, we only put a slash if there's a database, independent of whether there are options
							 | 
						||
| 
								 | 
							
								    if (uriObject.database) {
							 | 
						||
| 
								 | 
							
								        uri += '/' + encodeURIComponent(uriObject.database);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (uriObject.options) {
							 | 
						||
| 
								 | 
							
								        Object.keys(uriObject.options).forEach(function (k, i) {
							 | 
						||
| 
								 | 
							
								            uri += i === 0 ? '?' : '&';
							 | 
						||
| 
								 | 
							
								            uri += encodeURIComponent(k) + '=' + encodeURIComponent(uriObject.options[k]);
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return uri;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Formats the address portion of the uriObject, returning it.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {!Object} uriObject
							 | 
						||
| 
								 | 
							
								 * @return {String}
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								MongodbUriParser.prototype._formatAddress = function _formatAddress(uriObject) {
							 | 
						||
| 
								 | 
							
								    var address = '';
							 | 
						||
| 
								 | 
							
								    uriObject.hosts.forEach(function (h, i) {
							 | 
						||
| 
								 | 
							
								        if (i > 0) {
							 | 
						||
| 
								 | 
							
								            address += ',';
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        address += encodeURIComponent(h.host);
							 | 
						||
| 
								 | 
							
								        if (h.port) {
							 | 
						||
| 
								 | 
							
								            address += ':' + encodeURIComponent(h.port);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								    return address;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Takes either a URI object or string and returns a Mongoose connection string. Specifically, instead of listing all
							 | 
						||
| 
								 | 
							
								 * hosts and ports in a single URI, a Mongoose connection string contains a list of URIs each with a single host and
							 | 
						||
| 
								 | 
							
								 * port pair.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Useful in environments where a MongoDB URI environment variable is provided, but needs to be programmatically
							 | 
						||
| 
								 | 
							
								 * transformed into a string digestible by mongoose.connect()--for example, when deploying to a PaaS like Heroku
							 | 
						||
| 
								 | 
							
								 * using a MongoDB add-on like MongoLab.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {!Object|String} uri
							 | 
						||
| 
								 | 
							
								 * @return {String}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								MongodbUriParser.prototype.formatMongoose = function formatMongoose(uri) {
							 | 
						||
| 
								 | 
							
								    var parser = this;
							 | 
						||
| 
								 | 
							
								    if (typeof uri === 'string') {
							 | 
						||
| 
								 | 
							
								        uri = parser.parse(uri);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (!uri) {
							 | 
						||
| 
								 | 
							
								        return parser.format(uri);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    var connectionString = '';
							 | 
						||
| 
								 | 
							
								    uri.hosts.forEach(function (h, i) {
							 | 
						||
| 
								 | 
							
								        if (i > 0) {
							 | 
						||
| 
								 | 
							
								            connectionString += ',';
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        // This trick is okay because format() never dynamically inspects the keys in its argument
							 | 
						||
| 
								 | 
							
								        var singleUriObject = Object.create(uri);
							 | 
						||
| 
								 | 
							
								        singleUriObject.hosts = [ h ];
							 | 
						||
| 
								 | 
							
								        connectionString += parser.format(singleUriObject);
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								    return connectionString;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exports.MongodbUriParser = MongodbUriParser;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var defaultParser = new MongodbUriParser();
							 | 
						||
| 
								 | 
							
								[ 'parse', 'format', 'formatMongoose' ].forEach(function (f) {
							 | 
						||
| 
								 | 
							
								    exports[f] = defaultParser[f].bind(defaultParser);
							 | 
						||
| 
								 | 
							
								});
							 |