bundler 1.3.6 → 1.4.0.pre.1

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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -3
  3. data/CHANGELOG.md +27 -14
  4. data/CONTRIBUTING.md +2 -2
  5. data/{CONTRIBUTE.md → DEVELOPMENT.md} +31 -12
  6. data/ISSUES.md +1 -1
  7. data/README.md +6 -4
  8. data/Rakefile +1 -15
  9. data/bin/bundle +5 -8
  10. data/bundler.gemspec +1 -1
  11. data/lib/bundler.rb +37 -21
  12. data/lib/bundler/cli.rb +33 -21
  13. data/lib/bundler/constants.rb +5 -0
  14. data/lib/bundler/current_ruby.rb +88 -0
  15. data/lib/bundler/definition.rb +35 -11
  16. data/lib/bundler/dependency.rb +7 -78
  17. data/lib/bundler/dsl.rb +1 -1
  18. data/lib/bundler/fetcher.rb +37 -24
  19. data/lib/bundler/gem_helper.rb +2 -2
  20. data/lib/bundler/gem_installer.rb +9 -0
  21. data/lib/bundler/installer.rb +76 -7
  22. data/lib/bundler/parallel_workers.rb +18 -0
  23. data/lib/bundler/parallel_workers/thread_worker.rb +27 -0
  24. data/lib/bundler/parallel_workers/unix_worker.rb +88 -0
  25. data/lib/bundler/parallel_workers/worker.rb +68 -0
  26. data/lib/bundler/resolver.rb +17 -11
  27. data/lib/bundler/rubygems_ext.rb +2 -2
  28. data/lib/bundler/rubygems_integration.rb +37 -25
  29. data/lib/bundler/runtime.rb +8 -1
  30. data/lib/bundler/safe_catch.rb +101 -0
  31. data/lib/bundler/shared_helpers.rb +27 -1
  32. data/lib/bundler/source/git.rb +2 -1
  33. data/lib/bundler/source/git/git_proxy.rb +3 -3
  34. data/lib/bundler/source/path.rb +3 -2
  35. data/lib/bundler/source/rubygems.rb +5 -17
  36. data/lib/bundler/spec_set.rb +16 -1
  37. data/lib/bundler/templates/newgem/newgem.gemspec.tt +1 -1
  38. data/lib/bundler/vendor/net/http/persistent.rb +136 -38
  39. data/lib/bundler/vendor/thor.rb +211 -188
  40. data/lib/bundler/vendor/thor/actions.rb +19 -19
  41. data/lib/bundler/vendor/thor/actions/create_link.rb +3 -0
  42. data/lib/bundler/vendor/thor/actions/directory.rb +30 -10
  43. data/lib/bundler/vendor/thor/actions/empty_directory.rb +3 -19
  44. data/lib/bundler/vendor/thor/actions/file_manipulation.rb +6 -3
  45. data/lib/bundler/vendor/thor/base.rb +101 -97
  46. data/lib/bundler/vendor/thor/{task.rb → command.rb} +17 -13
  47. data/lib/bundler/vendor/thor/core_ext/io_binary_read.rb +12 -0
  48. data/lib/bundler/vendor/thor/error.rb +8 -11
  49. data/lib/bundler/vendor/thor/group.rb +35 -38
  50. data/lib/bundler/vendor/thor/invocation.rb +28 -26
  51. data/lib/bundler/vendor/thor/parser/options.rb +21 -19
  52. data/lib/bundler/vendor/thor/rake_compat.rb +3 -2
  53. data/lib/bundler/vendor/thor/runner.rb +22 -21
  54. data/lib/bundler/vendor/thor/shell/basic.rb +44 -22
  55. data/lib/bundler/vendor/thor/shell/color.rb +13 -9
  56. data/lib/bundler/vendor/thor/shell/html.rb +13 -9
  57. data/lib/bundler/vendor/thor/util.rb +214 -210
  58. data/lib/bundler/vendor/thor/version.rb +1 -1
  59. data/lib/bundler/version.rb +1 -1
  60. data/man/bundle-install.ronn +5 -1
  61. data/man/gemfile.5.ronn +10 -2
  62. data/spec/bundler/dsl_spec.rb +3 -3
  63. data/spec/bundler/gem_helper_spec.rb +14 -17
  64. data/spec/bundler/safe_catch_spec.rb +37 -0
  65. data/spec/install/gems/dependency_api_spec.rb +1 -36
  66. data/spec/install/gems/packed_spec.rb +4 -2
  67. data/spec/install/gems/resolving_spec.rb +37 -0
  68. data/spec/install/gems/simple_case_spec.rb +18 -16
  69. data/spec/install/git_spec.rb +1 -1
  70. data/spec/other/binstubs_spec.rb +24 -13
  71. data/spec/other/exec_spec.rb +24 -2
  72. data/spec/other/help_spec.rb +6 -6
  73. data/spec/other/outdated_spec.rb +3 -3
  74. data/spec/quality_spec.rb +3 -2
  75. data/spec/realworld/dependency_api_spec.rb +1 -1
  76. data/spec/realworld/edgecases_spec.rb +3 -3
  77. data/spec/realworld/parallel_install_spec.rb +19 -0
  78. data/spec/resolver/basic_spec.rb +11 -0
  79. data/spec/runtime/require_spec.rb +9 -0
  80. data/spec/runtime/setup_spec.rb +2 -3
  81. data/spec/spec_helper.rb +0 -1
  82. data/spec/support/builders.rb +2 -4
  83. data/spec/support/helpers.rb +4 -8
  84. data/spec/support/indexes.rb +18 -0
  85. data/spec/support/streams.rb +13 -0
  86. metadata +19 -11
  87. data/lib/bundler/vendor/thor/core_ext/dir_escape.rb +0 -0
  88. data/lib/bundler/vendor/thor/core_ext/file_binary_read.rb +0 -9
  89. data/spec/support/artifice/endpoint_host_redirect.rb +0 -15
  90. data/spec/support/permissions.rb +0 -11
@@ -50,6 +50,7 @@ module Bundler
50
50
  /^Missing \w+ (?:file\s*)?([^\s]+.rb)$/i,
51
51
  /^Missing API definition file in (.+)$/i,
52
52
  /^cannot load such file -- (.+)$/i,
53
+ /^dlopen\([^)]*\): Library not loaded: (.+)$/i,
53
54
  ]
54
55
 
55
56
  def require(*groups)
@@ -68,6 +69,8 @@ module Bundler
68
69
  # dependency. If there are none, use the dependency's name
69
70
  # as the autorequire.
70
71
  Array(dep.autorequire || dep.name).each do |file|
72
+ # Allow `require: true` as an alias for `require: <name>`
73
+ file = dep.name if file == true
71
74
  required_file = file
72
75
  Kernel.require file
73
76
  end
@@ -224,9 +227,13 @@ module Bundler
224
227
  rubyopt = [ENV["RUBYOPT"]].compact
225
228
  if rubyopt.empty? || rubyopt.first !~ /-rbundler\/setup/
226
229
  rubyopt.unshift %|-rbundler/setup|
227
- rubyopt.unshift %|-I#{File.expand_path('../..', __FILE__)}|
228
230
  ENV["RUBYOPT"] = rubyopt.join(' ')
229
231
  end
232
+
233
+ # Set RUBYLIB
234
+ rubylib = (ENV["RUBYLIB"] || "").split(File::PATH_SEPARATOR)
235
+ rubylib.unshift File.expand_path('../..', __FILE__)
236
+ ENV["RUBYLIB"] = rubylib.uniq.join(File::PATH_SEPARATOR)
230
237
  end
231
238
 
232
239
  private
@@ -0,0 +1,101 @@
1
+ # SafeCatch provides a mechanism to safely deepen the stack, performing
2
+ # stack-unrolling similar to catch/throw, but using Fiber or Thread to avoid
3
+ # deepening the stack too quickly.
4
+ #
5
+ # The API is the same as that of catch/throw: SafeCatch#safe_catch takes a "tag"
6
+ # to be rescued when some code deeper in the process raises it. If the catch
7
+ # block completes successfully, that value is returned. If the tag is "thrown"
8
+ # by safe_throw, the tag's value is returned. Other exceptions propagate out as
9
+ # normal.
10
+ #
11
+ # The implementation, however, uses fibers or threads along with raise/rescue to
12
+ # handle "deepening" the stack and unrolling it. On implementations where Fiber
13
+ # is available, it will be used. If Fiber is not available, Thread will be used.
14
+ # If neither of these classes are available, Proc will be used, effectively
15
+ # deepening the stack for each recursion as in normal catch/throw.
16
+ #
17
+ # In order to avoid causing a new issue of creating too many fibers or threads,
18
+ # especially on implementations where fibers are actually backed by native
19
+ # threads, the "safe" recursion mechanism is only used every 20 recursions.
20
+ # Based on experiments with JRuby (which seems to suffer the most from
21
+ # excessively deep stacks), this appears to be a sufficient granularity to
22
+ # prevent stack overflow without spinning up excessive numbers of fibers or
23
+ # threads. This value can be adjusted with the BUNDLER_SAFE_RECURSE_EVERY env
24
+ # var; setting it to zero effectively disables safe recursion.
25
+
26
+ module Bundler
27
+ module SafeCatch
28
+ def safe_catch(tag, &block)
29
+ if Bundler.current_ruby.jruby?
30
+ Internal.catch(tag, &block)
31
+ else
32
+ catch(tag, &block)
33
+ end
34
+ end
35
+
36
+ def safe_throw(tag, value = nil)
37
+ if Bundler.current_ruby.jruby?
38
+ Internal.throw(tag, value)
39
+ else
40
+ throw(tag, value)
41
+ end
42
+ end
43
+
44
+ module Internal
45
+ SAFE_RECURSE_EVERY = (ENV['BUNDLER_SAFE_RECURSE_EVERY'] || 20).to_i
46
+
47
+ SAFE_RECURSE_CLASS, SAFE_RECURSE_START = case
48
+ when defined?(Fiber)
49
+ [Fiber, :resume]
50
+ when defined?(Thread)
51
+ [Thread, :join]
52
+ else
53
+ [Proc, :call]
54
+ end
55
+
56
+ @recurse_count = 0
57
+
58
+ def self.catch(tag, &block)
59
+ @recurse_count += 1
60
+ if SAFE_RECURSE_EVERY >= 0 && @recurse_count % SAFE_RECURSE_EVERY == 0
61
+ SAFE_RECURSE_CLASS.new(&block).send(SAFE_RECURSE_START)
62
+ else
63
+ block.call
64
+ end
65
+ rescue Result.matcher(tag)
66
+ $!.value
67
+ end
68
+
69
+ def self.throw(tag, value = nil)
70
+ raise Result.new(tag, value)
71
+ end
72
+
73
+ class Result < StopIteration
74
+ def initialize(tag, value)
75
+ @tag = tag
76
+ @value = value
77
+ end
78
+
79
+ attr_reader :tag, :value
80
+
81
+ # The Matcher class is never instantiated; it is dup'ed and used as a
82
+ # rescue-clause argument to match Result exceptions based on their tags.
83
+ module Matcher
84
+ class << self
85
+ attr_accessor :tag
86
+
87
+ def ===(other)
88
+ other.respond_to? :tag and @tag.equal? other.tag
89
+ end
90
+ end
91
+ end
92
+
93
+ def self.matcher(tag)
94
+ matcher = Matcher.dup
95
+ matcher.tag = tag
96
+ matcher
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -1,7 +1,9 @@
1
1
  require 'pathname'
2
2
  require 'rubygems'
3
3
 
4
+ require 'bundler/constants'
4
5
  require 'bundler/rubygems_integration'
6
+ require 'bundler/current_ruby'
5
7
 
6
8
  module Gem
7
9
  class Dependency
@@ -31,6 +33,30 @@ module Bundler
31
33
  find_gemfile
32
34
  end
33
35
 
36
+ if Bundler.current_ruby.mswin? || Bundler.current_ruby.jruby?
37
+ require 'monitor'
38
+ @chdir_monitor = Monitor.new
39
+ def chdir(dir, &blk)
40
+ @chdir_monitor.synchronize do
41
+ Dir.chdir dir, &blk
42
+ end
43
+ end
44
+
45
+ def pwd
46
+ @chdir_monitor.synchronize do
47
+ Dir.pwd
48
+ end
49
+ end
50
+ else
51
+ def chdir(dir, &blk)
52
+ Dir.chdir dir, &blk
53
+ end
54
+
55
+ def pwd
56
+ Dir.pwd
57
+ end
58
+ end
59
+
34
60
  private
35
61
 
36
62
  def find_gemfile
@@ -38,7 +64,7 @@ module Bundler
38
64
  return given if given && !given.empty?
39
65
 
40
66
  previous = nil
41
- current = File.expand_path(Dir.pwd)
67
+ current = File.expand_path(SharedHelpers.pwd)
42
68
 
43
69
  until !File.directory?(current) || current == previous
44
70
  if ENV['BUNDLE_SPEC_RUN']
@@ -151,7 +151,7 @@ module Bundler
151
151
  end
152
152
 
153
153
  def install(spec)
154
- Bundler.ui.info "Using #{spec.name} (#{spec.version}) from #{to_s} "
154
+ Bundler.ui.info "Using #{spec.name} (#{spec.version}) from #{to_s}"
155
155
  if requires_checkout? && !@copied
156
156
  Bundler.ui.debug " * Checking out revision: #{ref}"
157
157
  git_proxy.copy_to(install_path, submodules)
@@ -159,6 +159,7 @@ module Bundler
159
159
  @copied = true
160
160
  end
161
161
  generate_bin(spec)
162
+ nil
162
163
  end
163
164
 
164
165
  def cache(spec)
@@ -58,7 +58,7 @@ module Bundler
58
58
  File.chmod((0777 & ~File.umask), destination)
59
59
  end
60
60
 
61
- Dir.chdir(destination) do
61
+ SharedHelpers.chdir(destination) do
62
62
  git %|fetch --force --quiet --tags "#{path}"|
63
63
  git "reset --hard #{@revision}"
64
64
 
@@ -95,7 +95,7 @@ module Bundler
95
95
  out
96
96
  else
97
97
  raise GitError, "Bundler is trying to run a `git #{command}` at runtime. You probably need to run `bundle install`. However, " \
98
- "this error message could probably be more useful. Please submit a ticket at http://github.com/carlhuda/bundler/issues " \
98
+ "this error message could probably be more useful. Please submit a ticket at http://github.com/bundler/bundler/issues " \
99
99
  "with steps to reproduce as well as the following\n\nCALLER: #{caller.join("\n")}"
100
100
  end
101
101
  end
@@ -127,7 +127,7 @@ module Bundler
127
127
 
128
128
  def in_path(&blk)
129
129
  checkout unless path.exist?
130
- Dir.chdir(path, &blk)
130
+ SharedHelpers.chdir(path, &blk)
131
131
  end
132
132
 
133
133
  def allowed_in_path
@@ -70,8 +70,9 @@ module Bundler
70
70
  end
71
71
 
72
72
  def install(spec)
73
- Bundler.ui.info "Using #{spec.name} (#{spec.version}) from #{to_s} "
73
+ Bundler.ui.info "Using #{spec.name} (#{spec.version}) from #{to_s}"
74
74
  generate_bin(spec, :disable_extensions)
75
+ nil
75
76
  end
76
77
 
77
78
  def cache(spec)
@@ -189,7 +190,7 @@ module Bundler
189
190
  Bundler.ui.warn "The validation message from Rubygems was:\n #{e.message}"
190
191
  ensure
191
192
  if gem_dir && gem_file
192
- Dir.chdir(gem_dir){ FileUtils.rm_rf(gem_file) if File.exist?(gem_file) }
193
+ FileUtils.rm_rf(gem_dir.join gem_file)
193
194
  end
194
195
  end
195
196
 
@@ -68,12 +68,12 @@ module Bundler
68
68
  end
69
69
 
70
70
  def install(spec)
71
- if installed_specs[spec].any? && gem_dir_exists?(spec)
72
- Bundler.ui.info "Using #{spec.name} (#{spec.version}) "
71
+ if installed_specs[spec].any?
72
+ Bundler.ui.info "Using #{spec.name} (#{spec.version})"
73
73
  return
74
74
  end
75
75
 
76
- Bundler.ui.info "Installing #{spec.name} (#{spec.version}) "
76
+ Bundler.ui.info "Installing #{spec.name} (#{spec.version})"
77
77
  path = cached_gem(spec)
78
78
  if Bundler.requires_sudo?
79
79
  install_path = Bundler.tmp
@@ -94,10 +94,6 @@ module Bundler
94
94
  ).install
95
95
  end
96
96
 
97
- if spec.post_install_message
98
- Installer.post_install_messages[spec.name] = spec.post_install_message
99
- end
100
-
101
97
  # SUDO HAX
102
98
  if Bundler.requires_sudo?
103
99
  Bundler.mkdir_p "#{Bundler.rubygems.gem_dir}/gems"
@@ -109,8 +105,10 @@ module Bundler
109
105
  Bundler.sudo "cp -R #{Bundler.tmp}/bin/#{exe} #{Bundler.system_bindir}"
110
106
  end
111
107
  end
108
+ Bundler.ui.info "Installed #{spec.name} (#{spec.version})"
112
109
  installed_spec.loaded_from = "#{Bundler.rubygems.gem_dir}/specifications/#{spec.full_name}.gemspec"
113
110
  spec.loaded_from = "#{Bundler.rubygems.gem_dir}/specifications/#{spec.full_name}.gemspec"
111
+ spec.post_install_message
114
112
  end
115
113
 
116
114
  def cache(spec)
@@ -250,16 +248,6 @@ module Bundler
250
248
  Bundler.rubygems.sources = old
251
249
  end
252
250
  end
253
-
254
- def gem_dir_exists?(spec)
255
- return true if spec.name == "bundler"
256
- # Ruby 2 default gems
257
- return true if spec.loaded_from.include?("specifications/default/")
258
- # Ruby 1.9 default gems
259
- return true if spec.summary =~ /is bundled with Ruby/
260
-
261
- File.directory?(spec.full_gem_path)
262
- end
263
251
  end
264
252
 
265
253
  end
@@ -109,7 +109,22 @@ module Bundler
109
109
 
110
110
  def sorted
111
111
  rake = @specs.find { |s| s.name == 'rake' }
112
- @sorted ||= ([rake] + tsort).compact.uniq
112
+ begin
113
+ @sorted ||= ([rake] + tsort).compact.uniq
114
+ rescue TSort::Cyclic => error
115
+ cgems = extract_circular_gems(error)
116
+ raise CyclicDependencyError, "Your Gemfile requires gems that depend" \
117
+ " depend on each other, creating an infinite loop. Please remove" \
118
+ " either gem '#{cgems[1]}' or gem '#{cgems[0]}' and try again."
119
+ end
120
+ end
121
+
122
+ def extract_circular_gems(error)
123
+ if Bundler.current_ruby.mri? && Bundler.current_ruby.on_19?
124
+ error.message.scan(/(\w+) \([^)]/).flatten
125
+ else
126
+ error.message.scan(/@name="(.*?)"/).flatten
127
+ end
113
128
  end
114
129
 
115
130
  def lookup
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
13
13
  spec.homepage = ""
14
14
  spec.license = "MIT"
15
15
 
16
- spec.files = `git ls-files -z`.split("\x0")
16
+ spec.files = `git ls-files`.split($/)
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
@@ -1,5 +1,9 @@
1
1
  require 'net/http'
2
- require 'net/https'
2
+ begin
3
+ require 'net/https'
4
+ rescue LoadError
5
+ # net/https or openssl
6
+ end if RUBY_VERSION < '1.9' # but only for 1.8
3
7
  require 'net/http/faster'
4
8
  require 'uri'
5
9
  require 'cgi' # for escaping
@@ -9,6 +13,8 @@ begin
9
13
  rescue LoadError
10
14
  end
11
15
 
16
+ autoload :OpenSSL, 'openssl'
17
+
12
18
  ##
13
19
  # Persistent connections for Net::HTTP
14
20
  #
@@ -37,6 +43,11 @@ end
37
43
  # # perform a GET
38
44
  # response = http.request uri
39
45
  #
46
+ # # or
47
+ #
48
+ # get = Net::HTTP::Get.new uri.request_uri
49
+ # response = http.request get
50
+ #
40
51
  # # create a POST
41
52
  # post_uri = uri + 'create'
42
53
  # post = Net::HTTP::Post.new post_uri.path
@@ -45,6 +56,10 @@ end
45
56
  # # perform the POST, the URI is always required
46
57
  # response http.request post_uri, post
47
58
  #
59
+ # Note that for GET, HEAD and other requests that do not have a body you want
60
+ # to use URI#request_uri not URI#path. The request_uri contains the query
61
+ # params which are sent in the body for other requests.
62
+ #
48
63
  # == SSL
49
64
  #
50
65
  # SSL connections are automatically created depending upon the scheme of the
@@ -105,6 +120,13 @@ end
105
120
  # The amount of time allowed between reading two chunks from the socket. Set
106
121
  # through #read_timeout
107
122
  #
123
+ # === Max Requests
124
+ #
125
+ # The number of requests that should be made before opening a new connection.
126
+ # Typically many keep-alive capable servers tune this to 100 or less, so the
127
+ # 101st request will fail with ECONNRESET. If unset (default), this value has no
128
+ # effect, if set, connections will be reset on the request after max_requests.
129
+ #
108
130
  # === Open Timeout
109
131
  #
110
132
  # The amount of time to wait for a connection to be opened. Set through
@@ -173,10 +195,30 @@ class Net::HTTP::Persistent
173
195
 
174
196
  EPOCH = Time.at 0 # :nodoc:
175
197
 
198
+ ##
199
+ # Is OpenSSL available? This test works with autoload
200
+
201
+ HAVE_OPENSSL = defined? OpenSSL::SSL # :nodoc:
202
+
176
203
  ##
177
204
  # The version of Net::HTTP::Persistent you are using
178
205
 
179
- VERSION = '2.8'
206
+ VERSION = '2.9'
207
+
208
+ ##
209
+ # Exceptions rescued for automatic retry on ruby 2.0.0. This overlaps with
210
+ # the exception list for ruby 1.x.
211
+
212
+ RETRIED_EXCEPTIONS = [ # :nodoc:
213
+ (Net::ReadTimeout if Net.const_defined? :ReadTimeout),
214
+ IOError,
215
+ EOFError,
216
+ Errno::ECONNRESET,
217
+ Errno::ECONNABORTED,
218
+ Errno::EPIPE,
219
+ (OpenSSL::SSL::SSLError if HAVE_OPENSSL),
220
+ Timeout::Error,
221
+ ].compact
180
222
 
181
223
  ##
182
224
  # Error class for errors raised by Net::HTTP::Persistent. Various
@@ -226,6 +268,8 @@ class Net::HTTP::Persistent
226
268
  $stderr.puts "sleeping #{sleep_time}" if $DEBUG
227
269
  sleep sleep_time
228
270
  end
271
+ rescue
272
+ # ignore StandardErrors, we've probably found the idle timeout.
229
273
  ensure
230
274
  http.shutdown
231
275
 
@@ -287,6 +331,12 @@ class Net::HTTP::Persistent
287
331
 
288
332
  attr_accessor :idle_timeout
289
333
 
334
+ ##
335
+ # Maximum number of requests on a connection before it is considered expired
336
+ # and automatically closed.
337
+
338
+ attr_accessor :max_requests
339
+
290
340
  ##
291
341
  # The value sent in the Keep-Alive header. Defaults to 30. Not needed for
292
342
  # HTTP/1.1 servers.
@@ -442,6 +492,7 @@ class Net::HTTP::Persistent
442
492
  @open_timeout = nil
443
493
  @read_timeout = nil
444
494
  @idle_timeout = 5
495
+ @max_requests = nil
445
496
  @socket_options = []
446
497
 
447
498
  @socket_options << [Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1] if
@@ -458,15 +509,22 @@ class Net::HTTP::Persistent
458
509
  @private_key = nil
459
510
  @ssl_version = nil
460
511
  @verify_callback = nil
461
- @verify_mode = OpenSSL::SSL::VERIFY_PEER
512
+ @verify_mode = nil
462
513
  @cert_store = nil
463
514
 
464
515
  @generation = 0 # incremented when proxy URI changes
465
516
  @ssl_generation = 0 # incremented when SSL session variables change
466
- @reuse_ssl_sessions = OpenSSL::SSL.const_defined? :Session
517
+
518
+ if HAVE_OPENSSL then
519
+ @verify_mode = OpenSSL::SSL::VERIFY_PEER
520
+ @reuse_ssl_sessions = OpenSSL::SSL.const_defined? :Session
521
+ end
467
522
 
468
523
  @retry_change_requests = false
469
524
 
525
+ @ruby_1 = RUBY_VERSION < '2'
526
+ @retried_on_ruby_2 = !@ruby_1
527
+
470
528
  self.proxy = proxy if proxy
471
529
  end
472
530
 
@@ -536,6 +594,9 @@ class Net::HTTP::Persistent
536
594
  use_ssl = uri.scheme.downcase == 'https'
537
595
 
538
596
  if use_ssl then
597
+ raise Net::HTTP::Persistent::Error, 'OpenSSL is not available' unless
598
+ HAVE_OPENSSL
599
+
539
600
  ssl_generation = @ssl_generation
540
601
 
541
602
  ssl_cleanup ssl_generation
@@ -606,10 +667,12 @@ class Net::HTTP::Persistent
606
667
  end
607
668
 
608
669
  ##
609
- # Returns true if the connection should be reset due to an idle timeout,
610
- # false otherwise.
670
+ # Returns true if the connection should be reset due to an idle timeout, or
671
+ # maximum request count, false otherwise.
611
672
 
612
673
  def expired? connection
674
+ requests = Thread.current[@request_key][connection.object_id]
675
+ return true if @max_requests && requests >= @max_requests
613
676
  return false unless @idle_timeout
614
677
  return true if @idle_timeout.zero?
615
678
 
@@ -679,10 +742,15 @@ class Net::HTTP::Persistent
679
742
  end
680
743
 
681
744
  ##
682
- # Is the request idempotent or is retry_change_requests allowed
745
+ # Is the request +req+ idempotent or is retry_change_requests allowed.
746
+ #
747
+ # If +retried_on_ruby_2+ is true, true will be returned if we are on ruby,
748
+ # retry_change_requests is allowed and the request is not idempotent.
683
749
 
684
- def can_retry? req
685
- retry_change_requests or idempotent?(req)
750
+ def can_retry? req, retried_on_ruby_2 = false
751
+ return @retry_change_requests && !idempotent?(req) if retried_on_ruby_2
752
+
753
+ @retry_change_requests || idempotent?(req)
686
754
  end
687
755
 
688
756
  if RUBY_VERSION > '1.9' then
@@ -901,31 +969,14 @@ class Net::HTTP::Persistent
901
969
  #
902
970
  # +req+ must be a Net::HTTPRequest subclass (see Net::HTTP for a list).
903
971
  #
904
- # If there is an error and the request is idempontent according to RFC 2616
972
+ # If there is an error and the request is idempotent according to RFC 2616
905
973
  # it will be retried automatically.
906
974
 
907
975
  def request uri, req = nil, &block
908
976
  retried = false
909
977
  bad_response = false
910
978
 
911
- req = Net::HTTP::Get.new uri.request_uri unless req
912
-
913
- @headers.each do |pair|
914
- req.add_field(*pair)
915
- end
916
-
917
- if uri.user or uri.password
918
- req.basic_auth uri.user, uri.password
919
- end
920
-
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
979
+ req = request_setup req || uri
929
980
 
930
981
  connection = connection_for uri
931
982
  connection_id = connection.object_id
@@ -950,23 +1001,25 @@ class Net::HTTP::Persistent
950
1001
 
951
1002
  bad_response = true
952
1003
  retry
953
- rescue IOError, EOFError, Timeout::Error,
954
- Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE,
955
- Errno::EINVAL, OpenSSL::SSL::SSLError => e
956
-
957
- if retried or not can_retry? req
958
- due_to = "(due to #{e.message} - #{e.class})"
959
- message = error_message connection
1004
+ rescue *RETRIED_EXCEPTIONS => e # retried on ruby 2
1005
+ request_failed e, req, connection if
1006
+ retried or not can_retry? req, @retried_on_ruby_2
960
1007
 
961
- finish connection
1008
+ reset connection
962
1009
 
963
- raise Error, "too many connection resets #{due_to} #{message}"
964
- end
1010
+ retried = true
1011
+ retry
1012
+ rescue Errno::EINVAL, Errno::ETIMEDOUT => e # not retried on ruby 2
1013
+ request_failed e, req, connection if retried or not can_retry? req
965
1014
 
966
1015
  reset connection
967
1016
 
968
1017
  retried = true
969
1018
  retry
1019
+ rescue Exception => e
1020
+ finish connection
1021
+
1022
+ raise
970
1023
  ensure
971
1024
  Thread.current[@timeout_key][connection_id] = Time.now
972
1025
  end
@@ -976,6 +1029,51 @@ class Net::HTTP::Persistent
976
1029
  response
977
1030
  end
978
1031
 
1032
+ ##
1033
+ # Raises an Error for +exception+ which resulted from attempting the request
1034
+ # +req+ on the +connection+.
1035
+ #
1036
+ # Finishes the +connection+.
1037
+
1038
+ def request_failed exception, req, connection # :nodoc:
1039
+ due_to = "(due to #{exception.message} - #{exception.class})"
1040
+ message = "too many connection resets #{due_to} #{error_message connection}"
1041
+
1042
+ finish connection
1043
+
1044
+
1045
+ raise Error, message, exception.backtrace
1046
+ end
1047
+
1048
+ ##
1049
+ # Creates a GET request if +req_or_uri+ is a URI and adds headers to the
1050
+ # request.
1051
+ #
1052
+ # Returns the request.
1053
+
1054
+ def request_setup req_or_uri # :nodoc:
1055
+ req = if URI === req_or_uri then
1056
+ Net::HTTP::Get.new req_or_uri.request_uri
1057
+ else
1058
+ req_or_uri
1059
+ end
1060
+
1061
+ @headers.each do |pair|
1062
+ req.add_field(*pair)
1063
+ end
1064
+
1065
+ @override_headers.each do |name, value|
1066
+ req[name] = value
1067
+ end
1068
+
1069
+ unless req['Connection'] then
1070
+ req.add_field 'Connection', 'keep-alive'
1071
+ req.add_field 'Keep-Alive', @keep_alive
1072
+ end
1073
+
1074
+ req
1075
+ end
1076
+
979
1077
  ##
980
1078
  # Shuts down all connections for +thread+.
981
1079
  #