bundler 1.3.6 → 1.4.0.pre.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of bundler might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.travis.yml +1 -3
- data/CHANGELOG.md +27 -14
- data/CONTRIBUTING.md +2 -2
- data/{CONTRIBUTE.md → DEVELOPMENT.md} +31 -12
- data/ISSUES.md +1 -1
- data/README.md +6 -4
- data/Rakefile +1 -15
- data/bin/bundle +5 -8
- data/bundler.gemspec +1 -1
- data/lib/bundler.rb +37 -21
- data/lib/bundler/cli.rb +33 -21
- data/lib/bundler/constants.rb +5 -0
- data/lib/bundler/current_ruby.rb +88 -0
- data/lib/bundler/definition.rb +35 -11
- data/lib/bundler/dependency.rb +7 -78
- data/lib/bundler/dsl.rb +1 -1
- data/lib/bundler/fetcher.rb +37 -24
- data/lib/bundler/gem_helper.rb +2 -2
- data/lib/bundler/gem_installer.rb +9 -0
- data/lib/bundler/installer.rb +76 -7
- data/lib/bundler/parallel_workers.rb +18 -0
- data/lib/bundler/parallel_workers/thread_worker.rb +27 -0
- data/lib/bundler/parallel_workers/unix_worker.rb +88 -0
- data/lib/bundler/parallel_workers/worker.rb +68 -0
- data/lib/bundler/resolver.rb +17 -11
- data/lib/bundler/rubygems_ext.rb +2 -2
- data/lib/bundler/rubygems_integration.rb +37 -25
- data/lib/bundler/runtime.rb +8 -1
- data/lib/bundler/safe_catch.rb +101 -0
- data/lib/bundler/shared_helpers.rb +27 -1
- data/lib/bundler/source/git.rb +2 -1
- data/lib/bundler/source/git/git_proxy.rb +3 -3
- data/lib/bundler/source/path.rb +3 -2
- data/lib/bundler/source/rubygems.rb +5 -17
- data/lib/bundler/spec_set.rb +16 -1
- data/lib/bundler/templates/newgem/newgem.gemspec.tt +1 -1
- data/lib/bundler/vendor/net/http/persistent.rb +136 -38
- data/lib/bundler/vendor/thor.rb +211 -188
- data/lib/bundler/vendor/thor/actions.rb +19 -19
- data/lib/bundler/vendor/thor/actions/create_link.rb +3 -0
- data/lib/bundler/vendor/thor/actions/directory.rb +30 -10
- data/lib/bundler/vendor/thor/actions/empty_directory.rb +3 -19
- data/lib/bundler/vendor/thor/actions/file_manipulation.rb +6 -3
- data/lib/bundler/vendor/thor/base.rb +101 -97
- data/lib/bundler/vendor/thor/{task.rb → command.rb} +17 -13
- data/lib/bundler/vendor/thor/core_ext/io_binary_read.rb +12 -0
- data/lib/bundler/vendor/thor/error.rb +8 -11
- data/lib/bundler/vendor/thor/group.rb +35 -38
- data/lib/bundler/vendor/thor/invocation.rb +28 -26
- data/lib/bundler/vendor/thor/parser/options.rb +21 -19
- data/lib/bundler/vendor/thor/rake_compat.rb +3 -2
- data/lib/bundler/vendor/thor/runner.rb +22 -21
- data/lib/bundler/vendor/thor/shell/basic.rb +44 -22
- data/lib/bundler/vendor/thor/shell/color.rb +13 -9
- data/lib/bundler/vendor/thor/shell/html.rb +13 -9
- data/lib/bundler/vendor/thor/util.rb +214 -210
- data/lib/bundler/vendor/thor/version.rb +1 -1
- data/lib/bundler/version.rb +1 -1
- data/man/bundle-install.ronn +5 -1
- data/man/gemfile.5.ronn +10 -2
- data/spec/bundler/dsl_spec.rb +3 -3
- data/spec/bundler/gem_helper_spec.rb +14 -17
- data/spec/bundler/safe_catch_spec.rb +37 -0
- data/spec/install/gems/dependency_api_spec.rb +1 -36
- data/spec/install/gems/packed_spec.rb +4 -2
- data/spec/install/gems/resolving_spec.rb +37 -0
- data/spec/install/gems/simple_case_spec.rb +18 -16
- data/spec/install/git_spec.rb +1 -1
- data/spec/other/binstubs_spec.rb +24 -13
- data/spec/other/exec_spec.rb +24 -2
- data/spec/other/help_spec.rb +6 -6
- data/spec/other/outdated_spec.rb +3 -3
- data/spec/quality_spec.rb +3 -2
- data/spec/realworld/dependency_api_spec.rb +1 -1
- data/spec/realworld/edgecases_spec.rb +3 -3
- data/spec/realworld/parallel_install_spec.rb +19 -0
- data/spec/resolver/basic_spec.rb +11 -0
- data/spec/runtime/require_spec.rb +9 -0
- data/spec/runtime/setup_spec.rb +2 -3
- data/spec/spec_helper.rb +0 -1
- data/spec/support/builders.rb +2 -4
- data/spec/support/helpers.rb +4 -8
- data/spec/support/indexes.rb +18 -0
- data/spec/support/streams.rb +13 -0
- metadata +19 -11
- data/lib/bundler/vendor/thor/core_ext/dir_escape.rb +0 -0
- data/lib/bundler/vendor/thor/core_ext/file_binary_read.rb +0 -9
- data/spec/support/artifice/endpoint_host_redirect.rb +0 -15
- data/spec/support/permissions.rb +0 -11
data/lib/bundler/runtime.rb
CHANGED
@@ -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(
|
67
|
+
current = File.expand_path(SharedHelpers.pwd)
|
42
68
|
|
43
69
|
until !File.directory?(current) || current == previous
|
44
70
|
if ENV['BUNDLE_SPEC_RUN']
|
data/lib/bundler/source/git.rb
CHANGED
@@ -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
|
-
|
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/
|
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
|
-
|
130
|
+
SharedHelpers.chdir(path, &blk)
|
131
131
|
end
|
132
132
|
|
133
133
|
def allowed_in_path
|
data/lib/bundler/source/path.rb
CHANGED
@@ -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
|
-
|
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?
|
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
|
data/lib/bundler/spec_set.rb
CHANGED
@@ -109,7 +109,22 @@ module Bundler
|
|
109
109
|
|
110
110
|
def sorted
|
111
111
|
rake = @specs.find { |s| s.name == 'rake' }
|
112
|
-
|
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
|
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
|
-
|
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.
|
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 =
|
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
|
-
|
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
|
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
|
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 =
|
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
|
954
|
-
|
955
|
-
|
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
|
-
|
1008
|
+
reset connection
|
962
1009
|
|
963
|
-
|
964
|
-
|
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
|
#
|