PageRenderTime 30ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/test/open-uri/test_open-uri.rb

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