bundler 1.3.0.pre.7 → 1.3.0.pre.8

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bundler might be problematic. Click here for more details.

Files changed (69) hide show
  1. data/.rspec +1 -1
  2. data/.travis.yml +5 -0
  3. data/CHANGELOG.md +47 -0
  4. data/CONTRIBUTE.md +1 -2
  5. data/CONTRIBUTING.md +1 -1
  6. data/{LICENSE → LICENSE.md} +1 -1
  7. data/Rakefile +16 -13
  8. data/bundler.gemspec +20 -24
  9. data/lib/bundler.rb +6 -6
  10. data/lib/bundler/cli.rb +48 -11
  11. data/lib/bundler/deployment.rb +2 -1
  12. data/lib/bundler/deprecate.rb +1 -1
  13. data/lib/bundler/dsl.rb +4 -1
  14. data/lib/bundler/env.rb +1 -1
  15. data/lib/bundler/fetcher.rb +85 -57
  16. data/lib/bundler/friendly_errors.rb +0 -12
  17. data/lib/bundler/index.rb +2 -2
  18. data/lib/bundler/injector.rb +1 -1
  19. data/lib/bundler/installer.rb +7 -3
  20. data/lib/bundler/resolver.rb +17 -0
  21. data/lib/bundler/rubygems_ext.rb +4 -3
  22. data/lib/bundler/rubygems_integration.rb +38 -19
  23. data/lib/bundler/runtime.rb +9 -3
  24. data/lib/bundler/source/git.rb +20 -12
  25. data/lib/bundler/source/git/git_proxy.rb +1 -0
  26. data/lib/bundler/source/path/installer.rb +1 -1
  27. data/lib/bundler/source/rubygems.rb +25 -31
  28. data/lib/bundler/templates/newgem/.travis.yml.tt +3 -0
  29. data/lib/bundler/templates/newgem/LICENSE.txt.tt +1 -1
  30. data/lib/bundler/templates/newgem/newgem.gemspec.tt +17 -16
  31. data/lib/bundler/templates/newgem/test/test_newgem.rb.tt +1 -1
  32. data/lib/bundler/ui.rb +3 -2
  33. data/lib/bundler/vendor/net/http/persistent.rb +743 -91
  34. data/lib/bundler/vendor/net/http/persistent/ssl_reuse.rb +129 -0
  35. data/lib/bundler/vendored_persistent.rb +11 -0
  36. data/lib/bundler/version.rb +1 -1
  37. data/lib/bundler/vlad.rb +1 -1
  38. data/man/bundle-config.ronn +3 -2
  39. data/man/bundle-install.ronn +19 -4
  40. data/man/bundle-package.ronn +1 -1
  41. data/man/bundle-platform.ronn +1 -1
  42. data/man/bundle-update.ronn +5 -5
  43. data/man/gemfile.5.ronn +1 -1
  44. data/spec/bundler/bundler_spec.rb +26 -0
  45. data/spec/cache/git_spec.rb +1 -1
  46. data/spec/install/gems/dependency_api_spec.rb +12 -23
  47. data/spec/install/gems/flex_spec.rb +1 -0
  48. data/spec/install/gems/groups_spec.rb +0 -19
  49. data/spec/install/gems/simple_case_spec.rb +4 -1
  50. data/spec/install/gems/sudo_spec.rb +11 -15
  51. data/spec/install/git_spec.rb +17 -0
  52. data/spec/install/security_policy_spec.rb +78 -0
  53. data/spec/other/licenses_spec.rb +18 -0
  54. data/spec/other/newgem_spec.rb +36 -0
  55. data/spec/other/outdated_spec.rb +10 -2
  56. data/spec/other/show_spec.rb +6 -1
  57. data/spec/realworld/dependency_api_spec.rb +2 -2
  58. data/spec/realworld/edgecases_spec.rb +3 -3
  59. data/spec/resolver/basic_spec.rb +7 -1
  60. data/spec/resolver/platform_spec.rb +1 -1
  61. data/spec/runtime/executable_spec.rb +2 -2
  62. data/spec/runtime/setup_spec.rb +14 -1
  63. data/spec/support/artifice/endpoint.rb +2 -0
  64. data/spec/support/builders.rb +74 -1
  65. data/spec/support/fakeweb/windows.rb +1 -1
  66. data/spec/support/indexes.rb +22 -0
  67. data/spec/support/path.rb +4 -0
  68. data/spec/support/rubygems_ext.rb +1 -0
  69. metadata +63 -83
@@ -8,4 +8,4 @@ class Test<%= config[:constant_name] %> < MiniTest::Unit::TestCase
8
8
  def test_it_does_something_useful
9
9
  assert false
10
10
  end
11
- end
11
+ end
@@ -25,6 +25,7 @@ module Bundler
25
25
  end
26
26
 
27
27
  class Shell < UI
28
+ attr_reader :quiet
28
29
  attr_writer :shell
29
30
 
30
31
  def initialize(options = {})
@@ -53,8 +54,8 @@ module Bundler
53
54
  tell_me(msg, :red, newline)
54
55
  end
55
56
 
56
- def be_quiet!
57
- @quiet = true
57
+ def quiet=(value)
58
+ @quiet = value
58
59
  end
59
60
 
60
61
  def quiet?
@@ -1,8 +1,14 @@
1
1
  require 'net/http'
2
+ require 'net/https'
2
3
  require 'net/http/faster'
3
4
  require 'uri'
4
5
  require 'cgi' # for escaping
5
6
 
7
+ begin
8
+ require 'net/http/pipeline'
9
+ rescue LoadError
10
+ end
11
+
6
12
  ##
7
13
  # Persistent connections for Net::HTTP
8
14
  #
@@ -22,22 +28,155 @@ require 'cgi' # for escaping
22
28
  #
23
29
  # Example:
24
30
  #
25
- # uri = URI.parse 'http://example.com/awesome/web/service'
26
- # http = Net::HTTP::Persistent.new
27
- # stuff = http.request uri # performs a GET
31
+ # require 'net/http/persistent'
32
+ #
33
+ # uri = URI 'http://example.com/awesome/web/service'
34
+ #
35
+ # http = Net::HTTP::Persistent.new 'my_app_name'
36
+ #
37
+ # # perform a GET
38
+ # response = http.request uri
28
39
  #
29
- # # perform a POST
40
+ # # create a POST
30
41
  # post_uri = uri + 'create'
31
42
  # post = Net::HTTP::Post.new post_uri.path
32
43
  # post.set_form_data 'some' => 'cool data'
33
- # http.request post_uri, post # URI is always required
44
+ #
45
+ # # perform the POST, the URI is always required
46
+ # response http.request post_uri, post
47
+ #
48
+ # == SSL
49
+ #
50
+ # SSL connections are automatically created depending upon the scheme of the
51
+ # URI. SSL connections are automatically verified against the default
52
+ # certificate store for your computer. You can override this by changing
53
+ # verify_mode or by specifying an alternate cert_store.
54
+ #
55
+ # Here are the SSL settings, see the individual methods for documentation:
56
+ #
57
+ # #certificate :: This client's certificate
58
+ # #ca_file :: The certificate-authority
59
+ # #cert_store :: An SSL certificate store
60
+ # #private_key :: The client's SSL private key
61
+ # #reuse_ssl_sessions :: Reuse a previously opened SSL session for a new
62
+ # connection
63
+ # #ssl_version :: Which specific SSL version to use
64
+ # #verify_callback :: For server certificate verification
65
+ # #verify_mode :: How connections should be verified
66
+ #
67
+ # == Proxies
68
+ #
69
+ # A proxy can be set through #proxy= or at initialization time by providing a
70
+ # second argument to ::new. The proxy may be the URI of the proxy server or
71
+ # <code>:ENV</code> which will consult environment variables.
72
+ #
73
+ # See #proxy= and #proxy_from_env for details.
74
+ #
75
+ # == Headers
76
+ #
77
+ # Headers may be specified for use in every request. #headers are appended to
78
+ # any headers on the request. #override_headers replace existing headers on
79
+ # the request.
80
+ #
81
+ # The difference between the two can be seen in setting the User-Agent. Using
82
+ # <code>http.headers['User-Agent'] = 'MyUserAgent'</code> will send "Ruby,
83
+ # MyUserAgent" while <code>http.override_headers['User-Agent'] =
84
+ # 'MyUserAgent'</code> will send "MyUserAgent".
85
+ #
86
+ # == Tuning
87
+ #
88
+ # === Segregation
89
+ #
90
+ # By providing an application name to ::new you can separate your connections
91
+ # from the connections of other applications.
92
+ #
93
+ # === Idle Timeout
94
+ #
95
+ # If a connection hasn't been used for this number of seconds it will automatically be
96
+ # reset upon the next use to avoid attempting to send to a closed connection.
97
+ # The default value is 5 seconds. nil means no timeout. Set through #idle_timeout.
98
+ #
99
+ # Reducing this value may help avoid the "too many connection resets" error
100
+ # when sending non-idempotent requests while increasing this value will cause
101
+ # fewer round-trips.
102
+ #
103
+ # === Read Timeout
104
+ #
105
+ # The amount of time allowed between reading two chunks from the socket. Set
106
+ # through #read_timeout
107
+ #
108
+ # === Open Timeout
109
+ #
110
+ # The amount of time to wait for a connection to be opened. Set through
111
+ # #open_timeout.
112
+ #
113
+ # === Socket Options
114
+ #
115
+ # Socket options may be set on newly-created connections. See #socket_options
116
+ # for details.
117
+ #
118
+ # === Non-Idempotent Requests
119
+ #
120
+ # By default non-idempotent requests will not be retried per RFC 2616. By
121
+ # setting retry_change_requests to true requests will automatically be retried
122
+ # once.
123
+ #
124
+ # Only do this when you know that retrying a POST or other non-idempotent
125
+ # request is safe for your application and will not create duplicate
126
+ # resources.
127
+ #
128
+ # The recommended way to handle non-idempotent requests is the following:
129
+ #
130
+ # require 'net/http/persistent'
131
+ #
132
+ # uri = URI 'http://example.com/awesome/web/service'
133
+ # post_uri = uri + 'create'
134
+ #
135
+ # http = Net::HTTP::Persistent.new 'my_app_name'
136
+ #
137
+ # post = Net::HTTP::Post.new post_uri.path
138
+ # # ... fill in POST request
139
+ #
140
+ # begin
141
+ # response = http.request post_uri, post
142
+ # rescue Net::HTTP::Persistent::Error
143
+ #
144
+ # # POST failed, make a new request to verify the server did not process
145
+ # # the request
146
+ # exists_uri = uri + '...'
147
+ # response = http.get exists_uri
148
+ #
149
+ # # Retry if it failed
150
+ # retry if response.code == '404'
151
+ # end
152
+ #
153
+ # The method of determining if the resource was created or not is unique to
154
+ # the particular service you are using. Of course, you will want to add
155
+ # protection from infinite looping.
156
+ #
157
+ # === Connection Termination
158
+ #
159
+ # If you are done using the Net::HTTP::Persistent instance you may shut down
160
+ # all the connections in the current thread with #shutdown. This is not
161
+ # recommended for normal use, it should only be used when it will be several
162
+ # minutes before you make another HTTP request.
163
+ #
164
+ # If you are using multiple threads, call #shutdown in each thread when the
165
+ # thread is done making requests. If you don't call shutdown, that's OK.
166
+ # Ruby will automatically garbage collect and shutdown your HTTP connections
167
+ # when the thread terminates.
34
168
 
35
169
  class Net::HTTP::Persistent
36
170
 
37
171
  ##
38
- # The version of Net::HTTP::Persistent use are using
172
+ # The beginning of Time
39
173
 
40
- VERSION = '1.4.1'
174
+ EPOCH = Time.at 0 # :nodoc:
175
+
176
+ ##
177
+ # The version of Net::HTTP::Persistent you are using
178
+
179
+ VERSION = '2.8'
41
180
 
42
181
  ##
43
182
  # Error class for errors raised by Net::HTTP::Persistent. Various
@@ -46,21 +185,72 @@ class Net::HTTP::Persistent
46
185
 
47
186
  class Error < StandardError; end
48
187
 
188
+ ##
189
+ # Use this method to detect the idle timeout of the host at +uri+. The
190
+ # value returned can be used to configure #idle_timeout. +max+ controls the
191
+ # maximum idle timeout to detect.
192
+ #
193
+ # After
194
+ #
195
+ # Idle timeout detection is performed by creating a connection then
196
+ # performing a HEAD request in a loop until the connection terminates
197
+ # waiting one additional second per loop.
198
+ #
199
+ # NOTE: This may not work on ruby > 1.9.
200
+
201
+ def self.detect_idle_timeout uri, max = 10
202
+ uri = URI uri unless URI::Generic === uri
203
+ uri += '/'
204
+
205
+ req = Net::HTTP::Head.new uri.request_uri
206
+
207
+ http = new 'net-http-persistent detect_idle_timeout'
208
+
209
+ connection = http.connection_for uri
210
+
211
+ sleep_time = 0
212
+
213
+ loop do
214
+ response = connection.request req
215
+
216
+ $stderr.puts "HEAD #{uri} => #{response.code}" if $DEBUG
217
+
218
+ unless Net::HTTPOK === response then
219
+ raise Error, "bad response code #{response.code} detecting idle timeout"
220
+ end
221
+
222
+ break if sleep_time >= max
223
+
224
+ sleep_time += 1
225
+
226
+ $stderr.puts "sleeping #{sleep_time}" if $DEBUG
227
+ sleep sleep_time
228
+ end
229
+ ensure
230
+ http.shutdown
231
+
232
+ return sleep_time unless $!
233
+ end
234
+
49
235
  ##
50
236
  # This client's OpenSSL::X509::Certificate
51
237
 
52
- attr_accessor :certificate
238
+ attr_reader :certificate
239
+
240
+ # For Net::HTTP parity
241
+ alias cert certificate
53
242
 
54
243
  ##
55
244
  # An SSL certificate authority. Setting this will set verify_mode to
56
245
  # VERIFY_PEER.
57
246
 
58
- attr_accessor :ca_file
247
+ attr_reader :ca_file
59
248
 
60
249
  ##
61
- # Where this instance's connections live in the thread local variables
250
+ # An SSL certificate store. Setting this will override the default
251
+ # certificate store. See verify_mode for more information.
62
252
 
63
- attr_reader :connection_key # :nodoc:
253
+ attr_reader :cert_store
64
254
 
65
255
  ##
66
256
  # Sends debug_output to this IO via Net::HTTP#set_debug_output.
@@ -71,7 +261,17 @@ class Net::HTTP::Persistent
71
261
  attr_accessor :debug_output
72
262
 
73
263
  ##
74
- # Headers that are added to every request
264
+ # Current connection generation
265
+
266
+ attr_reader :generation # :nodoc:
267
+
268
+ ##
269
+ # Where this instance's connections live in the thread local variables
270
+
271
+ attr_reader :generation_key # :nodoc:
272
+
273
+ ##
274
+ # Headers that are added to every request using Net::HTTP#add_field
75
275
 
76
276
  attr_reader :headers
77
277
 
@@ -81,6 +281,12 @@ class Net::HTTP::Persistent
81
281
 
82
282
  attr_reader :http_versions
83
283
 
284
+ ##
285
+ # Maximum time an unused connection can remain idle before being
286
+ # automatically closed.
287
+
288
+ attr_accessor :idle_timeout
289
+
84
290
  ##
85
291
  # The value sent in the Keep-Alive header. Defaults to 30. Not needed for
86
292
  # HTTP/1.1 servers.
@@ -103,16 +309,29 @@ class Net::HTTP::Persistent
103
309
 
104
310
  attr_accessor :open_timeout
105
311
 
312
+ ##
313
+ # Headers that are added to every request using Net::HTTP#[]=
314
+
315
+ attr_reader :override_headers
316
+
106
317
  ##
107
318
  # This client's SSL private key
108
319
 
109
- attr_accessor :private_key
320
+ attr_reader :private_key
321
+
322
+ # For Net::HTTP parity
323
+ alias key private_key
110
324
 
111
325
  ##
112
326
  # The URL through which requests will be proxied
113
327
 
114
328
  attr_reader :proxy_uri
115
329
 
330
+ ##
331
+ # List of host suffixes which will not be proxied
332
+
333
+ attr_reader :no_proxy
334
+
116
335
  ##
117
336
  # Seconds to wait until reading one block. See Net::HTTP#read_timeout
118
337
 
@@ -123,18 +342,75 @@ class Net::HTTP::Persistent
123
342
 
124
343
  attr_reader :request_key # :nodoc:
125
344
 
345
+ ##
346
+ # By default SSL sessions are reused to avoid extra SSL handshakes. Set
347
+ # this to false if you have problems communicating with an HTTPS server
348
+ # like:
349
+ #
350
+ # SSL_connect [...] read finished A: unexpected message (OpenSSL::SSL::SSLError)
351
+
352
+ attr_accessor :reuse_ssl_sessions
353
+
354
+ ##
355
+ # An array of options for Socket#setsockopt.
356
+ #
357
+ # By default the TCP_NODELAY option is set on sockets.
358
+ #
359
+ # To set additional options append them to this array:
360
+ #
361
+ # http.socket_options << [Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1]
362
+
363
+ attr_reader :socket_options
364
+
365
+ ##
366
+ # Current SSL connection generation
367
+
368
+ attr_reader :ssl_generation # :nodoc:
369
+
370
+ ##
371
+ # Where this instance's SSL connections live in the thread local variables
372
+
373
+ attr_reader :ssl_generation_key # :nodoc:
374
+
375
+ ##
376
+ # SSL version to use.
377
+ #
378
+ # By default, the version will be negotiated automatically between client
379
+ # and server. Ruby 1.9 and newer only.
380
+
381
+ attr_reader :ssl_version if RUBY_VERSION > '1.9'
382
+
383
+ ##
384
+ # Where this instance's last-use times live in the thread local variables
385
+
386
+ attr_reader :timeout_key # :nodoc:
387
+
126
388
  ##
127
389
  # SSL verification callback. Used when ca_file is set.
128
390
 
129
- attr_accessor :verify_callback
391
+ attr_reader :verify_callback
130
392
 
131
393
  ##
132
- # HTTPS verify mode. Defaults to OpenSSL::SSL::VERIFY_NONE which ignores
133
- # certificate problems.
394
+ # HTTPS verify mode. Defaults to OpenSSL::SSL::VERIFY_PEER which verifies
395
+ # the server certificate.
396
+ #
397
+ # If no ca_file or cert_store is set the default system certificate store is
398
+ # used.
134
399
  #
135
400
  # You can use +verify_mode+ to override any default values.
136
401
 
137
- attr_accessor :verify_mode
402
+ attr_reader :verify_mode
403
+
404
+ ##
405
+ # Enable retries of non-idempotent requests that change data (e.g. POST
406
+ # requests) when the server has disconnected.
407
+ #
408
+ # This will in the worst case lead to multiple requests with the same data,
409
+ # but it may be useful for some applications. Take care when enabling
410
+ # this option to ensure it is safe to POST or perform other non-idempotent
411
+ # requests to the server.
412
+
413
+ attr_accessor :retry_change_requests
138
414
 
139
415
  ##
140
416
  # Creates a new Net::HTTP::Persistent.
@@ -146,89 +422,166 @@ class Net::HTTP::Persistent
146
422
  # +proxy+ may be set to a URI::HTTP or :ENV to pick up proxy options from
147
423
  # the environment. See proxy_from_env for details.
148
424
  #
149
- # In order to use a URI for the proxy you'll need to do some extra work
150
- # beyond URI.parse:
425
+ # In order to use a URI for the proxy you may need to do some extra work
426
+ # beyond URI parsing if the proxy requires a password:
151
427
  #
152
- # proxy = URI.parse 'http://proxy.example'
428
+ # proxy = URI 'http://proxy.example'
153
429
  # proxy.user = 'AzureDiamond'
154
430
  # proxy.password = 'hunter2'
155
431
 
156
432
  def initialize name = nil, proxy = nil
157
433
  @name = name
158
434
 
159
- @proxy_uri = case proxy
160
- when :ENV then proxy_from_env
161
- when URI::HTTP then proxy
162
- when nil then # ignore
163
- else raise ArgumentError, 'proxy must be :ENV or a URI::HTTP'
164
- end
435
+ @debug_output = nil
436
+ @proxy_uri = nil
437
+ @no_proxy = []
438
+ @headers = {}
439
+ @override_headers = {}
440
+ @http_versions = {}
441
+ @keep_alive = 30
442
+ @open_timeout = nil
443
+ @read_timeout = nil
444
+ @idle_timeout = 5
445
+ @socket_options = []
446
+
447
+ @socket_options << [Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1] if
448
+ Socket.const_defined? :TCP_NODELAY
449
+
450
+ key = ['net_http_persistent', name].compact
451
+ @generation_key = [key, 'generations' ].join('_').intern
452
+ @ssl_generation_key = [key, 'ssl_generations'].join('_').intern
453
+ @request_key = [key, 'requests' ].join('_').intern
454
+ @timeout_key = [key, 'timeouts' ].join('_').intern
455
+
456
+ @certificate = nil
457
+ @ca_file = nil
458
+ @private_key = nil
459
+ @ssl_version = nil
460
+ @verify_callback = nil
461
+ @verify_mode = OpenSSL::SSL::VERIFY_PEER
462
+ @cert_store = nil
463
+
464
+ @generation = 0 # incremented when proxy URI changes
465
+ @ssl_generation = 0 # incremented when SSL session variables change
466
+ @reuse_ssl_sessions = OpenSSL::SSL.const_defined? :Session
467
+
468
+ @retry_change_requests = false
469
+
470
+ self.proxy = proxy if proxy
471
+ end
165
472
 
166
- if @proxy_uri then
167
- @proxy_args = [
168
- @proxy_uri.host,
169
- @proxy_uri.port,
170
- @proxy_uri.user,
171
- @proxy_uri.password,
172
- ]
473
+ ##
474
+ # Sets this client's OpenSSL::X509::Certificate
173
475
 
174
- @proxy_connection_id = [nil, *@proxy_args].join ':'
175
- end
476
+ def certificate= certificate
477
+ @certificate = certificate
478
+
479
+ reconnect_ssl
480
+ end
481
+
482
+ # For Net::HTTP parity
483
+ alias cert= certificate=
176
484
 
177
- @debug_output = nil
178
- @headers = {}
179
- @http_versions = {}
180
- @keep_alive = 30
181
- @open_timeout = nil
182
- @read_timeout = nil
485
+ ##
486
+ # Sets the SSL certificate authority file.
487
+
488
+ def ca_file= file
489
+ @ca_file = file
490
+
491
+ reconnect_ssl
492
+ end
493
+
494
+ ##
495
+ # Overrides the default SSL certificate store used for verifying
496
+ # connections.
183
497
 
184
- key = ['net_http_persistent', name, 'connections'].compact.join '_'
185
- @connection_key = key.intern
186
- key = ['net_http_persistent', name, 'requests'].compact.join '_'
187
- @request_key = key.intern
498
+ def cert_store= store
499
+ @cert_store = store
188
500
 
189
- @certificate = nil
190
- @ca_file = nil
191
- @private_key = nil
192
- @verify_callback = nil
193
- @verify_mode = nil
501
+ reconnect_ssl
502
+ end
503
+
504
+ ##
505
+ # Finishes all connections on the given +thread+ that were created before
506
+ # the given +generation+ in the threads +generation_key+ list.
507
+ #
508
+ # See #shutdown for a bunch of scary warning about misusing this method.
509
+
510
+ def cleanup(generation, thread = Thread.current,
511
+ generation_key = @generation_key) # :nodoc:
512
+ timeouts = thread[@timeout_key]
513
+
514
+ (0...generation).each do |old_generation|
515
+ next unless thread[generation_key]
516
+
517
+ conns = thread[generation_key].delete old_generation
518
+
519
+ conns.each_value do |conn|
520
+ finish conn, thread
521
+
522
+ timeouts.delete conn.object_id if timeouts
523
+ end if conns
524
+ end
194
525
  end
195
526
 
196
527
  ##
197
528
  # Creates a new connection for +uri+
198
529
 
199
530
  def connection_for uri
200
- Thread.current[@connection_key] ||= {}
201
- Thread.current[@request_key] ||= Hash.new 0
531
+ Thread.current[@generation_key] ||= Hash.new { |h,k| h[k] = {} }
532
+ Thread.current[@ssl_generation_key] ||= Hash.new { |h,k| h[k] = {} }
533
+ Thread.current[@request_key] ||= Hash.new 0
534
+ Thread.current[@timeout_key] ||= Hash.new EPOCH
535
+
536
+ use_ssl = uri.scheme.downcase == 'https'
537
+
538
+ if use_ssl then
539
+ ssl_generation = @ssl_generation
540
+
541
+ ssl_cleanup ssl_generation
202
542
 
203
- connections = Thread.current[@connection_key]
543
+ connections = Thread.current[@ssl_generation_key][ssl_generation]
544
+ else
545
+ generation = @generation
546
+
547
+ cleanup generation
548
+
549
+ connections = Thread.current[@generation_key][generation]
550
+ end
204
551
 
205
552
  net_http_args = [uri.host, uri.port]
206
553
  connection_id = net_http_args.join ':'
207
554
 
208
- if @proxy_uri then
555
+ if @proxy_uri and not proxy_bypass? uri.host, uri.port then
209
556
  connection_id << @proxy_connection_id
210
557
  net_http_args.concat @proxy_args
211
558
  end
212
559
 
560
+ connection = connections[connection_id]
561
+
213
562
  unless connection = connections[connection_id] then
214
- connections[connection_id] = Net::HTTP.new(*net_http_args)
563
+ connections[connection_id] = http_class.new(*net_http_args)
215
564
  connection = connections[connection_id]
216
- ssl connection if uri.scheme == 'https'
565
+ ssl connection if use_ssl
566
+ else
567
+ reset connection if expired? connection
217
568
  end
218
569
 
219
- unless connection.started? then
220
- connection.set_debug_output @debug_output if @debug_output
221
- connection.open_timeout = @open_timeout if @open_timeout
222
- connection.read_timeout = @read_timeout if @read_timeout
570
+ start connection unless connection.started?
223
571
 
224
- connection.start
225
- end
572
+ connection.read_timeout = @read_timeout if @read_timeout
226
573
 
227
574
  connection
228
575
  rescue Errno::ECONNREFUSED
229
- raise Error, "connection refused: #{connection.address}:#{connection.port}"
576
+ address = connection.proxy_address || connection.address
577
+ port = connection.proxy_port || connection.port
578
+
579
+ raise Error, "connection refused: #{address}:#{port}"
230
580
  rescue Errno::EHOSTDOWN
231
- raise Error, "host down: #{connection.address}:#{connection.port}"
581
+ address = connection.proxy_address || connection.address
582
+ port = connection.proxy_port || connection.port
583
+
584
+ raise Error, "host down: #{address}:#{port}"
232
585
  end
233
586
 
234
587
  ##
@@ -236,10 +589,13 @@ class Net::HTTP::Persistent
236
589
  # this connection
237
590
 
238
591
  def error_message connection
239
- requests =
240
- Thread.current[@request_key][connection.object_id]
592
+ requests = Thread.current[@request_key][connection.object_id] - 1 # fixup
593
+ last_use = Thread.current[@timeout_key][connection.object_id]
241
594
 
242
- "after #{requests} requests on #{connection.object_id}"
595
+ age = Time.now - last_use
596
+
597
+ "after #{requests} requests on #{connection.object_id}, " \
598
+ "last used #{age} seconds ago"
243
599
  end
244
600
 
245
601
  ##
@@ -249,16 +605,61 @@ class Net::HTTP::Persistent
249
605
  CGI.escape str if str
250
606
  end
251
607
 
608
+ ##
609
+ # Returns true if the connection should be reset due to an idle timeout,
610
+ # false otherwise.
611
+
612
+ def expired? connection
613
+ return false unless @idle_timeout
614
+ return true if @idle_timeout.zero?
615
+
616
+ last_used = Thread.current[@timeout_key][connection.object_id]
617
+
618
+ Time.now - last_used > @idle_timeout
619
+ end
620
+
621
+ ##
622
+ # Starts the Net::HTTP +connection+
623
+
624
+ def start connection
625
+ connection.set_debug_output @debug_output if @debug_output
626
+ connection.open_timeout = @open_timeout if @open_timeout
627
+
628
+ connection.start
629
+
630
+ socket = connection.instance_variable_get :@socket
631
+
632
+ if socket then # for fakeweb
633
+ @socket_options.each do |option|
634
+ socket.io.setsockopt(*option)
635
+ end
636
+ end
637
+ end
638
+
252
639
  ##
253
640
  # Finishes the Net::HTTP +connection+
254
641
 
255
- def finish connection
256
- Thread.current[@request_key].delete connection.object_id
642
+ def finish connection, thread = Thread.current
643
+ if requests = thread[@request_key] then
644
+ requests.delete connection.object_id
645
+ end
257
646
 
258
647
  connection.finish
259
648
  rescue IOError
260
649
  end
261
650
 
651
+ def http_class # :nodoc:
652
+ if RUBY_VERSION > '2.0' then
653
+ Net::HTTP
654
+ elsif [:Artifice, :FakeWeb, :WebMock].any? { |klass|
655
+ Object.const_defined?(klass)
656
+ } or not @reuse_ssl_sessions then
657
+ Net::HTTP
658
+ else
659
+ Net::HTTP::Persistent::SSLReuse
660
+ end
661
+ end
662
+
262
663
  ##
263
664
  # Returns the HTTP protocol version for +uri+
264
665
 
@@ -277,6 +678,54 @@ class Net::HTTP::Persistent
277
678
  end
278
679
  end
279
680
 
681
+ ##
682
+ # Is the request idempotent or is retry_change_requests allowed
683
+
684
+ def can_retry? req
685
+ retry_change_requests or idempotent?(req)
686
+ end
687
+
688
+ if RUBY_VERSION > '1.9' then
689
+ ##
690
+ # Workaround for missing Net::HTTPHeader#connection_close? on Ruby 1.8
691
+
692
+ def connection_close? header
693
+ header.connection_close?
694
+ end
695
+
696
+ ##
697
+ # Workaround for missing Net::HTTPHeader#connection_keep_alive? on Ruby 1.8
698
+
699
+ def connection_keep_alive? header
700
+ header.connection_keep_alive?
701
+ end
702
+ else
703
+ ##
704
+ # Workaround for missing Net::HTTPRequest#connection_close? on Ruby 1.8
705
+
706
+ def connection_close? header
707
+ header['connection'] =~ /close/ or header['proxy-connection'] =~ /close/
708
+ end
709
+
710
+ ##
711
+ # Workaround for missing Net::HTTPRequest#connection_keep_alive? on Ruby
712
+ # 1.8
713
+
714
+ def connection_keep_alive? header
715
+ header['connection'] =~ /keep-alive/ or
716
+ header['proxy-connection'] =~ /keep-alive/
717
+ end
718
+ end
719
+
720
+ ##
721
+ # Deprecated in favor of #expired?
722
+
723
+ def max_age # :nodoc:
724
+ return Time.now + 1 unless @idle_timeout
725
+
726
+ Time.now - @idle_timeout
727
+ end
728
+
280
729
  ##
281
730
  # Adds "http://" to the String +uri+ if it is missing.
282
731
 
@@ -284,6 +733,78 @@ class Net::HTTP::Persistent
284
733
  (uri =~ /^https?:/) ? uri : "http://#{uri}"
285
734
  end
286
735
 
736
+ ##
737
+ # Pipelines +requests+ to the HTTP server at +uri+ yielding responses if a
738
+ # block is given. Returns all responses recieved.
739
+ #
740
+ # See
741
+ # Net::HTTP::Pipeline[http://docs.seattlerb.org/net-http-pipeline/Net/HTTP/Pipeline.html]
742
+ # for further details.
743
+ #
744
+ # Only if <tt>net-http-pipeline</tt> was required before
745
+ # <tt>net-http-persistent</tt> #pipeline will be present.
746
+
747
+ def pipeline uri, requests, &block # :yields: responses
748
+ connection = connection_for uri
749
+
750
+ connection.pipeline requests, &block
751
+ end
752
+
753
+ ##
754
+ # Sets this client's SSL private key
755
+
756
+ def private_key= key
757
+ @private_key = key
758
+
759
+ reconnect_ssl
760
+ end
761
+
762
+ # For Net::HTTP parity
763
+ alias key= private_key=
764
+
765
+ ##
766
+ # Sets the proxy server. The +proxy+ may be the URI of the proxy server,
767
+ # the symbol +:ENV+ which will read the proxy from the environment or nil to
768
+ # disable use of a proxy. See #proxy_from_env for details on setting the
769
+ # proxy from the environment.
770
+ #
771
+ # If the proxy URI is set after requests have been made, the next request
772
+ # will shut-down and re-open all connections.
773
+ #
774
+ # The +no_proxy+ query parameter can be used to specify hosts which shouldn't
775
+ # be reached via proxy; if set it should be a comma separated list of
776
+ # hostname suffixes, optionally with +:port+ appended, for example
777
+ # <tt>example.com,some.host:8080</tt>.
778
+
779
+ def proxy= proxy
780
+ @proxy_uri = case proxy
781
+ when :ENV then proxy_from_env
782
+ when URI::HTTP then proxy
783
+ when nil then # ignore
784
+ else raise ArgumentError, 'proxy must be :ENV or a URI::HTTP'
785
+ end
786
+
787
+ @no_proxy.clear
788
+
789
+ if @proxy_uri then
790
+ @proxy_args = [
791
+ @proxy_uri.host,
792
+ @proxy_uri.port,
793
+ @proxy_uri.user,
794
+ @proxy_uri.password,
795
+ ]
796
+
797
+ @proxy_connection_id = [nil, *@proxy_args].join ':'
798
+
799
+ if @proxy_uri.query then
800
+ @no_proxy = CGI.parse(@proxy_uri.query)['no_proxy'].join(',').downcase.split(',').map { |x| x.strip }.reject { |x| x.empty? }
801
+ end
802
+ end
803
+
804
+ reconnect
805
+ reconnect_ssl
806
+ end
807
+
287
808
  ##
288
809
  # Creates a URI for an HTTP proxy server from ENV variables.
289
810
  #
@@ -293,7 +814,13 @@ class Net::HTTP::Persistent
293
814
  # indicated user and password unless HTTP_PROXY contains either of these in
294
815
  # the URI.
295
816
  #
296
- # For Windows users lowercase ENV variables are preferred over uppercase ENV
817
+ # The +NO_PROXY+ ENV variable can be used to specify hosts which shouldn't
818
+ # be reached via proxy; if set it should be a comma separated list of
819
+ # hostname suffixes, optionally with +:port+ appended, for example
820
+ # <tt>example.com,some.host:8080</tt>. When set to <tt>*</tt> no proxy will
821
+ # be returned.
822
+ #
823
+ # For Windows users, lowercase ENV variables are preferred over uppercase ENV
297
824
  # variables.
298
825
 
299
826
  def proxy_from_env
@@ -301,7 +828,16 @@ class Net::HTTP::Persistent
301
828
 
302
829
  return nil if env_proxy.nil? or env_proxy.empty?
303
830
 
304
- uri = URI.parse normalize_uri env_proxy
831
+ uri = URI normalize_uri env_proxy
832
+
833
+ env_no_proxy = ENV['no_proxy'] || ENV['NO_PROXY']
834
+
835
+ # '*' is special case for always bypass
836
+ return nil if env_no_proxy == '*'
837
+
838
+ if env_no_proxy then
839
+ uri.query = "no_proxy=#{escape(env_no_proxy)}"
840
+ end
305
841
 
306
842
  unless uri.user or uri.password then
307
843
  uri.user = escape ENV['http_proxy_user'] || ENV['HTTP_PROXY_USER']
@@ -311,15 +847,45 @@ class Net::HTTP::Persistent
311
847
  uri
312
848
  end
313
849
 
850
+ ##
851
+ # Returns true when proxy should by bypassed for host.
852
+
853
+ def proxy_bypass? host, port
854
+ host = host.downcase
855
+ host_port = [host, port].join ':'
856
+
857
+ @no_proxy.each do |name|
858
+ return true if host[-name.length, name.length] == name or
859
+ host_port[-name.length, name.length] == name
860
+ end
861
+
862
+ false
863
+ end
864
+
865
+ ##
866
+ # Forces reconnection of HTTP connections.
867
+
868
+ def reconnect
869
+ @generation += 1
870
+ end
871
+
872
+ ##
873
+ # Forces reconnection of SSL connections.
874
+
875
+ def reconnect_ssl
876
+ @ssl_generation += 1
877
+ end
878
+
314
879
  ##
315
880
  # Finishes then restarts the Net::HTTP +connection+
316
881
 
317
882
  def reset connection
318
883
  Thread.current[@request_key].delete connection.object_id
884
+ Thread.current[@timeout_key].delete connection.object_id
319
885
 
320
886
  finish connection
321
887
 
322
- connection.start
888
+ start connection
323
889
  rescue Errno::ECONNREFUSED
324
890
  raise Error, "connection refused: #{connection.address}:#{connection.port}"
325
891
  rescue Errno::EHOSTDOWN
@@ -344,7 +910,7 @@ class Net::HTTP::Persistent
344
910
 
345
911
  req = Net::HTTP::Get.new uri.request_uri unless req
346
912
 
347
- headers.each do |pair|
913
+ @headers.each do |pair|
348
914
  req.add_field(*pair)
349
915
  end
350
916
 
@@ -352,8 +918,14 @@ class Net::HTTP::Persistent
352
918
  req.basic_auth uri.user, uri.password
353
919
  end
354
920
 
355
- req.add_field 'Connection', 'keep-alive'
356
- req.add_field 'Keep-Alive', @keep_alive
921
+ @override_headers.each do |name, value|
922
+ req[name] = value
923
+ end
924
+
925
+ unless req['Connection'] then
926
+ req.add_field 'Connection', 'keep-alive'
927
+ req.add_field 'Keep-Alive', @keep_alive
928
+ end
357
929
 
358
930
  connection = connection_for uri
359
931
  connection_id = connection.object_id
@@ -362,20 +934,27 @@ class Net::HTTP::Persistent
362
934
  Thread.current[@request_key][connection_id] += 1
363
935
  response = connection.request req, &block
364
936
 
937
+ if connection_close?(req) or
938
+ (response.http_version <= '1.0' and
939
+ not connection_keep_alive?(response)) or
940
+ connection_close?(response) then
941
+ connection.finish
942
+ end
365
943
  rescue Net::HTTPBadResponse => e
366
944
  message = error_message connection
367
945
 
368
946
  finish connection
369
947
 
370
948
  raise Error, "too many bad responses #{message}" if
371
- bad_response or not idempotent? req
949
+ bad_response or not can_retry? req
372
950
 
373
951
  bad_response = true
374
952
  retry
375
953
  rescue IOError, EOFError, Timeout::Error,
376
- Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE => e
954
+ Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE,
955
+ Errno::EINVAL, OpenSSL::SSL::SSLError => e
377
956
 
378
- if retried or not idempotent? req
957
+ if retried or not can_retry? req
379
958
  due_to = "(due to #{e.message} - #{e.class})"
380
959
  message = error_message connection
381
960
 
@@ -388,6 +967,8 @@ class Net::HTTP::Persistent
388
967
 
389
968
  retried = true
390
969
  retry
970
+ ensure
971
+ Thread.current[@timeout_key][connection_id] = Time.now
391
972
  end
392
973
 
393
974
  @http_versions["#{uri.host}:#{uri.port}"] ||= response.http_version
@@ -409,17 +990,14 @@ class Net::HTTP::Persistent
409
990
  # best to call #shutdown in the thread at the appropriate time instead!
410
991
 
411
992
  def shutdown thread = Thread.current
412
- connections = thread[@connection_key]
993
+ generation = reconnect
994
+ cleanup generation, thread, @generation_key
413
995
 
414
- connections.each do |_, connection|
415
- begin
416
- connection.finish
417
- rescue IOError
418
- end
419
- end if connections
996
+ ssl_generation = reconnect_ssl
997
+ cleanup ssl_generation, thread, @ssl_generation_key
420
998
 
421
- thread[@connection_key] = nil
422
- thread[@request_key] = nil
999
+ thread[@request_key] = nil
1000
+ thread[@timeout_key] = nil
423
1001
  end
424
1002
 
425
1003
  ##
@@ -444,11 +1022,38 @@ class Net::HTTP::Persistent
444
1022
  # Enables SSL on +connection+
445
1023
 
446
1024
  def ssl connection
447
- require 'net/https'
448
1025
  connection.use_ssl = true
449
1026
 
450
- # suppress warning but allow override
451
- connection.verify_mode = OpenSSL::SSL::VERIFY_NONE unless @verify_mode
1027
+ connection.ssl_version = @ssl_version if @ssl_version
1028
+
1029
+ connection.verify_mode = @verify_mode
1030
+
1031
+ if OpenSSL::SSL::VERIFY_PEER == OpenSSL::SSL::VERIFY_NONE and
1032
+ not Object.const_defined?(:I_KNOW_THAT_OPENSSL_VERIFY_PEER_EQUALS_VERIFY_NONE_IS_WRONG) then
1033
+ warn <<-WARNING
1034
+ !!!SECURITY WARNING!!!
1035
+
1036
+ The SSL HTTP connection to:
1037
+
1038
+ #{connection.address}:#{connection.port}
1039
+
1040
+ !!!MAY NOT BE VERIFIED!!!
1041
+
1042
+ On your platform your OpenSSL implementation is broken.
1043
+
1044
+ There is no difference between the values of VERIFY_NONE and VERIFY_PEER.
1045
+
1046
+ This means that attempting to verify the security of SSL connections may not
1047
+ work. This exposes you to man-in-the-middle exploits, snooping on the
1048
+ contents of your connection and other dangers to the security of your data.
1049
+
1050
+ To disable this warning define the following constant at top-level in your
1051
+ application:
1052
+
1053
+ I_KNOW_THAT_OPENSSL_VERIFY_PEER_EQUALS_VERIFY_NONE_IS_WRONG = nil
1054
+
1055
+ WARNING
1056
+ end
452
1057
 
453
1058
  if @ca_file then
454
1059
  connection.ca_file = @ca_file
@@ -461,8 +1066,55 @@ class Net::HTTP::Persistent
461
1066
  connection.key = @private_key
462
1067
  end
463
1068
 
464
- connection.verify_mode = @verify_mode if @verify_mode
1069
+ connection.cert_store = if @cert_store then
1070
+ @cert_store
1071
+ else
1072
+ store = OpenSSL::X509::Store.new
1073
+ store.set_default_paths
1074
+ store
1075
+ end
1076
+ end
1077
+
1078
+ ##
1079
+ # Finishes all connections that existed before the given SSL parameter
1080
+ # +generation+.
1081
+
1082
+ def ssl_cleanup generation # :nodoc:
1083
+ cleanup generation, Thread.current, @ssl_generation_key
1084
+ end
1085
+
1086
+ ##
1087
+ # SSL version to use
1088
+
1089
+ def ssl_version= ssl_version
1090
+ @ssl_version = ssl_version
1091
+
1092
+ reconnect_ssl
1093
+ end if RUBY_VERSION > '1.9'
1094
+
1095
+ ##
1096
+ # Sets the HTTPS verify mode. Defaults to OpenSSL::SSL::VERIFY_PEER.
1097
+ #
1098
+ # Setting this to VERIFY_NONE is a VERY BAD IDEA and should NEVER be used.
1099
+ # Securely transfer the correct certificate and update the default
1100
+ # certificate store or set the ca file instead.
1101
+
1102
+ def verify_mode= verify_mode
1103
+ @verify_mode = verify_mode
1104
+
1105
+ reconnect_ssl
1106
+ end
1107
+
1108
+ ##
1109
+ # SSL verification callback.
1110
+
1111
+ def verify_callback= callback
1112
+ @verify_callback = callback
1113
+
1114
+ reconnect_ssl
465
1115
  end
466
1116
 
467
1117
  end
468
1118
 
1119
+ require 'net/http/persistent/ssl_reuse'
1120
+