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
						
					
					
				| /*
 | |
|  * 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);
 | |
| });
 |