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_handler.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'minitest/autorun'
|
1
2
|
require 'rack/handler'
|
2
3
|
|
3
4
|
class Rack::Handler::Lobster; end
|
@@ -5,66 +6,50 @@ class RockLobster; end
|
|
5
6
|
|
6
7
|
describe Rack::Handler do
|
7
8
|
it "has registered default handlers" do
|
8
|
-
Rack::Handler.get('cgi').
|
9
|
-
Rack::Handler.get('webrick').
|
9
|
+
Rack::Handler.get('cgi').must_equal Rack::Handler::CGI
|
10
|
+
Rack::Handler.get('webrick').must_equal Rack::Handler::WEBrick
|
10
11
|
|
11
12
|
begin
|
12
|
-
Rack::Handler.get('fastcgi').
|
13
|
-
rescue LoadError
|
14
|
-
end
|
15
|
-
|
16
|
-
begin
|
17
|
-
Rack::Handler.get('mongrel').should.equal Rack::Handler::Mongrel
|
13
|
+
Rack::Handler.get('fastcgi').must_equal Rack::Handler::FastCGI
|
18
14
|
rescue LoadError
|
19
15
|
end
|
20
16
|
end
|
21
17
|
|
22
|
-
|
18
|
+
it "raise LoadError if handler doesn't exist" do
|
23
19
|
lambda {
|
24
20
|
Rack::Handler.get('boom')
|
25
|
-
}.
|
26
|
-
end
|
21
|
+
}.must_raise(LoadError)
|
27
22
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
# constants, so the best we can do is no-op here and not test it.
|
32
|
-
begin
|
33
|
-
Rack::Handler._const_get('Object', false)
|
34
|
-
rescue NameError
|
35
|
-
lambda {
|
36
|
-
Rack::Handler.get('Object')
|
37
|
-
}.should.raise(LoadError)
|
38
|
-
end
|
23
|
+
lambda {
|
24
|
+
Rack::Handler.get('Object')
|
25
|
+
}.must_raise(LoadError)
|
39
26
|
end
|
40
27
|
|
41
|
-
|
42
|
-
Rack::Handler.get('Lobster').
|
28
|
+
it "get unregistered, but already required, handler by name" do
|
29
|
+
Rack::Handler.get('Lobster').must_equal Rack::Handler::Lobster
|
43
30
|
end
|
44
31
|
|
45
|
-
|
32
|
+
it "register custom handler" do
|
46
33
|
Rack::Handler.register('rock_lobster', 'RockLobster')
|
47
|
-
Rack::Handler.get('rock_lobster').
|
34
|
+
Rack::Handler.get('rock_lobster').must_equal RockLobster
|
48
35
|
end
|
49
36
|
|
50
|
-
|
37
|
+
it "not need registration for properly coded handlers even if not already required" do
|
51
38
|
begin
|
52
39
|
$LOAD_PATH.push File.expand_path('../unregistered_handler', __FILE__)
|
53
|
-
Rack::Handler.get('Unregistered').
|
54
|
-
lambda {
|
55
|
-
|
56
|
-
}.should.raise LoadError
|
57
|
-
Rack::Handler.get('UnregisteredLongOne').should.equal Rack::Handler::UnregisteredLongOne
|
40
|
+
Rack::Handler.get('Unregistered').must_equal Rack::Handler::Unregistered
|
41
|
+
lambda { Rack::Handler.get('UnRegistered') }.must_raise LoadError
|
42
|
+
Rack::Handler.get('UnregisteredLongOne').must_equal Rack::Handler::UnregisteredLongOne
|
58
43
|
ensure
|
59
44
|
$LOAD_PATH.delete File.expand_path('../unregistered_handler', __FILE__)
|
60
45
|
end
|
61
46
|
end
|
62
47
|
|
63
|
-
|
48
|
+
it "allow autoloaded handlers to be registered properly while being loaded" do
|
64
49
|
path = File.expand_path('../registering_handler', __FILE__)
|
65
50
|
begin
|
66
51
|
$LOAD_PATH.push path
|
67
|
-
Rack::Handler.get('registering_myself').
|
52
|
+
Rack::Handler.get('registering_myself').must_equal Rack::Handler::RegisteringMyself
|
68
53
|
ensure
|
69
54
|
$LOAD_PATH.delete path
|
70
55
|
end
|
data/test/spec_head.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'minitest/autorun'
|
1
2
|
require 'rack/head'
|
2
3
|
require 'rack/lint'
|
3
4
|
require 'rack/mock'
|
@@ -15,31 +16,31 @@ describe Rack::Head do
|
|
15
16
|
return response, body
|
16
17
|
end
|
17
18
|
|
18
|
-
|
19
|
+
it "pass GET, POST, PUT, DELETE, OPTIONS, TRACE requests" do
|
19
20
|
%w[GET POST PUT DELETE OPTIONS TRACE].each do |type|
|
20
21
|
resp, _ = test_response("REQUEST_METHOD" => type)
|
21
22
|
|
22
|
-
resp[0].
|
23
|
-
resp[1].
|
24
|
-
resp[2].to_enum.to_a.
|
23
|
+
resp[0].must_equal 200
|
24
|
+
resp[1].must_equal "Content-type" => "test/plain", "Content-length" => "3"
|
25
|
+
resp[2].to_enum.to_a.must_equal ["foo"]
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
28
|
-
|
29
|
+
it "remove body from HEAD requests" do
|
29
30
|
resp, _ = test_response("REQUEST_METHOD" => "HEAD")
|
30
31
|
|
31
|
-
resp[0].
|
32
|
-
resp[1].
|
33
|
-
resp[2].to_enum.to_a.
|
32
|
+
resp[0].must_equal 200
|
33
|
+
resp[1].must_equal "Content-type" => "test/plain", "Content-length" => "3"
|
34
|
+
resp[2].to_enum.to_a.must_equal []
|
34
35
|
end
|
35
36
|
|
36
|
-
|
37
|
+
it "close the body when it is removed" do
|
37
38
|
resp, body = test_response("REQUEST_METHOD" => "HEAD")
|
38
|
-
resp[0].
|
39
|
-
resp[1].
|
40
|
-
resp[2].to_enum.to_a.
|
41
|
-
body.
|
39
|
+
resp[0].must_equal 200
|
40
|
+
resp[1].must_equal "Content-type" => "test/plain", "Content-length" => "3"
|
41
|
+
resp[2].to_enum.to_a.must_equal []
|
42
|
+
body.wont_be :closed?
|
42
43
|
resp[2].close
|
43
|
-
body.
|
44
|
+
body.must_be :closed?
|
44
45
|
end
|
45
46
|
end
|
data/test/spec_lint.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'minitest/autorun'
|
1
2
|
require 'stringio'
|
2
3
|
require 'tempfile'
|
3
4
|
require 'rack/lint'
|
@@ -8,129 +9,127 @@ describe Rack::Lint do
|
|
8
9
|
Rack::MockRequest.env_for("/", *args)
|
9
10
|
end
|
10
11
|
|
11
|
-
|
12
|
-
lambda {
|
13
|
-
|
14
|
-
|
15
|
-
}).call(env({}))
|
16
|
-
}.should.not.raise
|
12
|
+
it "pass valid request" do
|
13
|
+
Rack::Lint.new(lambda { |env|
|
14
|
+
[200, {"Content-type" => "test/plain", "Content-length" => "3"}, ["foo"]]
|
15
|
+
}).call(env({})).first.must_equal 200
|
17
16
|
end
|
18
17
|
|
19
|
-
|
20
|
-
lambda { Rack::Lint.new(nil).call }.
|
21
|
-
message.
|
18
|
+
it "notice fatal errors" do
|
19
|
+
lambda { Rack::Lint.new(nil).call }.must_raise(Rack::Lint::LintError).
|
20
|
+
message.must_match(/No env given/)
|
22
21
|
end
|
23
22
|
|
24
|
-
|
25
|
-
lambda { Rack::Lint.new(nil).call 5 }.
|
26
|
-
message.
|
23
|
+
it "notice environment errors" do
|
24
|
+
lambda { Rack::Lint.new(nil).call 5 }.must_raise(Rack::Lint::LintError).
|
25
|
+
message.must_match(/not a Hash/)
|
27
26
|
|
28
27
|
lambda {
|
29
28
|
e = env
|
30
29
|
e.delete("REQUEST_METHOD")
|
31
30
|
Rack::Lint.new(nil).call(e)
|
32
|
-
}.
|
33
|
-
message.
|
31
|
+
}.must_raise(Rack::Lint::LintError).
|
32
|
+
message.must_match(/missing required key REQUEST_METHOD/)
|
34
33
|
|
35
34
|
lambda {
|
36
35
|
e = env
|
37
36
|
e.delete("SERVER_NAME")
|
38
37
|
Rack::Lint.new(nil).call(e)
|
39
|
-
}.
|
40
|
-
message.
|
38
|
+
}.must_raise(Rack::Lint::LintError).
|
39
|
+
message.must_match(/missing required key SERVER_NAME/)
|
41
40
|
|
42
41
|
|
43
42
|
lambda {
|
44
43
|
Rack::Lint.new(nil).call(env("HTTP_CONTENT_TYPE" => "text/plain"))
|
45
|
-
}.
|
46
|
-
message.
|
44
|
+
}.must_raise(Rack::Lint::LintError).
|
45
|
+
message.must_match(/contains HTTP_CONTENT_TYPE/)
|
47
46
|
|
48
47
|
lambda {
|
49
48
|
Rack::Lint.new(nil).call(env("HTTP_CONTENT_LENGTH" => "42"))
|
50
|
-
}.
|
51
|
-
message.
|
49
|
+
}.must_raise(Rack::Lint::LintError).
|
50
|
+
message.must_match(/contains HTTP_CONTENT_LENGTH/)
|
52
51
|
|
53
52
|
lambda {
|
54
53
|
Rack::Lint.new(nil).call(env("FOO" => Object.new))
|
55
|
-
}.
|
56
|
-
message.
|
54
|
+
}.must_raise(Rack::Lint::LintError).
|
55
|
+
message.must_match(/non-string value/)
|
57
56
|
|
58
57
|
lambda {
|
59
58
|
Rack::Lint.new(nil).call(env("rack.version" => "0.2"))
|
60
|
-
}.
|
61
|
-
message.
|
59
|
+
}.must_raise(Rack::Lint::LintError).
|
60
|
+
message.must_match(/must be an Array/)
|
62
61
|
|
63
62
|
lambda {
|
64
63
|
Rack::Lint.new(nil).call(env("rack.url_scheme" => "gopher"))
|
65
|
-
}.
|
66
|
-
message.
|
64
|
+
}.must_raise(Rack::Lint::LintError).
|
65
|
+
message.must_match(/url_scheme unknown/)
|
67
66
|
|
68
67
|
lambda {
|
69
68
|
Rack::Lint.new(nil).call(env("rack.session" => []))
|
70
|
-
}.
|
71
|
-
message.
|
69
|
+
}.must_raise(Rack::Lint::LintError).
|
70
|
+
message.must_equal "session [] must respond to store and []="
|
72
71
|
|
73
72
|
lambda {
|
74
73
|
Rack::Lint.new(nil).call(env("rack.logger" => []))
|
75
|
-
}.
|
76
|
-
message.
|
74
|
+
}.must_raise(Rack::Lint::LintError).
|
75
|
+
message.must_equal "logger [] must respond to info"
|
77
76
|
|
78
77
|
lambda {
|
79
78
|
Rack::Lint.new(nil).call(env("rack.multipart.buffer_size" => 0))
|
80
|
-
}.
|
81
|
-
message.
|
79
|
+
}.must_raise(Rack::Lint::LintError).
|
80
|
+
message.must_equal "rack.multipart.buffer_size must be an Integer > 0 if specified"
|
82
81
|
|
83
82
|
lambda {
|
84
83
|
Rack::Lint.new(nil).call(env("rack.multipart.tempfile_factory" => Tempfile))
|
85
|
-
}.
|
86
|
-
message.
|
84
|
+
}.must_raise(Rack::Lint::LintError).
|
85
|
+
message.must_equal "rack.multipart.tempfile_factory must respond to #call"
|
87
86
|
|
88
87
|
lambda {
|
89
88
|
Rack::Lint.new(lambda { |env|
|
90
89
|
env['rack.multipart.tempfile_factory'].call("testfile", "text/plain")
|
91
90
|
}).call(env("rack.multipart.tempfile_factory" => lambda { |filename, content_type| Object.new }))
|
92
|
-
}.
|
93
|
-
message.
|
91
|
+
}.must_raise(Rack::Lint::LintError).
|
92
|
+
message.must_equal "rack.multipart.tempfile_factory return value must respond to #<<"
|
94
93
|
|
95
94
|
lambda {
|
96
95
|
Rack::Lint.new(nil).call(env("REQUEST_METHOD" => "FUCKUP?"))
|
97
|
-
}.
|
98
|
-
message.
|
96
|
+
}.must_raise(Rack::Lint::LintError).
|
97
|
+
message.must_match(/REQUEST_METHOD/)
|
99
98
|
|
100
99
|
lambda {
|
101
100
|
Rack::Lint.new(nil).call(env("SCRIPT_NAME" => "howdy"))
|
102
|
-
}.
|
103
|
-
message.
|
101
|
+
}.must_raise(Rack::Lint::LintError).
|
102
|
+
message.must_match(/must start with/)
|
104
103
|
|
105
104
|
lambda {
|
106
105
|
Rack::Lint.new(nil).call(env("PATH_INFO" => "../foo"))
|
107
|
-
}.
|
108
|
-
message.
|
106
|
+
}.must_raise(Rack::Lint::LintError).
|
107
|
+
message.must_match(/must start with/)
|
109
108
|
|
110
109
|
lambda {
|
111
110
|
Rack::Lint.new(nil).call(env("CONTENT_LENGTH" => "xcii"))
|
112
|
-
}.
|
113
|
-
message.
|
111
|
+
}.must_raise(Rack::Lint::LintError).
|
112
|
+
message.must_match(/Invalid CONTENT_LENGTH/)
|
114
113
|
|
115
114
|
lambda {
|
116
115
|
e = env
|
117
116
|
e.delete("PATH_INFO")
|
118
117
|
e.delete("SCRIPT_NAME")
|
119
118
|
Rack::Lint.new(nil).call(e)
|
120
|
-
}.
|
121
|
-
message.
|
119
|
+
}.must_raise(Rack::Lint::LintError).
|
120
|
+
message.must_match(/One of .* must be set/)
|
122
121
|
|
123
122
|
lambda {
|
124
123
|
Rack::Lint.new(nil).call(env("SCRIPT_NAME" => "/"))
|
125
|
-
}.
|
126
|
-
message.
|
124
|
+
}.must_raise(Rack::Lint::LintError).
|
125
|
+
message.must_match(/cannot be .* make it ''/)
|
127
126
|
end
|
128
127
|
|
129
|
-
|
128
|
+
it "notice input errors" do
|
130
129
|
lambda {
|
131
130
|
Rack::Lint.new(nil).call(env("rack.input" => ""))
|
132
|
-
}.
|
133
|
-
message.
|
131
|
+
}.must_raise(Rack::Lint::LintError).
|
132
|
+
message.must_match(/does not respond to #gets/)
|
134
133
|
|
135
134
|
lambda {
|
136
135
|
input = Object.new
|
@@ -138,8 +137,8 @@ describe Rack::Lint do
|
|
138
137
|
false
|
139
138
|
end
|
140
139
|
Rack::Lint.new(nil).call(env("rack.input" => input))
|
141
|
-
}.
|
142
|
-
message.
|
140
|
+
}.must_raise(Rack::Lint::LintError).
|
141
|
+
message.must_match(/is not opened in binary mode/)
|
143
142
|
|
144
143
|
lambda {
|
145
144
|
input = Object.new
|
@@ -151,54 +150,54 @@ describe Rack::Lint do
|
|
151
150
|
result
|
152
151
|
end
|
153
152
|
Rack::Lint.new(nil).call(env("rack.input" => input))
|
154
|
-
}.
|
155
|
-
message.
|
153
|
+
}.must_raise(Rack::Lint::LintError).
|
154
|
+
message.must_match(/does not have ASCII-8BIT as its external encoding/)
|
156
155
|
end
|
157
156
|
|
158
|
-
|
157
|
+
it "notice error errors" do
|
159
158
|
lambda {
|
160
159
|
Rack::Lint.new(nil).call(env("rack.errors" => ""))
|
161
|
-
}.
|
162
|
-
message.
|
160
|
+
}.must_raise(Rack::Lint::LintError).
|
161
|
+
message.must_match(/does not respond to #puts/)
|
163
162
|
end
|
164
163
|
|
165
|
-
|
164
|
+
it "notice status errors" do
|
166
165
|
lambda {
|
167
166
|
Rack::Lint.new(lambda { |env|
|
168
167
|
["cc", {}, ""]
|
169
168
|
}).call(env({}))
|
170
|
-
}.
|
171
|
-
message.
|
169
|
+
}.must_raise(Rack::Lint::LintError).
|
170
|
+
message.must_match(/must be >=100 seen as integer/)
|
172
171
|
|
173
172
|
lambda {
|
174
173
|
Rack::Lint.new(lambda { |env|
|
175
174
|
[42, {}, ""]
|
176
175
|
}).call(env({}))
|
177
|
-
}.
|
178
|
-
message.
|
176
|
+
}.must_raise(Rack::Lint::LintError).
|
177
|
+
message.must_match(/must be >=100 seen as integer/)
|
179
178
|
end
|
180
179
|
|
181
|
-
|
180
|
+
it "notice header errors" do
|
182
181
|
lambda {
|
183
182
|
Rack::Lint.new(lambda { |env|
|
184
183
|
[200, Object.new, []]
|
185
184
|
}).call(env({}))
|
186
|
-
}.
|
187
|
-
message.
|
185
|
+
}.must_raise(Rack::Lint::LintError).
|
186
|
+
message.must_equal "headers object should respond to #each, but doesn't (got Object as headers)"
|
188
187
|
|
189
188
|
lambda {
|
190
189
|
Rack::Lint.new(lambda { |env|
|
191
190
|
[200, {true=>false}, []]
|
192
191
|
}).call(env({}))
|
193
|
-
}.
|
194
|
-
message.
|
192
|
+
}.must_raise(Rack::Lint::LintError).
|
193
|
+
message.must_equal "header key must be a string, was TrueClass"
|
195
194
|
|
196
195
|
lambda {
|
197
196
|
Rack::Lint.new(lambda { |env|
|
198
197
|
[200, {"Status" => "404"}, []]
|
199
198
|
}).call(env({}))
|
200
|
-
}.
|
201
|
-
message.
|
199
|
+
}.must_raise(Rack::Lint::LintError).
|
200
|
+
message.must_match(/must not contain Status/)
|
202
201
|
|
203
202
|
# From RFC 7230:<F24><F25>
|
204
203
|
# Most HTTP header field values are defined using common syntax
|
@@ -206,9 +205,9 @@ describe Rack::Lint do
|
|
206
205
|
# whitespace or specific delimiting characters. Delimiters are chosen
|
207
206
|
# from the set of US-ASCII visual characters not allowed in a token
|
208
207
|
# (DQUOTE and "(),/:;<=>?@[\]{}").
|
209
|
-
#
|
208
|
+
#
|
210
209
|
# token = 1*tchar
|
211
|
-
#
|
210
|
+
#
|
212
211
|
# tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
|
213
212
|
# / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
|
214
213
|
# / DIGIT / ALPHA
|
@@ -219,157 +218,151 @@ describe Rack::Lint do
|
|
219
218
|
Rack::Lint.new(lambda { |env|
|
220
219
|
[200, {invalid_header => "text/plain"}, []]
|
221
220
|
}).call(env({}))
|
222
|
-
}.
|
223
|
-
message.
|
221
|
+
}.must_raise(Rack::Lint::LintError, "on invalid header: #{invalid_header}").
|
222
|
+
message.must_equal("invalid header name: #{invalid_header}")
|
224
223
|
end
|
225
224
|
valid_headers = 0.upto(127).map(&:chr) - invalid_headers
|
226
225
|
valid_headers.each do |valid_header|
|
227
|
-
lambda {
|
228
|
-
|
229
|
-
|
230
|
-
}).call(env({}))
|
231
|
-
}.should.not.raise(Rack::Lint::LintError, "on valid header: #{valid_header}")
|
226
|
+
Rack::Lint.new(lambda { |env|
|
227
|
+
[200, {valid_header => "text/plain"}, []]
|
228
|
+
}).call(env({})).first.must_equal 200
|
232
229
|
end
|
233
230
|
|
234
231
|
lambda {
|
235
232
|
Rack::Lint.new(lambda { |env|
|
236
233
|
[200, {"Foo" => Object.new}, []]
|
237
234
|
}).call(env({}))
|
238
|
-
}.
|
239
|
-
message.
|
235
|
+
}.must_raise(Rack::Lint::LintError).
|
236
|
+
message.must_equal "a header value must be a String, but the value of 'Foo' is a Object"
|
240
237
|
|
241
238
|
lambda {
|
242
239
|
Rack::Lint.new(lambda { |env|
|
243
240
|
[200, {"Foo" => [1, 2, 3]}, []]
|
244
241
|
}).call(env({}))
|
245
|
-
}.
|
246
|
-
message.
|
242
|
+
}.must_raise(Rack::Lint::LintError).
|
243
|
+
message.must_equal "a header value must be a String, but the value of 'Foo' is a Array"
|
247
244
|
|
248
245
|
|
249
246
|
lambda {
|
250
247
|
Rack::Lint.new(lambda { |env|
|
251
248
|
[200, {"Foo-Bar" => "text\000plain"}, []]
|
252
249
|
}).call(env({}))
|
253
|
-
}.
|
254
|
-
message.
|
255
|
-
|
256
|
-
# line ends (010)
|
257
|
-
lambda {
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
Rack::Lint.new(lambda { |env|
|
266
|
-
[200, [%w(Content-Type text/plain), %w(Content-Length 0)], []]
|
267
|
-
}).call(env({}))
|
268
|
-
}.should.not.raise(TypeError)
|
250
|
+
}.must_raise(Rack::Lint::LintError).
|
251
|
+
message.must_match(/invalid header/)
|
252
|
+
|
253
|
+
# line ends (010).must_be :allowed in header values.?
|
254
|
+
Rack::Lint.new(lambda { |env|
|
255
|
+
[200, {"Foo-Bar" => "one\ntwo\nthree", "Content-Length" => "0", "Content-Type" => "text/plain" }, []]
|
256
|
+
}).call(env({})).first.must_equal 200
|
257
|
+
|
258
|
+
# non-Hash header responses.must_be :allowed?
|
259
|
+
Rack::Lint.new(lambda { |env|
|
260
|
+
[200, [%w(Content-Type text/plain), %w(Content-Length 0)], []]
|
261
|
+
}).call(env({})).first.must_equal 200
|
269
262
|
end
|
270
263
|
|
271
|
-
|
264
|
+
it "notice content-type errors" do
|
272
265
|
# lambda {
|
273
266
|
# Rack::Lint.new(lambda { |env|
|
274
267
|
# [200, {"Content-length" => "0"}, []]
|
275
268
|
# }).call(env({}))
|
276
|
-
# }.
|
277
|
-
# message.
|
269
|
+
# }.must_raise(Rack::Lint::LintError).
|
270
|
+
# message.must_match(/No Content-Type/)
|
278
271
|
|
279
|
-
[100, 101, 204,
|
272
|
+
[100, 101, 204, 304].each do |status|
|
280
273
|
lambda {
|
281
274
|
Rack::Lint.new(lambda { |env|
|
282
275
|
[status, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
283
276
|
}).call(env({}))
|
284
|
-
}.
|
285
|
-
message.
|
277
|
+
}.must_raise(Rack::Lint::LintError).
|
278
|
+
message.must_match(/Content-Type header found/)
|
286
279
|
end
|
287
280
|
end
|
288
281
|
|
289
|
-
|
290
|
-
[100, 101, 204,
|
282
|
+
it "notice content-length errors" do
|
283
|
+
[100, 101, 204, 304].each do |status|
|
291
284
|
lambda {
|
292
285
|
Rack::Lint.new(lambda { |env|
|
293
286
|
[status, {"Content-length" => "0"}, []]
|
294
287
|
}).call(env({}))
|
295
|
-
}.
|
296
|
-
message.
|
288
|
+
}.must_raise(Rack::Lint::LintError).
|
289
|
+
message.must_match(/Content-Length header found/)
|
297
290
|
end
|
298
291
|
|
299
292
|
lambda {
|
300
293
|
Rack::Lint.new(lambda { |env|
|
301
294
|
[200, {"Content-type" => "text/plain", "Content-Length" => "1"}, []]
|
302
295
|
}).call(env({}))[2].each { }
|
303
|
-
}.
|
304
|
-
message.
|
296
|
+
}.must_raise(Rack::Lint::LintError).
|
297
|
+
message.must_match(/Content-Length header was 1, but should be 0/)
|
305
298
|
end
|
306
299
|
|
307
|
-
|
300
|
+
it "notice body errors" do
|
308
301
|
lambda {
|
309
302
|
body = Rack::Lint.new(lambda { |env|
|
310
303
|
[200, {"Content-type" => "text/plain","Content-length" => "3"}, [1,2,3]]
|
311
304
|
}).call(env({}))[2]
|
312
305
|
body.each { |part| }
|
313
|
-
}.
|
314
|
-
message.
|
306
|
+
}.must_raise(Rack::Lint::LintError).
|
307
|
+
message.must_match(/yielded non-string/)
|
315
308
|
end
|
316
309
|
|
317
|
-
|
310
|
+
it "notice input handling errors" do
|
318
311
|
lambda {
|
319
312
|
Rack::Lint.new(lambda { |env|
|
320
313
|
env["rack.input"].gets("\r\n")
|
321
314
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
322
315
|
}).call(env({}))
|
323
|
-
}.
|
324
|
-
message.
|
316
|
+
}.must_raise(Rack::Lint::LintError).
|
317
|
+
message.must_match(/gets called with arguments/)
|
325
318
|
|
326
319
|
lambda {
|
327
320
|
Rack::Lint.new(lambda { |env|
|
328
321
|
env["rack.input"].read(1, 2, 3)
|
329
322
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
330
323
|
}).call(env({}))
|
331
|
-
}.
|
332
|
-
message.
|
324
|
+
}.must_raise(Rack::Lint::LintError).
|
325
|
+
message.must_match(/read called with too many arguments/)
|
333
326
|
|
334
327
|
lambda {
|
335
328
|
Rack::Lint.new(lambda { |env|
|
336
329
|
env["rack.input"].read("foo")
|
337
330
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
338
331
|
}).call(env({}))
|
339
|
-
}.
|
340
|
-
message.
|
332
|
+
}.must_raise(Rack::Lint::LintError).
|
333
|
+
message.must_match(/read called with non-integer and non-nil length/)
|
341
334
|
|
342
335
|
lambda {
|
343
336
|
Rack::Lint.new(lambda { |env|
|
344
337
|
env["rack.input"].read(-1)
|
345
338
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
346
339
|
}).call(env({}))
|
347
|
-
}.
|
348
|
-
message.
|
340
|
+
}.must_raise(Rack::Lint::LintError).
|
341
|
+
message.must_match(/read called with a negative length/)
|
349
342
|
|
350
343
|
lambda {
|
351
344
|
Rack::Lint.new(lambda { |env|
|
352
345
|
env["rack.input"].read(nil, nil)
|
353
346
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
354
347
|
}).call(env({}))
|
355
|
-
}.
|
356
|
-
message.
|
348
|
+
}.must_raise(Rack::Lint::LintError).
|
349
|
+
message.must_match(/read called with non-String buffer/)
|
357
350
|
|
358
351
|
lambda {
|
359
352
|
Rack::Lint.new(lambda { |env|
|
360
353
|
env["rack.input"].read(nil, 1)
|
361
354
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
362
355
|
}).call(env({}))
|
363
|
-
}.
|
364
|
-
message.
|
356
|
+
}.must_raise(Rack::Lint::LintError).
|
357
|
+
message.must_match(/read called with non-String buffer/)
|
365
358
|
|
366
359
|
lambda {
|
367
360
|
Rack::Lint.new(lambda { |env|
|
368
361
|
env["rack.input"].rewind(0)
|
369
362
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
370
363
|
}).call(env({}))
|
371
|
-
}.
|
372
|
-
message.
|
364
|
+
}.must_raise(Rack::Lint::LintError).
|
365
|
+
message.must_match(/rewind called with arguments/)
|
373
366
|
|
374
367
|
weirdio = Object.new
|
375
368
|
class << weirdio
|
@@ -413,40 +406,40 @@ describe Rack::Lint do
|
|
413
406
|
env["rack.input"].gets
|
414
407
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
415
408
|
}).call(env("rack.input" => weirdio))
|
416
|
-
}.
|
417
|
-
message.
|
409
|
+
}.must_raise(Rack::Lint::LintError).
|
410
|
+
message.must_match(/gets didn't return a String/)
|
418
411
|
|
419
412
|
lambda {
|
420
413
|
Rack::Lint.new(lambda { |env|
|
421
414
|
env["rack.input"].each { |x| }
|
422
415
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
423
416
|
}).call(env("rack.input" => weirdio))
|
424
|
-
}.
|
425
|
-
message.
|
417
|
+
}.must_raise(Rack::Lint::LintError).
|
418
|
+
message.must_match(/each didn't yield a String/)
|
426
419
|
|
427
420
|
lambda {
|
428
421
|
Rack::Lint.new(lambda { |env|
|
429
422
|
env["rack.input"].read
|
430
423
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
431
424
|
}).call(env("rack.input" => weirdio))
|
432
|
-
}.
|
433
|
-
message.
|
425
|
+
}.must_raise(Rack::Lint::LintError).
|
426
|
+
message.must_match(/read didn't return nil or a String/)
|
434
427
|
|
435
428
|
lambda {
|
436
429
|
Rack::Lint.new(lambda { |env|
|
437
430
|
env["rack.input"].read
|
438
431
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
439
432
|
}).call(env("rack.input" => eof_weirdio))
|
440
|
-
}.
|
441
|
-
message.
|
433
|
+
}.must_raise(Rack::Lint::LintError).
|
434
|
+
message.must_match(/read\(nil\) returned nil on EOF/)
|
442
435
|
|
443
436
|
lambda {
|
444
437
|
Rack::Lint.new(lambda { |env|
|
445
438
|
env["rack.input"].rewind
|
446
439
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
447
440
|
}).call(env("rack.input" => weirdio))
|
448
|
-
}.
|
449
|
-
message.
|
441
|
+
}.must_raise(Rack::Lint::LintError).
|
442
|
+
message.must_match(/rewind raised Errno::ESPIPE/)
|
450
443
|
|
451
444
|
|
452
445
|
lambda {
|
@@ -454,97 +447,69 @@ describe Rack::Lint do
|
|
454
447
|
env["rack.input"].close
|
455
448
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
456
449
|
}).call(env({}))
|
457
|
-
}.
|
458
|
-
message.
|
450
|
+
}.must_raise(Rack::Lint::LintError).
|
451
|
+
message.must_match(/close must not be called/)
|
459
452
|
end
|
460
453
|
|
461
|
-
|
454
|
+
it "notice error handling errors" do
|
462
455
|
lambda {
|
463
456
|
Rack::Lint.new(lambda { |env|
|
464
457
|
env["rack.errors"].write(42)
|
465
458
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
466
459
|
}).call(env({}))
|
467
|
-
}.
|
468
|
-
message.
|
460
|
+
}.must_raise(Rack::Lint::LintError).
|
461
|
+
message.must_match(/write not called with a String/)
|
469
462
|
|
470
463
|
lambda {
|
471
464
|
Rack::Lint.new(lambda { |env|
|
472
465
|
env["rack.errors"].close
|
473
466
|
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
474
467
|
}).call(env({}))
|
475
|
-
}.
|
476
|
-
message.
|
468
|
+
}.must_raise(Rack::Lint::LintError).
|
469
|
+
message.must_match(/close must not be called/)
|
477
470
|
end
|
478
471
|
|
479
|
-
|
480
|
-
lambda {
|
481
|
-
|
482
|
-
|
483
|
-
}).call(env({"REQUEST_METHOD" => "HEAD"}))
|
484
|
-
}.should.not.raise
|
472
|
+
it "notice HEAD errors" do
|
473
|
+
Rack::Lint.new(lambda { |env|
|
474
|
+
[200, {"Content-type" => "test/plain", "Content-length" => "3"}, []]
|
475
|
+
}).call(env({"REQUEST_METHOD" => "HEAD"})).first.must_equal 200
|
485
476
|
|
486
477
|
lambda {
|
487
478
|
Rack::Lint.new(lambda { |env|
|
488
479
|
[200, {"Content-type" => "test/plain", "Content-length" => "3"}, ["foo"]]
|
489
480
|
}).call(env({"REQUEST_METHOD" => "HEAD"}))[2].each { }
|
490
|
-
}.
|
491
|
-
message.
|
481
|
+
}.must_raise(Rack::Lint::LintError).
|
482
|
+
message.must_match(/body was given for HEAD/)
|
492
483
|
end
|
493
484
|
|
494
|
-
|
485
|
+
def assert_lint(*args)
|
495
486
|
hello_str = "hello world"
|
496
|
-
hello_str.force_encoding(
|
497
|
-
lambda {
|
498
|
-
Rack::Lint.new(lambda { |env|
|
499
|
-
env["rack.input"].read
|
500
|
-
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
501
|
-
}).call(env({"rack.input" => StringIO.new(hello_str)}))
|
502
|
-
}.should.not.raise(Rack::Lint::LintError)
|
503
|
-
|
504
|
-
lambda {
|
505
|
-
Rack::Lint.new(lambda { |env|
|
506
|
-
env["rack.input"].read(0)
|
507
|
-
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
508
|
-
}).call(env({"rack.input" => StringIO.new(hello_str)}))
|
509
|
-
}.should.not.raise(Rack::Lint::LintError)
|
510
|
-
|
511
|
-
lambda {
|
512
|
-
Rack::Lint.new(lambda { |env|
|
513
|
-
env["rack.input"].read(1)
|
514
|
-
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
515
|
-
}).call(env({"rack.input" => StringIO.new(hello_str)}))
|
516
|
-
}.should.not.raise(Rack::Lint::LintError)
|
517
|
-
|
518
|
-
lambda {
|
519
|
-
Rack::Lint.new(lambda { |env|
|
520
|
-
env["rack.input"].read(nil)
|
521
|
-
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
522
|
-
}).call(env({"rack.input" => StringIO.new(hello_str)}))
|
523
|
-
}.should.not.raise(Rack::Lint::LintError)
|
487
|
+
hello_str.force_encoding(Encoding::ASCII_8BIT)
|
524
488
|
|
525
|
-
lambda {
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
489
|
+
Rack::Lint.new(lambda { |env|
|
490
|
+
env["rack.input"].send(:read, *args)
|
491
|
+
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
492
|
+
}).call(env({"rack.input" => StringIO.new(hello_str)})).
|
493
|
+
first.must_equal 201
|
494
|
+
end
|
531
495
|
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
496
|
+
it "pass valid read calls" do
|
497
|
+
assert_lint
|
498
|
+
assert_lint 0
|
499
|
+
assert_lint 1
|
500
|
+
assert_lint nil
|
501
|
+
assert_lint nil, ''
|
502
|
+
assert_lint 1, ''
|
538
503
|
end
|
539
504
|
end
|
540
505
|
|
541
506
|
describe "Rack::Lint::InputWrapper" do
|
542
|
-
|
507
|
+
it "delegate :rewind to underlying IO object" do
|
543
508
|
io = StringIO.new("123")
|
544
509
|
wrapper = Rack::Lint::InputWrapper.new(io)
|
545
|
-
wrapper.read.
|
546
|
-
wrapper.read.
|
510
|
+
wrapper.read.must_equal "123"
|
511
|
+
wrapper.read.must_equal ""
|
547
512
|
wrapper.rewind
|
548
|
-
wrapper.read.
|
513
|
+
wrapper.read.must_equal "123"
|
549
514
|
end
|
550
515
|
end
|