"use strict"; /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch B.V. 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. */ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1; Object.defineProperty(exports, "__esModule", { value: true }); exports.lowerCaseHeaders = exports.generateRequestId = void 0; const tslib_1 = require("tslib"); const debug_1 = tslib_1.__importDefault(require("debug")); const os_1 = tslib_1.__importDefault(require("os")); const zlib_1 = tslib_1.__importDefault(require("zlib")); const buffer_1 = tslib_1.__importDefault(require("buffer")); const util_1 = require("util"); const ms_1 = tslib_1.__importDefault(require("ms")); const errors_1 = require("./errors"); const Diagnostic_1 = tslib_1.__importDefault(require("./Diagnostic")); const Serializer_1 = tslib_1.__importDefault(require("./Serializer")); const symbols_1 = require("./symbols"); const { version: clientVersion } = require('../package.json'); // eslint-disable-line const debug = (0, debug_1.default)('elasticsearch'); const gzip = (0, util_1.promisify)(zlib_1.default.gzip); const unzip = (0, util_1.promisify)(zlib_1.default.unzip); const { createGzip } = zlib_1.default; const userAgent = `elastic-transport-js/${clientVersion} (${os_1.default.platform()} ${os_1.default.release()}-${os_1.default.arch()}; Node.js ${process.version})`; // eslint-disable-line class Transport { constructor(opts) { var _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22; Object.defineProperty(this, _a, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _b, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _c, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _d, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _e, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _f, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _g, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _h, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _j, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _k, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _l, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _m, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _o, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _p, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _q, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _r, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _s, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _t, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _u, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _v, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _w, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _x, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _y, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _z, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _0, { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, _1, { enumerable: true, configurable: true, writable: true, value: void 0 }); if (opts.connectionPool == null) { throw new errors_1.ConfigurationError('The Connection Pool option is not defined'); } if (typeof opts.maxRetries === 'number' && opts.maxRetries < 0 && Number.isInteger(opts.maxRetries)) { throw new errors_1.ConfigurationError('The maxRetries option must be a positive integer or zero'); } if (opts.sniffInterval === true || (typeof opts.sniffInterval === 'number' && opts.sniffInterval < 0 && Number.isInteger(opts.sniffInterval))) { throw new errors_1.ConfigurationError('The sniffInterval option must be false or a positive integer'); } if (opts.maxResponseSize != null && opts.maxResponseSize > buffer_1.default.constants.MAX_STRING_LENGTH) { throw new errors_1.ConfigurationError(`The maxResponseSize cannot be bigger than ${buffer_1.default.constants.MAX_STRING_LENGTH}`); } if (opts.maxCompressedResponseSize != null && opts.maxCompressedResponseSize > buffer_1.default.constants.MAX_LENGTH) { throw new errors_1.ConfigurationError(`The maxCompressedResponseSize cannot be bigger than ${buffer_1.default.constants.MAX_LENGTH}`); } this[symbols_1.kNodeFilter] = (_2 = opts.nodeFilter) !== null && _2 !== void 0 ? _2 : defaultNodeFilter; this[symbols_1.kNodeSelector] = (_3 = opts.nodeSelector) !== null && _3 !== void 0 ? _3 : roundRobinSelector(); this[symbols_1.kHeaders] = Object.assign({}, { 'user-agent': userAgent }, opts.compression === true ? { 'accept-encoding': 'gzip,deflate' } : null, lowerCaseHeaders(opts.headers)); this[symbols_1.kDiagnostic] = (_4 = opts.diagnostic) !== null && _4 !== void 0 ? _4 : new Diagnostic_1.default(); this[symbols_1.kConnectionPool] = opts.connectionPool; this[symbols_1.kSerializer] = (_5 = opts.serializer) !== null && _5 !== void 0 ? _5 : new Serializer_1.default(); this[symbols_1.kContext] = (_6 = opts.context) !== null && _6 !== void 0 ? _6 : null; this[symbols_1.kGenerateRequestId] = (_7 = opts.generateRequestId) !== null && _7 !== void 0 ? _7 : generateRequestId(); this[symbols_1.kOpaqueIdPrefix] = (_8 = opts.opaqueIdPrefix) !== null && _8 !== void 0 ? _8 : null; this[symbols_1.kName] = (_9 = opts.name) !== null && _9 !== void 0 ? _9 : 'elastic-transport-js'; this[symbols_1.kMaxRetries] = typeof opts.maxRetries === 'number' ? opts.maxRetries : 3; this[symbols_1.kCompression] = opts.compression === true; this[symbols_1.kRequestTimeout] = opts.requestTimeout != null ? toMs(opts.requestTimeout) : 30000; this[symbols_1.kSniffInterval] = (_10 = opts.sniffInterval) !== null && _10 !== void 0 ? _10 : false; this[symbols_1.kSniffEnabled] = typeof this[symbols_1.kSniffInterval] === 'number'; this[symbols_1.kNextSniff] = this[symbols_1.kSniffEnabled] ? (Date.now() + this[symbols_1.kSniffInterval]) : 0; this[symbols_1.kIsSniffing] = false; this[symbols_1.kSniffOnConnectionFault] = (_11 = opts.sniffOnConnectionFault) !== null && _11 !== void 0 ? _11 : false; this[symbols_1.kSniffEndpoint] = (_12 = opts.sniffEndpoint) !== null && _12 !== void 0 ? _12 : null; this[symbols_1.kProductCheck] = (_13 = opts.productCheck) !== null && _13 !== void 0 ? _13 : null; this[symbols_1.kMaxResponseSize] = (_14 = opts.maxResponseSize) !== null && _14 !== void 0 ? _14 : buffer_1.default.constants.MAX_STRING_LENGTH; this[symbols_1.kMaxCompressedResponseSize] = (_15 = opts.maxCompressedResponseSize) !== null && _15 !== void 0 ? _15 : buffer_1.default.constants.MAX_LENGTH; this[symbols_1.kJsonContentType] = (_17 = (_16 = opts.vendoredHeaders) === null || _16 === void 0 ? void 0 : _16.jsonContentType) !== null && _17 !== void 0 ? _17 : 'application/json'; this[symbols_1.kNdjsonContentType] = (_19 = (_18 = opts.vendoredHeaders) === null || _18 === void 0 ? void 0 : _18.ndjsonContentType) !== null && _19 !== void 0 ? _19 : 'application/x-ndjson'; this[symbols_1.kAcceptHeader] = (_21 = (_20 = opts.vendoredHeaders) === null || _20 === void 0 ? void 0 : _20.accept) !== null && _21 !== void 0 ? _21 : 'application/json, text/plain'; this[symbols_1.kRedaction] = (_22 = opts.redaction) !== null && _22 !== void 0 ? _22 : { type: 'replace', additionalKeys: [] }; if (opts.sniffOnStart === true) { this.sniff({ reason: Transport.sniffReasons.SNIFF_ON_START, requestId: this[symbols_1.kGenerateRequestId]({ method: 'GET', path: this[symbols_1.kSniffEndpoint] }, { context: this[symbols_1.kContext] }), context: this[symbols_1.kContext] }); } } get connectionPool() { return this[symbols_1.kConnectionPool]; } get sniffEnabled() { return this[symbols_1.kSniffEnabled]; } get nextSniff() { return this[symbols_1.kNextSniff]; } get sniffEndpoint() { return this[symbols_1.kSniffEndpoint]; } get isSniffing() { return this[symbols_1.kIsSniffing]; } set isSniffing(val) { if (typeof val !== 'boolean') { throw new errors_1.ConfigurationError(`isSniffing must be a boolean, instead got ${typeof val}`); } this[symbols_1.kIsSniffing] = val; } get diagnostic() { return this[symbols_1.kDiagnostic]; } async request(params, options = {}) { var _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17; const connectionParams = { method: params.method, path: params.path }; const meta = { context: null, request: { params: connectionParams, options: options, id: (_2 = options.id) !== null && _2 !== void 0 ? _2 : this[symbols_1.kGenerateRequestId](params, options) }, name: this[symbols_1.kName], connection: null, attempts: 0, aborted: false }; const returnMeta = (_3 = options.meta) !== null && _3 !== void 0 ? _3 : false; if (this[symbols_1.kContext] != null && options.context != null) { meta.context = Object.assign({}, this[symbols_1.kContext], options.context); } else if (this[symbols_1.kContext] !== null) { meta.context = this[symbols_1.kContext]; } else if (options.context != null) { meta.context = options.context; } const result = { // the default body value can't be `null` // as it's a valid JSON value body: undefined, statusCode: 0, headers: {}, meta, get warnings() { var _2; if (((_2 = this.headers) === null || _2 === void 0 ? void 0 : _2.warning) == null) { return null; } return this.headers.warning .split(/(?!\B"[^"]*),(?![^"]*"\B)/) .filter((warning) => warning.match(/^\d\d\d Elasticsearch-/)); } }; // We should not retry if we are sending a stream body, because we should store in memory // a copy of the stream to be able to send it again, but since we don't know in advance // the size of the stream, we risk to take too much memory. // Furthermore, copying every time the stream is very a expensive operation. const maxRetries = isStream((_4 = params.body) !== null && _4 !== void 0 ? _4 : params.bulkBody) ? 0 : (typeof options.maxRetries === 'number' ? options.maxRetries : this[symbols_1.kMaxRetries]); const compression = typeof options.compression === 'boolean' ? options.compression : this[symbols_1.kCompression]; const signal = options.signal; const maxResponseSize = (_5 = options.maxResponseSize) !== null && _5 !== void 0 ? _5 : this[symbols_1.kMaxResponseSize]; const maxCompressedResponseSize = (_6 = options.maxCompressedResponseSize) !== null && _6 !== void 0 ? _6 : this[symbols_1.kMaxCompressedResponseSize]; const errorOptions = { redaction: typeof options.redaction === 'object' ? options.redaction : this[symbols_1.kRedaction] }; this[symbols_1.kDiagnostic].emit('serialization', null, result); const headers = Object.assign({}, this[symbols_1.kHeaders], lowerCaseHeaders(options.headers)); if (options.opaqueId !== undefined) { headers['x-opaque-id'] = typeof this[symbols_1.kOpaqueIdPrefix] === 'string' ? this[symbols_1.kOpaqueIdPrefix] + options.opaqueId // eslint-disable-line : options.opaqueId; } // handle json body if (params.body != null) { if (shouldSerialize(params.body)) { try { connectionParams.body = this[symbols_1.kSerializer].serialize(params.body); } catch (err) { this[symbols_1.kDiagnostic].emit('request', err, result); throw err; } headers['content-type'] = (_7 = headers['content-type']) !== null && _7 !== void 0 ? _7 : this[symbols_1.kJsonContentType]; headers.accept = (_8 = headers.accept) !== null && _8 !== void 0 ? _8 : this[symbols_1.kJsonContentType]; } else { if (params.body !== '') { headers['content-type'] = (_9 = headers['content-type']) !== null && _9 !== void 0 ? _9 : 'text/plain'; headers.accept = (_10 = headers.accept) !== null && _10 !== void 0 ? _10 : this[symbols_1.kAcceptHeader]; } connectionParams.body = params.body; } // handle ndjson body } else if (params.bulkBody != null) { if (shouldSerialize(params.bulkBody)) { try { connectionParams.body = this[symbols_1.kSerializer].ndserialize(params.bulkBody); } catch (err) { this[symbols_1.kDiagnostic].emit('request', err, result); throw err; } } else { connectionParams.body = params.bulkBody; } if (connectionParams.body !== '') { headers['content-type'] = (_11 = headers['content-type']) !== null && _11 !== void 0 ? _11 : this[symbols_1.kNdjsonContentType]; headers.accept = (_12 = headers.accept) !== null && _12 !== void 0 ? _12 : this[symbols_1.kJsonContentType]; } } // serializes the querystring if (options.querystring == null) { connectionParams.querystring = this[symbols_1.kSerializer].qserialize(params.querystring); } else { connectionParams.querystring = this[symbols_1.kSerializer].qserialize(Object.assign({}, params.querystring, options.querystring)); } // handle compression if (connectionParams.body !== '' && connectionParams.body != null) { if (isStream(connectionParams.body)) { if (compression) { headers['content-encoding'] = 'gzip'; connectionParams.body = connectionParams.body.pipe(createGzip()); } } else if (compression) { try { connectionParams.body = await gzip(connectionParams.body); } catch (err) { /* istanbul ignore next */ this[symbols_1.kDiagnostic].emit('request', err, result); /* istanbul ignore next */ throw err; } headers['content-encoding'] = 'gzip'; headers['content-length'] = '' + Buffer.byteLength(connectionParams.body); // eslint-disable-line } else { headers['content-length'] = '' + Buffer.byteLength(connectionParams.body); // eslint-disable-line } } headers.accept = (_13 = headers.accept) !== null && _13 !== void 0 ? _13 : this[symbols_1.kAcceptHeader]; connectionParams.headers = headers; while (meta.attempts <= maxRetries) { try { if (signal === null || signal === void 0 ? void 0 : signal.aborted) { // eslint-disable-line throw new errors_1.RequestAbortedError('Request has been aborted by the user', result, errorOptions); } meta.connection = this.getConnection({ requestId: meta.request.id, context: meta.context }); if (meta.connection === null) { throw new errors_1.NoLivingConnectionsError('There are no living connections', result, errorOptions); } this[symbols_1.kDiagnostic].emit('request', null, result); // perform the actual http request let { statusCode, headers, body } = await meta.connection.request(connectionParams, { requestId: meta.request.id, name: this[symbols_1.kName], context: meta.context, maxResponseSize, maxCompressedResponseSize, signal, timeout: toMs(options.requestTimeout != null ? options.requestTimeout : this[symbols_1.kRequestTimeout]), ...(options.asStream === true ? { asStream: true } : null) }); result.statusCode = statusCode; result.headers = headers; if (this[symbols_1.kProductCheck] != null && headers['x-elastic-product'] !== this[symbols_1.kProductCheck] && statusCode >= 200 && statusCode < 300) { /* eslint-disable @typescript-eslint/prefer-ts-expect-error */ // @ts-ignore throw new errors_1.ProductNotSupportedError(this[symbols_1.kProductCheck], result, errorOptions); /* eslint-enable @typescript-eslint/prefer-ts-expect-error */ } if (options.asStream === true) { result.body = body; this[symbols_1.kDiagnostic].emit('response', null, result); return returnMeta ? result : body; } const contentEncoding = ((_14 = headers['content-encoding']) !== null && _14 !== void 0 ? _14 : '').toLowerCase(); if (contentEncoding.includes('gzip') || contentEncoding.includes('deflate')) { body = await unzip(body); } const isVectorTile = ((_15 = headers['content-type']) !== null && _15 !== void 0 ? _15 : '').includes('application/vnd.mapbox-vector-tile'); if (Buffer.isBuffer(body) && !isVectorTile) { body = body.toString(); } const isHead = params.method === 'HEAD'; // we should attempt the payload deserialization only if: // - a `content-type` is defined and is equal to `application/json` // - the request is not a HEAD request // - the payload is not an empty string if (headers['content-type'] !== undefined && (((_16 = headers['content-type']) === null || _16 === void 0 ? void 0 : _16.includes('application/json')) || ((_17 = headers['content-type']) === null || _17 === void 0 ? void 0 : _17.includes('application/vnd.elasticsearch+json'))) && !isHead && body !== '') { // eslint-disable-line result.body = this[symbols_1.kSerializer].deserialize(body); } else { // cast to boolean if the request method was HEAD and there was no error result.body = isHead && statusCode < 400 ? true : body; } // we should ignore the statusCode if the user has configured the `ignore` field with // the statusCode we just got or if the request method is HEAD and the statusCode is 404 const ignoreStatusCode = (Array.isArray(options.ignore) && options.ignore.includes(statusCode)) || (isHead && statusCode === 404); if (!ignoreStatusCode && (statusCode === 502 || statusCode === 503 || statusCode === 504)) { // if the statusCode is 502/3/4 we should run our retry strategy // and mark the connection as dead this[symbols_1.kConnectionPool].markDead(meta.connection); // retry logic if (meta.attempts < maxRetries) { meta.attempts++; debug(`Retrying request, there are still ${maxRetries - meta.attempts} attempts`, params); continue; } } else { // everything has worked as expected, let's mark // the connection as alive (or confirm it) this[symbols_1.kConnectionPool].markAlive(meta.connection); } if (!ignoreStatusCode && statusCode >= 400) { throw new errors_1.ResponseError(result, errorOptions); } else { // cast to boolean if the request method was HEAD if (isHead && statusCode === 404) { result.body = false; } this[symbols_1.kDiagnostic].emit('response', null, result); return returnMeta ? result : result.body; } } catch (error) { switch (error.name) { // should not retry case 'ProductNotSupportedError': case 'NoLivingConnectionsError': case 'DeserializationError': case 'ResponseError': this[symbols_1.kDiagnostic].emit('response', error, result); throw error; case 'RequestAbortedError': { meta.aborted = true; // Wrap the error to get a clean stack trace const wrappedError = new errors_1.RequestAbortedError(error.message, result, errorOptions); this[symbols_1.kDiagnostic].emit('response', wrappedError, result); throw wrappedError; } // should retry case 'TimeoutError': case 'ConnectionError': { // if there is an error in the connection // let's mark the connection as dead this[symbols_1.kConnectionPool].markDead(meta.connection); if (this[symbols_1.kSniffOnConnectionFault]) { this.sniff({ reason: Transport.sniffReasons.SNIFF_ON_CONNECTION_FAULT, requestId: meta.request.id, context: meta.context }); } // retry logic if (meta.attempts < maxRetries) { meta.attempts++; debug(`Retrying request, there are still ${maxRetries - meta.attempts} attempts`, params); continue; } // Wrap the error to get a clean stack trace const wrappedError = error.name === 'TimeoutError' ? new errors_1.TimeoutError(error.message, result, errorOptions) : new errors_1.ConnectionError(error.message, result, errorOptions); this[symbols_1.kDiagnostic].emit('response', wrappedError, result); throw wrappedError; } // edge cases, such as bad compression default: this[symbols_1.kDiagnostic].emit('response', error, result); throw error; } } } return returnMeta ? result : result.body; } getConnection(opts) { const now = Date.now(); if (this[symbols_1.kSniffEnabled] && now > this[symbols_1.kNextSniff]) { this[symbols_1.kNextSniff] = now + this[symbols_1.kSniffInterval]; this.sniff({ reason: Transport.sniffReasons.SNIFF_INTERVAL, requestId: opts.requestId, context: opts.context }); } return this[symbols_1.kConnectionPool].getConnection({ filter: this[symbols_1.kNodeFilter], selector: this[symbols_1.kNodeSelector], requestId: opts.requestId, name: this[symbols_1.kName], context: opts.context, now }); } /* istanbul ignore next */ sniff(opts) { } } exports.default = Transport; _a = symbols_1.kNodeFilter, _b = symbols_1.kNodeSelector, _c = symbols_1.kHeaders, _d = symbols_1.kDiagnostic, _e = symbols_1.kConnectionPool, _f = symbols_1.kSerializer, _g = symbols_1.kContext, _h = symbols_1.kGenerateRequestId, _j = symbols_1.kOpaqueIdPrefix, _k = symbols_1.kName, _l = symbols_1.kMaxRetries, _m = symbols_1.kCompression, _o = symbols_1.kRequestTimeout, _p = symbols_1.kSniffEnabled, _q = symbols_1.kNextSniff, _r = symbols_1.kIsSniffing, _s = symbols_1.kSniffInterval, _t = symbols_1.kSniffOnConnectionFault, _u = symbols_1.kSniffEndpoint, _v = symbols_1.kProductCheck, _w = symbols_1.kMaxResponseSize, _x = symbols_1.kMaxCompressedResponseSize, _y = symbols_1.kJsonContentType, _z = symbols_1.kNdjsonContentType, _0 = symbols_1.kAcceptHeader, _1 = symbols_1.kRedaction; Object.defineProperty(Transport, "sniffReasons", { enumerable: true, configurable: true, writable: true, value: { SNIFF_ON_START: 'sniff-on-start', SNIFF_INTERVAL: 'sniff-interval', SNIFF_ON_CONNECTION_FAULT: 'sniff-on-connection-fault', DEFAULT: 'default' } }); function toMs(time) { if (typeof time === 'string') { return (0, ms_1.default)(time); } return time; } function shouldSerialize(obj) { return typeof obj !== 'string' && typeof obj.pipe !== 'function' && !Buffer.isBuffer(obj); } function isStream(obj) { return obj != null && typeof obj.pipe === 'function'; } function defaultNodeFilter(node) { return true; } function roundRobinSelector() { let current = -1; return function _roundRobinSelector(connections) { if (++current >= connections.length) { current = 0; } return connections[current]; }; } function generateRequestId() { const maxInt = 2147483647; let nextReqId = 0; return function genReqId(params, options) { return (nextReqId = (nextReqId + 1) & maxInt); }; } exports.generateRequestId = generateRequestId; function lowerCaseHeaders(oldHeaders) { if (oldHeaders == null) return null; const newHeaders = {}; for (const header in oldHeaders) { // @ts-expect-error newHeaders[header.toLowerCase()] = oldHeaders[header]; } return newHeaders; } exports.lowerCaseHeaders = lowerCaseHeaders; //# sourceMappingURL=Transport.js.map