Permalink
Browse files

Fix loading specs from the future, handle misbehaving HTTP servers.

git-svn-id: http://rubygems.rubyforge.org/svn/trunk@1792 3d4018f9-ac1a-0410-99e9-8a154d859a19
  • Loading branch information...
drbrain
drbrain committed Jun 21, 2008
1 parent a1f323f commit 505f81ebcdf3bf750167c90212b769064d64c6da
Showing with 63 additions and 14 deletions.
  1. +7 −0 ChangeLog
  2. +22 −4 lib/rubygems/remote_fetcher.rb
  3. +18 −9 lib/rubygems/specification.rb
  4. +16 −1 test/test_gem_specification.rb
View
@@ -1,3 +1,10 @@
+2008-06-21 Eric Hodel <drbrain@segment7.net>
+
+ * lib/rubygems/specification.rb: Load specifications from the future.
+ Roll back specification version change.
+ * lib/rubygems/remote_fetcher.rb: Reset connection when an HTTP
+ server misbehaves.
+
2008-06-20 Eric Hodel <drbrain@segment7.net>
* lib/rubygems/uninstaller.rb: Improve output when a gem to uninstall
@@ -158,7 +158,9 @@ def fetch_size(uri)
response = request uri, Net::HTTP::Head
- if response.code !~ /^2/ then
+ case response
+ when Net::HTTPOK then
+ else
raise FetchError.new("bad response #{response.message} #{response.code}", uri)
end
@@ -290,6 +292,7 @@ def request(uri, request_class = Net::HTTP::Get)
connection = connection_for uri
retried = false
+ bad_response = false
# HACK work around EOFError bug in Net::HTTP
# NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible
@@ -299,24 +302,39 @@ def request(uri, request_class = Net::HTTP::Get)
response = connection.request request
say "#{request.method} #{response.code} #{response.message}: #{uri}" if
Gem.configuration.really_verbose
+ rescue Net::HTTPBadResponse
+ reset connection
+
+ raise FetchError.new('too many bad responses', uri) if bad_response
+
+ bad_response = true
+ retry
rescue EOFError, Errno::ECONNABORTED, Errno::ECONNRESET
requests = @requests[connection.object_id]
say "connection reset after #{requests} requests, retrying" if
Gem.configuration.really_verbose
raise FetchError.new('too many connection resets', uri) if retried
- @requests.delete connection.object_id
+ reset connection
- connection.finish
- connection.start
retried = true
retry
end
response
end
+ ##
+ # Resets HTTP connection +connection+.
+
+ def reset(connection)
+ @requests.delete connection.object_id
+
+ connection.finish
+ connection.start
+ end
+
##
# Checks if the provided string is a file:// URI.
@@ -60,8 +60,10 @@ class Specification
# changes.
#--
# When updating this number, be sure to also update #to_ruby.
+ #
+ # NOTE RubyGems < 1.2 cannot load specification versions > 2.
- CURRENT_SPECIFICATION_VERSION = 3
+ CURRENT_SPECIFICATION_VERSION = 2
##
# An informal list of changes to the specification. The highest-valued
@@ -77,13 +79,10 @@ class Specification
'Added "required_rubygems_version"',
'Now forward-compatible with future versions',
],
- 3 => [
- 'Added dependency types',
- ],
}
# :stopdoc:
- MARSHAL_FIELDS = { -1 => 16, 1 => 16, 2 => 16, 3 => 16 }
+ MARSHAL_FIELDS = { -1 => 16, 1 => 16, 2 => 16 }
now = Time.at(Time.now.to_i)
TODAY = now - ((now.to_i + now.gmt_offset) % 86400)
@@ -248,10 +247,12 @@ def self.attribute_alias_singular(singular, plural)
}
end
+ ##
# Dump only crucial instance variables.
- #
+ #--
# MAINTAIN ORDER!
- def _dump(limit) # :nodoc:
+
+ def _dump(limit)
Marshal.dump [
@rubygems_version,
@specification_version,
@@ -273,7 +274,9 @@ def _dump(limit) # :nodoc:
]
end
+ ##
# Load custom marshal format, re-initializing defaults as needed
+
def self._load(str)
array = Marshal.load str
@@ -282,9 +285,15 @@ def self._load(str)
current_version = CURRENT_SPECIFICATION_VERSION
- field_count = MARSHAL_FIELDS[spec.specification_version]
+ field_count = if spec.specification_version > current_version then
+ spec.instance_variable_set :@specification_version,
+ current_version
+ MARSHAL_FIELDS[current_version]
+ else
+ MARSHAL_FIELDS[spec.specification_version]
+ end
- if field_count.nil? or array.size < field_count then
+ if array.size < field_count then
raise TypeError, "invalid Gem::Specification format #{array.inspect}"
end
@@ -71,6 +71,8 @@ def setup
File.open File.join(@tempdir, 'bin', 'exec'), 'w' do |fp|
fp.puts "#!#{Gem.ruby}"
end
+
+ @current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
end
def test_self_attribute_names
@@ -112,6 +114,19 @@ def test_self_attribute_names
assert_equal expected_value, actual_value
end
+ def test_self__load_future
+ spec = Gem::Specification.new
+ spec.name = 'a'
+ spec.version = '1'
+ spec.specification_version = @current_version + 1
+
+ new_spec = Marshal.load Marshal.dump(spec)
+
+ assert_equal 'a', new_spec.name
+ assert_equal Gem::Version.new(1), new_spec.version
+ assert_equal @current_version, new_spec.specification_version
+ end
+
def test_self_load
spec = File.join @gemhome, 'specifications', "#{@a2.full_name}.gemspec"
gs = Gem::Specification.load spec
@@ -689,7 +704,7 @@ def test_to_ruby_fancy
if s.respond_to? :specification_version then
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
- s.specification_version = 3
+ s.specification_version = 2
if current_version >= 3 then
s.add_runtime_dependency(%q<rake>, [\"> 0.4\"])

0 comments on commit 505f81e

Please sign in to comment.