rack 1.6.12 → 2.0.7
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rack might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/COPYING +1 -1
- data/HISTORY.md +138 -8
- data/README.rdoc +18 -28
- data/Rakefile +6 -14
- data/SPEC +10 -11
- data/contrib/rack_logo.svg +164 -111
- data/example/protectedlobster.rb +1 -1
- data/example/protectedlobster.ru +1 -1
- data/lib/rack.rb +70 -21
- data/lib/rack/auth/abstract/request.rb +5 -1
- data/lib/rack/auth/digest/params.rb +2 -3
- data/lib/rack/auth/digest/request.rb +1 -1
- data/lib/rack/body_proxy.rb +14 -9
- data/lib/rack/builder.rb +3 -3
- data/lib/rack/chunked.rb +5 -5
- data/lib/rack/{commonlogger.rb → common_logger.rb} +3 -3
- data/lib/rack/{conditionalget.rb → conditional_get.rb} +0 -0
- data/lib/rack/content_length.rb +2 -2
- data/lib/rack/deflater.rb +4 -39
- data/lib/rack/directory.rb +66 -54
- data/lib/rack/etag.rb +5 -4
- data/lib/rack/events.rb +154 -0
- data/lib/rack/file.rb +64 -40
- data/lib/rack/handler.rb +3 -25
- data/lib/rack/handler/cgi.rb +15 -16
- data/lib/rack/handler/fastcgi.rb +13 -14
- data/lib/rack/handler/lsws.rb +11 -11
- data/lib/rack/handler/scgi.rb +15 -15
- data/lib/rack/handler/thin.rb +3 -0
- data/lib/rack/handler/webrick.rb +24 -26
- data/lib/rack/head.rb +15 -17
- data/lib/rack/lint.rb +40 -40
- data/lib/rack/lobster.rb +1 -1
- data/lib/rack/lock.rb +15 -10
- data/lib/rack/logger.rb +2 -2
- data/lib/rack/media_type.rb +38 -0
- data/lib/rack/{methodoverride.rb → method_override.rb} +6 -6
- data/lib/rack/mime.rb +18 -5
- data/lib/rack/mock.rb +36 -54
- data/lib/rack/multipart.rb +35 -6
- data/lib/rack/multipart/generator.rb +5 -5
- data/lib/rack/multipart/parser.rb +270 -157
- data/lib/rack/multipart/uploaded_file.rb +1 -2
- data/lib/rack/{nulllogger.rb → null_logger.rb} +1 -1
- data/lib/rack/query_parser.rb +192 -0
- data/lib/rack/recursive.rb +8 -8
- data/lib/rack/request.rb +394 -305
- data/lib/rack/response.rb +130 -57
- data/lib/rack/rewindable_input.rb +1 -12
- data/lib/rack/runtime.rb +10 -18
- data/lib/rack/sendfile.rb +5 -7
- data/lib/rack/server.rb +30 -23
- data/lib/rack/session/abstract/id.rb +108 -138
- data/lib/rack/session/cookie.rb +26 -28
- data/lib/rack/session/memcache.rb +8 -14
- data/lib/rack/session/pool.rb +14 -21
- data/lib/rack/show_exceptions.rb +386 -0
- data/lib/rack/{showstatus.rb → show_status.rb} +3 -3
- data/lib/rack/static.rb +30 -5
- data/lib/rack/tempfile_reaper.rb +2 -2
- data/lib/rack/urlmap.rb +15 -14
- data/lib/rack/utils.rb +136 -211
- data/rack.gemspec +10 -9
- data/test/builder/an_underscore_app.rb +5 -0
- data/test/builder/options.ru +1 -1
- data/test/cgi/test.fcgi +1 -0
- data/test/cgi/test.gz +0 -0
- data/test/helper.rb +34 -0
- data/test/multipart/filename_with_encoded_words +7 -0
- data/test/multipart/filename_with_single_quote +7 -0
- data/test/multipart/quoted +15 -0
- data/test/multipart/rack-logo.png +0 -0
- data/test/multipart/unity3d_wwwform +11 -0
- data/test/registering_handler/rack/handler/registering_myself.rb +1 -1
- data/test/spec_auth_basic.rb +27 -19
- data/test/spec_auth_digest.rb +47 -46
- data/test/spec_body_proxy.rb +27 -27
- data/test/spec_builder.rb +51 -41
- data/test/spec_cascade.rb +24 -22
- data/test/spec_cgi.rb +49 -67
- data/test/spec_chunked.rb +37 -35
- data/test/{spec_commonlogger.rb → spec_common_logger.rb} +23 -21
- data/test/{spec_conditionalget.rb → spec_conditional_get.rb} +29 -28
- data/test/spec_config.rb +3 -2
- data/test/spec_content_length.rb +18 -17
- data/test/spec_content_type.rb +13 -12
- data/test/spec_deflater.rb +85 -49
- data/test/spec_directory.rb +87 -27
- data/test/spec_etag.rb +32 -31
- data/test/spec_events.rb +133 -0
- data/test/spec_fastcgi.rb +50 -72
- data/test/spec_file.rb +120 -77
- data/test/spec_handler.rb +19 -34
- data/test/spec_head.rb +15 -14
- data/test/spec_lint.rb +164 -199
- data/test/spec_lobster.rb +24 -23
- data/test/spec_lock.rb +79 -39
- data/test/spec_logger.rb +4 -3
- data/test/spec_media_type.rb +42 -0
- data/test/{spec_methodoverride.rb → spec_method_override.rb} +34 -35
- data/test/spec_mime.rb +19 -19
- data/test/spec_mock.rb +206 -144
- data/test/spec_multipart.rb +322 -200
- data/test/{spec_nulllogger.rb → spec_null_logger.rb} +5 -4
- data/test/spec_recursive.rb +17 -14
- data/test/spec_request.rb +780 -605
- data/test/spec_response.rb +215 -112
- data/test/spec_rewindable_input.rb +50 -40
- data/test/spec_runtime.rb +11 -10
- data/test/spec_sendfile.rb +30 -35
- data/test/spec_server.rb +78 -52
- data/test/spec_session_abstract_id.rb +11 -33
- data/test/spec_session_abstract_session_hash.rb +45 -0
- data/test/spec_session_cookie.rb +99 -67
- data/test/spec_session_memcache.rb +63 -101
- data/test/spec_session_pool.rb +48 -84
- data/test/{spec_showexceptions.rb → spec_show_exceptions.rb} +23 -28
- data/test/{spec_showstatus.rb → spec_show_status.rb} +36 -35
- data/test/spec_static.rb +71 -32
- data/test/spec_tempfile_reaper.rb +11 -10
- data/test/spec_thin.rb +55 -50
- data/test/spec_urlmap.rb +79 -78
- data/test/spec_utils.rb +441 -346
- data/test/spec_version.rb +2 -8
- data/test/spec_webrick.rb +93 -71
- data/test/static/foo.html +1 -0
- data/test/testrequest.rb +1 -1
- data/test/unregistered_handler/rack/handler/unregistered.rb +1 -1
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +1 -1
- metadata +92 -70
- data/KNOWN-ISSUES +0 -44
- data/lib/rack/backports/uri/common_18.rb +0 -56
- data/lib/rack/backports/uri/common_192.rb +0 -52
- data/lib/rack/backports/uri/common_193.rb +0 -29
- data/lib/rack/handler/evented_mongrel.rb +0 -8
- data/lib/rack/handler/mongrel.rb +0 -106
- data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
- data/lib/rack/showexceptions.rb +0 -387
- data/lib/rack/utils/okjson.rb +0 -600
- data/test/spec_mongrel.rb +0 -182
data/test/spec_etag.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'minitest/autorun'
|
1
2
|
require 'rack/etag'
|
2
3
|
require 'rack/lint'
|
3
4
|
require 'rack/mock'
|
@@ -7,101 +8,101 @@ describe Rack::ETag do
|
|
7
8
|
def etag(app, *args)
|
8
9
|
Rack::Lint.new Rack::ETag.new(app, *args)
|
9
10
|
end
|
10
|
-
|
11
|
+
|
11
12
|
def request
|
12
13
|
Rack::MockRequest.env_for
|
13
14
|
end
|
14
|
-
|
15
|
+
|
15
16
|
def sendfile_body
|
16
17
|
res = ['Hello World']
|
17
18
|
def res.to_path ; "/tmp/hello.txt" ; end
|
18
19
|
res
|
19
20
|
end
|
20
21
|
|
21
|
-
|
22
|
+
it "set ETag if none is set if status is 200" do
|
22
23
|
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
|
23
24
|
response = etag(app).call(request)
|
24
|
-
response[1]['ETag'].
|
25
|
+
response[1]['ETag'].must_equal "W/\"dffd6021bb2bd5b0af676290809ec3a5\""
|
25
26
|
end
|
26
27
|
|
27
|
-
|
28
|
+
it "set ETag if none is set if status is 201" do
|
28
29
|
app = lambda { |env| [201, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
|
29
30
|
response = etag(app).call(request)
|
30
|
-
response[1]['ETag'].
|
31
|
+
response[1]['ETag'].must_equal "W/\"dffd6021bb2bd5b0af676290809ec3a5\""
|
31
32
|
end
|
32
33
|
|
33
|
-
|
34
|
+
it "set Cache-Control to 'max-age=0, private, must-revalidate' (default) if none is set" do
|
34
35
|
app = lambda { |env| [201, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
|
35
36
|
response = etag(app).call(request)
|
36
|
-
response[1]['Cache-Control'].
|
37
|
+
response[1]['Cache-Control'].must_equal 'max-age=0, private, must-revalidate'
|
37
38
|
end
|
38
39
|
|
39
|
-
|
40
|
+
it "set Cache-Control to chosen one if none is set" do
|
40
41
|
app = lambda { |env| [201, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
|
41
42
|
response = etag(app, nil, 'public').call(request)
|
42
|
-
response[1]['Cache-Control'].
|
43
|
+
response[1]['Cache-Control'].must_equal 'public'
|
43
44
|
end
|
44
45
|
|
45
|
-
|
46
|
+
it "set a given Cache-Control even if digest could not be calculated" do
|
46
47
|
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, []] }
|
47
48
|
response = etag(app, 'no-cache').call(request)
|
48
|
-
response[1]['Cache-Control'].
|
49
|
+
response[1]['Cache-Control'].must_equal 'no-cache'
|
49
50
|
end
|
50
51
|
|
51
|
-
|
52
|
+
it "not set Cache-Control if it is already set" do
|
52
53
|
app = lambda { |env| [201, {'Content-Type' => 'text/plain', 'Cache-Control' => 'public'}, ["Hello, World!"]] }
|
53
54
|
response = etag(app).call(request)
|
54
|
-
response[1]['Cache-Control'].
|
55
|
+
response[1]['Cache-Control'].must_equal 'public'
|
55
56
|
end
|
56
57
|
|
57
|
-
|
58
|
+
it "not set Cache-Control if directive isn't present" do
|
58
59
|
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
|
59
60
|
response = etag(app, nil, nil).call(request)
|
60
|
-
response[1]['Cache-Control'].
|
61
|
+
response[1]['Cache-Control'].must_be_nil
|
61
62
|
end
|
62
63
|
|
63
|
-
|
64
|
+
it "not change ETag if it is already set" do
|
64
65
|
app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'ETag' => '"abc"'}, ["Hello, World!"]] }
|
65
66
|
response = etag(app).call(request)
|
66
|
-
response[1]['ETag'].
|
67
|
+
response[1]['ETag'].must_equal "\"abc\""
|
67
68
|
end
|
68
69
|
|
69
|
-
|
70
|
+
it "not set ETag if body is empty" do
|
70
71
|
app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Last-Modified' => Time.now.httpdate}, []] }
|
71
72
|
response = etag(app).call(request)
|
72
|
-
response[1]['ETag'].
|
73
|
+
response[1]['ETag'].must_be_nil
|
73
74
|
end
|
74
75
|
|
75
|
-
|
76
|
+
it "not set ETag if Last-Modified is set" do
|
76
77
|
app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Last-Modified' => Time.now.httpdate}, ["Hello, World!"]] }
|
77
78
|
response = etag(app).call(request)
|
78
|
-
response[1]['ETag'].
|
79
|
+
response[1]['ETag'].must_be_nil
|
79
80
|
end
|
80
81
|
|
81
|
-
|
82
|
+
it "not set ETag if a sendfile_body is given" do
|
82
83
|
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, sendfile_body] }
|
83
84
|
response = etag(app).call(request)
|
84
|
-
response[1]['ETag'].
|
85
|
+
response[1]['ETag'].must_be_nil
|
85
86
|
end
|
86
87
|
|
87
|
-
|
88
|
+
it "not set ETag if a status is not 200 or 201" do
|
88
89
|
app = lambda { |env| [401, {'Content-Type' => 'text/plain'}, ['Access denied.']] }
|
89
90
|
response = etag(app).call(request)
|
90
|
-
response[1]['ETag'].
|
91
|
+
response[1]['ETag'].must_be_nil
|
91
92
|
end
|
92
93
|
|
93
|
-
|
94
|
+
it "not set ETag if no-cache is given" do
|
94
95
|
app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Cache-Control' => 'no-cache, must-revalidate'}, ['Hello, World!']] }
|
95
96
|
response = etag(app).call(request)
|
96
|
-
response[1]['ETag'].
|
97
|
+
response[1]['ETag'].must_be_nil
|
97
98
|
end
|
98
99
|
|
99
|
-
|
100
|
+
it "close the original body" do
|
100
101
|
body = StringIO.new
|
101
102
|
app = lambda { |env| [200, {}, body] }
|
102
103
|
response = etag(app).call(request)
|
103
|
-
body.
|
104
|
+
body.wont_be :closed?
|
104
105
|
response[2].close
|
105
|
-
body.
|
106
|
+
body.must_be :closed?
|
106
107
|
end
|
107
108
|
end
|
data/test/spec_events.rb
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'rack/events'
|
3
|
+
|
4
|
+
module Rack
|
5
|
+
class TestEvents < Rack::TestCase
|
6
|
+
class EventMiddleware
|
7
|
+
attr_reader :events
|
8
|
+
|
9
|
+
def initialize events
|
10
|
+
@events = events
|
11
|
+
end
|
12
|
+
|
13
|
+
def on_start req, res
|
14
|
+
events << [self, __method__]
|
15
|
+
end
|
16
|
+
|
17
|
+
def on_commit req, res
|
18
|
+
events << [self, __method__]
|
19
|
+
end
|
20
|
+
|
21
|
+
def on_send req, res
|
22
|
+
events << [self, __method__]
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_finish req, res
|
26
|
+
events << [self, __method__]
|
27
|
+
end
|
28
|
+
|
29
|
+
def on_error req, res, e
|
30
|
+
events << [self, __method__]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_events_fire
|
35
|
+
events = []
|
36
|
+
ret = [200, {}, []]
|
37
|
+
app = lambda { |env| events << [app, :call]; ret }
|
38
|
+
se = EventMiddleware.new events
|
39
|
+
e = Events.new app, [se]
|
40
|
+
triple = e.call({})
|
41
|
+
response_body = []
|
42
|
+
triple[2].each { |x| response_body << x }
|
43
|
+
triple[2].close
|
44
|
+
triple[2] = response_body
|
45
|
+
assert_equal ret, triple
|
46
|
+
assert_equal [[se, :on_start],
|
47
|
+
[app, :call],
|
48
|
+
[se, :on_commit],
|
49
|
+
[se, :on_send],
|
50
|
+
[se, :on_finish],
|
51
|
+
], events
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_send_and_finish_are_not_run_until_body_is_sent
|
55
|
+
events = []
|
56
|
+
ret = [200, {}, []]
|
57
|
+
app = lambda { |env| events << [app, :call]; ret }
|
58
|
+
se = EventMiddleware.new events
|
59
|
+
e = Events.new app, [se]
|
60
|
+
triple = e.call({})
|
61
|
+
assert_equal [[se, :on_start],
|
62
|
+
[app, :call],
|
63
|
+
[se, :on_commit],
|
64
|
+
], events
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_send_is_called_on_each
|
68
|
+
events = []
|
69
|
+
ret = [200, {}, []]
|
70
|
+
app = lambda { |env| events << [app, :call]; ret }
|
71
|
+
se = EventMiddleware.new events
|
72
|
+
e = Events.new app, [se]
|
73
|
+
triple = e.call({})
|
74
|
+
triple[2].each { |x| }
|
75
|
+
assert_equal [[se, :on_start],
|
76
|
+
[app, :call],
|
77
|
+
[se, :on_commit],
|
78
|
+
[se, :on_send],
|
79
|
+
], events
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_finish_is_called_on_close
|
83
|
+
events = []
|
84
|
+
ret = [200, {}, []]
|
85
|
+
app = lambda { |env| events << [app, :call]; ret }
|
86
|
+
se = EventMiddleware.new events
|
87
|
+
e = Events.new app, [se]
|
88
|
+
triple = e.call({})
|
89
|
+
triple[2].each { |x| }
|
90
|
+
triple[2].close
|
91
|
+
assert_equal [[se, :on_start],
|
92
|
+
[app, :call],
|
93
|
+
[se, :on_commit],
|
94
|
+
[se, :on_send],
|
95
|
+
[se, :on_finish],
|
96
|
+
], events
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_finish_is_called_in_reverse_order
|
100
|
+
events = []
|
101
|
+
ret = [200, {}, []]
|
102
|
+
app = lambda { |env| events << [app, :call]; ret }
|
103
|
+
se1 = EventMiddleware.new events
|
104
|
+
se2 = EventMiddleware.new events
|
105
|
+
se3 = EventMiddleware.new events
|
106
|
+
|
107
|
+
e = Events.new app, [se1, se2, se3]
|
108
|
+
triple = e.call({})
|
109
|
+
triple[2].each { |x| }
|
110
|
+
triple[2].close
|
111
|
+
|
112
|
+
groups = events.group_by { |x| x.last }
|
113
|
+
assert_equal groups[:on_start].map(&:first), groups[:on_finish].map(&:first).reverse
|
114
|
+
assert_equal groups[:on_commit].map(&:first), groups[:on_finish].map(&:first)
|
115
|
+
assert_equal groups[:on_send].map(&:first), groups[:on_finish].map(&:first)
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_finish_is_called_if_there_is_an_exception
|
119
|
+
events = []
|
120
|
+
ret = [200, {}, []]
|
121
|
+
app = lambda { |env| raise }
|
122
|
+
se = EventMiddleware.new events
|
123
|
+
e = Events.new app, [se]
|
124
|
+
assert_raises(RuntimeError) do
|
125
|
+
e.call({})
|
126
|
+
end
|
127
|
+
assert_equal [[se, :on_start],
|
128
|
+
[se, :on_error],
|
129
|
+
[se, :on_finish],
|
130
|
+
], events
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
data/test/spec_fastcgi.rb
CHANGED
@@ -1,107 +1,85 @@
|
|
1
|
-
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
if defined? LIGHTTPD_PID
|
4
|
+
|
2
5
|
require File.expand_path('../testrequest', __FILE__)
|
3
6
|
require 'rack/handler/fastcgi'
|
4
7
|
|
5
8
|
describe Rack::Handler::FastCGI do
|
6
|
-
|
9
|
+
include TestRequest::Helpers
|
7
10
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
if `which lighttpd` && !$?.success?
|
12
|
-
raise "lighttpd not found"
|
11
|
+
before do
|
12
|
+
@host = '127.0.0.1'
|
13
|
+
@port = 9203
|
13
14
|
end
|
14
15
|
|
15
|
-
|
16
|
-
$pid = fork {
|
17
|
-
ENV['RACK_ENV'] = 'deployment'
|
18
|
-
ENV['RUBYLIB'] = [
|
19
|
-
File.expand_path('../../lib', __FILE__),
|
20
|
-
ENV['RUBYLIB'],
|
21
|
-
].compact.join(':')
|
22
|
-
|
23
|
-
Dir.chdir(File.expand_path("../cgi", __FILE__)) do
|
24
|
-
exec "lighttpd -D -f lighttpd.conf"
|
25
|
-
end
|
26
|
-
}
|
27
|
-
|
28
|
-
should "respond" do
|
16
|
+
it "respond" do
|
29
17
|
sleep 1
|
30
18
|
GET("/test")
|
31
|
-
response.
|
19
|
+
response.wont_be :nil?
|
32
20
|
end
|
33
21
|
|
34
|
-
|
22
|
+
it "respond via rackup server" do
|
35
23
|
GET("/sample_rackup.ru")
|
36
|
-
status.
|
24
|
+
status.must_equal 200
|
37
25
|
end
|
38
26
|
|
39
|
-
|
27
|
+
it "be a lighttpd" do
|
40
28
|
GET("/test.fcgi")
|
41
|
-
status.
|
42
|
-
response["SERVER_SOFTWARE"].
|
43
|
-
response["HTTP_VERSION"].
|
44
|
-
response["SERVER_PROTOCOL"].
|
45
|
-
response["SERVER_PORT"].
|
46
|
-
response["SERVER_NAME"].
|
29
|
+
status.must_equal 200
|
30
|
+
response["SERVER_SOFTWARE"].must_match(/lighttpd/)
|
31
|
+
response["HTTP_VERSION"].must_equal "HTTP/1.1"
|
32
|
+
response["SERVER_PROTOCOL"].must_equal "HTTP/1.1"
|
33
|
+
response["SERVER_PORT"].must_equal @port.to_s
|
34
|
+
response["SERVER_NAME"].must_equal @host
|
47
35
|
end
|
48
36
|
|
49
|
-
|
37
|
+
it "have rack headers" do
|
50
38
|
GET("/test.fcgi")
|
51
|
-
response["rack.version"].
|
52
|
-
response["rack.multithread"]
|
53
|
-
response["rack.multiprocess"]
|
54
|
-
response["rack.run_once"]
|
39
|
+
response["rack.version"].must_equal [1,3]
|
40
|
+
assert_equal false, response["rack.multithread"]
|
41
|
+
assert_equal true, response["rack.multiprocess"]
|
42
|
+
assert_equal false, response["rack.run_once"]
|
55
43
|
end
|
56
44
|
|
57
|
-
|
45
|
+
it "have CGI headers on GET" do
|
58
46
|
GET("/test.fcgi")
|
59
|
-
response["REQUEST_METHOD"].
|
60
|
-
response["SCRIPT_NAME"].
|
61
|
-
response["REQUEST_PATH"].
|
62
|
-
response["PATH_INFO"].
|
63
|
-
response["QUERY_STRING"].
|
64
|
-
response["test.postdata"].
|
47
|
+
response["REQUEST_METHOD"].must_equal "GET"
|
48
|
+
response["SCRIPT_NAME"].must_equal "/test.fcgi"
|
49
|
+
response["REQUEST_PATH"].must_equal "/"
|
50
|
+
response["PATH_INFO"].must_equal ""
|
51
|
+
response["QUERY_STRING"].must_equal ""
|
52
|
+
response["test.postdata"].must_equal ""
|
65
53
|
|
66
54
|
GET("/test.fcgi/foo?quux=1")
|
67
|
-
response["REQUEST_METHOD"].
|
68
|
-
response["SCRIPT_NAME"].
|
69
|
-
response["REQUEST_PATH"].
|
70
|
-
response["PATH_INFO"].
|
71
|
-
response["QUERY_STRING"].
|
55
|
+
response["REQUEST_METHOD"].must_equal "GET"
|
56
|
+
response["SCRIPT_NAME"].must_equal "/test.fcgi"
|
57
|
+
response["REQUEST_PATH"].must_equal "/"
|
58
|
+
response["PATH_INFO"].must_equal "/foo"
|
59
|
+
response["QUERY_STRING"].must_equal "quux=1"
|
72
60
|
end
|
73
61
|
|
74
|
-
|
62
|
+
it "have CGI headers on POST" do
|
75
63
|
POST("/test.fcgi", {"rack-form-data" => "23"}, {'X-test-header' => '42'})
|
76
|
-
status.
|
77
|
-
response["REQUEST_METHOD"].
|
78
|
-
response["SCRIPT_NAME"].
|
79
|
-
response["REQUEST_PATH"].
|
80
|
-
response["QUERY_STRING"].
|
81
|
-
response["HTTP_X_TEST_HEADER"].
|
82
|
-
response["test.postdata"].
|
64
|
+
status.must_equal 200
|
65
|
+
response["REQUEST_METHOD"].must_equal "POST"
|
66
|
+
response["SCRIPT_NAME"].must_equal "/test.fcgi"
|
67
|
+
response["REQUEST_PATH"].must_equal "/"
|
68
|
+
response["QUERY_STRING"].must_equal ""
|
69
|
+
response["HTTP_X_TEST_HEADER"].must_equal "42"
|
70
|
+
response["test.postdata"].must_equal "rack-form-data=23"
|
83
71
|
end
|
84
72
|
|
85
|
-
|
73
|
+
it "support HTTP auth" do
|
86
74
|
GET("/test.fcgi", {:user => "ruth", :passwd => "secret"})
|
87
|
-
response["HTTP_AUTHORIZATION"].
|
75
|
+
response["HTTP_AUTHORIZATION"].must_equal "Basic cnV0aDpzZWNyZXQ="
|
88
76
|
end
|
89
77
|
|
90
|
-
|
78
|
+
it "set status" do
|
91
79
|
GET("/test.fcgi?secret")
|
92
|
-
status.
|
93
|
-
response["rack.url_scheme"].
|
94
|
-
end
|
95
|
-
|
96
|
-
# Keep this last.
|
97
|
-
should "shutdown" do
|
98
|
-
Process.kill 15, $pid
|
99
|
-
Process.wait($pid).should.equal $pid
|
80
|
+
status.must_equal 403
|
81
|
+
response["rack.url_scheme"].must_equal "http"
|
100
82
|
end
|
101
83
|
end
|
102
84
|
|
103
|
-
|
104
|
-
$stderr.puts "Skipping Rack::Handler::FastCGI tests (lighttpd is required). Install lighttpd and try again."
|
105
|
-
rescue LoadError
|
106
|
-
$stderr.puts "Skipping Rack::Handler::FastCGI tests (FCGI is required). `gem install fcgi` and try again."
|
107
|
-
end
|
85
|
+
end # if defined? LIGHTTPD_PID
|
data/test/spec_file.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'minitest/autorun'
|
1
2
|
require 'rack/file'
|
2
3
|
require 'rack/lint'
|
3
4
|
require 'rack/mock'
|
@@ -9,213 +10,255 @@ describe Rack::File do
|
|
9
10
|
Rack::Lint.new Rack::File.new(*args)
|
10
11
|
end
|
11
12
|
|
12
|
-
|
13
|
+
it 'serves files with + in the file name' do
|
14
|
+
Dir.mktmpdir do |dir|
|
15
|
+
File.write File.join(dir, "you+me.txt"), "hello world"
|
16
|
+
app = file(dir)
|
17
|
+
env = Rack::MockRequest.env_for("/you+me.txt")
|
18
|
+
status,_,body = app.call env
|
19
|
+
|
20
|
+
assert_equal 200, status
|
21
|
+
|
22
|
+
str = ''
|
23
|
+
body.each { |x| str << x }
|
24
|
+
assert_match "hello world", str
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it "serve files" do
|
13
29
|
res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/test")
|
14
30
|
|
15
|
-
res.
|
16
|
-
res
|
31
|
+
res.must_be :ok?
|
32
|
+
assert_match(res, /ruby/)
|
17
33
|
end
|
18
34
|
|
19
|
-
|
35
|
+
it "set Last-Modified header" do
|
20
36
|
res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/test")
|
21
37
|
|
22
38
|
path = File.join(DOCROOT, "/cgi/test")
|
23
39
|
|
24
|
-
res.
|
25
|
-
res["Last-Modified"].
|
40
|
+
res.must_be :ok?
|
41
|
+
res["Last-Modified"].must_equal File.mtime(path).httpdate
|
26
42
|
end
|
27
43
|
|
28
|
-
|
44
|
+
it "return 304 if file isn't modified since last serve" do
|
29
45
|
path = File.join(DOCROOT, "/cgi/test")
|
30
46
|
res = Rack::MockRequest.new(file(DOCROOT)).
|
31
47
|
get("/cgi/test", 'HTTP_IF_MODIFIED_SINCE' => File.mtime(path).httpdate)
|
32
48
|
|
33
|
-
res.status.
|
34
|
-
res.body.
|
49
|
+
res.status.must_equal 304
|
50
|
+
res.body.must_be :empty?
|
35
51
|
end
|
36
52
|
|
37
|
-
|
53
|
+
it "return the file if it's modified since last serve" do
|
38
54
|
path = File.join(DOCROOT, "/cgi/test")
|
39
55
|
res = Rack::MockRequest.new(file(DOCROOT)).
|
40
56
|
get("/cgi/test", 'HTTP_IF_MODIFIED_SINCE' => (File.mtime(path) - 100).httpdate)
|
41
57
|
|
42
|
-
res.
|
58
|
+
res.must_be :ok?
|
43
59
|
end
|
44
60
|
|
45
|
-
|
61
|
+
it "serve files with URL encoded filenames" do
|
46
62
|
res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/%74%65%73%74") # "/cgi/test"
|
47
63
|
|
48
|
-
res.
|
49
|
-
res.
|
64
|
+
res.must_be :ok?
|
65
|
+
# res.must_match(/ruby/) # nope
|
66
|
+
# (/ruby/).must_match res # This is wierd, but an oddity of minitest
|
67
|
+
# assert_match(/ruby/, res) # nope
|
68
|
+
assert_match(res, /ruby/)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "serve uri with URL encoded null byte (%00) in filenames" do
|
72
|
+
res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/test%00")
|
73
|
+
res.must_be :bad_request?
|
50
74
|
end
|
51
75
|
|
52
|
-
|
76
|
+
it "allow safe directory traversal" do
|
53
77
|
req = Rack::MockRequest.new(file(DOCROOT))
|
54
78
|
|
55
79
|
res = req.get('/cgi/../cgi/test')
|
56
|
-
res.
|
80
|
+
res.must_be :successful?
|
57
81
|
|
58
82
|
res = req.get('.')
|
59
|
-
res.
|
83
|
+
res.must_be :not_found?
|
60
84
|
|
61
85
|
res = req.get("test/..")
|
62
|
-
res.
|
86
|
+
res.must_be :not_found?
|
63
87
|
end
|
64
88
|
|
65
|
-
|
89
|
+
it "not allow unsafe directory traversal" do
|
66
90
|
req = Rack::MockRequest.new(file(DOCROOT))
|
67
91
|
|
68
92
|
res = req.get("/../README.rdoc")
|
69
|
-
res.
|
93
|
+
res.must_be :client_error?
|
70
94
|
|
71
95
|
res = req.get("../test/spec_file.rb")
|
72
|
-
res.
|
96
|
+
res.must_be :client_error?
|
73
97
|
|
74
98
|
res = req.get("../README.rdoc")
|
75
|
-
res.
|
99
|
+
res.must_be :client_error?
|
76
100
|
|
77
|
-
res.
|
101
|
+
res.must_be :not_found?
|
78
102
|
end
|
79
103
|
|
80
|
-
|
104
|
+
it "allow files with .. in their name" do
|
81
105
|
req = Rack::MockRequest.new(file(DOCROOT))
|
82
106
|
res = req.get("/cgi/..test")
|
83
|
-
res.
|
107
|
+
res.must_be :not_found?
|
84
108
|
|
85
109
|
res = req.get("/cgi/test..")
|
86
|
-
res.
|
110
|
+
res.must_be :not_found?
|
87
111
|
|
88
112
|
res = req.get("/cgi../test..")
|
89
|
-
res.
|
113
|
+
res.must_be :not_found?
|
90
114
|
end
|
91
115
|
|
92
|
-
|
116
|
+
it "not allow unsafe directory traversal with encoded periods" do
|
93
117
|
res = Rack::MockRequest.new(file(DOCROOT)).get("/%2E%2E/README")
|
94
118
|
|
95
|
-
res.
|
96
|
-
res.
|
119
|
+
res.must_be :client_error?
|
120
|
+
res.must_be :not_found?
|
97
121
|
end
|
98
122
|
|
99
|
-
|
123
|
+
it "allow safe directory traversal with encoded periods" do
|
100
124
|
res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/%2E%2E/cgi/test")
|
101
125
|
|
102
|
-
res.
|
126
|
+
res.must_be :successful?
|
103
127
|
end
|
104
128
|
|
105
|
-
|
129
|
+
it "404 if it can't find the file" do
|
106
130
|
res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/blubb")
|
107
131
|
|
108
|
-
res.
|
132
|
+
res.must_be :not_found?
|
109
133
|
end
|
110
134
|
|
111
|
-
|
135
|
+
it "detect SystemCallErrors" do
|
112
136
|
res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi")
|
113
137
|
|
114
|
-
res.
|
138
|
+
res.must_be :not_found?
|
115
139
|
end
|
116
140
|
|
117
|
-
|
141
|
+
it "return bodies that respond to #to_path" do
|
118
142
|
env = Rack::MockRequest.env_for("/cgi/test")
|
119
143
|
status, _, body = Rack::File.new(DOCROOT).call(env)
|
120
144
|
|
121
145
|
path = File.join(DOCROOT, "/cgi/test")
|
122
146
|
|
123
|
-
status.
|
124
|
-
body.
|
125
|
-
body.to_path.
|
147
|
+
status.must_equal 200
|
148
|
+
body.must_respond_to :to_path
|
149
|
+
body.to_path.must_equal path
|
126
150
|
end
|
127
151
|
|
128
|
-
|
152
|
+
it "return correct byte range in body" do
|
129
153
|
env = Rack::MockRequest.env_for("/cgi/test")
|
130
154
|
env["HTTP_RANGE"] = "bytes=22-33"
|
131
155
|
res = Rack::MockResponse.new(*file(DOCROOT).call(env))
|
132
156
|
|
133
|
-
res.status.
|
134
|
-
res["Content-Length"].
|
135
|
-
res["Content-Range"].
|
136
|
-
res.body.
|
157
|
+
res.status.must_equal 206
|
158
|
+
res["Content-Length"].must_equal "12"
|
159
|
+
res["Content-Range"].must_equal "bytes 22-33/193"
|
160
|
+
res.body.must_equal "-*- ruby -*-"
|
137
161
|
end
|
138
162
|
|
139
|
-
|
163
|
+
it "return error for unsatisfiable byte range" do
|
140
164
|
env = Rack::MockRequest.env_for("/cgi/test")
|
141
165
|
env["HTTP_RANGE"] = "bytes=1234-5678"
|
142
166
|
res = Rack::MockResponse.new(*file(DOCROOT).call(env))
|
143
167
|
|
144
|
-
res.status.
|
145
|
-
res["Content-Range"].
|
168
|
+
res.status.must_equal 416
|
169
|
+
res["Content-Range"].must_equal "bytes */193"
|
146
170
|
end
|
147
171
|
|
148
|
-
|
172
|
+
it "support custom http headers" do
|
149
173
|
env = Rack::MockRequest.env_for("/cgi/test")
|
150
174
|
status, heads, _ = file(DOCROOT, 'Cache-Control' => 'public, max-age=38',
|
151
175
|
'Access-Control-Allow-Origin' => '*').call(env)
|
152
176
|
|
153
|
-
status.
|
154
|
-
heads['Cache-Control'].
|
155
|
-
heads['Access-Control-Allow-Origin'].
|
177
|
+
status.must_equal 200
|
178
|
+
heads['Cache-Control'].must_equal 'public, max-age=38'
|
179
|
+
heads['Access-Control-Allow-Origin'].must_equal '*'
|
156
180
|
end
|
157
181
|
|
158
|
-
|
182
|
+
it "support not add custom http headers if none are supplied" do
|
159
183
|
env = Rack::MockRequest.env_for("/cgi/test")
|
160
184
|
status, heads, _ = file(DOCROOT).call(env)
|
161
185
|
|
162
|
-
status.
|
163
|
-
heads['Cache-Control'].
|
164
|
-
heads['Access-Control-Allow-Origin'].
|
186
|
+
status.must_equal 200
|
187
|
+
heads['Cache-Control'].must_be_nil
|
188
|
+
heads['Access-Control-Allow-Origin'].must_be_nil
|
165
189
|
end
|
166
190
|
|
167
|
-
|
191
|
+
it "only support GET, HEAD, and OPTIONS requests" do
|
168
192
|
req = Rack::MockRequest.new(file(DOCROOT))
|
169
193
|
|
170
194
|
forbidden = %w[post put patch delete]
|
171
195
|
forbidden.each do |method|
|
172
196
|
res = req.send(method, "/cgi/test")
|
173
|
-
res.
|
174
|
-
res.
|
175
|
-
res.headers['Allow'].split(/, */).sort.
|
197
|
+
res.must_be :client_error?
|
198
|
+
res.must_be :method_not_allowed?
|
199
|
+
res.headers['Allow'].split(/, */).sort.must_equal %w(GET HEAD OPTIONS)
|
176
200
|
end
|
177
201
|
|
178
202
|
allowed = %w[get head options]
|
179
203
|
allowed.each do |method|
|
180
204
|
res = req.send(method, "/cgi/test")
|
181
|
-
res.
|
205
|
+
res.must_be :successful?
|
182
206
|
end
|
183
207
|
end
|
184
208
|
|
185
|
-
|
209
|
+
it "set Allow correctly for OPTIONS requests" do
|
186
210
|
req = Rack::MockRequest.new(file(DOCROOT))
|
187
211
|
res = req.options('/cgi/test')
|
188
|
-
res.
|
189
|
-
res.headers['Allow'].
|
190
|
-
res.headers['Allow'].split(/, */).sort.
|
212
|
+
res.must_be :successful?
|
213
|
+
res.headers['Allow'].wont_equal nil
|
214
|
+
res.headers['Allow'].split(/, */).sort.must_equal %w(GET HEAD OPTIONS)
|
191
215
|
end
|
192
216
|
|
193
|
-
|
217
|
+
it "set Content-Length correctly for HEAD requests" do
|
194
218
|
req = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT)))
|
195
219
|
res = req.head "/cgi/test"
|
196
|
-
res.
|
197
|
-
res['Content-Length'].
|
220
|
+
res.must_be :successful?
|
221
|
+
res['Content-Length'].must_equal "193"
|
198
222
|
end
|
199
223
|
|
200
|
-
|
224
|
+
it "default to a mime type of text/plain" do
|
201
225
|
req = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT)))
|
202
226
|
res = req.get "/cgi/test"
|
203
|
-
res.
|
204
|
-
res['Content-Type'].
|
227
|
+
res.must_be :successful?
|
228
|
+
res['Content-Type'].must_equal "text/plain"
|
205
229
|
end
|
206
230
|
|
207
|
-
|
231
|
+
it "allow the default mime type to be set" do
|
208
232
|
req = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT, nil, 'application/octet-stream')))
|
209
233
|
res = req.get "/cgi/test"
|
210
|
-
res.
|
211
|
-
res['Content-Type'].
|
234
|
+
res.must_be :successful?
|
235
|
+
res['Content-Type'].must_equal "application/octet-stream"
|
212
236
|
end
|
213
237
|
|
214
|
-
|
238
|
+
it "not set Content-Type if the mime type is not set" do
|
215
239
|
req = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT, nil, nil)))
|
216
240
|
res = req.get "/cgi/test"
|
217
|
-
res.
|
218
|
-
res['Content-Type'].
|
241
|
+
res.must_be :successful?
|
242
|
+
res['Content-Type'].must_be_nil
|
243
|
+
end
|
244
|
+
|
245
|
+
it "return error when file not found for head request" do
|
246
|
+
res = Rack::MockRequest.new(file(DOCROOT)).head("/cgi/missing")
|
247
|
+
res.must_be :not_found?
|
248
|
+
res.body.must_be :empty?
|
249
|
+
end
|
250
|
+
|
251
|
+
class MyFile < Rack::File
|
252
|
+
def response_body
|
253
|
+
"hello world"
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
it "behaves gracefully if response_body is present" do
|
258
|
+
file = Rack::Lint.new MyFile.new(DOCROOT)
|
259
|
+
res = Rack::MockRequest.new(file).get("/cgi/test")
|
260
|
+
|
261
|
+
res.must_be :ok?
|
219
262
|
end
|
220
263
|
|
221
264
|
end
|