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.instance_variable_set :@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.connect_to(host, port)
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\tgetting size of #{uri}", e.message
161 fetcher = Gem::RemoteFetcher.new nil
162 assert_data_from_server fetcher.fetch_path(@server_uri)
163 assert_equal SERVER_DATA.size, fetcher.fetch_size(@server_uri)
167 def util_fuck_with_fetcher data, blow = false
168 fetcher = Gem::RemoteFetcher.fetcher
169 fetcher.instance_variable_set :@test_data, data
172 def fetcher.fetch_path arg
177 def fetcher.fetch_path arg
178 # OMG I'm such an ass
179 class << self; remove_method :fetch_path; end
180 def self.fetch_path arg
185 raise Gem::RemoteFetcher::FetchError, "haha!"
194 File.open @a1_gem, 'rb' do |fp|
198 fetcher = util_fuck_with_fetcher a1_data
200 a1_cache_gem = File.join(@gemhome, 'cache', "#{@a1.full_name}.gem")
201 assert_equal a1_cache_gem, fetcher.download(@a1, 'http://gems.example.com')
202 assert_equal("http://gems.example.com/gems/a-1.gem",
203 fetcher.instance_variable_get(:@test_arg).to_s)
204 assert File.exist?(a1_cache_gem)
207 def test_download_cached
208 FileUtils.mv @a1_gem, @cache_dir
210 inst = Gem::RemoteFetcher.fetcher
212 assert_equal File.join(@gemhome, 'cache', "#{@a1.full_name}.gem"),
213 inst.download(@a1, 'http://gems.example.com')
216 def test_download_local
217 FileUtils.mv @a1_gem, @tempdir
218 local_path = File.join @tempdir, "#{@a1.full_name}.gem"
221 Dir.chdir @tempdir do
222 inst = Gem::RemoteFetcher.fetcher
225 assert_equal File.join(@gemhome, 'cache', "#{@a1.full_name}.gem"),
226 inst.download(@a1, local_path)
229 def test_download_install_dir
231 File.open @a1_gem, 'rb' do |fp|
235 fetcher = util_fuck_with_fetcher a1_data
237 install_dir = File.join @tempdir, 'more_gems'
239 a1_cache_gem = File.join install_dir, 'cache', "#{@a1.full_name}.gem"
240 actual = fetcher.download(@a1, 'http://gems.example.com', install_dir)
242 assert_equal a1_cache_gem, actual
243 assert_equal("http://gems.example.com/gems/a-1.gem",
244 fetcher.instance_variable_get(:@test_arg).to_s)
246 assert File.exist?(a1_cache_gem)
249 unless win_platform? then # File.chmod doesn't work
250 def test_download_local_read_only
251 FileUtils.mv @a1_gem, @tempdir
252 local_path = File.join @tempdir, "#{@a1.full_name}.gem"
254 File.chmod 0555, File.join(@gemhome, 'cache')
256 Dir.chdir @tempdir do
257 inst = Gem::RemoteFetcher.fetcher
260 assert_equal File.join(@tempdir, "#{@a1.full_name}.gem"),
261 inst.download(@a1, local_path)
263 File.chmod 0755, File.join(@gemhome, 'cache')
267 def test_download_platform_legacy
268 original_platform = 'old-platform'
270 e1, e1_gem = util_gem 'e', '1' do |s|
271 s.platform = Gem::Platform::CURRENT
272 s.instance_variable_set :@original_platform, original_platform
276 File.open e1_gem, 'rb' do |fp|
280 fetcher = util_fuck_with_fetcher e1_data, :blow_chunks
282 e1_cache_gem = File.join(@gemhome, 'cache', "#{e1.full_name}.gem")
284 assert_equal e1_cache_gem, fetcher.download(e1, 'http://gems.example.com')
286 assert_equal("http://gems.example.com/gems/#{e1.original_name}.gem",
287 fetcher.instance_variable_get(:@test_arg).to_s)
288 assert File.exist?(e1_cache_gem)
291 def test_download_unsupported
292 inst = Gem::RemoteFetcher.fetcher
294 e = assert_raise Gem::InstallError do
295 inst.download @a1, 'ftp://gems.rubyforge.org'
298 assert_equal 'unsupported URI scheme ftp', e.message
301 def test_explicit_proxy
303 fetcher = Gem::RemoteFetcher.new @proxy_uri
304 assert_equal PROXY_DATA.size, fetcher.fetch_size(@server_uri)
305 assert_data_from_proxy fetcher.fetch_path(@server_uri)
309 def test_explicit_proxy_with_user_auth
311 uri = URI.parse @proxy_uri
312 uri.user, uri.password = 'foo', 'bar'
313 fetcher = Gem::RemoteFetcher.new uri.to_s
314 proxy = fetcher.instance_variable_get("@proxy_uri")
315 assert_equal 'foo', proxy.user
316 assert_equal 'bar', proxy.password
317 assert_data_from_proxy fetcher.fetch_path(@server_uri)
321 uri = URI.parse @proxy_uri
322 uri.user, uri.password = 'domain%5Cuser', 'bar'
323 fetcher = Gem::RemoteFetcher.new uri.to_s
324 proxy = fetcher.instance_variable_get("@proxy_uri")
325 assert_equal 'domain\user', URI.unescape(proxy.user)
326 assert_equal 'bar', proxy.password
327 assert_data_from_proxy fetcher.fetch_path(@server_uri)
331 uri = URI.parse @proxy_uri
332 uri.user, uri.password = 'user', 'my%20pass'
333 fetcher = Gem::RemoteFetcher.new uri.to_s
334 proxy = fetcher.instance_variable_get("@proxy_uri")
335 assert_equal 'user', proxy.user
336 assert_equal 'my pass', URI.unescape(proxy.password)
337 assert_data_from_proxy fetcher.fetch_path(@server_uri)
341 def test_explicit_proxy_with_user_auth_in_env
343 ENV['http_proxy'] = @proxy_uri
344 ENV['http_proxy_user'] = 'foo'
345 ENV['http_proxy_pass'] = 'bar'
346 fetcher = Gem::RemoteFetcher.new nil
347 proxy = fetcher.instance_variable_get("@proxy_uri")
348 assert_equal 'foo', proxy.user
349 assert_equal 'bar', proxy.password
350 assert_data_from_proxy fetcher.fetch_path(@server_uri)
354 ENV['http_proxy'] = @proxy_uri
355 ENV['http_proxy_user'] = 'foo\user'
356 ENV['http_proxy_pass'] = 'my bar'
357 fetcher = Gem::RemoteFetcher.new nil
358 proxy = fetcher.instance_variable_get("@proxy_uri")
359 assert_equal 'foo\user', URI.unescape(proxy.user)
360 assert_equal 'my bar', URI.unescape(proxy.password)
361 assert_data_from_proxy fetcher.fetch_path(@server_uri)
365 def test_fetch_path_io_error
366 fetcher = Gem::RemoteFetcher.new nil
368 def fetcher.open_uri_or_path(uri) raise EOFError; end
370 e = assert_raise Gem::RemoteFetcher::FetchError do
371 fetcher.fetch_path 'uri'
374 assert_equal 'EOFError: EOFError reading uri', e.message
377 def test_fetch_path_socket_error
378 fetcher = Gem::RemoteFetcher.new nil
380 def fetcher.open_uri_or_path(uri) raise SocketError; end
382 e = assert_raise Gem::RemoteFetcher::FetchError do
383 fetcher.fetch_path 'uri'
386 assert_equal 'SocketError: SocketError reading uri', e.message
389 def test_fetch_path_system_call_error
390 fetcher = Gem::RemoteFetcher.new nil
392 def fetcher.open_uri_or_path(uri);
393 raise Errno::ECONNREFUSED, 'connect(2)'
396 e = assert_raise Gem::RemoteFetcher::FetchError do
397 fetcher.fetch_path 'uri'
400 assert_match %r|ECONNREFUSED:.*connect\(2\) reading uri\z|,
404 def test_get_proxy_from_env_empty
405 orig_env_HTTP_PROXY = ENV['HTTP_PROXY']
406 orig_env_http_proxy = ENV['http_proxy']
408 ENV['HTTP_PROXY'] = ''
409 ENV.delete 'http_proxy'
411 fetcher = Gem::RemoteFetcher.new nil
413 assert_equal nil, fetcher.send(:get_proxy_from_env)
416 orig_env_HTTP_PROXY.nil? ? ENV.delete('HTTP_PROXY') :
417 ENV['HTTP_PROXY'] = orig_env_HTTP_PROXY
418 orig_env_http_proxy.nil? ? ENV.delete('http_proxy') :
419 ENV['http_proxy'] = orig_env_http_proxy
422 def test_implicit_no_proxy
424 ENV['http_proxy'] = 'http://fakeurl:12345'
425 fetcher = Gem::RemoteFetcher.new :no_proxy
426 assert_data_from_server fetcher.fetch_path(@server_uri)
430 def test_implicit_proxy
432 ENV['http_proxy'] = @proxy_uri
433 fetcher = Gem::RemoteFetcher.new nil
434 assert_data_from_proxy fetcher.fetch_path(@server_uri)
438 def test_implicit_upper_case_proxy
440 ENV['HTTP_PROXY'] = @proxy_uri
441 fetcher = Gem::RemoteFetcher.new nil
442 assert_data_from_proxy fetcher.fetch_path(@server_uri)
446 def test_implicit_proxy_no_env
448 fetcher = Gem::RemoteFetcher.new nil
449 assert_data_from_server fetcher.fetch_path(@server_uri)
453 def test_open_uri_or_path
454 fetcher = Gem::RemoteFetcher.new nil
457 def conn.started?() true end
458 def conn.request(req)
459 unless defined? @requested then
461 res = Net::HTTPRedirection.new nil, 301, nil
462 res.add_field 'Location', 'http://gems.example.com/real_path'
465 res = Net::HTTPOK.new nil, 200, nil
466 def res.body() 'real_path' end
471 conn = { 'gems.example.com:80' => conn }
472 fetcher.instance_variable_set :@connections, conn
474 fetcher.send :open_uri_or_path, 'http://gems.example.com/redirect' do |io|
475 assert_equal 'real_path', io.read
479 def test_open_uri_or_path_limited_redirects
480 fetcher = Gem::RemoteFetcher.new nil
483 def conn.started?() true end
484 def conn.request(req)
485 res = Net::HTTPRedirection.new nil, 301, nil
486 res.add_field 'Location', 'http://gems.example.com/redirect'
490 conn = { 'gems.example.com:80' => conn }
491 fetcher.instance_variable_set :@connections, conn
493 e = assert_raise Gem::RemoteFetcher::FetchError do
494 fetcher.send :open_uri_or_path, 'http://gems.example.com/redirect'
497 assert_equal 'too many redirects', e.message
502 self.class.enable_zip = true
503 fetcher = Gem::RemoteFetcher.new nil
504 assert_equal SERVER_DATA.size, fetcher.fetch_size(@server_uri), "probably not from proxy"
505 zip_data = fetcher.fetch_path(@server_z_uri)
506 assert zip_data.size < SERVER_DATA.size, "Zipped data should be smaller"
512 self.class.enable_zip = false
513 fetcher = Gem::RemoteFetcher.new nil
514 assert_error { fetcher.fetch_path(@server_z_uri) }
518 def test_yaml_error_on_size
520 self.class.enable_yaml = false
521 fetcher = Gem::RemoteFetcher.new nil
522 assert_error { fetcher.size }
528 def assert_error(exception_class=Exception)
529 got_exception = false
532 rescue exception_class => ex
535 assert got_exception, "Expected exception conforming to #{exception_class}"
538 def assert_data_from_server(data)
539 assert_block("Data is not from server") { data =~ /0\.4\.11/ }
542 def assert_data_from_proxy(data)
543 assert_block("Data is not from proxy") { data =~ /0\.4\.2/ }
546 class NilLog < WEBrick::Log
547 def log(level, data) #Do nothing
552 attr_reader :normal_server, :proxy_server
553 attr_accessor :enable_zip, :enable_yaml
556 @normal_server ||= start_server(SERVER_PORT, SERVER_DATA)
557 @proxy_server ||= start_server(PROXY_PORT, PROXY_DATA)
564 def start_server(port, data)
567 null_logger = NilLog.new
568 s = WEBrick::HTTPServer.new(
570 :DocumentRoot => nil,
571 :Logger => null_logger,
572 :AccessLog => null_logger
574 s.mount_proc("/kill") { |req, res| s.shutdown }
575 s.mount_proc("/yaml") { |req, res|
578 res['Content-Type'] = 'text/plain'
579 res['content-length'] = data.size
582 res.body = "<h1>NOT FOUND</h1>"
583 res['Content-Type'] = 'text/html'
586 s.mount_proc("/yaml.Z") { |req, res|
588 res.body = Zlib::Deflate.deflate(data)
589 res['Content-Type'] = 'text/plain'
592 res.body = "<h1>NOT FOUND</h1>"
593 res['Content-Type'] = 'text/html'
597 rescue Exception => ex
599 puts "ERROR during server thread: #{ex.message}"
602 sleep 0.2 # Give the servers time to startup