6
6
7
7
const debug = require ( 'debug' ) ( 'elasticsearch' )
8
8
const os = require ( 'os' )
9
- const once = require ( 'once' )
10
- const { createGzip } = require ( 'zlib' )
11
- const intoStream = require ( 'into-stream' )
9
+ const { gzip, createGzip } = require ( 'zlib' )
12
10
const ms = require ( 'ms' )
13
11
const {
14
12
ConnectionError,
@@ -35,7 +33,11 @@ class Transport {
35
33
this . requestTimeout = toMs ( opts . requestTimeout )
36
34
this . suggestCompression = opts . suggestCompression === true
37
35
this . compression = opts . compression || false
38
- this . headers = Object . assign ( { } , { 'User-Agent' : userAgent } , opts . headers )
36
+ this . headers = Object . assign ( { } ,
37
+ { 'User-Agent' : userAgent } ,
38
+ opts . suggestCompression === true ? { 'Accept-Encoding' : 'gzip,deflate' } : null ,
39
+ opts . headers
40
+ )
39
41
this . sniffInterval = opts . sniffInterval
40
42
this . sniffOnConnectionFault = opts . sniffOnConnectionFault
41
43
this . sniffEndpoint = opts . sniffEndpoint
@@ -85,7 +87,6 @@ class Transport {
85
87
}
86
88
}
87
89
88
- callback = once ( callback )
89
90
const meta = {
90
91
context : options . context || null ,
91
92
request : {
@@ -107,8 +108,12 @@ class Transport {
107
108
meta
108
109
}
109
110
110
- const maxRetries = options . maxRetries || this . maxRetries
111
- const compression = options . compression || this . compression
111
+ // We should not retry if we are sending a stream body, because we should store in memory
112
+ // a copy of the stream to be able to send it again, but since we don't know in advance
113
+ // the size of the stream, we risk to take too much memory.
114
+ // Furthermore, copying everytime the stream is very a expensive operation.
115
+ const maxRetries = isStream ( params . body ) ? 0 : options . maxRetries || this . maxRetries
116
+ const compression = options . compression !== undefined ? options . compression : this . compression
112
117
var request = { abort : noop }
113
118
114
119
const makeRequest = ( ) => {
@@ -119,80 +124,9 @@ class Transport {
119
124
if ( meta . connection == null ) {
120
125
return callback ( new NoLivingConnectionsError ( ) , result )
121
126
}
122
- // TODO: make this assignment FAST
123
- const headers = Object . assign ( { } , this . headers , options . headers )
124
-
125
- if ( options . opaqueId !== undefined ) {
126
- headers [ 'X-Opaque-Id' ] = this . opaqueIdPrefix !== null
127
- ? this . opaqueIdPrefix + options . opaqueId
128
- : options . opaqueId
129
- }
130
-
131
- // handle json body
132
- if ( params . body != null ) {
133
- if ( shouldSerialize ( params . body ) === true ) {
134
- try {
135
- params . body = this . serializer . serialize ( params . body )
136
- } catch ( err ) {
137
- return callback ( err , result )
138
- }
139
- }
140
-
141
- if ( params . body !== '' ) {
142
- headers [ 'Content-Type' ] = headers [ 'Content-Type' ] || 'application/json'
143
- if ( compression === 'gzip' ) {
144
- if ( isStream ( params . body ) === false ) {
145
- params . body = intoStream ( params . body ) . pipe ( createGzip ( ) )
146
- } else {
147
- params . body = params . body . pipe ( createGzip ( ) )
148
- }
149
- headers [ 'Content-Encoding' ] = compression
150
- }
151
- }
152
-
153
- if ( isStream ( params . body ) === false ) {
154
- headers [ 'Content-Length' ] = '' + Buffer . byteLength ( params . body )
155
- }
156
- // handle ndjson body
157
- } else if ( params . bulkBody != null ) {
158
- if ( shouldSerialize ( params . bulkBody ) === true ) {
159
- try {
160
- params . body = this . serializer . ndserialize ( params . bulkBody )
161
- } catch ( err ) {
162
- return callback ( err , result )
163
- }
164
- } else {
165
- params . body = params . bulkBody
166
- }
167
- headers [ 'Content-Type' ] = headers [ 'Content-Type' ] || 'application/x-ndjson'
168
- if ( isStream ( params . body ) === false ) {
169
- headers [ 'Content-Length' ] = '' + Buffer . byteLength ( params . body )
170
- }
171
- }
172
-
173
- if ( this . suggestCompression === true ) {
174
- headers [ 'Accept-Encoding' ] = 'gzip,deflate'
175
- }
176
-
177
- params . headers = headers
178
- // serializes the querystring
179
- if ( options . querystring == null ) {
180
- params . querystring = this . serializer . qserialize ( params . querystring )
181
- } else {
182
- params . querystring = this . serializer . qserialize (
183
- Object . assign ( { } , params . querystring , options . querystring )
184
- )
185
- }
186
-
187
- meta . request . params = params
188
- meta . request . options = options
189
127
this . emit ( 'request' , null , result )
190
-
191
- // handles request timeout
192
- params . timeout = toMs ( options . requestTimeout || this . requestTimeout )
193
- if ( options . asStream === true ) params . asStream = true
194
128
// perform the actual http request
195
- return meta . connection . request ( params , onResponse )
129
+ request = meta . connection . request ( params , onResponse )
196
130
}
197
131
198
132
const onResponse = ( err , response ) => {
@@ -213,7 +147,7 @@ class Transport {
213
147
if ( meta . attempts < maxRetries ) {
214
148
meta . attempts ++
215
149
debug ( `Retrying request, there are still ${ maxRetries - meta . attempts } attempts` , params )
216
- request = makeRequest ( params , callback )
150
+ makeRequest ( )
217
151
return
218
152
}
219
153
}
@@ -226,7 +160,7 @@ class Transport {
226
160
const { statusCode, headers } = response
227
161
result . statusCode = statusCode
228
162
result . headers = headers
229
- if ( headers [ 'warning' ] != null ) {
163
+ if ( headers [ 'warning' ] !== undefined ) {
230
164
result . warnings = result . warnings || [ ]
231
165
// split the string over the commas not inside quotes
232
166
result . warnings . push . apply ( result . warnings , headers [ 'warning' ] . split ( / (? ! \B " [ ^ " ] * ) , (? ! [ ^ " ] * " \B ) / ) )
@@ -255,7 +189,7 @@ class Transport {
255
189
// - a `content-type` is defined and is equal to `application/json`
256
190
// - the request is not a HEAD request
257
191
// - the payload is not an empty string
258
- if ( headers [ 'content-type' ] != null &&
192
+ if ( headers [ 'content-type' ] !== undefined &&
259
193
headers [ 'content-type' ] . indexOf ( 'application/json' ) > - 1 &&
260
194
isHead === false &&
261
195
payload !== ''
@@ -285,7 +219,7 @@ class Transport {
285
219
if ( meta . attempts < maxRetries && statusCode !== 429 ) {
286
220
meta . attempts ++
287
221
debug ( `Retrying request, there are still ${ maxRetries - meta . attempts } attempts` , params )
288
- request = makeRequest ( params , callback )
222
+ makeRequest ( )
289
223
return
290
224
}
291
225
} else {
@@ -309,7 +243,86 @@ class Transport {
309
243
} )
310
244
}
311
245
312
- request = makeRequest ( )
246
+ const headers = Object . assign ( { } , this . headers , options . headers )
247
+
248
+ if ( options . opaqueId !== undefined ) {
249
+ headers [ 'X-Opaque-Id' ] = this . opaqueIdPrefix !== null
250
+ ? this . opaqueIdPrefix + options . opaqueId
251
+ : options . opaqueId
252
+ }
253
+
254
+ // handle json body
255
+ if ( params . body != null ) {
256
+ if ( shouldSerialize ( params . body ) === true ) {
257
+ try {
258
+ params . body = this . serializer . serialize ( params . body )
259
+ } catch ( err ) {
260
+ return callback ( err , result )
261
+ }
262
+ }
263
+
264
+ if ( params . body !== '' ) {
265
+ headers [ 'Content-Type' ] = headers [ 'Content-Type' ] || 'application/json'
266
+ }
267
+
268
+ // handle ndjson body
269
+ } else if ( params . bulkBody != null ) {
270
+ if ( shouldSerialize ( params . bulkBody ) === true ) {
271
+ try {
272
+ params . body = this . serializer . ndserialize ( params . bulkBody )
273
+ } catch ( err ) {
274
+ return callback ( err , result )
275
+ }
276
+ } else {
277
+ params . body = params . bulkBody
278
+ }
279
+ if ( params . body !== '' ) {
280
+ headers [ 'Content-Type' ] = headers [ 'Content-Type' ] || 'application/x-ndjson'
281
+ }
282
+ }
283
+
284
+ params . headers = headers
285
+ // serializes the querystring
286
+ if ( options . querystring == null ) {
287
+ params . querystring = this . serializer . qserialize ( params . querystring )
288
+ } else {
289
+ params . querystring = this . serializer . qserialize (
290
+ Object . assign ( { } , params . querystring , options . querystring )
291
+ )
292
+ }
293
+
294
+ // handles request timeout
295
+ params . timeout = toMs ( options . requestTimeout || this . requestTimeout )
296
+ if ( options . asStream === true ) params . asStream = true
297
+ meta . request . params = params
298
+ meta . request . options = options
299
+
300
+ // handle compression
301
+ if ( params . body !== '' && params . body != null ) {
302
+ if ( isStream ( params . body ) === true ) {
303
+ if ( compression === 'gzip' ) {
304
+ params . headers [ 'Content-Encoding' ] = compression
305
+ params . body = params . body . pipe ( createGzip ( ) )
306
+ }
307
+ makeRequest ( )
308
+ } else if ( compression === 'gzip' ) {
309
+ gzip ( params . body , ( err , buffer ) => {
310
+ /* istanbul ignore next */
311
+ if ( err ) {
312
+ return callback ( err , result )
313
+ }
314
+ params . headers [ 'Content-Encoding' ] = compression
315
+ params . headers [ 'Content-Length' ] = '' + Buffer . byteLength ( buffer )
316
+ params . body = buffer
317
+ makeRequest ( )
318
+ } )
319
+ } else {
320
+ params . headers [ 'Content-Length' ] = '' + Buffer . byteLength ( params . body )
321
+ makeRequest ( )
322
+ }
323
+ } else {
324
+ makeRequest ( )
325
+ }
313
326
314
327
return {
315
328
then ( onFulfilled , onRejected ) {
@@ -405,7 +418,7 @@ function shouldSerialize (obj) {
405
418
}
406
419
407
420
function isStream ( obj ) {
408
- return typeof obj . pipe === 'function'
421
+ return obj != null && typeof obj . pipe === 'function'
409
422
}
410
423
411
424
function defaultNodeFilter ( node ) {
0 commit comments