| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| 'use strict'; |
| |
| const util = require('util'); |
| const policies = require('./policies'); |
| const types = require('./types'); |
| const utils = require('./utils'); |
| const tracker = require('./tracker'); |
| const metrics = require('./metrics'); |
| const auth = require('./auth'); |
| |
| /** Core connections per host for protocol versions 1 and 2 */ |
| const coreConnectionsPerHostV2 = { |
| [types.distance.local]: 2, |
| [types.distance.remote]: 1, |
| [types.distance.ignored]: 0 |
| }; |
| |
| /** Core connections per host for protocol version 3 and above */ |
| const coreConnectionsPerHostV3 = { |
| [types.distance.local]: 1, |
| [types.distance.remote]: 1, |
| [types.distance.ignored]: 0 |
| }; |
| |
| /** Default maxRequestsPerConnection value for protocol v1 and v2 */ |
| const maxRequestsPerConnectionV2 = 128; |
| |
| /** Default maxRequestsPerConnection value for protocol v3+ */ |
| const maxRequestsPerConnectionV3 = 2048; |
| |
| const continuousPageUnitBytes = 'bytes'; |
| const continuousPageDefaultSize = 5000; |
| const continuousPageDefaultHighWaterMark = 10000; |
| |
| /** |
| * @returns {ClientOptions} |
| */ |
| function defaultOptions () { |
| return ({ |
| policies: { |
| addressResolution: policies.defaultAddressTranslator(), |
| loadBalancing: policies.defaultLoadBalancingPolicy(), |
| reconnection: policies.defaultReconnectionPolicy(), |
| retry: policies.defaultRetryPolicy(), |
| speculativeExecution: policies.defaultSpeculativeExecutionPolicy(), |
| timestampGeneration: policies.defaultTimestampGenerator() |
| }, |
| queryOptions: { |
| fetchSize: 5000, |
| prepare: false, |
| captureStackTrace: false |
| }, |
| protocolOptions: { |
| port: 9042, |
| maxSchemaAgreementWaitSeconds: 10, |
| maxVersion: 0, |
| noCompact: false |
| }, |
| pooling: { |
| heartBeatInterval: 30000, |
| warmup: true |
| }, |
| socketOptions: { |
| connectTimeout: 5000, |
| defunctReadTimeoutThreshold: 64, |
| keepAlive: true, |
| keepAliveDelay: 0, |
| readTimeout: 12000, |
| tcpNoDelay: true, |
| coalescingThreshold: 65536 |
| }, |
| authProvider: null, |
| requestTracker: null, |
| metrics: new metrics.DefaultMetrics(), |
| maxPrepared: 500, |
| refreshSchemaDelay: 1000, |
| isMetadataSyncEnabled: true, |
| prepareOnAllHosts: true, |
| rePrepareOnUp: true, |
| encoding: { |
| copyBuffer: true, |
| useUndefinedAsUnset: true |
| }, |
| monitorReporting: { |
| enabled: true |
| } |
| }); |
| } |
| |
| /** |
| * Extends and validates the user options |
| * @param {Object} [baseOptions] The source object instance that will be overridden |
| * @param {Object} userOptions |
| * @returns {Object} |
| */ |
| function extend(baseOptions, userOptions) { |
| if (arguments.length === 1) { |
| userOptions = arguments[0]; |
| baseOptions = {}; |
| } |
| const options = utils.deepExtend(baseOptions, defaultOptions(), userOptions); |
| |
| if (!options.cloud) { |
| if (!Array.isArray(options.contactPoints) || options.contactPoints.length === 0) { |
| throw new TypeError('Contacts points are not defined.'); |
| } |
| |
| for (let i = 0; i < options.contactPoints.length; i++) { |
| const hostName = options.contactPoints[i]; |
| if (!hostName) { |
| throw new TypeError(util.format('Contact point %s (%s) is not a valid host name, ' + |
| 'the following values are valid contact points: ipAddress, hostName or ipAddress:port', i, hostName)); |
| } |
| } |
| |
| options.sni = undefined; |
| } else { |
| validateCloudOptions(options); |
| } |
| |
| if (!options.logEmitter) { |
| options.logEmitter = function () {}; |
| } |
| if (!options.queryOptions) { |
| throw new TypeError('queryOptions not defined in options'); |
| } |
| |
| if (options.requestTracker !== null && !(options.requestTracker instanceof tracker.RequestTracker)) { |
| throw new TypeError('requestTracker must be an instance of RequestTracker'); |
| } |
| |
| if (!(options.metrics instanceof metrics.ClientMetrics)) { |
| throw new TypeError('metrics must be an instance of ClientMetrics'); |
| } |
| |
| validatePoliciesOptions(options.policies); |
| |
| validateProtocolOptions(options.protocolOptions); |
| |
| validateSocketOptions(options.socketOptions); |
| |
| validateAuthenticationOptions(options); |
| |
| options.encoding = options.encoding || {}; |
| |
| validateEncodingOptions(options.encoding); |
| |
| if (options.profiles && !Array.isArray(options.profiles)) { |
| throw new TypeError('profiles must be an Array of ExecutionProfile instances'); |
| } |
| |
| validateApplicationInfo(options); |
| |
| validateMonitorReporting(options); |
| |
| return options; |
| } |
| |
| /** |
| * Validates the options to connect to a cloud instance. |
| * @private |
| */ |
| function validateCloudOptions(options) { |
| const bundle = options.cloud.secureConnectBundle; |
| |
| // eslint-disable-next-line no-undef |
| if (!(typeof bundle === 'string' || (typeof URL !== 'undefined' && bundle instanceof URL))) { |
| throw new TypeError('secureConnectBundle in cloud options must be of type string'); |
| } |
| |
| if (options.contactPoints) { |
| throw new TypeError('Contact points can not be defined when cloud settings are provided'); |
| } |
| |
| if (options.sslOptions) { |
| throw new TypeError('SSL options can not be defined when cloud settings are provided'); |
| } |
| } |
| |
| /** |
| * Validates the policies from the client options. |
| * @param {ClientOptions.policies} policiesOptions |
| * @private |
| */ |
| function validatePoliciesOptions(policiesOptions) { |
| if (!policiesOptions) { |
| throw new TypeError('policies not defined in options'); |
| } |
| if (!(policiesOptions.loadBalancing instanceof policies.loadBalancing.LoadBalancingPolicy)) { |
| throw new TypeError('Load balancing policy must be an instance of LoadBalancingPolicy'); |
| } |
| if (!(policiesOptions.reconnection instanceof policies.reconnection.ReconnectionPolicy)) { |
| throw new TypeError('Reconnection policy must be an instance of ReconnectionPolicy'); |
| } |
| if (!(policiesOptions.retry instanceof policies.retry.RetryPolicy)) { |
| throw new TypeError('Retry policy must be an instance of RetryPolicy'); |
| } |
| if (!(policiesOptions.addressResolution instanceof policies.addressResolution.AddressTranslator)) { |
| throw new TypeError('Address resolution policy must be an instance of AddressTranslator'); |
| } |
| if (policiesOptions.timestampGeneration !== null && |
| !(policiesOptions.timestampGeneration instanceof policies.timestampGeneration.TimestampGenerator)) { |
| throw new TypeError('Timestamp generation policy must be an instance of TimestampGenerator'); |
| } |
| } |
| |
| /** |
| * Validates the protocol options. |
| * @param {ClientOptions.protocolOptions} protocolOptions |
| * @private |
| */ |
| function validateProtocolOptions(protocolOptions) { |
| if (!protocolOptions) { |
| throw new TypeError('protocolOptions not defined in options'); |
| } |
| const version = protocolOptions.maxVersion; |
| if (version && (typeof version !== 'number' || !types.protocolVersion.isSupported(version))) { |
| throw new TypeError(util.format('protocolOptions.maxVersion provided (%s) is invalid', version)); |
| } |
| } |
| |
| /** |
| * Validates the socket options. |
| * @param {ClientOptions.socketOptions} socketOptions |
| * @private |
| */ |
| function validateSocketOptions(socketOptions) { |
| if (!socketOptions) { |
| throw new TypeError('socketOptions not defined in options'); |
| } |
| if (typeof socketOptions.readTimeout !== 'number') { |
| throw new TypeError('socketOptions.readTimeout must be a Number'); |
| } |
| if (typeof socketOptions.coalescingThreshold !== 'number' || socketOptions.coalescingThreshold <= 0) { |
| throw new TypeError('socketOptions.coalescingThreshold must be a positive Number'); |
| } |
| } |
| |
| /** |
| * Validates authentication provider and credentials. |
| * @param {ClientOptions} options |
| * @private |
| */ |
| function validateAuthenticationOptions(options) { |
| if (!options.authProvider) { |
| const credentials = options.credentials; |
| if (credentials) { |
| if (typeof credentials.username !== 'string' || typeof credentials.password !== 'string') { |
| throw new TypeError('credentials username and password must be a string'); |
| } |
| |
| options.authProvider = new auth.PlainTextAuthProvider(credentials.username, credentials.password); |
| } else { |
| options.authProvider = new auth.NoAuthProvider(); |
| } |
| } else if (!(options.authProvider instanceof auth.AuthProvider)) { |
| throw new TypeError('options.authProvider must be an instance of AuthProvider'); |
| } |
| } |
| |
| /** |
| * Validates the encoding options. |
| * @param {ClientOptions.encoding} encodingOptions |
| * @private |
| */ |
| function validateEncodingOptions(encodingOptions) { |
| if (encodingOptions.map) { |
| const mapConstructor = encodingOptions.map; |
| if (typeof mapConstructor !== 'function' || |
| typeof mapConstructor.prototype.forEach !== 'function' || |
| typeof mapConstructor.prototype.set !== 'function') { |
| throw new TypeError('Map constructor not valid'); |
| } |
| } |
| |
| if (encodingOptions.set) { |
| const setConstructor = encodingOptions.set; |
| if (typeof setConstructor !== 'function' || |
| typeof setConstructor.prototype.forEach !== 'function' || |
| typeof setConstructor.prototype.add !== 'function') { |
| throw new TypeError('Set constructor not valid'); |
| } |
| } |
| |
| if ((encodingOptions.useBigIntAsLong || encodingOptions.useBigIntAsVarint) && typeof BigInt === 'undefined') { |
| throw new TypeError('BigInt is not supported by the JavaScript engine'); |
| } |
| } |
| |
| function validateApplicationInfo(options) { |
| function validateString(key) { |
| const str = options[key]; |
| |
| if (str !== null && str !== undefined && typeof str !== 'string') { |
| throw new TypeError(`${key} should be a String`); |
| } |
| } |
| |
| validateString('applicationName'); |
| validateString('applicationVersion'); |
| |
| if (options.id !== null && options.id !== undefined && !(options.id instanceof types.Uuid)) { |
| throw new TypeError('Client id must be a Uuid'); |
| } |
| } |
| |
| function validateMonitorReporting(options) { |
| const o = options.monitorReporting; |
| if (o === null || typeof o !== 'object') { |
| throw new TypeError(`Monitor reporting must be an object, obtained: ${o}`); |
| } |
| } |
| |
| /** |
| * Sets the default options that depend on the protocol version and other metadata. |
| * @param {Client} client |
| */ |
| function setMetadataDependent(client) { |
| const version = client.controlConnection.protocolVersion; |
| let coreConnectionsPerHost = coreConnectionsPerHostV3; |
| let maxRequestsPerConnection = maxRequestsPerConnectionV3; |
| |
| if (!types.protocolVersion.uses2BytesStreamIds(version)) { |
| coreConnectionsPerHost = coreConnectionsPerHostV2; |
| maxRequestsPerConnection = maxRequestsPerConnectionV2; |
| } |
| |
| if (client.options.queryOptions.consistency === undefined) { |
| client.options.queryOptions.consistency = |
| client.metadata.isDbaas() ? types.consistencies.localQuorum : types.consistencies.localOne; |
| } |
| |
| client.options.pooling = utils.deepExtend( |
| {}, { coreConnectionsPerHost, maxRequestsPerConnection }, client.options.pooling); |
| } |
| |
| exports.extend = extend; |
| exports.defaultOptions = defaultOptions; |
| exports.coreConnectionsPerHostV2 = coreConnectionsPerHostV2; |
| exports.coreConnectionsPerHostV3 = coreConnectionsPerHostV3; |
| exports.maxRequestsPerConnectionV2 = maxRequestsPerConnectionV2; |
| exports.maxRequestsPerConnectionV3 = maxRequestsPerConnectionV3; |
| exports.setMetadataDependent = setMetadataDependent; |
| exports.continuousPageUnitBytes = continuousPageUnitBytes; |
| exports.continuousPageDefaultSize = continuousPageDefaultSize; |
| exports.continuousPageDefaultHighWaterMark = continuousPageDefaultHighWaterMark; |