vault_ruby_client 0.18.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +287 -0
  3. data/LICENSE +364 -0
  4. data/README.md +223 -0
  5. data/lib/vault/api/approle.rb +221 -0
  6. data/lib/vault/api/auth.rb +324 -0
  7. data/lib/vault/api/auth_tls.rb +95 -0
  8. data/lib/vault/api/auth_token.rb +245 -0
  9. data/lib/vault/api/help.rb +36 -0
  10. data/lib/vault/api/kv.rb +230 -0
  11. data/lib/vault/api/logical.rb +153 -0
  12. data/lib/vault/api/secret.rb +171 -0
  13. data/lib/vault/api/sys/audit.rb +94 -0
  14. data/lib/vault/api/sys/auth.rb +119 -0
  15. data/lib/vault/api/sys/health.rb +66 -0
  16. data/lib/vault/api/sys/init.rb +86 -0
  17. data/lib/vault/api/sys/leader.rb +51 -0
  18. data/lib/vault/api/sys/lease.rb +52 -0
  19. data/lib/vault/api/sys/mount.rb +165 -0
  20. data/lib/vault/api/sys/namespace.rb +86 -0
  21. data/lib/vault/api/sys/policy.rb +95 -0
  22. data/lib/vault/api/sys/quota.rb +110 -0
  23. data/lib/vault/api/sys/seal.rb +84 -0
  24. data/lib/vault/api/sys.rb +30 -0
  25. data/lib/vault/api/transform/alphabet.rb +46 -0
  26. data/lib/vault/api/transform/role.rb +45 -0
  27. data/lib/vault/api/transform/template.rb +57 -0
  28. data/lib/vault/api/transform/transformation.rb +64 -0
  29. data/lib/vault/api/transform.rb +32 -0
  30. data/lib/vault/api.rb +17 -0
  31. data/lib/vault/client.rb +460 -0
  32. data/lib/vault/configurable.rb +53 -0
  33. data/lib/vault/defaults.rb +218 -0
  34. data/lib/vault/encode.rb +22 -0
  35. data/lib/vault/errors.rb +87 -0
  36. data/lib/vault/persistent/connection.rb +45 -0
  37. data/lib/vault/persistent/pool.rb +51 -0
  38. data/lib/vault/persistent/timed_stack_multi.rb +73 -0
  39. data/lib/vault/persistent.rb +1161 -0
  40. data/lib/vault/request.rb +47 -0
  41. data/lib/vault/response.rb +92 -0
  42. data/lib/vault/vendor/connection_pool/timed_stack.rb +181 -0
  43. data/lib/vault/vendor/connection_pool/version.rb +8 -0
  44. data/lib/vault/vendor/connection_pool.rb +153 -0
  45. data/lib/vault/version.rb +6 -0
  46. data/lib/vault_ruby_client.rb +53 -0
  47. metadata +158 -0
@@ -0,0 +1,1161 @@
1
+ # Copyright (c) HashiCorp, Inc.
2
+ # SPDX-License-Identifier: MPL-2.0
3
+
4
+ # Vendored and modified from github.com/drbrain/net-http-persistent
5
+ #
6
+ require 'net/http'
7
+ require 'uri'
8
+ require 'cgi' # for escaping
9
+ require 'vault/vendor/connection_pool'
10
+
11
+ begin
12
+ require 'net/http/pipeline'
13
+ rescue LoadError
14
+ end
15
+
16
+ autoload :OpenSSL, 'openssl'
17
+
18
+ ##
19
+ # Persistent connections for Net::HTTP
20
+ #
21
+ # PersistentHTTP maintains persistent connections across all the
22
+ # servers you wish to talk to. For each host:port you communicate with a
23
+ # single persistent connection is created.
24
+ #
25
+ # Multiple PersistentHTTP objects will share the same set of
26
+ # connections.
27
+ #
28
+ # For each thread you start a new connection will be created. A
29
+ # PersistentHTTP connection will not be shared across threads.
30
+ #
31
+ # You can shut down the HTTP connections when done by calling #shutdown. You
32
+ # should name your PersistentHTTP object if you intend to call this
33
+ # method.
34
+ #
35
+ # Example:
36
+ #
37
+ # require 'net/http/persistent'
38
+ #
39
+ # uri = URI 'http://example.com/awesome/web/service'
40
+ #
41
+ # http = PersistentHTTP.new 'my_app_name'
42
+ #
43
+ # # perform a GET
44
+ # response = http.request uri
45
+ #
46
+ # # or
47
+ #
48
+ # get = Net::HTTP::Get.new uri.request_uri
49
+ # response = http.request get
50
+ #
51
+ # # create a POST
52
+ # post_uri = uri + 'create'
53
+ # post = Net::HTTP::Post.new post_uri.path
54
+ # post.set_form_data 'some' => 'cool data'
55
+ #
56
+ # # perform the POST, the URI is always required
57
+ # response http.request post_uri, post
58
+ #
59
+ # Note that for GET, HEAD and other requests that do not have a body you want
60
+ # to use URI#request_uri not URI#path. The request_uri contains the query
61
+ # params which are sent in the body for other requests.
62
+ #
63
+ # == SSL
64
+ #
65
+ # SSL connections are automatically created depending upon the scheme of the
66
+ # URI. SSL connections are automatically verified against the default
67
+ # certificate store for your computer. You can override this by changing
68
+ # verify_mode or by specifying an alternate cert_store.
69
+ #
70
+ # Here are the SSL settings, see the individual methods for documentation:
71
+ #
72
+ # #certificate :: This client's certificate
73
+ # #ca_file :: The certificate-authorities
74
+ # #ca_path :: Directory with certificate-authorities
75
+ # #cert_store :: An SSL certificate store
76
+ # #ciphers :: List of SSl ciphers allowed
77
+ # #min_version :: Minimum SSL version to use
78
+ # #private_key :: The client's SSL private key
79
+ # #reuse_ssl_sessions :: Reuse a previously opened SSL session for a new
80
+ # connection
81
+ # #ssl_timeout :: SSL session lifetime
82
+ # #verify_callback :: For server certificate verification
83
+ # #verify_depth :: Depth of certificate verification
84
+ # #verify_mode :: How connections should be verified
85
+ #
86
+ # == Proxies
87
+ #
88
+ # A proxy can be set through #proxy= or at initialization time by providing a
89
+ # second argument to ::new. The proxy may be the URI of the proxy server or
90
+ # <code>:ENV</code> which will consult environment variables.
91
+ #
92
+ # See #proxy= and #proxy_from_env for details.
93
+ #
94
+ # == Headers
95
+ #
96
+ # Headers may be specified for use in every request. #headers are appended to
97
+ # any headers on the request. #override_headers replace existing headers on
98
+ # the request.
99
+ #
100
+ # The difference between the two can be seen in setting the User-Agent. Using
101
+ # <code>http.headers['User-Agent'] = 'MyUserAgent'</code> will send "Ruby,
102
+ # MyUserAgent" while <code>http.override_headers['User-Agent'] =
103
+ # 'MyUserAgent'</code> will send "MyUserAgent".
104
+ #
105
+ # == Tuning
106
+ #
107
+ # === Segregation
108
+ #
109
+ # By providing an application name to ::new you can separate your connections
110
+ # from the connections of other applications.
111
+ #
112
+ # === Idle Timeout
113
+ #
114
+ # If a connection hasn't been used for this number of seconds it will automatically be
115
+ # reset upon the next use to avoid attempting to send to a closed connection.
116
+ # The default value is 5 seconds. nil means no timeout. Set through #idle_timeout.
117
+ #
118
+ # Reducing this value may help avoid the "too many connection resets" error
119
+ # when sending non-idempotent requests while increasing this value will cause
120
+ # fewer round-trips.
121
+ #
122
+ # === Read Timeout
123
+ #
124
+ # The amount of time allowed between reading two chunks from the socket. Set
125
+ # through #read_timeout
126
+ #
127
+ # === Max Requests
128
+ #
129
+ # The number of requests that should be made before opening a new connection.
130
+ # Typically many keep-alive capable servers tune this to 100 or less, so the
131
+ # 101st request will fail with ECONNRESET. If unset (default), this value has no
132
+ # effect, if set, connections will be reset on the request after max_requests.
133
+ #
134
+ # === Open Timeout
135
+ #
136
+ # The amount of time to wait for a connection to be opened. Set through
137
+ # #open_timeout.
138
+ #
139
+ # === Socket Options
140
+ #
141
+ # Socket options may be set on newly-created connections. See #socket_options
142
+ # for details.
143
+ #
144
+ # === Non-Idempotent Requests
145
+ #
146
+ # By default non-idempotent requests will not be retried per RFC 2616. By
147
+ # setting retry_change_requests to true requests will automatically be retried
148
+ # once.
149
+ #
150
+ # Only do this when you know that retrying a POST or other non-idempotent
151
+ # request is safe for your application and will not create duplicate
152
+ # resources.
153
+ #
154
+ # The recommended way to handle non-idempotent requests is the following:
155
+ #
156
+ # require 'net/http/persistent'
157
+ #
158
+ # uri = URI 'http://example.com/awesome/web/service'
159
+ # post_uri = uri + 'create'
160
+ #
161
+ # http = PersistentHTTP.new 'my_app_name'
162
+ #
163
+ # post = Net::HTTP::Post.new post_uri.path
164
+ # # ... fill in POST request
165
+ #
166
+ # begin
167
+ # response = http.request post_uri, post
168
+ # rescue PersistentHTTP::Error
169
+ #
170
+ # # POST failed, make a new request to verify the server did not process
171
+ # # the request
172
+ # exists_uri = uri + '...'
173
+ # response = http.get exists_uri
174
+ #
175
+ # # Retry if it failed
176
+ # retry if response.code == '404'
177
+ # end
178
+ #
179
+ # The method of determining if the resource was created or not is unique to
180
+ # the particular service you are using. Of course, you will want to add
181
+ # protection from infinite looping.
182
+ #
183
+ # === Connection Termination
184
+ #
185
+ # If you are done using the PersistentHTTP instance you may shut down
186
+ # all the connections in the current thread with #shutdown. This is not
187
+ # recommended for normal use, it should only be used when it will be several
188
+ # minutes before you make another HTTP request.
189
+ #
190
+ # If you are using multiple threads, call #shutdown in each thread when the
191
+ # thread is done making requests. If you don't call shutdown, that's OK.
192
+ # Ruby will automatically garbage collect and shutdown your HTTP connections
193
+ # when the thread terminates.
194
+
195
+ module Vault
196
+ class PersistentHTTP
197
+
198
+ ##
199
+ # The beginning of Time
200
+
201
+ EPOCH = Time.at 0 # :nodoc:
202
+
203
+ ##
204
+ # Is OpenSSL available? This test works with autoload
205
+
206
+ HAVE_OPENSSL = defined? OpenSSL::SSL # :nodoc:
207
+
208
+ ##
209
+ # The version of PersistentHTTP you are using
210
+
211
+ VERSION = '3.0.0'
212
+
213
+ ##
214
+ # Exceptions rescued for automatic retry on ruby 2.0.0. This overlaps with
215
+ # the exception list for ruby 1.x.
216
+
217
+ RETRIED_EXCEPTIONS = [ # :nodoc:
218
+ (Net::ReadTimeout if Net.const_defined? :ReadTimeout),
219
+ IOError,
220
+ EOFError,
221
+ Errno::ECONNRESET,
222
+ Errno::ECONNABORTED,
223
+ Errno::EPIPE,
224
+ (OpenSSL::SSL::SSLError if HAVE_OPENSSL),
225
+ Timeout::Error,
226
+ ].compact
227
+
228
+ ##
229
+ # Error class for errors raised by PersistentHTTP. Various
230
+ # SystemCallErrors are re-raised with a human-readable message under this
231
+ # class.
232
+
233
+ class Error < StandardError; end
234
+
235
+ ##
236
+ # Use this method to detect the idle timeout of the host at +uri+. The
237
+ # value returned can be used to configure #idle_timeout. +max+ controls the
238
+ # maximum idle timeout to detect.
239
+ #
240
+ # After
241
+ #
242
+ # Idle timeout detection is performed by creating a connection then
243
+ # performing a HEAD request in a loop until the connection terminates
244
+ # waiting one additional second per loop.
245
+ #
246
+ # NOTE: This may not work on ruby > 1.9.
247
+
248
+ def self.detect_idle_timeout uri, max = 10
249
+ uri = URI uri unless URI::Generic === uri
250
+ uri += '/'
251
+
252
+ req = Net::HTTP::Head.new uri.request_uri
253
+
254
+ http = new 'net-http-persistent detect_idle_timeout'
255
+
256
+ http.connection_for uri do |connection|
257
+ sleep_time = 0
258
+
259
+ http = connection.http
260
+
261
+ loop do
262
+ response = http.request req
263
+
264
+ $stderr.puts "HEAD #{uri} => #{response.code}" if $DEBUG
265
+
266
+ unless Net::HTTPOK === response then
267
+ raise Error, "bad response code #{response.code} detecting idle timeout"
268
+ end
269
+
270
+ break if sleep_time >= max
271
+
272
+ sleep_time += 1
273
+
274
+ $stderr.puts "sleeping #{sleep_time}" if $DEBUG
275
+ sleep sleep_time
276
+ end
277
+ end
278
+ rescue
279
+ # ignore StandardErrors, we've probably found the idle timeout.
280
+ ensure
281
+ return sleep_time unless $!
282
+ end
283
+
284
+ ##
285
+ # This client's OpenSSL::X509::Certificate
286
+
287
+ attr_reader :certificate
288
+
289
+ ##
290
+ # For Net::HTTP parity
291
+
292
+ alias cert certificate
293
+
294
+ ##
295
+ # An SSL certificate authority. Setting this will set verify_mode to
296
+ # VERIFY_PEER.
297
+
298
+ attr_reader :ca_file
299
+
300
+ ##
301
+ # A directory of SSL certificates to be used as certificate authorities.
302
+ # Setting this will set verify_mode to VERIFY_PEER.
303
+
304
+ attr_reader :ca_path
305
+
306
+ ##
307
+ # An SSL certificate store. Setting this will override the default
308
+ # certificate store. See verify_mode for more information.
309
+
310
+ attr_reader :cert_store
311
+
312
+ ##
313
+ # The ciphers allowed for SSL connections
314
+
315
+ attr_reader :ciphers
316
+
317
+ ##
318
+ # Sends debug_output to this IO via Net::HTTP#set_debug_output.
319
+ #
320
+ # Never use this method in production code, it causes a serious security
321
+ # hole.
322
+
323
+ attr_accessor :debug_output
324
+
325
+ ##
326
+ # Current connection generation
327
+
328
+ attr_reader :generation # :nodoc:
329
+
330
+ ##
331
+ # Headers that are added to every request using Net::HTTP#add_field
332
+
333
+ attr_reader :headers
334
+
335
+ ##
336
+ # Maps host:port to an HTTP version. This allows us to enable version
337
+ # specific features.
338
+
339
+ attr_reader :http_versions
340
+
341
+ ##
342
+ # Maximum time an unused connection can remain idle before being
343
+ # automatically closed.
344
+
345
+ attr_accessor :idle_timeout
346
+
347
+ ##
348
+ # Maximum number of requests on a connection before it is considered expired
349
+ # and automatically closed.
350
+
351
+ attr_accessor :max_requests
352
+
353
+ ##
354
+ # The value sent in the Keep-Alive header. Defaults to 30. Not needed for
355
+ # HTTP/1.1 servers.
356
+ #
357
+ # This may not work correctly for HTTP/1.0 servers
358
+ #
359
+ # This method may be removed in a future version as RFC 2616 does not
360
+ # require this header.
361
+
362
+ attr_accessor :keep_alive
363
+
364
+ ##
365
+ # A name for this connection. Allows you to keep your connections apart
366
+ # from everybody else's.
367
+
368
+ attr_reader :name
369
+
370
+ ##
371
+ # Minimum SSL version to use.
372
+
373
+ attr_reader :min_version
374
+
375
+ ##
376
+ # Seconds to wait until a connection is opened. See Net::HTTP#open_timeout
377
+
378
+ attr_accessor :open_timeout
379
+
380
+ ##
381
+ # Headers that are added to every request using Net::HTTP#[]=
382
+
383
+ attr_reader :override_headers
384
+
385
+ ##
386
+ # This client's SSL private key
387
+
388
+ attr_reader :private_key
389
+
390
+ ##
391
+ # For Net::HTTP parity
392
+
393
+ alias key private_key
394
+
395
+ ##
396
+ # The URL through which requests will be proxied
397
+
398
+ attr_reader :proxy_uri
399
+
400
+ ##
401
+ # List of host suffixes which will not be proxied
402
+
403
+ attr_reader :no_proxy
404
+
405
+ ##
406
+ # Test-only accessor for the connection pool
407
+
408
+ attr_reader :pool # :nodoc:
409
+
410
+ ##
411
+ # Seconds to wait until reading one block. See Net::HTTP#read_timeout
412
+
413
+ attr_accessor :read_timeout
414
+
415
+ ##
416
+ # By default SSL sessions are reused to avoid extra SSL handshakes. Set
417
+ # this to false if you have problems communicating with an HTTPS server
418
+ # like:
419
+ #
420
+ # SSL_connect [...] read finished A: unexpected message (OpenSSL::SSL::SSLError)
421
+
422
+ attr_accessor :reuse_ssl_sessions
423
+
424
+ ##
425
+ # An array of options for Socket#setsockopt.
426
+ #
427
+ # By default the TCP_NODELAY option is set on sockets.
428
+ #
429
+ # To set additional options append them to this array:
430
+ #
431
+ # http.socket_options << [Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1]
432
+
433
+ attr_reader :socket_options
434
+
435
+ ##
436
+ # Current SSL connection generation
437
+
438
+ attr_reader :ssl_generation # :nodoc:
439
+
440
+ ##
441
+ # SSL session lifetime
442
+
443
+ attr_reader :ssl_timeout
444
+
445
+ ##
446
+ # Where this instance's last-use times live in the thread local variables
447
+
448
+ attr_reader :timeout_key # :nodoc:
449
+
450
+ ##
451
+ # SSL verification callback. Used when ca_file or ca_path is set.
452
+
453
+ attr_reader :verify_callback
454
+
455
+ ##
456
+ # Sets the depth of SSL certificate verification
457
+
458
+ attr_reader :verify_depth
459
+
460
+ ##
461
+ # HTTPS verify mode. Defaults to OpenSSL::SSL::VERIFY_PEER which verifies
462
+ # the server certificate.
463
+ #
464
+ # If no ca_file, ca_path or cert_store is set the default system certificate
465
+ # store is used.
466
+ #
467
+ # You can use +verify_mode+ to override any default values.
468
+
469
+ attr_reader :verify_mode
470
+
471
+ ##
472
+ # Enable retries of non-idempotent requests that change data (e.g. POST
473
+ # requests) when the server has disconnected.
474
+ #
475
+ # This will in the worst case lead to multiple requests with the same data,
476
+ # but it may be useful for some applications. Take care when enabling
477
+ # this option to ensure it is safe to POST or perform other non-idempotent
478
+ # requests to the server.
479
+
480
+ attr_accessor :retry_change_requests
481
+
482
+ ##
483
+ # Creates a new PersistentHTTP.
484
+ #
485
+ # Set +name+ to keep your connections apart from everybody else's. Not
486
+ # required currently, but highly recommended. Your library name should be
487
+ # good enough. This parameter will be required in a future version.
488
+ #
489
+ # +proxy+ may be set to a URI::HTTP or :ENV to pick up proxy options from
490
+ # the environment. See proxy_from_env for details.
491
+ #
492
+ # In order to use a URI for the proxy you may need to do some extra work
493
+ # beyond URI parsing if the proxy requires a password:
494
+ #
495
+ # proxy = URI 'http://proxy.example'
496
+ # proxy.user = 'AzureDiamond'
497
+ # proxy.password = 'hunter2'
498
+ #
499
+ # Set +pool_size+ to limit the maximum number of connections allowed.
500
+ # Defaults to 1/4 the number of allowed file handles. You can have no more
501
+ # than this many threads with active HTTP transactions.
502
+
503
+ def initialize name=nil, proxy=nil, pool_size=Vault::Defaults::DEFAULT_POOL_SIZE, pool_timeout=Vault::Defaults::DEFAULT_POOL_TIMEOUT
504
+ @name = name
505
+
506
+ @debug_output = nil
507
+ @proxy_uri = nil
508
+ @no_proxy = []
509
+ @headers = {}
510
+ @override_headers = {}
511
+ @http_versions = {}
512
+ @keep_alive = 30
513
+ @open_timeout = nil
514
+ @read_timeout = nil
515
+ @idle_timeout = 5
516
+ @max_requests = nil
517
+ @socket_options = []
518
+ @ssl_generation = 0 # incremented when SSL session variables change
519
+
520
+ @socket_options << [Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1] if
521
+ Socket.const_defined? :TCP_NODELAY
522
+
523
+ @pool = PersistentHTTP::Pool.new size: pool_size, timeout: pool_timeout do |http_args|
524
+ PersistentHTTP::Connection.new Net::HTTP, http_args, @ssl_generation
525
+ end
526
+
527
+ @certificate = nil
528
+ @ca_file = nil
529
+ @ca_path = nil
530
+ @ciphers = nil
531
+ @min_version = nil
532
+ @private_key = nil
533
+ @ssl_timeout = nil
534
+ @verify_callback = nil
535
+ @verify_depth = nil
536
+ @verify_mode = nil
537
+ @cert_store = nil
538
+
539
+ @generation = 0 # incremented when proxy URI changes
540
+
541
+ if HAVE_OPENSSL then
542
+ @verify_mode = OpenSSL::SSL::VERIFY_PEER
543
+ @reuse_ssl_sessions = OpenSSL::SSL.const_defined? :Session
544
+ end
545
+
546
+ @retry_change_requests = false
547
+
548
+ self.proxy = proxy if proxy
549
+ end
550
+
551
+ ##
552
+ # Sets this client's OpenSSL::X509::Certificate
553
+
554
+ def certificate= certificate
555
+ @certificate = certificate
556
+
557
+ reconnect_ssl
558
+ end
559
+
560
+ # For Net::HTTP parity
561
+ alias cert= certificate=
562
+
563
+ ##
564
+ # Sets the SSL certificate authority file.
565
+
566
+ def ca_file= file
567
+ @ca_file = file
568
+
569
+ reconnect_ssl
570
+ end
571
+
572
+ ##
573
+ # Sets the SSL certificate authority path.
574
+
575
+ def ca_path= path
576
+ @ca_path = path
577
+
578
+ reconnect_ssl
579
+ end
580
+
581
+ ##
582
+ # Overrides the default SSL certificate store used for verifying
583
+ # connections.
584
+
585
+ def cert_store= store
586
+ @cert_store = store
587
+
588
+ reconnect_ssl
589
+ end
590
+
591
+ ##
592
+ # The ciphers allowed for SSL connections
593
+
594
+ def ciphers= ciphers
595
+ @ciphers = ciphers
596
+
597
+ reconnect_ssl
598
+ end
599
+
600
+ ##
601
+ # Creates a new connection for +uri+
602
+
603
+ def connection_for uri
604
+ use_ssl = uri.scheme.downcase == 'https'
605
+
606
+ net_http_args = [uri.hostname, uri.port]
607
+
608
+ net_http_args.concat @proxy_args if
609
+ @proxy_uri and not proxy_bypass? uri.hostname, uri.port
610
+
611
+ connection = @pool.checkout net_http_args
612
+
613
+ http = connection.http
614
+
615
+ connection.ressl @ssl_generation if
616
+ connection.ssl_generation != @ssl_generation
617
+
618
+ if not http.started? then
619
+ ssl http if use_ssl
620
+ start http
621
+ elsif expired? connection then
622
+ reset connection
623
+ end
624
+
625
+ http.read_timeout = @read_timeout if @read_timeout
626
+ http.keep_alive_timeout = @idle_timeout if @idle_timeout
627
+
628
+ return yield connection
629
+ rescue Errno::ECONNREFUSED
630
+ address = http.proxy_address || http.address
631
+ port = http.proxy_port || http.port
632
+
633
+ raise Error, "connection refused: #{address}:#{port}"
634
+ rescue Errno::EHOSTDOWN
635
+ address = http.proxy_address || http.address
636
+ port = http.proxy_port || http.port
637
+
638
+ raise Error, "host down: #{address}:#{port}"
639
+ ensure
640
+ # Only perform checkin if we successfully checked a connection out
641
+ if connection
642
+ @pool.checkin net_http_args
643
+ end
644
+ end
645
+
646
+ ##
647
+ # Returns an error message containing the number of requests performed on
648
+ # this connection
649
+
650
+ def error_message connection
651
+ connection.requests -= 1 # fixup
652
+
653
+ age = Time.now - connection.last_use
654
+
655
+ "after #{connection.requests} requests on #{connection.http.object_id}, " \
656
+ "last used #{age} seconds ago"
657
+ end
658
+
659
+ ##
660
+ # URI::escape wrapper
661
+
662
+ def escape str
663
+ CGI.escape str if str
664
+ end
665
+
666
+ ##
667
+ # URI::unescape wrapper
668
+
669
+ def unescape str
670
+ CGI.unescape str if str
671
+ end
672
+
673
+
674
+ ##
675
+ # Returns true if the connection should be reset due to an idle timeout, or
676
+ # maximum request count, false otherwise.
677
+
678
+ def expired? connection
679
+ return true if @max_requests && connection.requests >= @max_requests
680
+ return false unless @idle_timeout
681
+ return true if @idle_timeout.zero?
682
+
683
+ Time.now - connection.last_use > @idle_timeout
684
+ end
685
+
686
+ ##
687
+ # Starts the Net::HTTP +connection+
688
+
689
+ def start http
690
+ http.set_debug_output @debug_output if @debug_output
691
+ http.open_timeout = @open_timeout if @open_timeout
692
+
693
+ http.start
694
+
695
+ socket = http.instance_variable_get :@socket
696
+
697
+ if socket then # for fakeweb
698
+ @socket_options.each do |option|
699
+ socket.io.setsockopt(*option)
700
+ end
701
+ end
702
+ end
703
+
704
+ ##
705
+ # Finishes the Net::HTTP +connection+
706
+
707
+ def finish connection
708
+ connection.finish
709
+
710
+ connection.http.instance_variable_set :@ssl_session, nil unless
711
+ @reuse_ssl_sessions
712
+ end
713
+
714
+ ##
715
+ # Returns the HTTP protocol version for +uri+
716
+
717
+ def http_version uri
718
+ @http_versions["#{uri.hostname}:#{uri.port}"]
719
+ end
720
+
721
+ ##
722
+ # Is +req+ idempotent according to RFC 2616?
723
+
724
+ def idempotent? req
725
+ case req
726
+ when Net::HTTP::Delete, Net::HTTP::Get, Net::HTTP::Head,
727
+ Net::HTTP::Options, Net::HTTP::Put, Net::HTTP::Trace then
728
+ true
729
+ end
730
+ end
731
+
732
+ ##
733
+ # Is the request +req+ idempotent or is retry_change_requests allowed.
734
+
735
+ def can_retry? req
736
+ @retry_change_requests && !idempotent?(req)
737
+ end
738
+
739
+ ##
740
+ # Adds "http://" to the String +uri+ if it is missing.
741
+
742
+ def normalize_uri uri
743
+ (uri =~ /^https?:/) ? uri : "http://#{uri}"
744
+ end
745
+
746
+ ##
747
+ # Pipelines +requests+ to the HTTP server at +uri+ yielding responses if a
748
+ # block is given. Returns all responses recieved.
749
+ #
750
+ # See
751
+ # Net::HTTP::Pipeline[http://docs.seattlerb.org/net-http-pipeline/Net/HTTP/Pipeline.html]
752
+ # for further details.
753
+ #
754
+ # Only if <tt>net-http-pipeline</tt> was required before
755
+ # <tt>net-http-persistent</tt> #pipeline will be present.
756
+
757
+ def pipeline uri, requests, &block # :yields: responses
758
+ connection_for uri do |connection|
759
+ connection.http.pipeline requests, &block
760
+ end
761
+ end
762
+
763
+ ##
764
+ # Sets this client's SSL private key
765
+
766
+ def private_key= key
767
+ @private_key = key
768
+
769
+ reconnect_ssl
770
+ end
771
+
772
+ # For Net::HTTP parity
773
+ alias key= private_key=
774
+
775
+ ##
776
+ # Sets the proxy server. The +proxy+ may be the URI of the proxy server,
777
+ # the symbol +:ENV+ which will read the proxy from the environment or nil to
778
+ # disable use of a proxy. See #proxy_from_env for details on setting the
779
+ # proxy from the environment.
780
+ #
781
+ # If the proxy URI is set after requests have been made, the next request
782
+ # will shut-down and re-open all connections.
783
+ #
784
+ # The +no_proxy+ query parameter can be used to specify hosts which shouldn't
785
+ # be reached via proxy; if set it should be a comma separated list of
786
+ # hostname suffixes, optionally with +:port+ appended, for example
787
+ # <tt>example.com,some.host:8080</tt>.
788
+
789
+ def proxy= proxy
790
+ @proxy_uri = case proxy
791
+ when :ENV then proxy_from_env
792
+ when URI::HTTP then proxy
793
+ when nil then # ignore
794
+ else raise ArgumentError, 'proxy must be :ENV or a URI::HTTP'
795
+ end
796
+
797
+ @no_proxy.clear
798
+
799
+ if @proxy_uri then
800
+ @proxy_args = [
801
+ @proxy_uri.hostname,
802
+ @proxy_uri.port,
803
+ unescape(@proxy_uri.user),
804
+ unescape(@proxy_uri.password),
805
+ ]
806
+
807
+ @proxy_connection_id = [nil, *@proxy_args].join ':'
808
+
809
+ if @proxy_uri.query then
810
+ @no_proxy = CGI.parse(@proxy_uri.query)['no_proxy'].join(',').downcase.split(',').map { |x| x.strip }.reject { |x| x.empty? }
811
+ end
812
+ end
813
+
814
+ reconnect
815
+ reconnect_ssl
816
+ end
817
+
818
+ ##
819
+ # Creates a URI for an HTTP proxy server from ENV variables.
820
+ #
821
+ # If +HTTP_PROXY+ is set a proxy will be returned.
822
+ #
823
+ # If +HTTP_PROXY_USER+ or +HTTP_PROXY_PASS+ are set the URI is given the
824
+ # indicated user and password unless HTTP_PROXY contains either of these in
825
+ # the URI.
826
+ #
827
+ # The +NO_PROXY+ ENV variable can be used to specify hosts which shouldn't
828
+ # be reached via proxy; if set it should be a comma separated list of
829
+ # hostname suffixes, optionally with +:port+ appended, for example
830
+ # <tt>example.com,some.host:8080</tt>. When set to <tt>*</tt> no proxy will
831
+ # be returned.
832
+ #
833
+ # For Windows users, lowercase ENV variables are preferred over uppercase ENV
834
+ # variables.
835
+
836
+ def proxy_from_env
837
+ env_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
838
+
839
+ return nil if env_proxy.nil? or env_proxy.empty?
840
+
841
+ uri = URI normalize_uri env_proxy
842
+
843
+ env_no_proxy = ENV['no_proxy'] || ENV['NO_PROXY']
844
+
845
+ # '*' is special case for always bypass
846
+ return nil if env_no_proxy == '*'
847
+
848
+ if env_no_proxy then
849
+ uri.query = "no_proxy=#{escape(env_no_proxy)}"
850
+ end
851
+
852
+ unless uri.user or uri.password then
853
+ uri.user = escape ENV['http_proxy_user'] || ENV['HTTP_PROXY_USER']
854
+ uri.password = escape ENV['http_proxy_pass'] || ENV['HTTP_PROXY_PASS']
855
+ end
856
+
857
+ uri
858
+ end
859
+
860
+ ##
861
+ # Returns true when proxy should by bypassed for host.
862
+
863
+ def proxy_bypass? host, port
864
+ host = host.downcase
865
+ host_port = [host, port].join ':'
866
+
867
+ @no_proxy.each do |name|
868
+ return true if host[-name.length, name.length] == name or
869
+ host_port[-name.length, name.length] == name
870
+ end
871
+
872
+ false
873
+ end
874
+
875
+ ##
876
+ # Forces reconnection of HTTP connections.
877
+
878
+ def reconnect
879
+ @generation += 1
880
+ end
881
+
882
+ ##
883
+ # Forces reconnection of SSL connections.
884
+
885
+ def reconnect_ssl
886
+ @ssl_generation += 1
887
+ end
888
+
889
+ ##
890
+ # Finishes then restarts the Net::HTTP +connection+
891
+
892
+ def reset connection
893
+ http = connection.http
894
+
895
+ finish connection
896
+
897
+ start http
898
+ rescue Errno::ECONNREFUSED
899
+ e = Error.new "connection refused: #{http.address}:#{http.port}"
900
+ e.set_backtrace $@
901
+ raise e
902
+ rescue Errno::EHOSTDOWN
903
+ e = Error.new "host down: #{http.address}:#{http.port}"
904
+ e.set_backtrace $@
905
+ raise e
906
+ end
907
+
908
+ ##
909
+ # Makes a request on +uri+. If +req+ is nil a Net::HTTP::Get is performed
910
+ # against +uri+.
911
+ #
912
+ # If a block is passed #request behaves like Net::HTTP#request (the body of
913
+ # the response will not have been read).
914
+ #
915
+ # +req+ must be a Net::HTTPRequest subclass (see Net::HTTP for a list).
916
+ #
917
+ # If there is an error and the request is idempotent according to RFC 2616
918
+ # it will be retried automatically.
919
+
920
+ def request uri, req = nil, &block
921
+ retried = false
922
+ bad_response = false
923
+
924
+ uri = URI uri
925
+ req = request_setup req || uri
926
+ response = nil
927
+
928
+ connection_for uri do |connection|
929
+ http = connection.http
930
+
931
+ begin
932
+ connection.requests += 1
933
+
934
+ response = http.request req, &block
935
+
936
+ if req.connection_close? or
937
+ (response.http_version <= '1.0' and
938
+ not response.connection_keep_alive?) or
939
+ response.connection_close? then
940
+ finish connection
941
+ end
942
+ rescue Net::HTTPBadResponse => e
943
+ message = error_message connection
944
+
945
+ finish connection
946
+
947
+ raise Error, "too many bad responses #{message}" if
948
+ bad_response or not can_retry? req
949
+
950
+ bad_response = true
951
+ retry
952
+ rescue *RETRIED_EXCEPTIONS => e
953
+ request_failed e, req, connection if
954
+ retried or not can_retry? req
955
+
956
+ reset connection
957
+
958
+ retried = true
959
+ retry
960
+ rescue Errno::EINVAL, Errno::ETIMEDOUT => e # not retried on ruby 2
961
+ request_failed e, req, connection if retried or not can_retry? req
962
+
963
+ reset connection
964
+
965
+ retried = true
966
+ retry
967
+ rescue Exception => e
968
+ finish connection
969
+
970
+ raise
971
+ ensure
972
+ connection.last_use = Time.now
973
+ end
974
+ end
975
+
976
+ @http_versions["#{uri.hostname}:#{uri.port}"] ||= response.http_version
977
+
978
+ response
979
+ end
980
+
981
+ ##
982
+ # Raises an Error for +exception+ which resulted from attempting the request
983
+ # +req+ on the +connection+.
984
+ #
985
+ # Finishes the +connection+.
986
+
987
+ def request_failed exception, req, connection # :nodoc:
988
+ due_to = "(due to #{exception.message} - #{exception.class})"
989
+ message = "too many connection resets #{due_to} #{error_message connection}"
990
+
991
+ finish connection
992
+
993
+ raise Error, message, exception.backtrace
994
+ end
995
+
996
+ ##
997
+ # Creates a GET request if +req_or_uri+ is a URI and adds headers to the
998
+ # request.
999
+ #
1000
+ # Returns the request.
1001
+
1002
+ def request_setup req_or_uri # :nodoc:
1003
+ req = if URI === req_or_uri then
1004
+ Net::HTTP::Get.new req_or_uri.request_uri
1005
+ else
1006
+ req_or_uri
1007
+ end
1008
+
1009
+ @headers.each do |pair|
1010
+ req.add_field(*pair)
1011
+ end
1012
+
1013
+ @override_headers.each do |name, value|
1014
+ req[name] = value
1015
+ end
1016
+
1017
+ unless req['Connection'] then
1018
+ req.add_field 'Connection', 'keep-alive'
1019
+ req.add_field 'Keep-Alive', @keep_alive
1020
+ end
1021
+
1022
+ req
1023
+ end
1024
+
1025
+ ##
1026
+ # Shuts down all connections
1027
+ #
1028
+ # *NOTE*: Calling shutdown for can be dangerous!
1029
+ #
1030
+ # If any thread is still using a connection it may cause an error! Call
1031
+ # #shutdown when you are completely done making requests!
1032
+
1033
+ def shutdown
1034
+ @pool.available.shutdown do |http|
1035
+ http.finish
1036
+ end
1037
+ end
1038
+
1039
+ ##
1040
+ # Enables SSL on +connection+
1041
+
1042
+ def ssl connection
1043
+ connection.use_ssl = true
1044
+
1045
+ connection.ciphers = @ciphers if @ciphers
1046
+
1047
+ if @min_version
1048
+ if connection.respond_to? :min_version=
1049
+ connection.min_version = @min_version
1050
+ else
1051
+ connection.ssl_version = @min_version
1052
+ end
1053
+ end
1054
+
1055
+ connection.ssl_timeout = @ssl_timeout if @ssl_timeout
1056
+
1057
+ connection.verify_depth = @verify_depth
1058
+ connection.verify_mode = @verify_mode
1059
+
1060
+ if OpenSSL::SSL::VERIFY_PEER == OpenSSL::SSL::VERIFY_NONE and
1061
+ not Object.const_defined?(:I_KNOW_THAT_OPENSSL_VERIFY_PEER_EQUALS_VERIFY_NONE_IS_WRONG) then
1062
+ warn <<-WARNING
1063
+ !!!SECURITY WARNING!!!
1064
+
1065
+ The SSL HTTP connection to:
1066
+
1067
+ #{connection.address}:#{connection.port}
1068
+
1069
+ !!!MAY NOT BE VERIFIED!!!
1070
+
1071
+ On your platform your OpenSSL implementation is broken.
1072
+
1073
+ There is no difference between the values of VERIFY_NONE and VERIFY_PEER.
1074
+
1075
+ This means that attempting to verify the security of SSL connections may not
1076
+ work. This exposes you to man-in-the-middle exploits, snooping on the
1077
+ contents of your connection and other dangers to the security of your data.
1078
+
1079
+ To disable this warning define the following constant at top-level in your
1080
+ application:
1081
+
1082
+ I_KNOW_THAT_OPENSSL_VERIFY_PEER_EQUALS_VERIFY_NONE_IS_WRONG = nil
1083
+
1084
+ WARNING
1085
+ end
1086
+
1087
+ connection.ca_file = @ca_file if @ca_file
1088
+ connection.ca_path = @ca_path if @ca_path
1089
+
1090
+ if @ca_file or @ca_path then
1091
+ connection.verify_callback = @verify_callback if @verify_callback
1092
+ end
1093
+
1094
+ if @certificate and @private_key then
1095
+ connection.cert = @certificate
1096
+ connection.key = @private_key
1097
+ end
1098
+
1099
+ connection.cert_store = if @cert_store then
1100
+ @cert_store
1101
+ else
1102
+ store = OpenSSL::X509::Store.new
1103
+ store.set_default_paths
1104
+ store
1105
+ end
1106
+ end
1107
+
1108
+ ##
1109
+ # Minimum SSL version to use
1110
+
1111
+ def min_version= min_version
1112
+ @min_version = min_version
1113
+
1114
+ reconnect_ssl
1115
+ end
1116
+
1117
+ ##
1118
+ # SSL session lifetime
1119
+
1120
+ def ssl_timeout= ssl_timeout
1121
+ @ssl_timeout = ssl_timeout
1122
+
1123
+ reconnect_ssl
1124
+ end
1125
+
1126
+ ##
1127
+ # Sets the depth of SSL certificate verification
1128
+
1129
+ def verify_depth= verify_depth
1130
+ @verify_depth = verify_depth
1131
+
1132
+ reconnect_ssl
1133
+ end
1134
+
1135
+ ##
1136
+ # Sets the HTTPS verify mode. Defaults to OpenSSL::SSL::VERIFY_PEER.
1137
+ #
1138
+ # Setting this to VERIFY_NONE is a VERY BAD IDEA and should NEVER be used.
1139
+ # Securely transfer the correct certificate and update the default
1140
+ # certificate store or set the ca file instead.
1141
+
1142
+ def verify_mode= verify_mode
1143
+ @verify_mode = verify_mode
1144
+
1145
+ reconnect_ssl
1146
+ end
1147
+
1148
+ ##
1149
+ # SSL verification callback.
1150
+
1151
+ def verify_callback= callback
1152
+ @verify_callback = callback
1153
+
1154
+ reconnect_ssl
1155
+ end
1156
+
1157
+ end
1158
+ end
1159
+
1160
+ require_relative 'persistent/connection'
1161
+ require_relative 'persistent/pool'