libgems 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +5811 -0
- data/History.txt +887 -0
- data/LICENSE.txt +51 -0
- data/README.md +87 -0
- data/Rakefile +113 -0
- data/lib/gauntlet_libgems.rb +50 -0
- data/lib/libgems.rb +1246 -0
- data/lib/libgems/builder.rb +102 -0
- data/lib/libgems/command.rb +534 -0
- data/lib/libgems/command_manager.rb +182 -0
- data/lib/libgems/commands/build_command.rb +53 -0
- data/lib/libgems/commands/cert_command.rb +86 -0
- data/lib/libgems/commands/check_command.rb +80 -0
- data/lib/libgems/commands/cleanup_command.rb +106 -0
- data/lib/libgems/commands/contents_command.rb +98 -0
- data/lib/libgems/commands/dependency_command.rb +195 -0
- data/lib/libgems/commands/environment_command.rb +133 -0
- data/lib/libgems/commands/fetch_command.rb +67 -0
- data/lib/libgems/commands/generate_index_command.rb +133 -0
- data/lib/libgems/commands/help_command.rb +172 -0
- data/lib/libgems/commands/install_command.rb +178 -0
- data/lib/libgems/commands/list_command.rb +35 -0
- data/lib/libgems/commands/lock_command.rb +110 -0
- data/lib/libgems/commands/mirror_command.rb +111 -0
- data/lib/libgems/commands/outdated_command.rb +33 -0
- data/lib/libgems/commands/owner_command.rb +75 -0
- data/lib/libgems/commands/pristine_command.rb +93 -0
- data/lib/libgems/commands/push_command.rb +56 -0
- data/lib/libgems/commands/query_command.rb +280 -0
- data/lib/libgems/commands/rdoc_command.rb +91 -0
- data/lib/libgems/commands/search_command.rb +31 -0
- data/lib/libgems/commands/server_command.rb +86 -0
- data/lib/libgems/commands/sources_command.rb +157 -0
- data/lib/libgems/commands/specification_command.rb +125 -0
- data/lib/libgems/commands/stale_command.rb +27 -0
- data/lib/libgems/commands/uninstall_command.rb +83 -0
- data/lib/libgems/commands/unpack_command.rb +121 -0
- data/lib/libgems/commands/update_command.rb +160 -0
- data/lib/libgems/commands/which_command.rb +86 -0
- data/lib/libgems/config_file.rb +345 -0
- data/lib/libgems/custom_require.rb +44 -0
- data/lib/libgems/defaults.rb +101 -0
- data/lib/libgems/dependency.rb +227 -0
- data/lib/libgems/dependency_installer.rb +286 -0
- data/lib/libgems/dependency_list.rb +208 -0
- data/lib/libgems/doc_manager.rb +242 -0
- data/lib/libgems/errors.rb +35 -0
- data/lib/libgems/exceptions.rb +91 -0
- data/lib/libgems/ext.rb +18 -0
- data/lib/libgems/ext/builder.rb +56 -0
- data/lib/libgems/ext/configure_builder.rb +25 -0
- data/lib/libgems/ext/ext_conf_builder.rb +24 -0
- data/lib/libgems/ext/rake_builder.rb +39 -0
- data/lib/libgems/format.rb +81 -0
- data/lib/libgems/gem_openssl.rb +92 -0
- data/lib/libgems/gem_path_searcher.rb +100 -0
- data/lib/libgems/gem_runner.rb +79 -0
- data/lib/libgems/gemcutter_utilities.rb +49 -0
- data/lib/libgems/indexer.rb +720 -0
- data/lib/libgems/install_update_options.rb +125 -0
- data/lib/libgems/installer.rb +604 -0
- data/lib/libgems/local_remote_options.rb +135 -0
- data/lib/libgems/old_format.rb +153 -0
- data/lib/libgems/package.rb +97 -0
- data/lib/libgems/package/f_sync_dir.rb +23 -0
- data/lib/libgems/package/tar_header.rb +266 -0
- data/lib/libgems/package/tar_input.rb +222 -0
- data/lib/libgems/package/tar_output.rb +144 -0
- data/lib/libgems/package/tar_reader.rb +106 -0
- data/lib/libgems/package/tar_reader/entry.rb +141 -0
- data/lib/libgems/package/tar_writer.rb +241 -0
- data/lib/libgems/package_task.rb +126 -0
- data/lib/libgems/platform.rb +183 -0
- data/lib/libgems/remote_fetcher.rb +414 -0
- data/lib/libgems/require_paths_builder.rb +18 -0
- data/lib/libgems/requirement.rb +153 -0
- data/lib/libgems/security.rb +814 -0
- data/lib/libgems/server.rb +872 -0
- data/lib/libgems/source_index.rb +597 -0
- data/lib/libgems/source_info_cache.rb +395 -0
- data/lib/libgems/source_info_cache_entry.rb +56 -0
- data/lib/libgems/spec_fetcher.rb +337 -0
- data/lib/libgems/specification.rb +1487 -0
- data/lib/libgems/test_utilities.rb +147 -0
- data/lib/libgems/text.rb +65 -0
- data/lib/libgems/uninstaller.rb +278 -0
- data/lib/libgems/user_interaction.rb +527 -0
- data/lib/libgems/validator.rb +240 -0
- data/lib/libgems/version.rb +316 -0
- data/lib/libgems/version_option.rb +65 -0
- data/lib/rbconfig/datadir.rb +20 -0
- data/test/bogussources.rb +8 -0
- data/test/data/gem-private_key.pem +27 -0
- data/test/data/gem-public_cert.pem +20 -0
- data/test/fake_certlib/openssl.rb +7 -0
- data/test/foo/discover.rb +0 -0
- data/test/gem_installer_test_case.rb +97 -0
- data/test/gem_package_tar_test_case.rb +132 -0
- data/test/gemutilities.rb +605 -0
- data/test/insure_session.rb +43 -0
- data/test/mockgemui.rb +56 -0
- data/test/plugin/exception/libgems_plugin.rb +2 -0
- data/test/plugin/load/libgems_plugin.rb +1 -0
- data/test/plugin/standarderror/libgems_plugin.rb +2 -0
- data/test/private_key.pem +27 -0
- data/test/public_cert.pem +20 -0
- data/test/rubygems_plugin.rb +21 -0
- data/test/simple_gem.rb +66 -0
- data/test/test_config.rb +12 -0
- data/test/test_gem.rb +780 -0
- data/test/test_gem_builder.rb +27 -0
- data/test/test_gem_command.rb +178 -0
- data/test/test_gem_command_manager.rb +207 -0
- data/test/test_gem_commands_build_command.rb +74 -0
- data/test/test_gem_commands_cert_command.rb +124 -0
- data/test/test_gem_commands_check_command.rb +18 -0
- data/test/test_gem_commands_contents_command.rb +156 -0
- data/test/test_gem_commands_dependency_command.rb +216 -0
- data/test/test_gem_commands_environment_command.rb +144 -0
- data/test/test_gem_commands_fetch_command.rb +76 -0
- data/test/test_gem_commands_generate_index_command.rb +135 -0
- data/test/test_gem_commands_install_command.rb +315 -0
- data/test/test_gem_commands_list_command.rb +36 -0
- data/test/test_gem_commands_lock_command.rb +68 -0
- data/test/test_gem_commands_mirror_command.rb +60 -0
- data/test/test_gem_commands_outdated_command.rb +40 -0
- data/test/test_gem_commands_owner_command.rb +105 -0
- data/test/test_gem_commands_pristine_command.rb +108 -0
- data/test/test_gem_commands_push_command.rb +81 -0
- data/test/test_gem_commands_query_command.rb +426 -0
- data/test/test_gem_commands_server_command.rb +59 -0
- data/test/test_gem_commands_sources_command.rb +209 -0
- data/test/test_gem_commands_specification_command.rb +139 -0
- data/test/test_gem_commands_stale_command.rb +38 -0
- data/test/test_gem_commands_uninstall_command.rb +83 -0
- data/test/test_gem_commands_unpack_command.rb +199 -0
- data/test/test_gem_commands_update_command.rb +207 -0
- data/test/test_gem_commands_which_command.rb +66 -0
- data/test/test_gem_config_file.rb +287 -0
- data/test/test_gem_dependency.rb +149 -0
- data/test/test_gem_dependency_installer.rb +661 -0
- data/test/test_gem_dependency_list.rb +230 -0
- data/test/test_gem_doc_manager.rb +31 -0
- data/test/test_gem_ext_configure_builder.rb +84 -0
- data/test/test_gem_ext_ext_conf_builder.rb +173 -0
- data/test/test_gem_ext_rake_builder.rb +81 -0
- data/test/test_gem_format.rb +70 -0
- data/test/test_gem_gem_path_searcher.rb +78 -0
- data/test/test_gem_gem_runner.rb +45 -0
- data/test/test_gem_gemcutter_utilities.rb +103 -0
- data/test/test_gem_indexer.rb +673 -0
- data/test/test_gem_install_update_options.rb +68 -0
- data/test/test_gem_installer.rb +857 -0
- data/test/test_gem_local_remote_options.rb +97 -0
- data/test/test_gem_package_tar_header.rb +130 -0
- data/test/test_gem_package_tar_input.rb +112 -0
- data/test/test_gem_package_tar_output.rb +97 -0
- data/test/test_gem_package_tar_reader.rb +46 -0
- data/test/test_gem_package_tar_reader_entry.rb +109 -0
- data/test/test_gem_package_tar_writer.rb +144 -0
- data/test/test_gem_package_task.rb +59 -0
- data/test/test_gem_platform.rb +264 -0
- data/test/test_gem_remote_fetcher.rb +740 -0
- data/test/test_gem_requirement.rb +292 -0
- data/test/test_gem_server.rb +356 -0
- data/test/test_gem_silent_ui.rb +113 -0
- data/test/test_gem_source_index.rb +461 -0
- data/test/test_gem_spec_fetcher.rb +410 -0
- data/test/test_gem_specification.rb +1334 -0
- data/test/test_gem_stream_ui.rb +218 -0
- data/test/test_gem_text.rb +43 -0
- data/test/test_gem_uninstaller.rb +146 -0
- data/test/test_gem_validator.rb +63 -0
- data/test/test_gem_version.rb +181 -0
- data/test/test_gem_version_option.rb +89 -0
- data/test/test_kernel.rb +59 -0
- metadata +402 -0
@@ -0,0 +1,183 @@
|
|
1
|
+
##
|
2
|
+
# Available list of platforms for targeting LibGems installations.
|
3
|
+
|
4
|
+
class LibGems::Platform
|
5
|
+
|
6
|
+
@local = nil
|
7
|
+
|
8
|
+
attr_accessor :cpu
|
9
|
+
|
10
|
+
attr_accessor :os
|
11
|
+
|
12
|
+
attr_accessor :version
|
13
|
+
|
14
|
+
def self.local
|
15
|
+
arch = LibGems::ConfigMap[:arch]
|
16
|
+
arch = "#{arch}_60" if arch =~ /mswin32$/
|
17
|
+
@local ||= new(arch)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.match(platform)
|
21
|
+
LibGems.platforms.any? do |local_platform|
|
22
|
+
platform.nil? or local_platform == platform or
|
23
|
+
(local_platform != LibGems::Platform::RUBY and local_platform =~ platform)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.new(arch) # :nodoc:
|
28
|
+
case arch
|
29
|
+
when LibGems::Platform::CURRENT then
|
30
|
+
LibGems::Platform.local
|
31
|
+
when LibGems::Platform::RUBY, nil, '' then
|
32
|
+
LibGems::Platform::RUBY
|
33
|
+
else
|
34
|
+
super
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(arch)
|
39
|
+
case arch
|
40
|
+
when Array then
|
41
|
+
@cpu, @os, @version = arch
|
42
|
+
when String then
|
43
|
+
arch = arch.split '-'
|
44
|
+
|
45
|
+
if arch.length > 2 and arch.last !~ /\d/ then # reassemble x86-linux-gnu
|
46
|
+
extra = arch.pop
|
47
|
+
arch.last << "-#{extra}"
|
48
|
+
end
|
49
|
+
|
50
|
+
cpu = arch.shift
|
51
|
+
|
52
|
+
@cpu = case cpu
|
53
|
+
when /i\d86/ then 'x86'
|
54
|
+
else cpu
|
55
|
+
end
|
56
|
+
|
57
|
+
if arch.length == 2 and arch.last =~ /^\d+(\.\d+)?$/ then # for command-line
|
58
|
+
@os, @version = arch
|
59
|
+
return
|
60
|
+
end
|
61
|
+
|
62
|
+
os, = arch
|
63
|
+
@cpu, os = nil, cpu if os.nil? # legacy jruby
|
64
|
+
|
65
|
+
@os, @version = case os
|
66
|
+
when /aix(\d+)/ then [ 'aix', $1 ]
|
67
|
+
when /cygwin/ then [ 'cygwin', nil ]
|
68
|
+
when /darwin(\d+)?/ then [ 'darwin', $1 ]
|
69
|
+
when /freebsd(\d+)/ then [ 'freebsd', $1 ]
|
70
|
+
when /hpux(\d+)/ then [ 'hpux', $1 ]
|
71
|
+
when /^java$/, /^jruby$/ then [ 'java', nil ]
|
72
|
+
when /^java([\d.]*)/ then [ 'java', $1 ]
|
73
|
+
when /^dotnet$/ then [ 'dotnet', nil ]
|
74
|
+
when /^dotnet([\d.]*)/ then [ 'dotnet', $1 ]
|
75
|
+
when /linux/ then [ 'linux', $1 ]
|
76
|
+
when /mingw32/ then [ 'mingw32', nil ]
|
77
|
+
when /(mswin\d+)(\_(\d+))?/ then
|
78
|
+
os, version = $1, $3
|
79
|
+
@cpu = 'x86' if @cpu.nil? and os =~ /32$/
|
80
|
+
[os, version]
|
81
|
+
when /netbsdelf/ then [ 'netbsdelf', nil ]
|
82
|
+
when /openbsd(\d+\.\d+)/ then [ 'openbsd', $1 ]
|
83
|
+
when /solaris(\d+\.\d+)/ then [ 'solaris', $1 ]
|
84
|
+
# test
|
85
|
+
when /^(\w+_platform)(\d+)/ then [ $1, $2 ]
|
86
|
+
else [ 'unknown', nil ]
|
87
|
+
end
|
88
|
+
when LibGems::Platform then
|
89
|
+
@cpu = arch.cpu
|
90
|
+
@os = arch.os
|
91
|
+
@version = arch.version
|
92
|
+
else
|
93
|
+
raise ArgumentError, "invalid argument #{arch.inspect}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def inspect
|
98
|
+
"#<%s:0x%x @cpu=%p, @os=%p, @version=%p>" % [self.class, object_id, *to_a]
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_a
|
102
|
+
[@cpu, @os, @version]
|
103
|
+
end
|
104
|
+
|
105
|
+
def to_s
|
106
|
+
to_a.compact.join '-'
|
107
|
+
end
|
108
|
+
|
109
|
+
def empty?
|
110
|
+
to_s.empty?
|
111
|
+
end
|
112
|
+
|
113
|
+
##
|
114
|
+
# Is +other+ equal to this platform? Two platforms are equal if they have
|
115
|
+
# the same CPU, OS and version.
|
116
|
+
|
117
|
+
def ==(other)
|
118
|
+
self.class === other and
|
119
|
+
@cpu == other.cpu and @os == other.os and @version == other.version
|
120
|
+
end
|
121
|
+
|
122
|
+
##
|
123
|
+
# Does +other+ match this platform? Two platforms match if they have the
|
124
|
+
# same CPU, or either has a CPU of 'universal', they have the same OS, and
|
125
|
+
# they have the same version, or either has no version.
|
126
|
+
|
127
|
+
def ===(other)
|
128
|
+
return nil unless LibGems::Platform === other
|
129
|
+
|
130
|
+
# cpu
|
131
|
+
(@cpu == 'universal' or other.cpu == 'universal' or @cpu == other.cpu) and
|
132
|
+
|
133
|
+
# os
|
134
|
+
@os == other.os and
|
135
|
+
|
136
|
+
# version
|
137
|
+
(@version.nil? or other.version.nil? or @version == other.version)
|
138
|
+
end
|
139
|
+
|
140
|
+
##
|
141
|
+
# Does +other+ match this platform? If +other+ is a String it will be
|
142
|
+
# converted to a LibGems::Platform first. See #=== for matching rules.
|
143
|
+
|
144
|
+
def =~(other)
|
145
|
+
case other
|
146
|
+
when LibGems::Platform then # nop
|
147
|
+
when String then
|
148
|
+
# This data is from http://gems.rubyforge.org/gems/yaml on 19 Aug 2007
|
149
|
+
other = case other
|
150
|
+
when /^i686-darwin(\d)/ then ['x86', 'darwin', $1 ]
|
151
|
+
when /^i\d86-linux/ then ['x86', 'linux', nil ]
|
152
|
+
when 'java', 'jruby' then [nil, 'java', nil ]
|
153
|
+
when /dotnet(\-(\d+\.\d+))?/ then ['universal','dotnet', $2 ]
|
154
|
+
when /mswin32(\_(\d+))?/ then ['x86', 'mswin32', $2 ]
|
155
|
+
when 'powerpc-darwin' then ['powerpc', 'darwin', nil ]
|
156
|
+
when /powerpc-darwin(\d)/ then ['powerpc', 'darwin', $1 ]
|
157
|
+
when /sparc-solaris2.8/ then ['sparc', 'solaris', '2.8' ]
|
158
|
+
when /universal-darwin(\d)/ then ['universal', 'darwin', $1 ]
|
159
|
+
else other
|
160
|
+
end
|
161
|
+
|
162
|
+
other = LibGems::Platform.new other
|
163
|
+
else
|
164
|
+
return nil
|
165
|
+
end
|
166
|
+
|
167
|
+
self === other
|
168
|
+
end
|
169
|
+
|
170
|
+
##
|
171
|
+
# A pure-ruby gem that may use LibGems::Specification#extensions to build
|
172
|
+
# binary files.
|
173
|
+
|
174
|
+
RUBY = 'ruby'
|
175
|
+
|
176
|
+
##
|
177
|
+
# A platform-specific gem that is built for the packaging ruby's platform.
|
178
|
+
# This will be replaced with LibGems::Platform::local.
|
179
|
+
|
180
|
+
CURRENT = 'current'
|
181
|
+
|
182
|
+
end
|
183
|
+
|
@@ -0,0 +1,414 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'stringio'
|
3
|
+
require 'time'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
require 'libgems'
|
7
|
+
|
8
|
+
##
|
9
|
+
# RemoteFetcher handles the details of fetching gems and gem information from
|
10
|
+
# a remote source.
|
11
|
+
|
12
|
+
class LibGems::RemoteFetcher
|
13
|
+
|
14
|
+
include LibGems::UserInteraction
|
15
|
+
|
16
|
+
##
|
17
|
+
# A FetchError exception wraps up the various possible IO and HTTP failures
|
18
|
+
# that could happen while downloading from the internet.
|
19
|
+
|
20
|
+
class FetchError < LibGems::Exception
|
21
|
+
|
22
|
+
##
|
23
|
+
# The URI which was being accessed when the exception happened.
|
24
|
+
|
25
|
+
attr_accessor :uri
|
26
|
+
|
27
|
+
def initialize(message, uri)
|
28
|
+
super message
|
29
|
+
@uri = uri
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_s # :nodoc:
|
33
|
+
"#{super} (#{uri})"
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
@fetcher = nil
|
39
|
+
|
40
|
+
##
|
41
|
+
# Cached RemoteFetcher instance.
|
42
|
+
|
43
|
+
def self.fetcher
|
44
|
+
@fetcher ||= self.new LibGems.configuration[:http_proxy]
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Initialize a remote fetcher using the source URI and possible proxy
|
49
|
+
# information.
|
50
|
+
#
|
51
|
+
# +proxy+
|
52
|
+
# * [String]: explicit specification of proxy; overrides any environment
|
53
|
+
# variable setting
|
54
|
+
# * nil: respect environment variables (HTTP_PROXY, HTTP_PROXY_USER,
|
55
|
+
# HTTP_PROXY_PASS)
|
56
|
+
# * <tt>:no_proxy</tt>: ignore environment variables and _don't_ use a proxy
|
57
|
+
|
58
|
+
def initialize(proxy = nil)
|
59
|
+
Socket.do_not_reverse_lookup = true
|
60
|
+
|
61
|
+
@connections = {}
|
62
|
+
@requests = Hash.new 0
|
63
|
+
@proxy_uri =
|
64
|
+
case proxy
|
65
|
+
when :no_proxy then nil
|
66
|
+
when nil then get_proxy_from_env
|
67
|
+
when URI::HTTP then proxy
|
68
|
+
else URI.parse(proxy)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
# Moves the gem +spec+ from +source_uri+ to the cache dir unless it is
|
74
|
+
# already there. If the source_uri is local the gem cache dir copy is
|
75
|
+
# always replaced.
|
76
|
+
|
77
|
+
def download(spec, source_uri, install_dir = LibGems.dir)
|
78
|
+
if File.writable?(install_dir)
|
79
|
+
cache_dir = File.join install_dir, 'cache'
|
80
|
+
else
|
81
|
+
cache_dir = File.join(LibGems.user_dir, 'cache')
|
82
|
+
end
|
83
|
+
|
84
|
+
gem_file_name = spec.file_name
|
85
|
+
local_gem_path = File.join cache_dir, gem_file_name
|
86
|
+
|
87
|
+
FileUtils.mkdir_p cache_dir rescue nil unless File.exist? cache_dir
|
88
|
+
|
89
|
+
# Always escape URI's to deal with potential spaces and such
|
90
|
+
unless URI::Generic === source_uri
|
91
|
+
source_uri = URI.parse(URI.const_defined?(:DEFAULT_PARSER) ?
|
92
|
+
URI::DEFAULT_PARSER.escape(source_uri) :
|
93
|
+
URI.escape(source_uri))
|
94
|
+
end
|
95
|
+
|
96
|
+
scheme = source_uri.scheme
|
97
|
+
|
98
|
+
# URI.parse gets confused by MS Windows paths with forward slashes.
|
99
|
+
scheme = nil if scheme =~ /^[a-z]$/i
|
100
|
+
|
101
|
+
case scheme
|
102
|
+
when 'http', 'https' then
|
103
|
+
unless File.exist? local_gem_path then
|
104
|
+
begin
|
105
|
+
say "Downloading gem #{gem_file_name}" if
|
106
|
+
LibGems.configuration.really_verbose
|
107
|
+
|
108
|
+
remote_gem_path = source_uri + "gems/#{gem_file_name}"
|
109
|
+
|
110
|
+
gem = self.fetch_path remote_gem_path
|
111
|
+
rescue LibGems::RemoteFetcher::FetchError
|
112
|
+
raise if spec.original_platform == spec.platform
|
113
|
+
|
114
|
+
alternate_name = "#{spec.original_name}.gem"
|
115
|
+
|
116
|
+
say "Failed, downloading gem #{alternate_name}" if
|
117
|
+
LibGems.configuration.really_verbose
|
118
|
+
|
119
|
+
remote_gem_path = source_uri + "gems/#{alternate_name}"
|
120
|
+
|
121
|
+
gem = self.fetch_path remote_gem_path
|
122
|
+
end
|
123
|
+
|
124
|
+
File.open local_gem_path, 'wb' do |fp|
|
125
|
+
fp.write gem
|
126
|
+
end
|
127
|
+
end
|
128
|
+
when 'file' then
|
129
|
+
begin
|
130
|
+
path = source_uri.path
|
131
|
+
path = File.dirname(path) if File.extname(path) == '.gem'
|
132
|
+
|
133
|
+
remote_gem_path = File.join(path, 'gems', gem_file_name)
|
134
|
+
|
135
|
+
FileUtils.cp(remote_gem_path, local_gem_path)
|
136
|
+
rescue Errno::EACCES
|
137
|
+
local_gem_path = source_uri.to_s
|
138
|
+
end
|
139
|
+
|
140
|
+
say "Using local gem #{local_gem_path}" if
|
141
|
+
LibGems.configuration.really_verbose
|
142
|
+
when nil then # TODO test for local overriding cache
|
143
|
+
source_path = if LibGems.win_platform? && source_uri.scheme &&
|
144
|
+
!source_uri.path.include?(':') then
|
145
|
+
"#{source_uri.scheme}:#{source_uri.path}"
|
146
|
+
else
|
147
|
+
source_uri.path
|
148
|
+
end
|
149
|
+
|
150
|
+
source_path = URI.unescape source_path
|
151
|
+
|
152
|
+
begin
|
153
|
+
FileUtils.cp source_path, local_gem_path unless
|
154
|
+
File.expand_path(source_path) == File.expand_path(local_gem_path)
|
155
|
+
rescue Errno::EACCES
|
156
|
+
local_gem_path = source_uri.to_s
|
157
|
+
end
|
158
|
+
|
159
|
+
say "Using local gem #{local_gem_path}" if
|
160
|
+
LibGems.configuration.really_verbose
|
161
|
+
else
|
162
|
+
raise LibGems::InstallError, "unsupported URI scheme #{source_uri.scheme}"
|
163
|
+
end
|
164
|
+
|
165
|
+
local_gem_path
|
166
|
+
end
|
167
|
+
|
168
|
+
##
|
169
|
+
# Downloads +uri+ and returns it as a String.
|
170
|
+
|
171
|
+
def fetch_path(uri, mtime = nil, head = false)
|
172
|
+
data = open_uri_or_path uri, mtime, head
|
173
|
+
data = LibGems.gunzip data if data and not head and uri.to_s =~ /gz$/
|
174
|
+
data
|
175
|
+
rescue FetchError
|
176
|
+
raise
|
177
|
+
rescue Timeout::Error
|
178
|
+
raise FetchError.new('timed out', uri)
|
179
|
+
rescue IOError, SocketError, SystemCallError => e
|
180
|
+
raise FetchError.new("#{e.class}: #{e}", uri)
|
181
|
+
end
|
182
|
+
|
183
|
+
##
|
184
|
+
# Returns the size of +uri+ in bytes.
|
185
|
+
|
186
|
+
def fetch_size(uri) # TODO: phase this out
|
187
|
+
response = fetch_path(uri, nil, true)
|
188
|
+
|
189
|
+
response['content-length'].to_i
|
190
|
+
end
|
191
|
+
|
192
|
+
def escape(str)
|
193
|
+
return unless str
|
194
|
+
URI.escape(str)
|
195
|
+
end
|
196
|
+
|
197
|
+
def unescape(str)
|
198
|
+
return unless str
|
199
|
+
URI.unescape(str)
|
200
|
+
end
|
201
|
+
|
202
|
+
##
|
203
|
+
# Returns an HTTP proxy URI if one is set in the environment variables.
|
204
|
+
|
205
|
+
def get_proxy_from_env
|
206
|
+
env_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
|
207
|
+
|
208
|
+
return nil if env_proxy.nil? or env_proxy.empty?
|
209
|
+
|
210
|
+
uri = URI.parse(normalize_uri(env_proxy))
|
211
|
+
|
212
|
+
if uri and uri.user.nil? and uri.password.nil? then
|
213
|
+
# Probably we have http_proxy_* variables?
|
214
|
+
uri.user = escape(ENV['http_proxy_user'] || ENV['HTTP_PROXY_USER'])
|
215
|
+
uri.password = escape(ENV['http_proxy_pass'] || ENV['HTTP_PROXY_PASS'])
|
216
|
+
end
|
217
|
+
|
218
|
+
uri
|
219
|
+
end
|
220
|
+
|
221
|
+
##
|
222
|
+
# Normalize the URI by adding "http://" if it is missing.
|
223
|
+
|
224
|
+
def normalize_uri(uri)
|
225
|
+
(uri =~ /^(https?|ftp|file):/) ? uri : "http://#{uri}"
|
226
|
+
end
|
227
|
+
|
228
|
+
##
|
229
|
+
# Creates or an HTTP connection based on +uri+, or retrieves an existing
|
230
|
+
# connection, using a proxy if needed.
|
231
|
+
|
232
|
+
def connection_for(uri)
|
233
|
+
net_http_args = [uri.host, uri.port]
|
234
|
+
|
235
|
+
if @proxy_uri then
|
236
|
+
net_http_args += [
|
237
|
+
@proxy_uri.host,
|
238
|
+
@proxy_uri.port,
|
239
|
+
@proxy_uri.user,
|
240
|
+
@proxy_uri.password
|
241
|
+
]
|
242
|
+
end
|
243
|
+
|
244
|
+
connection_id = [Thread.current.object_id, *net_http_args].join ':'
|
245
|
+
@connections[connection_id] ||= Net::HTTP.new(*net_http_args)
|
246
|
+
connection = @connections[connection_id]
|
247
|
+
|
248
|
+
if uri.scheme == 'https' and not connection.started? then
|
249
|
+
require 'net/https'
|
250
|
+
connection.use_ssl = true
|
251
|
+
connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
252
|
+
end
|
253
|
+
|
254
|
+
connection.start unless connection.started?
|
255
|
+
|
256
|
+
connection
|
257
|
+
rescue Errno::EHOSTDOWN => e
|
258
|
+
raise FetchError.new(e.message, uri)
|
259
|
+
end
|
260
|
+
|
261
|
+
##
|
262
|
+
# Read the data from the (source based) URI, but if it is a file:// URI,
|
263
|
+
# read from the filesystem instead.
|
264
|
+
|
265
|
+
def open_uri_or_path(uri, last_modified = nil, head = false, depth = 0)
|
266
|
+
raise "block is dead" if block_given?
|
267
|
+
|
268
|
+
uri = URI.parse uri unless URI::Generic === uri
|
269
|
+
|
270
|
+
# This check is redundant unless LibGems::RemoteFetcher is likely
|
271
|
+
# to be used directly, since the scheme is checked elsewhere.
|
272
|
+
# - Daniel Berger
|
273
|
+
unless ['http', 'https', 'file'].include?(uri.scheme)
|
274
|
+
raise ArgumentError, 'uri scheme is invalid'
|
275
|
+
end
|
276
|
+
|
277
|
+
if uri.scheme == 'file'
|
278
|
+
path = uri.path
|
279
|
+
|
280
|
+
# Deal with leading slash on Windows paths
|
281
|
+
if path[0].chr == '/' && path[1].chr =~ /[a-zA-Z]/ && path[2].chr == ':'
|
282
|
+
path = path[1..-1]
|
283
|
+
end
|
284
|
+
|
285
|
+
return LibGems.read_binary(path)
|
286
|
+
end
|
287
|
+
|
288
|
+
fetch_type = head ? Net::HTTP::Head : Net::HTTP::Get
|
289
|
+
response = request uri, fetch_type, last_modified
|
290
|
+
|
291
|
+
case response
|
292
|
+
when Net::HTTPOK, Net::HTTPNotModified then
|
293
|
+
head ? response : response.body
|
294
|
+
when Net::HTTPMovedPermanently, Net::HTTPFound, Net::HTTPSeeOther,
|
295
|
+
Net::HTTPTemporaryRedirect then
|
296
|
+
raise FetchError.new('too many redirects', uri) if depth > 10
|
297
|
+
|
298
|
+
open_uri_or_path(response['Location'], last_modified, head, depth + 1)
|
299
|
+
else
|
300
|
+
raise FetchError.new("bad response #{response.message} #{response.code}", uri)
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
##
|
305
|
+
# Performs a Net::HTTP request of type +request_class+ on +uri+ returning
|
306
|
+
# a Net::HTTP response object. request maintains a table of persistent
|
307
|
+
# connections to reduce connect overhead.
|
308
|
+
|
309
|
+
def request(uri, request_class, last_modified = nil)
|
310
|
+
request = request_class.new uri.request_uri
|
311
|
+
|
312
|
+
unless uri.nil? || uri.user.nil? || uri.user.empty? then
|
313
|
+
request.basic_auth uri.user, uri.password
|
314
|
+
end
|
315
|
+
|
316
|
+
ua = "#{LibGems::NAME}/#{LibGems::VERSION} #{LibGems::Platform.local}"
|
317
|
+
ua << " Ruby/#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}"
|
318
|
+
ua << " patchlevel #{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL
|
319
|
+
ua << ")"
|
320
|
+
|
321
|
+
request.add_field 'User-Agent', ua
|
322
|
+
request.add_field 'Connection', 'keep-alive'
|
323
|
+
request.add_field 'Keep-Alive', '30'
|
324
|
+
|
325
|
+
if last_modified then
|
326
|
+
last_modified = last_modified.utc
|
327
|
+
request.add_field 'If-Modified-Since', last_modified.rfc2822
|
328
|
+
end
|
329
|
+
|
330
|
+
yield request if block_given?
|
331
|
+
|
332
|
+
connection = connection_for uri
|
333
|
+
|
334
|
+
retried = false
|
335
|
+
bad_response = false
|
336
|
+
|
337
|
+
begin
|
338
|
+
@requests[connection.object_id] += 1
|
339
|
+
|
340
|
+
say "#{request.method} #{uri}" if
|
341
|
+
LibGems.configuration.really_verbose
|
342
|
+
|
343
|
+
file_name = File.basename(uri.path)
|
344
|
+
# perform download progress reporter only for gems
|
345
|
+
if request.response_body_permitted? && file_name =~ /\.gem$/
|
346
|
+
reporter = ui.download_reporter
|
347
|
+
response = connection.request(request) do |incomplete_response|
|
348
|
+
if Net::HTTPOK === incomplete_response
|
349
|
+
reporter.fetch(file_name, incomplete_response.content_length)
|
350
|
+
downloaded = 0
|
351
|
+
data = ''
|
352
|
+
|
353
|
+
incomplete_response.read_body do |segment|
|
354
|
+
data << segment
|
355
|
+
downloaded += segment.length
|
356
|
+
reporter.update(downloaded)
|
357
|
+
end
|
358
|
+
reporter.done
|
359
|
+
if incomplete_response.respond_to? :body=
|
360
|
+
incomplete_response.body = data
|
361
|
+
else
|
362
|
+
incomplete_response.instance_variable_set(:@body, data)
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
366
|
+
else
|
367
|
+
response = connection.request request
|
368
|
+
end
|
369
|
+
|
370
|
+
say "#{response.code} #{response.message}" if
|
371
|
+
LibGems.configuration.really_verbose
|
372
|
+
|
373
|
+
rescue Net::HTTPBadResponse
|
374
|
+
say "bad response" if LibGems.configuration.really_verbose
|
375
|
+
|
376
|
+
reset connection
|
377
|
+
|
378
|
+
raise FetchError.new('too many bad responses', uri) if bad_response
|
379
|
+
|
380
|
+
bad_response = true
|
381
|
+
retry
|
382
|
+
# HACK work around EOFError bug in Net::HTTP
|
383
|
+
# NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible
|
384
|
+
# to install gems.
|
385
|
+
rescue EOFError, Timeout::Error,
|
386
|
+
Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE
|
387
|
+
|
388
|
+
requests = @requests[connection.object_id]
|
389
|
+
say "connection reset after #{requests} requests, retrying" if
|
390
|
+
LibGems.configuration.really_verbose
|
391
|
+
|
392
|
+
raise FetchError.new('too many connection resets', uri) if retried
|
393
|
+
|
394
|
+
reset connection
|
395
|
+
|
396
|
+
retried = true
|
397
|
+
retry
|
398
|
+
end
|
399
|
+
|
400
|
+
response
|
401
|
+
end
|
402
|
+
|
403
|
+
##
|
404
|
+
# Resets HTTP connection +connection+.
|
405
|
+
|
406
|
+
def reset(connection)
|
407
|
+
@requests.delete connection.object_id
|
408
|
+
|
409
|
+
connection.finish
|
410
|
+
connection.start
|
411
|
+
end
|
412
|
+
|
413
|
+
end
|
414
|
+
|