PageRenderTime 32ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/Web appli/node_modules/mongodb/lib/url_parser.js

https://gitlab.com/Ziracmo/ProjetTransversal
JavaScript | 401 lines | 315 code | 39 blank | 47 comment | 154 complexity | be27f8721ef12c2225b70614bd4b68f2 MD5 | raw file
  1. "use strict";
  2. var ReadPreference = require('./read_preference'),
  3. parser = require('url'),
  4. f = require('util').format;
  5. module.exports = function(url, options) {
  6. // Ensure we have a default options object if none set
  7. options = options || {};
  8. // Variables
  9. var connection_part = '';
  10. var auth_part = '';
  11. var query_string_part = '';
  12. var dbName = 'admin';
  13. // Url parser result
  14. var result = parser.parse(url, true);
  15. if(result.protocol != 'mongodb:') {
  16. throw new Error('invalid schema, expected mongodb');
  17. }
  18. if((result.hostname == null || result.hostname == '') && url.indexOf('.sock') == -1) {
  19. throw new Error('no hostname or hostnames provided in connection string');
  20. }
  21. if(result.port == '0') {
  22. throw new Error('invalid port (zero) with hostname');
  23. }
  24. if(!isNaN(parseInt(result.port, 10)) && parseInt(result.port, 10) > 65535) {
  25. throw new Error('invalid port (larger than 65535) with hostname');
  26. }
  27. if(result.path
  28. && result.path.length > 0
  29. && result.path[0] != '/'
  30. && url.indexOf('.sock') == -1) {
  31. throw new Error('missing delimiting slash between hosts and options');
  32. }
  33. if(result.query) {
  34. for(var name in result.query) {
  35. if(name.indexOf('::') != -1) {
  36. throw new Error('double colon in host identifier');
  37. }
  38. if(result.query[name] == '') {
  39. throw new Error('query parameter ' + name + ' is an incomplete value pair');
  40. }
  41. }
  42. }
  43. if(result.auth) {
  44. var parts = result.auth.split(':');
  45. if(url.indexOf(result.auth) != -1 && parts.length > 2) {
  46. throw new Error('Username with password containing an unescaped colon');
  47. }
  48. if(url.indexOf(result.auth) != -1 && result.auth.indexOf('@') != -1) {
  49. throw new Error('Username containing an unescaped at-sign');
  50. }
  51. }
  52. // Remove query
  53. var clean = url.split('?').shift();
  54. // Extract the list of hosts
  55. var strings = clean.split(',');
  56. var hosts = [];
  57. for(var i = 0; i < strings.length; i++) {
  58. var hostString = strings[i];
  59. if(hostString.indexOf('mongodb') != -1) {
  60. if(hostString.indexOf('@') != -1) {
  61. hosts.push(hostString.split('@').pop())
  62. } else {
  63. hosts.push(hostString.substr('mongodb://'.length));
  64. }
  65. } else if(hostString.indexOf('/') != -1) {
  66. hosts.push(hostString.split('/').shift());
  67. } else if(hostString.indexOf('/') == -1) {
  68. hosts.push(hostString.trim());
  69. }
  70. }
  71. for(var i = 0; i < hosts.length; i++) {
  72. var r = parser.parse(f('mongodb://%s', hosts[i].trim()));
  73. if(r.path && r.path.indexOf(':') != -1) {
  74. throw new Error('double colon in host identifier');
  75. }
  76. }
  77. // If we have a ? mark cut the query elements off
  78. if(url.indexOf("?") != -1) {
  79. query_string_part = url.substr(url.indexOf("?") + 1);
  80. connection_part = url.substring("mongodb://".length, url.indexOf("?"))
  81. } else {
  82. connection_part = url.substring("mongodb://".length);
  83. }
  84. // Check if we have auth params
  85. if(connection_part.indexOf("@") != -1) {
  86. auth_part = connection_part.split("@")[0];
  87. connection_part = connection_part.split("@")[1];
  88. }
  89. // Check if the connection string has a db
  90. if(connection_part.indexOf(".sock") != -1) {
  91. if(connection_part.indexOf(".sock/") != -1) {
  92. dbName = connection_part.split(".sock/")[1];
  93. // Check if multiple database names provided, or just an illegal trailing backslash
  94. if (dbName.indexOf("/") != -1) {
  95. if (dbName.split("/").length == 2 && dbName.split("/")[1].length == 0) {
  96. throw new Error('Illegal trailing backslash after database name');
  97. }
  98. throw new Error('More than 1 database name in URL');
  99. }
  100. connection_part = connection_part.split("/", connection_part.indexOf(".sock") + ".sock".length);
  101. }
  102. } else if(connection_part.indexOf("/") != -1) {
  103. // Check if multiple database names provided, or just an illegal trailing backslash
  104. if (connection_part.split("/").length > 2) {
  105. if (connection_part.split("/")[2].length == 0) {
  106. throw new Error('Illegal trailing backslash after database name');
  107. }
  108. throw new Error('More than 1 database name in URL');
  109. }
  110. dbName = connection_part.split("/")[1];
  111. connection_part = connection_part.split("/")[0];
  112. }
  113. // Result object
  114. var object = {};
  115. // Pick apart the authentication part of the string
  116. var authPart = auth_part || '';
  117. var auth = authPart.split(':', 2);
  118. // Decode the URI components
  119. auth[0] = decodeURIComponent(auth[0]);
  120. if(auth[1]){
  121. auth[1] = decodeURIComponent(auth[1]);
  122. }
  123. // Add auth to final object if we have 2 elements
  124. if(auth.length == 2) object.auth = {user: auth[0], password: auth[1]};
  125. // Variables used for temporary storage
  126. var hostPart;
  127. var urlOptions;
  128. var servers;
  129. var serverOptions = {socketOptions: {}};
  130. var dbOptions = {read_preference_tags: []};
  131. var replSetServersOptions = {socketOptions: {}};
  132. var mongosOptions = {socketOptions: {}};
  133. // Add server options to final object
  134. object.server_options = serverOptions;
  135. object.db_options = dbOptions;
  136. object.rs_options = replSetServersOptions;
  137. object.mongos_options = mongosOptions;
  138. // Let's check if we are using a domain socket
  139. if(url.match(/\.sock/)) {
  140. // Split out the socket part
  141. var domainSocket = url.substring(
  142. url.indexOf("mongodb://") + "mongodb://".length
  143. , url.lastIndexOf(".sock") + ".sock".length);
  144. // Clean out any auth stuff if any
  145. if(domainSocket.indexOf("@") != -1) domainSocket = domainSocket.split("@")[1];
  146. servers = [{domain_socket: domainSocket}];
  147. } else {
  148. // Split up the db
  149. hostPart = connection_part;
  150. // Deduplicate servers
  151. var deduplicatedServers = {};
  152. // Parse all server results
  153. servers = hostPart.split(',').map(function(h) {
  154. var _host, _port, ipv6match;
  155. //check if it matches [IPv6]:port, where the port number is optional
  156. if ((ipv6match = /\[([^\]]+)\](?:\:(.+))?/.exec(h))) {
  157. _host = ipv6match[1];
  158. _port = parseInt(ipv6match[2], 10) || 27017;
  159. } else {
  160. //otherwise assume it's IPv4, or plain hostname
  161. var hostPort = h.split(':', 2);
  162. _host = hostPort[0] || 'localhost';
  163. _port = hostPort[1] != null ? parseInt(hostPort[1], 10) : 27017;
  164. // Check for localhost?safe=true style case
  165. if(_host.indexOf("?") != -1) _host = _host.split(/\?/)[0];
  166. }
  167. // No entry returned for duplicate servr
  168. if(deduplicatedServers[_host + "_" + _port]) return null;
  169. deduplicatedServers[_host + "_" + _port] = 1;
  170. // Return the mapped object
  171. return {host: _host, port: _port};
  172. }).filter(function(x) {
  173. return x != null;
  174. });
  175. }
  176. // Get the db name
  177. object.dbName = dbName || 'admin';
  178. // Split up all the options
  179. urlOptions = (query_string_part || '').split(/[&;]/);
  180. // Ugh, we have to figure out which options go to which constructor manually.
  181. urlOptions.forEach(function(opt) {
  182. if(!opt) return;
  183. var splitOpt = opt.split('='), name = splitOpt[0], value = splitOpt[1];
  184. // Options implementations
  185. switch(name) {
  186. case 'slaveOk':
  187. case 'slave_ok':
  188. serverOptions.slave_ok = (value == 'true');
  189. dbOptions.slaveOk = (value == 'true');
  190. break;
  191. case 'maxPoolSize':
  192. case 'poolSize':
  193. serverOptions.poolSize = parseInt(value, 10);
  194. replSetServersOptions.poolSize = parseInt(value, 10);
  195. break;
  196. case 'autoReconnect':
  197. case 'auto_reconnect':
  198. serverOptions.auto_reconnect = (value == 'true');
  199. break;
  200. case 'minPoolSize':
  201. throw new Error("minPoolSize not supported");
  202. case 'maxIdleTimeMS':
  203. throw new Error("maxIdleTimeMS not supported");
  204. case 'waitQueueMultiple':
  205. throw new Error("waitQueueMultiple not supported");
  206. case 'waitQueueTimeoutMS':
  207. throw new Error("waitQueueTimeoutMS not supported");
  208. case 'uuidRepresentation':
  209. throw new Error("uuidRepresentation not supported");
  210. case 'ssl':
  211. if(value == 'prefer') {
  212. serverOptions.ssl = value;
  213. replSetServersOptions.ssl = value;
  214. mongosOptions.ssl = value;
  215. break;
  216. }
  217. serverOptions.ssl = (value == 'true');
  218. replSetServersOptions.ssl = (value == 'true');
  219. mongosOptions.ssl = (value == 'true');
  220. break;
  221. case 'sslValidate':
  222. serverOptions.sslValidate = (value == 'true');
  223. replSetServersOptions.sslValidate = (value == 'true');
  224. mongosOptions.sslValidate = (value == 'true');
  225. break;
  226. case 'replicaSet':
  227. case 'rs_name':
  228. replSetServersOptions.rs_name = value;
  229. break;
  230. case 'reconnectWait':
  231. replSetServersOptions.reconnectWait = parseInt(value, 10);
  232. break;
  233. case 'retries':
  234. replSetServersOptions.retries = parseInt(value, 10);
  235. break;
  236. case 'readSecondary':
  237. case 'read_secondary':
  238. replSetServersOptions.read_secondary = (value == 'true');
  239. break;
  240. case 'fsync':
  241. dbOptions.fsync = (value == 'true');
  242. break;
  243. case 'journal':
  244. dbOptions.j = (value == 'true');
  245. break;
  246. case 'safe':
  247. dbOptions.safe = (value == 'true');
  248. break;
  249. case 'nativeParser':
  250. case 'native_parser':
  251. dbOptions.native_parser = (value == 'true');
  252. break;
  253. case 'readConcernLevel':
  254. dbOptions.readConcern = {level: value};
  255. break;
  256. case 'connectTimeoutMS':
  257. serverOptions.socketOptions.connectTimeoutMS = parseInt(value, 10);
  258. replSetServersOptions.socketOptions.connectTimeoutMS = parseInt(value, 10);
  259. mongosOptions.socketOptions.connectTimeoutMS = parseInt(value, 10);
  260. break;
  261. case 'socketTimeoutMS':
  262. serverOptions.socketOptions.socketTimeoutMS = parseInt(value, 10);
  263. replSetServersOptions.socketOptions.socketTimeoutMS = parseInt(value, 10);
  264. mongosOptions.socketOptions.socketTimeoutMS = parseInt(value, 10);
  265. break;
  266. case 'w':
  267. dbOptions.w = parseInt(value, 10);
  268. if(isNaN(dbOptions.w)) dbOptions.w = value;
  269. break;
  270. case 'authSource':
  271. dbOptions.authSource = value;
  272. break;
  273. case 'gssapiServiceName':
  274. dbOptions.gssapiServiceName = value;
  275. break;
  276. case 'authMechanism':
  277. if(value == 'GSSAPI') {
  278. // If no password provided decode only the principal
  279. if(object.auth == null) {
  280. var urlDecodeAuthPart = decodeURIComponent(authPart);
  281. if(urlDecodeAuthPart.indexOf("@") == -1) throw new Error("GSSAPI requires a provided principal");
  282. object.auth = {user: urlDecodeAuthPart, password: null};
  283. } else {
  284. object.auth.user = decodeURIComponent(object.auth.user);
  285. }
  286. } else if(value == 'MONGODB-X509') {
  287. object.auth = {user: decodeURIComponent(authPart)};
  288. }
  289. // Only support GSSAPI or MONGODB-CR for now
  290. if(value != 'GSSAPI'
  291. && value != 'MONGODB-X509'
  292. && value != 'MONGODB-CR'
  293. && value != 'DEFAULT'
  294. && value != 'SCRAM-SHA-1'
  295. && value != 'PLAIN')
  296. throw new Error("only DEFAULT, GSSAPI, PLAIN, MONGODB-X509, SCRAM-SHA-1 or MONGODB-CR is supported by authMechanism");
  297. // Authentication mechanism
  298. dbOptions.authMechanism = value;
  299. break;
  300. case 'authMechanismProperties':
  301. // Split up into key, value pairs
  302. var values = value.split(',');
  303. var o = {};
  304. // For each value split into key, value
  305. values.forEach(function(x) {
  306. var v = x.split(':');
  307. o[v[0]] = v[1];
  308. });
  309. // Set all authMechanismProperties
  310. dbOptions.authMechanismProperties = o;
  311. // Set the service name value
  312. if(typeof o.SERVICE_NAME == 'string') dbOptions.gssapiServiceName = o.SERVICE_NAME;
  313. if(typeof o.SERVICE_REALM == 'string') dbOptions.gssapiServiceRealm = o.SERVICE_REALM;
  314. if(typeof o.CANONICALIZE_HOST_NAME == 'string') dbOptions.gssapiCanonicalizeHostName = o.CANONICALIZE_HOST_NAME == 'true' ? true : false;
  315. break;
  316. case 'wtimeoutMS':
  317. dbOptions.wtimeout = parseInt(value, 10);
  318. break;
  319. case 'readPreference':
  320. if(!ReadPreference.isValid(value)) throw new Error("readPreference must be either primary/primaryPreferred/secondary/secondaryPreferred/nearest");
  321. dbOptions.readPreference = value;
  322. break;
  323. case 'readPreferenceTags':
  324. // Decode the value
  325. value = decodeURIComponent(value);
  326. // Contains the tag object
  327. var tagObject = {};
  328. if(value == null || value == '') {
  329. dbOptions.read_preference_tags.push(tagObject);
  330. break;
  331. }
  332. // Split up the tags
  333. var tags = value.split(/\,/);
  334. for(var i = 0; i < tags.length; i++) {
  335. var parts = tags[i].trim().split(/\:/);
  336. tagObject[parts[0]] = parts[1];
  337. }
  338. // Set the preferences tags
  339. dbOptions.read_preference_tags.push(tagObject);
  340. break;
  341. default:
  342. break;
  343. }
  344. });
  345. // No tags: should be null (not [])
  346. if(dbOptions.read_preference_tags.length === 0) {
  347. dbOptions.read_preference_tags = null;
  348. }
  349. // Validate if there are an invalid write concern combinations
  350. if((dbOptions.w == -1 || dbOptions.w == 0) && (
  351. dbOptions.journal == true
  352. || dbOptions.fsync == true
  353. || dbOptions.safe == true)) throw new Error("w set to -1 or 0 cannot be combined with safe/w/journal/fsync")
  354. // If no read preference set it to primary
  355. if(!dbOptions.readPreference) {
  356. dbOptions.readPreference = 'primary';
  357. }
  358. // Add servers to result
  359. object.servers = servers;
  360. // Returned parsed object
  361. return object;
  362. }