libgems 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. data/ChangeLog +5811 -0
  2. data/History.txt +887 -0
  3. data/LICENSE.txt +51 -0
  4. data/README.md +87 -0
  5. data/Rakefile +113 -0
  6. data/lib/gauntlet_libgems.rb +50 -0
  7. data/lib/libgems.rb +1246 -0
  8. data/lib/libgems/builder.rb +102 -0
  9. data/lib/libgems/command.rb +534 -0
  10. data/lib/libgems/command_manager.rb +182 -0
  11. data/lib/libgems/commands/build_command.rb +53 -0
  12. data/lib/libgems/commands/cert_command.rb +86 -0
  13. data/lib/libgems/commands/check_command.rb +80 -0
  14. data/lib/libgems/commands/cleanup_command.rb +106 -0
  15. data/lib/libgems/commands/contents_command.rb +98 -0
  16. data/lib/libgems/commands/dependency_command.rb +195 -0
  17. data/lib/libgems/commands/environment_command.rb +133 -0
  18. data/lib/libgems/commands/fetch_command.rb +67 -0
  19. data/lib/libgems/commands/generate_index_command.rb +133 -0
  20. data/lib/libgems/commands/help_command.rb +172 -0
  21. data/lib/libgems/commands/install_command.rb +178 -0
  22. data/lib/libgems/commands/list_command.rb +35 -0
  23. data/lib/libgems/commands/lock_command.rb +110 -0
  24. data/lib/libgems/commands/mirror_command.rb +111 -0
  25. data/lib/libgems/commands/outdated_command.rb +33 -0
  26. data/lib/libgems/commands/owner_command.rb +75 -0
  27. data/lib/libgems/commands/pristine_command.rb +93 -0
  28. data/lib/libgems/commands/push_command.rb +56 -0
  29. data/lib/libgems/commands/query_command.rb +280 -0
  30. data/lib/libgems/commands/rdoc_command.rb +91 -0
  31. data/lib/libgems/commands/search_command.rb +31 -0
  32. data/lib/libgems/commands/server_command.rb +86 -0
  33. data/lib/libgems/commands/sources_command.rb +157 -0
  34. data/lib/libgems/commands/specification_command.rb +125 -0
  35. data/lib/libgems/commands/stale_command.rb +27 -0
  36. data/lib/libgems/commands/uninstall_command.rb +83 -0
  37. data/lib/libgems/commands/unpack_command.rb +121 -0
  38. data/lib/libgems/commands/update_command.rb +160 -0
  39. data/lib/libgems/commands/which_command.rb +86 -0
  40. data/lib/libgems/config_file.rb +345 -0
  41. data/lib/libgems/custom_require.rb +44 -0
  42. data/lib/libgems/defaults.rb +101 -0
  43. data/lib/libgems/dependency.rb +227 -0
  44. data/lib/libgems/dependency_installer.rb +286 -0
  45. data/lib/libgems/dependency_list.rb +208 -0
  46. data/lib/libgems/doc_manager.rb +242 -0
  47. data/lib/libgems/errors.rb +35 -0
  48. data/lib/libgems/exceptions.rb +91 -0
  49. data/lib/libgems/ext.rb +18 -0
  50. data/lib/libgems/ext/builder.rb +56 -0
  51. data/lib/libgems/ext/configure_builder.rb +25 -0
  52. data/lib/libgems/ext/ext_conf_builder.rb +24 -0
  53. data/lib/libgems/ext/rake_builder.rb +39 -0
  54. data/lib/libgems/format.rb +81 -0
  55. data/lib/libgems/gem_openssl.rb +92 -0
  56. data/lib/libgems/gem_path_searcher.rb +100 -0
  57. data/lib/libgems/gem_runner.rb +79 -0
  58. data/lib/libgems/gemcutter_utilities.rb +49 -0
  59. data/lib/libgems/indexer.rb +720 -0
  60. data/lib/libgems/install_update_options.rb +125 -0
  61. data/lib/libgems/installer.rb +604 -0
  62. data/lib/libgems/local_remote_options.rb +135 -0
  63. data/lib/libgems/old_format.rb +153 -0
  64. data/lib/libgems/package.rb +97 -0
  65. data/lib/libgems/package/f_sync_dir.rb +23 -0
  66. data/lib/libgems/package/tar_header.rb +266 -0
  67. data/lib/libgems/package/tar_input.rb +222 -0
  68. data/lib/libgems/package/tar_output.rb +144 -0
  69. data/lib/libgems/package/tar_reader.rb +106 -0
  70. data/lib/libgems/package/tar_reader/entry.rb +141 -0
  71. data/lib/libgems/package/tar_writer.rb +241 -0
  72. data/lib/libgems/package_task.rb +126 -0
  73. data/lib/libgems/platform.rb +183 -0
  74. data/lib/libgems/remote_fetcher.rb +414 -0
  75. data/lib/libgems/require_paths_builder.rb +18 -0
  76. data/lib/libgems/requirement.rb +153 -0
  77. data/lib/libgems/security.rb +814 -0
  78. data/lib/libgems/server.rb +872 -0
  79. data/lib/libgems/source_index.rb +597 -0
  80. data/lib/libgems/source_info_cache.rb +395 -0
  81. data/lib/libgems/source_info_cache_entry.rb +56 -0
  82. data/lib/libgems/spec_fetcher.rb +337 -0
  83. data/lib/libgems/specification.rb +1487 -0
  84. data/lib/libgems/test_utilities.rb +147 -0
  85. data/lib/libgems/text.rb +65 -0
  86. data/lib/libgems/uninstaller.rb +278 -0
  87. data/lib/libgems/user_interaction.rb +527 -0
  88. data/lib/libgems/validator.rb +240 -0
  89. data/lib/libgems/version.rb +316 -0
  90. data/lib/libgems/version_option.rb +65 -0
  91. data/lib/rbconfig/datadir.rb +20 -0
  92. data/test/bogussources.rb +8 -0
  93. data/test/data/gem-private_key.pem +27 -0
  94. data/test/data/gem-public_cert.pem +20 -0
  95. data/test/fake_certlib/openssl.rb +7 -0
  96. data/test/foo/discover.rb +0 -0
  97. data/test/gem_installer_test_case.rb +97 -0
  98. data/test/gem_package_tar_test_case.rb +132 -0
  99. data/test/gemutilities.rb +605 -0
  100. data/test/insure_session.rb +43 -0
  101. data/test/mockgemui.rb +56 -0
  102. data/test/plugin/exception/libgems_plugin.rb +2 -0
  103. data/test/plugin/load/libgems_plugin.rb +1 -0
  104. data/test/plugin/standarderror/libgems_plugin.rb +2 -0
  105. data/test/private_key.pem +27 -0
  106. data/test/public_cert.pem +20 -0
  107. data/test/rubygems_plugin.rb +21 -0
  108. data/test/simple_gem.rb +66 -0
  109. data/test/test_config.rb +12 -0
  110. data/test/test_gem.rb +780 -0
  111. data/test/test_gem_builder.rb +27 -0
  112. data/test/test_gem_command.rb +178 -0
  113. data/test/test_gem_command_manager.rb +207 -0
  114. data/test/test_gem_commands_build_command.rb +74 -0
  115. data/test/test_gem_commands_cert_command.rb +124 -0
  116. data/test/test_gem_commands_check_command.rb +18 -0
  117. data/test/test_gem_commands_contents_command.rb +156 -0
  118. data/test/test_gem_commands_dependency_command.rb +216 -0
  119. data/test/test_gem_commands_environment_command.rb +144 -0
  120. data/test/test_gem_commands_fetch_command.rb +76 -0
  121. data/test/test_gem_commands_generate_index_command.rb +135 -0
  122. data/test/test_gem_commands_install_command.rb +315 -0
  123. data/test/test_gem_commands_list_command.rb +36 -0
  124. data/test/test_gem_commands_lock_command.rb +68 -0
  125. data/test/test_gem_commands_mirror_command.rb +60 -0
  126. data/test/test_gem_commands_outdated_command.rb +40 -0
  127. data/test/test_gem_commands_owner_command.rb +105 -0
  128. data/test/test_gem_commands_pristine_command.rb +108 -0
  129. data/test/test_gem_commands_push_command.rb +81 -0
  130. data/test/test_gem_commands_query_command.rb +426 -0
  131. data/test/test_gem_commands_server_command.rb +59 -0
  132. data/test/test_gem_commands_sources_command.rb +209 -0
  133. data/test/test_gem_commands_specification_command.rb +139 -0
  134. data/test/test_gem_commands_stale_command.rb +38 -0
  135. data/test/test_gem_commands_uninstall_command.rb +83 -0
  136. data/test/test_gem_commands_unpack_command.rb +199 -0
  137. data/test/test_gem_commands_update_command.rb +207 -0
  138. data/test/test_gem_commands_which_command.rb +66 -0
  139. data/test/test_gem_config_file.rb +287 -0
  140. data/test/test_gem_dependency.rb +149 -0
  141. data/test/test_gem_dependency_installer.rb +661 -0
  142. data/test/test_gem_dependency_list.rb +230 -0
  143. data/test/test_gem_doc_manager.rb +31 -0
  144. data/test/test_gem_ext_configure_builder.rb +84 -0
  145. data/test/test_gem_ext_ext_conf_builder.rb +173 -0
  146. data/test/test_gem_ext_rake_builder.rb +81 -0
  147. data/test/test_gem_format.rb +70 -0
  148. data/test/test_gem_gem_path_searcher.rb +78 -0
  149. data/test/test_gem_gem_runner.rb +45 -0
  150. data/test/test_gem_gemcutter_utilities.rb +103 -0
  151. data/test/test_gem_indexer.rb +673 -0
  152. data/test/test_gem_install_update_options.rb +68 -0
  153. data/test/test_gem_installer.rb +857 -0
  154. data/test/test_gem_local_remote_options.rb +97 -0
  155. data/test/test_gem_package_tar_header.rb +130 -0
  156. data/test/test_gem_package_tar_input.rb +112 -0
  157. data/test/test_gem_package_tar_output.rb +97 -0
  158. data/test/test_gem_package_tar_reader.rb +46 -0
  159. data/test/test_gem_package_tar_reader_entry.rb +109 -0
  160. data/test/test_gem_package_tar_writer.rb +144 -0
  161. data/test/test_gem_package_task.rb +59 -0
  162. data/test/test_gem_platform.rb +264 -0
  163. data/test/test_gem_remote_fetcher.rb +740 -0
  164. data/test/test_gem_requirement.rb +292 -0
  165. data/test/test_gem_server.rb +356 -0
  166. data/test/test_gem_silent_ui.rb +113 -0
  167. data/test/test_gem_source_index.rb +461 -0
  168. data/test/test_gem_spec_fetcher.rb +410 -0
  169. data/test/test_gem_specification.rb +1334 -0
  170. data/test/test_gem_stream_ui.rb +218 -0
  171. data/test/test_gem_text.rb +43 -0
  172. data/test/test_gem_uninstaller.rb +146 -0
  173. data/test/test_gem_validator.rb +63 -0
  174. data/test/test_gem_version.rb +181 -0
  175. data/test/test_gem_version_option.rb +89 -0
  176. data/test/test_kernel.rb +59 -0
  177. 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
+