3 # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
5 # See LICENSE.txt for permissions.
9 require File.join(File.expand_path(File.dirname(__FILE__)), 'gemutilities')
12 require 'rubygems/remote_fetcher'
14 # = Testing Proxy Settings
16 # These tests check the proper proxy server settings by running two
17 # web servers. The web server at http://localhost:#{SERVER_PORT}
18 # represents the normal gem server and returns a gemspec with a rake
19 # version of 0.4.11. The web server at http://localhost:#{PROXY_PORT}
20 # represents the proxy server and returns a different dataset where
21 # rake has version 0.4.2. This allows us to detect which server is
24 # Note that the proxy server is not a *real* proxy server. But our
25 # software doesn't really care, as long as we hit the proxy URL when a
26 # proxy is configured.
28 class TestGemRemoteFetcher < RubyGemTestCase
30 include Gem::DefaultUserInteraction
33 --- !ruby/object:Gem::Cache
35 rake-0.4.11: !ruby/object:Gem::Specification
36 rubygems_version: "0.7"
37 specification_version: 1
39 version: !ruby/object:Gem::Version
42 summary: Ruby based make-like utility.
46 email: jim@weirichhouse.org
47 homepage: http://rake.rubyforge.org
48 rubyforge_project: rake
49 description: Rake is a Make-like program implemented in Ruby. Tasks and dependencies are specified in standard Ruby syntax.
51 default_executable: rake
54 required_ruby_version: !ruby/object:Gem::Version::Requirement
58 - !ruby/object:Gem::Version
75 PROXY_DATA = SERVER_DATA.gsub(/0.4.11/, '0.4.2')
77 # don't let 1.8 and 1.9 autotest collide
78 RUBY_VERSION =~ /(\d+)\.(\d+)\.(\d+)/
79 # don't let parallel runners collide
80 PROXY_PORT = process_based_port + 100 + $1.to_i * 100 + $2.to_i * 10 + $3.to_i
81 SERVER_PORT = process_based_port + 200 + $1.to_i * 100 + $2.to_i * 10 + $3.to_i
85 self.class.start_servers
86 self.class.enable_yaml = true
87 self.class.enable_zip = false
88 ENV.delete 'http_proxy'
89 ENV.delete 'HTTP_PROXY'
90 ENV.delete 'http_proxy_user'
91 ENV.delete 'HTTP_PROXY_USER'
92 ENV.delete 'http_proxy_pass'
93 ENV.delete 'HTTP_PROXY_PASS'
95 base_server_uri = "http://localhost:#{SERVER_PORT}"
96 @proxy_uri = "http://localhost:#{PROXY_PORT}"
98 @server_uri = base_server_uri + "/yaml"
99 @server_z_uri = base_server_uri + "/yaml.Z"
101 # REFACTOR: copied from test_gem_dependency_installer.rb
102 @gems_dir = File.join @tempdir, 'gems'
103 @cache_dir = File.join @gemhome, 'cache'
104 FileUtils.mkdir @gems_dir
106 @a1, @a1_gem = util_gem 'a', '1' do |s| s.executables << 'a_bin' end
108 Gem::RemoteFetcher.fetcher = nil
111 def test_self_fetcher
112 fetcher = Gem::RemoteFetcher.fetcher
113 assert_not_nil fetcher
114 assert_kind_of Gem::RemoteFetcher, fetcher
117 def test_self_fetcher_with_proxy
118 proxy_uri = 'http://proxy.example.com'
119 Gem.configuration[:http_proxy] = proxy_uri
120 fetcher = Gem::RemoteFetcher.fetcher
121 assert_not_nil fetcher
122 assert_kind_of Gem::RemoteFetcher, fetcher
123 assert_equal proxy_uri, fetcher.instance_variable_get(:@proxy_uri).to_s
126 def test_self_fetcher_with_proxy_URI
127 proxy_uri = URI.parse 'http://proxy.example.com'
128 Gem.configuration[:http_proxy] = proxy_uri
129 fetcher = Gem::RemoteFetcher.fetcher
130 assert_not_nil fetcher
131 assert_kind_of Gem::RemoteFetcher, fetcher
132 assert_equal proxy_uri, fetcher.instance_variable_get(:@proxy_uri)
135 def test_fetch_size_bad_uri
136 fetcher = Gem::RemoteFetcher.new nil
138 e = assert_raise ArgumentError do
139 fetcher.fetch_size 'gems.example.com/yaml'
142 assert_equal 'uri is not an HTTP URI', e.message
145 def test_fetch_size_socket_error
146 fetcher = Gem::RemoteFetcher.new nil
147 def fetcher.connection_for(uri)
151 uri = 'http://gems.example.com/yaml'
152 e = assert_raise Gem::RemoteFetcher::FetchError do
153 fetcher.fetch_size uri
156 assert_equal "SocketError (SocketError)\n\tfetching size (#{uri})",
162 fetcher = Gem::RemoteFetcher.new nil
163 assert_data_from_server fetcher.fetch_path(@server_uri)
164 assert_equal SERVER_DATA.size, fetcher.fetch_size(@server_uri)
168 def util_fuck_with_fetcher data, blow = false
169 fetcher = Gem::RemoteFetcher.fetcher
170 fetcher.instance_variable_set :@test_data, data
173 def fetcher.fetch_path arg
178 def fetcher.fetch_path arg
179 # OMG I'm such an ass
180 class << self; remove_method :fetch_path; end
181 def self.fetch_path arg
186 raise Gem::RemoteFetcher::FetchError.new("haha!", nil)
195 File.open @a1_gem, 'rb' do |fp|
199 fetcher = util_fuck_with_fetcher a1_data
201 a1_cache_gem = File.join(@gemhome, 'cache', "#{@a1.full_name}.gem")
202 assert_equal a1_cache_gem, fetcher.download(@a1, 'http://gems.example.com')
203 assert_equal("http://gems.example.com/gems/a-1.gem",
204 fetcher.instance_variable_get(:@test_arg).to_s)
205 assert File.exist?(a1_cache_gem)
208 def test_download_cached
209 FileUtils.mv @a1_gem, @cache_dir
211 inst = Gem::RemoteFetcher.fetcher
213 assert_equal File.join(@gemhome, 'cache', "#{@a1.full_name}.gem"),
214 inst.download(@a1, 'http://gems.example.com')
217 def test_download_local
218 FileUtils.mv @a1_gem, @tempdir
219 local_path = File.join @tempdir, "#{@a1.full_name}.gem"
222 Dir.chdir @tempdir do
223 inst = Gem::RemoteFetcher.fetcher
226 assert_equal File.join(@gemhome, 'cache', "#{@a1.full_name}.gem"),
227 inst.download(@a1, local_path)
230 def test_download_install_dir
232 File.open @a1_gem, 'rb' do |fp|
236 fetcher = util_fuck_with_fetcher a1_data
238 install_dir = File.join @tempdir, 'more_gems'
240 a1_cache_gem = File.join install_dir, 'cache', "#{@a1.full_name}.gem"
241 actual = fetcher.download(@a1, 'http://gems.example.com', install_dir)
243 assert_equal a1_cache_gem, actual
244 assert_equal("http://gems.example.com/gems/a-1.gem",
245 fetcher.instance_variable_get(:@test_arg).to_s)
247 assert File.exist?(a1_cache_gem)
250 unless win_platform? then # File.chmod doesn't work
251 def test_download_local_read_only
252 FileUtils.mv @a1_gem, @tempdir
253 local_path = File.join @tempdir, "#{@a1.full_name}.gem"
255 File.chmod 0555, File.join(@gemhome, 'cache')
257 Dir.chdir @tempdir do
258 inst = Gem::RemoteFetcher.fetcher
261 assert_equal File.join(@tempdir, "#{@a1.full_name}.gem"),
262 inst.download(@a1, local_path)
264 File.chmod 0755, File.join(@gemhome, 'cache')
268 def test_download_platform_legacy
269 original_platform = 'old-platform'
271 e1, e1_gem = util_gem 'e', '1' do |s|
272 s.platform = Gem::Platform::CURRENT
273 s.instance_variable_set :@original_platform, original_platform
277 File.open e1_gem, 'rb' do |fp|
281 fetcher = util_fuck_with_fetcher e1_data, :blow_chunks
283 e1_cache_gem = File.join(@gemhome, 'cache', "#{e1.full_name}.gem")
285 assert_equal e1_cache_gem, fetcher.download(e1, 'http://gems.example.com')
287 assert_equal("http://gems.example.com/gems/#{e1.original_name}.gem",
288 fetcher.instance_variable_get(:@test_arg).to_s)
289 assert File.exist?(e1_cache_gem)
292 def test_download_unsupported
293 inst = Gem::RemoteFetcher.fetcher
295 e = assert_raise Gem::InstallError do
296 inst.download @a1, 'ftp://gems.rubyforge.org'
299 assert_equal 'unsupported URI scheme ftp', e.message
302 def test_explicit_proxy
304 fetcher = Gem::RemoteFetcher.new @proxy_uri
305 assert_equal PROXY_DATA.size, fetcher.fetch_size(@server_uri)
306 assert_data_from_proxy fetcher.fetch_path(@server_uri)
310 def test_explicit_proxy_with_user_auth
312 uri = URI.parse @proxy_uri
313 uri.user, uri.password = 'foo', 'bar'
314 fetcher = Gem::RemoteFetcher.new uri.to_s
315 proxy = fetcher.instance_variable_get("@proxy_uri")
316 assert_equal 'foo', proxy.user
317 assert_equal 'bar', proxy.password
318 assert_data_from_proxy fetcher.fetch_path(@server_uri)
322 uri = URI.parse @proxy_uri
323 uri.user, uri.password = 'domain%5Cuser', 'bar'
324 fetcher = Gem::RemoteFetcher.new uri.to_s
325 proxy = fetcher.instance_variable_get("@proxy_uri")
326 assert_equal 'domain\user', URI.unescape(proxy.user)
327 assert_equal 'bar', proxy.password
328 assert_data_from_proxy fetcher.fetch_path(@server_uri)
332 uri = URI.parse @proxy_uri
333 uri.user, uri.password = 'user', 'my%20pass'
334 fetcher = Gem::RemoteFetcher.new uri.to_s
335 proxy = fetcher.instance_variable_get("@proxy_uri")
336 assert_equal 'user', proxy.user
337 assert_equal 'my pass', URI.unescape(proxy.password)
338 assert_data_from_proxy fetcher.fetch_path(@server_uri)
342 def test_explicit_proxy_with_user_auth_in_env
344 ENV['http_proxy'] = @proxy_uri
345 ENV['http_proxy_user'] = 'foo'
346 ENV['http_proxy_pass'] = 'bar'
347 fetcher = Gem::RemoteFetcher.new nil
348 proxy = fetcher.instance_variable_get("@proxy_uri")
349 assert_equal 'foo', proxy.user
350 assert_equal 'bar', proxy.password
351 assert_data_from_proxy fetcher.fetch_path(@server_uri)
355 ENV['http_proxy'] = @proxy_uri
356 ENV['http_proxy_user'] = 'foo\user'
357 ENV['http_proxy_pass'] = 'my bar'
358 fetcher = Gem::RemoteFetcher.new nil
359 proxy = fetcher.instance_variable_get("@proxy_uri")
360 assert_equal 'foo\user', URI.unescape(proxy.user)
361 assert_equal 'my bar', URI.unescape(proxy.password)
362 assert_data_from_proxy fetcher.fetch_path(@server_uri)
366 def test_fetch_path_io_error
367 fetcher = Gem::RemoteFetcher.new nil
369 def fetcher.open_uri_or_path(uri) raise EOFError; end
371 e = assert_raise Gem::RemoteFetcher::FetchError do
372 fetcher.fetch_path 'uri'
375 assert_equal 'EOFError: EOFError (uri)', e.message
376 assert_equal 'uri', e.uri
379 def test_fetch_path_socket_error
380 fetcher = Gem::RemoteFetcher.new nil
382 def fetcher.open_uri_or_path(uri) raise SocketError; end
384 e = assert_raise Gem::RemoteFetcher::FetchError do
385 fetcher.fetch_path 'uri'
388 assert_equal 'SocketError: SocketError (uri)', e.message
389 assert_equal 'uri', e.uri
392 def test_fetch_path_system_call_error
393 fetcher = Gem::RemoteFetcher.new nil
395 def fetcher.open_uri_or_path(uri);
396 raise Errno::ECONNREFUSED, 'connect(2)'
399 e = assert_raise Gem::RemoteFetcher::FetchError do
400 fetcher.fetch_path 'uri'
403 assert_match %r|ECONNREFUSED:.*connect\(2\) \(uri\)\z|,
405 assert_equal 'uri', e.uri
408 def test_get_proxy_from_env_empty
409 orig_env_HTTP_PROXY = ENV['HTTP_PROXY']
410 orig_env_http_proxy = ENV['http_proxy']
412 ENV['HTTP_PROXY'] = ''
413 ENV.delete 'http_proxy'
415 fetcher = Gem::RemoteFetcher.new nil
417 assert_equal nil, fetcher.send(:get_proxy_from_env)
420 orig_env_HTTP_PROXY.nil? ? ENV.delete('HTTP_PROXY') :
421 ENV['HTTP_PROXY'] = orig_env_HTTP_PROXY
422 orig_env_http_proxy.nil? ? ENV.delete('http_proxy') :
423 ENV['http_proxy'] = orig_env_http_proxy
426 def test_implicit_no_proxy
428 ENV['http_proxy'] = 'http://fakeurl:12345'
429 fetcher = Gem::RemoteFetcher.new :no_proxy
430 assert_data_from_server fetcher.fetch_path(@server_uri)
434 def test_implicit_proxy
436 ENV['http_proxy'] = @proxy_uri
437 fetcher = Gem::RemoteFetcher.new nil
438 assert_data_from_proxy fetcher.fetch_path(@server_uri)
442 def test_implicit_upper_case_proxy
444 ENV['HTTP_PROXY'] = @proxy_uri
445 fetcher = Gem::RemoteFetcher.new nil
446 assert_data_from_proxy fetcher.fetch_path(@server_uri)
450 def test_implicit_proxy_no_env
452 fetcher = Gem::RemoteFetcher.new nil
453 assert_data_from_server fetcher.fetch_path(@server_uri)
457 def test_open_uri_or_path
458 fetcher = Gem::RemoteFetcher.new nil
461 def conn.started?() true end
462 def conn.request(req)
463 unless defined? @requested then
465 res = Net::HTTPRedirection.new nil, 301, nil
466 res.add_field 'Location', 'http://gems.example.com/real_path'
469 res = Net::HTTPOK.new nil, 200, nil
470 def res.body() 'real_path' end
475 conn = { 'gems.example.com:80' => conn }
476 fetcher.instance_variable_set :@connections, conn
478 fetcher.send :open_uri_or_path, 'http://gems.example.com/redirect' do |io|
479 assert_equal 'real_path', io.read
483 def test_open_uri_or_path_limited_redirects
484 fetcher = Gem::RemoteFetcher.new nil
487 def conn.started?() true end
488 def conn.request(req)
489 res = Net::HTTPRedirection.new nil, 301, nil
490 res.add_field 'Location', 'http://gems.example.com/redirect'
494 conn = { 'gems.example.com:80' => conn }
495 fetcher.instance_variable_set :@connections, conn
497 e = assert_raise Gem::RemoteFetcher::FetchError do
498 fetcher.send :open_uri_or_path, 'http://gems.example.com/redirect'
501 assert_equal 'too many redirects (http://gems.example.com/redirect)',
507 self.class.enable_zip = true
508 fetcher = Gem::RemoteFetcher.new nil
509 assert_equal SERVER_DATA.size, fetcher.fetch_size(@server_uri), "probably not from proxy"
510 zip_data = fetcher.fetch_path(@server_z_uri)
511 assert zip_data.size < SERVER_DATA.size, "Zipped data should be smaller"
517 self.class.enable_zip = false
518 fetcher = Gem::RemoteFetcher.new nil
519 assert_error { fetcher.fetch_path(@server_z_uri) }
523 def test_yaml_error_on_size
525 self.class.enable_yaml = false
526 fetcher = Gem::RemoteFetcher.new nil
527 assert_error { fetcher.size }
533 def assert_error(exception_class=Exception)
534 got_exception = false
537 rescue exception_class => ex
540 assert got_exception, "Expected exception conforming to #{exception_class}"
543 def assert_data_from_server(data)
544 assert_block("Data is not from server") { data =~ /0\.4\.11/ }
547 def assert_data_from_proxy(data)
548 assert_block("Data is not from proxy") { data =~ /0\.4\.2/ }
551 class NilLog < WEBrick::Log
552 def log(level, data) #Do nothing
557 attr_reader :normal_server, :proxy_server
558 attr_accessor :enable_zip, :enable_yaml
561 @normal_server ||= start_server(SERVER_PORT, SERVER_DATA)
562 @proxy_server ||= start_server(PROXY_PORT, PROXY_DATA)
569 def start_server(port, data)
572 null_logger = NilLog.new
573 s = WEBrick::HTTPServer.new(
575 :DocumentRoot => nil,
576 :Logger => null_logger,
577 :AccessLog => null_logger
579 s.mount_proc("/kill") { |req, res| s.shutdown }
580 s.mount_proc("/yaml") { |req, res|
583 res['Content-Type'] = 'text/plain'
584 res['content-length'] = data.size
587 res.body = "<h1>NOT FOUND</h1>"
588 res['Content-Type'] = 'text/html'
591 s.mount_proc("/yaml.Z") { |req, res|
593 res.body = Zlib::Deflate.deflate(data)
594 res['Content-Type'] = 'text/plain'
597 res.body = "<h1>NOT FOUND</h1>"
598 res['Content-Type'] = 'text/html'
602 rescue Exception => ex
604 puts "ERROR during server thread: #{ex.message}"
607 sleep 0.2 # Give the servers time to startup