diamond-mechanize 2.2 → 2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. data/Rakefile +49 -0
  2. data/lib/mechanize.rb +1079 -0
  3. data/lib/mechanize/content_type_error.rb +13 -0
  4. data/lib/mechanize/cookie.rb +232 -0
  5. data/lib/mechanize/cookie_jar.rb +194 -0
  6. data/lib/mechanize/download.rb +59 -0
  7. data/lib/mechanize/element_matcher.rb +36 -0
  8. data/lib/mechanize/file.rb +65 -0
  9. data/lib/mechanize/file_connection.rb +17 -0
  10. data/lib/mechanize/file_request.rb +26 -0
  11. data/lib/mechanize/file_response.rb +74 -0
  12. data/lib/mechanize/file_saver.rb +39 -0
  13. data/lib/mechanize/form.rb +543 -0
  14. data/lib/mechanize/form/button.rb +6 -0
  15. data/lib/mechanize/form/check_box.rb +12 -0
  16. data/lib/mechanize/form/field.rb +54 -0
  17. data/lib/mechanize/form/file_upload.rb +21 -0
  18. data/lib/mechanize/form/hidden.rb +3 -0
  19. data/lib/mechanize/form/image_button.rb +19 -0
  20. data/lib/mechanize/form/keygen.rb +34 -0
  21. data/lib/mechanize/form/multi_select_list.rb +94 -0
  22. data/lib/mechanize/form/option.rb +50 -0
  23. data/lib/mechanize/form/radio_button.rb +55 -0
  24. data/lib/mechanize/form/reset.rb +3 -0
  25. data/lib/mechanize/form/select_list.rb +44 -0
  26. data/lib/mechanize/form/submit.rb +3 -0
  27. data/lib/mechanize/form/text.rb +3 -0
  28. data/lib/mechanize/form/textarea.rb +3 -0
  29. data/lib/mechanize/headers.rb +23 -0
  30. data/lib/mechanize/history.rb +82 -0
  31. data/lib/mechanize/http.rb +8 -0
  32. data/lib/mechanize/http/agent.rb +1004 -0
  33. data/lib/mechanize/http/auth_challenge.rb +59 -0
  34. data/lib/mechanize/http/auth_realm.rb +31 -0
  35. data/lib/mechanize/http/content_disposition_parser.rb +188 -0
  36. data/lib/mechanize/http/www_authenticate_parser.rb +155 -0
  37. data/lib/mechanize/monkey_patch.rb +16 -0
  38. data/lib/mechanize/page.rb +440 -0
  39. data/lib/mechanize/page/base.rb +7 -0
  40. data/lib/mechanize/page/frame.rb +27 -0
  41. data/lib/mechanize/page/image.rb +30 -0
  42. data/lib/mechanize/page/label.rb +20 -0
  43. data/lib/mechanize/page/link.rb +98 -0
  44. data/lib/mechanize/page/meta_refresh.rb +68 -0
  45. data/lib/mechanize/parser.rb +173 -0
  46. data/lib/mechanize/pluggable_parsers.rb +144 -0
  47. data/lib/mechanize/redirect_limit_reached_error.rb +19 -0
  48. data/lib/mechanize/redirect_not_get_or_head_error.rb +21 -0
  49. data/lib/mechanize/response_code_error.rb +21 -0
  50. data/lib/mechanize/response_read_error.rb +27 -0
  51. data/lib/mechanize/robots_disallowed_error.rb +28 -0
  52. data/lib/mechanize/test_case.rb +663 -0
  53. data/lib/mechanize/unauthorized_error.rb +3 -0
  54. data/lib/mechanize/unsupported_scheme_error.rb +6 -0
  55. data/lib/mechanize/util.rb +101 -0
  56. data/test/data/htpasswd +1 -0
  57. data/test/data/server.crt +16 -0
  58. data/test/data/server.csr +12 -0
  59. data/test/data/server.key +15 -0
  60. data/test/data/server.pem +15 -0
  61. data/test/htdocs/alt_text.html +10 -0
  62. data/test/htdocs/bad_form_test.html +9 -0
  63. data/test/htdocs/button.jpg +0 -0
  64. data/test/htdocs/canonical_uri.html +9 -0
  65. data/test/htdocs/dir with spaces/foo.html +1 -0
  66. data/test/htdocs/empty_form.html +6 -0
  67. data/test/htdocs/file_upload.html +26 -0
  68. data/test/htdocs/find_link.html +41 -0
  69. data/test/htdocs/form_multi_select.html +16 -0
  70. data/test/htdocs/form_multival.html +37 -0
  71. data/test/htdocs/form_no_action.html +18 -0
  72. data/test/htdocs/form_no_input_name.html +16 -0
  73. data/test/htdocs/form_order_test.html +11 -0
  74. data/test/htdocs/form_select.html +16 -0
  75. data/test/htdocs/form_set_fields.html +14 -0
  76. data/test/htdocs/form_test.html +188 -0
  77. data/test/htdocs/frame_referer_test.html +10 -0
  78. data/test/htdocs/frame_test.html +30 -0
  79. data/test/htdocs/google.html +13 -0
  80. data/test/htdocs/index.html +6 -0
  81. data/test/htdocs/link with space.html +5 -0
  82. data/test/htdocs/meta_cookie.html +11 -0
  83. data/test/htdocs/no_title_test.html +6 -0
  84. data/test/htdocs/noindex.html +9 -0
  85. data/test/htdocs/rails_3_encoding_hack_form_test.html +27 -0
  86. data/test/htdocs/relative/tc_relative_links.html +21 -0
  87. data/test/htdocs/robots.html +8 -0
  88. data/test/htdocs/robots.txt +2 -0
  89. data/test/htdocs/tc_bad_charset.html +9 -0
  90. data/test/htdocs/tc_bad_links.html +5 -0
  91. data/test/htdocs/tc_base_link.html +8 -0
  92. data/test/htdocs/tc_blank_form.html +11 -0
  93. data/test/htdocs/tc_charset.html +6 -0
  94. data/test/htdocs/tc_checkboxes.html +19 -0
  95. data/test/htdocs/tc_encoded_links.html +5 -0
  96. data/test/htdocs/tc_field_precedence.html +11 -0
  97. data/test/htdocs/tc_follow_meta.html +8 -0
  98. data/test/htdocs/tc_form_action.html +48 -0
  99. data/test/htdocs/tc_links.html +19 -0
  100. data/test/htdocs/tc_meta_in_body.html +9 -0
  101. data/test/htdocs/tc_pretty_print.html +17 -0
  102. data/test/htdocs/tc_referer.html +16 -0
  103. data/test/htdocs/tc_relative_links.html +19 -0
  104. data/test/htdocs/tc_textarea.html +23 -0
  105. data/test/htdocs/test_click.html +11 -0
  106. data/test/htdocs/unusual______.html +5 -0
  107. data/test/test_mechanize.rb +1164 -0
  108. data/test/test_mechanize_cookie.rb +451 -0
  109. data/test/test_mechanize_cookie_jar.rb +483 -0
  110. data/test/test_mechanize_download.rb +43 -0
  111. data/test/test_mechanize_file.rb +61 -0
  112. data/test/test_mechanize_file_connection.rb +21 -0
  113. data/test/test_mechanize_file_request.rb +19 -0
  114. data/test/test_mechanize_file_saver.rb +21 -0
  115. data/test/test_mechanize_form.rb +875 -0
  116. data/test/test_mechanize_form_check_box.rb +38 -0
  117. data/test/test_mechanize_form_encoding.rb +114 -0
  118. data/test/test_mechanize_form_field.rb +63 -0
  119. data/test/test_mechanize_form_file_upload.rb +20 -0
  120. data/test/test_mechanize_form_image_button.rb +12 -0
  121. data/test/test_mechanize_form_keygen.rb +32 -0
  122. data/test/test_mechanize_form_multi_select_list.rb +84 -0
  123. data/test/test_mechanize_form_option.rb +55 -0
  124. data/test/test_mechanize_form_radio_button.rb +78 -0
  125. data/test/test_mechanize_form_select_list.rb +76 -0
  126. data/test/test_mechanize_form_textarea.rb +52 -0
  127. data/test/test_mechanize_headers.rb +35 -0
  128. data/test/test_mechanize_history.rb +103 -0
  129. data/test/test_mechanize_http_agent.rb +1225 -0
  130. data/test/test_mechanize_http_auth_challenge.rb +39 -0
  131. data/test/test_mechanize_http_auth_realm.rb +49 -0
  132. data/test/test_mechanize_http_content_disposition_parser.rb +118 -0
  133. data/test/test_mechanize_http_www_authenticate_parser.rb +146 -0
  134. data/test/test_mechanize_link.rb +80 -0
  135. data/test/test_mechanize_page.rb +118 -0
  136. data/test/test_mechanize_page_encoding.rb +182 -0
  137. data/test/test_mechanize_page_frame.rb +16 -0
  138. data/test/test_mechanize_page_link.rb +390 -0
  139. data/test/test_mechanize_page_meta_refresh.rb +127 -0
  140. data/test/test_mechanize_parser.rb +289 -0
  141. data/test/test_mechanize_pluggable_parser.rb +52 -0
  142. data/test/test_mechanize_redirect_limit_reached_error.rb +24 -0
  143. data/test/test_mechanize_redirect_not_get_or_head_error.rb +14 -0
  144. data/test/test_mechanize_subclass.rb +22 -0
  145. data/test/test_mechanize_util.rb +103 -0
  146. data/test/test_multi_select.rb +119 -0
  147. metadata +148 -71
@@ -0,0 +1,19 @@
1
+ ##
2
+ # Raised when too many redirects are sent
3
+
4
+ class Mechanize::RedirectLimitReachedError < Mechanize::Error
5
+
6
+ attr_reader :page
7
+ attr_reader :redirects
8
+ attr_reader :response_code
9
+
10
+ def initialize page, redirects
11
+ @page = page
12
+ @redirects = redirects
13
+ @response_code = page.code
14
+
15
+ super "Redirect limit of #{redirects} reached"
16
+ end
17
+
18
+ end
19
+
@@ -0,0 +1,21 @@
1
+ ##
2
+ # Raised when a POST, PUT, or DELETE request results in a redirect
3
+ # see RFC 2616 10.3.2, 10.3.3 http://www.ietf.org/rfc/rfc2616.txt
4
+
5
+ class Mechanize::RedirectNotGetOrHeadError < Mechanize::Error
6
+ attr_reader :page, :response_code, :verb, :uri
7
+ def initialize(page, verb)
8
+ @page = page
9
+ @verb = verb
10
+ @uri = page.uri
11
+ @response_code = page.code
12
+ end
13
+
14
+ def to_s
15
+ method = @verb.to_s.upcase
16
+ "#{@response_code} redirect received after a #{method} request"
17
+ end
18
+
19
+ alias :inspect :to_s
20
+ end
21
+
@@ -0,0 +1,21 @@
1
+ # This error is raised when Mechanize encounters a response code it does not
2
+ # know how to handle. Currently, this exception will be thrown if Mechanize
3
+ # encounters response codes other than 200, 301, or 302. Any other response
4
+ # code is up to the user to handle.
5
+
6
+ class Mechanize::ResponseCodeError < Mechanize::Error
7
+ attr_reader :response_code
8
+ attr_reader :page
9
+
10
+ def initialize(page)
11
+ @page = page
12
+ @response_code = page.code.to_s
13
+ end
14
+
15
+ def to_s
16
+ "#{@response_code} => #{Net::HTTPResponse::CODE_TO_OBJ[@response_code]}"
17
+ end
18
+
19
+ alias inspect to_s
20
+ end
21
+
@@ -0,0 +1,27 @@
1
+ ##
2
+ # Raised when Mechanize encounters an error while reading the response body
3
+ # from the server. Contains the response headers and the response body up to
4
+ # the error along with the initial error.
5
+
6
+ class Mechanize::ResponseReadError < Mechanize::Error
7
+
8
+ attr_reader :body_io
9
+ attr_reader :error
10
+ attr_reader :response
11
+
12
+ ##
13
+ # Creates a new ResponseReadError with the +error+ raised, the +response+
14
+ # and the +body_io+ for content read so far.
15
+
16
+ def initialize error, response, body_io
17
+ @error = error
18
+ @response = response
19
+ @body_io = body_io
20
+ end
21
+
22
+ def message # :nodoc:
23
+ "#{@error.message} (#{self.class})"
24
+ end
25
+
26
+ end
27
+
@@ -0,0 +1,28 @@
1
+ # Exception that is raised when an access to a resource is disallowed by
2
+ # robots.txt or by HTML document itself.
3
+
4
+ class Mechanize::RobotsDisallowedError < Mechanize::Error
5
+ def initialize(url)
6
+ if url.is_a?(URI)
7
+ @url = url.to_s
8
+ @uri = url
9
+ else
10
+ @url = url.to_s
11
+ end
12
+ end
13
+
14
+ # Returns the URL (string) of the resource that caused this error.
15
+ attr_reader :url
16
+
17
+ # Returns the URL (URI object) of the resource that caused this
18
+ # error. URI::InvalidURIError may be raised if the URL happens to
19
+ # be invalid or not understood by the URI library.
20
+ def uri
21
+ @uri ||= URI.parse(url)
22
+ end
23
+
24
+ def to_s
25
+ "Robots access is disallowed for URL: #{url}"
26
+ end
27
+ alias :inspect :to_s
28
+ end
@@ -0,0 +1,663 @@
1
+ require 'mechanize'
2
+ require 'logger'
3
+ require 'tempfile'
4
+ require 'tmpdir'
5
+ require 'webrick'
6
+ require 'zlib'
7
+
8
+ require 'rubygems'
9
+
10
+ begin
11
+ gem 'minitest'
12
+ rescue Gem::LoadError
13
+ end
14
+
15
+ require 'minitest/autorun'
16
+
17
+ class Mechanize::TestCase < MiniTest::Unit::TestCase
18
+
19
+ TEST_DIR = File.expand_path '../../../test', __FILE__
20
+ REQUESTS = []
21
+
22
+ def setup
23
+ super
24
+
25
+ REQUESTS.clear
26
+ @mech = Mechanize.new
27
+ @ssl_private_key = nil
28
+ @ssl_certificate = nil
29
+ end
30
+
31
+ def fake_page agent = @mech
32
+ uri = URI 'http://fake.example/'
33
+ html = <<-END
34
+ <html>
35
+ <body>
36
+ <form><input type="submit" value="submit" /></form>
37
+ </body>
38
+ </html>
39
+ END
40
+
41
+ response = { 'content-type' => 'text/html' }
42
+
43
+ Mechanize::Page.new uri, response, html, 200, agent
44
+ end
45
+
46
+ def have_encoding?
47
+ Object.const_defined? :Encoding
48
+ end
49
+
50
+ def html_page body
51
+ uri = URI 'http://example/'
52
+ Mechanize::Page.new uri, { 'content-type' => 'text/html' }, body, 200, @mech
53
+ end
54
+
55
+ def in_tmpdir
56
+ Dir.mktmpdir do |dir|
57
+ Dir.chdir dir do
58
+ yield
59
+ end
60
+ end
61
+ end
62
+
63
+ def node element, attributes = {}
64
+ doc = Nokogiri::HTML::Document.new
65
+
66
+ node = Nokogiri::XML::Node.new element, doc
67
+
68
+ attributes.each do |name, value|
69
+ node[name] = value
70
+ end
71
+
72
+ node
73
+ end
74
+
75
+ def page uri, content_type = 'text/html', body = '', code = 200
76
+ uri = URI uri unless URI::Generic === uri
77
+
78
+ Mechanize::Page.new(uri, { 'content-type' => content_type }, body, code,
79
+ @mech)
80
+ end
81
+
82
+ def requests
83
+ REQUESTS
84
+ end
85
+
86
+ def ssl_private_key
87
+ @ssl_private_key ||= OpenSSL::PKey::RSA.new <<-KEY
88
+ -----BEGIN RSA PRIVATE KEY-----
89
+ MIG7AgEAAkEA8pmEfmP0Ibir91x6pbts4JmmsVZd3xvD5p347EFvBCbhBW1nv1Gs
90
+ bCBEFlSiT1q2qvxGb5IlbrfdhdgyqdTXUQIBAQIBAQIhAPumXslvf6YasXa1hni3
91
+ p80joKOug2UUgqOLD2GUSO//AiEA9ssY6AFxjHWuwo/+/rkLmkfO2s1Lz3OeUEWq
92
+ 6DiHOK8CAQECAQECIQDt8bc4vS6wh9VXApNSKIpVygtxSFe/IwLeX26n77j6Qg==
93
+ -----END RSA PRIVATE KEY-----
94
+ KEY
95
+ end
96
+
97
+ def ssl_certificate
98
+ @ssl_certificate ||= OpenSSL::X509::Certificate.new <<-CERT
99
+ -----BEGIN CERTIFICATE-----
100
+ MIIBQjCB7aADAgECAgEAMA0GCSqGSIb3DQEBBQUAMCoxDzANBgNVBAMMBm5vYm9k
101
+ eTEXMBUGCgmSJomT8ixkARkWB2V4YW1wbGUwIBcNMTExMTAzMjEwODU5WhgPOTk5
102
+ OTEyMzExMjU5NTlaMCoxDzANBgNVBAMMBm5vYm9keTEXMBUGCgmSJomT8ixkARkW
103
+ B2V4YW1wbGUwWjANBgkqhkiG9w0BAQEFAANJADBGAkEA8pmEfmP0Ibir91x6pbts
104
+ 4JmmsVZd3xvD5p347EFvBCbhBW1nv1GsbCBEFlSiT1q2qvxGb5IlbrfdhdgyqdTX
105
+ UQIBATANBgkqhkiG9w0BAQUFAANBAAAB////////////////////////////////
106
+ //8AMCEwCQYFKw4DAhoFAAQUePiv+QrJxyjtEJNnH5pB9OTWIqA=
107
+ -----END CERTIFICATE-----
108
+ CERT
109
+ end
110
+
111
+ end
112
+
113
+ class BasicAuthServlet < WEBrick::HTTPServlet::AbstractServlet
114
+ def do_GET(req,res)
115
+ htpd = WEBrick::HTTPAuth::Htpasswd.new('dot.htpasswd')
116
+ htpd.set_passwd('Blah', 'user', 'pass')
117
+ authenticator = WEBrick::HTTPAuth::BasicAuth.new({
118
+ :UserDB => htpd,
119
+ :Realm => 'Blah',
120
+ :Logger => Logger.new(nil)
121
+ }
122
+ )
123
+ begin
124
+ authenticator.authenticate(req,res)
125
+ res.body = 'You are authenticated'
126
+ rescue WEBrick::HTTPStatus::Unauthorized
127
+ res.status = 401
128
+ end
129
+ FileUtils.rm('dot.htpasswd')
130
+ end
131
+ alias :do_POST :do_GET
132
+ end
133
+
134
+ class ContentTypeServlet < WEBrick::HTTPServlet::AbstractServlet
135
+ def do_GET(req, res)
136
+ ct = req.query['ct'] || "text/html; charset=utf-8"
137
+ res['Content-Type'] = ct
138
+ res.body = "Hello World"
139
+ end
140
+ end
141
+
142
+ class DigestAuthServlet < WEBrick::HTTPServlet::AbstractServlet
143
+ htpd = WEBrick::HTTPAuth::Htdigest.new('digest.htpasswd')
144
+ htpd.set_passwd('Blah', 'user', 'pass')
145
+ @@authenticator = WEBrick::HTTPAuth::DigestAuth.new({
146
+ :UserDB => htpd,
147
+ :Realm => 'Blah',
148
+ :Algorithm => 'MD5',
149
+ :Logger => Logger.new(nil)
150
+ }
151
+ )
152
+ def do_GET(req,res)
153
+ def req.request_time; Time.now; end
154
+ def req.request_uri; '/digest_auth'; end
155
+ def req.request_method; "GET"; end
156
+
157
+ begin
158
+ @@authenticator.authenticate(req,res)
159
+ res.body = 'You are authenticated'
160
+ rescue WEBrick::HTTPStatus::Unauthorized
161
+ res.status = 401
162
+ end
163
+ FileUtils.rm('digest.htpasswd') if File.exists?('digest.htpasswd')
164
+ end
165
+ alias :do_POST :do_GET
166
+ end
167
+
168
+ class FileUploadServlet < WEBrick::HTTPServlet::AbstractServlet
169
+ def do_POST(req, res)
170
+ res.body = req.body
171
+ end
172
+ end
173
+
174
+ class FormServlet < WEBrick::HTTPServlet::AbstractServlet
175
+ def do_GET(req, res)
176
+ res.body = "<HTML><body>"
177
+ req.query.each_key { |k|
178
+ req.query[k].each_data { |data|
179
+ res.body << "<a href=\"#\">#{WEBrick::HTTPUtils.unescape(k)}:#{WEBrick::HTTPUtils.unescape(data)}</a><br />"
180
+ }
181
+ }
182
+ res.body << "<div id=\"query\">#{res.query}</div></body></HTML>"
183
+ res['Content-Type'] = "text/html"
184
+ end
185
+
186
+ def do_POST(req, res)
187
+ res.body = "<HTML><body>"
188
+
189
+ req.query.each_key { |k|
190
+ req.query[k].each_data { |data|
191
+ res.body << "<a href=\"#\">#{k}:#{data}</a><br />"
192
+ }
193
+ }
194
+
195
+ res.body << "<div id=\"query\">#{req.body}</div></body></HTML>"
196
+ res['Content-Type'] = "text/html"
197
+ end
198
+ end
199
+
200
+ class GzipServlet < WEBrick::HTTPServlet::AbstractServlet
201
+ def do_GET(req, res)
202
+ if req['Accept-Encoding'] =~ /gzip/
203
+ if name = req.query['file'] then
204
+ open("#{Mechanize::TestCase::TEST_DIR}/htdocs/#{name}", 'r') do |io|
205
+ string = ""
206
+ zipped = StringIO.new string, 'w'
207
+ Zlib::GzipWriter.wrap zipped do |gz|
208
+ gz.write io.read
209
+ end
210
+ res.body = string
211
+ end
212
+ else
213
+ res.body = ''
214
+ end
215
+ res['Content-Encoding'] = req['X-ResponseContentEncoding'] || 'gzip'
216
+ res['Content-Type'] = "text/html"
217
+ else
218
+ res.code = 400
219
+ res.body = 'no gzip'
220
+ end
221
+ end
222
+ end
223
+
224
+ class HeaderServlet < WEBrick::HTTPServlet::AbstractServlet
225
+ def do_GET(req, res)
226
+ res['Content-Type'] = "text/html"
227
+
228
+ req.query.each do |x,y|
229
+ res[x] = y
230
+ end
231
+
232
+ body = ''
233
+ req.each_header do |k,v|
234
+ body << "#{k}|#{v}\n"
235
+ end
236
+ res.body = body
237
+ end
238
+ end
239
+
240
+ class HttpRefreshServlet < WEBrick::HTTPServlet::AbstractServlet
241
+ def do_GET(req, res)
242
+ res['Content-Type'] = req.query['ct'] || "text/html"
243
+ refresh_time = req.query['refresh_time'] || 0
244
+ refresh_url = req.query['refresh_url'] || '/index.html'
245
+ res['Refresh'] = " #{refresh_time};url=#{refresh_url}\r\n";
246
+ end
247
+ end
248
+
249
+ class InfiniteRedirectServlet < WEBrick::HTTPServlet::AbstractServlet
250
+ def do_GET(req, res)
251
+ res['Content-Type'] = req.query['ct'] || "text/html"
252
+ res.status = req.query['code'] ? req.query['code'].to_i : '302'
253
+ number = req.query['q'] ? req.query['q'].to_i : 0
254
+ res['Location'] = "/infinite_redirect?q=#{number + 1}"
255
+ end
256
+ alias :do_POST :do_GET
257
+ end
258
+
259
+ class InfiniteRefreshServlet < WEBrick::HTTPServlet::AbstractServlet
260
+ def do_GET(req, res)
261
+ res['Content-Type'] = req.query['ct'] || "text/html"
262
+ res.status = req.query['code'] ? req.query['code'].to_i : '302'
263
+ number = req.query['q'] ? req.query['q'].to_i : 0
264
+ res['Refresh'] = " 0;url=http://localhost/infinite_refresh?q=#{number + 1}\r\n";
265
+ end
266
+ end
267
+
268
+ class ManyCookiesAsStringServlet < WEBrick::HTTPServlet::AbstractServlet
269
+ def do_GET(req, res)
270
+ cookies = []
271
+ name_cookie = WEBrick::Cookie.new("name", "Aaron")
272
+ name_cookie.path = "/"
273
+ name_cookie.expires = Time.now + 86400
274
+ name_cookie.domain = 'localhost'
275
+ cookies << name_cookie
276
+ cookies << name_cookie
277
+ cookies << name_cookie
278
+ cookies << "#{name_cookie}; HttpOnly"
279
+
280
+ expired_cookie = WEBrick::Cookie.new("expired", "doh")
281
+ expired_cookie.path = "/"
282
+ expired_cookie.expires = Time.now - 86400
283
+ cookies << expired_cookie
284
+
285
+ different_path_cookie = WEBrick::Cookie.new("a_path", "some_path")
286
+ different_path_cookie.path = "/some_path"
287
+ different_path_cookie.expires = Time.now + 86400
288
+ cookies << different_path_cookie
289
+
290
+ no_path_cookie = WEBrick::Cookie.new("no_path", "no_path")
291
+ no_path_cookie.expires = Time.now + 86400
292
+ cookies << no_path_cookie
293
+
294
+ no_exp_path_cookie = WEBrick::Cookie.new("no_expires", "nope")
295
+ no_exp_path_cookie.path = "/"
296
+ cookies << no_exp_path_cookie
297
+
298
+ res['Set-Cookie'] = cookies.join(', ')
299
+
300
+ res['Content-Type'] = "text/html"
301
+ res.body = "<html><body>hello</body></html>"
302
+ end
303
+ end
304
+
305
+ class ManyCookiesServlet < WEBrick::HTTPServlet::AbstractServlet
306
+ def do_GET(req, res)
307
+ name_cookie = WEBrick::Cookie.new("name", "Aaron")
308
+ name_cookie.path = "/"
309
+ name_cookie.expires = Time.now + 86400
310
+ res.cookies << name_cookie
311
+ res.cookies << name_cookie
312
+ res.cookies << name_cookie
313
+ res.cookies << name_cookie
314
+
315
+ expired_cookie = WEBrick::Cookie.new("expired", "doh")
316
+ expired_cookie.path = "/"
317
+ expired_cookie.expires = Time.now - 86400
318
+ res.cookies << expired_cookie
319
+
320
+ different_path_cookie = WEBrick::Cookie.new("a_path", "some_path")
321
+ different_path_cookie.path = "/some_path"
322
+ different_path_cookie.expires = Time.now + 86400
323
+ res.cookies << different_path_cookie
324
+
325
+ no_path_cookie = WEBrick::Cookie.new("no_path", "no_path")
326
+ no_path_cookie.expires = Time.now + 86400
327
+ res.cookies << no_path_cookie
328
+
329
+ no_exp_path_cookie = WEBrick::Cookie.new("no_expires", "nope")
330
+ no_exp_path_cookie.path = "/"
331
+ res.cookies << no_exp_path_cookie
332
+
333
+ res['Content-Type'] = "text/html"
334
+ res.body = "<html><body>hello</body></html>"
335
+ end
336
+ end
337
+
338
+ class ModifiedSinceServlet < WEBrick::HTTPServlet::AbstractServlet
339
+ def do_GET(req, res)
340
+ s_time = 'Fri, 04 May 2001 00:00:38 GMT'
341
+
342
+ my_time = Time.parse(s_time)
343
+
344
+ if req['If-Modified-Since']
345
+ your_time = Time.parse(req['If-Modified-Since'])
346
+ if my_time > your_time
347
+ res.body = 'This page was updated since you requested'
348
+ else
349
+ res.status = 304
350
+ end
351
+ else
352
+ res.body = 'You did not send an If-Modified-Since header'
353
+ end
354
+
355
+ res['Last-Modified'] = s_time
356
+ end
357
+ end
358
+
359
+ class NTLMServlet < WEBrick::HTTPServlet::AbstractServlet
360
+
361
+ def do_GET(req, res)
362
+ if req['Authorization'] =~ /^NTLM (.*)/ then
363
+ authorization = $1.unpack('m*').first
364
+
365
+ if authorization =~ /^NTLMSSP\000\001/ then
366
+ type_2 = 'TlRMTVNTUAACAAAADAAMADAAAAABAoEAASNFZ4mr' \
367
+ 'ze8AAAAAAAAAAGIAYgA8AAAARABPAE0AQQBJAE4A' \
368
+ 'AgAMAEQATwBNAEEASQBOAAEADABTAEUAUgBWAEUA' \
369
+ 'UgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIA' \
370
+ 'cwBlAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMA' \
371
+ 'bwBtAAAAAAA='
372
+
373
+ res['WWW-Authenticate'] = "NTLM #{type_2}"
374
+ res.status = 401
375
+ elsif authorization =~ /^NTLMSSP\000\003/ then
376
+ res.body = 'ok'
377
+ else
378
+ res['WWW-Authenticate'] = 'NTLM'
379
+ res.status = 401
380
+ end
381
+ else
382
+ res['WWW-Authenticate'] = 'NTLM'
383
+ res.status = 401
384
+ end
385
+ end
386
+
387
+ end
388
+
389
+ class OneCookieNoSpacesServlet < WEBrick::HTTPServlet::AbstractServlet
390
+ def do_GET(req, res)
391
+ cookie = WEBrick::Cookie.new("foo", "bar")
392
+ cookie.path = "/"
393
+ cookie.expires = Time.now + 86400
394
+ res.cookies << cookie.to_s.gsub(/; /, ';')
395
+ res['Content-Type'] = "text/html"
396
+ res.body = "<html><body>hello</body></html>"
397
+ end
398
+ end
399
+
400
+ class OneCookieServlet < WEBrick::HTTPServlet::AbstractServlet
401
+ def do_GET(req, res)
402
+ cookie = WEBrick::Cookie.new("foo", "bar")
403
+ cookie.path = "/"
404
+ cookie.expires = Time.now + 86400
405
+ res.cookies << cookie
406
+ res['Content-Type'] = "text/html"
407
+ res.body = "<html><body>hello</body></html>"
408
+ end
409
+ end
410
+
411
+ class QuotedValueCookieServlet < WEBrick::HTTPServlet::AbstractServlet
412
+ def do_GET(req, res)
413
+ cookie = WEBrick::Cookie.new("quoted", "\"value\"")
414
+ cookie.path = "/"
415
+ cookie.expires = Time.now + 86400
416
+ res.cookies << cookie
417
+ res['Content-Type'] = "text/html"
418
+ res.body = "<html><body>hello</body></html>"
419
+ end
420
+ end
421
+
422
+ class RedirectServlet < WEBrick::HTTPServlet::AbstractServlet
423
+ def do_GET(req, res)
424
+ res['Content-Type'] = req.query['ct'] || "text/html"
425
+ res.status = req.query['code'] ? req.query['code'].to_i : '302'
426
+ res['Location'] = "/verb"
427
+ end
428
+
429
+ alias :do_POST :do_GET
430
+ alias :do_HEAD :do_GET
431
+ alias :do_PUT :do_GET
432
+ alias :do_DELETE :do_GET
433
+ end
434
+
435
+ class RefererServlet < WEBrick::HTTPServlet::AbstractServlet
436
+ def do_GET(req, res)
437
+ res['Content-Type'] = "text/html"
438
+ res.body = req['Referer'] || ''
439
+ end
440
+
441
+ def do_POST(req, res)
442
+ res['Content-Type'] = "text/html"
443
+ res.body = req['Referer'] || ''
444
+ end
445
+ end
446
+
447
+ class RefreshWithoutUrl < WEBrick::HTTPServlet::AbstractServlet
448
+ @@count = 0
449
+ def do_GET(req, res)
450
+ res['Content-Type'] = "text/html"
451
+ @@count += 1
452
+ if @@count > 1
453
+ res['Refresh'] = "0; url=http://localhost/index.html";
454
+ else
455
+ res['Refresh'] = "0";
456
+ end
457
+ end
458
+ end
459
+
460
+ class RefreshWithEmptyUrl < WEBrick::HTTPServlet::AbstractServlet
461
+ @@count = 0
462
+ def do_GET(req, res)
463
+ res['Content-Type'] = "text/html"
464
+ @@count += 1
465
+ if @@count > 1
466
+ res['Refresh'] = "0; url=http://localhost/index.html";
467
+ else
468
+ res['Refresh'] = "0; url=";
469
+ end
470
+ end
471
+ end
472
+
473
+ class ResponseCodeServlet < WEBrick::HTTPServlet::AbstractServlet
474
+ def do_GET(req, res)
475
+ res['Content-Type'] = req.query['ct'] || "text/html"
476
+ if req.query['code']
477
+ code = req.query['code'].to_i
478
+ case code
479
+ when 300, 301, 302, 303, 304, 305, 307
480
+ res['Location'] = "/index.html"
481
+ end
482
+ res.status = code
483
+ else
484
+ end
485
+ end
486
+ end
487
+
488
+ class SendCookiesServlet < WEBrick::HTTPServlet::AbstractServlet
489
+ def do_GET(req, res)
490
+ res['Content-Type'] = "text/html"
491
+ res.body = "<html><body>"
492
+ req.cookies.each { |c|
493
+ res.body << "<a href=\"#\">#{c.name}:#{c.value}</a>"
494
+ }
495
+ res.body << "</body></html>"
496
+ end
497
+ end
498
+
499
+ class VerbServlet < WEBrick::HTTPServlet::AbstractServlet
500
+ %w(HEAD GET POST PUT DELETE).each do |verb|
501
+ eval(<<-eomethod)
502
+ def do_#{verb}(req, res)
503
+ res.header['X-Request-Method'] = #{verb.dump}
504
+ end
505
+ eomethod
506
+ end
507
+ end
508
+
509
+ class Net::HTTP
510
+ alias :old_do_start :do_start
511
+
512
+ def do_start
513
+ @started = true
514
+ end
515
+
516
+ SERVLETS = {
517
+ '/gzip' => GzipServlet,
518
+ '/form_post' => FormServlet,
519
+ '/basic_auth' => BasicAuthServlet,
520
+ '/form post' => FormServlet,
521
+ '/response_code' => ResponseCodeServlet,
522
+ '/http_refresh' => HttpRefreshServlet,
523
+ '/content_type_test' => ContentTypeServlet,
524
+ '/referer' => RefererServlet,
525
+ '/file_upload' => FileUploadServlet,
526
+ '/one_cookie' => OneCookieServlet,
527
+ '/one_cookie_no_space' => OneCookieNoSpacesServlet,
528
+ '/many_cookies' => ManyCookiesServlet,
529
+ '/many_cookies_as_string' => ManyCookiesAsStringServlet,
530
+ '/ntlm' => NTLMServlet,
531
+ '/send_cookies' => SendCookiesServlet,
532
+ '/quoted_value_cookie' => QuotedValueCookieServlet,
533
+ '/if_modified_since' => ModifiedSinceServlet,
534
+ '/http_headers' => HeaderServlet,
535
+ '/infinite_redirect' => InfiniteRedirectServlet,
536
+ '/infinite_refresh' => InfiniteRefreshServlet,
537
+ '/redirect' => RedirectServlet,
538
+ '/refresh_without_url' => RefreshWithoutUrl,
539
+ '/refresh_with_empty_url' => RefreshWithEmptyUrl,
540
+ '/digest_auth' => DigestAuthServlet,
541
+ '/verb' => VerbServlet,
542
+ }
543
+
544
+ PAGE_CACHE = {}
545
+
546
+ alias :old_request :request
547
+
548
+ def request(req, *data, &block)
549
+ url = URI.parse(req.path)
550
+ path = WEBrick::HTTPUtils.unescape(url.path)
551
+
552
+ path = '/index.html' if path == '/'
553
+
554
+ res = ::Response.new
555
+ res.query_params = url.query
556
+
557
+ req.query = if 'POST' != req.method && url.query then
558
+ WEBrick::HTTPUtils.parse_query url.query
559
+ elsif req['content-type'] =~ /www-form-urlencoded/ then
560
+ WEBrick::HTTPUtils.parse_query req.body
561
+ elsif req['content-type'] =~ /boundary=(.+)/ then
562
+ boundary = WEBrick::HTTPUtils.dequote $1
563
+ WEBrick::HTTPUtils.parse_form_data req.body, boundary
564
+ else
565
+ {}
566
+ end
567
+
568
+ req.cookies = WEBrick::Cookie.parse(req['Cookie'])
569
+
570
+ Mechanize::TestCase::REQUESTS << req
571
+
572
+ if servlet_klass = SERVLETS[path]
573
+ servlet = servlet_klass.new({})
574
+ servlet.send "do_#{req.method}", req, res
575
+ else
576
+ filename = "htdocs#{path.gsub(/[^\/\\.\w\s]/, '_')}"
577
+ unless PAGE_CACHE[filename]
578
+ open("#{Mechanize::TestCase::TEST_DIR}/#{filename}", 'rb') { |io|
579
+ PAGE_CACHE[filename] = io.read
580
+ }
581
+ end
582
+
583
+ res.body = PAGE_CACHE[filename]
584
+ case filename
585
+ when /\.txt$/
586
+ res['Content-Type'] = 'text/plain'
587
+ when /\.jpg$/
588
+ res['Content-Type'] = 'image/jpeg'
589
+ end
590
+ end
591
+
592
+ res['Content-Type'] ||= 'text/html'
593
+ res.code ||= "200"
594
+
595
+ response_klass = Net::HTTPResponse::CODE_TO_OBJ[res.code.to_s]
596
+ response = response_klass.new res.http_version, res.code, res.message
597
+
598
+ res.header.each do |k,v|
599
+ v = v.first if v.length == 1
600
+ response[k] = v
601
+ end
602
+
603
+ res.cookies.each do |cookie|
604
+ response.add_field 'Set-Cookie', cookie.to_s
605
+ end
606
+
607
+ response['Content-Type'] ||= 'text/html'
608
+ response['Content-Length'] = res['Content-Length'] || res.body.length.to_s
609
+
610
+ io = StringIO.new(res.body)
611
+ response.instance_variable_set :@socket, io
612
+ def io.read clen, dest, _
613
+ dest << string[0, clen]
614
+ end
615
+
616
+ body_exist = req.response_body_permitted? &&
617
+ response_klass.body_permitted?
618
+
619
+ response.instance_variable_set :@body_exist, body_exist
620
+
621
+ yield response if block_given?
622
+
623
+ response
624
+ end
625
+ end
626
+
627
+ class Net::HTTPRequest
628
+ attr_accessor :query, :body, :cookies, :user
629
+ end
630
+
631
+ class Response
632
+ include Net::HTTPHeader
633
+
634
+ attr_reader :code
635
+ attr_accessor :body, :query, :cookies
636
+ attr_accessor :query_params, :http_version
637
+ attr_accessor :header
638
+
639
+ def code=(c)
640
+ @code = c.to_s
641
+ end
642
+
643
+ alias :status :code
644
+ alias :status= :code=
645
+
646
+ def initialize
647
+ @header = {}
648
+ @body = ''
649
+ @code = nil
650
+ @query = nil
651
+ @cookies = []
652
+ @http_version = '1.1'
653
+ end
654
+
655
+ def read_body
656
+ yield body
657
+ end
658
+
659
+ def message
660
+ ''
661
+ end
662
+ end
663
+