vault 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,178 @@
1
+ require 'thread'
2
+ require 'timeout'
3
+
4
+ module Vault; end
5
+
6
+ ##
7
+ # Raised when you attempt to retrieve a connection from a pool that has been
8
+ # shut down.
9
+
10
+ class Vault::ConnectionPool::PoolShuttingDownError < RuntimeError; end
11
+
12
+ ##
13
+ # The TimedStack manages a pool of homogeneous connections (or any resource
14
+ # you wish to manage). Connections are created lazily up to a given maximum
15
+ # number.
16
+
17
+ # Examples:
18
+ #
19
+ # ts = TimedStack.new(1) { MyConnection.new }
20
+ #
21
+ # # fetch a connection
22
+ # conn = ts.pop
23
+ #
24
+ # # return a connection
25
+ # ts.push conn
26
+ #
27
+ # conn = ts.pop
28
+ # ts.pop timeout: 5
29
+ # #=> raises Timeout::Error after 5 seconds
30
+
31
+ module Vault
32
+ class ConnectionPool::TimedStack
33
+
34
+ ##
35
+ # Creates a new pool with +size+ connections that are created from the given
36
+ # +block+.
37
+
38
+ def initialize(size = 0, &block)
39
+ @create_block = block
40
+ @created = 0
41
+ @que = []
42
+ @max = size
43
+ @mutex = Mutex.new
44
+ @resource = ConditionVariable.new
45
+ @shutdown_block = nil
46
+ end
47
+
48
+ ##
49
+ # Returns +obj+ to the stack. +options+ is ignored in TimedStack but may be
50
+ # used by subclasses that extend TimedStack.
51
+
52
+ def push(obj, options = {})
53
+ @mutex.synchronize do
54
+ if @shutdown_block
55
+ @shutdown_block.call(obj)
56
+ else
57
+ store_connection obj, options
58
+ end
59
+
60
+ @resource.broadcast
61
+ end
62
+ end
63
+ alias_method :<<, :push
64
+
65
+ ##
66
+ # Retrieves a connection from the stack. If a connection is available it is
67
+ # immediately returned. If no connection is available within the given
68
+ # timeout a Timeout::Error is raised.
69
+ #
70
+ # +:timeout+ is the only checked entry in +options+ and is preferred over
71
+ # the +timeout+ argument (which will be removed in a future release). Other
72
+ # options may be used by subclasses that extend TimedStack.
73
+
74
+ def pop(timeout = 0.5, options = {})
75
+ options, timeout = timeout, 0.5 if Hash === timeout
76
+ timeout = options.fetch :timeout, timeout
77
+
78
+ deadline = Time.now + timeout
79
+ @mutex.synchronize do
80
+ loop do
81
+ raise ConnectionPool::PoolShuttingDownError if @shutdown_block
82
+ return fetch_connection(options) if connection_stored?(options)
83
+
84
+ connection = try_create(options)
85
+ return connection if connection
86
+
87
+ to_wait = deadline - Time.now
88
+ raise Timeout::Error, "Waited #{timeout} sec" if to_wait <= 0
89
+ @resource.wait(@mutex, to_wait)
90
+ end
91
+ end
92
+ end
93
+
94
+ ##
95
+ # Shuts down the TimedStack which prevents connections from being checked
96
+ # out. The +block+ is called once for each connection on the stack.
97
+
98
+ def shutdown(&block)
99
+ raise ArgumentError, "shutdown must receive a block" unless block_given?
100
+
101
+ @mutex.synchronize do
102
+ @shutdown_block = block
103
+ @resource.broadcast
104
+
105
+ shutdown_connections
106
+ end
107
+ end
108
+
109
+ ##
110
+ # Returns +true+ if there are no available connections.
111
+
112
+ def empty?
113
+ (@created - @que.length) >= @max
114
+ end
115
+
116
+ ##
117
+ # The number of connections available on the stack.
118
+
119
+ def length
120
+ @max - @created + @que.length
121
+ end
122
+
123
+ private
124
+
125
+ ##
126
+ # This is an extension point for TimedStack and is called with a mutex.
127
+ #
128
+ # This method must returns true if a connection is available on the stack.
129
+
130
+ def connection_stored?(options = nil)
131
+ !@que.empty?
132
+ end
133
+
134
+ ##
135
+ # This is an extension point for TimedStack and is called with a mutex.
136
+ #
137
+ # This method must return a connection from the stack.
138
+
139
+ def fetch_connection(options = nil)
140
+ @que.pop
141
+ end
142
+
143
+ ##
144
+ # This is an extension point for TimedStack and is called with a mutex.
145
+ #
146
+ # This method must shut down all connections on the stack.
147
+
148
+ def shutdown_connections(options = nil)
149
+ while connection_stored?(options)
150
+ conn = fetch_connection(options)
151
+ @shutdown_block.call(conn)
152
+ end
153
+ end
154
+
155
+ ##
156
+ # This is an extension point for TimedStack and is called with a mutex.
157
+ #
158
+ # This method must return +obj+ to the stack.
159
+
160
+ def store_connection(obj, options = nil)
161
+ @que.push obj
162
+ end
163
+
164
+ ##
165
+ # This is an extension point for TimedStack and is called with a mutex.
166
+ #
167
+ # This method must create a connection if and only if the total number of
168
+ # connections allowed has not been met.
169
+
170
+ def try_create(options = nil)
171
+ unless @created == @max
172
+ object = @create_block.call
173
+ @created += 1
174
+ object
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,5 @@
1
+ module Vault
2
+ class ConnectionPool
3
+ VERSION = "2.2.0"
4
+ end
5
+ end
@@ -0,0 +1,1154 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+ require 'cgi' # for escaping
4
+ require 'vault/vendor/connection_pool'
5
+
6
+ begin
7
+ require 'net/http/pipeline'
8
+ rescue LoadError
9
+ end
10
+
11
+ autoload :OpenSSL, 'openssl'
12
+
13
+ ##
14
+ # Persistent connections for Net::HTTP
15
+ #
16
+ # Net::HTTP::Persistent maintains persistent connections across all the
17
+ # servers you wish to talk to. For each host:port you communicate with a
18
+ # single persistent connection is created.
19
+ #
20
+ # Multiple Net::HTTP::Persistent objects will share the same set of
21
+ # connections.
22
+ #
23
+ # For each thread you start a new connection will be created. A
24
+ # Net::HTTP::Persistent connection will not be shared across threads.
25
+ #
26
+ # You can shut down the HTTP connections when done by calling #shutdown. You
27
+ # should name your Net::HTTP::Persistent object if you intend to call this
28
+ # method.
29
+ #
30
+ # Example:
31
+ #
32
+ # require 'net/http/persistent'
33
+ #
34
+ # uri = URI 'http://example.com/awesome/web/service'
35
+ #
36
+ # http = Net::HTTP::Persistent.new 'my_app_name'
37
+ #
38
+ # # perform a GET
39
+ # response = http.request uri
40
+ #
41
+ # # or
42
+ #
43
+ # get = Net::HTTP::Get.new uri.request_uri
44
+ # response = http.request get
45
+ #
46
+ # # create a POST
47
+ # post_uri = uri + 'create'
48
+ # post = Net::HTTP::Post.new post_uri.path
49
+ # post.set_form_data 'some' => 'cool data'
50
+ #
51
+ # # perform the POST, the URI is always required
52
+ # response http.request post_uri, post
53
+ #
54
+ # Note that for GET, HEAD and other requests that do not have a body you want
55
+ # to use URI#request_uri not URI#path. The request_uri contains the query
56
+ # params which are sent in the body for other requests.
57
+ #
58
+ # == SSL
59
+ #
60
+ # SSL connections are automatically created depending upon the scheme of the
61
+ # URI. SSL connections are automatically verified against the default
62
+ # certificate store for your computer. You can override this by changing
63
+ # verify_mode or by specifying an alternate cert_store.
64
+ #
65
+ # Here are the SSL settings, see the individual methods for documentation:
66
+ #
67
+ # #certificate :: This client's certificate
68
+ # #ca_file :: The certificate-authorities
69
+ # #ca_path :: Directory with certificate-authorities
70
+ # #cert_store :: An SSL certificate store
71
+ # #ciphers :: List of SSl ciphers allowed
72
+ # #private_key :: The client's SSL private key
73
+ # #reuse_ssl_sessions :: Reuse a previously opened SSL session for a new
74
+ # connection
75
+ # #ssl_timeout :: SSL session lifetime
76
+ # #ssl_version :: Which specific SSL version to use
77
+ # #verify_callback :: For server certificate verification
78
+ # #verify_depth :: Depth of certificate verification
79
+ # #verify_mode :: How connections should be verified
80
+ #
81
+ # == Proxies
82
+ #
83
+ # A proxy can be set through #proxy= or at initialization time by providing a
84
+ # second argument to ::new. The proxy may be the URI of the proxy server or
85
+ # <code>:ENV</code> which will consult environment variables.
86
+ #
87
+ # See #proxy= and #proxy_from_env for details.
88
+ #
89
+ # == Headers
90
+ #
91
+ # Headers may be specified for use in every request. #headers are appended to
92
+ # any headers on the request. #override_headers replace existing headers on
93
+ # the request.
94
+ #
95
+ # The difference between the two can be seen in setting the User-Agent. Using
96
+ # <code>http.headers['User-Agent'] = 'MyUserAgent'</code> will send "Ruby,
97
+ # MyUserAgent" while <code>http.override_headers['User-Agent'] =
98
+ # 'MyUserAgent'</code> will send "MyUserAgent".
99
+ #
100
+ # == Tuning
101
+ #
102
+ # === Segregation
103
+ #
104
+ # By providing an application name to ::new you can separate your connections
105
+ # from the connections of other applications.
106
+ #
107
+ # === Idle Timeout
108
+ #
109
+ # If a connection hasn't been used for this number of seconds it will automatically be
110
+ # reset upon the next use to avoid attempting to send to a closed connection.
111
+ # The default value is 5 seconds. nil means no timeout. Set through #idle_timeout.
112
+ #
113
+ # Reducing this value may help avoid the "too many connection resets" error
114
+ # when sending non-idempotent requests while increasing this value will cause
115
+ # fewer round-trips.
116
+ #
117
+ # === Read Timeout
118
+ #
119
+ # The amount of time allowed between reading two chunks from the socket. Set
120
+ # through #read_timeout
121
+ #
122
+ # === Max Requests
123
+ #
124
+ # The number of requests that should be made before opening a new connection.
125
+ # Typically many keep-alive capable servers tune this to 100 or less, so the
126
+ # 101st request will fail with ECONNRESET. If unset (default), this value has no
127
+ # effect, if set, connections will be reset on the request after max_requests.
128
+ #
129
+ # === Open Timeout
130
+ #
131
+ # The amount of time to wait for a connection to be opened. Set through
132
+ # #open_timeout.
133
+ #
134
+ # === Socket Options
135
+ #
136
+ # Socket options may be set on newly-created connections. See #socket_options
137
+ # for details.
138
+ #
139
+ # === Non-Idempotent Requests
140
+ #
141
+ # By default non-idempotent requests will not be retried per RFC 2616. By
142
+ # setting retry_change_requests to true requests will automatically be retried
143
+ # once.
144
+ #
145
+ # Only do this when you know that retrying a POST or other non-idempotent
146
+ # request is safe for your application and will not create duplicate
147
+ # resources.
148
+ #
149
+ # The recommended way to handle non-idempotent requests is the following:
150
+ #
151
+ # require 'net/http/persistent'
152
+ #
153
+ # uri = URI 'http://example.com/awesome/web/service'
154
+ # post_uri = uri + 'create'
155
+ #
156
+ # http = Net::HTTP::Persistent.new 'my_app_name'
157
+ #
158
+ # post = Net::HTTP::Post.new post_uri.path
159
+ # # ... fill in POST request
160
+ #
161
+ # begin
162
+ # response = http.request post_uri, post
163
+ # rescue Net::HTTP::Persistent::Error
164
+ #
165
+ # # POST failed, make a new request to verify the server did not process
166
+ # # the request
167
+ # exists_uri = uri + '...'
168
+ # response = http.get exists_uri
169
+ #
170
+ # # Retry if it failed
171
+ # retry if response.code == '404'
172
+ # end
173
+ #
174
+ # The method of determining if the resource was created or not is unique to
175
+ # the particular service you are using. Of course, you will want to add
176
+ # protection from infinite looping.
177
+ #
178
+ # === Connection Termination
179
+ #
180
+ # If you are done using the Net::HTTP::Persistent instance you may shut down
181
+ # all the connections in the current thread with #shutdown. This is not
182
+ # recommended for normal use, it should only be used when it will be several
183
+ # minutes before you make another HTTP request.
184
+ #
185
+ # If you are using multiple threads, call #shutdown in each thread when the
186
+ # thread is done making requests. If you don't call shutdown, that's OK.
187
+ # Ruby will automatically garbage collect and shutdown your HTTP connections
188
+ # when the thread terminates.
189
+
190
+ module Vault
191
+ class Net::HTTP::Persistent
192
+
193
+ ##
194
+ # The beginning of Time
195
+
196
+ EPOCH = Time.at 0 # :nodoc:
197
+
198
+ ##
199
+ # Is OpenSSL available? This test works with autoload
200
+
201
+ HAVE_OPENSSL = defined? OpenSSL::SSL # :nodoc:
202
+
203
+ ##
204
+ # The default connection pool size is 1/4 the allowed open files.
205
+
206
+ DEFAULT_POOL_SIZE = Process.getrlimit(Process::RLIMIT_NOFILE).first / 4
207
+
208
+ ##
209
+ # The version of Net::HTTP::Persistent 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 Net::HTTP::Persistent. 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
+ # Seconds to wait until a connection is opened. See Net::HTTP#open_timeout
372
+
373
+ attr_accessor :open_timeout
374
+
375
+ ##
376
+ # Headers that are added to every request using Net::HTTP#[]=
377
+
378
+ attr_reader :override_headers
379
+
380
+ ##
381
+ # This client's SSL private key
382
+
383
+ attr_reader :private_key
384
+
385
+ ##
386
+ # For Net::HTTP parity
387
+
388
+ alias key private_key
389
+
390
+ ##
391
+ # The URL through which requests will be proxied
392
+
393
+ attr_reader :proxy_uri
394
+
395
+ ##
396
+ # List of host suffixes which will not be proxied
397
+
398
+ attr_reader :no_proxy
399
+
400
+ ##
401
+ # Test-only accessor for the connection pool
402
+
403
+ attr_reader :pool # :nodoc:
404
+
405
+ ##
406
+ # Seconds to wait until reading one block. See Net::HTTP#read_timeout
407
+
408
+ attr_accessor :read_timeout
409
+
410
+ ##
411
+ # By default SSL sessions are reused to avoid extra SSL handshakes. Set
412
+ # this to false if you have problems communicating with an HTTPS server
413
+ # like:
414
+ #
415
+ # SSL_connect [...] read finished A: unexpected message (OpenSSL::SSL::SSLError)
416
+
417
+ attr_accessor :reuse_ssl_sessions
418
+
419
+ ##
420
+ # An array of options for Socket#setsockopt.
421
+ #
422
+ # By default the TCP_NODELAY option is set on sockets.
423
+ #
424
+ # To set additional options append them to this array:
425
+ #
426
+ # http.socket_options << [Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1]
427
+
428
+ attr_reader :socket_options
429
+
430
+ ##
431
+ # Current SSL connection generation
432
+
433
+ attr_reader :ssl_generation # :nodoc:
434
+
435
+ ##
436
+ # SSL session lifetime
437
+
438
+ attr_reader :ssl_timeout
439
+
440
+ ##
441
+ # SSL version to use.
442
+ #
443
+ # By default, the version will be negotiated automatically between client
444
+ # and server. Ruby 1.9 and newer only.
445
+
446
+ attr_reader :ssl_version
447
+
448
+ ##
449
+ # Where this instance's last-use times live in the thread local variables
450
+
451
+ attr_reader :timeout_key # :nodoc:
452
+
453
+ ##
454
+ # SSL verification callback. Used when ca_file or ca_path is set.
455
+
456
+ attr_reader :verify_callback
457
+
458
+ ##
459
+ # Sets the depth of SSL certificate verification
460
+
461
+ attr_reader :verify_depth
462
+
463
+ ##
464
+ # HTTPS verify mode. Defaults to OpenSSL::SSL::VERIFY_PEER which verifies
465
+ # the server certificate.
466
+ #
467
+ # If no ca_file, ca_path or cert_store is set the default system certificate
468
+ # store is used.
469
+ #
470
+ # You can use +verify_mode+ to override any default values.
471
+
472
+ attr_reader :verify_mode
473
+
474
+ ##
475
+ # Enable retries of non-idempotent requests that change data (e.g. POST
476
+ # requests) when the server has disconnected.
477
+ #
478
+ # This will in the worst case lead to multiple requests with the same data,
479
+ # but it may be useful for some applications. Take care when enabling
480
+ # this option to ensure it is safe to POST or perform other non-idempotent
481
+ # requests to the server.
482
+
483
+ attr_accessor :retry_change_requests
484
+
485
+ ##
486
+ # Creates a new Net::HTTP::Persistent.
487
+ #
488
+ # Set +name+ to keep your connections apart from everybody else's. Not
489
+ # required currently, but highly recommended. Your library name should be
490
+ # good enough. This parameter will be required in a future version.
491
+ #
492
+ # +proxy+ may be set to a URI::HTTP or :ENV to pick up proxy options from
493
+ # the environment. See proxy_from_env for details.
494
+ #
495
+ # In order to use a URI for the proxy you may need to do some extra work
496
+ # beyond URI parsing if the proxy requires a password:
497
+ #
498
+ # proxy = URI 'http://proxy.example'
499
+ # proxy.user = 'AzureDiamond'
500
+ # proxy.password = 'hunter2'
501
+ #
502
+ # Set +pool_size+ to limit the maximum number of connections allowed.
503
+ # Defaults to 1/4 the number of allowed file handles. You can have no more
504
+ # than this many threads with active HTTP transactions.
505
+
506
+ def initialize name: nil, proxy: nil, pool_size: DEFAULT_POOL_SIZE
507
+ @name = name
508
+
509
+ @debug_output = nil
510
+ @proxy_uri = nil
511
+ @no_proxy = []
512
+ @headers = {}
513
+ @override_headers = {}
514
+ @http_versions = {}
515
+ @keep_alive = 30
516
+ @open_timeout = nil
517
+ @read_timeout = nil
518
+ @idle_timeout = 5
519
+ @max_requests = nil
520
+ @socket_options = []
521
+ @ssl_generation = 0 # incremented when SSL session variables change
522
+
523
+ @socket_options << [Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1] if
524
+ Socket.const_defined? :TCP_NODELAY
525
+
526
+ @pool = Net::HTTP::Persistent::Pool.new size: pool_size do |http_args|
527
+ Net::HTTP::Persistent::Connection.new Net::HTTP, http_args, @ssl_generation
528
+ end
529
+
530
+ @certificate = nil
531
+ @ca_file = nil
532
+ @ca_path = nil
533
+ @ciphers = nil
534
+ @private_key = nil
535
+ @ssl_timeout = nil
536
+ @ssl_version = nil
537
+ @verify_callback = nil
538
+ @verify_depth = nil
539
+ @verify_mode = nil
540
+ @cert_store = nil
541
+
542
+ @generation = 0 # incremented when proxy URI changes
543
+
544
+ if HAVE_OPENSSL then
545
+ @verify_mode = OpenSSL::SSL::VERIFY_PEER
546
+ @reuse_ssl_sessions = OpenSSL::SSL.const_defined? :Session
547
+ end
548
+
549
+ @retry_change_requests = false
550
+
551
+ self.proxy = proxy if proxy
552
+ end
553
+
554
+ ##
555
+ # Sets this client's OpenSSL::X509::Certificate
556
+
557
+ def certificate= certificate
558
+ @certificate = certificate
559
+
560
+ reconnect_ssl
561
+ end
562
+
563
+ # For Net::HTTP parity
564
+ alias cert= certificate=
565
+
566
+ ##
567
+ # Sets the SSL certificate authority file.
568
+
569
+ def ca_file= file
570
+ @ca_file = file
571
+
572
+ reconnect_ssl
573
+ end
574
+
575
+ ##
576
+ # Sets the SSL certificate authority path.
577
+
578
+ def ca_path= path
579
+ @ca_path = path
580
+
581
+ reconnect_ssl
582
+ end
583
+
584
+ ##
585
+ # Overrides the default SSL certificate store used for verifying
586
+ # connections.
587
+
588
+ def cert_store= store
589
+ @cert_store = store
590
+
591
+ reconnect_ssl
592
+ end
593
+
594
+ ##
595
+ # The ciphers allowed for SSL connections
596
+
597
+ def ciphers= ciphers
598
+ @ciphers = ciphers
599
+
600
+ reconnect_ssl
601
+ end
602
+
603
+ ##
604
+ # Creates a new connection for +uri+
605
+
606
+ def connection_for uri
607
+ use_ssl = uri.scheme.downcase == 'https'
608
+
609
+ net_http_args = [uri.host, uri.port]
610
+
611
+ net_http_args.concat @proxy_args if
612
+ @proxy_uri and not proxy_bypass? uri.host, uri.port
613
+
614
+ connection = @pool.checkout net_http_args
615
+
616
+ http = connection.http
617
+
618
+ connection.ressl @ssl_generation if
619
+ connection.ssl_generation != @ssl_generation
620
+
621
+ if not http.started? then
622
+ ssl http if use_ssl
623
+ start http
624
+ elsif expired? connection then
625
+ reset connection
626
+ end
627
+
628
+ http.read_timeout = @read_timeout if @read_timeout
629
+ http.keep_alive_timeout = @idle_timeout if @idle_timeout
630
+
631
+ return yield connection
632
+ rescue Errno::ECONNREFUSED
633
+ address = http.proxy_address || http.address
634
+ port = http.proxy_port || http.port
635
+
636
+ raise Error, "connection refused: #{address}:#{port}"
637
+ rescue Errno::EHOSTDOWN
638
+ address = http.proxy_address || http.address
639
+ port = http.proxy_port || http.port
640
+
641
+ raise Error, "host down: #{address}:#{port}"
642
+ ensure
643
+ @pool.checkin net_http_args
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.host}:#{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.host,
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.host}:#{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
+ connection.ssl_timeout = @ssl_timeout if @ssl_timeout
1047
+ connection.ssl_version = @ssl_version if @ssl_version
1048
+
1049
+ connection.verify_depth = @verify_depth
1050
+ connection.verify_mode = @verify_mode
1051
+
1052
+ if OpenSSL::SSL::VERIFY_PEER == OpenSSL::SSL::VERIFY_NONE and
1053
+ not Object.const_defined?(:I_KNOW_THAT_OPENSSL_VERIFY_PEER_EQUALS_VERIFY_NONE_IS_WRONG) then
1054
+ warn <<-WARNING
1055
+ !!!SECURITY WARNING!!!
1056
+
1057
+ The SSL HTTP connection to:
1058
+
1059
+ #{connection.address}:#{connection.port}
1060
+
1061
+ !!!MAY NOT BE VERIFIED!!!
1062
+
1063
+ On your platform your OpenSSL implementation is broken.
1064
+
1065
+ There is no difference between the values of VERIFY_NONE and VERIFY_PEER.
1066
+
1067
+ This means that attempting to verify the security of SSL connections may not
1068
+ work. This exposes you to man-in-the-middle exploits, snooping on the
1069
+ contents of your connection and other dangers to the security of your data.
1070
+
1071
+ To disable this warning define the following constant at top-level in your
1072
+ application:
1073
+
1074
+ I_KNOW_THAT_OPENSSL_VERIFY_PEER_EQUALS_VERIFY_NONE_IS_WRONG = nil
1075
+
1076
+ WARNING
1077
+ end
1078
+
1079
+ connection.ca_file = @ca_file if @ca_file
1080
+ connection.ca_path = @ca_path if @ca_path
1081
+
1082
+ if @ca_file or @ca_path then
1083
+ connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
1084
+ connection.verify_callback = @verify_callback if @verify_callback
1085
+ end
1086
+
1087
+ if @certificate and @private_key then
1088
+ connection.cert = @certificate
1089
+ connection.key = @private_key
1090
+ end
1091
+
1092
+ connection.cert_store = if @cert_store then
1093
+ @cert_store
1094
+ else
1095
+ store = OpenSSL::X509::Store.new
1096
+ store.set_default_paths
1097
+ store
1098
+ end
1099
+ end
1100
+
1101
+ ##
1102
+ # SSL session lifetime
1103
+
1104
+ def ssl_timeout= ssl_timeout
1105
+ @ssl_timeout = ssl_timeout
1106
+
1107
+ reconnect_ssl
1108
+ end
1109
+
1110
+ ##
1111
+ # SSL version to use
1112
+
1113
+ def ssl_version= ssl_version
1114
+ @ssl_version = ssl_version
1115
+
1116
+ reconnect_ssl
1117
+ end
1118
+
1119
+ ##
1120
+ # Sets the depth of SSL certificate verification
1121
+
1122
+ def verify_depth= verify_depth
1123
+ @verify_depth = verify_depth
1124
+
1125
+ reconnect_ssl
1126
+ end
1127
+
1128
+ ##
1129
+ # Sets the HTTPS verify mode. Defaults to OpenSSL::SSL::VERIFY_PEER.
1130
+ #
1131
+ # Setting this to VERIFY_NONE is a VERY BAD IDEA and should NEVER be used.
1132
+ # Securely transfer the correct certificate and update the default
1133
+ # certificate store or set the ca file instead.
1134
+
1135
+ def verify_mode= verify_mode
1136
+ @verify_mode = verify_mode
1137
+
1138
+ reconnect_ssl
1139
+ end
1140
+
1141
+ ##
1142
+ # SSL verification callback.
1143
+
1144
+ def verify_callback= callback
1145
+ @verify_callback = callback
1146
+
1147
+ reconnect_ssl
1148
+ end
1149
+
1150
+ end
1151
+ end
1152
+
1153
+ require_relative 'persistent/connection'
1154
+ require_relative 'persistent/pool'