elasticsearch-transport 7.5.0 → 7.13.3

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +28 -11
  3. data/README.md +158 -58
  4. data/Rakefile +16 -3
  5. data/elasticsearch-transport.gemspec +57 -63
  6. data/lib/elasticsearch/transport/client.rb +150 -56
  7. data/lib/elasticsearch/transport/meta_header.rb +135 -0
  8. data/lib/elasticsearch/transport/redacted.rb +16 -3
  9. data/lib/elasticsearch/transport/transport/base.rb +32 -12
  10. data/lib/elasticsearch/transport/transport/connections/collection.rb +18 -8
  11. data/lib/elasticsearch/transport/transport/connections/connection.rb +23 -8
  12. data/lib/elasticsearch/transport/transport/connections/selector.rb +16 -3
  13. data/lib/elasticsearch/transport/transport/errors.rb +16 -3
  14. data/lib/elasticsearch/transport/transport/http/curb.rb +16 -3
  15. data/lib/elasticsearch/transport/transport/http/faraday.rb +27 -6
  16. data/lib/elasticsearch/transport/transport/http/manticore.rb +16 -3
  17. data/lib/elasticsearch/transport/transport/loggable.rb +16 -3
  18. data/lib/elasticsearch/transport/transport/response.rb +16 -4
  19. data/lib/elasticsearch/transport/transport/serializer/multi_json.rb +16 -3
  20. data/lib/elasticsearch/transport/transport/sniffer.rb +35 -15
  21. data/lib/elasticsearch/transport/version.rb +17 -4
  22. data/lib/elasticsearch/transport.rb +16 -3
  23. data/lib/elasticsearch-transport.rb +16 -3
  24. data/spec/elasticsearch/connections/collection_spec.rb +28 -3
  25. data/spec/elasticsearch/connections/selector_spec.rb +16 -3
  26. data/spec/elasticsearch/transport/base_spec.rb +60 -38
  27. data/spec/elasticsearch/transport/client_spec.rb +628 -132
  28. data/spec/elasticsearch/transport/meta_header_spec.rb +265 -0
  29. data/spec/elasticsearch/transport/sniffer_spec.rb +16 -16
  30. data/spec/spec_helper.rb +19 -1
  31. data/test/integration/transport_test.rb +30 -4
  32. data/test/profile/client_benchmark_test.rb +16 -3
  33. data/test/test_helper.rb +16 -3
  34. data/test/unit/connection_test.rb +23 -5
  35. data/test/unit/response_test.rb +17 -4
  36. data/test/unit/serializer_test.rb +16 -3
  37. data/test/unit/transport_base_test.rb +17 -4
  38. data/test/unit/transport_curb_test.rb +16 -3
  39. data/test/unit/transport_faraday_test.rb +18 -5
  40. data/test/unit/transport_manticore_test.rb +29 -16
  41. metadata +70 -69
@@ -1,11 +1,23 @@
1
- # Licensed to Elasticsearch B.V under one or more agreements.
2
- # Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3
- # See the LICENSE file in the project root for more information
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
4
17
 
5
18
  require 'spec_helper'
6
19
 
7
20
  describe Elasticsearch::Transport::Client do
8
-
9
21
  let(:client) do
10
22
  described_class.new.tap do |_client|
11
23
  allow(_client).to receive(:__build_connections)
@@ -49,7 +61,6 @@ describe Elasticsearch::Transport::Client do
49
61
  end
50
62
 
51
63
  context 'when a User-Agent header is specified as client option' do
52
-
53
64
  let(:client) do
54
65
  described_class.new(transport_options: { headers: { 'User-Agent' => 'testing' } })
55
66
  end
@@ -59,6 +70,49 @@ describe Elasticsearch::Transport::Client do
59
70
  end
60
71
  end
61
72
 
73
+ context 'when an encoded api_key is provided' do
74
+ let(:client) do
75
+ described_class.new(api_key: 'an_api_key')
76
+ end
77
+ let(:authorization_header) do
78
+ client.transport.connections.first.connection.headers['Authorization']
79
+ end
80
+
81
+ it 'Adds the ApiKey header to the connection' do
82
+ expect(authorization_header).to eq('ApiKey an_api_key')
83
+ end
84
+ end
85
+
86
+ context 'when an un-encoded api_key is provided' do
87
+ let(:client) do
88
+ described_class.new(api_key: { id: 'my_id', api_key: 'my_api_key' })
89
+ end
90
+ let(:authorization_header) do
91
+ client.transport.connections.first.connection.headers['Authorization']
92
+ end
93
+
94
+ it 'Adds the ApiKey header to the connection' do
95
+ expect(authorization_header).to eq("ApiKey #{Base64.strict_encode64('my_id:my_api_key')}")
96
+ end
97
+ end
98
+
99
+ context 'when basic auth and api_key are provided' do
100
+ let(:client) do
101
+ described_class.new(
102
+ api_key: { id: 'my_id', api_key: 'my_api_key' },
103
+ host: 'http://elastic:password@localhost:9200'
104
+ )
105
+ end
106
+ let(:authorization_header) do
107
+ client.transport.connections.first.connection.headers['Authorization']
108
+ end
109
+
110
+ it 'removes basic auth credentials' do
111
+ expect(authorization_header).not_to match(/^Basic/)
112
+ expect(authorization_header).to match(/^ApiKey/)
113
+ end
114
+ end
115
+
62
116
  context 'when a user-agent header is specified as client option in lower-case' do
63
117
 
64
118
  let(:client) do
@@ -176,45 +230,55 @@ describe Elasticsearch::Transport::Client do
176
230
  end
177
231
 
178
232
  describe 'adapter' do
179
-
180
233
  context 'when no adapter is specified' do
181
-
182
234
  let(:adapter) do
183
- client.transport.connections.all.first.connection.builder.handlers
235
+ client.transport.connections.all.first.connection.builder.adapter
184
236
  end
185
237
 
186
238
  it 'uses Faraday NetHttp' do
187
- expect(adapter).to include(Faraday::Adapter::NetHttp)
239
+ expect(adapter).to eq Faraday::Adapter::NetHttp
188
240
  end
189
241
  end
190
242
 
191
- context 'when the adapter is specified' do
192
-
243
+ context 'when the adapter is patron' do
193
244
  let(:adapter) do
194
- client.transport.connections.all.first.connection.builder.handlers
245
+ client.transport.connections.all.first.connection.builder.adapter
195
246
  end
196
247
 
197
248
  let(:client) do
198
- described_class.new(adapter: :typhoeus)
249
+ described_class.new(adapter: :patron, enable_meta_header: false)
199
250
  end
200
251
 
201
252
  it 'uses Faraday with the adapter' do
202
- expect(adapter).to include(Faraday::Adapter::Typhoeus)
253
+ expect(adapter).to eq Faraday::Adapter::Patron
203
254
  end
204
255
  end
205
256
 
206
- context 'when the adapter is specified as a string key' do
257
+ context 'when the adapter is typhoeus' do
258
+ let(:adapter) do
259
+ client.transport.connections.all.first.connection.builder.adapter
260
+ end
261
+
262
+ let(:client) do
263
+ described_class.new(adapter: :typhoeus, enable_meta_header: false)
264
+ end
207
265
 
266
+ it 'uses Faraday with the adapter' do
267
+ expect(adapter).to eq Faraday::Adapter::Typhoeus
268
+ end
269
+ end unless jruby?
270
+
271
+ context 'when the adapter is specified as a string key' do
208
272
  let(:adapter) do
209
- client.transport.connections.all.first.connection.builder.handlers
273
+ client.transport.connections.all.first.connection.builder.adapter
210
274
  end
211
275
 
212
276
  let(:client) do
213
- described_class.new('adapter' => :typhoeus)
277
+ described_class.new(adapter: :patron, enable_meta_header: false)
214
278
  end
215
279
 
216
280
  it 'uses Faraday with the adapter' do
217
- expect(adapter).to include(Faraday::Adapter::Typhoeus)
281
+ expect(adapter).to eq Faraday::Adapter::Patron
218
282
  end
219
283
  end
220
284
 
@@ -226,11 +290,11 @@ describe Elasticsearch::Transport::Client do
226
290
  end
227
291
 
228
292
  let(:adapter) do
229
- client.transport.connections.all.first.connection.builder.handlers
293
+ client.transport.connections.all.first.connection.builder.adapter
230
294
  end
231
295
 
232
296
  it 'uses the detected adapter' do
233
- expect(adapter).to include(Faraday::Adapter::Patron)
297
+ expect(adapter).to eq Faraday::Adapter::Patron
234
298
  end
235
299
  end
236
300
 
@@ -238,17 +302,21 @@ describe Elasticsearch::Transport::Client do
238
302
 
239
303
  let(:client) do
240
304
  described_class.new do |faraday|
241
- faraday.adapter :typhoeus
305
+ faraday.adapter :patron
242
306
  faraday.response :logger
243
307
  end
244
308
  end
245
309
 
310
+ let(:adapter) do
311
+ client.transport.connections.all.first.connection.builder.adapter
312
+ end
313
+
246
314
  let(:handlers) do
247
315
  client.transport.connections.all.first.connection.builder.handlers
248
316
  end
249
317
 
250
318
  it 'sets the adapter' do
251
- expect(handlers).to include(Faraday::Adapter::Typhoeus)
319
+ expect(adapter).to eq Faraday::Adapter::Patron
252
320
  end
253
321
 
254
322
  it 'sets the logger' do
@@ -260,7 +328,11 @@ describe Elasticsearch::Transport::Client do
260
328
  context 'when cloud credentials are provided' do
261
329
 
262
330
  let(:client) do
263
- described_class.new(cloud_id: 'name:bG9jYWxob3N0JGFiY2QkZWZnaA==', user: 'elastic', password: 'changeme')
331
+ described_class.new(
332
+ cloud_id: 'name:bG9jYWxob3N0JGFiY2QkZWZnaA==',
333
+ user: 'elastic',
334
+ password: 'changeme'
335
+ )
264
336
  end
265
337
 
266
338
  let(:hosts) do
@@ -272,17 +344,19 @@ describe Elasticsearch::Transport::Client do
272
344
  expect(hosts[0][:protocol]).to eq('https')
273
345
  expect(hosts[0][:user]).to eq('elastic')
274
346
  expect(hosts[0][:password]).to eq('changeme')
275
- expect(hosts[0][:port]).to eq(9243)
347
+ expect(hosts[0][:port]).to eq(443)
276
348
  end
277
349
 
278
350
  it 'creates the correct full url' do
279
- expect(client.transport.__full_url(client.transport.hosts[0])).to eq('https://elastic:changeme@abcd.localhost:9243')
351
+ expect(
352
+ client.transport.__full_url(client.transport.hosts[0])
353
+ ).to eq('https://elastic:changeme@abcd.localhost:443')
280
354
  end
281
355
 
282
356
  context 'when a port is specified' do
283
357
 
284
358
  let(:client) do
285
- described_class.new(cloud_id: 'name:bG9jYWxob3N0JGFiY2QkZWZnaA==', user: 'elastic', password: 'changeme', port: 9200 )
359
+ described_class.new(cloud_id: 'name:bG9jYWxob3N0JGFiY2QkZWZnaA==', user: 'elastic', password: 'changeme', port: 9250)
286
360
  end
287
361
 
288
362
  it 'sets the specified port along with the cloud credentials' do
@@ -290,18 +364,22 @@ describe Elasticsearch::Transport::Client do
290
364
  expect(hosts[0][:protocol]).to eq('https')
291
365
  expect(hosts[0][:user]).to eq('elastic')
292
366
  expect(hosts[0][:password]).to eq('changeme')
293
- expect(hosts[0][:port]).to eq(9200)
367
+ expect(hosts[0][:port]).to eq(9250)
294
368
  end
295
369
 
296
370
  it 'creates the correct full url' do
297
- expect(client.transport.__full_url(client.transport.hosts[0])).to eq('https://elastic:changeme@abcd.localhost:9200')
371
+ expect(client.transport.__full_url(client.transport.hosts[0])).to eq('https://elastic:changeme@abcd.localhost:9250')
298
372
  end
299
373
  end
300
374
 
301
375
  context 'when the cluster has alternate names' do
302
376
 
303
377
  let(:client) do
304
- described_class.new(cloud_id: 'myCluster:bG9jYWxob3N0JGFiY2QkZWZnaA==', user: 'elasticfantastic', password: 'tobechanged')
378
+ described_class.new(
379
+ cloud_id: 'myCluster:bG9jYWxob3N0JGFiY2QkZWZnaA==',
380
+ user: 'elasticfantastic',
381
+ password: 'tobechanged'
382
+ )
305
383
  end
306
384
 
307
385
  let(:hosts) do
@@ -313,124 +391,334 @@ describe Elasticsearch::Transport::Client do
313
391
  expect(hosts[0][:protocol]).to eq('https')
314
392
  expect(hosts[0][:user]).to eq('elasticfantastic')
315
393
  expect(hosts[0][:password]).to eq('tobechanged')
316
- expect(hosts[0][:port]).to eq(9243)
394
+ expect(hosts[0][:port]).to eq(443)
317
395
  end
318
396
 
319
397
  it 'creates the correct full url' do
320
- expect(client.transport.__full_url(client.transport.hosts[0])).to eq('https://elasticfantastic:tobechanged@abcd.localhost:9243')
398
+ expect(
399
+ client.transport.__full_url(client.transport.hosts[0])
400
+ ).to eq('https://elasticfantastic:tobechanged@abcd.localhost:443')
321
401
  end
322
-
323
402
  end
324
- end
325
403
 
326
- shared_examples_for 'a client that extracts hosts' do
404
+ context 'when decoded cloud id has a trailing dollar sign' do
405
+ let(:client) do
406
+ described_class.new(
407
+ cloud_id: 'a_cluster:bG9jYWxob3N0JGFiY2Qk',
408
+ user: 'elasticfantastic',
409
+ password: 'changeme'
410
+ )
411
+ end
327
412
 
328
- context 'when the hosts are a String' do
413
+ let(:hosts) do
414
+ client.transport.hosts
415
+ end
329
416
 
330
- let(:host) do
331
- 'myhost'
417
+ it 'extracts the cloud credentials' do
418
+ expect(hosts[0][:host]).to eq('abcd.localhost')
419
+ expect(hosts[0][:protocol]).to eq('https')
420
+ expect(hosts[0][:user]).to eq('elasticfantastic')
421
+ expect(hosts[0][:password]).to eq('changeme')
422
+ expect(hosts[0][:port]).to eq(443)
332
423
  end
333
424
 
334
- it 'extracts the host' do
335
- expect(hosts[0][:host]).to eq('myhost')
336
- expect(hosts[0][:protocol]).to eq('http')
337
- expect(hosts[0][:port]).to be(9200)
425
+ it 'creates the correct full url' do
426
+ expect(
427
+ client.transport.__full_url(client.transport.hosts[0])
428
+ ).to eq('https://elasticfantastic:changeme@abcd.localhost:443')
338
429
  end
430
+ end
339
431
 
340
- context 'when IPv6 format is used' do
432
+ context 'when the cloud host provides a port' do
433
+ let(:client) do
434
+ described_class.new(
435
+ cloud_id: 'name:ZWxhc3RpY19zZXJ2ZXI6OTI0MyRlbGFzdGljX2lk',
436
+ user: 'elastic',
437
+ password: 'changeme'
438
+ )
439
+ end
341
440
 
342
- around do |example|
343
- original_setting = Faraday.ignore_env_proxy
344
- Faraday.ignore_env_proxy = true
345
- example.run
346
- Faraday.ignore_env_proxy = original_setting
347
- end
441
+ let(:hosts) do
442
+ client.transport.hosts
443
+ end
348
444
 
349
- let(:host) do
350
- 'https://[2090:db8:85a3:9811::1f]:8080'
351
- end
445
+ it 'creates the correct full url' do
446
+ expect(hosts[0][:host]).to eq('elastic_id.elastic_server')
447
+ expect(hosts[0][:protocol]).to eq('https')
448
+ expect(hosts[0][:user]).to eq('elastic')
449
+ expect(hosts[0][:password]).to eq('changeme')
450
+ expect(hosts[0][:port]).to eq(9243)
451
+ end
452
+ end
352
453
 
353
- it 'extracts the host' do
354
- expect(hosts[0][:host]).to eq('[2090:db8:85a3:9811::1f]')
355
- expect(hosts[0][:scheme]).to eq('https')
356
- expect(hosts[0][:port]).to be(8080)
357
- end
454
+ context 'when the cloud host provides a port and the port is also specified' do
455
+ let(:client) do
456
+ described_class.new(
457
+ cloud_id: 'name:ZWxhc3RpY19zZXJ2ZXI6OTI0MyRlbGFzdGljX2lk',
458
+ user: 'elastic',
459
+ password: 'changeme',
460
+ port: 9200
461
+ )
462
+ end
358
463
 
359
- it 'creates the correct full url' do
360
- expect(client.transport.__full_url(client.transport.hosts[0])).to eq('https://[2090:db8:85a3:9811::1f]:8080')
361
- end
464
+ let(:hosts) do
465
+ client.transport.hosts
362
466
  end
363
467
 
364
- context 'when a path is specified' do
468
+ it 'creates the correct full url' do
469
+ expect(hosts[0][:host]).to eq('elastic_id.elastic_server')
470
+ expect(hosts[0][:protocol]).to eq('https')
471
+ expect(hosts[0][:user]).to eq('elastic')
472
+ expect(hosts[0][:password]).to eq('changeme')
473
+ expect(hosts[0][:port]).to eq(9243)
474
+ end
475
+ end
476
+ end
365
477
 
366
- let(:host) do
367
- 'https://myhost:8080/api'
368
- end
478
+ shared_examples_for 'a client that extracts hosts' do
369
479
 
370
- it 'extracts the host' do
371
- expect(hosts[0][:host]).to eq('myhost')
372
- expect(hosts[0][:scheme]).to eq('https')
373
- expect(hosts[0][:path]).to eq('/api')
374
- expect(hosts[0][:port]).to be(8080)
375
- end
376
- end
480
+ context 'when the host is a String' do
377
481
 
378
- context 'when a scheme is specified' do
482
+ context 'when there is a protocol specified' do
379
483
 
380
- let(:host) do
381
- 'https://myhost:8080'
382
- end
484
+ context 'when credentials are specified \'http://USERNAME:PASSWORD@myhost:8080\'' do
383
485
 
384
- it 'extracts the host' do
385
- expect(hosts[0][:host]).to eq('myhost')
386
- expect(hosts[0][:scheme]).to eq('https')
387
- expect(hosts[0][:port]).to be(8080)
486
+ let(:host) do
487
+ 'http://USERNAME:PASSWORD@myhost:8080'
488
+ end
489
+
490
+ it 'extracts the credentials' do
491
+ expect(hosts[0][:user]).to eq('USERNAME')
492
+ expect(hosts[0][:password]).to eq('PASSWORD')
493
+ end
494
+
495
+ it 'extracts the host' do
496
+ expect(hosts[0][:host]).to eq('myhost')
497
+ end
498
+
499
+ it 'extracts the port' do
500
+ expect(hosts[0][:port]).to be(8080)
501
+ end
388
502
  end
389
- end
390
503
 
391
- context 'when credentials are specified' do
504
+ context 'when there is a trailing slash \'http://myhost/\'' do
392
505
 
393
- let(:host) do
394
- 'http://USERNAME:PASSWORD@myhost:8080'
506
+ let(:host) do
507
+ 'http://myhost/'
508
+ end
509
+
510
+ it 'extracts the host' do
511
+ expect(hosts[0][:host]).to eq('myhost')
512
+ expect(hosts[0][:scheme]).to eq('http')
513
+ expect(hosts[0][:path]).to eq('')
514
+ end
515
+
516
+ it 'extracts the scheme' do
517
+ expect(hosts[0][:scheme]).to eq('http')
518
+ end
519
+
520
+ it 'extracts the path' do
521
+ expect(hosts[0][:path]).to eq('')
522
+ end
395
523
  end
396
524
 
397
- it 'extracts the host' do
398
- expect(hosts[0][:host]).to eq('myhost')
399
- expect(hosts[0][:scheme]).to eq('http')
400
- expect(hosts[0][:user]).to eq('USERNAME')
401
- expect(hosts[0][:password]).to eq('PASSWORD')
402
- expect(hosts[0][:port]).to be(8080)
525
+ context 'when there is a trailing slash with a path \'http://myhost/foo/bar/\'' do
526
+
527
+ let(:host) do
528
+ 'http://myhost/foo/bar/'
529
+ end
530
+
531
+ it 'extracts the host' do
532
+ expect(hosts[0][:host]).to eq('myhost')
533
+ expect(hosts[0][:scheme]).to eq('http')
534
+ expect(hosts[0][:path]).to eq('/foo/bar')
535
+ end
403
536
  end
404
- end
405
537
 
406
- context 'when there is a trailing slash' do
538
+ context 'when the protocol is http' do
407
539
 
408
- let(:host) do
409
- 'http://myhost/'
540
+ context 'when there is no port specified \'http://myhost\'' do
541
+
542
+ let(:host) do
543
+ 'http://myhost'
544
+ end
545
+
546
+ it 'extracts the host' do
547
+ expect(hosts[0][:host]).to eq('myhost')
548
+ end
549
+
550
+ it 'extracts the protocol' do
551
+ expect(hosts[0][:protocol]).to eq('http')
552
+ end
553
+
554
+ it 'defaults to port 9200' do
555
+ expect(hosts[0][:port]).to be(9200)
556
+ end
557
+ end
558
+
559
+ context 'when there is a port specified \'http://myhost:7101\'' do
560
+
561
+ let(:host) do
562
+ 'http://myhost:7101'
563
+ end
564
+
565
+ it 'extracts the host' do
566
+ expect(hosts[0][:host]).to eq('myhost')
567
+ end
568
+
569
+ it 'extracts the protocol' do
570
+ expect(hosts[0][:protocol]).to eq('http')
571
+ end
572
+
573
+ it 'extracts the port' do
574
+ expect(hosts[0][:port]).to be(7101)
575
+ end
576
+
577
+ context 'when there is a path specified \'http://myhost:7101/api\'' do
578
+
579
+ let(:host) do
580
+ 'http://myhost:7101/api'
581
+ end
582
+
583
+ it 'sets the path' do
584
+ expect(hosts[0][:host]).to eq('myhost')
585
+ expect(hosts[0][:protocol]).to eq('http')
586
+ expect(hosts[0][:path]).to eq('/api')
587
+ expect(hosts[0][:port]).to be(7101)
588
+ end
589
+
590
+ it 'extracts the host' do
591
+ expect(hosts[0][:host]).to eq('myhost')
592
+ end
593
+
594
+ it 'extracts the protocol' do
595
+ expect(hosts[0][:protocol]).to eq('http')
596
+ end
597
+
598
+ it 'extracts the port' do
599
+ expect(hosts[0][:port]).to be(7101)
600
+ end
601
+
602
+ it 'extracts the path' do
603
+ expect(hosts[0][:path]).to eq('/api')
604
+ end
605
+ end
606
+ end
410
607
  end
411
608
 
412
- it 'extracts the host' do
413
- expect(hosts[0][:host]).to eq('myhost')
414
- expect(hosts[0][:scheme]).to eq('http')
415
- expect(hosts[0][:path]).to eq('')
609
+ context 'when the protocol is https' do
610
+
611
+ context 'when there is no port specified \'https://myhost\'' do
612
+
613
+ let(:host) do
614
+ 'https://myhost'
615
+ end
616
+
617
+ it 'extracts the host' do
618
+ expect(hosts[0][:host]).to eq('myhost')
619
+ end
620
+
621
+ it 'extracts the protocol' do
622
+ expect(hosts[0][:protocol]).to eq('https')
623
+ end
624
+
625
+ it 'defaults to port 443' do
626
+ expect(hosts[0][:port]).to be(443)
627
+ end
628
+ end
629
+
630
+ context 'when there is a port specified \'https://myhost:7101\'' do
631
+
632
+ let(:host) do
633
+ 'https://myhost:7101'
634
+ end
635
+
636
+ it 'extracts the host' do
637
+ expect(hosts[0][:host]).to eq('myhost')
638
+ end
639
+
640
+ it 'extracts the protocol' do
641
+ expect(hosts[0][:protocol]).to eq('https')
642
+ end
643
+
644
+ it 'extracts the port' do
645
+ expect(hosts[0][:port]).to be(7101)
646
+ end
647
+
648
+ context 'when there is a path specified \'https://myhost:7101/api\'' do
649
+
650
+ let(:host) do
651
+ 'https://myhost:7101/api'
652
+ end
653
+
654
+ it 'extracts the host' do
655
+ expect(hosts[0][:host]).to eq('myhost')
656
+ end
657
+
658
+ it 'extracts the protocol' do
659
+ expect(hosts[0][:protocol]).to eq('https')
660
+ end
661
+
662
+ it 'extracts the port' do
663
+ expect(hosts[0][:port]).to be(7101)
664
+ end
665
+
666
+ it 'extracts the path' do
667
+ expect(hosts[0][:path]).to eq('/api')
668
+ end
669
+ end
670
+ end
671
+
672
+ context 'when IPv6 format is used' do
673
+
674
+ around do |example|
675
+ original_setting = Faraday.ignore_env_proxy
676
+ Faraday.ignore_env_proxy = true
677
+ example.run
678
+ Faraday.ignore_env_proxy = original_setting
679
+ end
680
+
681
+ let(:host) do
682
+ 'https://[2090:db8:85a3:9811::1f]:8080'
683
+ end
684
+
685
+ it 'extracts the host' do
686
+ expect(hosts[0][:host]).to eq('[2090:db8:85a3:9811::1f]')
687
+ end
688
+
689
+ it 'extracts the protocol' do
690
+ expect(hosts[0][:protocol]).to eq('https')
691
+ end
692
+
693
+ it 'extracts the port' do
694
+ expect(hosts[0][:port]).to be(8080)
695
+ end
696
+
697
+ it 'creates the correct full url' do
698
+ expect(client.transport.__full_url(client.transport.hosts[0])).to eq('https://[2090:db8:85a3:9811::1f]:8080')
699
+ end
700
+ end
416
701
  end
417
702
  end
418
703
 
419
- context 'when there is a trailing slash with a path' do
704
+ context 'when no protocol is specified \'myhost\'' do
420
705
 
421
706
  let(:host) do
422
- 'http://myhost/foo/bar/'
707
+ 'myhost'
423
708
  end
424
709
 
425
- it 'extracts the host' do
710
+ it 'defaults to http' do
426
711
  expect(hosts[0][:host]).to eq('myhost')
427
- expect(hosts[0][:scheme]).to eq('http')
428
- expect(hosts[0][:path]).to eq('/foo/bar')
712
+ expect(hosts[0][:protocol]).to eq('http')
713
+ end
714
+
715
+ it 'uses port 9200' do
716
+ expect(hosts[0][:port]).to be(9200)
429
717
  end
430
718
  end
431
719
  end
432
720
 
433
- context 'when the hosts are a Hash' do
721
+ context 'when the host is a Hash' do
434
722
 
435
723
  let(:host) do
436
724
  { :host => 'myhost', :scheme => 'https' }
@@ -438,7 +726,13 @@ describe Elasticsearch::Transport::Client do
438
726
 
439
727
  it 'extracts the host' do
440
728
  expect(hosts[0][:host]).to eq('myhost')
441
- expect(hosts[0][:scheme]).to eq('https')
729
+ end
730
+
731
+ it 'extracts the protocol' do
732
+ expect(hosts[0][:protocol]).to eq('https')
733
+ end
734
+
735
+ it 'extracts the port' do
442
736
  expect(hosts[0][:port]).to be(9200)
443
737
  end
444
738
 
@@ -497,7 +791,13 @@ describe Elasticsearch::Transport::Client do
497
791
 
498
792
  it 'extracts the host' do
499
793
  expect(hosts[0][:host]).to eq('myhost')
794
+ end
795
+
796
+ it 'extracts the protocol' do
500
797
  expect(hosts[0][:scheme]).to eq('https')
798
+ end
799
+
800
+ it 'converts the port to an integer' do
501
801
  expect(hosts[0][:port]).to be(443)
502
802
  end
503
803
  end
@@ -510,7 +810,13 @@ describe Elasticsearch::Transport::Client do
510
810
 
511
811
  it 'extracts the host' do
512
812
  expect(hosts[0][:host]).to eq('myhost')
813
+ end
814
+
815
+ it 'extracts the protocol' do
513
816
  expect(hosts[0][:scheme]).to eq('https')
817
+ end
818
+
819
+ it 'extracts port as an integer' do
514
820
  expect(hosts[0][:port]).to be(443)
515
821
  end
516
822
  end
@@ -524,7 +830,13 @@ describe Elasticsearch::Transport::Client do
524
830
 
525
831
  it 'extracts the host' do
526
832
  expect(hosts[0][:host]).to eq('myhost')
833
+ end
834
+
835
+ it 'extracts the protocol' do
527
836
  expect(hosts[0][:scheme]).to eq('https')
837
+ end
838
+
839
+ it 'converts the port to an integer' do
528
840
  expect(hosts[0][:port]).to be(9200)
529
841
  end
530
842
 
@@ -536,7 +848,13 @@ describe Elasticsearch::Transport::Client do
536
848
 
537
849
  it 'extracts the host' do
538
850
  expect(hosts[0][:host]).to eq('myhost')
851
+ end
852
+
853
+ it 'extracts the protocol' do
539
854
  expect(hosts[0][:scheme]).to eq('https')
855
+ end
856
+
857
+ it 'converts the port to an integer' do
540
858
  expect(hosts[0][:port]).to be(443)
541
859
  end
542
860
  end
@@ -549,7 +867,13 @@ describe Elasticsearch::Transport::Client do
549
867
 
550
868
  it 'extracts the host' do
551
869
  expect(hosts[0][:host]).to eq('myhost')
870
+ end
871
+
872
+ it 'extracts the protocol' do
552
873
  expect(hosts[0][:scheme]).to eq('https')
874
+ end
875
+
876
+ it 'extracts port as an integer' do
553
877
  expect(hosts[0][:port]).to be(443)
554
878
  end
555
879
  end
@@ -565,7 +889,13 @@ describe Elasticsearch::Transport::Client do
565
889
 
566
890
  it 'extracts the host' do
567
891
  expect(hosts[0][:host]).to eq('myhost')
892
+ end
893
+
894
+ it 'extracts the protocol' do
568
895
  expect(hosts[0][:protocol]).to eq('http')
896
+ end
897
+
898
+ it 'defaults to port 9200' do
569
899
  expect(hosts[0][:port]).to be(9200)
570
900
  end
571
901
  end
@@ -578,20 +908,13 @@ describe Elasticsearch::Transport::Client do
578
908
 
579
909
  it 'extracts the host' do
580
910
  expect(hosts[0][:host]).to eq('myhost')
581
- expect(hosts[0][:protocol]).to eq('http')
582
- expect(hosts[0][:port]).to be(9200)
583
911
  end
584
- end
585
912
 
586
- context 'when there is one host with a protocol and no port' do
587
-
588
- let(:host) do
589
- ['http://myhost']
913
+ it 'extracts the protocol' do
914
+ expect(hosts[0][:scheme]).to eq('http')
590
915
  end
591
916
 
592
- it 'extracts the host' do
593
- expect(hosts[0][:host]).to eq('myhost')
594
- expect(hosts[0][:protocol]).to eq('http')
917
+ it 'defaults to port 9200' do
595
918
  expect(hosts[0][:port]).to be(9200)
596
919
  end
597
920
  end
@@ -616,7 +939,7 @@ describe Elasticsearch::Transport::Client do
616
939
  end
617
940
  end
618
941
 
619
- context 'when there is one host with a scheme, protocol and no port' do
942
+ context 'when there is one host with a protocol and no port' do
620
943
 
621
944
  let(:host) do
622
945
  ['https://myhost']
@@ -624,12 +947,18 @@ describe Elasticsearch::Transport::Client do
624
947
 
625
948
  it 'extracts the host' do
626
949
  expect(hosts[0][:host]).to eq('myhost')
627
- expect(hosts[0][:protocol]).to eq('https')
628
- expect(hosts[0][:port]).to be(9200)
950
+ end
951
+
952
+ it 'extracts the protocol' do
953
+ expect(hosts[0][:scheme]).to eq('https')
954
+ end
955
+
956
+ it 'defaults to port 443' do
957
+ expect(hosts[0][:port]).to be(443)
629
958
  end
630
959
  end
631
960
 
632
- context 'when there is one host with a scheme, protocol, path, and no port' do
961
+ context 'when there is one host with a protocol, path, and no port' do
633
962
 
634
963
  let(:host) do
635
964
  ['http://myhost/foo/bar']
@@ -637,9 +966,18 @@ describe Elasticsearch::Transport::Client do
637
966
 
638
967
  it 'extracts the host' do
639
968
  expect(hosts[0][:host]).to eq('myhost')
640
- expect(hosts[0][:protocol]).to eq('http')
969
+ end
970
+
971
+ it 'extracts the protocol' do
972
+ expect(hosts[0][:scheme]).to eq('http')
973
+ end
974
+
975
+ it 'defaults to port 9200' do
641
976
  expect(hosts[0][:port]).to be(9200)
642
- expect(hosts[0][:path]).to eq("/foo/bar")
977
+ end
978
+
979
+ it 'extracts the path' do
980
+ expect(hosts[0][:path]).to eq('/foo/bar')
643
981
  end
644
982
  end
645
983
 
@@ -649,7 +987,7 @@ describe Elasticsearch::Transport::Client do
649
987
  ['host1', 'host2']
650
988
  end
651
989
 
652
- it 'extracts the host' do
990
+ it 'extracts the hosts' do
653
991
  expect(hosts[0][:host]).to eq('host1')
654
992
  expect(hosts[0][:protocol]).to eq('http')
655
993
  expect(hosts[0][:port]).to be(9200)
@@ -665,7 +1003,7 @@ describe Elasticsearch::Transport::Client do
665
1003
  ['host1:1000', 'host2:2000']
666
1004
  end
667
1005
 
668
- it 'extracts the host' do
1006
+ it 'extracts the hosts' do
669
1007
  expect(hosts[0][:host]).to eq('host1')
670
1008
  expect(hosts[0][:protocol]).to eq('http')
671
1009
  expect(hosts[0][:port]).to be(1000)
@@ -1055,10 +1393,142 @@ describe Elasticsearch::Transport::Client do
1055
1393
  expect(request).to be(true)
1056
1394
  end
1057
1395
  end
1396
+
1397
+ context 'when x-opaque-id is set' do
1398
+ let(:client) { described_class.new(host: hosts) }
1399
+
1400
+ it 'uses x-opaque-id on a request' do
1401
+ expect(client.perform_request('GET', '/', { opaque_id: '12345' }).headers['x-opaque-id']).to eq('12345')
1402
+ end
1403
+ end
1404
+
1405
+ context 'when an x-opaque-id prefix is set on initialization' do
1406
+ let(:prefix) { 'elastic_cloud' }
1407
+ let(:client) do
1408
+ described_class.new(host: hosts, opaque_id_prefix: prefix)
1409
+ end
1410
+
1411
+ it 'uses x-opaque-id on a request' do
1412
+ expect(client.perform_request('GET', '/', { opaque_id: '12345' }).headers['x-opaque-id']).to eq("#{prefix}12345")
1413
+ end
1414
+
1415
+ context 'when using an API call' do
1416
+ let(:client) { described_class.new(host: hosts) }
1417
+
1418
+ it 'doesnae raise an ArgumentError' do
1419
+ expect { client.search(opaque_id: 'no_error') }.not_to raise_error
1420
+ end
1421
+
1422
+ it 'uses X-Opaque-Id in the header' do
1423
+ allow(client).to receive(:perform_request) { OpenStruct.new(body: '') }
1424
+ expect { client.search(opaque_id: 'opaque_id') }.not_to raise_error
1425
+ expect(client).to have_received(:perform_request)
1426
+ .with('GET', '_search', { opaque_id: 'opaque_id' }, nil, {})
1427
+ end
1428
+ end
1429
+ end
1430
+
1431
+ context 'when using the API Compatibility Header' do
1432
+ it 'sets the API compatibility headers' do
1433
+ ENV['ELASTIC_CLIENT_APIVERSIONING'] = 'true'
1434
+ client = described_class.new(host: hosts)
1435
+ headers = client.transport.connections.first.connection.headers
1436
+
1437
+ expect(headers['Content-Type']).to eq('application/vnd.elasticsearch+json; compatible-with=7')
1438
+ expect(headers['Accept']).to eq('application/vnd.elasticsearch+json;compatible-with=7')
1439
+
1440
+ response = client.perform_request('GET', '/')
1441
+ expect(response.headers['content-type']).to eq('application/json; charset=UTF-8')
1442
+
1443
+ ENV.delete('ELASTIC_CLIENT_APIVERSIONING')
1444
+ end
1445
+
1446
+ it 'does not use API compatibility headers' do
1447
+ val = ENV.delete('ELASTIC_CLIENT_APIVERSIONING')
1448
+ client = described_class.new(host: hosts)
1449
+ expect(client.transport.connections.first.connection.headers['Content-Type']).to eq('application/json')
1450
+ ENV['ELASTIC_CLIENT_APIVERSIONING'] = val
1451
+ end
1452
+
1453
+ it 'does not use API compatibility headers when it is set to unsupported values' do
1454
+ val = ENV.delete('ELASTIC_CLIENT_APIVERSIONING')
1455
+
1456
+ ENV['ELASTIC_CLIENT_APIVERSIONING'] = 'test'
1457
+ client = described_class.new(host: hosts)
1458
+ expect(client.transport.connections.first.connection.headers['Content-Type']).to eq('application/json')
1459
+
1460
+ ENV['ELASTIC_CLIENT_APIVERSIONING'] = 'false'
1461
+ client = described_class.new(host: hosts)
1462
+ expect(client.transport.connections.first.connection.headers['Content-Type']).to eq('application/json')
1463
+
1464
+ ENV['ELASTIC_CLIENT_APIVERSIONING'] = '3'
1465
+ client = described_class.new(host: hosts)
1466
+ expect(client.transport.connections.first.connection.headers['Content-Type']).to eq('application/json')
1467
+ ENV['ELASTIC_CLIENT_APIVERSIONING'] = val
1468
+ end
1469
+ end
1470
+
1471
+ context 'when Elasticsearch response includes a warning header' do
1472
+ let(:client) do
1473
+ Elasticsearch::Transport::Client.new(hosts: hosts)
1474
+ end
1475
+
1476
+ let(:warning) { 'Elasticsearch warning: "deprecation warning"' }
1477
+
1478
+ it 'prints a warning' do
1479
+ allow_any_instance_of(Elasticsearch::Transport::Transport::Response).to receive(:headers) do
1480
+ { 'warning' => warning }
1481
+ end
1482
+
1483
+ begin
1484
+ stderr = $stderr
1485
+ fake_stderr = StringIO.new
1486
+ $stderr = fake_stderr
1487
+
1488
+ client.perform_request('GET', '/')
1489
+ fake_stderr.rewind
1490
+ expect(fake_stderr.string).to eq("warning: #{warning}\n")
1491
+ ensure
1492
+ $stderr = stderr
1493
+ end
1494
+ end
1495
+ end
1496
+
1497
+ context 'when a header is set on an endpoint request' do
1498
+ let(:client) { described_class.new(host: hosts) }
1499
+ let(:headers) { { 'user-agent' => 'my ruby app' } }
1500
+
1501
+ it 'performs the request with the header' do
1502
+ allow(client).to receive(:perform_request) { OpenStruct.new(body: '') }
1503
+ expect { client.search(headers: headers) }.not_to raise_error
1504
+ expect(client).to have_received(:perform_request)
1505
+ .with('GET', '_search', {}, nil, headers)
1506
+ end
1507
+ end
1508
+
1509
+ context 'when a header is set on an endpoint request and on initialization' do
1510
+ let!(:client) do
1511
+ described_class.new(
1512
+ host: hosts,
1513
+ transport_options: { headers: instance_headers }
1514
+ )
1515
+ end
1516
+ let(:instance_headers) { { set_in_instantiation: 'header value' } }
1517
+ let(:param_headers) {{'user-agent' => 'My Ruby Tests', 'set-on-method-call' => 'header value'}}
1518
+
1519
+ it 'performs the request with the header' do
1520
+ expected_headers = client.transport.connections.connections.first.connection.headers.merge(param_headers)
1521
+
1522
+ expect_any_instance_of(Faraday::Connection)
1523
+ .to receive(:run_request)
1524
+ .with(:get, "http://#{hosts[0]}/_search", nil, expected_headers) { OpenStruct.new(body: '')}
1525
+
1526
+ client.search(headers: param_headers)
1527
+ end
1528
+ end
1058
1529
  end
1059
1530
 
1060
1531
  context 'when the client connects to Elasticsearch' do
1061
-
1062
1532
  let(:logger) do
1063
1533
  Logger.new(STDERR).tap do |logger|
1064
1534
  logger.formatter = proc do |severity, datetime, progname, msg|
@@ -1136,15 +1606,14 @@ describe Elasticsearch::Transport::Client do
1136
1606
  end
1137
1607
 
1138
1608
  context 'when the Faraday adapter is set in the block' do
1139
-
1140
1609
  let(:client) do
1141
1610
  Elasticsearch::Client.new(host: ELASTICSEARCH_HOSTS.first, logger: logger) do |client|
1142
1611
  client.adapter(:net_http_persistent)
1143
1612
  end
1144
1613
  end
1145
1614
 
1146
- let(:connection_handler) do
1147
- client.transport.connections.first.connection.builder.handlers.first
1615
+ let(:handler_name) do
1616
+ client.transport.connections.first.connection.builder.adapter.name
1148
1617
  end
1149
1618
 
1150
1619
  let(:response) do
@@ -1152,7 +1621,7 @@ describe Elasticsearch::Transport::Client do
1152
1621
  end
1153
1622
 
1154
1623
  it 'sets the adapter' do
1155
- expect(connection_handler.name).to eq('Faraday::Adapter::NetHttpPersistent')
1624
+ expect(handler_name).to eq('Faraday::Adapter::NetHttpPersistent')
1156
1625
  end
1157
1626
 
1158
1627
  it 'uses the adapter to connect' do
@@ -1202,7 +1671,7 @@ describe Elasticsearch::Transport::Client do
1202
1671
  expect(client.perform_request('GET', '_nodes/_local'))
1203
1672
  expect {
1204
1673
  client.perform_request('GET', '_nodes/_local')
1205
- }.to raise_exception(Faraday::Error::ConnectionFailed)
1674
+ }.to raise_exception(Faraday::ConnectionFailed)
1206
1675
  end
1207
1676
  end
1208
1677
 
@@ -1276,7 +1745,7 @@ describe Elasticsearch::Transport::Client do
1276
1745
  context 'when using the HTTPClient adapter' do
1277
1746
 
1278
1747
  let(:client) do
1279
- described_class.new(hosts: ELASTICSEARCH_HOSTS, compression: true, adapter: :httpclient)
1748
+ described_class.new(hosts: ELASTICSEARCH_HOSTS, compression: true, adapter: :httpclient, enable_meta_header: false)
1280
1749
  end
1281
1750
 
1282
1751
  it 'compresses the request and decompresses the response' do
@@ -1347,7 +1816,7 @@ describe Elasticsearch::Transport::Client do
1347
1816
  it 'preserves the other headers' do
1348
1817
  expect(client.transport.connections[0].connection.headers['User-Agent'])
1349
1818
  end
1350
- end
1819
+ end unless jruby?
1351
1820
  end
1352
1821
  end
1353
1822
 
@@ -1486,12 +1955,39 @@ describe Elasticsearch::Transport::Client do
1486
1955
  { adapter: :patron }
1487
1956
  end
1488
1957
 
1489
- let(:connection_handler) do
1490
- client.transport.connections.first.connection.builder.handlers.first
1958
+ let(:adapter) do
1959
+ client.transport.connections.first.connection.builder.adapter
1960
+ end
1961
+
1962
+ it 'uses the patron connection handler' do
1963
+ expect(adapter).to eq('Faraday::Adapter::Patron')
1964
+ end
1965
+
1966
+ it 'keeps connections open' do
1967
+ response = client.perform_request('GET', '_nodes/stats/http')
1968
+ connections_before = response.body['nodes'].values.find { |n| n['name'] == node_names.first }['http']['total_opened']
1969
+ client.transport.reload_connections!
1970
+ response = client.perform_request('GET', '_nodes/stats/http')
1971
+ connections_after = response.body['nodes'].values.find { |n| n['name'] == node_names.first }['http']['total_opened']
1972
+ expect(connections_after).to be >= (connections_before)
1973
+ end
1974
+ end
1975
+
1976
+ context 'when typhoeus is used as an adapter', unless: jruby? do
1977
+ before do
1978
+ require 'typhoeus'
1979
+ end
1980
+
1981
+ let(:options) do
1982
+ { adapter: :typhoeus }
1983
+ end
1984
+
1985
+ let(:adapter) do
1986
+ client.transport.connections.first.connection.builder.adapter
1491
1987
  end
1492
1988
 
1493
1989
  it 'uses the patron connection handler' do
1494
- expect(connection_handler).to eq('Faraday::Adapter::Patron')
1990
+ expect(adapter).to eq('Faraday::Adapter::Typhoeus')
1495
1991
  end
1496
1992
 
1497
1993
  it 'keeps connections open' do