@elastic/elasticsearch 7.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/.dockerignore +5 -0
  2. package/LICENSE +202 -0
  3. package/README.md +232 -0
  4. package/api/api/async_search.js +141 -0
  5. package/api/api/autoscaling.js +147 -0
  6. package/api/api/bulk.js +70 -0
  7. package/api/api/cat.js +648 -0
  8. package/api/api/ccr.js +403 -0
  9. package/api/api/clear_scroll.js +55 -0
  10. package/api/api/close_point_in_time.js +50 -0
  11. package/api/api/cluster.js +420 -0
  12. package/api/api/count.js +64 -0
  13. package/api/api/create.js +69 -0
  14. package/api/api/dangling_indices.js +115 -0
  15. package/api/api/delete.js +65 -0
  16. package/api/api/delete_by_query.js +71 -0
  17. package/api/api/delete_by_query_rethrottle.js +60 -0
  18. package/api/api/delete_script.js +56 -0
  19. package/api/api/enrich.js +173 -0
  20. package/api/api/eql.js +150 -0
  21. package/api/api/exists.js +65 -0
  22. package/api/api/exists_source.js +74 -0
  23. package/api/api/explain.js +65 -0
  24. package/api/api/features.js +81 -0
  25. package/api/api/field_caps.js +55 -0
  26. package/api/api/fleet.js +65 -0
  27. package/api/api/get.js +65 -0
  28. package/api/api/get_script.js +56 -0
  29. package/api/api/get_script_context.js +50 -0
  30. package/api/api/get_script_languages.js +50 -0
  31. package/api/api/get_source.js +65 -0
  32. package/api/api/graph.js +72 -0
  33. package/api/api/ilm.js +317 -0
  34. package/api/api/index.js +71 -0
  35. package/api/api/indices.js +1753 -0
  36. package/api/api/info.js +50 -0
  37. package/api/api/ingest.js +200 -0
  38. package/api/api/license.js +188 -0
  39. package/api/api/logstash.js +125 -0
  40. package/api/api/mget.js +70 -0
  41. package/api/api/migration.js +60 -0
  42. package/api/api/ml.js +2010 -0
  43. package/api/api/monitoring.js +66 -0
  44. package/api/api/msearch.js +70 -0
  45. package/api/api/msearch_template.js +70 -0
  46. package/api/api/mtermvectors.js +64 -0
  47. package/api/api/nodes.js +268 -0
  48. package/api/api/open_point_in_time.js +56 -0
  49. package/api/api/ping.js +50 -0
  50. package/api/api/put_script.js +71 -0
  51. package/api/api/rank_eval.js +61 -0
  52. package/api/api/reindex.js +56 -0
  53. package/api/api/reindex_rethrottle.js +60 -0
  54. package/api/api/render_search_template.js +55 -0
  55. package/api/api/rollup.js +319 -0
  56. package/api/api/scripts_painless_execute.js +50 -0
  57. package/api/api/scroll.js +55 -0
  58. package/api/api/search.js +64 -0
  59. package/api/api/search_mvt.js +87 -0
  60. package/api/api/search_shards.js +55 -0
  61. package/api/api/search_template.js +70 -0
  62. package/api/api/searchable_snapshots.js +186 -0
  63. package/api/api/security.js +1261 -0
  64. package/api/api/shutdown.js +124 -0
  65. package/api/api/slm.js +256 -0
  66. package/api/api/snapshot.js +439 -0
  67. package/api/api/sql.js +203 -0
  68. package/api/api/ssl.js +55 -0
  69. package/api/api/tasks.js +108 -0
  70. package/api/api/terms_enum.js +56 -0
  71. package/api/api/termvectors.js +67 -0
  72. package/api/api/text_structure.js +65 -0
  73. package/api/api/transform.js +268 -0
  74. package/api/api/update.js +69 -0
  75. package/api/api/update_by_query.js +67 -0
  76. package/api/api/update_by_query_rethrottle.js +60 -0
  77. package/api/api/watcher.js +333 -0
  78. package/api/api/xpack.js +76 -0
  79. package/api/index.js +508 -0
  80. package/api/new.d.ts +1585 -0
  81. package/api/requestParams.d.ts +2920 -0
  82. package/api/types.d.ts +15420 -0
  83. package/api/utils.js +58 -0
  84. package/codecov.yml +14 -0
  85. package/index.d.ts +2991 -0
  86. package/index.js +349 -0
  87. package/index.mjs +29 -0
  88. package/lib/Connection.d.ts +99 -0
  89. package/lib/Connection.js +392 -0
  90. package/lib/Helpers.d.ts +124 -0
  91. package/lib/Helpers.js +770 -0
  92. package/lib/Serializer.d.ts +30 -0
  93. package/lib/Serializer.js +94 -0
  94. package/lib/Transport.d.ts +162 -0
  95. package/lib/Transport.js +689 -0
  96. package/lib/errors.d.ts +90 -0
  97. package/lib/errors.js +159 -0
  98. package/lib/pool/BaseConnectionPool.js +262 -0
  99. package/lib/pool/CloudConnectionPool.js +64 -0
  100. package/lib/pool/ConnectionPool.js +246 -0
  101. package/lib/pool/index.d.ts +220 -0
  102. package/lib/pool/index.js +30 -0
  103. package/package.json +106 -0
@@ -0,0 +1,689 @@
1
+ /*
2
+ * Licensed to Elasticsearch B.V. under one or more contributor
3
+ * license agreements. See the NOTICE file distributed with
4
+ * this work for additional information regarding copyright
5
+ * ownership. Elasticsearch B.V. licenses this file to you under
6
+ * the Apache License, Version 2.0 (the "License"); you may
7
+ * not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing,
13
+ * software distributed under the License is distributed on an
14
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ * KIND, either express or implied. See the License for the
16
+ * specific language governing permissions and limitations
17
+ * under the License.
18
+ */
19
+
20
+ 'use strict'
21
+
22
+ const debug = require('debug')('elasticsearch')
23
+ const os = require('os')
24
+ const { gzip, unzip, createGzip } = require('zlib')
25
+ const buffer = require('buffer')
26
+ const ms = require('ms')
27
+ const { EventEmitter } = require('events')
28
+ const {
29
+ ConnectionError,
30
+ RequestAbortedError,
31
+ NoLivingConnectionsError,
32
+ ResponseError,
33
+ ConfigurationError,
34
+ ProductNotSupportedError
35
+ } = require('./errors')
36
+
37
+ const noop = () => {}
38
+
39
+ const clientVersion = require('../package.json').version
40
+ const userAgent = `elasticsearch-js/${clientVersion} (${os.platform()} ${os.release()}-${os.arch()}; Node.js ${process.version})`
41
+ const MAX_BUFFER_LENGTH = buffer.constants.MAX_LENGTH
42
+ const MAX_STRING_LENGTH = buffer.constants.MAX_STRING_LENGTH
43
+ const kProductCheck = Symbol('product check')
44
+ const kApiVersioning = Symbol('api versioning')
45
+ const kEventEmitter = Symbol('event emitter')
46
+
47
+ class Transport {
48
+ constructor (opts) {
49
+ if (typeof opts.compression === 'string' && opts.compression !== 'gzip') {
50
+ throw new ConfigurationError(`Invalid compression: '${opts.compression}'`)
51
+ }
52
+
53
+ this.emit = opts.emit
54
+ this.connectionPool = opts.connectionPool
55
+ this.serializer = opts.serializer
56
+ this.maxRetries = opts.maxRetries
57
+ this.requestTimeout = toMs(opts.requestTimeout)
58
+ this.suggestCompression = opts.suggestCompression === true
59
+ this.compression = opts.compression || false
60
+ this.context = opts.context || null
61
+ this.headers = Object.assign({},
62
+ { 'user-agent': userAgent },
63
+ opts.suggestCompression === true ? { 'accept-encoding': 'gzip,deflate' } : null,
64
+ lowerCaseHeaders(opts.headers)
65
+ )
66
+ this.sniffInterval = opts.sniffInterval
67
+ this.sniffOnConnectionFault = opts.sniffOnConnectionFault
68
+ this.sniffEndpoint = opts.sniffEndpoint
69
+ this.generateRequestId = opts.generateRequestId || generateRequestId()
70
+ this.name = opts.name
71
+ this.opaqueIdPrefix = opts.opaqueIdPrefix
72
+ this[kProductCheck] = 0 // 0 = to be checked, 1 = checking, 2 = checked-ok, 3 checked-notok, 4 checked-nodefault
73
+ this[kApiVersioning] = process.env.ELASTIC_CLIENT_APIVERSIONING === 'true'
74
+ this[kEventEmitter] = new EventEmitter()
75
+
76
+ this.nodeFilter = opts.nodeFilter || defaultNodeFilter
77
+ if (typeof opts.nodeSelector === 'function') {
78
+ this.nodeSelector = opts.nodeSelector
79
+ } else if (opts.nodeSelector === 'round-robin') {
80
+ this.nodeSelector = roundRobinSelector()
81
+ } else if (opts.nodeSelector === 'random') {
82
+ this.nodeSelector = randomSelector
83
+ } else {
84
+ this.nodeSelector = roundRobinSelector()
85
+ }
86
+
87
+ this._sniffEnabled = typeof this.sniffInterval === 'number'
88
+ this._nextSniff = this._sniffEnabled ? (Date.now() + this.sniffInterval) : 0
89
+ this._isSniffing = false
90
+
91
+ if (opts.sniffOnStart === true) {
92
+ // timer needed otherwise it will clash
93
+ // with the product check testing
94
+ setTimeout(() => {
95
+ this.sniff({ reason: Transport.sniffReasons.SNIFF_ON_START })
96
+ }, 10)
97
+ }
98
+ }
99
+
100
+ request (params, options, callback) {
101
+ options = options || {}
102
+ if (typeof options === 'function') {
103
+ callback = options
104
+ options = {}
105
+ }
106
+ let p = null
107
+
108
+ // promises support
109
+ if (callback === undefined) {
110
+ let onFulfilled = null
111
+ let onRejected = null
112
+ p = new Promise((resolve, reject) => {
113
+ onFulfilled = resolve
114
+ onRejected = reject
115
+ })
116
+ callback = function callback (err, result) {
117
+ err ? onRejected(err) : onFulfilled(result)
118
+ }
119
+ }
120
+
121
+ const meta = {
122
+ context: null,
123
+ request: {
124
+ params: null,
125
+ options: null,
126
+ id: options.id || this.generateRequestId(params, options)
127
+ },
128
+ name: this.name,
129
+ connection: null,
130
+ attempts: 0,
131
+ aborted: false
132
+ }
133
+
134
+ if (this.context != null && options.context != null) {
135
+ meta.context = Object.assign({}, this.context, options.context)
136
+ } else if (this.context != null) {
137
+ meta.context = this.context
138
+ } else if (options.context != null) {
139
+ meta.context = options.context
140
+ }
141
+
142
+ const result = {
143
+ body: null,
144
+ statusCode: null,
145
+ headers: null,
146
+ meta
147
+ }
148
+
149
+ Object.defineProperty(result, 'warnings', {
150
+ get () {
151
+ return this.headers && this.headers.warning
152
+ ? this.headers.warning.split(/(?!\B"[^"]*),(?![^"]*"\B)/)
153
+ : null
154
+ }
155
+ })
156
+
157
+ // We should not retry if we are sending a stream body, because we should store in memory
158
+ // a copy of the stream to be able to send it again, but since we don't know in advance
159
+ // the size of the stream, we risk to take too much memory.
160
+ // Furthermore, copying everytime the stream is very a expensive operation.
161
+ const maxRetries = isStream(params.body) || isStream(params.bulkBody)
162
+ ? 0
163
+ : (typeof options.maxRetries === 'number' ? options.maxRetries : this.maxRetries)
164
+ const compression = options.compression !== undefined ? options.compression : this.compression
165
+ let request = { abort: noop }
166
+ const transportReturn = {
167
+ then (onFulfilled, onRejected) {
168
+ return p.then(onFulfilled, onRejected)
169
+ },
170
+ catch (onRejected) {
171
+ return p.catch(onRejected)
172
+ },
173
+ abort () {
174
+ meta.aborted = true
175
+ request.abort()
176
+ debug('Aborting request', params)
177
+ return this
178
+ },
179
+ finally (onFinally) {
180
+ return p.finally(onFinally)
181
+ }
182
+ }
183
+
184
+ const makeRequest = () => {
185
+ if (meta.aborted === true) {
186
+ this.emit('request', new RequestAbortedError(), result)
187
+ return process.nextTick(callback, new RequestAbortedError(), result)
188
+ }
189
+ meta.connection = this.getConnection({ requestId: meta.request.id })
190
+ if (meta.connection == null) {
191
+ return process.nextTick(callback, new NoLivingConnectionsError(), result)
192
+ }
193
+ this.emit('request', null, result)
194
+ // perform the actual http request
195
+ request = meta.connection.request(params, onResponse)
196
+ }
197
+
198
+ const onConnectionError = (err) => {
199
+ if (err.name !== 'RequestAbortedError') {
200
+ // if there is an error in the connection
201
+ // let's mark the connection as dead
202
+ this.connectionPool.markDead(meta.connection)
203
+
204
+ if (this.sniffOnConnectionFault === true) {
205
+ this.sniff({
206
+ reason: Transport.sniffReasons.SNIFF_ON_CONNECTION_FAULT,
207
+ requestId: meta.request.id
208
+ })
209
+ }
210
+
211
+ // retry logic
212
+ if (meta.attempts < maxRetries) {
213
+ meta.attempts++
214
+ debug(`Retrying request, there are still ${maxRetries - meta.attempts} attempts`, params)
215
+ makeRequest()
216
+ return
217
+ }
218
+ }
219
+
220
+ err.meta = result
221
+ this.emit('response', err, result)
222
+ return callback(err, result)
223
+ }
224
+
225
+ const onResponse = (err, response) => {
226
+ if (err !== null) {
227
+ return onConnectionError(err)
228
+ }
229
+
230
+ result.statusCode = response.statusCode
231
+ result.headers = response.headers
232
+
233
+ if (options.asStream === true) {
234
+ result.body = response
235
+ this.emit('response', null, result)
236
+ callback(null, result)
237
+ return
238
+ }
239
+
240
+ const contentEncoding = (result.headers['content-encoding'] || '').toLowerCase()
241
+ const isCompressed = contentEncoding.indexOf('gzip') > -1 || contentEncoding.indexOf('deflate') > -1
242
+ const isVectorTile = (result.headers['content-type'] || '').indexOf('application/vnd.mapbox-vector-tile') > -1
243
+
244
+ /* istanbul ignore else */
245
+ if (result.headers['content-length'] !== undefined) {
246
+ const contentLength = Number(result.headers['content-length'])
247
+ if (isCompressed && contentLength > MAX_BUFFER_LENGTH) {
248
+ response.destroy()
249
+ return onConnectionError(
250
+ new RequestAbortedError(`The content length (${contentLength}) is bigger than the maximum allowed buffer (${MAX_BUFFER_LENGTH})`, result)
251
+ )
252
+ } else if (contentLength > MAX_STRING_LENGTH) {
253
+ response.destroy()
254
+ return onConnectionError(
255
+ new RequestAbortedError(`The content length (${contentLength}) is bigger than the maximum allowed string (${MAX_STRING_LENGTH})`, result)
256
+ )
257
+ }
258
+ }
259
+ // if the response is compressed, we must handle it
260
+ // as buffer for allowing decompression later
261
+ // while if it's a vector tile, we should return it as buffer
262
+ let payload = isCompressed || isVectorTile ? [] : ''
263
+ const onData = isCompressed || isVectorTile
264
+ ? chunk => { payload.push(chunk) }
265
+ : chunk => { payload += chunk }
266
+ const onEnd = err => {
267
+ response.removeListener('data', onData)
268
+ response.removeListener('end', onEnd)
269
+ response.removeListener('error', onEnd)
270
+ response.removeListener('aborted', onAbort)
271
+
272
+ if (err) {
273
+ return onConnectionError(new ConnectionError(err.message))
274
+ }
275
+
276
+ if (isCompressed) {
277
+ unzip(Buffer.concat(payload), onBody)
278
+ } else {
279
+ onBody(null, isVectorTile ? Buffer.concat(payload) : payload)
280
+ }
281
+ }
282
+
283
+ const onAbort = () => {
284
+ response.destroy()
285
+ onEnd(new Error('Response aborted while reading the body'))
286
+ }
287
+
288
+ if (!isCompressed && !isVectorTile) {
289
+ response.setEncoding('utf8')
290
+ }
291
+
292
+ this.emit('deserialization', null, result)
293
+ response.on('data', onData)
294
+ response.on('error', onEnd)
295
+ response.on('end', onEnd)
296
+ response.on('aborted', onAbort)
297
+ }
298
+
299
+ const onBody = (err, payload) => {
300
+ if (err) {
301
+ this.emit('response', err, result)
302
+ return callback(err, result)
303
+ }
304
+
305
+ const isVectorTile = (result.headers['content-type'] || '').indexOf('application/vnd.mapbox-vector-tile') > -1
306
+ if (Buffer.isBuffer(payload) && !isVectorTile) {
307
+ payload = payload.toString()
308
+ }
309
+ const isHead = params.method === 'HEAD'
310
+ // we should attempt the payload deserialization only if:
311
+ // - a `content-type` is defined and is equal to `application/json`
312
+ // - the request is not a HEAD request
313
+ // - the payload is not an empty string
314
+ if (result.headers['content-type'] !== undefined &&
315
+ (result.headers['content-type'].indexOf('application/json') > -1 ||
316
+ result.headers['content-type'].indexOf('application/vnd.elasticsearch+json') > -1) &&
317
+ isHead === false &&
318
+ payload !== ''
319
+ ) {
320
+ try {
321
+ result.body = this.serializer.deserialize(payload)
322
+ } catch (err) {
323
+ this.emit('response', err, result)
324
+ return callback(err, result)
325
+ }
326
+ } else {
327
+ // cast to boolean if the request method was HEAD and there was no error
328
+ result.body = isHead === true && result.statusCode < 400 ? true : payload
329
+ }
330
+
331
+ // we should ignore the statusCode if the user has configured the `ignore` field with
332
+ // the statusCode we just got or if the request method is HEAD and the statusCode is 404
333
+ const ignoreStatusCode = (Array.isArray(options.ignore) && options.ignore.indexOf(result.statusCode) > -1) ||
334
+ (isHead === true && result.statusCode === 404)
335
+
336
+ if (ignoreStatusCode === false &&
337
+ (result.statusCode === 502 || result.statusCode === 503 || result.statusCode === 504)) {
338
+ // if the statusCode is 502/3/4 we should run our retry strategy
339
+ // and mark the connection as dead
340
+ this.connectionPool.markDead(meta.connection)
341
+ // retry logic (we shoukd not retry on "429 - Too Many Requests")
342
+ if (meta.attempts < maxRetries && result.statusCode !== 429) {
343
+ meta.attempts++
344
+ debug(`Retrying request, there are still ${maxRetries - meta.attempts} attempts`, params)
345
+ makeRequest()
346
+ return
347
+ }
348
+ } else {
349
+ // everything has worked as expected, let's mark
350
+ // the connection as alive (or confirm it)
351
+ this.connectionPool.markAlive(meta.connection)
352
+ }
353
+
354
+ if (ignoreStatusCode === false && result.statusCode >= 400) {
355
+ const error = new ResponseError(result)
356
+ this.emit('response', error, result)
357
+ callback(error, result)
358
+ } else {
359
+ // cast to boolean if the request method was HEAD
360
+ if (isHead === true && result.statusCode === 404) {
361
+ result.body = false
362
+ }
363
+ this.emit('response', null, result)
364
+ callback(null, result)
365
+ }
366
+ }
367
+
368
+ const prepareRequest = () => {
369
+ this.emit('serialization', null, result)
370
+ const headers = Object.assign({}, this.headers, lowerCaseHeaders(options.headers))
371
+
372
+ if (options.opaqueId !== undefined) {
373
+ headers['x-opaque-id'] = this.opaqueIdPrefix !== null
374
+ ? this.opaqueIdPrefix + options.opaqueId
375
+ : options.opaqueId
376
+ }
377
+
378
+ // handle json body
379
+ if (params.body != null) {
380
+ if (shouldSerialize(params.body) === true) {
381
+ try {
382
+ params.body = this.serializer.serialize(params.body)
383
+ } catch (err) {
384
+ this.emit('request', err, result)
385
+ process.nextTick(callback, err, result)
386
+ return transportReturn
387
+ }
388
+ }
389
+
390
+ if (params.body !== '') {
391
+ headers['content-type'] = headers['content-type'] || (this[kApiVersioning] ? 'application/vnd.elasticsearch+json; compatible-with=7' : 'application/json')
392
+ }
393
+
394
+ // handle ndjson body
395
+ } else if (params.bulkBody != null) {
396
+ if (shouldSerialize(params.bulkBody) === true) {
397
+ try {
398
+ params.body = this.serializer.ndserialize(params.bulkBody)
399
+ } catch (err) {
400
+ this.emit('request', err, result)
401
+ process.nextTick(callback, err, result)
402
+ return transportReturn
403
+ }
404
+ } else {
405
+ params.body = params.bulkBody
406
+ }
407
+ if (params.body !== '') {
408
+ headers['content-type'] = headers['content-type'] || (this[kApiVersioning] ? 'application/vnd.elasticsearch+x-ndjson; compatible-with=7' : 'application/x-ndjson')
409
+ }
410
+ }
411
+
412
+ params.headers = headers
413
+ // serializes the querystring
414
+ if (options.querystring == null) {
415
+ params.querystring = this.serializer.qserialize(params.querystring)
416
+ } else {
417
+ params.querystring = this.serializer.qserialize(
418
+ Object.assign({}, params.querystring, options.querystring)
419
+ )
420
+ }
421
+
422
+ // handles request timeout
423
+ params.timeout = toMs(options.requestTimeout || this.requestTimeout)
424
+ if (options.asStream === true) params.asStream = true
425
+
426
+ // handle compression
427
+ if (params.body !== '' && params.body != null) {
428
+ if (isStream(params.body) === true) {
429
+ if (compression === 'gzip') {
430
+ params.headers['content-encoding'] = compression
431
+ params.body = params.body.pipe(createGzip())
432
+ }
433
+ makeRequest()
434
+ } else if (compression === 'gzip') {
435
+ gzip(params.body, (err, buffer) => {
436
+ /* istanbul ignore next */
437
+ if (err) {
438
+ this.emit('request', err, result)
439
+ return callback(err, result)
440
+ }
441
+ params.headers['content-encoding'] = compression
442
+ params.headers['content-length'] = '' + Buffer.byteLength(buffer)
443
+ params.body = buffer
444
+ makeRequest()
445
+ })
446
+ } else {
447
+ params.headers['content-length'] = '' + Buffer.byteLength(params.body)
448
+ makeRequest()
449
+ }
450
+ } else {
451
+ makeRequest()
452
+ }
453
+ }
454
+
455
+ meta.request.params = params
456
+ meta.request.options = options
457
+ // still need to check the product or waiting for the check to finish
458
+ if (this[kProductCheck] === 0 || this[kProductCheck] === 1) {
459
+ // let pass info requests
460
+ if (params.method === 'GET' && params.path === '/') {
461
+ prepareRequest()
462
+ } else {
463
+ // wait for product check to finish
464
+ this[kEventEmitter].once('product-check', (error, status) => {
465
+ if (status === false) {
466
+ const err = error || new ProductNotSupportedError(result)
467
+ if (this[kProductCheck] === 4) {
468
+ err.message = 'The client noticed that the server is not a supported distribution of Elasticsearch'
469
+ }
470
+ this.emit('request', err, result)
471
+ process.nextTick(callback, err, result)
472
+ } else {
473
+ prepareRequest()
474
+ }
475
+ })
476
+ // the very first request triggers the product check
477
+ if (this[kProductCheck] === 0) {
478
+ this.productCheck()
479
+ }
480
+ }
481
+ // the product check is finished and it's not Elasticsearch
482
+ } else if (this[kProductCheck] === 3 || this[kProductCheck] === 4) {
483
+ const err = new ProductNotSupportedError(result)
484
+ if (this[kProductCheck] === 4) {
485
+ err.message = 'The client noticed that the server is not a supported distribution of Elasticsearch'
486
+ }
487
+ this.emit('request', err, result)
488
+ process.nextTick(callback, err, result)
489
+ // the product check finished and it's Elasticsearch
490
+ } else {
491
+ prepareRequest()
492
+ }
493
+
494
+ return transportReturn
495
+ }
496
+
497
+ getConnection (opts) {
498
+ const now = Date.now()
499
+ if (this._sniffEnabled === true && now > this._nextSniff) {
500
+ this.sniff({ reason: Transport.sniffReasons.SNIFF_INTERVAL, requestId: opts.requestId })
501
+ }
502
+ return this.connectionPool.getConnection({
503
+ filter: this.nodeFilter,
504
+ selector: this.nodeSelector,
505
+ requestId: opts.requestId,
506
+ name: this.name,
507
+ now
508
+ })
509
+ }
510
+
511
+ sniff (opts, callback = noop) {
512
+ if (this._isSniffing === true) return
513
+ this._isSniffing = true
514
+ debug('Started sniffing request')
515
+
516
+ if (typeof opts === 'function') {
517
+ callback = opts
518
+ opts = { reason: Transport.sniffReasons.DEFAULT }
519
+ }
520
+
521
+ const { reason } = opts
522
+
523
+ const request = {
524
+ method: 'GET',
525
+ path: this.sniffEndpoint
526
+ }
527
+
528
+ this.request(request, { id: opts.requestId }, (err, result) => {
529
+ this._isSniffing = false
530
+ if (this._sniffEnabled === true) {
531
+ this._nextSniff = Date.now() + this.sniffInterval
532
+ }
533
+
534
+ if (err != null) {
535
+ debug('Sniffing errored', err)
536
+ result.meta.sniff = { hosts: [], reason }
537
+ this.emit('sniff', err, result)
538
+ return callback(err)
539
+ }
540
+
541
+ debug('Sniffing ended successfully', result.body)
542
+ const protocol = result.meta.connection.url.protocol || /* istanbul ignore next */ 'http:'
543
+ const hosts = this.connectionPool.nodesToHost(result.body.nodes, protocol)
544
+ this.connectionPool.update(hosts)
545
+
546
+ result.meta.sniff = { hosts, reason }
547
+ this.emit('sniff', null, result)
548
+ callback(null, hosts)
549
+ })
550
+ }
551
+
552
+ productCheck () {
553
+ debug('Start product check')
554
+ this[kProductCheck] = 1
555
+ this.request({
556
+ method: 'GET',
557
+ path: '/'
558
+ }, (err, result) => {
559
+ this[kProductCheck] = 3
560
+ if (err) {
561
+ debug('Product check failed', err)
562
+ if (err.statusCode === 401 || err.statusCode === 403) {
563
+ this[kProductCheck] = 2
564
+ process.emitWarning(
565
+ 'The client is unable to verify that the server is Elasticsearch due to security privileges on the server side. Some functionality may not be compatible if the server is running an unsupported product.',
566
+ 'ProductNotSupportedSecurityError'
567
+ )
568
+ this[kEventEmitter].emit('product-check', null, true)
569
+ } else {
570
+ this[kProductCheck] = 0
571
+ this[kEventEmitter].emit('product-check', err, false)
572
+ }
573
+ } else {
574
+ debug('Checking elasticsearch version', result.body, result.headers)
575
+ if (result.body.version == null || typeof result.body.version.number !== 'string') {
576
+ debug('Can\'t access Elasticsearch version')
577
+ return this[kEventEmitter].emit('product-check', null, false)
578
+ }
579
+ const tagline = result.body.tagline
580
+ const version = result.body.version.number.split('.')
581
+ const major = Number(version[0])
582
+ const minor = Number(version[1])
583
+ if (major < 6) {
584
+ return this[kEventEmitter].emit('product-check', null, false)
585
+ } else if (major >= 6 && major < 7) {
586
+ if (tagline !== 'You Know, for Search') {
587
+ debug('Bad tagline')
588
+ return this[kEventEmitter].emit('product-check', null, false)
589
+ }
590
+ } else if (major === 7 && minor < 14) {
591
+ if (tagline !== 'You Know, for Search') {
592
+ debug('Bad tagline')
593
+ return this[kEventEmitter].emit('product-check', null, false)
594
+ }
595
+
596
+ if (result.body.version.build_flavor !== 'default') {
597
+ debug('Bad build_flavor')
598
+ this[kProductCheck] = 4
599
+ return this[kEventEmitter].emit('product-check', null, false)
600
+ }
601
+ } else {
602
+ if (result.headers['x-elastic-product'] !== 'Elasticsearch') {
603
+ debug('x-elastic-product not recognized')
604
+ return this[kEventEmitter].emit('product-check', null, false)
605
+ }
606
+ }
607
+ debug('Valid Elasticsearch distribution')
608
+ this[kProductCheck] = 2
609
+ this[kEventEmitter].emit('product-check', null, true)
610
+ }
611
+ })
612
+ }
613
+ }
614
+
615
+ Transport.sniffReasons = {
616
+ SNIFF_ON_START: 'sniff-on-start',
617
+ SNIFF_INTERVAL: 'sniff-interval',
618
+ SNIFF_ON_CONNECTION_FAULT: 'sniff-on-connection-fault',
619
+ // TODO: find a better name
620
+ DEFAULT: 'default'
621
+ }
622
+
623
+ function toMs (time) {
624
+ if (typeof time === 'string') {
625
+ return ms(time)
626
+ }
627
+ return time
628
+ }
629
+
630
+ function shouldSerialize (obj) {
631
+ return typeof obj !== 'string' &&
632
+ typeof obj.pipe !== 'function' &&
633
+ Buffer.isBuffer(obj) === false
634
+ }
635
+
636
+ function isStream (obj) {
637
+ return obj != null && typeof obj.pipe === 'function'
638
+ }
639
+
640
+ function defaultNodeFilter (node) {
641
+ // avoid master only nodes
642
+ if (node.roles.master === true &&
643
+ node.roles.data === false &&
644
+ node.roles.ingest === false) {
645
+ return false
646
+ }
647
+ return true
648
+ }
649
+
650
+ function roundRobinSelector () {
651
+ let current = -1
652
+ return function _roundRobinSelector (connections) {
653
+ if (++current >= connections.length) {
654
+ current = 0
655
+ }
656
+ return connections[current]
657
+ }
658
+ }
659
+
660
+ function randomSelector (connections) {
661
+ const index = Math.floor(Math.random() * connections.length)
662
+ return connections[index]
663
+ }
664
+
665
+ function generateRequestId () {
666
+ const maxInt = 2147483647
667
+ let nextReqId = 0
668
+ return function genReqId (params, options) {
669
+ return (nextReqId = (nextReqId + 1) & maxInt)
670
+ }
671
+ }
672
+
673
+ function lowerCaseHeaders (oldHeaders) {
674
+ if (oldHeaders == null) return oldHeaders
675
+ const newHeaders = {}
676
+ for (const header in oldHeaders) {
677
+ newHeaders[header.toLowerCase()] = oldHeaders[header]
678
+ }
679
+ return newHeaders
680
+ }
681
+
682
+ module.exports = Transport
683
+ module.exports.internals = {
684
+ defaultNodeFilter,
685
+ roundRobinSelector,
686
+ randomSelector,
687
+ generateRequestId,
688
+ lowerCaseHeaders
689
+ }