elasticsearch-transport 7.5.0 → 7.13.3

Sign up to get free protection for your applications and to get access to all the features.
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