connection_config.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. 'use strict';
  2. const urlParse = require('url').parse;
  3. const ClientConstants = require('./constants/client');
  4. const Charsets = require('./constants/charsets');
  5. let SSLProfiles = null;
  6. const validOptions = {
  7. authPlugins: 1,
  8. authSwitchHandler: 1,
  9. bigNumberStrings: 1,
  10. charset: 1,
  11. charsetNumber: 1,
  12. compress: 1,
  13. connectAttributes: 1,
  14. connectTimeout: 1,
  15. database: 1,
  16. dateStrings: 1,
  17. debug: 1,
  18. decimalNumbers: 1,
  19. enableKeepAlive: 1,
  20. flags: 1,
  21. host: 1,
  22. insecureAuth: 1,
  23. isServer: 1,
  24. keepAliveInitialDelay: 1,
  25. localAddress: 1,
  26. maxPreparedStatements: 1,
  27. multipleStatements: 1,
  28. namedPlaceholders: 1,
  29. nestTables: 1,
  30. password: 1,
  31. passwordSha1: 1,
  32. pool: 1,
  33. port: 1,
  34. queryFormat: 1,
  35. rowsAsArray: 1,
  36. socketPath: 1,
  37. ssl: 1,
  38. stream: 1,
  39. stringifyObjects: 1,
  40. supportBigNumbers: 1,
  41. timezone: 1,
  42. trace: 1,
  43. typeCast: 1,
  44. uri: 1,
  45. user: 1,
  46. // These options are used for Pool
  47. connectionLimit: 1,
  48. Promise: 1,
  49. queueLimit: 1,
  50. waitForConnections: 1
  51. };
  52. class ConnectionConfig {
  53. constructor(options) {
  54. if (typeof options === 'string') {
  55. options = ConnectionConfig.parseUrl(options);
  56. } else if (options && options.uri) {
  57. const uriOptions = ConnectionConfig.parseUrl(options.uri);
  58. for (const key in uriOptions) {
  59. if (!Object.prototype.hasOwnProperty.call(uriOptions, key)) continue;
  60. if (options[key]) continue;
  61. options[key] = uriOptions[key];
  62. }
  63. }
  64. for (const key in options) {
  65. if (!Object.prototype.hasOwnProperty.call(options, key)) continue;
  66. if (validOptions[key] !== 1) {
  67. // REVIEW: Should this be emitted somehow?
  68. // eslint-disable-next-line no-console
  69. console.error(
  70. `Ignoring invalid configuration option passed to Connection: ${key}. This is currently a warning, but in future versions of MySQL2, an error will be thrown if you pass an invalid configuration option to a Connection`
  71. );
  72. }
  73. }
  74. this.isServer = options.isServer;
  75. this.stream = options.stream;
  76. this.host = options.host || 'localhost';
  77. this.port = options.port || 3306;
  78. this.localAddress = options.localAddress;
  79. this.socketPath = options.socketPath;
  80. this.user = options.user || undefined;
  81. this.password = options.password || undefined;
  82. this.passwordSha1 = options.passwordSha1 || undefined;
  83. this.database = options.database;
  84. this.connectTimeout = isNaN(options.connectTimeout)
  85. ? 10 * 1000
  86. : options.connectTimeout;
  87. this.insecureAuth = options.insecureAuth || false;
  88. this.supportBigNumbers = options.supportBigNumbers || false;
  89. this.bigNumberStrings = options.bigNumberStrings || false;
  90. this.decimalNumbers = options.decimalNumbers || false;
  91. this.dateStrings = options.dateStrings || false;
  92. this.debug = options.debug;
  93. this.trace = options.trace !== false;
  94. this.stringifyObjects = options.stringifyObjects || false;
  95. this.enableKeepAlive = !!options.enableKeepAlive;
  96. this.keepAliveInitialDelay = options.keepAliveInitialDelay || 0;
  97. if (
  98. options.timezone &&
  99. !/^(?:local|Z|[ +-]\d\d:\d\d)$/.test(options.timezone)
  100. ) {
  101. // strictly supports timezones specified by mysqljs/mysql:
  102. // https://github.com/mysqljs/mysql#user-content-connection-options
  103. // eslint-disable-next-line no-console
  104. console.error(
  105. `Ignoring invalid timezone passed to Connection: ${options.timezone}. This is currently a warning, but in future versions of MySQL2, an error will be thrown if you pass an invalid configuration option to a Connection`
  106. );
  107. // SqlStrings falls back to UTC on invalid timezone
  108. this.timezone = 'Z';
  109. } else {
  110. this.timezone = options.timezone || 'local';
  111. }
  112. this.queryFormat = options.queryFormat;
  113. this.pool = options.pool || undefined;
  114. this.ssl =
  115. typeof options.ssl === 'string'
  116. ? ConnectionConfig.getSSLProfile(options.ssl)
  117. : options.ssl || false;
  118. this.multipleStatements = options.multipleStatements || false;
  119. this.rowsAsArray = options.rowsAsArray || false;
  120. this.namedPlaceholders = options.namedPlaceholders || false;
  121. this.nestTables =
  122. options.nestTables === undefined ? undefined : options.nestTables;
  123. this.typeCast = options.typeCast === undefined ? true : options.typeCast;
  124. if (this.timezone[0] === ' ') {
  125. // "+" is a url encoded char for space so it
  126. // gets translated to space when giving a
  127. // connection string..
  128. this.timezone = `+${this.timezone.substr(1)}`;
  129. }
  130. if (this.ssl) {
  131. if (typeof this.ssl !== 'object') {
  132. throw new TypeError(
  133. `SSL profile must be an object, instead it's a ${typeof this.ssl}`
  134. );
  135. }
  136. // Default rejectUnauthorized to true
  137. this.ssl.rejectUnauthorized = this.ssl.rejectUnauthorized !== false;
  138. }
  139. this.maxPacketSize = 0;
  140. this.charsetNumber = options.charset
  141. ? ConnectionConfig.getCharsetNumber(options.charset)
  142. : options.charsetNumber || Charsets.UTF8MB4_UNICODE_CI;
  143. this.compress = options.compress || false;
  144. this.authPlugins = options.authPlugins;
  145. this.authSwitchHandler = options.authSwitchHandler;
  146. this.clientFlags = ConnectionConfig.mergeFlags(
  147. ConnectionConfig.getDefaultFlags(options),
  148. options.flags || ''
  149. );
  150. this.connectAttributes = options.connectAttributes;
  151. this.maxPreparedStatements = options.maxPreparedStatements || 16000;
  152. }
  153. static mergeFlags(default_flags, user_flags) {
  154. let flags = 0x0,
  155. i;
  156. if (!Array.isArray(user_flags)) {
  157. user_flags = String(user_flags || '')
  158. .toUpperCase()
  159. .split(/\s*,+\s*/);
  160. }
  161. // add default flags unless "blacklisted"
  162. for (i in default_flags) {
  163. if (user_flags.indexOf(`-${default_flags[i]}`) >= 0) {
  164. continue;
  165. }
  166. flags |= ClientConstants[default_flags[i]] || 0x0;
  167. }
  168. // add user flags unless already already added
  169. for (i in user_flags) {
  170. if (user_flags[i][0] === '-') {
  171. continue;
  172. }
  173. if (default_flags.indexOf(user_flags[i]) >= 0) {
  174. continue;
  175. }
  176. flags |= ClientConstants[user_flags[i]] || 0x0;
  177. }
  178. return flags;
  179. }
  180. static getDefaultFlags(options) {
  181. const defaultFlags = [
  182. 'LONG_PASSWORD',
  183. 'FOUND_ROWS',
  184. 'LONG_FLAG',
  185. 'CONNECT_WITH_DB',
  186. 'ODBC',
  187. 'LOCAL_FILES',
  188. 'IGNORE_SPACE',
  189. 'PROTOCOL_41',
  190. 'IGNORE_SIGPIPE',
  191. 'TRANSACTIONS',
  192. 'RESERVED',
  193. 'SECURE_CONNECTION',
  194. 'MULTI_RESULTS',
  195. 'TRANSACTIONS',
  196. 'SESSION_TRACK'
  197. ];
  198. if (options && options.multipleStatements) {
  199. defaultFlags.push('MULTI_STATEMENTS');
  200. }
  201. defaultFlags.push('PLUGIN_AUTH');
  202. defaultFlags.push('PLUGIN_AUTH_LENENC_CLIENT_DATA');
  203. if (options && options.connectAttributes) {
  204. defaultFlags.push('CONNECT_ATTRS');
  205. }
  206. return defaultFlags;
  207. }
  208. static getCharsetNumber(charset) {
  209. const num = Charsets[charset.toUpperCase()];
  210. if (num === undefined) {
  211. throw new TypeError(`Unknown charset '${charset}'`);
  212. }
  213. return num;
  214. }
  215. static getSSLProfile(name) {
  216. if (!SSLProfiles) {
  217. SSLProfiles = require('./constants/ssl_profiles.js');
  218. }
  219. const ssl = SSLProfiles[name];
  220. if (ssl === undefined) {
  221. throw new TypeError(`Unknown SSL profile '${name}'`);
  222. }
  223. return ssl;
  224. }
  225. static parseUrl(url) {
  226. url = urlParse(url, true);
  227. const options = {
  228. host: url.hostname,
  229. port: url.port,
  230. database: url.pathname.substr(1)
  231. };
  232. if (url.auth) {
  233. const auth = url.auth.split(':');
  234. options.user = auth[0];
  235. options.password = auth[1];
  236. }
  237. if (url.query) {
  238. for (const key in url.query) {
  239. const value = url.query[key];
  240. try {
  241. // Try to parse this as a JSON expression first
  242. options[key] = JSON.parse(value);
  243. } catch (err) {
  244. // Otherwise assume it is a plain string
  245. options[key] = value;
  246. }
  247. }
  248. }
  249. return options;
  250. }
  251. }
  252. module.exports = ConnectionConfig;