1require 'test/unit' 2require 'open-uri' 3require 'webrick' 4require 'webrick/httpproxy' 5begin 6 require 'zlib' 7rescue LoadError 8end 9 10class TestOpenURI < Test::Unit::TestCase 11 12 NullLog = Object.new 13 def NullLog.<<(arg) 14 end 15 16 def with_http 17 Dir.mktmpdir {|dr| 18 srv = WEBrick::HTTPServer.new({ 19 :DocumentRoot => dr, 20 :ServerType => Thread, 21 :Logger => WEBrick::Log.new(NullLog), 22 :AccessLog => [[NullLog, ""]], 23 :BindAddress => '127.0.0.1', 24 :Port => 0}) 25 _, port, _, host = srv.listeners[0].addr 26 begin 27 srv.start 28 yield srv, dr, "http://#{host}:#{port}" 29 ensure 30 srv.shutdown 31 until srv.status == :Stop 32 sleep 0.1 33 end 34 end 35 } 36 end 37 38 def with_env(h) 39 begin 40 old = {} 41 h.each_key {|k| old[k] = ENV[k] } 42 h.each {|k, v| ENV[k] = v } 43 yield 44 ensure 45 h.each_key {|k| ENV[k] = old[k] } 46 end 47 end 48 49 def setup 50 @proxies = %w[http_proxy HTTP_PROXY ftp_proxy FTP_PROXY no_proxy] 51 @old_proxies = @proxies.map {|k| ENV[k] } 52 @proxies.each {|k| ENV[k] = nil } 53 end 54 55 def teardown 56 @proxies.each_with_index {|k, i| ENV[k] = @old_proxies[i] } 57 end 58 59 def test_200 60 with_http {|srv, dr, url| 61 srv.mount_proc("/foo200", lambda { |req, res| res.body = "foo200" } ) 62 open("#{url}/foo200") {|f| 63 assert_equal("200", f.status[0]) 64 assert_equal("foo200", f.read) 65 } 66 } 67 end 68 69 def test_200big 70 with_http {|srv, dr, url| 71 content = "foo200big"*10240 72 srv.mount_proc("/foo200big", lambda { |req, res| res.body = content } ) 73 open("#{url}/foo200big") {|f| 74 assert_equal("200", f.status[0]) 75 assert_equal(content, f.read) 76 } 77 } 78 end 79 80 def test_404 81 with_http {|srv, dr, url| 82 exc = assert_raise(OpenURI::HTTPError) { open("#{url}/not-exist") {} } 83 assert_equal("404", exc.io.status[0]) 84 } 85 end 86 87 def test_open_uri 88 with_http {|srv, dr, url| 89 srv.mount_proc("/foo_ou", lambda { |req, res| res.body = "foo_ou" } ) 90 u = URI("#{url}/foo_ou") 91 open(u) {|f| 92 assert_equal("200", f.status[0]) 93 assert_equal("foo_ou", f.read) 94 } 95 } 96 end 97 98 def test_open_too_many_arg 99 assert_raise(ArgumentError) { open("http://192.0.2.1/tma", "r", 0666, :extra) {} } 100 end 101 102 def test_read_timeout 103 TCPServer.open("127.0.0.1", 0) {|serv| 104 port = serv.addr[1] 105 th = Thread.new { 106 sock = serv.accept 107 begin 108 req = sock.gets("\r\n\r\n") 109 assert_match(%r{\AGET /foo/bar }, req) 110 sock.print "HTTP/1.0 200 OK\r\n" 111 sock.print "Content-Length: 4\r\n\r\n" 112 sleep 1 113 sock.print "ab\r\n" 114 ensure 115 sock.close 116 end 117 } 118 begin 119 assert_raise(Net::ReadTimeout) { URI("http://127.0.0.1:#{port}/foo/bar").read(:read_timeout=>0.1) } 120 ensure 121 Thread.kill(th) 122 th.join 123 end 124 } 125 end 126 127 def test_invalid_option 128 assert_raise(ArgumentError) { open("http://127.0.0.1/", :invalid_option=>true) {} } 129 end 130 131 def test_mode 132 with_http {|srv, dr, url| 133 srv.mount_proc("/mode", lambda { |req, res| res.body = "mode" } ) 134 open("#{url}/mode", "r") {|f| 135 assert_equal("200", f.status[0]) 136 assert_equal("mode", f.read) 137 } 138 open("#{url}/mode", "r", 0600) {|f| 139 assert_equal("200", f.status[0]) 140 assert_equal("mode", f.read) 141 } 142 assert_raise(ArgumentError) { open("#{url}/mode", "a") {} } 143 open("#{url}/mode", "r:us-ascii") {|f| 144 assert_equal(Encoding::US_ASCII, f.read.encoding) 145 } 146 open("#{url}/mode", "r:utf-8") {|f| 147 assert_equal(Encoding::UTF_8, f.read.encoding) 148 } 149 assert_raise(ArgumentError) { open("#{url}/mode", "r:invalid-encoding") {} } 150 } 151 end 152 153 def test_without_block 154 with_http {|srv, dr, url| 155 srv.mount_proc("/without_block", lambda { |req, res| res.body = "without_block" } ) 156 begin 157 f = open("#{url}/without_block") 158 assert_equal("200", f.status[0]) 159 assert_equal("without_block", f.read) 160 ensure 161 f.close if f && !f.closed? 162 end 163 } 164 end 165 166 def test_header 167 myheader1 = 'barrrr' 168 myheader2 = nil 169 with_http {|srv, dr, url| 170 srv.mount_proc("/h/") {|req, res| myheader2 = req['myheader']; res.body = "foo" } 171 open("#{url}/h/", 'MyHeader'=>myheader1) {|f| 172 assert_equal("foo", f.read) 173 assert_equal(myheader1, myheader2) 174 } 175 } 176 end 177 178 def test_multi_proxy_opt 179 assert_raise(ArgumentError) { 180 open("http://127.0.0.1/", :proxy_http_basic_authentication=>true, :proxy=>true) {} 181 } 182 end 183 184 def test_non_http_proxy 185 assert_raise(RuntimeError) { 186 open("http://127.0.0.1/", :proxy=>URI("ftp://127.0.0.1/")) {} 187 } 188 end 189 190 def test_proxy 191 with_http {|srv, dr, url| 192 log = '' 193 proxy = WEBrick::HTTPProxyServer.new({ 194 :ServerType => Thread, 195 :Logger => WEBrick::Log.new(NullLog), 196 :AccessLog => [[NullLog, ""]], 197 :ProxyAuthProc => lambda {|req, res| 198 log << req.request_line 199 }, 200 :BindAddress => '127.0.0.1', 201 :Port => 0}) 202 _, proxy_port, _, proxy_host = proxy.listeners[0].addr 203 proxy_url = "http://#{proxy_host}:#{proxy_port}/" 204 begin 205 proxy.start 206 srv.mount_proc("/proxy", lambda { |req, res| res.body = "proxy" } ) 207 open("#{url}/proxy", :proxy=>proxy_url) {|f| 208 assert_equal("200", f.status[0]) 209 assert_equal("proxy", f.read) 210 } 211 assert_match(/#{Regexp.quote url}/, log); log.clear 212 open("#{url}/proxy", :proxy=>URI(proxy_url)) {|f| 213 assert_equal("200", f.status[0]) 214 assert_equal("proxy", f.read) 215 } 216 assert_match(/#{Regexp.quote url}/, log); log.clear 217 open("#{url}/proxy", :proxy=>nil) {|f| 218 assert_equal("200", f.status[0]) 219 assert_equal("proxy", f.read) 220 } 221 assert_equal("", log); log.clear 222 assert_raise(ArgumentError) { 223 open("#{url}/proxy", :proxy=>:invalid) {} 224 } 225 assert_equal("", log); log.clear 226 with_env("http_proxy"=>proxy_url) { 227 # should not use proxy for 127.0.0.0/8. 228 open("#{url}/proxy") {|f| 229 assert_equal("200", f.status[0]) 230 assert_equal("proxy", f.read) 231 } 232 } 233 assert_equal("", log); log.clear 234 ensure 235 proxy.shutdown 236 end 237 } 238 end 239 240 def test_proxy_http_basic_authentication 241 with_http {|srv, dr, url| 242 log = '' 243 proxy = WEBrick::HTTPProxyServer.new({ 244 :ServerType => Thread, 245 :Logger => WEBrick::Log.new(NullLog), 246 :AccessLog => [[NullLog, ""]], 247 :ProxyAuthProc => lambda {|req, res| 248 log << req.request_line 249 if req["Proxy-Authorization"] != "Basic #{['user:pass'].pack('m').chomp}" 250 raise WEBrick::HTTPStatus::ProxyAuthenticationRequired 251 end 252 }, 253 :BindAddress => '127.0.0.1', 254 :Port => 0}) 255 _, proxy_port, _, proxy_host = proxy.listeners[0].addr 256 proxy_url = "http://#{proxy_host}:#{proxy_port}/" 257 begin 258 proxy.start 259 srv.mount_proc("/proxy", lambda { |req, res| res.body = "proxy" } ) 260 exc = assert_raise(OpenURI::HTTPError) { open("#{url}/proxy", :proxy=>proxy_url) {} } 261 assert_equal("407", exc.io.status[0]) 262 assert_match(/#{Regexp.quote url}/, log); log.clear 263 open("#{url}/proxy", 264 :proxy_http_basic_authentication=>[proxy_url, "user", "pass"]) {|f| 265 assert_equal("200", f.status[0]) 266 assert_equal("proxy", f.read) 267 } 268 assert_match(/#{Regexp.quote url}/, log); log.clear 269 assert_raise(ArgumentError) { 270 open("#{url}/proxy", 271 :proxy_http_basic_authentication=>[true, "user", "pass"]) {} 272 } 273 assert_equal("", log); log.clear 274 ensure 275 proxy.shutdown 276 end 277 } 278 end 279 280 def test_redirect 281 with_http {|srv, dr, url| 282 srv.mount_proc("/r1/") {|req, res| res.status = 301; res["location"] = "#{url}/r2"; res.body = "r1" } 283 srv.mount_proc("/r2/") {|req, res| res.body = "r2" } 284 srv.mount_proc("/to-file/") {|req, res| res.status = 301; res["location"] = "file:///foo" } 285 open("#{url}/r1/") {|f| 286 assert_equal("#{url}/r2", f.base_uri.to_s) 287 assert_equal("r2", f.read) 288 } 289 assert_raise(OpenURI::HTTPRedirect) { open("#{url}/r1/", :redirect=>false) {} } 290 assert_raise(RuntimeError) { open("#{url}/to-file/") {} } 291 } 292 end 293 294 def test_redirect_loop 295 with_http {|srv, dr, url| 296 srv.mount_proc("/r1/") {|req, res| res.status = 301; res["location"] = "#{url}/r2"; res.body = "r1" } 297 srv.mount_proc("/r2/") {|req, res| res.status = 301; res["location"] = "#{url}/r1"; res.body = "r2" } 298 assert_raise(RuntimeError) { open("#{url}/r1/") {} } 299 } 300 end 301 302 def test_redirect_relative 303 TCPServer.open("127.0.0.1", 0) {|serv| 304 port = serv.addr[1] 305 th = Thread.new { 306 sock = serv.accept 307 begin 308 req = sock.gets("\r\n\r\n") 309 assert_match(%r{\AGET /foo/bar }, req) 310 sock.print "HTTP/1.0 302 Found\r\n" 311 sock.print "Location: ../baz\r\n\r\n" 312 ensure 313 sock.close 314 end 315 sock = serv.accept 316 begin 317 req = sock.gets("\r\n\r\n") 318 assert_match(%r{\AGET /baz }, req) 319 sock.print "HTTP/1.0 200 OK\r\n" 320 sock.print "Content-Length: 4\r\n\r\n" 321 sock.print "ab\r\n" 322 ensure 323 sock.close 324 end 325 } 326 begin 327 content = URI("http://127.0.0.1:#{port}/foo/bar").read 328 assert_equal("ab\r\n", content) 329 ensure 330 Thread.kill(th) 331 th.join 332 end 333 } 334 end 335 336 def test_redirect_invalid 337 TCPServer.open("127.0.0.1", 0) {|serv| 338 port = serv.addr[1] 339 th = Thread.new { 340 sock = serv.accept 341 begin 342 req = sock.gets("\r\n\r\n") 343 assert_match(%r{\AGET /foo/bar }, req) 344 sock.print "HTTP/1.0 302 Found\r\n" 345 sock.print "Location: ::\r\n\r\n" 346 ensure 347 sock.close 348 end 349 } 350 begin 351 assert_raise(OpenURI::HTTPError) { 352 URI("http://127.0.0.1:#{port}/foo/bar").read 353 } 354 ensure 355 Thread.kill(th) 356 th.join 357 end 358 } 359 end 360 361 def test_redirect_auth 362 with_http {|srv, dr, url| 363 srv.mount_proc("/r1/") {|req, res| res.status = 301; res["location"] = "#{url}/r2" } 364 srv.mount_proc("/r2/") {|req, res| 365 if req["Authorization"] != "Basic #{['user:pass'].pack('m').chomp}" 366 raise WEBrick::HTTPStatus::Unauthorized 367 end 368 res.body = "r2" 369 } 370 exc = assert_raise(OpenURI::HTTPError) { open("#{url}/r2/") {} } 371 assert_equal("401", exc.io.status[0]) 372 open("#{url}/r2/", :http_basic_authentication=>['user', 'pass']) {|f| 373 assert_equal("r2", f.read) 374 } 375 exc = assert_raise(OpenURI::HTTPError) { open("#{url}/r1/", :http_basic_authentication=>['user', 'pass']) {} } 376 assert_equal("401", exc.io.status[0]) 377 } 378 end 379 380 def test_userinfo 381 if "1.9.0" <= RUBY_VERSION 382 assert_raise(ArgumentError) { open("http://user:pass@127.0.0.1/") {} } 383 end 384 end 385 386 def test_progress 387 with_http {|srv, dr, url| 388 content = "a" * 100000 389 srv.mount_proc("/data/") {|req, res| res.body = content } 390 length = [] 391 progress = [] 392 open("#{url}/data/", 393 :content_length_proc => lambda {|n| length << n }, 394 :progress_proc => lambda {|n| progress << n } 395 ) {|f| 396 assert_equal(1, length.length) 397 assert_equal(content.length, length[0]) 398 assert(progress.length>1,"maybe test is wrong") 399 assert(progress.sort == progress,"monotone increasing expected but was\n#{progress.inspect}") 400 assert_equal(content.length, progress[-1]) 401 assert_equal(content, f.read) 402 } 403 } 404 end 405 406 def test_progress_chunked 407 with_http {|srv, dr, url| 408 content = "a" * 100000 409 srv.mount_proc("/data/") {|req, res| res.body = content; res.chunked = true } 410 length = [] 411 progress = [] 412 open("#{url}/data/", 413 :content_length_proc => lambda {|n| length << n }, 414 :progress_proc => lambda {|n| progress << n } 415 ) {|f| 416 assert_equal(1, length.length) 417 assert_equal(nil, length[0]) 418 assert(progress.length>1,"maybe test is worng") 419 assert(progress.sort == progress,"monotone increasing expected but was\n#{progress.inspect}") 420 assert_equal(content.length, progress[-1]) 421 assert_equal(content, f.read) 422 } 423 } 424 end 425 426 def test_uri_read 427 with_http {|srv, dr, url| 428 srv.mount_proc("/uriread", lambda { |req, res| res.body = "uriread" } ) 429 data = URI("#{url}/uriread").read 430 assert_equal("200", data.status[0]) 431 assert_equal("uriread", data) 432 } 433 end 434 435 def test_encoding 436 with_http {|srv, dr, url| 437 content_u8 = "\u3042" 438 content_ej = "\xa2\xa4".force_encoding("euc-jp") 439 srv.mount_proc("/u8/") {|req, res| res.body = content_u8; res['content-type'] = 'text/plain; charset=utf-8' } 440 srv.mount_proc("/ej/") {|req, res| res.body = content_ej; res['content-type'] = 'TEXT/PLAIN; charset=EUC-JP' } 441 srv.mount_proc("/nc/") {|req, res| res.body = "aa"; res['content-type'] = 'Text/Plain' } 442 open("#{url}/u8/") {|f| 443 assert_equal(content_u8, f.read) 444 assert_equal("text/plain", f.content_type) 445 assert_equal("utf-8", f.charset) 446 } 447 open("#{url}/ej/") {|f| 448 assert_equal(content_ej, f.read) 449 assert_equal("text/plain", f.content_type) 450 assert_equal("euc-jp", f.charset) 451 } 452 open("#{url}/nc/") {|f| 453 assert_equal("aa", f.read) 454 assert_equal("text/plain", f.content_type) 455 assert_equal("iso-8859-1", f.charset) 456 assert_equal("unknown", f.charset { "unknown" }) 457 } 458 } 459 end 460 461 def test_quoted_attvalue 462 with_http {|srv, dr, url| 463 content_u8 = "\u3042" 464 srv.mount_proc("/qu8/") {|req, res| res.body = content_u8; res['content-type'] = 'text/plain; charset="utf\-8"' } 465 open("#{url}/qu8/") {|f| 466 assert_equal(content_u8, f.read) 467 assert_equal("text/plain", f.content_type) 468 assert_equal("utf-8", f.charset) 469 } 470 } 471 end 472 473 def test_last_modified 474 with_http {|srv, dr, url| 475 srv.mount_proc("/data/") {|req, res| res.body = "foo"; res['last-modified'] = 'Fri, 07 Aug 2009 06:05:04 GMT' } 476 open("#{url}/data/") {|f| 477 assert_equal("foo", f.read) 478 assert_equal(Time.utc(2009,8,7,6,5,4), f.last_modified) 479 } 480 } 481 end 482 483 def test_content_encoding 484 with_http {|srv, dr, url| 485 content = "abc" * 10000 486 Zlib::GzipWriter.wrap(StringIO.new(content_gz="".force_encoding("ascii-8bit"))) {|z| z.write content } 487 srv.mount_proc("/data/") {|req, res| res.body = content_gz; res['content-encoding'] = 'gzip' } 488 srv.mount_proc("/data2/") {|req, res| res.body = content_gz; res['content-encoding'] = 'gzip'; res.chunked = true } 489 srv.mount_proc("/noce/") {|req, res| res.body = content_gz } 490 open("#{url}/data/") {|f| 491 assert_equal [], f.content_encoding 492 assert_equal(content, f.read) 493 } 494 open("#{url}/data2/") {|f| 495 assert_equal [], f.content_encoding 496 assert_equal(content, f.read) 497 } 498 open("#{url}/noce/") {|f| 499 assert_equal [], f.content_encoding 500 assert_equal(content_gz, f.read.force_encoding("ascii-8bit")) 501 } 502 } 503 end if defined?(Zlib::GzipWriter) 504 505 # 192.0.2.0/24 is TEST-NET. [RFC3330] 506 507 def test_ftp_invalid_request 508 assert_raise(ArgumentError) { URI("ftp://127.0.0.1/").read } 509 assert_raise(ArgumentError) { URI("ftp://127.0.0.1/a%0Db").read } 510 assert_raise(ArgumentError) { URI("ftp://127.0.0.1/a%0Ab").read } 511 assert_raise(ArgumentError) { URI("ftp://127.0.0.1/a%0Db/f").read } 512 assert_raise(ArgumentError) { URI("ftp://127.0.0.1/a%0Ab/f").read } 513 assert_raise(URI::InvalidComponentError) { URI("ftp://127.0.0.1/d/f;type=x") } 514 end 515 516 def test_ftp 517 TCPServer.open("127.0.0.1", 0) {|serv| 518 _, port, _, host = serv.addr 519 th = Thread.new { 520 s = serv.accept 521 begin 522 s.print "220 Test FTP Server\r\n" 523 assert_equal("USER anonymous\r\n", s.gets); s.print "331 name ok\r\n" 524 assert_match(/\APASS .*\r\n\z/, s.gets); s.print "230 logged in\r\n" 525 assert_equal("TYPE I\r\n", s.gets); s.print "200 type set to I\r\n" 526 assert_equal("CWD foo\r\n", s.gets); s.print "250 CWD successful\r\n" 527 assert_equal("PASV\r\n", s.gets) 528 TCPServer.open("127.0.0.1", 0) {|data_serv| 529 _, data_serv_port, _, _ = data_serv.addr 530 hi = data_serv_port >> 8 531 lo = data_serv_port & 0xff 532 s.print "227 Entering Passive Mode (127,0,0,1,#{hi},#{lo}).\r\n" 533 assert_equal("RETR bar\r\n", s.gets); s.print "150 file okay\r\n" 534 data_sock = data_serv.accept 535 begin 536 data_sock << "content" 537 ensure 538 data_sock.close 539 end 540 s.print "226 transfer complete\r\n" 541 assert_nil(s.gets) 542 } 543 ensure 544 s.close if s 545 end 546 } 547 begin 548 content = URI("ftp://#{host}:#{port}/foo/bar").read 549 assert_equal("content", content) 550 ensure 551 Thread.kill(th) 552 th.join 553 end 554 } 555 end 556 557 def test_ftp_active 558 TCPServer.open("127.0.0.1", 0) {|serv| 559 _, port, _, host = serv.addr 560 th = Thread.new { 561 s = serv.accept 562 begin 563 content = "content" 564 s.print "220 Test FTP Server\r\n" 565 assert_equal("USER anonymous\r\n", s.gets); s.print "331 name ok\r\n" 566 assert_match(/\APASS .*\r\n\z/, s.gets); s.print "230 logged in\r\n" 567 assert_equal("TYPE I\r\n", s.gets); s.print "200 type set to I\r\n" 568 assert_equal("CWD foo\r\n", s.gets); s.print "250 CWD successful\r\n" 569 assert(m = /\APORT 127,0,0,1,(\d+),(\d+)\r\n\z/.match(s.gets)) 570 active_port = m[1].to_i << 8 | m[2].to_i 571 TCPSocket.open("127.0.0.1", active_port) {|data_sock| 572 s.print "200 data connection opened\r\n" 573 assert_equal("RETR bar\r\n", s.gets); s.print "150 file okay\r\n" 574 begin 575 data_sock << content 576 ensure 577 data_sock.close 578 end 579 s.print "226 transfer complete\r\n" 580 assert_nil(s.gets) 581 } 582 ensure 583 s.close if s 584 end 585 } 586 begin 587 content = URI("ftp://#{host}:#{port}/foo/bar").read(:ftp_active_mode=>true) 588 assert_equal("content", content) 589 ensure 590 Thread.kill(th) 591 th.join 592 end 593 } 594 end 595 596 def test_ftp_ascii 597 TCPServer.open("127.0.0.1", 0) {|serv| 598 _, port, _, host = serv.addr 599 th = Thread.new { 600 s = serv.accept 601 begin 602 content = "content" 603 s.print "220 Test FTP Server\r\n" 604 assert_equal("USER anonymous\r\n", s.gets); s.print "331 name ok\r\n" 605 assert_match(/\APASS .*\r\n\z/, s.gets); s.print "230 logged in\r\n" 606 assert_equal("TYPE I\r\n", s.gets); s.print "200 type set to I\r\n" 607 assert_equal("CWD /foo\r\n", s.gets); s.print "250 CWD successful\r\n" 608 assert_equal("TYPE A\r\n", s.gets); s.print "200 type set to A\r\n" 609 assert_equal("SIZE bar\r\n", s.gets); s.print "213 #{content.bytesize}\r\n" 610 assert_equal("PASV\r\n", s.gets) 611 TCPServer.open("127.0.0.1", 0) {|data_serv| 612 _, data_serv_port, _, _ = data_serv.addr 613 hi = data_serv_port >> 8 614 lo = data_serv_port & 0xff 615 s.print "227 Entering Passive Mode (127,0,0,1,#{hi},#{lo}).\r\n" 616 assert_equal("RETR bar\r\n", s.gets); s.print "150 file okay\r\n" 617 data_sock = data_serv.accept 618 begin 619 data_sock << content 620 ensure 621 data_sock.close 622 end 623 s.print "226 transfer complete\r\n" 624 assert_nil(s.gets) 625 } 626 ensure 627 s.close if s 628 end 629 } 630 begin 631 length = [] 632 progress = [] 633 content = URI("ftp://#{host}:#{port}/%2Ffoo/b%61r;type=a").read( 634 :content_length_proc => lambda {|n| length << n }, 635 :progress_proc => lambda {|n| progress << n }) 636 assert_equal("content", content) 637 assert_equal([7], length) 638 assert_equal(7, progress.inject(&:+)) 639 ensure 640 Thread.kill(th) 641 th.join 642 end 643 } 644 end 645 646 def test_ftp_over_http_proxy 647 TCPServer.open("127.0.0.1", 0) {|proxy_serv| 648 proxy_port = proxy_serv.addr[1] 649 th = Thread.new { 650 proxy_sock = proxy_serv.accept 651 begin 652 req = proxy_sock.gets("\r\n\r\n") 653 assert_match(%r{\AGET ftp://192.0.2.1/foo/bar }, req) 654 proxy_sock.print "HTTP/1.0 200 OK\r\n" 655 proxy_sock.print "Content-Length: 4\r\n\r\n" 656 proxy_sock.print "ab\r\n" 657 ensure 658 proxy_sock.close 659 end 660 } 661 begin 662 with_env('ftp_proxy'=>"http://127.0.0.1:#{proxy_port}") { 663 content = URI("ftp://192.0.2.1/foo/bar").read 664 assert_equal("ab\r\n", content) 665 } 666 ensure 667 Thread.kill(th) 668 th.join 669 end 670 } 671 end 672 673 def test_ftp_over_http_proxy_auth 674 TCPServer.open("127.0.0.1", 0) {|proxy_serv| 675 proxy_port = proxy_serv.addr[1] 676 th = Thread.new { 677 proxy_sock = proxy_serv.accept 678 begin 679 req = proxy_sock.gets("\r\n\r\n") 680 assert_match(%r{\AGET ftp://192.0.2.1/foo/bar }, req) 681 assert_match(%r{Proxy-Authorization: Basic #{['proxy-user:proxy-password'].pack('m').chomp}\r\n}, req) 682 proxy_sock.print "HTTP/1.0 200 OK\r\n" 683 proxy_sock.print "Content-Length: 4\r\n\r\n" 684 proxy_sock.print "ab\r\n" 685 ensure 686 proxy_sock.close 687 end 688 } 689 begin 690 content = URI("ftp://192.0.2.1/foo/bar").read( 691 :proxy_http_basic_authentication => ["http://127.0.0.1:#{proxy_port}", "proxy-user", "proxy-password"]) 692 assert_equal("ab\r\n", content) 693 ensure 694 Thread.kill(th) 695 th.join 696 end 697 } 698 end 699 700end 701 702