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.

Files changed (154) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +2 -0
  4. data/.autotest +1 -1
  5. data/History.txt +82 -153
  6. data/Manifest.txt +35 -9
  7. data/Rakefile +35 -36
  8. data/lib/rubygems.rb +106 -18
  9. data/lib/rubygems/available_set.rb +68 -0
  10. data/lib/rubygems/basic_specification.rb +139 -0
  11. data/lib/rubygems/command_manager.rb +37 -40
  12. data/lib/rubygems/commands/cert_command.rb +78 -29
  13. data/lib/rubygems/commands/cleanup_command.rb +2 -2
  14. data/lib/rubygems/commands/contents_command.rb +101 -58
  15. data/lib/rubygems/commands/dependency_command.rb +94 -53
  16. data/lib/rubygems/commands/environment_command.rb +70 -53
  17. data/lib/rubygems/commands/fetch_command.rb +1 -2
  18. data/lib/rubygems/commands/help_command.rb +85 -55
  19. data/lib/rubygems/commands/install_command.rb +84 -42
  20. data/lib/rubygems/commands/outdated_command.rb +2 -12
  21. data/lib/rubygems/commands/owner_command.rb +6 -0
  22. data/lib/rubygems/commands/pristine_command.rb +26 -16
  23. data/lib/rubygems/commands/sources_command.rb +85 -70
  24. data/lib/rubygems/commands/uninstall_command.rb +32 -2
  25. data/lib/rubygems/commands/update_command.rb +111 -75
  26. data/lib/rubygems/config_file.rb +15 -3
  27. data/lib/rubygems/core_ext/kernel_require.rb +9 -31
  28. data/lib/rubygems/defaults.rb +8 -0
  29. data/lib/rubygems/dependency.rb +4 -2
  30. data/lib/rubygems/dependency_installer.rb +180 -170
  31. data/lib/rubygems/dependency_resolver.rb +191 -526
  32. data/lib/rubygems/dependency_resolver/activation_request.rb +109 -0
  33. data/lib/rubygems/dependency_resolver/api_set.rb +65 -0
  34. data/lib/rubygems/dependency_resolver/api_specification.rb +36 -0
  35. data/lib/rubygems/dependency_resolver/composed_set.rb +18 -0
  36. data/lib/rubygems/dependency_resolver/current_set.rb +16 -0
  37. data/lib/rubygems/dependency_resolver/dependency_conflict.rb +85 -0
  38. data/lib/rubygems/dependency_resolver/dependency_request.rb +51 -0
  39. data/lib/rubygems/dependency_resolver/index_set.rb +59 -0
  40. data/lib/rubygems/dependency_resolver/index_specification.rb +53 -0
  41. data/lib/rubygems/dependency_resolver/installed_specification.rb +38 -0
  42. data/lib/rubygems/dependency_resolver/installer_set.rb +130 -0
  43. data/lib/rubygems/exceptions.rb +88 -1
  44. data/lib/rubygems/ext/builder.rb +1 -1
  45. data/lib/rubygems/gem_runner.rb +17 -9
  46. data/lib/rubygems/gemcutter_utilities.rb +72 -42
  47. data/lib/rubygems/install_default_message.rb +12 -0
  48. data/lib/rubygems/install_update_options.rb +3 -0
  49. data/lib/rubygems/installer.rb +55 -30
  50. data/lib/rubygems/name_tuple.rb +18 -7
  51. data/lib/rubygems/package.rb +50 -25
  52. data/lib/rubygems/package/tar_test_case.rb +9 -9
  53. data/lib/rubygems/package/tar_writer.rb +35 -12
  54. data/lib/rubygems/package_task.rb +2 -5
  55. data/lib/rubygems/path_support.rb +10 -0
  56. data/lib/rubygems/platform.rb +9 -3
  57. data/lib/rubygems/psych_additions.rb +1 -1
  58. data/lib/rubygems/remote_fetcher.rb +9 -276
  59. data/lib/rubygems/request.rb +267 -0
  60. data/lib/rubygems/request_set.rb +123 -125
  61. data/lib/rubygems/request_set/gem_dependency_api.rb +39 -0
  62. data/lib/rubygems/security.rb +32 -23
  63. data/lib/rubygems/security/policy.rb +35 -9
  64. data/lib/rubygems/security/signer.rb +2 -2
  65. data/lib/rubygems/server.rb +8 -16
  66. data/lib/rubygems/source.rb +25 -14
  67. data/lib/rubygems/source/installed.rb +28 -0
  68. data/lib/rubygems/source/local.rb +122 -0
  69. data/lib/rubygems/source/specific_file.rb +28 -0
  70. data/lib/rubygems/source_local.rb +2 -89
  71. data/lib/rubygems/source_specific_file.rb +2 -26
  72. data/lib/rubygems/spec_fetcher.rb +11 -11
  73. data/lib/rubygems/specification.rb +186 -198
  74. data/lib/rubygems/ssl_certs/AddTrustExternalCARoot.pem +88 -30
  75. data/lib/rubygems/ssl_certs/Entrust_net-Secure-Server-Certification-Authority.pem +90 -0
  76. data/lib/rubygems/ssl_certs/{GeoTrustGlobalCA.pem → GeoTrust_Global_CA.pem} +20 -20
  77. data/lib/rubygems/ssl_certs/VerisignClass3PublicPrimaryCertificationAuthority-G2.pem +57 -0
  78. data/lib/rubygems/stub_specification.rb +119 -0
  79. data/lib/rubygems/test_case.rb +117 -49
  80. data/lib/rubygems/uninstaller.rb +14 -9
  81. data/lib/rubygems/uri_formatter.rb +39 -0
  82. data/lib/rubygems/util/list.rb +44 -0
  83. data/lib/rubygems/version.rb +15 -5
  84. data/lib/rubygems/version_option.rb +8 -2
  85. data/test/rubygems/ca_cert.pem +23 -0
  86. data/test/rubygems/client.pem +49 -0
  87. data/test/rubygems/encrypted_private_key.pem +30 -0
  88. data/test/rubygems/invalid_client.pem +49 -0
  89. data/test/rubygems/specifications/bar-0.0.2.gemspec +9 -0
  90. data/test/rubygems/specifications/foo-0.0.1.gemspec +0 -0
  91. data/test/rubygems/test_gem.rb +76 -454
  92. data/test/rubygems/test_gem_command_manager.rb +23 -21
  93. data/test/rubygems/test_gem_commands_cert_command.rb +154 -14
  94. data/test/rubygems/test_gem_commands_cleanup_command.rb +15 -0
  95. data/test/rubygems/test_gem_commands_contents_command.rb +32 -4
  96. data/test/rubygems/test_gem_commands_environment_command.rb +9 -1
  97. data/test/rubygems/test_gem_commands_fetch_command.rb +2 -28
  98. data/test/rubygems/test_gem_commands_help_command.rb +6 -3
  99. data/test/rubygems/test_gem_commands_install_command.rb +2 -65
  100. data/test/rubygems/test_gem_commands_owner_command.rb +49 -0
  101. data/test/rubygems/test_gem_commands_pristine_command.rb +30 -0
  102. data/test/rubygems/test_gem_commands_sources_command.rb +1 -1
  103. data/test/rubygems/test_gem_commands_uninstall_command.rb +33 -0
  104. data/test/rubygems/test_gem_commands_update_command.rb +2 -1
  105. data/test/rubygems/test_gem_config_file.rb +12 -0
  106. data/test/rubygems/test_gem_dependency_installer.rb +58 -65
  107. data/test/rubygems/test_gem_dependency_resolver.rb +6 -3
  108. data/test/rubygems/test_gem_dependency_resolver_dependency_conflict.rb +36 -0
  109. data/test/rubygems/test_gem_ext_builder.rb +2 -4
  110. data/test/rubygems/test_gem_ext_ext_conf_builder.rb +7 -2
  111. data/test/rubygems/test_gem_gem_runner.rb +17 -13
  112. data/test/rubygems/test_gem_gemcutter_utilities.rb +6 -19
  113. data/test/rubygems/test_gem_impossible_dependencies_error.rb +41 -0
  114. data/test/rubygems/test_gem_install_update_options.rb +4 -1
  115. data/test/rubygems/test_gem_installer.rb +31 -2
  116. data/test/rubygems/test_gem_name_tuple.rb +22 -0
  117. data/test/rubygems/test_gem_package.rb +122 -11
  118. data/test/rubygems/test_gem_package_old.rb +8 -0
  119. data/test/rubygems/test_gem_package_tar_reader.rb +9 -8
  120. data/test/rubygems/test_gem_package_tar_reader_entry.rb +1 -1
  121. data/test/rubygems/test_gem_package_tar_writer.rb +78 -56
  122. data/test/rubygems/test_gem_package_task.rb +2 -23
  123. data/test/rubygems/test_gem_path_support.rb +17 -0
  124. data/test/rubygems/test_gem_platform.rb +18 -0
  125. data/test/rubygems/test_gem_remote_fetcher.rb +106 -385
  126. data/test/rubygems/test_gem_request.rb +239 -0
  127. data/test/rubygems/test_gem_requirement.rb +9 -11
  128. data/test/rubygems/test_gem_security.rb +58 -2
  129. data/test/rubygems/test_gem_security_policy.rb +42 -1
  130. data/test/rubygems/test_gem_security_signer.rb +13 -1
  131. data/test/rubygems/test_gem_security_trust_dir.rb +5 -1
  132. data/test/rubygems/test_gem_server.rb +1 -105
  133. data/test/rubygems/test_gem_source.rb +4 -14
  134. data/test/rubygems/test_gem_source_local.rb +4 -4
  135. data/test/rubygems/test_gem_source_specific_file.rb +1 -1
  136. data/test/rubygems/test_gem_spec_fetcher.rb +0 -12
  137. data/test/rubygems/test_gem_specification.rb +452 -28
  138. data/test/rubygems/test_gem_stub_specification.rb +30 -0
  139. data/test/rubygems/test_gem_uninstaller.rb +14 -0
  140. data/test/rubygems/test_gem_uri_formatter.rb +20 -0
  141. data/test/rubygems/test_gem_version.rb +23 -13
  142. data/test/rubygems/test_gem_version_option.rb +63 -1
  143. data/test/rubygems/test_require.rb +0 -12
  144. data/util/create_encrypted_key.rb +16 -0
  145. metadata +161 -23
  146. metadata.gz.sig +0 -0
  147. data/CVE-2013-4287.txt +0 -36
  148. data/CVE-2013-4363.txt +0 -45
  149. data/lib/rubygems/ssl_certs/AddTrustExternalCARoot-2048.pem +0 -25
  150. data/lib/rubygems/ssl_certs/Class3PublicPrimaryCertificationAuthority.pem +0 -14
  151. data/lib/rubygems/ssl_certs/DigiCertHighAssuranceEVRootCA.pem +0 -23
  152. data/lib/rubygems/ssl_certs/EntrustnetSecureServerCertificationAuthority.pem +0 -28
  153. data/test/rubygems/test_bundled_ca.rb +0 -59
  154. 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
@@ -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) and
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-ruby gem that may use Gem::Specification#extensions to build
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 ruby's platform.
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'
@@ -3,7 +3,7 @@
3
3
  # in Specification._load, but if we don't have the constant, Marshal
4
4
  # blows up.
5
5
 
6
- module Psych
6
+ module Psych # :nodoc:
7
7
  class PrivateType
8
8
  end
9
9
  end
@@ -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
- @connections = {}
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
- target = res.target.to_s.strip
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 source_path
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 = request_class.new uri.request_uri
485
-
486
- unless uri.nil? || uri.user.nil? || uri.user.empty? then
487
- request.basic_auth uri.user, uri.password
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
+