require 'rubygems' require 'mechanize' require 'logger' require 'webrick' require 'test/unit' if false # Try a new version of to_absolute_uri to see if it fixes the problem module WWW class Mechanize def to_absolute_uri(url, cur_page=current_page()) # was: # url = URI.parse( # URI.unescape(Util.html_unescape(url.to_s.strip)).gsub(/ /, '%20') # ) unless url.is_a? URI url = URI.parse( URI.escape(Util.html_unescape(url.to_s.strip)).gsub(/ /, '%20') ) unless url.is_a? URI # construct an absolute uri if url.relative? raise 'no history. please specify an absolute URL' unless cur_page.uri url = cur_page.uri + url end return url end end end end # The test below uses WEBrick to host a few test pages. Unfortunately, WEBrick # "fixes" the bad redirect, hiding the problem I was seeing with the real server. So, # replace the HTTPResponse setup_header method with one that doesn't try to fix the # 'Location' header. (The only change is marked "HACK!" below) module WEBrick class HTTPResponse def setup_header() @reason_phrase ||= HTTPStatus::reason_phrase(@status) @header['server'] ||= @config[:ServerSoftware] @header['date'] ||= Time.now.httpdate # HTTP/0.9 features if @request_http_version < "1.0" @http_version = HTTPVersion.new("0.9") @keep_alive = false end # HTTP/1.0 features if @request_http_version < "1.1" if chunked? @chunked = false ver = @request_http_version.to_s msg = "chunked is set for an HTTP/#{ver} request. (ignored)" @logger.warn(msg) end end # Determin the message length (RFC2616 -- 4.4 Message Length) if @status == 304 || @status == 204 || HTTPStatus::info?(@status) @header.delete('content-length') @body = "" elsif chunked? @header["transfer-encoding"] = "chunked" @header.delete('content-length') elsif %r{^multipart/byteranges} =~ @header['content-type'] @header.delete('content-length') elsif @header['content-length'].nil? unless @body.is_a?(IO) @header['content-length'] = @body ? @body.size : 0 end end # Keep-Alive connection. if @header['connection'] == "close" @keep_alive = false elsif keep_alive? if chunked? || @header['content-length'] @header['connection'] = "Keep-Alive" end else @header['connection'] = "close" end # Location is a single absoluteURI. if location = @header['location'] if @request_uri # HACK! Use the location as-is; was: # @header['location'] = @request_uri.merge(location) @header['location'] = location end end end end end # OK, the test: Start a webserver that serves three pages: # a simple form, a handler that redirects, and a target for # the redirection. class BadForwardTest < Test::Unit::TestCase def setup # Start a thread containing a WEBRick server that serves three "pages"... @server_thread = Thread.new do server = WEBrick::HTTPServer.new(:Port =>8080) # /page1, which contains a POST form whose action is /page2.html server.mount_proc('/page1') do |req, resp| resp['Content-type'] = 'text/html' resp.body = %{
} end # /page2.html, which redirects using a badly-formatted URL to /page3.html server.mount_proc('/page2') do |req, resp| resp.status = 302 resp['Location'] = 'page3?|' end # /page3.html, which just gives the submit's value server.mount_proc('/page3') do |req, resp| resp['Content-type'] = 'text/html' resp.body = "This is page 3." end server.start end end def test_form_forward # Finally, we get to the test: submit our form, and see what comes back. agent = WWW::Mechanize.new { |a| a.log = Logger.new(STDOUT) } page1 = agent.get('http://localhost:8080/page1') result = page1.forms[0].submit assert result.body == 'This is page 3.' end def teardown @server_thread.kill end end