rubygems-update 2.0.17 → 2.1.0.rc.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rubygems-update might be problematic. Click here for more details.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +2 -0
- data/.autotest +1 -1
- data/History.txt +82 -153
- data/Manifest.txt +35 -9
- data/Rakefile +35 -36
- data/lib/rubygems.rb +106 -18
- data/lib/rubygems/available_set.rb +68 -0
- data/lib/rubygems/basic_specification.rb +139 -0
- data/lib/rubygems/command_manager.rb +37 -40
- data/lib/rubygems/commands/cert_command.rb +78 -29
- data/lib/rubygems/commands/cleanup_command.rb +2 -2
- data/lib/rubygems/commands/contents_command.rb +101 -58
- data/lib/rubygems/commands/dependency_command.rb +94 -53
- data/lib/rubygems/commands/environment_command.rb +70 -53
- data/lib/rubygems/commands/fetch_command.rb +1 -2
- data/lib/rubygems/commands/help_command.rb +85 -55
- data/lib/rubygems/commands/install_command.rb +84 -42
- data/lib/rubygems/commands/outdated_command.rb +2 -12
- data/lib/rubygems/commands/owner_command.rb +6 -0
- data/lib/rubygems/commands/pristine_command.rb +26 -16
- data/lib/rubygems/commands/sources_command.rb +85 -70
- data/lib/rubygems/commands/uninstall_command.rb +32 -2
- data/lib/rubygems/commands/update_command.rb +111 -75
- data/lib/rubygems/config_file.rb +15 -3
- data/lib/rubygems/core_ext/kernel_require.rb +9 -31
- data/lib/rubygems/defaults.rb +8 -0
- data/lib/rubygems/dependency.rb +4 -2
- data/lib/rubygems/dependency_installer.rb +180 -170
- data/lib/rubygems/dependency_resolver.rb +191 -526
- data/lib/rubygems/dependency_resolver/activation_request.rb +109 -0
- data/lib/rubygems/dependency_resolver/api_set.rb +65 -0
- data/lib/rubygems/dependency_resolver/api_specification.rb +36 -0
- data/lib/rubygems/dependency_resolver/composed_set.rb +18 -0
- data/lib/rubygems/dependency_resolver/current_set.rb +16 -0
- data/lib/rubygems/dependency_resolver/dependency_conflict.rb +85 -0
- data/lib/rubygems/dependency_resolver/dependency_request.rb +51 -0
- data/lib/rubygems/dependency_resolver/index_set.rb +59 -0
- data/lib/rubygems/dependency_resolver/index_specification.rb +53 -0
- data/lib/rubygems/dependency_resolver/installed_specification.rb +38 -0
- data/lib/rubygems/dependency_resolver/installer_set.rb +130 -0
- data/lib/rubygems/exceptions.rb +88 -1
- data/lib/rubygems/ext/builder.rb +1 -1
- data/lib/rubygems/gem_runner.rb +17 -9
- data/lib/rubygems/gemcutter_utilities.rb +72 -42
- data/lib/rubygems/install_default_message.rb +12 -0
- data/lib/rubygems/install_update_options.rb +3 -0
- data/lib/rubygems/installer.rb +55 -30
- data/lib/rubygems/name_tuple.rb +18 -7
- data/lib/rubygems/package.rb +50 -25
- data/lib/rubygems/package/tar_test_case.rb +9 -9
- data/lib/rubygems/package/tar_writer.rb +35 -12
- data/lib/rubygems/package_task.rb +2 -5
- data/lib/rubygems/path_support.rb +10 -0
- data/lib/rubygems/platform.rb +9 -3
- data/lib/rubygems/psych_additions.rb +1 -1
- data/lib/rubygems/remote_fetcher.rb +9 -276
- data/lib/rubygems/request.rb +267 -0
- data/lib/rubygems/request_set.rb +123 -125
- data/lib/rubygems/request_set/gem_dependency_api.rb +39 -0
- data/lib/rubygems/security.rb +32 -23
- data/lib/rubygems/security/policy.rb +35 -9
- data/lib/rubygems/security/signer.rb +2 -2
- data/lib/rubygems/server.rb +8 -16
- data/lib/rubygems/source.rb +25 -14
- data/lib/rubygems/source/installed.rb +28 -0
- data/lib/rubygems/source/local.rb +122 -0
- data/lib/rubygems/source/specific_file.rb +28 -0
- data/lib/rubygems/source_local.rb +2 -89
- data/lib/rubygems/source_specific_file.rb +2 -26
- data/lib/rubygems/spec_fetcher.rb +11 -11
- data/lib/rubygems/specification.rb +186 -198
- data/lib/rubygems/ssl_certs/AddTrustExternalCARoot.pem +88 -30
- data/lib/rubygems/ssl_certs/Entrust_net-Secure-Server-Certification-Authority.pem +90 -0
- data/lib/rubygems/ssl_certs/{GeoTrustGlobalCA.pem → GeoTrust_Global_CA.pem} +20 -20
- data/lib/rubygems/ssl_certs/VerisignClass3PublicPrimaryCertificationAuthority-G2.pem +57 -0
- data/lib/rubygems/stub_specification.rb +119 -0
- data/lib/rubygems/test_case.rb +117 -49
- data/lib/rubygems/uninstaller.rb +14 -9
- data/lib/rubygems/uri_formatter.rb +39 -0
- data/lib/rubygems/util/list.rb +44 -0
- data/lib/rubygems/version.rb +15 -5
- data/lib/rubygems/version_option.rb +8 -2
- data/test/rubygems/ca_cert.pem +23 -0
- data/test/rubygems/client.pem +49 -0
- data/test/rubygems/encrypted_private_key.pem +30 -0
- data/test/rubygems/invalid_client.pem +49 -0
- data/test/rubygems/specifications/bar-0.0.2.gemspec +9 -0
- data/test/rubygems/specifications/foo-0.0.1.gemspec +0 -0
- data/test/rubygems/test_gem.rb +76 -454
- data/test/rubygems/test_gem_command_manager.rb +23 -21
- data/test/rubygems/test_gem_commands_cert_command.rb +154 -14
- data/test/rubygems/test_gem_commands_cleanup_command.rb +15 -0
- data/test/rubygems/test_gem_commands_contents_command.rb +32 -4
- data/test/rubygems/test_gem_commands_environment_command.rb +9 -1
- data/test/rubygems/test_gem_commands_fetch_command.rb +2 -28
- data/test/rubygems/test_gem_commands_help_command.rb +6 -3
- data/test/rubygems/test_gem_commands_install_command.rb +2 -65
- data/test/rubygems/test_gem_commands_owner_command.rb +49 -0
- data/test/rubygems/test_gem_commands_pristine_command.rb +30 -0
- data/test/rubygems/test_gem_commands_sources_command.rb +1 -1
- data/test/rubygems/test_gem_commands_uninstall_command.rb +33 -0
- data/test/rubygems/test_gem_commands_update_command.rb +2 -1
- data/test/rubygems/test_gem_config_file.rb +12 -0
- data/test/rubygems/test_gem_dependency_installer.rb +58 -65
- data/test/rubygems/test_gem_dependency_resolver.rb +6 -3
- data/test/rubygems/test_gem_dependency_resolver_dependency_conflict.rb +36 -0
- data/test/rubygems/test_gem_ext_builder.rb +2 -4
- data/test/rubygems/test_gem_ext_ext_conf_builder.rb +7 -2
- data/test/rubygems/test_gem_gem_runner.rb +17 -13
- data/test/rubygems/test_gem_gemcutter_utilities.rb +6 -19
- data/test/rubygems/test_gem_impossible_dependencies_error.rb +41 -0
- data/test/rubygems/test_gem_install_update_options.rb +4 -1
- data/test/rubygems/test_gem_installer.rb +31 -2
- data/test/rubygems/test_gem_name_tuple.rb +22 -0
- data/test/rubygems/test_gem_package.rb +122 -11
- data/test/rubygems/test_gem_package_old.rb +8 -0
- data/test/rubygems/test_gem_package_tar_reader.rb +9 -8
- data/test/rubygems/test_gem_package_tar_reader_entry.rb +1 -1
- data/test/rubygems/test_gem_package_tar_writer.rb +78 -56
- data/test/rubygems/test_gem_package_task.rb +2 -23
- data/test/rubygems/test_gem_path_support.rb +17 -0
- data/test/rubygems/test_gem_platform.rb +18 -0
- data/test/rubygems/test_gem_remote_fetcher.rb +106 -385
- data/test/rubygems/test_gem_request.rb +239 -0
- data/test/rubygems/test_gem_requirement.rb +9 -11
- data/test/rubygems/test_gem_security.rb +58 -2
- data/test/rubygems/test_gem_security_policy.rb +42 -1
- data/test/rubygems/test_gem_security_signer.rb +13 -1
- data/test/rubygems/test_gem_security_trust_dir.rb +5 -1
- data/test/rubygems/test_gem_server.rb +1 -105
- data/test/rubygems/test_gem_source.rb +4 -14
- data/test/rubygems/test_gem_source_local.rb +4 -4
- data/test/rubygems/test_gem_source_specific_file.rb +1 -1
- data/test/rubygems/test_gem_spec_fetcher.rb +0 -12
- data/test/rubygems/test_gem_specification.rb +452 -28
- data/test/rubygems/test_gem_stub_specification.rb +30 -0
- data/test/rubygems/test_gem_uninstaller.rb +14 -0
- data/test/rubygems/test_gem_uri_formatter.rb +20 -0
- data/test/rubygems/test_gem_version.rb +23 -13
- data/test/rubygems/test_gem_version_option.rb +63 -1
- data/test/rubygems/test_require.rb +0 -12
- data/util/create_encrypted_key.rb +16 -0
- metadata +161 -23
- metadata.gz.sig +0 -0
- data/CVE-2013-4287.txt +0 -36
- data/CVE-2013-4363.txt +0 -45
- data/lib/rubygems/ssl_certs/AddTrustExternalCARoot-2048.pem +0 -25
- data/lib/rubygems/ssl_certs/Class3PublicPrimaryCertificationAuthority.pem +0 -14
- data/lib/rubygems/ssl_certs/DigiCertHighAssuranceEVRootCA.pem +0 -23
- data/lib/rubygems/ssl_certs/EntrustnetSecureServerCertificationAuthority.pem +0 -28
- data/test/rubygems/test_bundled_ca.rb +0 -59
- data/util/update_bundled_ca_certificates.rb +0 -103
@@ -96,15 +96,12 @@ class Gem::PackageTask < Rake::PackageTask
|
|
96
96
|
def define
|
97
97
|
super
|
98
98
|
|
99
|
+
task :package => [:gem]
|
100
|
+
|
99
101
|
gem_file = File.basename gem_spec.cache_file
|
100
102
|
gem_path = File.join package_dir, gem_file
|
101
103
|
gem_dir = File.join package_dir, gem_spec.full_name
|
102
104
|
|
103
|
-
task :package => [:gem]
|
104
|
-
|
105
|
-
directory package_dir
|
106
|
-
directory gem_dir
|
107
|
-
|
108
105
|
desc "Build the gem file #{gem_file}"
|
109
106
|
task :gem => [gem_path]
|
110
107
|
|
@@ -12,6 +12,10 @@ class Gem::PathSupport
|
|
12
12
|
# Array of paths to search for Gems.
|
13
13
|
attr_reader :path
|
14
14
|
|
15
|
+
##
|
16
|
+
# Directory with spec cache
|
17
|
+
attr_reader :spec_cache_dir # :nodoc:
|
18
|
+
|
15
19
|
##
|
16
20
|
#
|
17
21
|
# Constructor. Takes a single argument which is to be treated like a
|
@@ -28,6 +32,12 @@ class Gem::PathSupport
|
|
28
32
|
end
|
29
33
|
|
30
34
|
self.path = env["GEM_PATH"] || ENV["GEM_PATH"]
|
35
|
+
|
36
|
+
@spec_cache_dir =
|
37
|
+
env["GEM_SPEC_CACHE"] || ENV["GEM_SPEC_CACHE"] ||
|
38
|
+
Gem.default_spec_cache_dir
|
39
|
+
|
40
|
+
@spec_cache_dir = @spec_cache_dir.dup.untaint
|
31
41
|
end
|
32
42
|
|
33
43
|
private
|
data/lib/rubygems/platform.rb
CHANGED
@@ -2,6 +2,8 @@ require "rubygems/deprecate"
|
|
2
2
|
|
3
3
|
##
|
4
4
|
# Available list of platforms for targeting Gem installations.
|
5
|
+
#
|
6
|
+
# See `gem help platform` for information on platform matching.
|
5
7
|
|
6
8
|
class Gem::Platform
|
7
9
|
|
@@ -129,12 +131,16 @@ class Gem::Platform
|
|
129
131
|
# Does +other+ match this platform? Two platforms match if they have the
|
130
132
|
# same CPU, or either has a CPU of 'universal', they have the same OS, and
|
131
133
|
# they have the same version, or either has no version.
|
134
|
+
#
|
135
|
+
# Additionally, the platform will match if the local CPU is 'arm' and the
|
136
|
+
# other CPU starts with "arm" (for generic ARM family support).
|
132
137
|
|
133
138
|
def ===(other)
|
134
139
|
return nil unless Gem::Platform === other
|
135
140
|
|
136
141
|
# cpu
|
137
|
-
(@cpu == 'universal' or other.cpu == 'universal' or @cpu == other.cpu
|
142
|
+
(@cpu == 'universal' or other.cpu == 'universal' or @cpu == other.cpu or
|
143
|
+
(@cpu == 'arm' and other.cpu =~ /\Aarm/)) and
|
138
144
|
|
139
145
|
# os
|
140
146
|
@os == other.os and
|
@@ -175,13 +181,13 @@ class Gem::Platform
|
|
175
181
|
end
|
176
182
|
|
177
183
|
##
|
178
|
-
# A pure-
|
184
|
+
# A pure-Ruby gem that may use Gem::Specification#extensions to build
|
179
185
|
# binary files.
|
180
186
|
|
181
187
|
RUBY = 'ruby'
|
182
188
|
|
183
189
|
##
|
184
|
-
# A platform-specific gem that is built for the packaging
|
190
|
+
# A platform-specific gem that is built for the packaging Ruby's platform.
|
185
191
|
# This will be replaced with Gem::Platform::local.
|
186
192
|
|
187
193
|
CURRENT = 'current'
|
@@ -1,8 +1,7 @@
|
|
1
1
|
require 'rubygems'
|
2
|
+
require 'rubygems/request'
|
3
|
+
require 'rubygems/uri_formatter'
|
2
4
|
require 'rubygems/user_interaction'
|
3
|
-
require 'cgi'
|
4
|
-
require 'thread'
|
5
|
-
require 'uri'
|
6
5
|
require 'resolv'
|
7
6
|
|
8
7
|
##
|
@@ -73,18 +72,7 @@ class Gem::RemoteFetcher
|
|
73
72
|
|
74
73
|
Socket.do_not_reverse_lookup = true
|
75
74
|
|
76
|
-
@
|
77
|
-
@connections_mutex = Mutex.new
|
78
|
-
@requests = Hash.new 0
|
79
|
-
@proxy_uri =
|
80
|
-
case proxy
|
81
|
-
when :no_proxy then nil
|
82
|
-
when nil then get_proxy_from_env
|
83
|
-
when URI::HTTP then proxy
|
84
|
-
else URI.parse(proxy)
|
85
|
-
end
|
86
|
-
@user_agent = user_agent
|
87
|
-
@env_no_proxy = get_no_proxy_from_env
|
75
|
+
@proxy = proxy
|
88
76
|
|
89
77
|
@dns = dns
|
90
78
|
end
|
@@ -103,13 +91,7 @@ class Gem::RemoteFetcher
|
|
103
91
|
rescue Resolv::ResolvError
|
104
92
|
uri
|
105
93
|
else
|
106
|
-
|
107
|
-
|
108
|
-
if /\.#{Regexp.quote(host)}\z/ =~ target
|
109
|
-
return URI.parse "#{uri.scheme}://#{target}#{uri.path}"
|
110
|
-
end
|
111
|
-
|
112
|
-
uri
|
94
|
+
URI.parse "#{res.target}#{uri.path}"
|
113
95
|
end
|
114
96
|
end
|
115
97
|
|
@@ -209,7 +191,7 @@ class Gem::RemoteFetcher
|
|
209
191
|
source_uri.path
|
210
192
|
end
|
211
193
|
|
212
|
-
source_path = unescape
|
194
|
+
source_path = Gem::UriFormatter.new(source_path).unescape
|
213
195
|
|
214
196
|
begin
|
215
197
|
FileUtils.cp source_path, local_gem_path unless
|
@@ -328,136 +310,6 @@ class Gem::RemoteFetcher
|
|
328
310
|
response['content-length'].to_i
|
329
311
|
end
|
330
312
|
|
331
|
-
def escape_auth_info(str)
|
332
|
-
str && CGI.escape(str)
|
333
|
-
end
|
334
|
-
|
335
|
-
def unescape_auth_info(str)
|
336
|
-
str && CGI.unescape(str)
|
337
|
-
end
|
338
|
-
|
339
|
-
def escape(str)
|
340
|
-
return unless str
|
341
|
-
@uri_parser ||= uri_escaper
|
342
|
-
@uri_parser.escape str
|
343
|
-
end
|
344
|
-
|
345
|
-
def unescape(str)
|
346
|
-
return unless str
|
347
|
-
@uri_parser ||= uri_escaper
|
348
|
-
@uri_parser.unescape str
|
349
|
-
end
|
350
|
-
|
351
|
-
def uri_escaper
|
352
|
-
URI::Parser.new
|
353
|
-
rescue NameError
|
354
|
-
URI
|
355
|
-
end
|
356
|
-
|
357
|
-
##
|
358
|
-
# Returns list of no_proxy entries (if any) from the environment
|
359
|
-
|
360
|
-
def get_no_proxy_from_env
|
361
|
-
env_no_proxy = ENV['no_proxy'] || ENV['NO_PROXY']
|
362
|
-
|
363
|
-
return [] if env_no_proxy.nil? or env_no_proxy.empty?
|
364
|
-
|
365
|
-
env_no_proxy.split(/\s*,\s*/)
|
366
|
-
end
|
367
|
-
|
368
|
-
##
|
369
|
-
# Returns an HTTP proxy URI if one is set in the environment variables.
|
370
|
-
|
371
|
-
def get_proxy_from_env
|
372
|
-
env_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
|
373
|
-
|
374
|
-
return nil if env_proxy.nil? or env_proxy.empty?
|
375
|
-
|
376
|
-
uri = URI.parse(normalize_uri(env_proxy))
|
377
|
-
|
378
|
-
if uri and uri.user.nil? and uri.password.nil? then
|
379
|
-
# Probably we have http_proxy_* variables?
|
380
|
-
uri.user = escape_auth_info(ENV['http_proxy_user'] || ENV['HTTP_PROXY_USER'])
|
381
|
-
uri.password = escape_auth_info(ENV['http_proxy_pass'] || ENV['HTTP_PROXY_PASS'])
|
382
|
-
end
|
383
|
-
|
384
|
-
uri
|
385
|
-
end
|
386
|
-
|
387
|
-
##
|
388
|
-
# Normalize the URI by adding "http://" if it is missing.
|
389
|
-
|
390
|
-
def normalize_uri(uri)
|
391
|
-
(uri =~ /^(https?|ftp|file):/i) ? uri : "http://#{uri}"
|
392
|
-
end
|
393
|
-
|
394
|
-
##
|
395
|
-
# Creates or an HTTP connection based on +uri+, or retrieves an existing
|
396
|
-
# connection, using a proxy if needed.
|
397
|
-
|
398
|
-
def connection_for(uri)
|
399
|
-
net_http_args = [uri.host, uri.port]
|
400
|
-
|
401
|
-
if @proxy_uri and not no_proxy?(uri.host) then
|
402
|
-
net_http_args += [
|
403
|
-
@proxy_uri.host,
|
404
|
-
@proxy_uri.port,
|
405
|
-
unescape_auth_info(@proxy_uri.user),
|
406
|
-
unescape_auth_info(@proxy_uri.password)
|
407
|
-
]
|
408
|
-
end
|
409
|
-
|
410
|
-
connection_id = [Thread.current.object_id, *net_http_args].join ':'
|
411
|
-
|
412
|
-
connection = @connections_mutex.synchronize do
|
413
|
-
@connections[connection_id] ||= Net::HTTP.new(*net_http_args)
|
414
|
-
@connections[connection_id]
|
415
|
-
end
|
416
|
-
|
417
|
-
if https?(uri) and not connection.started? then
|
418
|
-
configure_connection_for_https(connection)
|
419
|
-
end
|
420
|
-
|
421
|
-
connection.start unless connection.started?
|
422
|
-
|
423
|
-
connection
|
424
|
-
rescue defined?(OpenSSL::SSL) ? OpenSSL::SSL::SSLError : Errno::EHOSTDOWN,
|
425
|
-
Errno::EHOSTDOWN => e
|
426
|
-
raise FetchError.new(e.message, uri)
|
427
|
-
end
|
428
|
-
|
429
|
-
def configure_connection_for_https(connection)
|
430
|
-
require 'net/https'
|
431
|
-
connection.use_ssl = true
|
432
|
-
connection.verify_mode =
|
433
|
-
Gem.configuration.ssl_verify_mode || OpenSSL::SSL::VERIFY_PEER
|
434
|
-
store = OpenSSL::X509::Store.new
|
435
|
-
if Gem.configuration.ssl_ca_cert
|
436
|
-
if File.directory? Gem.configuration.ssl_ca_cert
|
437
|
-
store.add_path Gem.configuration.ssl_ca_cert
|
438
|
-
else
|
439
|
-
store.add_file Gem.configuration.ssl_ca_cert
|
440
|
-
end
|
441
|
-
else
|
442
|
-
store.set_default_paths
|
443
|
-
add_rubygems_trusted_certs(store)
|
444
|
-
end
|
445
|
-
connection.cert_store = store
|
446
|
-
rescue LoadError => e
|
447
|
-
raise unless (e.respond_to?(:path) && e.path == 'openssl') ||
|
448
|
-
e.message =~ / -- openssl$/
|
449
|
-
|
450
|
-
raise Gem::Exception.new(
|
451
|
-
'Unable to require openssl, install OpenSSL and rebuild ruby (preferred) or use non-HTTPS sources')
|
452
|
-
end
|
453
|
-
|
454
|
-
def add_rubygems_trusted_certs(store)
|
455
|
-
pattern = File.expand_path("./ssl_certs/*.pem", File.dirname(__FILE__))
|
456
|
-
Dir.glob(pattern).each do |ssl_cert_file|
|
457
|
-
store.add_file ssl_cert_file
|
458
|
-
end
|
459
|
-
end
|
460
|
-
|
461
313
|
def correct_for_windows_path(path)
|
462
314
|
if path[0].chr == '/' && path[1].chr =~ /[a-z]/i && path[2].chr == ':'
|
463
315
|
path = path[1..-1]
|
@@ -466,136 +318,17 @@ class Gem::RemoteFetcher
|
|
466
318
|
end
|
467
319
|
end
|
468
320
|
|
469
|
-
def no_proxy? host
|
470
|
-
host = host.downcase
|
471
|
-
@env_no_proxy.each do |pattern|
|
472
|
-
pattern = pattern.downcase
|
473
|
-
return true if host[-pattern.length, pattern.length ] == pattern
|
474
|
-
end
|
475
|
-
return false
|
476
|
-
end
|
477
|
-
|
478
321
|
##
|
479
322
|
# Performs a Net::HTTP request of type +request_class+ on +uri+ returning
|
480
323
|
# a Net::HTTP response object. request maintains a table of persistent
|
481
324
|
# connections to reduce connect overhead.
|
482
325
|
|
483
326
|
def request(uri, request_class, last_modified = nil)
|
484
|
-
request =
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
end
|
489
|
-
|
490
|
-
request.add_field 'User-Agent', @user_agent
|
491
|
-
request.add_field 'Connection', 'keep-alive'
|
492
|
-
request.add_field 'Keep-Alive', '30'
|
493
|
-
|
494
|
-
if last_modified then
|
495
|
-
last_modified = last_modified.utc
|
496
|
-
request.add_field 'If-Modified-Since', last_modified.rfc2822
|
327
|
+
request = Gem::Request.new uri, request_class, last_modified, @proxy
|
328
|
+
|
329
|
+
request.fetch do |req|
|
330
|
+
yield req if block_given?
|
497
331
|
end
|
498
|
-
|
499
|
-
yield request if block_given?
|
500
|
-
|
501
|
-
connection = connection_for uri
|
502
|
-
|
503
|
-
retried = false
|
504
|
-
bad_response = false
|
505
|
-
|
506
|
-
begin
|
507
|
-
@requests[connection.object_id] += 1
|
508
|
-
|
509
|
-
say "#{request.method} #{uri}" if
|
510
|
-
Gem.configuration.really_verbose
|
511
|
-
|
512
|
-
file_name = File.basename(uri.path)
|
513
|
-
# perform download progress reporter only for gems
|
514
|
-
if request.response_body_permitted? && file_name =~ /\.gem$/
|
515
|
-
reporter = ui.download_reporter
|
516
|
-
response = connection.request(request) do |incomplete_response|
|
517
|
-
if Net::HTTPOK === incomplete_response
|
518
|
-
reporter.fetch(file_name, incomplete_response.content_length)
|
519
|
-
downloaded = 0
|
520
|
-
data = ''
|
521
|
-
|
522
|
-
incomplete_response.read_body do |segment|
|
523
|
-
data << segment
|
524
|
-
downloaded += segment.length
|
525
|
-
reporter.update(downloaded)
|
526
|
-
end
|
527
|
-
reporter.done
|
528
|
-
if incomplete_response.respond_to? :body=
|
529
|
-
incomplete_response.body = data
|
530
|
-
else
|
531
|
-
incomplete_response.instance_variable_set(:@body, data)
|
532
|
-
end
|
533
|
-
end
|
534
|
-
end
|
535
|
-
else
|
536
|
-
response = connection.request request
|
537
|
-
end
|
538
|
-
|
539
|
-
say "#{response.code} #{response.message}" if
|
540
|
-
Gem.configuration.really_verbose
|
541
|
-
|
542
|
-
rescue Net::HTTPBadResponse
|
543
|
-
say "bad response" if Gem.configuration.really_verbose
|
544
|
-
|
545
|
-
reset connection
|
546
|
-
|
547
|
-
raise FetchError.new('too many bad responses', uri) if bad_response
|
548
|
-
|
549
|
-
bad_response = true
|
550
|
-
retry
|
551
|
-
# HACK work around EOFError bug in Net::HTTP
|
552
|
-
# NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible
|
553
|
-
# to install gems.
|
554
|
-
rescue EOFError, Timeout::Error,
|
555
|
-
Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE
|
556
|
-
|
557
|
-
requests = @requests[connection.object_id]
|
558
|
-
say "connection reset after #{requests} requests, retrying" if
|
559
|
-
Gem.configuration.really_verbose
|
560
|
-
|
561
|
-
raise FetchError.new('too many connection resets', uri) if retried
|
562
|
-
|
563
|
-
reset connection
|
564
|
-
|
565
|
-
retried = true
|
566
|
-
retry
|
567
|
-
end
|
568
|
-
|
569
|
-
response
|
570
|
-
end
|
571
|
-
|
572
|
-
##
|
573
|
-
# Resets HTTP connection +connection+.
|
574
|
-
|
575
|
-
def reset(connection)
|
576
|
-
@requests.delete connection.object_id
|
577
|
-
|
578
|
-
connection.finish
|
579
|
-
connection.start
|
580
|
-
end
|
581
|
-
|
582
|
-
def user_agent
|
583
|
-
ua = "RubyGems/#{Gem::VERSION} #{Gem::Platform.local}"
|
584
|
-
|
585
|
-
ruby_version = RUBY_VERSION
|
586
|
-
ruby_version += 'dev' if RUBY_PATCHLEVEL == -1
|
587
|
-
|
588
|
-
ua << " Ruby/#{ruby_version} (#{RUBY_RELEASE_DATE}"
|
589
|
-
if RUBY_PATCHLEVEL >= 0 then
|
590
|
-
ua << " patchlevel #{RUBY_PATCHLEVEL}"
|
591
|
-
elsif defined?(RUBY_REVISION) then
|
592
|
-
ua << " revision #{RUBY_REVISION}"
|
593
|
-
end
|
594
|
-
ua << ")"
|
595
|
-
|
596
|
-
ua << " #{RUBY_ENGINE}" if defined?(RUBY_ENGINE) and RUBY_ENGINE != 'ruby'
|
597
|
-
|
598
|
-
ua
|
599
332
|
end
|
600
333
|
|
601
334
|
def https?(uri)
|
@@ -0,0 +1,267 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'thread'
|
3
|
+
require 'time'
|
4
|
+
require 'rubygems/user_interaction'
|
5
|
+
|
6
|
+
class Gem::Request
|
7
|
+
|
8
|
+
include Gem::UserInteraction
|
9
|
+
|
10
|
+
attr_reader :proxy_uri
|
11
|
+
|
12
|
+
def initialize(uri, request_class, last_modified, proxy)
|
13
|
+
@uri = uri
|
14
|
+
@request_class = request_class
|
15
|
+
@last_modified = last_modified
|
16
|
+
@requests = Hash.new 0
|
17
|
+
@connections = {}
|
18
|
+
@connections_mutex = Mutex.new
|
19
|
+
@user_agent = user_agent
|
20
|
+
|
21
|
+
@proxy_uri =
|
22
|
+
case proxy
|
23
|
+
when :no_proxy then nil
|
24
|
+
when nil then get_proxy_from_env
|
25
|
+
when URI::HTTP then proxy
|
26
|
+
else URI.parse(proxy)
|
27
|
+
end
|
28
|
+
@env_no_proxy = get_no_proxy_from_env
|
29
|
+
end
|
30
|
+
|
31
|
+
def add_rubygems_trusted_certs(store)
|
32
|
+
pattern = File.expand_path("./ssl_certs/*.pem", File.dirname(__FILE__))
|
33
|
+
Dir.glob(pattern).each do |ssl_cert_file|
|
34
|
+
store.add_file ssl_cert_file
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def configure_connection_for_https(connection)
|
39
|
+
require 'net/https'
|
40
|
+
connection.use_ssl = true
|
41
|
+
connection.verify_mode =
|
42
|
+
Gem.configuration.ssl_verify_mode || OpenSSL::SSL::VERIFY_PEER
|
43
|
+
store = OpenSSL::X509::Store.new
|
44
|
+
|
45
|
+
if Gem.configuration.ssl_client_cert then
|
46
|
+
pem = File.read Gem.configuration.ssl_client_cert
|
47
|
+
connection.cert = OpenSSL::X509::Certificate.new pem
|
48
|
+
connection.key = OpenSSL::PKey::RSA.new pem
|
49
|
+
end
|
50
|
+
|
51
|
+
if Gem.configuration.ssl_ca_cert
|
52
|
+
if File.directory? Gem.configuration.ssl_ca_cert
|
53
|
+
store.add_path Gem.configuration.ssl_ca_cert
|
54
|
+
else
|
55
|
+
store.add_file Gem.configuration.ssl_ca_cert
|
56
|
+
end
|
57
|
+
else
|
58
|
+
store.set_default_paths
|
59
|
+
add_rubygems_trusted_certs(store)
|
60
|
+
end
|
61
|
+
connection.cert_store = store
|
62
|
+
rescue LoadError => e
|
63
|
+
raise unless (e.respond_to?(:path) && e.path == 'openssl') ||
|
64
|
+
e.message =~ / -- openssl$/
|
65
|
+
|
66
|
+
raise Gem::Exception.new(
|
67
|
+
'Unable to require openssl, install OpenSSL and rebuild ruby (preferred) or use non-HTTPS sources')
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Creates or an HTTP connection based on +uri+, or retrieves an existing
|
72
|
+
# connection, using a proxy if needed.
|
73
|
+
|
74
|
+
def connection_for(uri)
|
75
|
+
net_http_args = [uri.host, uri.port]
|
76
|
+
|
77
|
+
if @proxy_uri and not no_proxy?(uri.host) then
|
78
|
+
net_http_args += [
|
79
|
+
@proxy_uri.host,
|
80
|
+
@proxy_uri.port,
|
81
|
+
@proxy_uri.user,
|
82
|
+
@proxy_uri.password
|
83
|
+
]
|
84
|
+
end
|
85
|
+
|
86
|
+
connection_id = [Thread.current.object_id, *net_http_args].join ':'
|
87
|
+
|
88
|
+
connection = @connections_mutex.synchronize do
|
89
|
+
@connections[connection_id] ||= Net::HTTP.new(*net_http_args)
|
90
|
+
@connections[connection_id]
|
91
|
+
end
|
92
|
+
|
93
|
+
if https?(uri) and not connection.started? then
|
94
|
+
configure_connection_for_https(connection)
|
95
|
+
end
|
96
|
+
|
97
|
+
connection.start unless connection.started?
|
98
|
+
|
99
|
+
connection
|
100
|
+
rescue defined?(OpenSSL::SSL) ? OpenSSL::SSL::SSLError : Errno::EHOSTDOWN,
|
101
|
+
Errno::EHOSTDOWN => e
|
102
|
+
raise Gem::RemoteFetcher::FetchError.new(e.message, uri)
|
103
|
+
end
|
104
|
+
|
105
|
+
def fetch
|
106
|
+
request = @request_class.new @uri.request_uri
|
107
|
+
|
108
|
+
unless @uri.nil? || @uri.user.nil? || @uri.user.empty? then
|
109
|
+
request.basic_auth @uri.user, @uri.password
|
110
|
+
end
|
111
|
+
|
112
|
+
request.add_field 'User-Agent', @user_agent
|
113
|
+
request.add_field 'Connection', 'keep-alive'
|
114
|
+
request.add_field 'Keep-Alive', '30'
|
115
|
+
|
116
|
+
if @last_modified then
|
117
|
+
@last_modified = @last_modified.utc
|
118
|
+
request.add_field 'If-Modified-Since', @last_modified.rfc2822
|
119
|
+
end
|
120
|
+
|
121
|
+
yield request if block_given?
|
122
|
+
|
123
|
+
connection = connection_for @uri
|
124
|
+
|
125
|
+
retried = false
|
126
|
+
bad_response = false
|
127
|
+
|
128
|
+
begin
|
129
|
+
@requests[connection.object_id] += 1
|
130
|
+
|
131
|
+
say "#{request.method} #{@uri}" if
|
132
|
+
Gem.configuration.really_verbose
|
133
|
+
|
134
|
+
file_name = File.basename(@uri.path)
|
135
|
+
# perform download progress reporter only for gems
|
136
|
+
if request.response_body_permitted? && file_name =~ /\.gem$/
|
137
|
+
reporter = ui.download_reporter
|
138
|
+
response = connection.request(request) do |incomplete_response|
|
139
|
+
if Net::HTTPOK === incomplete_response
|
140
|
+
reporter.fetch(file_name, incomplete_response.content_length)
|
141
|
+
downloaded = 0
|
142
|
+
data = ''
|
143
|
+
|
144
|
+
incomplete_response.read_body do |segment|
|
145
|
+
data << segment
|
146
|
+
downloaded += segment.length
|
147
|
+
reporter.update(downloaded)
|
148
|
+
end
|
149
|
+
reporter.done
|
150
|
+
if incomplete_response.respond_to? :body=
|
151
|
+
incomplete_response.body = data
|
152
|
+
else
|
153
|
+
incomplete_response.instance_variable_set(:@body, data)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
else
|
158
|
+
response = connection.request request
|
159
|
+
end
|
160
|
+
|
161
|
+
say "#{response.code} #{response.message}" if
|
162
|
+
Gem.configuration.really_verbose
|
163
|
+
|
164
|
+
rescue Net::HTTPBadResponse
|
165
|
+
say "bad response" if Gem.configuration.really_verbose
|
166
|
+
|
167
|
+
reset connection
|
168
|
+
|
169
|
+
raise Gem::RemoteFetcher::FetchError.new('too many bad responses', @uri) if bad_response
|
170
|
+
|
171
|
+
bad_response = true
|
172
|
+
retry
|
173
|
+
# HACK work around EOFError bug in Net::HTTP
|
174
|
+
# NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible
|
175
|
+
# to install gems.
|
176
|
+
rescue EOFError, Timeout::Error,
|
177
|
+
Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE
|
178
|
+
|
179
|
+
requests = @requests[connection.object_id]
|
180
|
+
say "connection reset after #{requests} requests, retrying" if
|
181
|
+
Gem.configuration.really_verbose
|
182
|
+
|
183
|
+
raise Gem::RemoteFetcher::FetchError.new('too many connection resets', @uri) if retried
|
184
|
+
|
185
|
+
reset connection
|
186
|
+
|
187
|
+
retried = true
|
188
|
+
retry
|
189
|
+
end
|
190
|
+
|
191
|
+
response
|
192
|
+
end
|
193
|
+
|
194
|
+
##
|
195
|
+
# Returns list of no_proxy entries (if any) from the environment
|
196
|
+
|
197
|
+
def get_no_proxy_from_env
|
198
|
+
env_no_proxy = ENV['no_proxy'] || ENV['NO_PROXY']
|
199
|
+
|
200
|
+
return [] if env_no_proxy.nil? or env_no_proxy.empty?
|
201
|
+
|
202
|
+
env_no_proxy.split(/\s*,\s*/)
|
203
|
+
end
|
204
|
+
|
205
|
+
##
|
206
|
+
# Returns an HTTP proxy URI if one is set in the environment variables.
|
207
|
+
|
208
|
+
def get_proxy_from_env
|
209
|
+
env_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
|
210
|
+
|
211
|
+
return nil if env_proxy.nil? or env_proxy.empty?
|
212
|
+
|
213
|
+
uri = URI(Gem::UriFormatter.new(env_proxy).normalize)
|
214
|
+
|
215
|
+
if uri and uri.user.nil? and uri.password.nil? then
|
216
|
+
# Probably we have http_proxy_* variables?
|
217
|
+
uri.user = Gem::UriFormatter.new(ENV['http_proxy_user'] || ENV['HTTP_PROXY_USER']).escape
|
218
|
+
uri.password = Gem::UriFormatter.new(ENV['http_proxy_pass'] || ENV['HTTP_PROXY_PASS']).escape
|
219
|
+
end
|
220
|
+
|
221
|
+
uri
|
222
|
+
end
|
223
|
+
|
224
|
+
def https?(uri)
|
225
|
+
uri.scheme.downcase == 'https'
|
226
|
+
end
|
227
|
+
|
228
|
+
def no_proxy? host
|
229
|
+
host = host.downcase
|
230
|
+
@env_no_proxy.each do |pattern|
|
231
|
+
pattern = pattern.downcase
|
232
|
+
return true if host[-pattern.length, pattern.length ] == pattern
|
233
|
+
end
|
234
|
+
return false
|
235
|
+
end
|
236
|
+
|
237
|
+
##
|
238
|
+
# Resets HTTP connection +connection+.
|
239
|
+
|
240
|
+
def reset(connection)
|
241
|
+
@requests.delete connection.object_id
|
242
|
+
|
243
|
+
connection.finish
|
244
|
+
connection.start
|
245
|
+
end
|
246
|
+
|
247
|
+
def user_agent
|
248
|
+
ua = "RubyGems/#{Gem::VERSION} #{Gem::Platform.local}"
|
249
|
+
|
250
|
+
ruby_version = RUBY_VERSION
|
251
|
+
ruby_version += 'dev' if RUBY_PATCHLEVEL == -1
|
252
|
+
|
253
|
+
ua << " Ruby/#{ruby_version} (#{RUBY_RELEASE_DATE}"
|
254
|
+
if RUBY_PATCHLEVEL >= 0 then
|
255
|
+
ua << " patchlevel #{RUBY_PATCHLEVEL}"
|
256
|
+
elsif defined?(RUBY_REVISION) then
|
257
|
+
ua << " revision #{RUBY_REVISION}"
|
258
|
+
end
|
259
|
+
ua << ")"
|
260
|
+
|
261
|
+
ua << " #{RUBY_ENGINE}" if defined?(RUBY_ENGINE) and RUBY_ENGINE != 'ruby'
|
262
|
+
|
263
|
+
ua
|
264
|
+
end
|
265
|
+
|
266
|
+
end
|
267
|
+
|