<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>CHANGELOG.txt</filename>
    </added>
    <added>
      <filename>EXAMPLES.txt</filename>
    </added>
    <added>
      <filename>FAQ.txt</filename>
    </added>
    <added>
      <filename>GUIDE.txt</filename>
    </added>
    <added>
      <filename>LICENSE.txt</filename>
    </added>
    <added>
      <filename>Manifest.txt</filename>
    </added>
    <added>
      <filename>NOTES.txt</filename>
    </added>
    <added>
      <filename>README.txt</filename>
    </added>
    <added>
      <filename>lib/mechanize/history.rb</filename>
    </added>
    <added>
      <filename>lib/mechanize/monkey_patch.rb</filename>
    </added>
    <added>
      <filename>lib/mechanize/parsers/rexml_page.rb</filename>
    </added>
    <added>
      <filename>lib/mechanize/rexml.rb</filename>
    </added>
    <added>
      <filename>test/htdocs/empty_form.html</filename>
    </added>
    <added>
      <filename>test/htdocs/link with space.html</filename>
    </added>
    <added>
      <filename>test/htdocs/relative/tc_relative_links.html</filename>
    </added>
    <added>
      <filename>test/htdocs/tc_base_link.html</filename>
    </added>
    <added>
      <filename>test/htdocs/tc_blank_form.html</filename>
    </added>
    <added>
      <filename>test/htdocs/tc_encoded_links.html</filename>
    </added>
    <added>
      <filename>test/htdocs/tc_follow_meta.html</filename>
    </added>
    <added>
      <filename>test/htdocs/tc_form_action.html</filename>
    </added>
    <added>
      <filename>test/htdocs/tc_links.html</filename>
    </added>
    <added>
      <filename>test/htdocs/tc_referer.html</filename>
    </added>
    <added>
      <filename>test/htdocs/tc_relative_links.html</filename>
    </added>
    <added>
      <filename>test/htdocs/unusual______.html</filename>
    </added>
    <added>
      <filename>test/tc_blank_form.rb</filename>
    </added>
    <added>
      <filename>test/tc_encoded_links.rb</filename>
    </added>
    <added>
      <filename>test/tc_follow_meta.rb</filename>
    </added>
    <added>
      <filename>test/tc_form_action.rb</filename>
    </added>
    <added>
      <filename>test/tc_form_as_hash.rb</filename>
    </added>
    <added>
      <filename>test/tc_form_button.rb</filename>
    </added>
    <added>
      <filename>test/tc_history.rb</filename>
    </added>
    <added>
      <filename>test/tc_html_unscape_forms.rb</filename>
    </added>
    <added>
      <filename>test/tc_if_modified_since.rb</filename>
    </added>
    <added>
      <filename>test/tc_keep_alive.rb</filename>
    </added>
    <added>
      <filename>test/tc_referer.rb</filename>
    </added>
    <added>
      <filename>test/tc_relative_links.rb</filename>
    </added>
    <added>
      <filename>test/tc_subclass.rb</filename>
    </added>
    <added>
      <filename>test/test_all.rb</filename>
    </added>
    <added>
      <filename>test/test_mechanize_file.rb</filename>
    </added>
    <added>
      <filename>test/test_servlets.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -1,101 +1,60 @@
 require 'rubygems'
-require 'rake'
-require 'rake/testtask'
-require 'rake/gempackagetask'
-require 'rake/rdoctask'
-require 'rake/contrib/sshpublisher'
-
-def announce(msg='')
-  STDERR.puts msg
-end
-
-PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
-PKG_NAME = 'mechanize'
-PKG_VERSION = '0.6.0' + PKG_BUILD
-PKG_FILES = FileList[&quot;{doc,lib,test}/**/*&quot;].exclude(&quot;rdoc&quot;).to_a
-
-spec = Gem::Specification.new do |s|
-  s.name      = PKG_NAME
-  s.version   = PKG_VERSION
-  s.author    = &quot;Aaron Patterson&quot;
-  s.email     = &quot;aaronp@rubyforge.org&quot;
-  s.homepage  = &quot;#{PKG_NAME}.rubyforge.org&quot;
-  s.platform  = Gem::Platform::RUBY
-  s.summary   = &quot;Mechanize provides automated web-browsing&quot;
-  s.files     = Dir.glob(&quot;{bin,test,lib,doc}/**/*&quot;).delete_if {|item| item.include?(&quot;.svn&quot;) }
-  s.require_path  = &quot;lib&quot;
-  s.has_rdoc      = true
-  s.extra_rdoc_files = [&quot;README&quot;, &quot;EXAMPLES&quot;, &quot;CHANGELOG&quot;, &quot;LICENSE&quot;, &quot;NOTES&quot;,
-                        &quot;GUIDE&quot;]
-  s.rdoc_options &lt;&lt; &quot;--main&quot; &lt;&lt; 'README' &lt;&lt; &quot;--title&quot; &lt;&lt; &quot;'WWW::Mechanize RDoc'&quot;
-  s.rubyforge_project = PKG_NAME
-  s.add_dependency('hpricot') 
-end
-
-Rake::GemPackageTask.new(spec) do |p|
-  p.gem_spec = spec
-  p.need_tar = true
-  p.need_zip = true
-end
-
-Rake::RDocTask.new do |p|
-  p.main = &quot;README&quot;
-  p.rdoc_dir = &quot;doc&quot;
-  p.rdoc_files.include(&quot;README&quot;, &quot;CHANGELOG&quot;, &quot;LICENSE&quot;, &quot;EXAMPLES&quot;, &quot;NOTES&quot;,
-                       &quot;GUIDE&quot;, &quot;lib/**/*.rb&quot;)
-  p.options &lt;&lt; &quot;--main&quot; &lt;&lt; 'README' &lt;&lt; &quot;--title&quot; &lt;&lt; &quot;WWW::Mechanize RDoc&quot;
-end
-
-desc &quot;Publish the API documentation&quot;
-task :pubrdoc =&gt; [ :rdoc ] do
-  Rake::SshDirPublisher.new(
-    &quot;#{ENV['USER']}@rubyforge.org&quot;,
-    &quot;/var/www/gforge-projects/#{PKG_NAME}/&quot;,
-    &quot;doc&quot; ).upload
-end
-
-task :update_version do
-  announce &quot;Updating Mechanize Version to #{PKG_VERSION}&quot;
-  File.open(&quot;lib/mechanize/mech_version.rb&quot;, &quot;w&quot;) do |f|
-    f.puts &quot;module WWW&quot;
-    f.puts &quot;  class Mechanize&quot;
-    f.puts &quot;    Version = '#{PKG_VERSION}'&quot;
-    f.puts &quot;  end&quot;
-    f.puts &quot;end&quot;
+require 'hoe'
+
+$LOAD_PATH.unshift File.join(File.dirname(__FILE__), &quot;lib&quot;)
+require 'mechanize'
+
+class MechHoe &lt; Hoe
+  def define_tasks
+    super
+
+    desc &quot;Tag code&quot;
+    task :tag do |p|
+      abort &quot;Must supply VERSION=x.y.z&quot; unless ENV['VERSION']
+      v = ENV['VERSION'].gsub(/\./, '_')
+
+      rf = RubyForge.new
+      user = rf.userconfig['username']
+
+      baseurl = &quot;svn+ssh://#{user}@rubyforge.org//var/svn/#{name}&quot;
+      sh &quot;svn cp -m 'tagged REL-#{v}' . #{ baseurl }/tags/REL-#{ v }&quot;
+    end
+
+    desc &quot;Branch code&quot;
+    Rake::Task.define_task(&quot;branch&quot;) do |p|
+      abort &quot;Must supply VERSION=x.y.z&quot; unless ENV['VERSION']
+      v = ENV['VERSION'].split(/\./)[0..1].join('_')
+
+      rf = RubyForge.new
+      user = rf.userconfig['username']
+
+      baseurl = &quot;svn+ssh://#{user}@rubyforge.org/var/svn/#{name}&quot;
+      sh &quot;svn cp -m'branched #{v}' #{baseurl}/trunk #{baseurl}/branches/RB-#{v}&quot;
+    end
+    
+    desc &quot;Update SSL Certificate&quot;
+    Rake::Task.define_task('ssl_cert') do |p|
+      sh &quot;openssl genrsa -des3 -out server.key 1024&quot;
+      sh &quot;openssl req -new -key server.key -out server.csr&quot;
+      sh &quot;cp server.key server.key.org&quot;
+      sh &quot;openssl rsa -in server.key.org -out server.key&quot;
+      sh &quot;openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt&quot;
+      sh &quot;cp server.key server.pem&quot;
+      sh &quot;mv server.key server.csr server.crt server.pem test/data/&quot;
+      sh &quot;rm server.key.org&quot;
+    end
   end
-  sh 'svn commit -m&quot;updating version&quot; lib/mechanize/mech_version.rb'
 end
 
-desc &quot;Create a new release&quot;
-task :release =&gt; [ :clobber, :update_version, :package, :tag ] do
-  announce 
-  announce &quot;**************************************************************&quot;
-  announce &quot;* Release #{PKG_VERSION} Complete.&quot;
-  announce &quot;* Packages ready to upload.&quot;
-  announce &quot;**************************************************************&quot;
-  announce 
+MechHoe.new('mechanize', WWW::Mechanize::VERSION) do |p|
+  p.rubyforge_name  = 'mechanize'
+  p.author          = 'Aaron Patterson'
+  p.email           = 'aaronp@rubyforge.org'
+  p.summary         = &quot;Mechanize provides automated web-browsing&quot;
+  p.description     = p.paragraphs_of('README.txt', 3).join(&quot;\n\n&quot;)
+  p.url             = p.paragraphs_of('README.txt', 1).first.strip
+  p.changes         = p.paragraphs_of('CHANGELOG.txt', 0..2).join(&quot;\n\n&quot;)
+  p.extra_deps      = [['hpricot', '&gt;= 0.5.0']]
 end
 
-desc &quot;Tag code&quot;
-Rake::Task.define_task(&quot;tag&quot;) do |p|
-  baseurl = &quot;svn+ssh://#{ENV['USER']}@rubyforge.org//var/svn/#{PKG_NAME}&quot;
-  sh &quot;svn cp -m 'tagged #{ PKG_VERSION }' . #{ baseurl }/tags/REL-#{ PKG_VERSION }&quot;
-end
-
-desc &quot;Branch code&quot;
-Rake::Task.define_task(&quot;branch&quot;) do |p|
-  baseurl = &quot;svn+ssh://#{ENV['USER']}@rubyforge.org/var/svn/#{PKG_NAME}&quot;
-  sh &quot;svn cp -m 'branched #{ PKG_VERSION }' #{baseurl}/trunk #{ baseurl }/branches/RB-#{ PKG_VERSION }&quot;
-end
 
-desc &quot;Update SSL Certificate&quot;
-Rake::Task.define_task('ssl_cert') do |p|
-  sh &quot;openssl genrsa -des3 -out server.key 1024&quot;
-  sh &quot;openssl req -new -key server.key -out server.csr&quot;
-  sh &quot;cp server.key server.key.org&quot;
-  sh &quot;openssl rsa -in server.key.org -out server.key&quot;
-  sh &quot;openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt&quot;
-  sh &quot;cp server.key server.pem&quot;
-  sh &quot;mv server.key server.csr server.crt server.pem test/data/&quot;
-  sh &quot;rm server.key.org&quot;
-end</diff>
      <filename>Rakefile</filename>
    </modified>
    <modified>
      <diff>@@ -11,20 +11,31 @@
 unless RUBY_VERSION &gt; &quot;1.8.2&quot;
   $LOAD_PATH.unshift File.join(File.dirname(__FILE__), &quot;mechanize&quot;, &quot;net-overrides&quot;)
 end
+
 require 'net/http'
 require 'net/https'
 
+# Monkey patch for ruby 1.8.4
+unless RUBY_VERSION &gt; &quot;1.8.4&quot;
+module Net # :nodoc:
+  class HTTPResponse # :nodoc:
+    CODE_TO_OBJ['500'] = HTTPInternalServerError
+  end
+end
+end
+
 require 'uri'
 require 'webrick/httputils'
 require 'zlib'
 require 'stringio'
-require 'mechanize/hpricot'
-require 'mechanize/mech_version'
+require 'digest/md5'
+require 'mechanize/monkey_patch'
 require 'mechanize/cookie'
 require 'mechanize/errors'
 require 'mechanize/pluggable_parsers'
 require 'mechanize/form'
 require 'mechanize/form_elements'
+require 'mechanize/history'
 require 'mechanize/list'
 require 'mechanize/page'
 require 'mechanize/page_elements'
@@ -50,20 +61,27 @@ module WWW
 #  search_results = agent.submit(search_form)
 #  puts search_results.body
 class Mechanize
+  ##
+  # The version of Mechanize you are using.
+ 
+  VERSION = '0.6.11'
+
+  ##
+  # User Agent aliases
   AGENT_ALIASES = {
     'Windows IE 6' =&gt; 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)',
+    'Windows IE 7' =&gt; 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)',
     'Windows Mozilla' =&gt; 'Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.4b) Gecko/20030516 Mozilla Firebird/0.6',
     'Mac Safari' =&gt; 'Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/418 (KHTML, like Gecko) Safari/417.9.3',
     'Mac FireFox' =&gt; 'Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3',
     'Mac Mozilla' =&gt; 'Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.4a) Gecko/20030401',
     'Linux Mozilla' =&gt; 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4) Gecko/20030624',
     'Linux Konqueror' =&gt; 'Mozilla/5.0 (compatible; Konqueror/3; Linux)',
-    'Mechanize' =&gt; &quot;WWW-Mechanize/#{Version} (http://rubyforge.org/projects/mechanize/)&quot;
+    'Mechanize' =&gt; &quot;WWW-Mechanize/#{VERSION} (http://rubyforge.org/projects/mechanize/)&quot;
   }
 
   attr_accessor :cookie_jar
   attr_accessor :log
-  attr_accessor :max_history
   attr_accessor :open_timeout, :read_timeout
   attr_accessor :user_agent
   attr_accessor :watch_for_set
@@ -71,15 +89,24 @@ class Mechanize
   attr_accessor :key
   attr_accessor :cert
   attr_accessor :pass
+  attr_accessor :redirect_ok
+  attr_accessor :keep_alive_time
+  attr_accessor :keep_alive
+  attr_accessor :conditional_requests
+  attr_accessor :follow_meta_refresh
 
   attr_reader :history
   attr_reader :pluggable_parser
 
+  alias :follow_redirect? :redirect_ok
+
+  @@nonce_count = -1
+  CNONCE = Digest::MD5.hexdigest(&quot;%x&quot; % (Time.now.to_i + rand(65535)))
+
   def initialize
     # attr_accessors
-    @cookie_jar = CookieJar.new
+    @cookie_jar     = CookieJar.new
     @log            = nil
-    @max_history    = nil
     @open_timeout   = nil
     @read_timeout   = nil
     @user_agent     = AGENT_ALIASES['Mechanize']
@@ -88,14 +115,17 @@ class Mechanize
     @cert           = nil # OpenSSL Certificate
     @key            = nil # OpenSSL Private Key
     @pass           = nil # OpenSSL Password
+    @redirect_ok    = true # Should we follow redirects?
     
     # attr_readers
-    @history        = []
+    @history        = WWW::Mechanize::History.new
     @pluggable_parser = PluggableParser.new
 
-    # Basic Auth variables
-    @user           = nil # Basic Auth User
-    @password       = nil # Basic Auth Password
+    # Auth variables
+    @user           = nil # Auth User
+    @password       = nil # Auth Password
+    @digest         = nil # DigestAuth Digest
+    @auth_hash      = {}  # Keep track of urls for sending auth
 
     # Proxy settings
     @proxy_addr     = nil
@@ -103,9 +133,21 @@ class Mechanize
     @proxy_port     = nil
     @proxy_user     = nil
 
+    @conditional_requests = true
+
+    @follow_meta_refresh  = false
+
+    # Connection Cache &amp; Keep alive
+    @connection_cache = {}
+    @keep_alive_time  = 300
+    @keep_alive       = true
+
     yield self if block_given?
   end
 
+  def max_history=(length); @history.max_size = length; end
+  def max_history; @history.max_size; end
+
   # Sets the proxy address, port, user, and password
   def set_proxy(addr, port, user = nil, pass = nil)
     @proxy_addr, @proxy_port, @proxy_user, @proxy_pass = addr, port, user, pass
@@ -124,18 +166,23 @@ class Mechanize
 
   # Sets the user and password to be used for basic authentication.
   def basic_auth(user, password)
-    @user = user
-    @password = password
+    auth(user, password)
+  end
+
+  def auth(user, password)
+    @user       = user
+    @password   = password
   end
 
   # Fetches the URL passed in and returns a page.
-  def get(url)
-    cur_page = current_page || Page.new( nil, {'content-type'=&gt;'text/html'})
+  def get(url, referer=nil, &amp;block)
+    cur_page = referer || current_page ||
+                    Page.new( nil, {'content-type'=&gt;'text/html'})
 
     # fetch the page
     abs_uri = to_absolute_uri(url, cur_page)
     request = fetch_request(abs_uri)
-    page = fetch_page(abs_uri, request, cur_page)
+    page = fetch_page(abs_uri, request, cur_page, &amp;block)
     add_to_history(page)
     page
   end
@@ -149,10 +196,17 @@ class Mechanize
   # Clicks the WWW::Mechanize::Link object passed in and returns the
   # page fetched.
   def click(link)
+    referer =
+      begin
+        link.page
+      rescue
+        nil
+      end
     uri = to_absolute_uri(
-      link.attributes['href'] || link.attributes['src'] || link.href
+      link.attributes['href'] || link.attributes['src'] || link.href,
+      referer || current_page()
     )
-    get(uri)
+    get(uri, referer)
   end
 
   # Equivalent to the browser back button.  Returns the most recent page
@@ -169,9 +223,8 @@ class Mechanize
   #  agent.post('http://example.com/', [ [&quot;foo&quot;, &quot;bar&quot;] ])
   def post(url, query={})
     node = Hpricot::Elem.new(Hpricot::STag.new('form'))
-    node.attributes = {}
-    node.attributes['method'] = 'POST'
-    node.attributes['enctype'] = 'application/x-www-form-urlencoded'
+    node['method'] = 'POST'
+    node['enctype'] = 'application/x-www-form-urlencoded'
 
     form = Form.new(node)
     query.each { |k,v|
@@ -188,17 +241,12 @@ class Mechanize
   #  agent.submit(page.forms.first, page.forms.first.buttons.first)
   def submit(form, button=nil)
     form.add_button_to_query(button) if button
-    uri = to_absolute_uri(form.action)
+    uri = to_absolute_uri(form.action, form.page)
     case form.method.upcase
     when 'POST'
       post_form(uri, form) 
     when 'GET'
-      if uri.query.nil?
-        uri.query = WWW::Mechanize.build_query_string(form.build_query)
-      else
-        uri.query = uri.query + &quot;&amp;&quot; +
-          WWW::Mechanize.build_query_string(form.build_query)
-      end
+      uri.query = WWW::Mechanize.build_query_string(form.build_query)
       get(uri)
     else
       raise &quot;unsupported method: #{form.method.upcase}&quot;
@@ -212,9 +260,15 @@ class Mechanize
 
   # Returns whether or not a url has been visited
   def visited?(url)
-    url = url.uri if url.respond_to? :uri
-    uri = to_absolute_uri(url).to_s
-    ! @history.find { |h| h.uri.to_s == uri }.nil?
+    ! visited_page(url).nil?
+  end
+
+  # Returns a visited page for the url passed in, otherwise nil
+  def visited_page(url)
+    if url.respond_to? :href
+      url = url.href
+    end
+    @history.visited_page(to_absolute_uri(url))
   end
 
   # Runs given block, then resets the page history as it was before. self is
@@ -230,22 +284,136 @@ class Mechanize
 
   alias :page :current_page
 
+  protected
+  def set_headers(uri, request, cur_page)
+    if @keep_alive
+      request.add_field('Connection', 'keep-alive')
+      request.add_field('Keep-Alive', keep_alive_time.to_s)
+    else
+      request.add_field('Connection', 'close')
+    end
+    request.add_field('Accept-Encoding', 'gzip,identity')
+    request.add_field('Accept-Language', 'en-us,en;q0.5')
+    request.add_field('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.7')
+
+    unless @cookie_jar.empty?(uri)
+      cookies = @cookie_jar.cookies(uri)
+      cookie = cookies.length &gt; 0 ? cookies.join(&quot;; &quot;) : nil
+      if log
+        cookies.each do |c|
+          log.debug(&quot;using cookie: #{c}&quot;)
+        end
+      end
+      request.add_field('Cookie', cookie)
+    end
+
+    # Add Referer header to request
+    unless cur_page.uri.nil?
+      request.add_field('Referer', cur_page.uri.to_s)
+    end
+
+    # Add User-Agent header to request
+    request.add_field('User-Agent', @user_agent) if @user_agent 
+
+    # Add If-Modified-Since if page is in history
+    if @conditional_requests
+      if( (page = visited_page(uri)) &amp;&amp; page.response['Last-Modified'] )
+        request.add_field('If-Modified-Since', page.response['Last-Modified'])
+      end
+    end
+
+    if( @auth_hash[uri.host] )
+      case @auth_hash[uri.host]
+      when :basic
+        request.basic_auth(@user, @password)
+      when :digest
+        @digest_response ||= nil
+        @digest_response = self.gen_auth_header(uri,request,@digest) if @digest
+        request.add_field('Authorization', @digest_response) if @digest_response
+      end
+    end
+
+    request
+  end
+
+  def gen_auth_header(uri, request, auth_header, is_IIS = false)
+    @@nonce_count += 1
+
+    user = @digest_user
+    password = @digest_password
+
+    auth_header =~ /^(\w+) (.*)/
+
+    params = {}
+    $2.gsub(/(\w+)=&quot;(.*?)&quot;/) { params[$1] = $2 }
+
+    a_1 = &quot;#{@user}:#{params['realm']}:#{@password}&quot;
+    a_2 = &quot;#{request.method}:#{uri.path}&quot;
+    request_digest = ''
+    request_digest &lt;&lt; Digest::MD5.hexdigest(a_1)
+    request_digest &lt;&lt; ':' &lt;&lt; params['nonce']
+    request_digest &lt;&lt; ':' &lt;&lt; ('%08x' % @@nonce_count)
+    request_digest &lt;&lt; ':' &lt;&lt; CNONCE
+    request_digest &lt;&lt; ':' &lt;&lt; params['qop']
+    request_digest &lt;&lt; ':' &lt;&lt; Digest::MD5.hexdigest(a_2)
+
+    header = ''
+    header &lt;&lt; &quot;Digest username=\&quot;#{@user}\&quot;, &quot;
+    header &lt;&lt; &quot;realm=\&quot;#{params['realm']}\&quot;, &quot;
+    if is_IIS then
+      header &lt;&lt; &quot;qop=\&quot;#{params['qop']}\&quot;, &quot;
+    else
+      header &lt;&lt; &quot;qop=#{params['qop']}, &quot;
+    end
+    header &lt;&lt; &quot;uri=\&quot;#{uri.path}\&quot;, &quot;
+    header &lt;&lt; &quot;algorithm=MD5, &quot;
+    header &lt;&lt; &quot;nonce=\&quot;#{params['nonce']}\&quot;, &quot;
+    header &lt;&lt; &quot;nc=#{'%08x' % @@nonce_count}, &quot;
+    header &lt;&lt; &quot;cnonce=\&quot;#{CNONCE}\&quot;, &quot;
+    header &lt;&lt; &quot;response=\&quot;#{Digest::MD5.hexdigest(request_digest)}\&quot;&quot;
+
+    return header
+  end
+
   private
 
   def to_absolute_uri(url, cur_page=current_page())
-    url =  URI.parse(URI.escape(url.to_s.strip)) unless url.is_a? URI
+    unless url.is_a? URI
+      url = url.to_s.strip.gsub(/[^#{0.chr}-#{125.chr}]/) { |match|
+        sprintf('%%%X', match.unpack($KCODE == 'UTF8' ? 'U' : 'c')[0])
+      }
+
+      url = URI.parse(
+              Util.html_unescape(
+                url.split(/%[0-9A-Fa-f]{2}|#/).zip(
+                  url.scan(/%[0-9A-Fa-f]{2}|#/)
+                ).map { |x,y|
+                  &quot;#{URI.escape(x)}#{y}&quot;
+                }.join('')
+              )
+            )
+    end
+
+    url.path = '/' if url.path.length == 0
 
     # construct an absolute uri
     if url.relative?
       raise 'no history. please specify an absolute URL' unless cur_page.uri
+      base = cur_page.respond_to?(:bases) ? cur_page.bases.last : nil
+      url = ((base &amp;&amp; base.uri &amp;&amp; base.uri.absolute?) ?
+              base.uri :
+              cur_page.uri) + url
       url = cur_page.uri + url
+      # Strip initial &quot;/..&quot; bits from the path
+      url.path.sub!(/^(\/\.\.)+(?=\/)/, '')
     end
 
     return url
   end
 
   def post_form(url, form)
-    cur_page = current_page || Page.new(nil, {'content-type'=&gt;'text/html'})
+    cur_page = form.page || current_page ||
+                    Page.new( nil, {'content-type'=&gt;'text/html'})
 
     request_data = form.request_data
 
@@ -276,19 +444,32 @@ class Mechanize
   def fetch_page(uri, request, cur_page=current_page(), request_data=[])
     raise &quot;unsupported scheme&quot; unless ['http', 'https'].include?(uri.scheme)
 
-    log.info(&quot;#{ request.class }: #{ uri.to_s }&quot;) if log
+    log.info(&quot;#{ request.class }: #{ request.path }&quot;) if log
 
     page = nil
 
-    http_obj = Net::HTTP.new( uri.host,
-                          uri.port,
-                          @proxy_addr,
-                          @proxy_port,
-                          @proxy_user,
-                          @proxy_pass
-                        )
+    cache_obj = (@connection_cache[&quot;#{uri.host}:#{uri.port}&quot;] ||= {
+      :connection         =&gt; nil,
+      :keep_alive_options =&gt; {},
+    })
+    http_obj = cache_obj[:connection]
+    if http_obj.nil? || ! http_obj.started?
+      http_obj = cache_obj[:connection] =
+          Net::HTTP.new( uri.host,
+                  uri.port,
+                  @proxy_addr,
+                  @proxy_port,
+                  @proxy_user,
+                  @proxy_pass
+                )
+      cache_obj[:keep_alive_options] = {}
+
+      # Specify timeouts if given
+      http_obj.open_timeout = @open_timeout if @open_timeout
+      http_obj.read_timeout = @read_timeout if @read_timeout
+    end
 
-    if uri.scheme == 'https'
+    if uri.scheme == 'https' &amp;&amp; ! http_obj.started?
       http_obj.use_ssl = true
       http_obj.verify_mode = OpenSSL::SSL::VERIFY_NONE
       if @ca_file
@@ -301,28 +482,24 @@ class Mechanize
       end
     end
 
-    request.add_field('Accept-Encoding', 'gzip,identity')
+    # If we're keeping connections alive and the last request time is too
+    # long ago, stop the connection.  Or, if the max requests left is 1,
+    # reset the connection.
+    if @keep_alive &amp;&amp; http_obj.started?
+      opts = cache_obj[:keep_alive_options]
+      if((opts[:timeout] &amp;&amp;
+         Time.now.to_i - cache_obj[:last_request_time] &gt; opts[:timeout].to_i) ||
+          opts[:max] &amp;&amp; opts[:max].to_i == 1)
 
-    unless @cookie_jar.empty?(uri)
-      cookies = @cookie_jar.cookies(uri)
-      cookie = cookies.length &gt; 0 ? cookies.join(&quot;; &quot;) : nil
-      if log
-        cookies.each do |c|
-          log.debug(&quot;using cookie: #{c}&quot;)
-        end
-      end
-      request.add_field('Cookie', cookie)
-    end
+        log.debug('Finishing stale connection') if log
+        http_obj.finish
 
-    # Add Referer header to request
-    unless cur_page.uri.nil?
-      request.add_field('Referer', cur_page.uri.to_s)
+      end
     end
 
-    # Add User-Agent header to request
-    request.add_field('User-Agent', @user_agent) if @user_agent 
+    http_obj.start unless http_obj.started?
 
-    request.basic_auth(@user, @password) if @user || @password
+    request = set_headers(uri, request, cur_page)
 
     # Log specified headers for the request
     if log
@@ -331,76 +508,115 @@ class Mechanize
       end
     end
 
-    http_obj.start { |http|
-      # Specify timeouts if given
-      http.open_timeout = @open_timeout if @open_timeout
-      http.read_timeout = @read_timeout if @read_timeout
-
-      # Send the request
-      http.request(request, *request_data) {|response|
-        if log
-          response.each_header {|k,v|
-            log.debug(&quot;response-header: #{ k } =&gt; #{ v }&quot;)
-          }
-        end
+    cache_obj[:last_request_time] = Time.now.to_i
 
-        (response.get_fields('Set-Cookie')||[]).each do |cookie|
-          Cookie::parse(uri, cookie) { |c|
-            log.debug(&quot;saved cookie: #{c}&quot;) if log
-            @cookie_jar.add(uri, c)
-          }
-        end
+    # Send the request
+    response = http_obj.request(request, *request_data) {|response|
+
+      body = StringIO.new
+      total = 0
+      response.read_body { |part|
+        total += part.length
+        body.write(part)
+        log.debug(&quot;Read #{total} bytes&quot;) if log
+      }
+      body.rewind
 
-        response.read_body
+      response.each_header { |k,v|
+        log.debug(&quot;response-header: #{ k } =&gt; #{ v }&quot;)
+      } if log
 
-        content_type = nil
-        unless response['Content-Type'].nil?
-          data = response['Content-Type'].match(/^([^;]*)/)
-          content_type = data[1].downcase unless data.nil?
-        end
+      content_type = nil
+      unless response['Content-Type'].nil?
+        data = response['Content-Type'].match(/^([^;]*)/)
+        content_type = data[1].downcase unless data.nil?
+      end
 
-        response_body = 
-        if encoding = response['Content-Encoding']
-          case encoding.downcase
-          when 'gzip'
-            log.debug('gunzip body') if log
-            Zlib::GzipReader.new(StringIO.new(response.body)).read
-          else
-            raise 'Unsupported content encoding'
-          end
+      response_body = 
+      if encoding = response['Content-Encoding']
+        case encoding.downcase
+        when 'gzip'
+          log.debug('gunzip body') if log
+          Zlib::GzipReader.new(body).read
+        when 'x-gzip'
+          body.read
         else
-          response.body
+          raise 'Unsupported content encoding'
         end
+      else
+        body.read
+      end
 
-        # Find our pluggable parser
-        page = @pluggable_parser.parser(content_type).new(
-          uri,
-          response,
-          response_body,
-          response.code
-        )
+      # Find our pluggable parser
+      page = @pluggable_parser.parser(content_type).new(
+        uri,
+        response,
+        response_body,
+        response.code
+      ) { |parser|
+        parser.mech = self if parser.respond_to? :mech=
+        if parser.respond_to?(:watch_for_set=) &amp;&amp; @watch_for_set
+          parser.watch_for_set = @watch_for_set
+        end
+      }
 
-        log.info(&quot;status: #{ page.code }&quot;) if log
+    }
 
-        if page.respond_to? :watch_for_set
-          page.watch_for_set = @watch_for_set
-        end
+    # If the server sends back keep alive options, save them
+    if keep_alive_info = response['keep-alive']
+      keep_alive_info.split(/,\s*/).each do |option|
+        k, v = option.split(/=/)
+        cache_obj[:keep_alive_options] ||= {}
+        cache_obj[:keep_alive_options][k.intern] = v
+      end
+    end
 
-        case page.code
-        when &quot;200&quot;
-          return page
-        when &quot;301&quot;, &quot;302&quot;
-          log.info(&quot;follow redirect to: #{ response['Location'] }&quot;) if log
-          abs_uri = to_absolute_uri(
-            URI.parse(
-              URI.escape(URI.unescape(response['Location'].to_s))), page)
-          request = fetch_request(abs_uri)
-          return fetch_page(abs_uri, request, page)
-        else
-          raise ResponseCodeError.new(page.code), &quot;Unhandled response&quot;, caller
-        end
+    (response.get_fields('Set-Cookie')||[]).each do |cookie|
+      Cookie::parse(uri, cookie, log) { |c|
+        log.debug(&quot;saved cookie: #{c}&quot;) if log
+        @cookie_jar.add(uri, c)
       }
-    }
+    end
+
+    log.info(&quot;status: #{ page.code }&quot;) if log
+
+    res_klass = Net::HTTPResponse::CODE_TO_OBJ[page.code.to_s]
+
+    if follow_meta_refresh &amp;&amp; page.respond_to?(:meta) &amp;&amp;
+      (redirect = page.meta.first)
+      return redirect.click
+    end
+
+    return page if res_klass &lt;= Net::HTTPSuccess
+
+    if res_klass == Net::HTTPNotModified
+      log.debug(&quot;Got cached page&quot;) if log
+      return visited_page(uri)
+    elsif res_klass &lt;= Net::HTTPRedirection
+      return page unless follow_redirect?
+      log.info(&quot;follow redirect to: #{ response['Location'] }&quot;) if log
+      from_uri  = page.uri
+      abs_uri   = to_absolute_uri(response['Location'].to_s, page)
+      page = fetch_page(abs_uri, fetch_request(abs_uri), page)
+      @history.push(page, from_uri)
+      return page
+    elsif res_klass &lt;= Net::HTTPUnauthorized
+      raise ResponseCodeError.new(page) unless @user || @password
+      raise ResponseCodeError.new(page) if @auth_hash.has_key?(uri.host)
+      if response['www-authenticate'] =~ /Digest/i
+        @auth_hash[uri.host] = :digest
+        @digest = response['www-authenticate']
+      else
+        @auth_hash[uri.host] = :basic
+      end
+      return fetch_page(  uri,
+                          fetch_request(uri, request.method.downcase.to_sym),
+                          cur_page,
+                          request_data
+                       )
+    end
+
+    raise ResponseCodeError.new(page), &quot;Unhandled response&quot;, caller
   end
 
   def self.build_query_string(parameters)
@@ -416,12 +632,26 @@ class Mechanize
   end
 
   def add_to_history(page)
-    @history.push(page)
-    if @max_history and @history.size &gt; @max_history
-      # keep only the last @max_history entries
-      @history = @history[@history.size - @max_history, @max_history] 
+    @history.push(page, to_absolute_uri(page.uri))
+  end
+
+  # :stopdoc:
+  class Util
+    def self.html_unescape(s)
+      return s unless s
+      s.gsub(/&amp;(\w+|#[0-9]+);/) { |match|
+        number = case match
+        when /&amp;(\w+);/
+          Hpricot::NamedCharacters[$1]
+        when /&amp;#([0-9]+);/
+          $1.to_i
+        end
+
+        number ? ([number].pack('U') rescue match) : match
+      }
     end
   end
+  # :startdoc:
 end
 
 end # module WWW</diff>
      <filename>lib/mechanize.rb</filename>
    </modified>
    <modified>
      <diff>@@ -6,9 +6,8 @@ module WWW
   class Mechanize
   # This class is used to represent an HTTP Cookie.
     class Cookie &lt; WEBrick::Cookie
-      def self.parse(uri, str)
-        cookies = []
-        str.gsub(/(,([^;,]*=)|,$)/) { &quot;\r\n#{$2}&quot; }.split(/\r\n/).each { |c|
+      def self.parse(uri, str, log = nil)
+        return str.split(/,(?=[^;,]*=)|,$/).collect { |c|
           cookie_elem = c.split(/;/)
           first_elem = cookie_elem.shift
           first_elem.strip!
@@ -24,25 +23,38 @@ module WWW
             when &quot;domain&quot;  then cookie.domain  = value.sub(/^\./, '')
             when &quot;path&quot;    then cookie.path    = value
             when 'expires'
-              cookie.expires = begin
-                Time::parse(value)
+              begin
+                cookie.expires = Time::parse(value)
+              rescue
+                if log
+                  log.warn(&quot;Couldn't parse expires: #{value}&quot;)
+                end
+              end
+            when &quot;max-age&quot; then
+              begin
+                cookie.max_age = Integer(value)
               rescue
-                Time.now
+                log.warn(&quot;Couldn't parse max age '#{value}'&quot;) if log
+                cookie.max_age = nil
               end
-            when &quot;max-age&quot; then cookie.max_age = Integer(value)
             when &quot;comment&quot; then cookie.comment = value
-            when &quot;version&quot; then cookie.version = Integer(value)
+            when &quot;version&quot; then
+              begin
+                cookie.version = Integer(value)
+              rescue
+                log.warn(&quot;Couldn't parse version '#{value}'&quot;) if log
+                cookie.version = nil
+              end
             when &quot;secure&quot;  then cookie.secure = true
             end
           }
-          cookie.path    ||= uri.path
+
+          cookie.path    ||= uri.path.to_s.sub(/[^\/]*$/, '')
           cookie.secure  ||= false
           cookie.domain  ||= uri.host
           # Move this in to the cookie jar
           yield cookie if block_given?
-          cookies &lt;&lt; cookie
         }
-        return cookies
       end
     
       def to_s
@@ -61,12 +73,13 @@ module WWW
     
       # Add a cookie to the Jar.
       def add(uri, cookie)
-        return unless uri.host =~ /#{cookie.domain}$/
-        unless @jar.has_key?(cookie.domain)
-          @jar[cookie.domain] = Hash.new
+        return unless uri.host =~ /#{cookie.domain}$/i
+        normal_domain = cookie.domain.downcase
+        unless @jar.has_key?(normal_domain)
+          @jar[normal_domain] = Hash.new
         end
     
-        @jar[cookie.domain][cookie.name] = cookie
+        @jar[normal_domain][cookie.name] = cookie
         cleanup()
         cookie
       end
@@ -77,7 +90,7 @@ module WWW
         cookies = []
         url.path = '/' if url.path.empty?
         @jar.each_key do |domain|
-          if url.host =~ /#{domain}$/
+          if url.host =~ /#{domain}$/i
             @jar[domain].each_key do |name|
               if url.path =~ /^#{@jar[domain][name].path}/
                 if @jar[domain][name].expires.nil?</diff>
      <filename>lib/mechanize/cookie.rb</filename>
    </modified>
    <modified>
      <diff>@@ -20,10 +20,18 @@ module WWW
     # Any other response code is up to the user to handle.
     class ResponseCodeError &lt; RuntimeError
       attr_reader :response_code
+      attr_reader :page
     
-      def initialize(response_code)
-        @response_code = response_code
+      def initialize(page)
+        @page          = page
+        @response_code = page.code
       end
+
+      def to_s
+        &quot;#{response_code} =&gt; #{Net::HTTPResponse::CODE_TO_OBJ[response_code]}&quot;
+      end
+
+      def inspect; to_s; end
     end
   end
 end</diff>
      <filename>lib/mechanize/errors.rb</filename>
    </modified>
    <modified>
      <diff>@@ -26,15 +26,16 @@ module WWW
     
       attr_reader :fields, :buttons, :file_uploads, :radiobuttons, :checkboxes
       attr_reader :enctype
+
+      alias :elements :fields
     
       def initialize(form_node, elements_node)
         @form_node, @elements_node = form_node, elements_node
     
-        @form_node.attributes ||= {}
-        @method = (@form_node.attributes['method'] || 'GET').upcase
-        @action = @form_node.attributes['action'] 
-        @name = @form_node.attributes['name']
-        @enctype = @form_node.attributes['enctype'] || 'application/x-www-form-urlencoded'
+        @method = (@form_node['method'] || 'GET').upcase
+        @action = Util::html_unescape(@form_node['action'])
+        @name = @form_node['name']
+        @enctype = @form_node['enctype'] || 'application/x-www-form-urlencoded'
         @clicked_buttons = []
     
         parse
@@ -47,7 +48,6 @@ module WWW
         query = []
     
         fields().each do |f|
-          next unless f.value
           query.push(*f.query_value)
         end
     
@@ -105,6 +105,11 @@ module WWW
         end
       end
     
+      # Removes all fields with name +field_name+. 
+      def delete_field!(field_name)
+        @fields.delete_if{ |f| f.name == field_name}
+      end
+          
       private
       def parse
         @fields       = WWW::Mechanize::List.new
@@ -115,41 +120,40 @@ module WWW
     
         # Find all input tags
         (@elements_node/'input').each do |node|
-          node.attributes ||= {}
-          type = (node.attributes['type'] || 'text').downcase
-          name = node.attributes['name']
-          next if type != 'submit' &amp;&amp; name.nil?
+          type = (node['type'] || 'text').downcase
+          name = node['name']
+          next if name.nil? &amp;&amp; !(type == 'submit' || type =='button')
           case type
           when 'text', 'password', 'hidden', 'int'
-            @fields &lt;&lt; Field.new(node.attributes['name'], node.attributes['value'] || '') 
+            @fields &lt;&lt; Field.new(node['name'], node['value'] || '') 
           when 'radio'
-            @radiobuttons &lt;&lt; RadioButton.new(node.attributes['name'], node.attributes['value'], node.attributes.has_key?('checked'), self)
+            @radiobuttons &lt;&lt; RadioButton.new(node['name'], node['value'], node.has_attribute?('checked'), self)
           when 'checkbox'
-            @checkboxes &lt;&lt; CheckBox.new(node.attributes['name'], node.attributes['value'], node.attributes.has_key?('checked'), self)
+            @checkboxes &lt;&lt; CheckBox.new(node['name'], node['value'], node.has_attribute?('checked'), self)
           when 'file'
-            @file_uploads &lt;&lt; FileUpload.new(node.attributes['name'], nil) 
+            @file_uploads &lt;&lt; FileUpload.new(node['name'], nil) 
           when 'submit'
-            @buttons &lt;&lt; Button.new(node.attributes['name'], node.attributes['value'])
+            @buttons &lt;&lt; Button.new(node['name'], node['value'])
+          when 'button'
+            @buttons &lt;&lt; Button.new(node['name'], node['value'])
           when 'image'
-            @buttons &lt;&lt; ImageButton.new(node.attributes['name'], node.attributes['value'])
+            @buttons &lt;&lt; ImageButton.new(node['name'], node['value'])
           end
         end
 
         # Find all textarea tags
         (@elements_node/'textarea').each do |node|
-          next if node.attributes.nil?
-          next if node.attributes['name'].nil?
-          @fields &lt;&lt; Field.new(node.attributes['name'], node.all_text)
+          next if node['name'].nil?
+          @fields &lt;&lt; Field.new(node['name'], node.inner_text)
         end
 
         # Find all select tags
         (@elements_node/'select').each do |node|
-          next if node.attributes.nil?
-          next if node.attributes['name'].nil?
-          if node.attributes.has_key? 'multiple'
-            @fields &lt;&lt; MultiSelectList.new(node.attributes['name'], node)
+          next if node['name'].nil?
+          if node.has_attribute? 'multiple'
+            @fields &lt;&lt; MultiSelectList.new(node['name'], node)
           else
-            @fields &lt;&lt; SelectList.new(node.attributes['name'], node)
+            @fields &lt;&lt; SelectList.new(node['name'], node)
           end
         end
       end
@@ -172,9 +176,10 @@ module WWW
       end
     
       def file_to_multipart(file)
+        file_name = file.file_name ? ::File.basename(file.file_name) : ''
         body =  &quot;Content-Disposition: form-data; name=\&quot;&quot; +
                 &quot;#{mime_value_quote(file.name)}\&quot;; &quot; +
-                &quot;filename=\&quot;#{mime_value_quote(file.file_name || '')}\&quot;\r\n&quot; +
+                &quot;filename=\&quot;#{mime_value_quote(file_name)}\&quot;\r\n&quot; +
                 &quot;Content-Transfer-Encoding: binary\r\n&quot;
 
         if file.file_data.nil? and ! file.file_name.nil?
@@ -212,17 +217,39 @@ module WWW
     #  puts form['name']
     class Form &lt; GlobalForm
       attr_reader :node
+      attr_reader :page
     
-      def initialize(node)
-        @node = node
-        super(@node, @node)
+      def initialize(node, mech=nil, page=nil)
+        super(node, node)
+        @page = page
+        @mech = mech
+      end
+
+      # Returns whether or not the form contains a field with +field_name+
+      def has_field?(field_name)
+        ! fields.find { |f| f.name.eql? field_name }.nil?
       end
 
-      # Fetch the first field whose name is equal to field_name
+      alias :has_key? :has_field?
+
+      def has_value?(value)
+        ! fields.find { |f| f.value.eql? value }.nil?
+      end
+
+      def keys; fields.map { |f| f.name }; end
+
+      def values; fields.map { |f| f.value }; end
+
+      # Fetch the first field whose name is equal to +field_name+
       def field(field_name)
         fields.find { |f| f.name.eql? field_name }
       end
 
+      # Add a field with +field_name+ and +value+
+      def add_field!(field_name, value = nil)
+        fields &lt;&lt; WWW::Mechanize::Field.new(field_name, value)
+      end
+
       # This method sets multiple fields on the form.  It takes a list of field
       # name, value pairs.  If there is more than one field found with the
       # same name, this method will set the first one found.  If you want to
@@ -248,7 +275,8 @@ module WWW
       # Fetch the value set in the input field 'name'
       #  puts form['name']
       def [](field_name)
-        field(field_name).value
+        f = field(field_name)
+        f &amp;&amp; f.value
       end
 
       # Set the value of the first input field with the name passed in
@@ -256,7 +284,12 @@ module WWW
       # Set the value in the input field 'name' to &quot;Aaron&quot;
       #  form['name'] = 'Aaron'
       def []=(field_name, value)
-        field(field_name).value = value
+        f = field(field_name)
+        if f.nil?
+          add_field!(field_name, value)
+        else
+          f.value = value
+        end
       end
 
       # Treat form fields like accessors.
@@ -268,6 +301,11 @@ module WWW
         end
         super
       end
+
+      # Submit this form with the button passed in
+      def submit(button=nil)
+        @mech.submit(self, button)
+      end
     end
   end
 end</diff>
      <filename>lib/mechanize/form.rb</filename>
    </modified>
    <modified>
      <diff>@@ -10,7 +10,12 @@ module WWW
     attr_accessor :name, :value
   
     def initialize(name, value)
-      @name, @value = name, value
+      @name = Util.html_unescape(name)
+      @value = if value.is_a? String
+                 Util.html_unescape(value)
+               else
+                 value
+               end
     end
   
     def query_value
@@ -22,9 +27,8 @@ module WWW
   # class, set WWW::FileUpload#file_data= to the data of the file you want
   # to upload and WWW::FileUpload#mime_type= to the appropriate mime type
   # of the file.
-  # See the example in EXAMPLES[link://files/EXAMPLES.html]
+  # See the example in EXAMPLES[link://files/EXAMPLES_txt.html]
   class FileUpload &lt; Field
-    attr_accessor :name # Field name
     attr_accessor :file_name # File name
     attr_accessor :mime_type # Mime Type (Optional)
     
@@ -32,7 +36,7 @@ module WWW
     alias :file_data= :value=
   
     def initialize(name, file_name)
-      @file_name = file_name
+      @file_name = Util.html_unescape(file_name)
       @file_data = nil
       super(name, @file_data)
     end
@@ -125,7 +129,7 @@ module WWW
     end
 
     def query_value
-      value.collect { |v| [name, v] }
+      value ? value.collect { |v| [name, v] } : ''
     end
 
     # Select no options
@@ -213,9 +217,9 @@ module WWW
     alias :selected? :selected
 
     def initialize(node, select_list)
-      @text     = node.all_text
-      @value    = node.attributes['value']
-      @selected = node.attributes.has_key?('selected') ? true : false
+      @text     = node.inner_text
+      @value    = Util.html_unescape(node['value'])
+      @selected = node.has_attribute? 'selected'
       @select_list = select_list # The select list this option belongs to
     end
 </diff>
      <filename>lib/mechanize/form_elements.rb</filename>
    </modified>
    <modified>
      <diff>@@ -40,6 +40,9 @@ module WWW
           }
         }
       end
+      if RUBY_VERSION &gt; '1.8.4'
+        alias :inspect  :pretty_inspect
+      end
     end
 
     class Link
@@ -49,6 +52,9 @@ module WWW
           q.breakable; q.pp href
         }
       end
+      if RUBY_VERSION &gt; '1.8.4'
+        alias :inspect  :pretty_inspect
+      end
     end
 
     class Form</diff>
      <filename>lib/mechanize/inspect.rb</filename>
    </modified>
    <modified>
      <diff>@@ -54,12 +54,16 @@ module WWW
       alias :and :with
 
       def method_missing(meth_sym, *args)
-        return first.send(meth_sym) if args.empty?
-        arg = args.first
-        if arg.class == Regexp
-          WWW::Mechanize::List.new(find_all { |e| e.send(meth_sym) =~ arg })
+        if length &gt; 0
+          return first.send(meth_sym) if args.empty?
+          arg = args.first
+          if arg.class == Regexp
+            WWW::Mechanize::List.new(find_all { |e| e.send(meth_sym) =~ arg })
+          else
+            WWW::Mechanize::List.new(find_all { |e| e.send(meth_sym) == arg })
+          end
         else
-          WWW::Mechanize::List.new(find_all { |e| e.send(meth_sym) == arg })
+          ''
         end
       end
     end</diff>
      <filename>lib/mechanize/list.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-#
+# :enddoc:
 # = net/http.rb
 #
 # Copyright (C) 1999-2005 Yukihiro Matsumoto
@@ -1826,7 +1826,7 @@ module Net # :nodoc:
       '416' =&gt; HTTPRequestedRangeNotSatisfiable,
       '417' =&gt; HTTPExpectationFailed,
 
-      '501' =&gt; HTTPInternalServerError,
+      '500' =&gt; HTTPInternalServerError,
       '501' =&gt; HTTPNotImplemented,
       '502' =&gt; HTTPBadGateway,
       '503' =&gt; HTTPServiceUnavailable,</diff>
      <filename>lib/mechanize/net-overrides/net/http.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,3 +1,4 @@
+# :enddoc:
 =begin
 
 = $RCSfile: https.rb,v $ -- SSL/TLS enhancement for Net::HTTP.</diff>
      <filename>lib/mechanize/net-overrides/net/https.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-#
+# :enddoc:
 # = net/protocol.rb
 #
 #--</diff>
      <filename>lib/mechanize/net-overrides/net/protocol.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,6 @@
 require 'fileutils'
 require 'hpricot'
+require 'forwardable'
 
 module WWW
   class Mechanize
@@ -15,23 +16,27 @@ module WWW
     #  agent.get('http://google.com/').class  #=&gt; WWW::Mechanize::Page
     #
     class Page &lt; File
-      attr_reader :root, :title, :watch_for_set
-      attr_reader :frames, :iframes, :links, :forms, :meta, :watches
+      extend Forwardable
 
-      def initialize(uri=nil, response=nil, body=nil, code=nil)
-        super(uri, response, body, code)
-        @watch_for_set = {}
+      attr_reader :parser, :title, :watch_for_set
+      attr_reader :frames, :iframes, :links, :forms, :meta, :watches, :bases
+      attr_accessor :mech
+
+      alias :root :parser
 
-        yield self if block_given?
+      def initialize(uri=nil, response=nil, body=nil, code=nil, mech=nil)
+        super(uri, response, body, code)
+        @watch_for_set  ||= {}
+        @mech           ||= mech
 
         raise Mechanize::ContentTypeError.new(response['content-type']) unless
             content_type() =~ /^text\/html/ 
-        parse_html if body &amp;&amp; response
-      end
-    
-      # Get the response header
-      def header
-        @response
+
+        # construct parser and feed with HTML
+        if body &amp;&amp; response
+          @parser ||= Hpricot.parse(body)
+          parse_html
+        end
       end
     
       # Get the content type
@@ -40,84 +45,88 @@ module WWW
       end
 
       # Search through the page like HPricot
-      def search(*args)
-        @root.search(*args)
-      end
+      def_delegator :@parser, :search, :search
+      def_delegator :@parser, :/, :/
+      def_delegator :@parser, :at, :at
 
-      def at(*args)
-        @root.at(*args)
-      end
-
-      alias :/ :search
-    
       def watch_for_set=(obj)
         @watch_for_set = obj
-        parse_html if @body
+        parse_html if @body &amp;&amp; @watch_for_set
       end
 
+      # Find a form with +name+.  Form will be yeilded if a block is given.
       def form(name)
-        forms.name(name).first
+        f = forms.name(name).first
+        yield f if block_given?
+        f
       end
     
       private
     
       def parse_html
-        # construct parser and feed with HTML
-        @root = Hpricot.parse(@body)
-    
         @forms    = WWW::Mechanize::List.new
         @links    = WWW::Mechanize::List.new
         @meta     = WWW::Mechanize::List.new
         @frames   = WWW::Mechanize::List.new
         @iframes  = WWW::Mechanize::List.new
+        @bases    = WWW::Mechanize::List.new
         @watches  = {}
     
         # Set the title
-        @title = if (@root/'title').text.length &gt; 0
-          (@root/'title').text
+        @title = if (@parser/'title').text.length &gt; 0
+          (@parser/'title').text
+        end
+
+        # Find all 'base' tags
+        (@parser/'base').each do |node|
+          @bases &lt;&lt; Base.new(node, @mech, self)
         end
 
         # Find all the form tags
-        (@root/'form').each do |html_form|
-          form = Form.new(html_form)
+        (@parser/'form').each do |html_form|
+          form = Form.new(html_form, @mech, self)
           form.action ||= @uri
           @forms &lt;&lt; form
         end
 
         # Find all the 'a' tags
-        (@root/'a').each do |node|
-          @links &lt;&lt; Link.new(node)
+        (@parser/'a').each do |node|
+          @links &lt;&lt; Link.new(node, @mech, self)
+        end
+
+        # Find all the 'area' tags
+        (@parser/'area').each do |node|
+          @links &lt;&lt; Link.new(node, @mech, self)
         end
 
         # Find all 'meta' tags
-        (@root/'meta').each do |node|
-          next if node.attributes.nil?
-          next unless node.attributes.has_key? 'http-equiv'
-          next unless node.attributes.has_key? 'content'
-          equiv   = node.attributes['http-equiv']
-          content = node.attributes['content']
+        (@parser/'meta').each do |node|
+          next unless node['http-equiv']
+          next unless node['content']
+          equiv   = node['http-equiv']
+          content = node['content']
           if equiv != nil &amp;&amp; equiv.downcase == 'refresh'
-            if content != nil &amp;&amp; content =~ /^\d+\s*;\s*url\s*=\s*(\S+)/i
-              node.attributes['href'] = $1
-              @meta &lt;&lt; Meta.new(node)
+            if content != nil &amp;&amp; content =~ /^\d+\s*;\s*url\s*=\s*'?([^\s']+)/i
+              node['href'] = $1
+              @meta &lt;&lt; Meta.new(node, @mech, self)
             end
           end
         end
 
         # Find all 'frame' tags
-        (@root/'frame').each do |node|
-          @frames &lt;&lt; Frame.new(node)
+        (@parser/'frame').each do |node|
+          @frames &lt;&lt; Frame.new(node, @mech, self)
         end
 
         # Find all 'iframe' tags
-        (@root/'iframe').each do |node|
-          @iframes &lt;&lt; Frame.new(node)
+        (@parser/'iframe').each do |node|
+          @iframes &lt;&lt; Frame.new(node, @mech, self)
         end
 
         # Find all watch tags
         unless @watch_for_set.nil?
           @watch_for_set.each do |key, klass|
-            (@root/key).each do |node|
+            (@parser/key).each do |node|
               @watches[key] ||= []
               @watches[key] &lt;&lt; (klass ? klass.new(node) : node)
             end</diff>
      <filename>lib/mechanize/page.rb</filename>
    </modified>
    <modified>
      <diff>@@ -13,21 +13,23 @@ module WWW
       attr_reader :href
       attr_reader :text
       attr_reader :attributes
+      attr_reader :page
       alias :to_s :text
+      alias :referer :page
     
-      def initialize(node)
-        node.attributes ||= {}
+      def initialize(node, mech, page)
         @node = node
-        @href = node.attributes['href'] 
-        @text = node.all_text
-        @attributes = node.attributes
+        @href = node['href'] 
+        @text = node.inner_text
+        @page = page
+        @mech = mech
+        @attributes = node
 
         # If there is no text, try to find an image and use it's alt text
         if (@text.nil? || @text.length == 0) &amp;&amp; (node/'img').length &gt; 0
           @text = ''
           (node/'img').each do |e|
-            e.attributes ||= {}
-            @text &lt;&lt; (e.attributes.has_key?('alt') ? e.attributes['alt'] : '')
+            @text &lt;&lt; ( e['alt'] || '')
           end
         end
 
@@ -36,6 +38,11 @@ module WWW
       def uri
         URI.parse(@href)
       end
+
+      # Click on this link
+      def click
+        @mech.click self
+      end
     end
     
     # This class encapsulates a Meta tag.  Mechanize treats meta tags just
@@ -53,12 +60,18 @@ module WWW
       alias :src :href
       alias :name :text
 
-      def initialize(node)
-        node.attributes ||= {}
+      def initialize(node, mech, referer)
+        super(node, mech, referer)
         @node = node
-        @text = node.attributes['name']
-        @href = node.attributes['src']
+        @text = node['name']
+        @href = node['src']
       end
     end
+
+    # This class encapsulates a Base tag.  Mechanize treats base tags just like
+    # 'a' tags.  Base objects will contain links, but most likely will have
+    # no text.
+    class Base &lt; Link
+    end
   end
 end</diff>
      <filename>lib/mechanize/page_elements.rb</filename>
    </modified>
    <modified>
      <diff>@@ -17,20 +17,57 @@ module WWW
     #  agent.get('http://example.com/foo.jpg').class  #=&gt; WWW::Mechanize::File
     #
     class File
-      attr_accessor :uri, :response, :body, :code
+      attr_accessor :uri, :response, :body, :code, :filename
+      alias :header :response
 
       alias :content :body
 
       def initialize(uri=nil, response=nil, body=nil, code=nil)
-        @uri, @response, @body, @code = uri, response, body, code
+        @uri, @body, @code = uri, body, code
+        @response = Headers.new
+
+        # Copy the headers in to a hash to prevent memory leaks
+        if response
+          response.each { |k,v|
+            @response[k] = v
+          }
+        end
+
+        @filename = 'index.html'
+
+        # Set the filename
+        if disposition = @response['content-disposition']
+          disposition.split(/;\s*/).each do |pair|
+            k,v = pair.split(/=/, 2)
+            @filename = v if k.downcase == 'filename'
+          end
+        else
+          if @uri
+            @filename = @uri.path.split(/\//).last || 'index.html'
+            @filename &lt;&lt; &quot;.html&quot; unless @filename =~ /\./
+          end
+        end
+
+        yield self if block_given?
       end
 
       # Use this method to save the content of this object to filename
-      def save_as(filename)
+      def save_as(filename = nil)
+        if filename.nil?
+          filename = @filename
+          number = 1
+          while(::File.exists?(filename))
+            filename = &quot;#{@filename}.#{number}&quot;
+            number += 1
+          end
+        end
+
         ::File::open(filename, &quot;wb&quot;) { |f|
           f.write body
         }
       end
+
+      alias :save :save_as
     end
 
     # = Synopsis
@@ -50,7 +87,7 @@ module WWW
       attr_reader :filename
 
       def initialize(uri=nil, response=nil, body=nil, code=nil)
-        @uri, @response, @body, @code = uri, response, body, code
+        super(uri, response, body, code)
         path = uri.path.empty? ? 'index.html' : uri.path.gsub(/^[\/]*/, '')
         path += 'index.html' if path =~ /\/$/
 
@@ -154,5 +191,14 @@ module WWW
         @parsers[content_type] = klass
       end
     end
+
+    class Headers &lt; Hash
+      def [](key)
+        super(key.downcase)
+      end
+      def []=(key, value)
+        super(key.downcase, value)
+      end
+    end
   end
 end</diff>
      <filename>lib/mechanize/pluggable_parsers.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,6 +3,7 @@
   &lt;body&gt;
     &lt;a href=&quot;alt_text.html&quot;&gt;&lt;img alt=&quot;alt text&quot; src=&quot;hello&quot;&gt;&lt;/a&gt;
     &lt;a href=&quot;no_alt_text.html&quot;&gt;&lt;img src=&quot;hello&quot;&gt;&lt;/a&gt;
+    &lt;a href=&quot;nil_alt_text.html&quot;&gt;&lt;img alt src=&quot;hello&quot;&gt;&lt;/a&gt;
     &lt;a href=&quot;no_image.html&quot;&gt;no image&lt;/a&gt;
     &lt;a href=&quot;no_text.html&quot;&gt;&lt;/a&gt;
   &lt;/body&gt;</diff>
      <filename>test/htdocs/alt_text.html</filename>
    </modified>
    <modified>
      <diff>@@ -3,6 +3,7 @@
         &lt;meta http_equiv=&quot;Refresh&quot; content=&quot;0; url=http://www.incorrect.com&quot;&gt;
         &lt;meta http-equiv=&quot;Rfresh&quot; content=&quot;0; url=http://www.also-wrong.com&quot;&gt;
         &lt;meta http-equiv=&quot;Refresh&quot; content=&quot;0; url=http://www.drphil.com/&quot;&gt;
+        &lt;meta http-equiv=&quot;Refresh&quot; content=&quot;0; url='http://tenderlovemaking.com/'&quot;&gt;
         &lt;META HTTP-EQUIV=&quot;REFRESH&quot; CONTENT=&quot;0; URL=HTTP://WWW.UPCASE.COM/&quot;&gt;
         &lt;TITLE&gt;Testing the links&lt;/TITLE&gt;
     &lt;/head&gt;</diff>
      <filename>test/htdocs/find_link.html</filename>
    </modified>
    <modified>
      <diff>@@ -4,6 +4,7 @@
       &lt;select name=&quot;list&quot;&gt;
         &lt;option value=&quot;1&quot;&gt;Option 1&lt;/option&gt;
         &lt;option value=&quot;2&quot;&gt;Option 2&lt;/option&gt;
+        &lt;option&gt;Option No Value&lt;/option&gt;
         &lt;option value=&quot;3&quot;&gt;Option 3&lt;/option&gt;
         &lt;option value=&quot;4&quot;&gt;Option 4&lt;/option&gt;
         &lt;option value=&quot;5&quot;&gt;Option 5&lt;/option&gt;</diff>
      <filename>test/htdocs/form_select_none.html</filename>
    </modified>
    <modified>
      <diff>@@ -13,14 +13,23 @@ class BasicAuthTest &lt; Test::Unit::TestCase
   end
 
   def test_auth_success
-    @agent.basic_auth('mech', 'password')
-    page = @agent.get(&quot;http://localhost:#{PORT}/htpasswd_auth&quot;)
+    @agent.basic_auth('user', 'pass')
+    page = @agent.get(&quot;http://localhost/basic_auth&quot;)
     assert_equal('You are authenticated', page.body)
   end
 
+  def test_auth_bad_user_pass
+    @agent.basic_auth('aaron', 'aaron')
+    begin
+      page = @agent.get(&quot;http://localhost/basic_auth&quot;)
+    rescue WWW::Mechanize::ResponseCodeError =&gt; e
+      assert_equal(&quot;401&quot;, e.response_code)
+    end
+  end
+
   def test_auth_failure
     begin
-      page = @agent.get(&quot;http://localhost:#{PORT}/htpasswd_auth&quot;)
+      page = @agent.get(&quot;http://localhost/basic_auth&quot;)
     rescue WWW::Mechanize::ResponseCodeError =&gt; e
       assert_equal(&quot;401&quot;, e.response_code)
     end</diff>
      <filename>test/tc_authenticate.rb</filename>
    </modified>
    <modified>
      <diff>@@ -10,7 +10,7 @@ class TestCheckBoxes &lt; Test::Unit::TestCase
 
   def setup
     @agent = WWW::Mechanize.new
-    @page  = @agent.get(&quot;http://localhost:#{PORT}/tc_checkboxes.html&quot;)
+    @page = @agent.get('http://localhost/tc_checkboxes.html')
   end
 
   def test_select_one</diff>
      <filename>test/tc_checkboxes.rb</filename>
    </modified>
    <modified>
      <diff>@@ -7,8 +7,8 @@ require 'test_includes'
 
 module Enumerable
   def combine
-    masks = inject([[], 1]){|(ar, m), e| [ar&lt;&lt;m, m&lt;&lt;1]}[0]
-    all = masks.inject(0){|al, m| al|m}
+    masks = inject([[], 1]){|(ar, m), e| [ar &lt;&lt; m, m &lt;&lt; 1 ] }[0]
+    all = masks.inject(0){ |al, m| al|m }
 
     result = []
     for i in 1..all do
@@ -23,6 +23,14 @@ module Enumerable
 end
 
 class CookieClassTest &lt; Test::Unit::TestCase
+  def silently
+    warn_level = $VERBOSE
+    $VERBOSE = false
+    res = yield
+    $VERBOSE = warn_level
+    res
+  end
+
   def test_parse_dates
     url = URI.parse('http://localhost/')
 
@@ -47,12 +55,30 @@ class CookieClassTest &lt; Test::Unit::TestCase
 
     dates.each do |date|
       cookie = &quot;PREF=1; expires=#{date}&quot;
-      WWW::Mechanize::Cookie.parse(url, cookie) { |cookie|
-        assert_equal(true, cookie.expires &lt; yesterday)
-      }
+      silently do
+        WWW::Mechanize::Cookie.parse(url, cookie) { |cookie|
+          assert_equal(true, cookie.expires &lt; yesterday)
+        }
+      end
     end
   end
 
+  def test_parse_bad_version
+    bad_cookie = 'PRETANET=TGIAqbFXtt; Name=/PRETANET; Path=/; Version=1.2; Content-type=text/html; Domain=192.168.6.196; expires=Friday, 13-November-2026  23:01:46 GMT;'
+    url = URI.parse('http://localhost/')
+    WWW::Mechanize::Cookie.parse(url, bad_cookie) { |cookie|
+      assert_nil(cookie.version)
+    }
+  end
+
+  def test_parse_bad_max_age
+    bad_cookie = 'PRETANET=TGIAqbFXtt; Name=/PRETANET; Path=/; Max-Age=1.2; Content-type=text/html; Domain=192.168.6.196; expires=Friday, 13-November-2026  23:01:46 GMT;'
+    url = URI.parse('http://localhost/')
+    WWW::Mechanize::Cookie.parse(url, bad_cookie) { |cookie|
+      assert_nil(cookie.max_age)
+    }
+  end
+
   def test_parse_date_fail
     url = URI.parse('http://localhost/')
 
@@ -60,11 +86,13 @@ class CookieClassTest &lt; Test::Unit::TestCase
               &quot;20/06/95 21:07&quot;,
     ]
 
-    dates.each do |date|
-      cookie = &quot;PREF=1; expires=#{date}&quot;
-      WWW::Mechanize::Cookie.parse(url, cookie) { |cookie|
-        assert_equal(true, cookie.expires &gt; (Time.now - 86400))
-      }
+    silently do
+      dates.each do |date|
+        cookie = &quot;PREF=1; expires=#{date}&quot;
+        WWW::Mechanize::Cookie.parse(url, cookie) { |cookie|
+          assert_equal(true, cookie.expires.nil?)
+        }
+      end
     end
   end
 
@@ -104,9 +132,46 @@ class CookieClassTest &lt; Test::Unit::TestCase
     end
   end
 
+  def test_parse_valid_cookie_empty_value
+    url = URI.parse('http://rubyforge.org/')
+    cookie_params = {}
+    cookie_params['expires']   = 'expires=Sun, 27-Sep-2037 00:00:00 GMT'
+    cookie_params['path']      = 'path=/'
+    cookie_params['domain']    = 'domain=.rubyforge.org'
+    cookie_params['httponly']  = 'HttpOnly'
+    cookie_value = '12345%7D='
+
+    expires = Time.parse('Sun, 27-Sep-2037 00:00:00 GMT')
+    
+    cookie_params.keys.combine.each do |c|
+      cookie_text = &quot;#{cookie_value}; &quot;
+      c.each_with_index do |key, idx|
+        if idx == (c.length - 1)
+          cookie_text &lt;&lt; &quot;#{cookie_params[key]}&quot;
+        else
+          cookie_text &lt;&lt; &quot;#{cookie_params[key]}; &quot;
+        end
+      end
+      cookie = nil
+      WWW::Mechanize::Cookie.parse(url, cookie_text) { |p_cookie| cookie = p_cookie }
+      assert_not_nil(cookie)
+      assert_equal('12345%7D=', cookie.to_s)
+      assert_equal('', cookie.value)
+      assert_equal('/', cookie.path)
+      assert_equal('rubyforge.org', cookie.domain)
+
+      # if expires was set, make sure we parsed it
+      if c.find { |k| k == 'expires' }
+        assert_equal(expires, cookie.expires)
+      else
+        assert_nil(cookie.expires)
+      end
+    end
+  end
+
   # If no path was given, use the one from the URL
   def test_cookie_using_url_path
-    url = URI.parse('http://rubyforge.org/login')
+    url = URI.parse('http://rubyforge.org/login.php')
     cookie_params = {}
     cookie_params['expires']   = 'expires=Sun, 27-Sep-2037 00:00:00 GMT'
     cookie_params['path']      = 'path=/'
@@ -131,7 +196,7 @@ class CookieClassTest &lt; Test::Unit::TestCase
       assert_not_nil(cookie)
       assert_equal('12345%7D=ASDFWEE345%3DASda', cookie.to_s)
       assert_equal('rubyforge.org', cookie.domain)
-      assert_equal('/login', cookie.path)
+      assert_equal('/', cookie.path)
 
       # if expires was set, make sure we parsed it
       if c.find { |k| k == 'expires' }</diff>
      <filename>test/tc_cookie_class.rb</filename>
    </modified>
    <modified>
      <diff>@@ -15,6 +15,61 @@ class CookieJarTest &lt; Test::Unit::TestCase
     }
     c
   end
+
+  def test_domain_case
+    values = {  :name     =&gt; 'Foo',
+                :value    =&gt; 'Bar',
+                :path     =&gt; '/',
+                :expires  =&gt; Time.now + (10 * 86400),
+                :domain   =&gt; 'rubyforge.org'
+             }
+    url = URI.parse('http://rubyforge.org/')
+
+    jar = WWW::Mechanize::CookieJar.new
+    assert_equal(0, jar.cookies(url).length)
+
+    # Add one cookie with an expiration date in the future
+    cookie = cookie_from_hash(values)
+    jar.add(url, cookie)
+    assert_equal(1, jar.cookies(url).length)
+
+    jar.add(url, cookie_from_hash( values.merge(  :domain =&gt; 'RuByForge.Org',
+                                                  :name   =&gt; 'aaron'
+                                               ) ) )
+                  
+    assert_equal(2, jar.cookies(url).length)
+
+    url2 = URI.parse('http://RuByFoRgE.oRg/')
+    assert_equal(2, jar.cookies(url2).length)
+  end
+
+  def test_empty_value
+    values = {  :name     =&gt; 'Foo',
+                :value    =&gt; '',
+                :path     =&gt; '/',
+                :expires  =&gt; Time.now + (10 * 86400),
+                :domain   =&gt; 'rubyforge.org'
+             }
+    url = URI.parse('http://rubyforge.org/')
+
+    jar = WWW::Mechanize::CookieJar.new
+    assert_equal(0, jar.cookies(url).length)
+
+    # Add one cookie with an expiration date in the future
+    cookie = cookie_from_hash(values)
+    jar.add(url, cookie)
+    assert_equal(1, jar.cookies(url).length)
+
+    jar.add(url, cookie_from_hash( values.merge(  :domain =&gt; 'RuByForge.Org',
+                                                  :name   =&gt; 'aaron'
+                                               ) ) )
+                  
+    assert_equal(2, jar.cookies(url).length)
+
+    url2 = URI.parse('http://RuByFoRgE.oRg/')
+    assert_equal(2, jar.cookies(url2).length)
+  end
+
   def test_add_future_cookies
     values = {  :name     =&gt; 'Foo',
                 :value    =&gt; 'Bar',</diff>
      <filename>test/tc_cookie_jar.rb</filename>
    </modified>
    <modified>
      <diff>@@ -15,7 +15,7 @@ class CookiesMechTest &lt; Test::Unit::TestCase
   def test_send_cookies
     page = @agent.get(&quot;http://localhost:#{PORT}/many_cookies&quot;)
     page = @agent.get(&quot;http://localhost:#{PORT}/send_cookies&quot;)
-    assert_equal(2, page.links.length)
+    assert_equal(3, page.links.length)
     assert_not_nil(page.links.find { |l| l.text == &quot;name:Aaron&quot; })
     assert_not_nil(page.links.find { |l| l.text == &quot;no_expires:nope&quot; })
   end
@@ -57,7 +57,7 @@ class CookiesMechTest &lt; Test::Unit::TestCase
     no_path_cookie = @agent.cookies.find { |k| k.name == &quot;no_path&quot; }
     assert_not_nil(no_path_cookie, &quot;No path cookie is nil&quot;)
     assert_equal(&quot;no_path&quot;, no_path_cookie.value)
-    assert_equal(&quot;/many_cookies_as_string&quot;, no_path_cookie.path)
+    assert_equal(&quot;/&quot;, no_path_cookie.path)
     assert_equal(true, Time.now &lt; no_path_cookie.expires)
   end
 
@@ -88,7 +88,7 @@ class CookiesMechTest &lt; Test::Unit::TestCase
     no_path_cookie = @agent.cookies.find { |k| k.name == &quot;no_path&quot; }
     assert_not_nil(no_path_cookie, &quot;No path cookie is nil&quot;)
     assert_equal(&quot;no_path&quot;, no_path_cookie.value)
-    assert_equal(&quot;/many_cookies&quot;, no_path_cookie.path)
+    assert_equal(&quot;/&quot;, no_path_cookie.path)
     assert_equal(true, Time.now &lt; no_path_cookie.expires)
   end
 </diff>
      <filename>test/tc_cookies.rb</filename>
    </modified>
    <modified>
      <diff>@@ -19,6 +19,14 @@ class MechErrorsTest &lt; Test::Unit::TestCase
     }
   end
 
+  def test_non_exist
+    begin
+      page = @agent.get(&quot;http://localhost:#{PORT}/bad_form_test.html&quot;)
+    rescue RuntimeError =&gt; ex
+      assert_equal(&quot;404&quot;, ex.inspect)
+    end
+  end
+
   def test_too_many_radio
     page = @agent.get(&quot;http://localhost:#{PORT}/form_test.html&quot;)
     form = page.forms.name('post_form1').first</diff>
      <filename>test/tc_errors.rb</filename>
    </modified>
    <modified>
      <diff>@@ -10,11 +10,11 @@ class FormNoInputNameTest &lt; Test::Unit::TestCase
 
   def setup
     @agent = WWW::Mechanize.new
+    @page = @agent.get('http://localhost/form_no_input_name.html')
   end
 
   def test_no_input_name
-    page = @agent.get(&quot;http://localhost:#{PORT}/form_no_input_name.html&quot;)
-    form = page.forms.first
+    form = @page.forms.first
     assert_equal(0, form.fields.length)
     assert_equal(0, form.radiobuttons.length)
     assert_equal(0, form.checkboxes.length)</diff>
      <filename>test/tc_form_no_inputname.rb</filename>
    </modified>
    <modified>
      <diff>@@ -39,6 +39,26 @@ class FormsMechTest &lt; Test::Unit::TestCase
     assert_not_nil(page.links.text('first:Patterson').first)
   end
 
+  # Test calling submit on the form object
+  def test_submit_on_form
+    page = @agent.get(&quot;http://localhost:#{PORT}/form_multival.html&quot;)
+    form = page.forms.name('post_form').first
+
+    assert_not_nil(form)
+    assert_equal(2, form.fields.name('first').length)
+
+    form.fields.name('first')[0].value = 'Aaron'
+    form.fields.name('first')[1].value = 'Patterson'
+
+    page = form.submit
+
+    assert_not_nil(page)
+
+    assert_equal(2, page.links.length)
+    assert_not_nil(page.links.text('first:Aaron').first)
+    assert_not_nil(page.links.text('first:Patterson').first)
+  end
+
   # Test submitting form with two fields of the same name
   def test_get_multival
     page = @agent.get(&quot;http://localhost:#{PORT}/form_multival.html&quot;)
@@ -436,7 +456,7 @@ class FormsMechTest &lt; Test::Unit::TestCase
     page = @agent.submit(get_form, get_form.buttons.first)
 
     # Check that the submitted fields exist
-    assert_equal(5, page.links.size, &quot;Not enough links&quot;)
+    assert_equal(3, page.links.size, &quot;Not enough links&quot;)
     assert_not_nil(
       page.links.find { |l| l.text == &quot;likes ham:on&quot; },
       &quot;likes ham check box missing&quot;
@@ -449,14 +469,6 @@ class FormsMechTest &lt; Test::Unit::TestCase
       page.links.find { |l| l.text == &quot;gender:male&quot; },
       &quot;gender field missing&quot;
     )
-    assert_not_nil(
-      page.links.find { |l| l.text == &quot;great day:yes&quot; },
-      &quot;great day field missing&quot;
-    )
-    assert_not_nil(
-      page.links.find { |l| l.text == &quot;one:two&quot; },
-      &quot;one field missing&quot;
-    )
   end
 
   def test_field_addition
@@ -477,15 +489,50 @@ class FormsMechTest &lt; Test::Unit::TestCase
     assert_equal('Aaron', form.first)
   end
 
-  def test_fields_as_hash
+  def test_add_field
     page = @agent.get(&quot;http://localhost:#{PORT}/form_multival.html&quot;)
     form = page.forms.name('post_form').first
 
     assert_not_nil(form)
-    assert_equal(2, form.fields.name('first').length)
+    number_of_fields = form.fields.length
+
+    f = form.add_field!('intarweb')
+    assert_not_nil(f)
+    assert_equal(number_of_fields + 1, form.fields.length)
+  end
+  
+  def test_delete_field
+    page = @agent.get(&quot;http://localhost:#{PORT}/form_multival.html&quot;)
+    form = page.forms.name('post_form').first
+
+    assert_not_nil(form)
+    number_of_fields = form.fields.length
+
+    form.delete_field!('first')
+    assert_nil(form['first'])
+    assert_equal(number_of_fields - 2, form.fields.length)    
+  end
+
+  def test_has_field
+    page = @agent.get(&quot;http://localhost:#{PORT}/form_multival.html&quot;)
+    form = page.forms.name('post_form').first
+
+    assert_not_nil(form)
+    assert_equal(false, form.has_field?('intarweb'))
+    f = form.add_field!('intarweb')
+    assert_not_nil(f)
+    assert_equal(true, form.has_field?('intarweb'))
+  end
+
+  def test_field_error
+    @page = @agent.get('http://localhost/empty_form.html')
+    form = @page.forms.first
+    assert_raise(NoMethodError) {
+      form.foo = 'asdfasdf'
+    }
 
-    form['first'] = 'Aaron'
-    assert_equal('Aaron', form['first'])
-    assert_equal('Aaron', form.fields.name('first').first.value)
+    assert_raise(NoMethodError) {
+      form.foo
+    }
   end
 end</diff>
      <filename>test/tc_forms.rb</filename>
    </modified>
    <modified>
      <diff>@@ -12,21 +12,30 @@ class LinksMechTest &lt; Test::Unit::TestCase
     @agent = WWW::Mechanize.new
   end
 
+  def test_base
+    page = @agent.get(&quot;http://google.com/tc_base_link.html&quot;)
+    page = page.links.first.click
+    assert @agent.visited?(&quot;http://localhost/index.html&quot;)
+  end
+
   def test_find_meta
     page = @agent.get(&quot;http://localhost:#{PORT}/find_link.html&quot;)
-    assert_equal(2, page.meta.length)
-    assert_equal(&quot;http://www.drphil.com/&quot;, page.meta[0].href.downcase)
-    assert_equal(&quot;http://www.upcase.com/&quot;, page.meta[1].href.downcase)
+    assert_equal(3, page.meta.length)
+    assert_equal(%w{
+      http://www.drphil.com/
+      http://www.upcase.com/
+      http://tenderlovemaking.com/ }.sort,
+      page.meta.map { |x| x.href.downcase }.sort)
   end
 
   def test_find_link
     page = @agent.get(&quot;http://localhost:#{PORT}/find_link.html&quot;)
-    assert_equal(15, page.links.length)
+    assert_equal(18, page.links.length)
   end
 
   def test_alt_text
     page = @agent.get(&quot;http://localhost:#{PORT}/alt_text.html&quot;)
-    assert_equal(4, page.links.length)
+    assert_equal(5, page.links.length)
     assert_equal(1, page.meta.length)
 
     assert_equal('', page.meta.first.text)
@@ -34,6 +43,7 @@ class LinksMechTest &lt; Test::Unit::TestCase
     assert_equal('', page.links.href('no_alt_text.html').first.text)
     assert_equal('no image', page.links.href('no_image.html').first.text)
     assert_equal('', page.links.href('no_text.html').first.text)
+    assert_equal('', page.links.href('nil_alt_text.html').first.text)
   end
 
   def test_click_link
@@ -46,4 +56,51 @@ class LinksMechTest &lt; Test::Unit::TestCase
     assert_equal(&quot;http://localhost:#{PORT}/form_test.html&quot;,
       @agent.history.last.uri.to_s)
   end
+
+  def test_click_method
+    page = @agent.get(&quot;http://localhost:#{PORT}/frame_test.html&quot;)
+    link = page.links.text(&quot;Form Test&quot;)
+    assert_not_nil(link)
+    assert_equal('Form Test', link.text)
+    page = link.click
+    assert_equal(&quot;http://localhost:#{PORT}/form_test.html&quot;,
+      @agent.history.last.uri.to_s)
+  end
+
+  def test_find_bold_link
+    page = @agent.get(&quot;http://localhost:#{PORT}/tc_links.html&quot;)
+    link = page.links.text(/Bold Dude/)
+    assert_equal(1, link.length)
+    assert_equal('Bold Dude', link.first.text)
+
+    link = page.links.text('Aaron James Patterson')
+    assert_equal(1, link.length)
+    assert_equal('Aaron James Patterson', link.first.text)
+
+    link = page.links.text('Aaron Patterson')
+    assert_equal(1, link.length)
+    assert_equal('Aaron Patterson', link.first.text)
+
+    link = page.links.text('Ruby Rocks!')
+    assert_equal(1, link.length)
+    assert_equal('Ruby Rocks!', link.first.text)
+  end
+
+  def test_link_with_encoded_space
+    page = @agent.get(&quot;http://localhost:#{PORT}/tc_links.html&quot;)
+    link = page.links.text('encoded space').first
+    page = @agent.click link
+  end
+
+  def test_link_with_space
+    page = @agent.get(&quot;http://localhost:#{PORT}/tc_links.html&quot;)
+    link = page.links.text('not encoded space').first
+    page = @agent.click link
+  end
+
+  def test_link_with_unusual_characters
+    page = @agent.get(&quot;http://localhost:#{PORT}/tc_links.html&quot;)
+    link = page.links.text('unusual characters').first
+    assert_nothing_raised { @agent.click link }
+  end
 end</diff>
      <filename>test/tc_links.rb</filename>
    </modified>
    <modified>
      <diff>@@ -13,6 +13,26 @@ class TestMechMethods &lt; Test::Unit::TestCase
     @agent = WWW::Mechanize.new
   end
 
+  def test_weird_url
+    assert_nothing_raised {
+      @agent.get('http://localhost/?action=bing&amp;bang=boom=1|a=|b=|c=')
+    }
+    assert_nothing_raised {
+      @agent.get('http://localhost/?a=b&amp;#038;b=c&amp;#038;c=d')
+    }
+    assert_nothing_raised {
+      @agent.get(&quot;http://localhost/?a=#{[0xd6].pack('U')}&quot;)
+    }
+  end
+
+  def test_kcode_url
+    $KCODE = 'u'
+    page = @agent.get(&quot;http://localhost/?a=#{[0xd6].pack('U')}&quot;)
+    assert_not_nil(page)
+    assert_equal('http://localhost/?a=%D6', page.uri.to_s)
+    $KCODE = 'NONE'
+  end
+
   def test_history
     0.upto(25) do |i|
       assert_equal(i, @agent.history.size)
@@ -24,6 +44,8 @@ class TestMechMethods &lt; Test::Unit::TestCase
       @agent.history.last.uri.to_s)
     assert_equal(&quot;http://localhost:#{PORT}/&quot;,
       @agent.history[-2].uri.to_s)
+    assert_equal(&quot;http://localhost:#{PORT}/&quot;,
+      @agent.history[-2].uri.to_s)
 
     assert_equal(true, @agent.visited?(&quot;http://localhost:#{PORT}/&quot;))
     assert_equal(true, @agent.visited?(&quot;/form_test.html&quot;))
@@ -32,6 +54,24 @@ class TestMechMethods &lt; Test::Unit::TestCase
 
   end
 
+  def test_visited
+    @agent.get(&quot;http://localhost/content_type_test?ct=application/pdf&quot;)
+    assert_equal(true,
+      @agent.visited?(&quot;http://localhost/content_type_test?ct=application/pdf&quot;))
+    assert_equal(false,
+      @agent.visited?(&quot;http://localhost/content_type_test&quot;))
+    assert_equal(false,
+      @agent.visited?(&quot;http://localhost/content_type_test?ct=text/html&quot;))
+  end
+
+  def test_visited_after_redirect
+    @agent.get(&quot;http://localhost/response_code?code=302&quot;)
+    assert_equal(&quot;http://localhost/index.html&quot;,
+      @agent.current_page.uri.to_s)
+    assert_equal(true,
+                 @agent.visited?('http://localhost/response_code?code=302'))
+  end
+
   def test_max_history
     @agent.max_history = 10
     0.upto(10) do |i|
@@ -45,6 +85,23 @@ class TestMechMethods &lt; Test::Unit::TestCase
     end
   end
 
+  def test_max_history_order
+    @agent.max_history = 2
+    assert_equal(0, @agent.history.length)
+
+    @agent.get('http://localhost/form_test.html')
+    assert_equal(1, @agent.history.length)
+
+    @agent.get('http://localhost/empty_form.html')
+    assert_equal(2, @agent.history.length)
+
+    @agent.get('http://localhost/tc_checkboxes.html')
+    assert_equal(2, @agent.history.length)
+    assert_equal('http://localhost/empty_form.html', @agent.history[0].uri.to_s)
+    assert_equal('http://localhost/tc_checkboxes.html',
+                 @agent.history[1].uri.to_s)
+  end
+
   def test_back_button
     0.upto(5) do |i|
       assert_equal(i, @agent.history.size)</diff>
      <filename>test/tc_mech.rb</filename>
    </modified>
    <modified>
      <diff>@@ -14,7 +14,7 @@ class TestNoAttributes &lt; Test::Unit::TestCase
 
   def test_parse_no_attributes
     assert_nothing_raised do
-      page = @agent.get(&quot;http://localhost:#{PORT}/tc_no_attributes.html&quot;)
+      page = @agent.get('http://localhost/tc_no_attributes.html')
     end
   end
 end</diff>
      <filename>test/tc_no_attributes.rb</filename>
    </modified>
    <modified>
      <diff>@@ -63,7 +63,7 @@ class PluggableParserTest &lt; Test::Unit::TestCase
     @agent.pluggable_parser.html = Filter
     page = @agent.get(&quot;http://localhost:#{PORT}/find_link.html&quot;)
     assert_kind_of(Filter, page)
-    assert_equal(16, page.links.length)
+    assert_equal(19, page.links.length)
     assert_not_nil(page.links.text('Net::DAAP::Client').first)
     assert_equal(1, page.links.text('Net::DAAP::Client').length)
   end
@@ -74,7 +74,7 @@ class PluggableParserTest &lt; Test::Unit::TestCase
     assert_kind_of(Class, @agent.pluggable_parser['text/html'])
     assert_equal(Filter, @agent.pluggable_parser['text/html'])
     assert_kind_of(Filter, page)
-    assert_equal(16, page.links.length)
+    assert_equal(19, page.links.length)
     assert_not_nil(page.links.text('Net::DAAP::Client').first)
     assert_equal(1, page.links.text('Net::DAAP::Client').length)
   end</diff>
      <filename>test/tc_pluggable_parser.rb</filename>
    </modified>
    <modified>
      <diff>@@ -13,6 +13,10 @@ class ResponseCodeMechTest &lt; Test::Unit::TestCase
   end
 
   def test_redirect
+    @agent.get(&quot;http://localhost:#{PORT}/response_code?code=300&quot;)
+    assert_equal(&quot;http://localhost:#{PORT}/index.html&quot;,
+      @agent.current_page.uri.to_s)
+
     @agent.get(&quot;http://localhost:#{PORT}/response_code?code=301&quot;)
     assert_equal(&quot;http://localhost:#{PORT}/index.html&quot;,
       @agent.current_page.uri.to_s)
@@ -20,6 +24,22 @@ class ResponseCodeMechTest &lt; Test::Unit::TestCase
     @agent.get(&quot;http://localhost:#{PORT}/response_code?code=302&quot;)
     assert_equal(&quot;http://localhost:#{PORT}/index.html&quot;,
       @agent.current_page.uri.to_s)
+
+    @agent.get(&quot;http://localhost:#{PORT}/response_code?code=303&quot;)
+    assert_equal(&quot;http://localhost:#{PORT}/index.html&quot;,
+      @agent.current_page.uri.to_s)
+
+    @agent.get(&quot;http://localhost:#{PORT}/response_code?code=307&quot;)
+    assert_equal(&quot;http://localhost:#{PORT}/index.html&quot;,
+      @agent.current_page.uri.to_s)
+  end
+
+  def test_do_not_follow_redirect
+    @agent.redirect_ok = false
+
+    @agent.get(&quot;http://localhost:#{PORT}/response_code?code=302&quot;)
+    assert_equal(&quot;http://localhost:#{PORT}/response_code?code=302&quot;,
+      @agent.current_page.uri.to_s)
   end
 
   def test_error</diff>
      <filename>test/tc_response_code.rb</filename>
    </modified>
    <modified>
      <diff>@@ -22,4 +22,35 @@ class TestSaveFile &lt; Test::Unit::TestCase
     FileUtils.rm(&quot;test.html&quot;)
     assert_equal(length.to_i, file_length)
   end
+
+  def test_save_file_default
+    page = WWW::Mechanize::File.new(
+                                    URI.parse('http://localhost/test.html'),
+                                    {},
+                                    &quot;hello&quot;
+                                   )
+    page.save
+    assert(File.exists?('test.html'))
+    page.save
+    assert(File.exists?('test.html.1'))
+    page.save
+    assert(File.exists?('test.html.2'))
+    FileUtils.rm(&quot;test.html&quot;)
+    FileUtils.rm(&quot;test.html.1&quot;)
+    FileUtils.rm(&quot;test.html.2&quot;)
+  end
+
+  def test_save_file_default_with_dots
+    page = WWW::Mechanize::File.new(
+                                    URI.parse('http://localhost/../test.html'),
+                                    {},
+                                    &quot;hello&quot;
+                                   )
+    page.save
+    assert(File.exists?('test.html'))
+    page.save
+    assert(File.exists?('test.html.1'))
+    FileUtils.rm(&quot;test.html&quot;)
+    FileUtils.rm(&quot;test.html.1&quot;)
+  end
 end</diff>
      <filename>test/tc_save_file.rb</filename>
    </modified>
    <modified>
      <diff>@@ -10,99 +10,94 @@ class UploadMechTest &lt; Test::Unit::TestCase
 
   def setup
     @agent = WWW::Mechanize.new
+    @page = @agent.get(&quot;http://localhost:#{PORT}/file_upload.html&quot;)
   end
 
   def test_form_enctype
-    page = @agent.get(&quot;http://localhost:#{PORT}/file_upload.html&quot;)
-    assert_equal('multipart/form-data', page.forms[0].enctype)
+    assert_equal('multipart/form-data', @page.forms[0].enctype)
 
-    form = page.forms.first
-    form.file_uploads.first.file_name = &quot;README&quot;
+    form = @page.forms.first
+    form.file_uploads.first.file_name = &quot;#{BASE_DIR}/test_all.rb&quot;
     form.file_uploads.first.mime_type = &quot;text/plain&quot;
     form.file_uploads.first.file_data = &quot;Hello World\n\n&quot;
 
-    page = @agent.submit(form)
+    @page = @agent.submit(form)
 
     assert_match(
-      &quot;Content-Disposition: form-data; name=\&quot;userfile1\&quot;; filename=\&quot;README\&quot;&quot;,
-      page.body
+      &quot;Content-Disposition: form-data; name=\&quot;userfile1\&quot;; filename=\&quot;test_all.rb\&quot;&quot;,
+      @page.body
     )
     assert_match(
       &quot;Content-Disposition: form-data; name=\&quot;name\&quot;&quot;,
-      page.body
+      @page.body
     )
-    assert_match('Content-Type: text/plain', page.body)
-    assert_match('Hello World', page.body)
-    assert_match('foo[aaron]', page.body)
+    assert_match('Content-Type: text/plain', @page.body)
+    assert_match('Hello World', @page.body)
+    assert_match('foo[aaron]', @page.body)
   end
 
   def test_form_multipart
-    page = @agent.get(&quot;http://localhost:#{PORT}/file_upload.html&quot;)
-    assert_equal('multipart/form-data', page.forms[1].enctype)
+    assert_equal('multipart/form-data', @page.forms[1].enctype)
 
-    form = page.forms[1]
-    form.file_uploads.first.file_name = &quot;README&quot;
+    form = @page.forms[1]
+    form.file_uploads.first.file_name = &quot;#{BASE_DIR}/test_all.rb&quot;
     form.file_uploads.first.mime_type = &quot;text/plain&quot;
     form.file_uploads.first.file_data = &quot;Hello World\n\n&quot;
 
-    page = @agent.submit(form)
+    @page = @agent.submit(form)
 
     assert_match(
-      &quot;Content-Disposition: form-data; name=\&quot;green[eggs]\&quot;; filename=\&quot;README\&quot;&quot;,
-      page.body
+      &quot;Content-Disposition: form-data; name=\&quot;green[eggs]\&quot;; filename=\&quot;test_all.rb\&quot;&quot;,
+      @page.body
     )
   end
 
   def test_form_read_file
-    page = @agent.get(&quot;http://localhost:#{PORT}/file_upload.html&quot;)
-    assert_equal('multipart/form-data', page.forms[1].enctype)
+    assert_equal('multipart/form-data', @page.forms[1].enctype)
 
-    form = page.forms[1]
-    form.file_uploads.first.file_name = &quot;README&quot;
+    form = @page.forms[1]
+    form.file_uploads.first.file_name = &quot;#{BASE_DIR}/test_all.rb&quot;
 
-    page = @agent.submit(form)
+    @page = @agent.submit(form)
 
-    contents = File.open(&quot;README&quot;, 'rb') { |f| f.read }
+    contents = File.open(&quot;#{BASE_DIR}/test_all.rb&quot;, 'rb') { |f| f.read }
     assert_match(
-      &quot;Content-Disposition: form-data; name=\&quot;green[eggs]\&quot;; filename=\&quot;README\&quot;&quot;,
-      page.body
+      &quot;Content-Disposition: form-data; name=\&quot;green[eggs]\&quot;; filename=\&quot;test_all.rb\&quot;&quot;,
+      @page.body
     )
-    assert_match(contents, page.body)
+    assert_match(contents, @page.body)
   end
 
   def test_form_io_obj
-    page = @agent.get(&quot;http://localhost:#{PORT}/file_upload.html&quot;)
-    assert_equal('multipart/form-data', page.forms[1].enctype)
+    assert_equal('multipart/form-data', @page.forms[1].enctype)
 
-    form = page.forms[1]
-    form.file_uploads.first.file_name = &quot;README&quot;
-    form.file_uploads.first.file_data = File.open(&quot;README&quot;, 'rb')
+    form = @page.forms[1]
+    form.file_uploads.first.file_name = &quot;#{BASE_DIR}/test_all.rb&quot;
+    form.file_uploads.first.file_data = File.open(&quot;#{BASE_DIR}/test_all.rb&quot;, 'rb')
 
-    page = @agent.submit(form)
+    @page = @agent.submit(form)
 
-    contents = File.open(&quot;README&quot;, 'rb') { |f| f.read }
+    contents = File.open(&quot;#{BASE_DIR}/test_all.rb&quot;, 'rb') { |f| f.read }
     assert_match(
-      &quot;Content-Disposition: form-data; name=\&quot;green[eggs]\&quot;; filename=\&quot;README\&quot;&quot;,
-      page.body
+      &quot;Content-Disposition: form-data; name=\&quot;green[eggs]\&quot;; filename=\&quot;test_all.rb\&quot;&quot;,
+      @page.body
     )
-    assert_match(contents, page.body)
+    assert_match(contents, @page.body)
   end
 
   def test_submit_no_file
-    page = @agent.get(&quot;http://localhost:#{PORT}/file_upload.html&quot;)
-    form = page.forms.first
+    form = @page.forms.first
     form.fields.name('name').value = 'Aaron'
-    page = @agent.submit(form)
-    assert_match('Aaron', page.body)
+    @page = @agent.submit(form)
+    assert_match('Aaron', @page.body)
     assert_match(
       &quot;Content-Disposition: form-data; name=\&quot;userfile1\&quot;; filename=\&quot;\&quot;&quot;,
-      page.body
+      @page.body
     )
   end
 
   def test_no_value
-    page = @agent.get(&quot;http://localhost:#{PORT}/file_upload.html&quot;)
-    form = page.form('value_test')
+    form = @page.form('value_test')
     assert_nil(form.file_uploads.first.value)
     assert_nil(form.file_uploads.first.file_name)
   end</diff>
      <filename>test/tc_upload.rb</filename>
    </modified>
    <modified>
      <diff>@@ -9,7 +9,7 @@ class Area
   attr_reader :name
 
   def initialize(node)
-    @name = node.attributes['name']
+    @name = node['name']
   end
 end
 </diff>
      <filename>test/tc_watches.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,121 @@
+require 'net/http'
+require 'test_servlets'
+require 'webrick/httputils'
+
+BASE_DIR = File.dirname(__FILE__)
+
+class Net::HTTP
+  #def self.new(*args)
+  #  obj = allocate
+  #  return obj
+  #end
+
+  alias :old_do_start :do_start
+
+  def do_start
+    @started = true
+  end
+
+  SERVLETS = {
+    '/gzip'                   =&gt; GzipServlet,
+    '/form_post'              =&gt; FormTest,
+    '/basic_auth'             =&gt; BasicAuthServlet,
+    '/form post'              =&gt; FormTest,
+    '/response_code'          =&gt; ResponseCodeTest,
+    '/bad_content_type'       =&gt; BadContentTypeTest,
+    '/content_type_test'      =&gt; ContentTypeTest,
+    '/referer'                =&gt; RefererServlet,
+    '/file_upload'            =&gt; FileUploadTest,
+    '/one_cookie'             =&gt; OneCookieTest,
+    '/one_cookie_no_space'    =&gt; OneCookieNoSpacesTest,
+    '/many_cookies'           =&gt; ManyCookiesTest,
+    '/many_cookies_as_string' =&gt; ManyCookiesAsStringTest,
+    '/send_cookies'           =&gt; SendCookiesTest,
+    '/if_modified_since'      =&gt; ModifiedSinceServlet,
+    '/http_headers'           =&gt; HeaderServlet,
+  }
+
+  PAGE_CACHE = {}
+
+  alias :old_request :request
+
+  def request(request, *data, &amp;block)
+    url = URI.parse(request.path)
+    path = URI.unescape(url.path)
+
+    path = '/index.html' if path == '/'
+
+    res = Response.new
+    request.query = WEBrick::HTTPUtils.parse_query(url.query)
+    request.cookies = WEBrick::Cookie.parse(request['Cookie'])
+    if SERVLETS[path]
+      if request.method == &quot;POST&quot;
+        if request['Content-Type'] =~ /^multipart\/form-data/
+          request.body = data.first
+        else
+          request.query = WEBrick::HTTPUtils.parse_query(data.first)
+        end
+      end
+      SERVLETS[path].new({}).send(&quot;do_#{request.method}&quot;, request, res)
+    else
+      filename = &quot;htdocs#{path.gsub(/[^\/\\.\w_\s]/, '_')}&quot;
+      unless PAGE_CACHE[filename]
+        File.open(&quot;#{BASE_DIR}/#{filename}&quot;, 'rb') { |file|
+          PAGE_CACHE[filename] = file.read
+        }
+      end
+      res.body = PAGE_CACHE[filename]
+    end
+
+    res['Content-Type'] ||= 'text/html'
+    res['Content-Length'] ||= res.body.length.to_s
+    res.code ||= &quot;200&quot;
+
+    res.cookies.each do |cookie|
+      res.add_field('Set-Cookie', cookie.to_s)
+    end
+    yield res if block_given?
+    res
+  end
+end
+
+class Net::HTTPRequest
+  attr_accessor :query, :body, :cookies, :user
+end
+
+class Response
+  include Net::HTTPHeader
+
+  attr_reader :code
+  attr_accessor :body, :query, :cookies
+  
+  def code=(c)
+    @code = c.to_s
+  end
+
+  alias :status :code
+  alias :status= :code=
+
+  def initialize
+    @header = {}
+    @body = ''
+    @code = nil
+    @query = nil
+    @cookies = []
+  end
+
+  def read_body
+    yield body
+  end
+end
+
+
 module TestMethods
   PORT      = 2000
   PROXYPORT = 2001
   SSLPORT   = 2002
+
+  def html_response
+    { 'content-type' =&gt; 'text/html' }
+  end
 end</diff>
      <filename>test/test_includes.rb</filename>
    </modified>
  </modified>
  <removed type="array">
    <removed>
      <filename>CHANGELOG</filename>
    </removed>
    <removed>
      <filename>EXAMPLES</filename>
    </removed>
    <removed>
      <filename>GUIDE</filename>
    </removed>
    <removed>
      <filename>LICENSE</filename>
    </removed>
    <removed>
      <filename>NOTES</filename>
    </removed>
    <removed>
      <filename>README</filename>
    </removed>
    <removed>
      <filename>lib/mechanize/hpricot.rb</filename>
    </removed>
    <removed>
      <filename>lib/mechanize/mech_version.rb</filename>
    </removed>
    <removed>
      <filename>test/README</filename>
    </removed>
    <removed>
      <filename>test/proxy.rb</filename>
    </removed>
    <removed>
      <filename>test/server.rb</filename>
    </removed>
    <removed>
      <filename>test/servlets.rb</filename>
    </removed>
    <removed>
      <filename>test/ts_mech.rb</filename>
    </removed>
  </removed>
  <parents type="array">
    <parent>
      <id>7af819fa236093fe989f4a98272f970c2e61a1fb</id>
    </parent>
  </parents>
  <author>
    <name>aaronp</name>
    <email>aaronp@f1cf478b-080f-0410-abad-959bfeec9ea8</email>
  </author>
  <url>http://github.com/github/mechanize/commit/63d4e1539fdd02e128b4ace7d346806d355deaa2</url>
  <id>63d4e1539fdd02e128b4ace7d346806d355deaa2</id>
  <committed-date>2007-12-04T20:17:00-08:00</committed-date>
  <authored-date>2007-12-04T20:17:00-08:00</authored-date>
  <message>merging REL-0.9.0 -&gt; trunk 308:HEAD

git-svn-id: svn+ssh://rubyforge.org/var/svn/mechanize/trunk@451 f1cf478b-080f-0410-abad-959bfeec9ea8</message>
  <tree>8a49a5128e4a07f214d804ab73cc601da262fcd7</tree>
  <committer>
    <name>aaronp</name>
    <email>aaronp@f1cf478b-080f-0410-abad-959bfeec9ea8</email>
  </committer>
</commit>
