rubygems-update 1.1.1 → 1.2.0

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 (152) hide show
  1. data.tar.gz.sig +0 -0
  2. data/ChangeLog +267 -1
  3. data/Rakefile +13 -7
  4. data/doc/release_notes/rel_1_2_0.rdoc +105 -0
  5. data/lib/rubygems.rb +82 -14
  6. data/lib/rubygems/command.rb +1 -1
  7. data/lib/rubygems/command_manager.rb +3 -2
  8. data/lib/rubygems/commands/cert_command.rb +1 -1
  9. data/lib/rubygems/commands/dependency_command.rb +57 -19
  10. data/lib/rubygems/commands/environment_command.rb +2 -0
  11. data/lib/rubygems/commands/fetch_command.rb +3 -1
  12. data/lib/rubygems/commands/install_command.rb +3 -3
  13. data/lib/rubygems/commands/list_command.rb +30 -28
  14. data/lib/rubygems/commands/lock_command.rb +1 -1
  15. data/lib/rubygems/commands/outdated_command.rb +5 -2
  16. data/lib/rubygems/commands/pristine_command.rb +3 -44
  17. data/lib/rubygems/commands/query_command.rb +80 -21
  18. data/lib/rubygems/commands/sources_command.rb +56 -28
  19. data/lib/rubygems/commands/specification_command.rb +4 -3
  20. data/lib/rubygems/commands/stale_command.rb +27 -0
  21. data/lib/rubygems/commands/update_command.rb +35 -22
  22. data/lib/rubygems/config_file.rb +29 -12
  23. data/lib/rubygems/custom_require.rb +1 -1
  24. data/lib/rubygems/defaults.rb +1 -1
  25. data/lib/rubygems/dependency.rb +63 -9
  26. data/lib/rubygems/dependency_installer.rb +60 -23
  27. data/lib/rubygems/dependency_list.rb +1 -1
  28. data/lib/rubygems/doc_manager.rb +5 -5
  29. data/lib/rubygems/gem_openssl.rb +1 -1
  30. data/lib/rubygems/indexer.rb +269 -84
  31. data/lib/rubygems/install_update_options.rb +6 -0
  32. data/lib/rubygems/installer.rb +35 -12
  33. data/lib/rubygems/local_remote_options.rb +26 -5
  34. data/lib/rubygems/platform.rb +15 -1
  35. data/lib/rubygems/remote_fetcher.rb +158 -90
  36. data/lib/rubygems/requirement.rb +16 -12
  37. data/lib/rubygems/rubygems_version.rb +1 -1
  38. data/lib/rubygems/security.rb +2 -2
  39. data/lib/rubygems/server.rb +239 -110
  40. data/lib/rubygems/source_index.rb +44 -18
  41. data/lib/rubygems/source_info_cache.rb +1 -1
  42. data/lib/rubygems/spec_fetcher.rb +251 -0
  43. data/lib/rubygems/specification.rb +120 -38
  44. data/lib/rubygems/test_utilities.rb +120 -0
  45. data/lib/rubygems/uninstaller.rb +11 -10
  46. data/lib/rubygems/user_interaction.rb +149 -74
  47. data/lib/rubygems/validator.rb +3 -3
  48. data/lib/rubygems/version.rb +23 -21
  49. data/setup.rb +105 -100
  50. data/test/gemutilities.rb +63 -86
  51. data/test/test_config.rb +0 -5
  52. data/test/test_gem.rb +22 -2
  53. data/test/test_gem_command_manager.rb +1 -1
  54. data/test/test_gem_commands_dependency_command.rb +125 -6
  55. data/test/test_gem_commands_environment_command.rb +1 -0
  56. data/test/test_gem_commands_fetch_command.rb +24 -4
  57. data/test/test_gem_commands_install_command.rb +6 -8
  58. data/test/{test_gem_outdated_command.rb → test_gem_commands_outdated_command.rb} +5 -2
  59. data/test/test_gem_commands_pristine_command.rb +13 -4
  60. data/test/test_gem_commands_query_command.rb +113 -37
  61. data/test/test_gem_commands_sources_command.rb +101 -31
  62. data/test/test_gem_commands_specification_command.rb +4 -1
  63. data/test/test_gem_commands_stale_command.rb +39 -0
  64. data/test/test_gem_commands_update_command.rb +17 -27
  65. data/test/test_gem_config_file.rb +38 -1
  66. data/test/test_gem_dependency.rb +51 -0
  67. data/test/test_gem_dependency_installer.rb +133 -25
  68. data/test/test_gem_gem_path_searcher.rb +4 -1
  69. data/test/test_gem_indexer.rb +124 -19
  70. data/test/test_gem_installer.rb +32 -2
  71. data/test/test_gem_local_remote_options.rb +6 -5
  72. data/test/test_gem_remote_fetcher.rb +14 -9
  73. data/test/test_gem_server.rb +207 -21
  74. data/test/test_gem_source_index.rb +203 -63
  75. data/test/test_gem_source_info_cache.rb +8 -6
  76. data/test/test_gem_source_info_cache_entry.rb +11 -9
  77. data/test/test_gem_spec_fetcher.rb +303 -0
  78. data/test/test_gem_specification.rb +91 -7
  79. data/test/test_gem_uninstaller.rb +21 -0
  80. data/test/test_gem_version.rb +14 -5
  81. data/test/test_kernel.rb +1 -1
  82. metadata +10 -73
  83. metadata.gz.sig +0 -0
  84. data/lib/rubygems/indexer/abstract_index_builder.rb +0 -88
  85. data/lib/rubygems/indexer/latest_index_builder.rb +0 -35
  86. data/lib/rubygems/indexer/marshal_index_builder.rb +0 -17
  87. data/lib/rubygems/indexer/master_index_builder.rb +0 -54
  88. data/lib/rubygems/indexer/quick_index_builder.rb +0 -50
  89. data/test/gem_installer_test_case.rbc +0 -0
  90. data/test/gem_package_tar_test_case.rbc +0 -0
  91. data/test/gemutilities.rbc +0 -0
  92. data/test/mockgemui.rbc +0 -0
  93. data/test/simple_gem.rbc +0 -0
  94. data/test/test_config.rbc +0 -0
  95. data/test/test_gem.rbc +0 -0
  96. data/test/test_gem_builder.rbc +0 -0
  97. data/test/test_gem_command.rbc +0 -0
  98. data/test/test_gem_command_manager.rbc +0 -0
  99. data/test/test_gem_commands_build_command.rbc +0 -0
  100. data/test/test_gem_commands_cert_command.rbc +0 -0
  101. data/test/test_gem_commands_check_command.rbc +0 -0
  102. data/test/test_gem_commands_contents_command.rbc +0 -0
  103. data/test/test_gem_commands_dependency_command.rbc +0 -0
  104. data/test/test_gem_commands_environment_command.rbc +0 -0
  105. data/test/test_gem_commands_fetch_command.rbc +0 -0
  106. data/test/test_gem_commands_generate_index_command.rbc +0 -0
  107. data/test/test_gem_commands_install_command.rbc +0 -0
  108. data/test/test_gem_commands_mirror_command.rbc +0 -0
  109. data/test/test_gem_commands_pristine_command.rbc +0 -0
  110. data/test/test_gem_commands_query_command.rbc +0 -0
  111. data/test/test_gem_commands_server_command.rbc +0 -0
  112. data/test/test_gem_commands_sources_command.rbc +0 -0
  113. data/test/test_gem_commands_specification_command.rbc +0 -0
  114. data/test/test_gem_commands_unpack_command.rbc +0 -0
  115. data/test/test_gem_commands_update_command.rbc +0 -0
  116. data/test/test_gem_config_file.rbc +0 -0
  117. data/test/test_gem_dependency.rbc +0 -0
  118. data/test/test_gem_dependency_installer.rbc +0 -0
  119. data/test/test_gem_dependency_list.rbc +0 -0
  120. data/test/test_gem_digest.rbc +0 -0
  121. data/test/test_gem_doc_manager.rbc +0 -0
  122. data/test/test_gem_ext_configure_builder.rbc +0 -0
  123. data/test/test_gem_ext_ext_conf_builder.rbc +0 -0
  124. data/test/test_gem_ext_rake_builder.rbc +0 -0
  125. data/test/test_gem_format.rbc +0 -0
  126. data/test/test_gem_gem_path_searcher.rbc +0 -0
  127. data/test/test_gem_gem_runner.rbc +0 -0
  128. data/test/test_gem_indexer.rbc +0 -0
  129. data/test/test_gem_install_update_options.rbc +0 -0
  130. data/test/test_gem_installer.rbc +0 -0
  131. data/test/test_gem_local_remote_options.rbc +0 -0
  132. data/test/test_gem_outdated_command.rbc +0 -0
  133. data/test/test_gem_package_tar_header.rbc +0 -0
  134. data/test/test_gem_package_tar_input.rbc +0 -0
  135. data/test/test_gem_package_tar_output.rbc +0 -0
  136. data/test/test_gem_package_tar_reader.rbc +0 -0
  137. data/test/test_gem_package_tar_reader_entry.rbc +0 -0
  138. data/test/test_gem_package_tar_writer.rbc +0 -0
  139. data/test/test_gem_platform.rbc +0 -0
  140. data/test/test_gem_remote_fetcher.rbc +0 -0
  141. data/test/test_gem_requirement.rbc +0 -0
  142. data/test/test_gem_server.rbc +0 -0
  143. data/test/test_gem_source_index.rbc +0 -0
  144. data/test/test_gem_source_info_cache.rbc +0 -0
  145. data/test/test_gem_source_info_cache_entry.rbc +0 -0
  146. data/test/test_gem_specification.rbc +0 -0
  147. data/test/test_gem_stream_ui.rbc +0 -0
  148. data/test/test_gem_uninstaller.rbc +0 -0
  149. data/test/test_gem_validator.rbc +0 -0
  150. data/test/test_gem_version.rbc +0 -0
  151. data/test/test_gem_version_option.rbc +0 -0
  152. data/test/test_kernel.rbc +0 -0
@@ -89,6 +89,12 @@ module Gem::InstallUpdateOptions
89
89
  'foo_exec18') do |value, options|
90
90
  options[:format_executable] = value
91
91
  end
92
+
93
+ add_option(:"Install/Update", "--development",
94
+ "Install any additional development",
95
+ "dependencies") do |value, options|
96
+ options[:development] = true
97
+ end
92
98
  end
93
99
 
94
100
  # Default options for the gem install command.
@@ -56,6 +56,7 @@ class Gem::Installer
56
56
  # foo_exec18.
57
57
  # :security_policy:: Use the specified security policy. See Gem::Security
58
58
  # :wrappers:: Install wrappers if true, symlinks if false.
59
+
59
60
  def initialize(gem, options={})
60
61
  @gem = gem
61
62
 
@@ -76,6 +77,7 @@ class Gem::Installer
76
77
  @security_policy = options[:security_policy]
77
78
  @wrappers = options[:wrappers]
78
79
  @bin_dir = options[:bin_dir]
80
+ @development = options[:development]
79
81
 
80
82
  begin
81
83
  @format = Gem::Format.from_file_by_path @gem, @security_policy
@@ -98,6 +100,7 @@ class Gem::Installer
98
100
  # cache/<gem-version>.gem #=> a cached copy of the installed gem
99
101
  # gems/<gem-version>/... #=> extracted files
100
102
  # specifications/<gem-version>.gemspec #=> the Gem::Specification
103
+
101
104
  def install
102
105
  # If we're forcing the install then disable security unless the security
103
106
  # policy says that we only install singed gems.
@@ -119,7 +122,10 @@ class Gem::Installer
119
122
  end
120
123
 
121
124
  unless @ignore_dependencies then
122
- @spec.dependencies.each do |dep_gem|
125
+ deps = @spec.runtime_dependencies
126
+ deps |= @spec.development_dependencies if @development
127
+
128
+ deps.each do |dep_gem|
123
129
  ensure_dependency @spec, dep_gem
124
130
  end
125
131
  end
@@ -150,6 +156,8 @@ class Gem::Installer
150
156
  @spec.loaded_from = File.join(@gem_home, 'specifications',
151
157
  "#{@spec.full_name}.gemspec")
152
158
 
159
+ Gem.source_index.add_spec @spec
160
+
153
161
  return @spec
154
162
  rescue Zlib::GzipFile::Error
155
163
  raise Gem::InstallError, "gzip error installing #{@gem}"
@@ -161,6 +169,7 @@ class Gem::Installer
161
169
  #
162
170
  # spec :: Gem::Specification
163
171
  # dependency :: Gem::Dependency
172
+
164
173
  def ensure_dependency(spec, dependency)
165
174
  unless installation_satisfies_dependency? dependency then
166
175
  raise Gem::InstallError, "#{spec.name} requires #{dependency}"
@@ -170,17 +179,15 @@ class Gem::Installer
170
179
  end
171
180
 
172
181
  ##
173
- # True if the current installed gems satisfy the given dependency.
174
- #
175
- # dependency :: Gem::Dependency
182
+ # True if the gems in Gem.source_index satisfy +dependency+.
183
+
176
184
  def installation_satisfies_dependency?(dependency)
177
- current_index = Gem::SourceIndex.from_installed_gems
178
- current_index.find_name(dependency.name, dependency.version_requirements).size > 0
185
+ Gem.source_index.find_name(dependency.name, dependency.version_requirements).size > 0
179
186
  end
180
187
 
181
188
  ##
182
189
  # Unpacks the gem into the given directory.
183
- #
190
+
184
191
  def unpack(directory)
185
192
  @gem_dir = directory
186
193
  @format = Gem::Format.from_file_by_path @gem, @security_policy
@@ -193,7 +200,7 @@ class Gem::Installer
193
200
  #
194
201
  # spec:: [Gem::Specification] The Gem specification to output
195
202
  # spec_path:: [String] The location (path) to write the gemspec to
196
- #
203
+
197
204
  def write_spec
198
205
  rubycode = @spec.to_ruby
199
206
 
@@ -208,7 +215,7 @@ class Gem::Installer
208
215
 
209
216
  ##
210
217
  # Creates windows .bat files for easy running of commands
211
- #
218
+
212
219
  def generate_windows_script(bindir, filename)
213
220
  if Gem.win_platform? then
214
221
  script_name = filename + ".bat"
@@ -227,7 +234,7 @@ class Gem::Installer
227
234
  # If the user has asked for the gem to be installed in a directory that is
228
235
  # the system gem directory, then use the system bin directory, else create
229
236
  # (or use) a new bin dir under the gem_home.
230
- bindir = @bin_dir ? @bin_dir : (Gem.bindir @gem_home)
237
+ bindir = @bin_dir ? @bin_dir : Gem.bindir(@gem_home)
231
238
 
232
239
  Dir.mkdir bindir unless File.exist? bindir
233
240
  raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir
@@ -252,7 +259,7 @@ class Gem::Installer
252
259
  # The Windows script is generated in addition to the regular one due to a
253
260
  # bug or misfeature in the Windows shell's pipe. See
254
261
  # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/193379
255
- #
262
+
256
263
  def generate_bin_script(filename, bindir)
257
264
  bin_script_path = File.join bindir, formatted_program_filename(filename)
258
265
 
@@ -260,6 +267,8 @@ class Gem::Installer
260
267
 
261
268
  # HACK some gems don't have #! in their executables, restore 2008/06
262
269
  #if File.read(exec_path, 2) == '#!' then
270
+ FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers
271
+
263
272
  File.open bin_script_path, 'w', 0755 do |file|
264
273
  file.print app_script_text(filename)
265
274
  end
@@ -277,7 +286,7 @@ class Gem::Installer
277
286
  ##
278
287
  # Creates the symlinks to run the applications in the gem. Moves
279
288
  # the symlink if the gem being installed has a newer version.
280
- #
289
+
281
290
  def generate_bin_symlink(filename, bindir)
282
291
  if Gem.win_platform? then
283
292
  alert_warning "Unable to use symlinks on Windows, installing wrapper"
@@ -303,6 +312,7 @@ class Gem::Installer
303
312
  ##
304
313
  # Generates a #! line for +bin_file_name+'s wrapper copying arguments if
305
314
  # necessary.
315
+
306
316
  def shebang(bin_file_name)
307
317
  if @env_shebang then
308
318
  "#!/usr/bin/env " + Gem::ConfigMap[:ruby_install_name]
@@ -324,7 +334,9 @@ class Gem::Installer
324
334
  end
325
335
  end
326
336
 
337
+ ##
327
338
  # Return the text for an application file.
339
+
328
340
  def app_script_text(bin_file_name)
329
341
  <<-TEXT
330
342
  #{shebang bin_file_name}
@@ -349,7 +361,9 @@ load '#{bin_file_name}'
349
361
  TEXT
350
362
  end
351
363
 
364
+ ##
352
365
  # return the stub script text used to launch the true ruby script
366
+
353
367
  def windows_stub_script(bindir, bin_file_name)
354
368
  <<-TEXT
355
369
  @ECHO OFF
@@ -361,8 +375,10 @@ GOTO :EOF
361
375
  TEXT
362
376
  end
363
377
 
378
+ ##
364
379
  # Builds extensions. Valid types of extensions are extconf.rb files,
365
380
  # configure scripts and rakefiles or mkrf_conf files.
381
+
366
382
  def build_extensions
367
383
  return if @spec.extensions.empty?
368
384
  say "Building native extensions. This could take a while..."
@@ -418,6 +434,7 @@ Results logged to #{File.join(Dir.pwd, 'gem_make.out')}
418
434
  # Reads the file index and extracts each file into the gem directory.
419
435
  #
420
436
  # Ensures that files can't be installed outside the gem directory.
437
+
421
438
  def extract_files
422
439
  expand_and_validate_gem_dir
423
440
 
@@ -445,11 +462,15 @@ Results logged to #{File.join(Dir.pwd, 'gem_make.out')}
445
462
  out.write file_data
446
463
  end
447
464
 
465
+ FileUtils.chmod entry['mode'], path
466
+
448
467
  say path if Gem.configuration.really_verbose
449
468
  end
450
469
  end
451
470
 
471
+ ##
452
472
  # Prefix and suffix the program filename the same as ruby.
473
+
453
474
  def formatted_program_filename(filename)
454
475
  if @format_executable then
455
476
  self.class.exec_format % File.basename(filename)
@@ -460,7 +481,9 @@ Results logged to #{File.join(Dir.pwd, 'gem_make.out')}
460
481
 
461
482
  private
462
483
 
484
+ ##
463
485
  # HACK Pathname is broken on windows.
486
+
464
487
  def absolute_path? pathname
465
488
  pathname.absolute? or (Gem.win_platform? and pathname.to_s =~ /\A[a-z]:/i)
466
489
  end
@@ -4,27 +4,34 @@
4
4
  # See LICENSE.txt for permissions.
5
5
  #++
6
6
 
7
+ require 'uri'
7
8
  require 'rubygems'
8
9
 
10
+ ##
9
11
  # Mixin methods for local and remote Gem::Command options.
12
+
10
13
  module Gem::LocalRemoteOptions
11
14
 
15
+ ##
12
16
  # Allows OptionParser to handle HTTP URIs.
17
+
13
18
  def accept_uri_http
14
19
  OptionParser.accept URI::HTTP do |value|
15
20
  begin
16
- value = URI.parse value
21
+ uri = URI.parse value
17
22
  rescue URI::InvalidURIError
18
23
  raise OptionParser::InvalidArgument, value
19
24
  end
20
25
 
21
- raise OptionParser::InvalidArgument, value unless value.scheme == 'http'
26
+ raise OptionParser::InvalidArgument, value unless uri.scheme == 'http'
22
27
 
23
28
  value
24
29
  end
25
30
  end
26
31
 
32
+ ##
27
33
  # Add local/remote options to the command line parser.
34
+
28
35
  def add_local_remote_options
29
36
  add_option(:"Local/Remote", '-l', '--local',
30
37
  'Restrict operations to the LOCAL domain') do |value, options|
@@ -47,7 +54,9 @@ module Gem::LocalRemoteOptions
47
54
  add_update_sources_option
48
55
  end
49
56
 
57
+ ##
50
58
  # Add the --bulk-threshold option
59
+
51
60
  def add_bulk_threshold_option
52
61
  add_option(:"Local/Remote", '-B', '--bulk-threshold COUNT',
53
62
  "Threshold for switching to bulk",
@@ -57,7 +66,9 @@ module Gem::LocalRemoteOptions
57
66
  end
58
67
  end
59
68
 
69
+ ##
60
70
  # Add the --http-proxy option
71
+
61
72
  def add_proxy_option
62
73
  accept_uri_http
63
74
 
@@ -68,22 +79,28 @@ module Gem::LocalRemoteOptions
68
79
  end
69
80
  end
70
81
 
82
+ ##
71
83
  # Add the --source option
84
+
72
85
  def add_source_option
73
86
  accept_uri_http
74
87
 
75
88
  add_option(:"Local/Remote", '--source URL', URI::HTTP,
76
- 'Use URL as the remote source for gems') do |value, options|
89
+ 'Use URL as the remote source for gems') do |source, options|
90
+ source << '/' if source !~ /\/\z/
91
+
77
92
  if options[:added_source] then
78
- Gem.sources << value
93
+ Gem.sources << source
79
94
  else
80
95
  options[:added_source] = true
81
- Gem.sources.replace [value]
96
+ Gem.sources.replace [source]
82
97
  end
83
98
  end
84
99
  end
85
100
 
101
+ ##
86
102
  # Add the --source option
103
+
87
104
  def add_update_sources_option
88
105
 
89
106
  add_option(:"Local/Remote", '-u', '--[no-]update-sources',
@@ -92,12 +109,16 @@ module Gem::LocalRemoteOptions
92
109
  end
93
110
  end
94
111
 
112
+ ##
95
113
  # Is local fetching enabled?
114
+
96
115
  def local?
97
116
  options[:domain] == :local || options[:domain] == :both
98
117
  end
99
118
 
119
+ ##
100
120
  # Is remote fetching enabled?
121
+
101
122
  def remote?
102
123
  options[:domain] == :remote || options[:domain] == :both
103
124
  end
@@ -1,7 +1,8 @@
1
1
  require 'rubygems'
2
2
 
3
+ ##
3
4
  # Available list of platforms for targeting Gem installations.
4
- #
5
+
5
6
  class Gem::Platform
6
7
 
7
8
  @local = nil
@@ -122,11 +123,20 @@ class Gem::Platform
122
123
  to_a.compact.join '-'
123
124
  end
124
125
 
126
+ ##
127
+ # Is +other+ equal to this platform? Two platforms are equal if they have
128
+ # the same CPU, OS and version.
129
+
125
130
  def ==(other)
126
131
  self.class === other and
127
132
  @cpu == other.cpu and @os == other.os and @version == other.version
128
133
  end
129
134
 
135
+ ##
136
+ # Does +other+ match this platform? Two platforms match if they have the
137
+ # same CPU, or either has a CPU of 'universal', they have the same OS, and
138
+ # they have the same version, or either has no version.
139
+
130
140
  def ===(other)
131
141
  return nil unless Gem::Platform === other
132
142
 
@@ -140,6 +150,10 @@ class Gem::Platform
140
150
  (@version.nil? or other.version.nil? or @version == other.version)
141
151
  end
142
152
 
153
+ ##
154
+ # Does +other+ match this platform? If +other+ is a String it will be
155
+ # converted to a Gem::Platform first. See #=== for matching rules.
156
+
143
157
  def =~(other)
144
158
  case other
145
159
  when Gem::Platform then # nop
@@ -1,4 +1,5 @@
1
1
  require 'net/http'
2
+ require 'stringio'
2
3
  require 'uri'
3
4
 
4
5
  require 'rubygems'
@@ -11,15 +12,38 @@ class Gem::RemoteFetcher
11
12
 
12
13
  include Gem::UserInteraction
13
14
 
14
- class FetchError < Gem::Exception; end
15
+ ##
16
+ # A FetchError exception wraps up the various possible IO and HTTP failures
17
+ # that could happen while downloading from the internet.
18
+
19
+ class FetchError < Gem::Exception
20
+
21
+ ##
22
+ # The URI which was being accessed when the exception happened.
23
+
24
+ attr_accessor :uri
25
+
26
+ def initialize(message, uri)
27
+ super message
28
+ @uri = uri
29
+ end
30
+
31
+ def to_s # :nodoc:
32
+ "#{super} (#{uri})"
33
+ end
34
+
35
+ end
15
36
 
16
37
  @fetcher = nil
17
38
 
39
+ ##
18
40
  # Cached RemoteFetcher instance.
41
+
19
42
  def self.fetcher
20
43
  @fetcher ||= self.new Gem.configuration[:http_proxy]
21
44
  end
22
45
 
46
+ ##
23
47
  # Initialize a remote fetcher using the source URI and possible proxy
24
48
  # information.
25
49
  #
@@ -29,6 +53,7 @@ class Gem::RemoteFetcher
29
53
  # * nil: respect environment variables (HTTP_PROXY, HTTP_PROXY_USER,
30
54
  # HTTP_PROXY_PASS)
31
55
  # * <tt>:no_proxy</tt>: ignore environment variables and _don't_ use a proxy
56
+
32
57
  def initialize(proxy)
33
58
  Socket.do_not_reverse_lookup = true
34
59
 
@@ -47,11 +72,13 @@ class Gem::RemoteFetcher
47
72
  # Moves the gem +spec+ from +source_uri+ to the cache dir unless it is
48
73
  # already there. If the source_uri is local the gem cache dir copy is
49
74
  # always replaced.
75
+
50
76
  def download(spec, source_uri, install_dir = Gem.dir)
77
+ cache_dir = File.join install_dir, 'cache'
51
78
  gem_file_name = "#{spec.full_name}.gem"
52
- local_gem_path = File.join install_dir, 'cache', gem_file_name
79
+ local_gem_path = File.join cache_dir, gem_file_name
53
80
 
54
- Gem.ensure_gem_subdirectories install_dir
81
+ FileUtils.mkdir_p cache_dir rescue nil unless File.exist? cache_dir
55
82
 
56
83
  source_uri = URI.parse source_uri unless URI::Generic === source_uri
57
84
  scheme = source_uri.scheme
@@ -102,21 +129,26 @@ class Gem::RemoteFetcher
102
129
  local_gem_path
103
130
  end
104
131
 
105
- # Downloads +uri+.
132
+ ##
133
+ # Downloads +uri+ and returns it as a String.
134
+
106
135
  def fetch_path(uri)
107
136
  open_uri_or_path(uri) do |input|
108
137
  input.read
109
138
  end
139
+ rescue FetchError
140
+ raise
110
141
  rescue Timeout::Error
111
- raise FetchError, "timed out fetching #{uri}"
142
+ raise FetchError.new('timed out', uri)
112
143
  rescue IOError, SocketError, SystemCallError => e
113
- raise FetchError, "#{e.class}: #{e} reading #{uri}"
144
+ raise FetchError.new("#{e.class}: #{e}", uri)
114
145
  rescue => e
115
- message = "#{e.class}: #{e} reading #{uri}"
116
- raise FetchError, message
146
+ raise FetchError.new("#{e.class}: #{e}", uri)
117
147
  end
118
148
 
149
+ ##
119
150
  # Returns the size of +uri+ in bytes.
151
+
120
152
  def fetch_size(uri)
121
153
  return File.size(get_file_uri_path(uri)) if file_uri? uri
122
154
 
@@ -124,30 +156,23 @@ class Gem::RemoteFetcher
124
156
 
125
157
  raise ArgumentError, 'uri is not an HTTP URI' unless URI::HTTP === uri
126
158
 
127
- http = connect_to uri.host, uri.port
128
-
129
- request = Net::HTTP::Head.new uri.request_uri
159
+ response = request uri, Net::HTTP::Head
130
160
 
131
- request.basic_auth unescape(uri.user), unescape(uri.password) unless
132
- uri.user.nil? or uri.user.empty?
133
-
134
- resp = http.request request
135
-
136
- if resp.code !~ /^2/ then
137
- raise Gem::RemoteSourceException,
138
- "HTTP Response #{resp.code} fetching #{uri}"
161
+ case response
162
+ when Net::HTTPOK then
163
+ else
164
+ raise FetchError.new("bad response #{response.message} #{response.code}", uri)
139
165
  end
140
166
 
141
- if resp['content-length'] then
142
- return resp['content-length'].to_i
167
+ if response['content-length'] then
168
+ return response['content-length'].to_i
143
169
  else
144
- resp = http.get uri.request_uri
145
- return resp.body.size
170
+ response = http.get uri.request_uri
171
+ return response.body.size
146
172
  end
147
173
 
148
174
  rescue SocketError, SystemCallError, Timeout::Error => e
149
- raise Gem::RemoteFetcher::FetchError,
150
- "#{e.message} (#{e.class})\n\tgetting size of #{uri}"
175
+ raise FetchError.new("#{e.message} (#{e.class})\n\tfetching size", uri)
151
176
  end
152
177
 
153
178
  private
@@ -162,7 +187,9 @@ class Gem::RemoteFetcher
162
187
  URI.unescape(str)
163
188
  end
164
189
 
190
+ ##
165
191
  # Returns an HTTP proxy URI if one is set in the environment variables.
192
+
166
193
  def get_proxy_from_env
167
194
  env_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
168
195
 
@@ -179,104 +206,145 @@ class Gem::RemoteFetcher
179
206
  uri
180
207
  end
181
208
 
209
+ ##
182
210
  # Normalize the URI by adding "http://" if it is missing.
211
+
183
212
  def normalize_uri(uri)
184
213
  (uri =~ /^(https?|ftp|file):/) ? uri : "http://#{uri}"
185
214
  end
186
215
 
187
- # Connect to the source host/port, using a proxy if needed.
188
- def connect_to(host, port)
189
- if @proxy_uri
190
- Net::HTTP::Proxy(@proxy_uri.host, @proxy_uri.port, unescape(@proxy_uri.user), unescape(@proxy_uri.password)).new(host, port)
191
- else
192
- Net::HTTP.new(host, port)
216
+ ##
217
+ # Creates or an HTTP connection based on +uri+, or retrieves an existing
218
+ # connection, using a proxy if needed.
219
+
220
+ def connection_for(uri)
221
+ net_http_args = [uri.host, uri.port]
222
+
223
+ if @proxy_uri then
224
+ net_http_args += [
225
+ @proxy_uri.host,
226
+ @proxy_uri.port,
227
+ @proxy_uri.user,
228
+ @proxy_uri.password
229
+ ]
193
230
  end
231
+
232
+ connection_id = net_http_args.join ':'
233
+ @connections[connection_id] ||= Net::HTTP.new(*net_http_args)
234
+ connection = @connections[connection_id]
235
+
236
+ if uri.scheme == 'https' and not connection.started? then
237
+ http_obj.use_ssl = true
238
+ http_obj.verify_mode = OpenSSL::SSL::VERIFY_NONE
239
+ end
240
+
241
+ connection.start unless connection.started?
242
+
243
+ connection
194
244
  end
195
245
 
246
+ ##
196
247
  # Read the data from the (source based) URI, but if it is a file:// URI,
197
248
  # read from the filesystem instead.
249
+
198
250
  def open_uri_or_path(uri, depth = 0, &block)
199
251
  if file_uri?(uri)
200
252
  open(get_file_uri_path(uri), &block)
201
253
  else
202
254
  uri = URI.parse uri unless URI::Generic === uri
203
- net_http_args = [uri.host, uri.port]
204
-
205
- if @proxy_uri then
206
- net_http_args += [ @proxy_uri.host,
207
- @proxy_uri.port,
208
- @proxy_uri.user,
209
- @proxy_uri.password
210
- ]
211
- end
212
255
 
213
- connection_id = net_http_args.join ':'
214
- @connections[connection_id] ||= Net::HTTP.new(*net_http_args)
215
- connection = @connections[connection_id]
216
-
217
- if uri.scheme == 'https' && ! connection.started?
218
- http_obj.use_ssl = true
219
- http_obj.verify_mode = OpenSSL::SSL::VERIFY_NONE
220
- end
221
-
222
- connection.start unless connection.started?
223
-
224
- request = Net::HTTP::Get.new(uri.request_uri)
225
- unless uri.nil? || uri.user.nil? || uri.user.empty? then
226
- request.basic_auth(uri.user, uri.password)
227
- end
228
-
229
- ua = "RubyGems/#{Gem::RubyGemsVersion} #{Gem::Platform.local}"
230
- ua << " Ruby/#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}"
231
- ua << " patchlevel #{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL
232
- ua << ")"
233
-
234
- request.add_field 'User-Agent', ua
235
- request.add_field 'Connection', 'keep-alive'
236
- request.add_field 'Keep-Alive', '30'
237
-
238
- # HACK work around EOFError bug in Net::HTTP
239
- # NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible
240
- # to install gems.
241
- retried = false
242
- begin
243
- @requests[connection_id] += 1
244
- response = connection.request(request)
245
- rescue EOFError, Errno::ECONNABORTED
246
- requests = @requests[connection_id]
247
- say "connection reset after #{requests} requests, retrying" if
248
- Gem.configuration.really_verbose
249
-
250
- raise Gem::RemoteFetcher::FetchError, 'too many connection resets' if
251
- retried
252
-
253
- @requests[connection_id] = 0
254
-
255
- connection.finish
256
- connection.start
257
- retried = true
258
- retry
259
- end
256
+ response = request uri
260
257
 
261
258
  case response
262
259
  when Net::HTTPOK then
263
260
  block.call(StringIO.new(response.body)) if block
264
261
  when Net::HTTPRedirection then
265
- raise Gem::RemoteFetcher::FetchError, "too many redirects" if depth > 10
262
+ raise FetchError.new('too many redirects', uri) if depth > 10
263
+
266
264
  open_uri_or_path(response['Location'], depth + 1, &block)
267
265
  else
268
- raise Gem::RemoteFetcher::FetchError,
269
- "bad response #{response.message} #{response.code}"
266
+ raise FetchError.new("bad response #{response.message} #{response.code}", uri)
270
267
  end
271
268
  end
272
269
  end
273
270
 
271
+ ##
272
+ # Performs a Net::HTTP request of type +request_class+ on +uri+ returning
273
+ # a Net::HTTP response object. request maintains a table of persistent
274
+ # connections to reduce connect overhead.
275
+
276
+ def request(uri, request_class = Net::HTTP::Get)
277
+ request = request_class.new uri.request_uri
278
+
279
+ unless uri.nil? || uri.user.nil? || uri.user.empty? then
280
+ request.basic_auth uri.user, uri.password
281
+ end
282
+
283
+ ua = "RubyGems/#{Gem::RubyGemsVersion} #{Gem::Platform.local}"
284
+ ua << " Ruby/#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}"
285
+ ua << " patchlevel #{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL
286
+ ua << ")"
287
+
288
+ request.add_field 'User-Agent', ua
289
+ request.add_field 'Connection', 'keep-alive'
290
+ request.add_field 'Keep-Alive', '30'
291
+
292
+ connection = connection_for uri
293
+
294
+ retried = false
295
+ bad_response = false
296
+
297
+ # HACK work around EOFError bug in Net::HTTP
298
+ # NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible
299
+ # to install gems.
300
+ begin
301
+ @requests[connection.object_id] += 1
302
+ response = connection.request request
303
+ say "#{request.method} #{response.code} #{response.message}: #{uri}" if
304
+ Gem.configuration.really_verbose
305
+ rescue Net::HTTPBadResponse
306
+ reset connection
307
+
308
+ raise FetchError.new('too many bad responses', uri) if bad_response
309
+
310
+ bad_response = true
311
+ retry
312
+ rescue EOFError, Errno::ECONNABORTED, Errno::ECONNRESET
313
+ requests = @requests[connection.object_id]
314
+ say "connection reset after #{requests} requests, retrying" if
315
+ Gem.configuration.really_verbose
316
+
317
+ raise FetchError.new('too many connection resets', uri) if retried
318
+
319
+ reset connection
320
+
321
+ retried = true
322
+ retry
323
+ end
324
+
325
+ response
326
+ end
327
+
328
+ ##
329
+ # Resets HTTP connection +connection+.
330
+
331
+ def reset(connection)
332
+ @requests.delete connection.object_id
333
+
334
+ connection.finish
335
+ connection.start
336
+ end
337
+
338
+ ##
274
339
  # Checks if the provided string is a file:// URI.
340
+
275
341
  def file_uri?(uri)
276
342
  uri =~ %r{\Afile://}
277
343
  end
278
344
 
345
+ ##
279
346
  # Given a file:// URI, returns its local path.
347
+
280
348
  def get_file_uri_path(uri)
281
349
  uri.sub(%r{\Afile://}, '')
282
350
  end