vault 0.6.0 → 0.7.0

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