freels-mongrel 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +12 -0
- data/COPYING +55 -0
- data/LICENSE +55 -0
- data/Manifest +70 -0
- data/README +74 -0
- data/Rakefile +237 -0
- data/TODO +4 -0
- data/bin/mongrel_rails +284 -0
- data/examples/builder.rb +29 -0
- data/examples/camping/README +3 -0
- data/examples/camping/blog.rb +294 -0
- data/examples/camping/tepee.rb +149 -0
- data/examples/httpd.conf +474 -0
- data/examples/mime.yaml +3 -0
- data/examples/mongrel.conf +9 -0
- data/examples/mongrel_simple_ctrl.rb +92 -0
- data/examples/mongrel_simple_service.rb +116 -0
- data/examples/monitrc +57 -0
- data/examples/random_thrash.rb +19 -0
- data/examples/simpletest.rb +52 -0
- data/examples/webrick_compare.rb +20 -0
- data/ext/http11/ext_help.h +15 -0
- data/ext/http11/extconf.rb +6 -0
- data/ext/http11/http11.c +527 -0
- data/ext/http11/http11_parser.c +1216 -0
- data/ext/http11/http11_parser.h +49 -0
- data/ext/http11/http11_parser.java.rl +171 -0
- data/ext/http11/http11_parser.rl +165 -0
- data/ext/http11/http11_parser_common.rl +55 -0
- data/ext/http11_java/Http11Service.java +13 -0
- data/ext/http11_java/org/jruby/mongrel/Http11.java +266 -0
- data/ext/http11_java/org/jruby/mongrel/Http11Parser.java +572 -0
- data/lib/mongrel.rb +359 -0
- data/lib/mongrel/camping.rb +107 -0
- data/lib/mongrel/cgi.rb +182 -0
- data/lib/mongrel/command.rb +220 -0
- data/lib/mongrel/configurator.rb +389 -0
- data/lib/mongrel/const.rb +114 -0
- data/lib/mongrel/debug.rb +203 -0
- data/lib/mongrel/gems.rb +22 -0
- data/lib/mongrel/handlers.rb +472 -0
- data/lib/mongrel/header_out.rb +28 -0
- data/lib/mongrel/http_request.rb +155 -0
- data/lib/mongrel/http_response.rb +163 -0
- data/lib/mongrel/init.rb +10 -0
- data/lib/mongrel/logger.rb +74 -0
- data/lib/mongrel/mime_types.yml +616 -0
- data/lib/mongrel/rails.rb +185 -0
- data/lib/mongrel/stats.rb +89 -0
- data/lib/mongrel/tcphack.rb +18 -0
- data/lib/mongrel/uri_classifier.rb +76 -0
- data/mongrel-public_cert.pem +20 -0
- data/mongrel.gemspec +47 -0
- data/setup.rb +1585 -0
- data/test/mime.yaml +3 -0
- data/test/mongrel.conf +1 -0
- data/test/test_cgi_wrapper.rb +26 -0
- data/test/test_command.rb +86 -0
- data/test/test_conditional.rb +107 -0
- data/test/test_configurator.rb +88 -0
- data/test/test_debug.rb +25 -0
- data/test/test_handlers.rb +104 -0
- data/test/test_http11.rb +272 -0
- data/test/test_redirect_handler.rb +44 -0
- data/test/test_request_progress.rb +100 -0
- data/test/test_response.rb +127 -0
- data/test/test_stats.rb +35 -0
- data/test/test_uriclassifier.rb +261 -0
- data/test/test_ws.rb +116 -0
- data/test/testhelp.rb +74 -0
- data/tools/trickletest.rb +45 -0
- metadata +202 -0
data/test/test_http11.rb
ADDED
@@ -0,0 +1,272 @@
|
|
1
|
+
# Copyright (c) 2005 Zed A. Shaw
|
2
|
+
# You can redistribute it and/or modify it under the same terms as Ruby.
|
3
|
+
#
|
4
|
+
# Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html
|
5
|
+
# for more information.
|
6
|
+
|
7
|
+
require 'test/testhelp'
|
8
|
+
|
9
|
+
include Mongrel
|
10
|
+
|
11
|
+
class HttpParserTest < Test::Unit::TestCase
|
12
|
+
|
13
|
+
def test_parse_simple
|
14
|
+
parser = HttpParser.new
|
15
|
+
req = {}
|
16
|
+
http = "GET / HTTP/1.1\r\n\r\n"
|
17
|
+
nread = parser.execute(req, http, 0)
|
18
|
+
|
19
|
+
assert nread == http.length, "Failed to parse the full HTTP request"
|
20
|
+
assert parser.finished?, "Parser didn't finish"
|
21
|
+
assert !parser.error?, "Parser had error"
|
22
|
+
assert nread == parser.nread, "Number read returned from execute does not match"
|
23
|
+
|
24
|
+
assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
|
25
|
+
assert_equal '/', req['REQUEST_PATH']
|
26
|
+
assert_equal 'HTTP/1.1', req['HTTP_VERSION']
|
27
|
+
assert_equal '/', req['REQUEST_URI']
|
28
|
+
assert_equal 'CGI/1.2', req['GATEWAY_INTERFACE']
|
29
|
+
assert_equal 'GET', req['REQUEST_METHOD']
|
30
|
+
assert_nil req['FRAGMENT']
|
31
|
+
assert_nil req['QUERY_STRING']
|
32
|
+
|
33
|
+
parser.reset
|
34
|
+
assert parser.nread == 0, "Number read after reset should be 0"
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_parse_dumbfuck_headers
|
38
|
+
parser = HttpParser.new
|
39
|
+
req = {}
|
40
|
+
should_be_good = "GET / HTTP/1.1\r\naaaaaaaaaaaaa:++++++++++\r\n\r\n"
|
41
|
+
nread = parser.execute(req, should_be_good, 0)
|
42
|
+
assert_equal should_be_good.length, nread
|
43
|
+
assert parser.finished?
|
44
|
+
assert !parser.error?
|
45
|
+
assert_equal "++++++++++", req["HTTP_AAAAAAAAAAAAA"]
|
46
|
+
|
47
|
+
nasty_pound_header = "GET / HTTP/1.1\r\nX-SSL-Bullshit: -----BEGIN CERTIFICATE-----\r\n\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\r\n\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\r\n\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\r\n\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\r\n\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\r\n\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\r\n\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\r\n\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\r\n\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\r\n\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgEBBAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\r\n\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\r\n\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\r\n\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\r\n\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\r\n\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\r\n\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\r\n\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\r\n\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\r\n\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\r\n\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\r\n\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\r\n\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\r\n\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\r\n\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\r\n\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\r\n\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\r\n\tRA==\r\n\t-----END CERTIFICATE-----\r\n\r\n"
|
48
|
+
parser = HttpParser.new
|
49
|
+
req = {}
|
50
|
+
#nread = parser.execute(req, nasty_pound_header, 0)
|
51
|
+
#assert_equal nasty_pound_header.length, nread
|
52
|
+
#assert parser.finished?
|
53
|
+
#assert !parser.error?
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_parse_ie6_urls
|
57
|
+
%w(/some/random/path"
|
58
|
+
/some/random/path>
|
59
|
+
/some/random/path<
|
60
|
+
/we/love/you/ie6?q=<"">
|
61
|
+
/url?<="&>="
|
62
|
+
/mal"formed"?
|
63
|
+
).each do |path|
|
64
|
+
parser = HttpParser.new
|
65
|
+
req = {}
|
66
|
+
sorta_safe = %(GET #{path} HTTP/1.1\r\n\r\n)
|
67
|
+
nread = parser.execute(req, sorta_safe, 0)
|
68
|
+
assert_equal sorta_safe.length, nread
|
69
|
+
assert parser.finished?
|
70
|
+
assert !parser.error?
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_parse_error
|
75
|
+
parser = HttpParser.new
|
76
|
+
req = {}
|
77
|
+
bad_http = "GET / SsUTF/1.1"
|
78
|
+
|
79
|
+
error = false
|
80
|
+
begin
|
81
|
+
nread = parser.execute(req, bad_http, 0)
|
82
|
+
rescue => details
|
83
|
+
error = true
|
84
|
+
end
|
85
|
+
|
86
|
+
assert error, "failed to throw exception"
|
87
|
+
assert !parser.finished?, "Parser shouldn't be finished"
|
88
|
+
assert parser.error?, "Parser SHOULD have error"
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_parse_like_optimized_header
|
92
|
+
parser = HttpParser.new
|
93
|
+
req = {}
|
94
|
+
should_be_good = "GET / HTTP/1.1\r\nAuthorizationn: zz\r\n\r\n"
|
95
|
+
nread = parser.execute(req, should_be_good, 0)
|
96
|
+
assert_equal should_be_good.length, nread
|
97
|
+
assert parser.finished?
|
98
|
+
assert !parser.error?
|
99
|
+
assert_equal "zz", req["HTTP_AUTHORIZATIONN"]
|
100
|
+
assert ! req["HTTP_AUTHORIZATION"]
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_parse_twin_lookalike_optimized_headers
|
104
|
+
parser = HttpParser.new
|
105
|
+
req = {}
|
106
|
+
should_be_good = "GET / HTTP/1.1\r\n" \
|
107
|
+
"Accept-Encoding: abcdef\r\n" \
|
108
|
+
"Accept-Language: zyxvut\r\n" \
|
109
|
+
"\r\n"
|
110
|
+
nread = parser.execute(req, should_be_good, 0)
|
111
|
+
assert_equal should_be_good.length, nread
|
112
|
+
assert parser.finished?
|
113
|
+
assert !parser.error?
|
114
|
+
assert_equal "abcdef", req["HTTP_ACCEPT_ENCODING"]
|
115
|
+
assert_equal "zyxvut", req["HTTP_ACCEPT_LANGUAGE"]
|
116
|
+
end
|
117
|
+
|
118
|
+
if RUBY_PLATFORM !~ /java/
|
119
|
+
# as of now, the Java version does not have the same global-object
|
120
|
+
# reuse optimization the C version does
|
121
|
+
|
122
|
+
def test_parse_optimized_headers_global_objects_used
|
123
|
+
parser = HttpParser.new
|
124
|
+
req = {}
|
125
|
+
should_be_good = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
|
126
|
+
nread = parser.execute(req, should_be_good, 0)
|
127
|
+
assert_equal should_be_good.length, nread
|
128
|
+
assert parser.finished?
|
129
|
+
assert !parser.error?
|
130
|
+
assert_equal "example.com", req["HTTP_HOST"]
|
131
|
+
|
132
|
+
frozen_host_a = nil
|
133
|
+
req.each { |k,v| k == "HTTP_HOST" && frozen_host_a = k }
|
134
|
+
|
135
|
+
parser = HttpParser.new
|
136
|
+
req = {}
|
137
|
+
should_be_good = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
|
138
|
+
nread = parser.execute(req, should_be_good, 0)
|
139
|
+
assert_equal should_be_good.length, nread
|
140
|
+
assert parser.finished?
|
141
|
+
assert !parser.error?
|
142
|
+
|
143
|
+
frozen_host_b = nil
|
144
|
+
req.each { |k,v| k == "HTTP_HOST" && frozen_host_b = k }
|
145
|
+
assert_equal "HTTP_HOST", frozen_host_a
|
146
|
+
assert_equal "HTTP_HOST", frozen_host_b
|
147
|
+
assert_equal frozen_host_a.object_id, frozen_host_b.object_id
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def test_host_port_parsing
|
152
|
+
parser = HttpParser.new
|
153
|
+
req = {}
|
154
|
+
should_be_good = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
|
155
|
+
nread = parser.execute(req, should_be_good, 0)
|
156
|
+
assert_equal should_be_good.length, nread
|
157
|
+
assert parser.finished?
|
158
|
+
assert !parser.error?
|
159
|
+
assert_equal "example.com", req["HTTP_HOST"]
|
160
|
+
assert_equal "example.com", req["SERVER_NAME"]
|
161
|
+
assert_equal "80", req["SERVER_PORT"]
|
162
|
+
|
163
|
+
parser = HttpParser.new
|
164
|
+
req = {}
|
165
|
+
should_be_good = "GET / HTTP/1.1\r\nHost: example.com:123\r\n\r\n"
|
166
|
+
nread = parser.execute(req, should_be_good, 0)
|
167
|
+
assert_equal should_be_good.length, nread
|
168
|
+
assert parser.finished?
|
169
|
+
assert !parser.error?
|
170
|
+
assert_equal "example.com:123", req["HTTP_HOST"]
|
171
|
+
assert_equal "example.com", req["SERVER_NAME"]
|
172
|
+
assert_equal "123", req["SERVER_PORT"]
|
173
|
+
|
174
|
+
# null character in domain name is never actually valid, but if it
|
175
|
+
# becomes valid in Web 3.0, we'll be ready for it.
|
176
|
+
parser = HttpParser.new
|
177
|
+
req = {}
|
178
|
+
should_be_good = "GET / HTTP/1.1\r\nHost: example.com\0:123\r\n\r\n"
|
179
|
+
nread = parser.execute(req, should_be_good, 0)
|
180
|
+
assert_equal should_be_good.length, nread
|
181
|
+
assert parser.finished?
|
182
|
+
assert !parser.error?
|
183
|
+
assert_equal "example.com\0:123", req["HTTP_HOST"]
|
184
|
+
assert_equal "example.com\0", req["SERVER_NAME"]
|
185
|
+
assert_equal "123", req["SERVER_PORT"]
|
186
|
+
end
|
187
|
+
|
188
|
+
def test_fragment_in_uri
|
189
|
+
parser = HttpParser.new
|
190
|
+
req = {}
|
191
|
+
get = "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n\r\n"
|
192
|
+
assert_nothing_raised do
|
193
|
+
parser.execute(req, get, 0)
|
194
|
+
end
|
195
|
+
assert parser.finished?
|
196
|
+
assert_equal '/forums/1/topics/2375?page=1', req['REQUEST_URI']
|
197
|
+
assert_equal 'posts-17408', req['FRAGMENT']
|
198
|
+
end
|
199
|
+
|
200
|
+
# lame random garbage maker
|
201
|
+
def rand_data(min, max, readable=true)
|
202
|
+
count = min + ((rand(max)+1) *10).to_i
|
203
|
+
res = count.to_s + "/"
|
204
|
+
|
205
|
+
if readable
|
206
|
+
res << Digest::SHA1.hexdigest(rand(count * 100).to_s) * (count / 40)
|
207
|
+
else
|
208
|
+
res << Digest::SHA1.digest(rand(count * 100).to_s) * (count / 20)
|
209
|
+
end
|
210
|
+
|
211
|
+
return res
|
212
|
+
end
|
213
|
+
|
214
|
+
|
215
|
+
def test_horrible_queries
|
216
|
+
parser = HttpParser.new
|
217
|
+
|
218
|
+
# then that large header names are caught
|
219
|
+
10.times do |c|
|
220
|
+
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-#{rand_data(1024, 1024+(c*1024))}: Test\r\n\r\n"
|
221
|
+
assert_raises Mongrel::HttpParserError do
|
222
|
+
parser.execute({}, get, 0)
|
223
|
+
parser.reset
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# then that large mangled field values are caught
|
228
|
+
10.times do |c|
|
229
|
+
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-Test: #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
|
230
|
+
assert_raises Mongrel::HttpParserError do
|
231
|
+
parser.execute({}, get, 0)
|
232
|
+
parser.reset
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# then large headers are rejected too
|
237
|
+
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\n"
|
238
|
+
get << "X-Test: test\r\n" * (80 * 1024)
|
239
|
+
assert_raises Mongrel::HttpParserError do
|
240
|
+
parser.execute({}, get, 0)
|
241
|
+
parser.reset
|
242
|
+
end
|
243
|
+
|
244
|
+
# finally just that random garbage gets blocked all the time
|
245
|
+
10.times do |c|
|
246
|
+
get = "GET #{rand_data(1024, 1024+(c*1024), false)} #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
|
247
|
+
assert_raises Mongrel::HttpParserError do
|
248
|
+
parser.execute({}, get, 0)
|
249
|
+
parser.reset
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
|
257
|
+
def test_query_parse
|
258
|
+
res = HttpRequest.query_parse("zed=1&frank=#{HttpRequest.escape('&&& ')}")
|
259
|
+
assert res["zed"], "didn't get the request right"
|
260
|
+
assert res["frank"], "no frank"
|
261
|
+
assert_equal "1", res["zed"], "wrong result"
|
262
|
+
assert_equal "&&& ", HttpRequest.unescape(res["frank"]), "wrong result"
|
263
|
+
|
264
|
+
res = HttpRequest.query_parse("zed=1&zed=2&zed=3&frank=11;zed=45")
|
265
|
+
assert res["zed"], "didn't get the request right"
|
266
|
+
assert res["frank"], "no frank"
|
267
|
+
assert_equal 4,res["zed"].length, "wrong number for zed"
|
268
|
+
assert_equal "11",res["frank"], "wrong number for frank"
|
269
|
+
end
|
270
|
+
|
271
|
+
end
|
272
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Copyright (c) 2005 Zed A. Shaw
|
2
|
+
# You can redistribute it and/or modify it under the same terms as Ruby.
|
3
|
+
#
|
4
|
+
# Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html
|
5
|
+
# for more information.
|
6
|
+
|
7
|
+
require 'test/testhelp'
|
8
|
+
|
9
|
+
class RedirectHandlerTest < Test::Unit::TestCase
|
10
|
+
|
11
|
+
def setup
|
12
|
+
redirect_test_io do
|
13
|
+
@server = Mongrel::HttpServer.new('127.0.0.1', process_based_port)
|
14
|
+
end
|
15
|
+
@server.run
|
16
|
+
@client = Net::HTTP.new('127.0.0.1', process_based_port)
|
17
|
+
end
|
18
|
+
|
19
|
+
def teardown
|
20
|
+
@server.stop(true)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_simple_redirect
|
24
|
+
tester = Mongrel::RedirectHandler.new('/yo')
|
25
|
+
@server.register("/test", tester)
|
26
|
+
|
27
|
+
sleep(1)
|
28
|
+
res = @client.request_get('/test')
|
29
|
+
assert res != nil, "Didn't get a response"
|
30
|
+
assert_equal ['/yo'], res.get_fields('Location')
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_rewrite
|
34
|
+
tester = Mongrel::RedirectHandler.new(/(\w+)/, '+\1+')
|
35
|
+
@server.register("/test", tester)
|
36
|
+
|
37
|
+
sleep(1)
|
38
|
+
res = @client.request_get('/test/something')
|
39
|
+
assert_equal ['/+test+/+something+'], res.get_fields('Location')
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# Copyright (c) 2005 Zed A. Shaw
|
2
|
+
# You can redistribute it and/or modify it under the same terms as Ruby.
|
3
|
+
#
|
4
|
+
# Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html
|
5
|
+
# for more information.
|
6
|
+
|
7
|
+
require 'test/testhelp'
|
8
|
+
|
9
|
+
class UploadBeginHandler < Mongrel::HttpHandler
|
10
|
+
attr_reader :request_began, :request_progressed, :request_processed
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@request_notify = true
|
14
|
+
end
|
15
|
+
|
16
|
+
def reset
|
17
|
+
@request_began = false
|
18
|
+
@request_progressed = false
|
19
|
+
@request_processed = false
|
20
|
+
end
|
21
|
+
|
22
|
+
def request_begins(params)
|
23
|
+
@request_began = true
|
24
|
+
end
|
25
|
+
|
26
|
+
def request_progress(params,len,total)
|
27
|
+
@request_progressed = true
|
28
|
+
end
|
29
|
+
|
30
|
+
def process(request, response)
|
31
|
+
@request_processed = true
|
32
|
+
response.start do |head,body|
|
33
|
+
body.write("test")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
class RequestProgressTest < Test::Unit::TestCase
|
40
|
+
def setup
|
41
|
+
@port = process_based_port
|
42
|
+
redirect_test_io do
|
43
|
+
@server = Mongrel::HttpServer.new("127.0.0.1", @port)
|
44
|
+
end
|
45
|
+
@handler = UploadBeginHandler.new
|
46
|
+
@server.register("/upload", @handler)
|
47
|
+
@server.run
|
48
|
+
end
|
49
|
+
|
50
|
+
def teardown
|
51
|
+
@server.stop(true)
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_begin_end_progress
|
55
|
+
Net::HTTP.get("localhost", "/upload", @port)
|
56
|
+
assert @handler.request_began
|
57
|
+
assert @handler.request_progressed
|
58
|
+
assert @handler.request_processed
|
59
|
+
end
|
60
|
+
|
61
|
+
def call_and_assert_handlers_in_turn(handlers)
|
62
|
+
# reset all handlers
|
63
|
+
handlers.each { |h| h.reset }
|
64
|
+
|
65
|
+
# make the call
|
66
|
+
Net::HTTP.get("localhost", "/upload", @port)
|
67
|
+
|
68
|
+
# assert that each one was fired
|
69
|
+
handlers.each { |h|
|
70
|
+
assert h.request_began && h.request_progressed && h.request_processed,
|
71
|
+
"Callbacks NOT fired for #{h}"
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_more_than_one_begin_end_progress
|
76
|
+
handlers = [@handler]
|
77
|
+
|
78
|
+
second = UploadBeginHandler.new
|
79
|
+
@server.register("/upload", second)
|
80
|
+
handlers << second
|
81
|
+
call_and_assert_handlers_in_turn(handlers)
|
82
|
+
|
83
|
+
# check three handlers
|
84
|
+
third = UploadBeginHandler.new
|
85
|
+
@server.register("/upload", third)
|
86
|
+
handlers << third
|
87
|
+
call_and_assert_handlers_in_turn(handlers)
|
88
|
+
|
89
|
+
# remove handlers to make sure they've all gone away
|
90
|
+
@server.unregister("/upload")
|
91
|
+
handlers.each { |h| h.reset }
|
92
|
+
Net::HTTP.get("localhost", "/upload", @port)
|
93
|
+
handlers.each { |h|
|
94
|
+
assert !h.request_began && !h.request_progressed && !h.request_processed
|
95
|
+
}
|
96
|
+
|
97
|
+
# re-register upload to the state before this test
|
98
|
+
@server.register("/upload", @handler)
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# Copyright (c) 2005 Zed A. Shaw
|
2
|
+
# You can redistribute it and/or modify it under the same terms as Ruby.
|
3
|
+
#
|
4
|
+
# Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html
|
5
|
+
# for more information.
|
6
|
+
|
7
|
+
require 'test/testhelp'
|
8
|
+
|
9
|
+
include Mongrel
|
10
|
+
|
11
|
+
class ResponseTest < Test::Unit::TestCase
|
12
|
+
|
13
|
+
def test_response_headers
|
14
|
+
out = StringIO.new
|
15
|
+
resp = HttpResponse.new(out)
|
16
|
+
resp.status = 200
|
17
|
+
resp.header["Accept"] = "text/plain"
|
18
|
+
resp.header["X-Whatever"] = "stuff"
|
19
|
+
resp.body.write("test")
|
20
|
+
resp.finished
|
21
|
+
|
22
|
+
assert out.length > 0, "output didn't have data"
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_response_200
|
26
|
+
io = StringIO.new
|
27
|
+
resp = HttpResponse.new(io)
|
28
|
+
resp.start do |head,out|
|
29
|
+
head["Accept"] = "text/plain"
|
30
|
+
out.write("tested")
|
31
|
+
out.write("hello!")
|
32
|
+
end
|
33
|
+
|
34
|
+
resp.finished
|
35
|
+
assert io.length > 0, "output didn't have data"
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_response_duplicate_header_squash
|
39
|
+
io = StringIO.new
|
40
|
+
resp = HttpResponse.new(io)
|
41
|
+
resp.start do |head,out|
|
42
|
+
head["Content-Length"] = 30
|
43
|
+
head["Content-Length"] = 0
|
44
|
+
end
|
45
|
+
|
46
|
+
resp.finished
|
47
|
+
|
48
|
+
assert_equal io.length, 95, "too much output"
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
def test_response_some_duplicates_allowed
|
53
|
+
allowed_duplicates = ["Set-Cookie", "Set-Cookie2", "Warning", "WWW-Authenticate"]
|
54
|
+
io = StringIO.new
|
55
|
+
resp = HttpResponse.new(io)
|
56
|
+
resp.start do |head,out|
|
57
|
+
allowed_duplicates.each do |dup|
|
58
|
+
10.times do |i|
|
59
|
+
head[dup] = i
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
resp.finished
|
65
|
+
|
66
|
+
assert_equal io.length, 734, "wrong amount of output"
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_response_404
|
70
|
+
io = StringIO.new
|
71
|
+
|
72
|
+
resp = HttpResponse.new(io)
|
73
|
+
resp.start(404) do |head,out|
|
74
|
+
head['Accept'] = "text/plain"
|
75
|
+
out.write("NOT FOUND")
|
76
|
+
end
|
77
|
+
|
78
|
+
resp.finished
|
79
|
+
assert io.length > 0, "output didn't have data"
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_response_file
|
83
|
+
contents = "PLAIN TEXT\r\nCONTENTS\r\n"
|
84
|
+
require 'tempfile'
|
85
|
+
tmpf = Tempfile.new("test_response_file")
|
86
|
+
tmpf.binmode
|
87
|
+
tmpf.write(contents)
|
88
|
+
tmpf.rewind
|
89
|
+
|
90
|
+
io = StringIO.new
|
91
|
+
resp = HttpResponse.new(io)
|
92
|
+
resp.start(200) do |head,out|
|
93
|
+
head['Content-Type'] = 'text/plain'
|
94
|
+
resp.send_header
|
95
|
+
resp.send_file(tmpf.path)
|
96
|
+
end
|
97
|
+
io.rewind
|
98
|
+
tmpf.close
|
99
|
+
|
100
|
+
assert io.length > 0, "output didn't have data"
|
101
|
+
assert io.read[-contents.length..-1] == contents, "output doesn't end with file payload"
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_response_with_custom_reason
|
105
|
+
reason = "You made a bad request"
|
106
|
+
io = StringIO.new
|
107
|
+
resp = HttpResponse.new(io)
|
108
|
+
resp.start(400, false, reason) { |head,out| }
|
109
|
+
resp.finished
|
110
|
+
|
111
|
+
io.rewind
|
112
|
+
assert_match(/.* #{reason}$/, io.readline.chomp, "wrong custom reason phrase")
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_response_with_default_reason
|
116
|
+
code = 400
|
117
|
+
io = StringIO.new
|
118
|
+
resp = HttpResponse.new(io)
|
119
|
+
resp.start(code) { |head,out| }
|
120
|
+
resp.finished
|
121
|
+
|
122
|
+
io.rewind
|
123
|
+
assert_match(/.* #{HTTP_STATUS_CODES[code]}$/, io.readline.chomp, "wrong default reason phrase")
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|