1require 'rubygems/test_case' 2require 'ostruct' 3require 'webrick' 4require 'webrick/https' 5require 'rubygems/remote_fetcher' 6require 'rubygems/package' 7require 'minitest/mock' 8 9# = Testing Proxy Settings 10# 11# These tests check the proper proxy server settings by running two 12# web servers. The web server at http://localhost:#{SERVER_PORT} 13# represents the normal gem server and returns a gemspec with a rake 14# version of 0.4.11. The web server at http://localhost:#{PROXY_PORT} 15# represents the proxy server and returns a different dataset where 16# rake has version 0.4.2. This allows us to detect which server is 17# returning the data. 18# 19# Note that the proxy server is not a *real* proxy server. But our 20# software doesn't really care, as long as we hit the proxy URL when a 21# proxy is configured. 22 23class TestGemRemoteFetcher < Gem::TestCase 24 25 include Gem::DefaultUserInteraction 26 27 SERVER_DATA = <<-EOY 28--- !ruby/object:Gem::Cache 29gems: 30 rake-0.4.11: !ruby/object:Gem::Specification 31 rubygems_version: "0.7" 32 specification_version: 1 33 name: rake 34 version: !ruby/object:Gem::Version 35 version: 0.4.11 36 date: 2004-11-12 37 summary: Ruby based make-like utility. 38 require_paths: 39 - lib 40 author: Jim Weirich 41 email: jim@weirichhouse.org 42 homepage: http://rake.rubyforge.org 43 rubyforge_project: rake 44 description: Rake is a Make-like program implemented in Ruby. Tasks and dependencies are specified in standard Ruby syntax. 45 autorequire: 46 default_executable: rake 47 bindir: bin 48 has_rdoc: true 49 required_ruby_version: !ruby/object:Gem::Version::Requirement 50 requirements: 51 - 52 - ">" 53 - !ruby/object:Gem::Version 54 version: 0.0.0 55 version: 56 platform: ruby 57 files: 58 - README 59 test_files: [] 60 library_stubs: 61 rdoc_options: 62 extra_rdoc_files: 63 executables: 64 - rake 65 extensions: [] 66 requirements: [] 67 dependencies: [] 68 EOY 69 70 PROXY_DATA = SERVER_DATA.gsub(/0.4.11/, '0.4.2') 71 72 DIR = File.expand_path(File.dirname(__FILE__)) 73 74 def setup 75 @proxies = %w[http_proxy HTTP_PROXY http_proxy_user HTTP_PROXY_USER http_proxy_pass HTTP_PROXY_PASS no_proxy NO_PROXY] 76 @old_proxies = @proxies.map {|k| ENV[k] } 77 @proxies.each {|k| ENV[k] = nil } 78 79 super 80 self.class.start_servers 81 self.class.enable_yaml = true 82 self.class.enable_zip = false 83 84 base_server_uri = "http://localhost:#{self.class.normal_server_port}" 85 @proxy_uri = "http://localhost:#{self.class.proxy_server_port}" 86 87 @server_uri = base_server_uri + "/yaml" 88 @server_z_uri = base_server_uri + "/yaml.Z" 89 90 # REFACTOR: copied from test_gem_dependency_installer.rb 91 @gems_dir = File.join @tempdir, 'gems' 92 @cache_dir = File.join @gemhome, "cache" 93 FileUtils.mkdir @gems_dir 94 95 # TODO: why does the remote fetcher need it written to disk? 96 @a1, @a1_gem = util_gem 'a', '1' do |s| s.executables << 'a_bin' end 97 @a1.loaded_from = File.join(@gemhome, 'specifications', @a1.full_name) 98 99 Gem::RemoteFetcher.fetcher = nil 100 101 @fetcher = Gem::RemoteFetcher.fetcher 102 end 103 104 def teardown 105 super 106 Gem.configuration[:http_proxy] = nil 107 @proxies.each_with_index {|k, i| ENV[k] = @old_proxies[i] } 108 end 109 110 def test_self_fetcher 111 fetcher = Gem::RemoteFetcher.fetcher 112 refute_nil fetcher 113 assert_kind_of Gem::RemoteFetcher, fetcher 114 end 115 116 def test_self_fetcher_with_proxy 117 proxy_uri = 'http://proxy.example.com' 118 Gem.configuration[:http_proxy] = proxy_uri 119 Gem::RemoteFetcher.fetcher = nil 120 121 fetcher = Gem::RemoteFetcher.fetcher 122 123 refute_nil fetcher 124 assert_kind_of Gem::RemoteFetcher, fetcher 125 assert_equal proxy_uri, fetcher.instance_variable_get(:@proxy_uri).to_s 126 end 127 128 def test_self_fetcher_with_proxy_URI 129 proxy_uri = URI.parse 'http://proxy.example.com' 130 Gem.configuration[:http_proxy] = proxy_uri 131 Gem::RemoteFetcher.fetcher = nil 132 133 fetcher = Gem::RemoteFetcher.fetcher 134 refute_nil fetcher 135 136 assert_kind_of Gem::RemoteFetcher, fetcher 137 assert_equal proxy_uri, fetcher.instance_variable_get(:@proxy_uri) 138 end 139 140 def test_escape_auth_info 141 assert_equal 'a%40b%5Cc', @fetcher.escape_auth_info('a@b\c') 142 end 143 144 def test_unescape_auth_info 145 assert_equal 'a@b\c', @fetcher.unescape_auth_info('a%40b%5Cc') 146 end 147 148 def test_fetch_size_bad_uri 149 fetcher = Gem::RemoteFetcher.new nil 150 151 e = assert_raises ArgumentError do 152 fetcher.fetch_size 'gems.example.com/yaml' 153 end 154 155 assert_equal 'uri scheme is invalid: nil', e.message 156 end 157 158 def test_fetch_size_socket_error 159 fetcher = Gem::RemoteFetcher.new nil 160 def fetcher.connection_for(uri) 161 raise SocketError, "tarded" 162 end 163 164 uri = 'http://gems.example.com/yaml' 165 e = assert_raises Gem::RemoteFetcher::FetchError do 166 fetcher.fetch_size uri 167 end 168 169 assert_equal "SocketError: tarded (#{uri})", e.message 170 end 171 172 def test_no_proxy 173 use_ui @ui do 174 assert_data_from_server @fetcher.fetch_path(@server_uri) 175 assert_equal SERVER_DATA.size, @fetcher.fetch_size(@server_uri) 176 end 177 end 178 179 def test_api_endpoint 180 uri = URI.parse "http://gems.example.com/foo" 181 target = MiniTest::Mock.new 182 target.expect :target, "http://blah.com" 183 184 dns = MiniTest::Mock.new 185 dns.expect :getresource, target, [String, Object] 186 187 fetch = Gem::RemoteFetcher.new nil, dns 188 assert_equal URI.parse("http://blah.com/foo"), fetch.api_endpoint(uri) 189 190 target.verify 191 dns.verify 192 end 193 194 def test_cache_update_path 195 uri = URI 'http://example/file' 196 path = File.join @tempdir, 'file' 197 198 fetcher = util_fuck_with_fetcher 'hello' 199 200 data = fetcher.cache_update_path uri, path 201 202 assert_equal 'hello', data 203 204 assert_equal 'hello', File.read(path) 205 end 206 207 def test_cache_update_path_no_update 208 uri = URI 'http://example/file' 209 path = File.join @tempdir, 'file' 210 211 fetcher = util_fuck_with_fetcher 'hello' 212 213 data = fetcher.cache_update_path uri, path, false 214 215 assert_equal 'hello', data 216 217 refute_path_exists path 218 end 219 220 def util_fuck_with_fetcher data, blow = false 221 fetcher = Gem::RemoteFetcher.fetcher 222 fetcher.instance_variable_set :@test_data, data 223 224 unless blow then 225 def fetcher.fetch_path arg 226 @test_arg = arg 227 @test_data 228 end 229 else 230 def fetcher.fetch_path arg 231 # OMG I'm such an ass 232 class << self; remove_method :fetch_path; end 233 def self.fetch_path arg 234 @test_arg = arg 235 @test_data 236 end 237 238 raise Gem::RemoteFetcher::FetchError.new("haha!", nil) 239 end 240 end 241 242 fetcher 243 end 244 245 def test_download 246 a1_data = nil 247 File.open @a1_gem, 'rb' do |fp| 248 a1_data = fp.read 249 end 250 251 fetcher = util_fuck_with_fetcher a1_data 252 253 a1_cache_gem = @a1.cache_file 254 assert_equal a1_cache_gem, fetcher.download(@a1, 'http://gems.example.com') 255 assert_equal("http://gems.example.com/gems/a-1.gem", 256 fetcher.instance_variable_get(:@test_arg).to_s) 257 assert File.exist?(a1_cache_gem) 258 end 259 260 def test_download_cached 261 FileUtils.mv @a1_gem, @cache_dir 262 263 inst = Gem::RemoteFetcher.fetcher 264 265 assert_equal @a1.cache_file, inst.download(@a1, 'http://gems.example.com') 266 end 267 268 def test_download_local 269 FileUtils.mv @a1_gem, @tempdir 270 local_path = File.join @tempdir, @a1.file_name 271 inst = nil 272 273 Dir.chdir @tempdir do 274 inst = Gem::RemoteFetcher.fetcher 275 end 276 277 assert_equal @a1.cache_file, inst.download(@a1, local_path) 278 end 279 280 def test_download_local_space 281 space_path = File.join @tempdir, 'space path' 282 FileUtils.mkdir space_path 283 FileUtils.mv @a1_gem, space_path 284 local_path = File.join space_path, @a1.file_name 285 inst = nil 286 287 Dir.chdir @tempdir do 288 inst = Gem::RemoteFetcher.fetcher 289 end 290 291 assert_equal @a1.cache_file, inst.download(@a1, local_path) 292 end 293 294 def test_download_install_dir 295 a1_data = File.open @a1_gem, 'rb' do |fp| 296 fp.read 297 end 298 299 fetcher = util_fuck_with_fetcher a1_data 300 301 install_dir = File.join @tempdir, 'more_gems' 302 303 a1_cache_gem = File.join install_dir, "cache", @a1.file_name 304 FileUtils.mkdir_p(File.dirname(a1_cache_gem)) 305 actual = fetcher.download(@a1, 'http://gems.example.com', install_dir) 306 307 assert_equal a1_cache_gem, actual 308 assert_equal("http://gems.example.com/gems/a-1.gem", 309 fetcher.instance_variable_get(:@test_arg).to_s) 310 311 assert File.exist?(a1_cache_gem) 312 end 313 314 unless win_platform? # File.chmod doesn't work 315 def test_download_local_read_only 316 FileUtils.mv @a1_gem, @tempdir 317 local_path = File.join @tempdir, @a1.file_name 318 inst = nil 319 FileUtils.chmod 0555, @a1.cache_dir 320 321 Dir.chdir @tempdir do 322 inst = Gem::RemoteFetcher.fetcher 323 end 324 325 assert_equal(File.join(@tempdir, @a1.file_name), 326 inst.download(@a1, local_path)) 327 ensure 328 FileUtils.chmod 0755, @a1.cache_dir 329 end 330 331 def test_download_read_only 332 FileUtils.chmod 0555, @a1.cache_dir 333 FileUtils.chmod 0555, @gemhome 334 335 fetcher = util_fuck_with_fetcher File.read(@a1_gem) 336 fetcher.download(@a1, 'http://gems.example.com') 337 a1_cache_gem = File.join Gem.user_dir, "cache", @a1.file_name 338 assert File.exist? a1_cache_gem 339 ensure 340 FileUtils.chmod 0755, @gemhome 341 FileUtils.chmod 0755, @a1.cache_dir 342 end 343 end 344 345 def test_download_platform_legacy 346 original_platform = 'old-platform' 347 348 e1, e1_gem = util_gem 'e', '1' do |s| 349 s.platform = Gem::Platform::CURRENT 350 s.instance_variable_set :@original_platform, original_platform 351 end 352 e1.loaded_from = File.join(@gemhome, 'specifications', e1.full_name) 353 354 e1_data = nil 355 File.open e1_gem, 'rb' do |fp| 356 e1_data = fp.read 357 end 358 359 fetcher = util_fuck_with_fetcher e1_data, :blow_chunks 360 361 e1_cache_gem = e1.cache_file 362 363 assert_equal e1_cache_gem, fetcher.download(e1, 'http://gems.example.com') 364 365 assert_equal("http://gems.example.com/gems/#{e1.original_name}.gem", 366 fetcher.instance_variable_get(:@test_arg).to_s) 367 assert File.exist?(e1_cache_gem) 368 end 369 370 def test_download_same_file 371 FileUtils.mv @a1_gem, @tempdir 372 local_path = File.join @tempdir, @a1.file_name 373 inst = nil 374 375 Dir.chdir @tempdir do 376 inst = Gem::RemoteFetcher.fetcher 377 end 378 379 cache_path = @a1.cache_file 380 FileUtils.mv local_path, cache_path 381 382 gem = Gem::Package.new cache_path 383 384 assert_equal cache_path, inst.download(gem.spec, cache_path) 385 end 386 387 def test_download_unsupported 388 inst = Gem::RemoteFetcher.fetcher 389 390 e = assert_raises ArgumentError do 391 inst.download @a1, 'ftp://gems.rubyforge.org' 392 end 393 394 assert_equal 'unsupported URI scheme ftp', e.message 395 end 396 397 def test_download_to_cache 398 @a2, @a2_gem = util_gem 'a', '2' 399 400 util_setup_spec_fetcher @a1, @a2 401 @fetcher.instance_variable_set :@a1, @a1 402 @fetcher.instance_variable_set :@a2, @a2 403 def @fetcher.fetch_path uri, mtime = nil, head = false 404 case uri.request_uri 405 when /#{@a1.spec_name}/ then 406 Gem.deflate Marshal.dump @a1 407 when /#{@a2.spec_name}/ then 408 Gem.deflate Marshal.dump @a2 409 else 410 uri.to_s 411 end 412 end 413 414 gem = Gem::RemoteFetcher.fetcher.download_to_cache dep 'a' 415 416 assert_equal @a2.file_name, File.basename(gem) 417 end 418 419 def test_explicit_proxy 420 use_ui @ui do 421 fetcher = Gem::RemoteFetcher.new @proxy_uri 422 assert_equal PROXY_DATA.size, fetcher.fetch_size(@server_uri) 423 assert_data_from_proxy fetcher.fetch_path(@server_uri) 424 end 425 end 426 427 def test_explicit_proxy_with_user_auth 428 use_ui @ui do 429 uri = URI.parse @proxy_uri 430 uri.user, uri.password = 'foo', 'bar' 431 fetcher = Gem::RemoteFetcher.new uri.to_s 432 proxy = fetcher.instance_variable_get("@proxy_uri") 433 assert_equal 'foo', proxy.user 434 assert_equal 'bar', proxy.password 435 assert_data_from_proxy fetcher.fetch_path(@server_uri) 436 end 437 438 use_ui @ui do 439 uri = URI.parse @proxy_uri 440 uri.user, uri.password = 'domain%5Cuser', 'bar' 441 fetcher = Gem::RemoteFetcher.new uri.to_s 442 proxy = fetcher.instance_variable_get("@proxy_uri") 443 assert_equal 'domain\user', fetcher.unescape_auth_info(proxy.user) 444 assert_equal 'bar', proxy.password 445 assert_data_from_proxy fetcher.fetch_path(@server_uri) 446 end 447 448 use_ui @ui do 449 uri = URI.parse @proxy_uri 450 uri.user, uri.password = 'user', 'my%20pass' 451 fetcher = Gem::RemoteFetcher.new uri.to_s 452 proxy = fetcher.instance_variable_get("@proxy_uri") 453 assert_equal 'user', proxy.user 454 assert_equal 'my pass', fetcher.unescape_auth_info(proxy.password) 455 assert_data_from_proxy fetcher.fetch_path(@server_uri) 456 end 457 end 458 459 def test_explicit_proxy_with_user_auth_in_env 460 use_ui @ui do 461 ENV['http_proxy'] = @proxy_uri 462 ENV['http_proxy_user'] = 'foo' 463 ENV['http_proxy_pass'] = 'bar' 464 fetcher = Gem::RemoteFetcher.new nil 465 proxy = fetcher.instance_variable_get("@proxy_uri") 466 assert_equal 'foo', proxy.user 467 assert_equal 'bar', proxy.password 468 assert_data_from_proxy fetcher.fetch_path(@server_uri) 469 end 470 471 use_ui @ui do 472 ENV['http_proxy'] = @proxy_uri 473 ENV['http_proxy_user'] = 'foo\user' 474 ENV['http_proxy_pass'] = 'my bar' 475 fetcher = Gem::RemoteFetcher.new nil 476 proxy = fetcher.instance_variable_get("@proxy_uri") 477 assert_equal 'foo\user', fetcher.unescape_auth_info(proxy.user) 478 assert_equal 'my bar', fetcher.unescape_auth_info(proxy.password) 479 assert_data_from_proxy fetcher.fetch_path(@server_uri) 480 end 481 482 use_ui @ui do 483 ENV['http_proxy'] = @proxy_uri 484 ENV['http_proxy_user'] = 'foo@user' 485 ENV['http_proxy_pass'] = 'my@bar' 486 fetcher = Gem::RemoteFetcher.new nil 487 proxy = fetcher.instance_variable_get("@proxy_uri") 488 assert_equal 'foo@user', fetcher.unescape_auth_info(proxy.user) 489 assert_equal 'my@bar', fetcher.unescape_auth_info(proxy.password) 490 assert_data_from_proxy fetcher.fetch_path(@server_uri) 491 end 492 end 493 494 def test_fetch_path_gzip 495 fetcher = Gem::RemoteFetcher.new nil 496 497 def fetcher.fetch_http(uri, mtime, head = nil) 498 Gem.gzip 'foo' 499 end 500 501 assert_equal 'foo', fetcher.fetch_path(@uri + 'foo.gz') 502 end 503 504 def test_fetch_path_gzip_unmodified 505 fetcher = Gem::RemoteFetcher.new nil 506 507 def fetcher.fetch_http(uri, mtime, head = nil) 508 nil 509 end 510 511 assert_equal nil, fetcher.fetch_path(@uri + 'foo.gz', Time.at(0)) 512 end 513 514 def test_fetch_path_io_error 515 fetcher = Gem::RemoteFetcher.new nil 516 517 def fetcher.fetch_http(*) 518 raise EOFError 519 end 520 521 url = 'http://example.com/uri' 522 523 e = assert_raises Gem::RemoteFetcher::FetchError do 524 fetcher.fetch_path url 525 end 526 527 assert_equal "EOFError: EOFError (#{url})", e.message 528 assert_equal url, e.uri 529 end 530 531 def test_fetch_path_socket_error 532 fetcher = Gem::RemoteFetcher.new nil 533 534 def fetcher.fetch_http(uri, mtime, head = nil) 535 raise SocketError 536 end 537 538 url = 'http://example.com/uri' 539 540 e = assert_raises Gem::RemoteFetcher::FetchError do 541 fetcher.fetch_path url 542 end 543 544 assert_equal "SocketError: SocketError (#{url})", e.message 545 assert_equal url, e.uri 546 end 547 548 def test_fetch_path_system_call_error 549 fetcher = Gem::RemoteFetcher.new nil 550 551 def fetcher.fetch_http(uri, mtime = nil, head = nil) 552 raise Errno::ECONNREFUSED, 'connect(2)' 553 end 554 555 url = 'http://example.com/uri' 556 557 e = assert_raises Gem::RemoteFetcher::FetchError do 558 fetcher.fetch_path url 559 end 560 561 assert_match %r|ECONNREFUSED:.*connect\(2\) \(#{Regexp.escape url}\)\z|, 562 e.message 563 assert_equal url, e.uri 564 end 565 566 def test_fetch_path_unmodified 567 fetcher = Gem::RemoteFetcher.new nil 568 569 def fetcher.fetch_http(uri, mtime, head = nil) 570 nil 571 end 572 573 assert_equal nil, fetcher.fetch_path(URI.parse(@gem_repo), Time.at(0)) 574 end 575 576 def test_get_proxy_from_env_auto_normalizes 577 fetcher = Gem::RemoteFetcher.new(nil) 578 ENV['HTTP_PROXY'] = 'fakeurl:12345' 579 580 assert_equal('http://fakeurl:12345', fetcher.get_proxy_from_env.to_s) 581 end 582 583 def test_get_proxy_from_env_empty 584 ENV['HTTP_PROXY'] = '' 585 ENV.delete 'http_proxy' 586 587 fetcher = Gem::RemoteFetcher.new nil 588 589 assert_equal nil, fetcher.send(:get_proxy_from_env) 590 end 591 592 def test_implicit_no_proxy 593 use_ui @ui do 594 ENV['http_proxy'] = 'http://fakeurl:12345' 595 fetcher = Gem::RemoteFetcher.new :no_proxy 596 assert_data_from_server fetcher.fetch_path(@server_uri) 597 end 598 end 599 600 def test_implicit_proxy 601 use_ui @ui do 602 ENV['http_proxy'] = @proxy_uri 603 fetcher = Gem::RemoteFetcher.new nil 604 assert_data_from_proxy fetcher.fetch_path(@server_uri) 605 end 606 end 607 608 def test_implicit_upper_case_proxy 609 use_ui @ui do 610 ENV['HTTP_PROXY'] = @proxy_uri 611 fetcher = Gem::RemoteFetcher.new nil 612 assert_data_from_proxy fetcher.fetch_path(@server_uri) 613 end 614 end 615 616 def test_implicit_proxy_no_env 617 use_ui @ui do 618 fetcher = Gem::RemoteFetcher.new nil 619 assert_data_from_server fetcher.fetch_path(@server_uri) 620 end 621 end 622 623 def test_fetch_http 624 fetcher = Gem::RemoteFetcher.new nil 625 url = 'http://gems.example.com/redirect' 626 627 conn = Object.new 628 def conn.started?() true end 629 def conn.request(req) 630 url = 'http://gems.example.com/redirect' 631 unless defined? @requested then 632 @requested = true 633 res = Net::HTTPMovedPermanently.new nil, 301, nil 634 res.add_field 'Location', url 635 res 636 else 637 res = Net::HTTPOK.new nil, 200, nil 638 def res.body() 'real_path' end 639 res 640 end 641 end 642 643 conn = { "#{Thread.current.object_id}:gems.example.com:80" => conn } 644 fetcher.instance_variable_set :@connections, conn 645 646 data = fetcher.fetch_http URI.parse(url) 647 648 assert_equal 'real_path', data 649 end 650 651 def test_fetch_http_redirects 652 fetcher = Gem::RemoteFetcher.new nil 653 url = 'http://gems.example.com/redirect' 654 655 conn = Object.new 656 def conn.started?() true end 657 def conn.request(req) 658 url = 'http://gems.example.com/redirect' 659 res = Net::HTTPMovedPermanently.new nil, 301, nil 660 res.add_field 'Location', url 661 res 662 end 663 664 conn = { "#{Thread.current.object_id}:gems.example.com:80" => conn } 665 fetcher.instance_variable_set :@connections, conn 666 667 e = assert_raises Gem::RemoteFetcher::FetchError do 668 fetcher.fetch_http URI.parse(url) 669 end 670 671 assert_equal "too many redirects (#{url})", e.message 672 end 673 674 def test_normalize_uri 675 assert_equal 'FILE://example/', @fetcher.normalize_uri('FILE://example/') 676 assert_equal 'FTP://example/', @fetcher.normalize_uri('FTP://example/') 677 assert_equal 'HTTP://example/', @fetcher.normalize_uri('HTTP://example/') 678 assert_equal 'HTTPS://example/', @fetcher.normalize_uri('HTTPS://example/') 679 assert_equal 'http://example/', @fetcher.normalize_uri('example/') 680 end 681 682 def test_observe_no_proxy_env_single_host 683 use_ui @ui do 684 ENV["http_proxy"] = @proxy_uri 685 ENV["no_proxy"] = URI::parse(@server_uri).host 686 fetcher = Gem::RemoteFetcher.new nil 687 assert_data_from_server fetcher.fetch_path(@server_uri) 688 end 689 end 690 691 def test_observe_no_proxy_env_list 692 use_ui @ui do 693 ENV["http_proxy"] = @proxy_uri 694 ENV["no_proxy"] = "fakeurl.com, #{URI::parse(@server_uri).host}" 695 fetcher = Gem::RemoteFetcher.new nil 696 assert_data_from_server fetcher.fetch_path(@server_uri) 697 end 698 end 699 700 def test_request 701 uri = URI.parse "#{@gem_repo}/specs.#{Gem.marshal_version}" 702 util_stub_connection_for :body => :junk, :code => 200 703 704 response = @fetcher.request uri, Net::HTTP::Get 705 706 assert_equal 200, response.code 707 assert_equal :junk, response.body 708 end 709 710 def test_request_head 711 uri = URI.parse "#{@gem_repo}/specs.#{Gem.marshal_version}" 712 util_stub_connection_for :body => '', :code => 200 713 response = @fetcher.request uri, Net::HTTP::Head 714 715 assert_equal 200, response.code 716 assert_equal '', response.body 717 end 718 719 def test_request_unmodified 720 uri = URI.parse "#{@gem_repo}/specs.#{Gem.marshal_version}" 721 conn = util_stub_connection_for :body => '', :code => 304 722 723 t = Time.now 724 response = @fetcher.request uri, Net::HTTP::Head, t 725 726 assert_equal 304, response.code 727 assert_equal '', response.body 728 729 assert_equal t.rfc2822, conn.payload['if-modified-since'] 730 end 731 732 def test_user_agent 733 ua = @fetcher.user_agent 734 735 assert_match %r%^RubyGems/\S+ \S+ Ruby/\S+ \(.*?\)%, ua 736 assert_match %r%RubyGems/#{Regexp.escape Gem::VERSION}%, ua 737 assert_match %r% #{Regexp.escape Gem::Platform.local.to_s} %, ua 738 assert_match %r%Ruby/#{Regexp.escape RUBY_VERSION}%, ua 739 assert_match %r%\(#{Regexp.escape RUBY_RELEASE_DATE} %, ua 740 end 741 742 def test_user_agent_engine 743 util_save_version 744 745 Object.send :remove_const, :RUBY_ENGINE if defined?(RUBY_ENGINE) 746 Object.send :const_set, :RUBY_ENGINE, 'vroom' 747 748 ua = @fetcher.user_agent 749 750 assert_match %r%\) vroom%, ua 751 ensure 752 util_restore_version 753 end 754 755 def test_user_agent_engine_ruby 756 util_save_version 757 758 Object.send :remove_const, :RUBY_ENGINE if defined?(RUBY_ENGINE) 759 Object.send :const_set, :RUBY_ENGINE, 'ruby' 760 761 ua = @fetcher.user_agent 762 763 assert_match %r%\)%, ua 764 ensure 765 util_restore_version 766 end 767 768 def test_user_agent_patchlevel 769 util_save_version 770 771 Object.send :remove_const, :RUBY_PATCHLEVEL 772 Object.send :const_set, :RUBY_PATCHLEVEL, 5 773 774 ua = @fetcher.user_agent 775 776 assert_match %r% patchlevel 5\)%, ua 777 ensure 778 util_restore_version 779 end 780 781 def test_user_agent_revision 782 util_save_version 783 784 Object.send :remove_const, :RUBY_PATCHLEVEL 785 Object.send :const_set, :RUBY_PATCHLEVEL, -1 786 Object.send :remove_const, :RUBY_REVISION if defined?(RUBY_REVISION) 787 Object.send :const_set, :RUBY_REVISION, 6 788 789 ua = @fetcher.user_agent 790 791 assert_match %r% revision 6\)%, ua 792 assert_match %r%Ruby/#{Regexp.escape RUBY_VERSION}dev%, ua 793 ensure 794 util_restore_version 795 end 796 797 def test_user_agent_revision_missing 798 util_save_version 799 800 Object.send :remove_const, :RUBY_PATCHLEVEL 801 Object.send :const_set, :RUBY_PATCHLEVEL, -1 802 Object.send :remove_const, :RUBY_REVISION if defined?(RUBY_REVISION) 803 804 ua = @fetcher.user_agent 805 806 assert_match %r%\(#{Regexp.escape RUBY_RELEASE_DATE}\)%, ua 807 ensure 808 util_restore_version 809 end 810 811 def test_yaml_error_on_size 812 use_ui @ui do 813 self.class.enable_yaml = false 814 fetcher = Gem::RemoteFetcher.new nil 815 assert_error { fetcher.size } 816 end 817 end 818 819 def test_ssl_connection 820 ssl_server = self.class.start_ssl_server 821 temp_ca_cert = File.join(DIR, 'ca_cert.pem') 822 with_configured_fetcher(":ssl_ca_cert: #{temp_ca_cert}") do |fetcher| 823 fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml") 824 end 825 end 826 827 def test_do_not_allow_insecure_ssl_connection_by_default 828 ssl_server = self.class.start_ssl_server 829 with_configured_fetcher do |fetcher| 830 assert_raises Gem::RemoteFetcher::FetchError do 831 fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml") 832 end 833 end 834 end 835 836 def test_ssl_connection_allow_verify_none 837 ssl_server = self.class.start_ssl_server 838 with_configured_fetcher(":ssl_verify_mode: 0") do |fetcher| 839 fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml") 840 end 841 end 842 843 def test_do_not_follow_insecure_redirect 844 ssl_server = self.class.start_ssl_server 845 temp_ca_cert = File.join(DIR, 'ca_cert.pem'), 846 with_configured_fetcher(":ssl_ca_cert: #{temp_ca_cert}") do |fetcher| 847 assert_raises Gem::RemoteFetcher::FetchError do 848 fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/insecure_redirect?to=#{@server_uri}") 849 end 850 end 851 end 852 853 def with_configured_fetcher(config_str = nil, &block) 854 if config_str 855 temp_conf = File.join @tempdir, '.gemrc' 856 File.open temp_conf, 'w' do |fp| 857 fp.puts config_str 858 end 859 Gem.configuration = Gem::ConfigFile.new %W[--config-file #{temp_conf}] 860 end 861 yield Gem::RemoteFetcher.new 862 ensure 863 Gem.configuration = nil 864 end 865 866 def util_stub_connection_for hash 867 def @fetcher.connection= conn 868 @conn = conn 869 end 870 871 def @fetcher.connection_for uri 872 @conn 873 end 874 875 @fetcher.connection = Conn.new OpenStruct.new(hash) 876 end 877 878 def assert_error(exception_class=Exception) 879 got_exception = false 880 881 begin 882 yield 883 rescue exception_class 884 got_exception = true 885 end 886 887 assert got_exception, "Expected exception conforming to #{exception_class}" 888 end 889 890 def assert_data_from_server(data) 891 assert_match(/0\.4\.11/, data, "Data is not from server") 892 end 893 894 def assert_data_from_proxy(data) 895 assert_match(/0\.4\.2/, data, "Data is not from proxy") 896 end 897 898 class Conn 899 attr_accessor :payload 900 901 def initialize(response) 902 @response = response 903 self.payload = nil 904 end 905 906 def request(req) 907 self.payload = req 908 @response 909 end 910 end 911 912 class NilLog < WEBrick::Log 913 def log(level, data) #Do nothing 914 end 915 end 916 917 class << self 918 attr_reader :normal_server, :proxy_server 919 attr_accessor :enable_zip, :enable_yaml 920 921 def start_servers 922 @normal_server ||= start_server(SERVER_DATA) 923 @proxy_server ||= start_server(PROXY_DATA) 924 @enable_yaml = true 925 @enable_zip = false 926 end 927 928 def normal_server_port 929 @normal_server[:server].config[:Port] 930 end 931 932 def proxy_server_port 933 @proxy_server[:server].config[:Port] 934 end 935 936 DIR = File.expand_path(File.dirname(__FILE__)) 937 DH_PARAM = OpenSSL::PKey::DH.new(128) 938 939 def start_ssl_server(config = {}) 940 null_logger = NilLog.new 941 server = WEBrick::HTTPServer.new({ 942 :Port => 0, 943 :Logger => null_logger, 944 :AccessLog => [], 945 :SSLEnable => true, 946 :SSLCACertificateFile => File.join(DIR, 'ca_cert.pem'), 947 :SSLCertificate => cert('ssl_cert.pem'), 948 :SSLPrivateKey => key('ssl_key.pem'), 949 :SSLVerifyClient => nil, 950 :SSLCertName => nil 951 }.merge(config)) 952 server.mount_proc("/yaml") { |req, res| 953 res.body = "--- true\n" 954 } 955 server.mount_proc("/insecure_redirect") { |req, res| 956 res.set_redirect(WEBrick::HTTPStatus::MovedPermanently, req.query['to']) 957 } 958 server.ssl_context.tmp_dh_callback = proc { DH_PARAM } 959 t = Thread.new do 960 begin 961 server.start 962 rescue Exception => ex 963 abort ex.message 964 puts "ERROR during server thread: #{ex.message}" 965 end 966 end 967 while server.status != :Running 968 sleep 0.1 969 unless t.alive? 970 t.join 971 raise 972 end 973 end 974 server 975 end 976 977 978 979 private 980 981 def start_server(data) 982 null_logger = NilLog.new 983 s = WEBrick::HTTPServer.new( 984 :Port => 0, 985 :DocumentRoot => nil, 986 :Logger => null_logger, 987 :AccessLog => null_logger 988 ) 989 s.mount_proc("/kill") { |req, res| s.shutdown } 990 s.mount_proc("/yaml") { |req, res| 991 if @enable_yaml 992 res.body = data 993 res['Content-Type'] = 'text/plain' 994 res['content-length'] = data.size 995 else 996 res.status = "404" 997 res.body = "<h1>NOT FOUND</h1>" 998 res['Content-Type'] = 'text/html' 999 end 1000 } 1001 s.mount_proc("/yaml.Z") { |req, res| 1002 if @enable_zip 1003 res.body = Zlib::Deflate.deflate(data) 1004 res['Content-Type'] = 'text/plain' 1005 else 1006 res.status = "404" 1007 res.body = "<h1>NOT FOUND</h1>" 1008 res['Content-Type'] = 'text/html' 1009 end 1010 } 1011 th = Thread.new do 1012 begin 1013 s.start 1014 rescue Exception => ex 1015 abort "ERROR during server thread: #{ex.message}" 1016 end 1017 end 1018 th[:server] = s 1019 th 1020 end 1021 1022 def cert(filename) 1023 OpenSSL::X509::Certificate.new(File.read(File.join(DIR, filename))) 1024 end 1025 1026 def key(filename) 1027 OpenSSL::PKey::RSA.new(File.read(File.join(DIR, filename))) 1028 end 1029 end 1030 1031 def test_correct_for_windows_path 1032 path = "/C:/WINDOWS/Temp/gems" 1033 assert_equal "C:/WINDOWS/Temp/gems", @fetcher.correct_for_windows_path(path) 1034 1035 path = "/home/skillet" 1036 assert_equal "/home/skillet", @fetcher.correct_for_windows_path(path) 1037 end 1038 1039 def util_save_version 1040 @orig_RUBY_ENGINE = RUBY_ENGINE if defined? RUBY_ENGINE 1041 @orig_RUBY_PATCHLEVEL = RUBY_PATCHLEVEL 1042 @orig_RUBY_REVISION = RUBY_REVISION if defined? RUBY_REVISION 1043 end 1044 1045 def util_restore_version 1046 Object.send :remove_const, :RUBY_ENGINE if defined?(RUBY_ENGINE) 1047 Object.send :const_set, :RUBY_ENGINE, @orig_RUBY_ENGINE if 1048 defined?(@orig_RUBY_ENGINE) 1049 1050 Object.send :remove_const, :RUBY_PATCHLEVEL 1051 Object.send :const_set, :RUBY_PATCHLEVEL, @orig_RUBY_PATCHLEVEL 1052 1053 Object.send :remove_const, :RUBY_REVISION if defined?(RUBY_REVISION) 1054 Object.send :const_set, :RUBY_REVISION, @orig_RUBY_REVISION if 1055 defined?(@orig_RUBY_REVISION) 1056 end 1057 1058end 1059 1060