rack 1.6.13 → 2.0.0.alpha
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/HISTORY.md +139 -18
- data/README.rdoc +17 -25
- data/Rakefile +6 -14
- data/SPEC +8 -9
- data/contrib/rack_logo.svg +164 -111
- data/lib/rack.rb +70 -21
- 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} +2 -2
- data/lib/rack/{conditionalget.rb → conditional_get.rb} +0 -0
- data/lib/rack/content_length.rb +2 -2
- data/lib/rack/deflater.rb +4 -4
- data/lib/rack/directory.rb +49 -55
- data/lib/rack/etag.rb +2 -1
- data/lib/rack/events.rb +154 -0
- data/lib/rack/file.rb +55 -40
- data/lib/rack/handler.rb +2 -24
- 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 +22 -24
- data/lib/rack/head.rb +15 -17
- data/lib/rack/lint.rb +38 -38
- data/lib/rack/lobster.rb +1 -1
- data/lib/rack/lock.rb +6 -10
- data/lib/rack/logger.rb +2 -2
- data/lib/rack/media_type.rb +38 -0
- data/lib/rack/{methodoverride.rb → method_override.rb} +4 -11
- data/lib/rack/mime.rb +18 -5
- data/lib/rack/mock.rb +35 -52
- data/lib/rack/multipart.rb +35 -6
- data/lib/rack/multipart/generator.rb +4 -4
- data/lib/rack/multipart/parser.rb +273 -158
- 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 +174 -0
- data/lib/rack/recursive.rb +8 -8
- data/lib/rack/reloader.rb +1 -2
- data/lib/rack/request.rb +370 -304
- data/lib/rack/response.rb +129 -56
- 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 +31 -25
- data/lib/rack/session/abstract/id.rb +93 -135
- 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 +13 -14
- data/lib/rack/utils.rb +128 -221
- data/rack.gemspec +9 -5
- 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 +31 -0
- data/test/multipart/filename_with_encoded_words +7 -0
- data/test/multipart/{filename_with_null_byte → filename_with_single_quote} +1 -1
- data/test/multipart/quoted +15 -0
- data/test/multipart/rack-logo.png +0 -0
- data/test/registering_handler/rack/handler/registering_myself.rb +1 -1
- data/test/spec_auth_basic.rb +20 -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 +36 -34
- 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 +66 -40
- data/test/spec_directory.rb +72 -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 +96 -77
- data/test/spec_handler.rb +19 -34
- data/test/spec_head.rb +15 -14
- data/test/spec_lint.rb +162 -197
- data/test/spec_lobster.rb +24 -23
- data/test/spec_lock.rb +69 -39
- data/test/spec_logger.rb +4 -3
- data/test/spec_media_type.rb +42 -0
- data/test/spec_method_override.rb +83 -0
- data/test/spec_mime.rb +19 -19
- data/test/spec_mock.rb +196 -151
- data/test/spec_multipart.rb +310 -202
- data/test/{spec_nulllogger.rb → spec_null_logger.rb} +5 -4
- data/test/spec_recursive.rb +17 -14
- data/test/spec_request.rb +763 -607
- data/test/spec_response.rb +209 -156
- 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_cookie.rb +97 -65
- data/test/spec_session_memcache.rb +63 -101
- data/test/spec_session_pool.rb +48 -84
- data/test/spec_show_exceptions.rb +80 -0
- 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 +417 -345
- data/test/spec_version.rb +2 -8
- data/test/spec_webrick.rb +77 -67
- 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 +116 -71
- 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_methodoverride.rb +0 -111
- data/test/spec_mongrel.rb +0 -182
- data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
- data/test/spec_showexceptions.rb +0 -98
data/test/spec_thin.rb
CHANGED
@@ -1,89 +1,94 @@
|
|
1
|
+
require 'minitest/autorun'
|
1
2
|
begin
|
2
3
|
require 'rack/handler/thin'
|
3
4
|
require File.expand_path('../testrequest', __FILE__)
|
4
5
|
require 'timeout'
|
5
6
|
|
6
7
|
describe Rack::Handler::Thin do
|
7
|
-
|
8
|
+
include TestRequest::Helpers
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
before do
|
11
|
+
@app = Rack::Lint.new(TestRequest.new)
|
12
|
+
@server = nil
|
13
|
+
Thin::Logging.silent = true
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
|
15
|
+
@thread = Thread.new do
|
16
|
+
Rack::Handler::Thin.run(@app, :Host => @host='127.0.0.1', :Port => @port=9204, :tag => "tag") do |server|
|
17
|
+
@server = server
|
18
|
+
end
|
16
19
|
end
|
20
|
+
|
21
|
+
Thread.pass until @server && @server.running?
|
22
|
+
end
|
23
|
+
|
24
|
+
after do
|
25
|
+
@server.stop!
|
26
|
+
@thread.join
|
17
27
|
end
|
18
28
|
|
19
|
-
Thread.pass until @server && @server.running?
|
20
29
|
|
21
|
-
|
30
|
+
it "respond" do
|
22
31
|
GET("/")
|
23
|
-
response.
|
32
|
+
response.wont_be :nil?
|
24
33
|
end
|
25
34
|
|
26
|
-
|
35
|
+
it "be a Thin" do
|
27
36
|
GET("/")
|
28
|
-
status.
|
29
|
-
response["SERVER_SOFTWARE"].
|
30
|
-
response["HTTP_VERSION"].
|
31
|
-
response["SERVER_PROTOCOL"].
|
32
|
-
response["SERVER_PORT"].
|
33
|
-
response["SERVER_NAME"].
|
37
|
+
status.must_equal 200
|
38
|
+
response["SERVER_SOFTWARE"].must_match(/thin/)
|
39
|
+
response["HTTP_VERSION"].must_equal "HTTP/1.1"
|
40
|
+
response["SERVER_PROTOCOL"].must_equal "HTTP/1.1"
|
41
|
+
response["SERVER_PORT"].must_equal "9204"
|
42
|
+
response["SERVER_NAME"].must_equal "127.0.0.1"
|
34
43
|
end
|
35
44
|
|
36
|
-
|
45
|
+
it "have rack headers" do
|
37
46
|
GET("/")
|
38
|
-
response["rack.version"].
|
39
|
-
response["rack.multithread"].
|
40
|
-
response["rack.multiprocess"].
|
41
|
-
response["rack.run_once"].
|
47
|
+
response["rack.version"].must_equal [1,0]
|
48
|
+
response["rack.multithread"].must_equal false
|
49
|
+
response["rack.multiprocess"].must_equal false
|
50
|
+
response["rack.run_once"].must_equal false
|
42
51
|
end
|
43
52
|
|
44
|
-
|
53
|
+
it "have CGI headers on GET" do
|
45
54
|
GET("/")
|
46
|
-
response["REQUEST_METHOD"].
|
47
|
-
response["REQUEST_PATH"].
|
48
|
-
response["PATH_INFO"].
|
49
|
-
response["QUERY_STRING"].
|
50
|
-
response["test.postdata"].
|
55
|
+
response["REQUEST_METHOD"].must_equal "GET"
|
56
|
+
response["REQUEST_PATH"].must_equal "/"
|
57
|
+
response["PATH_INFO"].must_equal "/"
|
58
|
+
response["QUERY_STRING"].must_equal ""
|
59
|
+
response["test.postdata"].must_equal ""
|
51
60
|
|
52
61
|
GET("/test/foo?quux=1")
|
53
|
-
response["REQUEST_METHOD"].
|
54
|
-
response["REQUEST_PATH"].
|
55
|
-
response["PATH_INFO"].
|
56
|
-
response["QUERY_STRING"].
|
62
|
+
response["REQUEST_METHOD"].must_equal "GET"
|
63
|
+
response["REQUEST_PATH"].must_equal "/test/foo"
|
64
|
+
response["PATH_INFO"].must_equal "/test/foo"
|
65
|
+
response["QUERY_STRING"].must_equal "quux=1"
|
57
66
|
end
|
58
67
|
|
59
|
-
|
68
|
+
it "have CGI headers on POST" do
|
60
69
|
POST("/", {"rack-form-data" => "23"}, {'X-test-header' => '42'})
|
61
|
-
status.
|
62
|
-
response["REQUEST_METHOD"].
|
63
|
-
response["REQUEST_PATH"].
|
64
|
-
response["QUERY_STRING"].
|
65
|
-
response["HTTP_X_TEST_HEADER"].
|
66
|
-
response["test.postdata"].
|
70
|
+
status.must_equal 200
|
71
|
+
response["REQUEST_METHOD"].must_equal "POST"
|
72
|
+
response["REQUEST_PATH"].must_equal "/"
|
73
|
+
response["QUERY_STRING"].must_equal ""
|
74
|
+
response["HTTP_X_TEST_HEADER"].must_equal "42"
|
75
|
+
response["test.postdata"].must_equal "rack-form-data=23"
|
67
76
|
end
|
68
77
|
|
69
|
-
|
78
|
+
it "support HTTP auth" do
|
70
79
|
GET("/test", {:user => "ruth", :passwd => "secret"})
|
71
|
-
response["HTTP_AUTHORIZATION"].
|
80
|
+
response["HTTP_AUTHORIZATION"].must_equal "Basic cnV0aDpzZWNyZXQ="
|
72
81
|
end
|
73
82
|
|
74
|
-
|
83
|
+
it "set status" do
|
75
84
|
GET("/test?secret")
|
76
|
-
status.
|
77
|
-
response["rack.url_scheme"].
|
85
|
+
status.must_equal 403
|
86
|
+
response["rack.url_scheme"].must_equal "http"
|
78
87
|
end
|
79
88
|
|
80
|
-
|
81
|
-
@server.tag.
|
89
|
+
it "set tag for server" do
|
90
|
+
@server.tag.must_equal 'tag'
|
82
91
|
end
|
83
|
-
|
84
|
-
@server.stop!
|
85
|
-
@thread.kill
|
86
|
-
|
87
92
|
end
|
88
93
|
|
89
94
|
rescue LoadError
|
data/test/spec_urlmap.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'minitest/autorun'
|
1
2
|
require 'rack/urlmap'
|
2
3
|
require 'rack/mock'
|
3
4
|
|
@@ -17,51 +18,51 @@ describe Rack::URLMap do
|
|
17
18
|
}))
|
18
19
|
|
19
20
|
res = Rack::MockRequest.new(map).get("/")
|
20
|
-
res.
|
21
|
+
res.must_be :not_found?
|
21
22
|
|
22
23
|
res = Rack::MockRequest.new(map).get("/qux")
|
23
|
-
res.
|
24
|
+
res.must_be :not_found?
|
24
25
|
|
25
26
|
res = Rack::MockRequest.new(map).get("/foo")
|
26
|
-
res.
|
27
|
-
res["X-ScriptName"].
|
28
|
-
res["X-PathInfo"].
|
27
|
+
res.must_be :ok?
|
28
|
+
res["X-ScriptName"].must_equal "/foo"
|
29
|
+
res["X-PathInfo"].must_equal ""
|
29
30
|
|
30
31
|
res = Rack::MockRequest.new(map).get("/foo/")
|
31
|
-
res.
|
32
|
-
res["X-ScriptName"].
|
33
|
-
res["X-PathInfo"].
|
32
|
+
res.must_be :ok?
|
33
|
+
res["X-ScriptName"].must_equal "/foo"
|
34
|
+
res["X-PathInfo"].must_equal "/"
|
34
35
|
|
35
36
|
res = Rack::MockRequest.new(map).get("/foo/bar")
|
36
|
-
res.
|
37
|
-
res["X-ScriptName"].
|
38
|
-
res["X-PathInfo"].
|
37
|
+
res.must_be :ok?
|
38
|
+
res["X-ScriptName"].must_equal "/foo/bar"
|
39
|
+
res["X-PathInfo"].must_equal ""
|
39
40
|
|
40
41
|
res = Rack::MockRequest.new(map).get("/foo/bar/")
|
41
|
-
res.
|
42
|
-
res["X-ScriptName"].
|
43
|
-
res["X-PathInfo"].
|
42
|
+
res.must_be :ok?
|
43
|
+
res["X-ScriptName"].must_equal "/foo/bar"
|
44
|
+
res["X-PathInfo"].must_equal "/"
|
44
45
|
|
45
46
|
res = Rack::MockRequest.new(map).get("/foo///bar//quux")
|
46
|
-
res.status.
|
47
|
-
res.
|
48
|
-
res["X-ScriptName"].
|
49
|
-
res["X-PathInfo"].
|
47
|
+
res.status.must_equal 200
|
48
|
+
res.must_be :ok?
|
49
|
+
res["X-ScriptName"].must_equal "/foo/bar"
|
50
|
+
res["X-PathInfo"].must_equal "//quux"
|
50
51
|
|
51
52
|
res = Rack::MockRequest.new(map).get("/foo/quux", "SCRIPT_NAME" => "/bleh")
|
52
|
-
res.
|
53
|
-
res["X-ScriptName"].
|
54
|
-
res["X-PathInfo"].
|
53
|
+
res.must_be :ok?
|
54
|
+
res["X-ScriptName"].must_equal "/bleh/foo"
|
55
|
+
res["X-PathInfo"].must_equal "/quux"
|
55
56
|
|
56
57
|
res = Rack::MockRequest.new(map).get("/bar", 'HTTP_HOST' => 'foo.org')
|
57
|
-
res.
|
58
|
-
res["X-ScriptName"].
|
59
|
-
res["X-PathInfo"].
|
58
|
+
res.must_be :ok?
|
59
|
+
res["X-ScriptName"].must_equal "/bar"
|
60
|
+
res["X-PathInfo"].must_be :empty?
|
60
61
|
|
61
62
|
res = Rack::MockRequest.new(map).get("/bar/", 'HTTP_HOST' => 'foo.org')
|
62
|
-
res.
|
63
|
-
res["X-ScriptName"].
|
64
|
-
res["X-PathInfo"].
|
63
|
+
res.must_be :ok?
|
64
|
+
res["X-ScriptName"].must_equal "/bar"
|
65
|
+
res["X-PathInfo"].must_equal '/'
|
65
66
|
end
|
66
67
|
|
67
68
|
|
@@ -93,37 +94,37 @@ describe Rack::URLMap do
|
|
93
94
|
))
|
94
95
|
|
95
96
|
res = Rack::MockRequest.new(map).get("/")
|
96
|
-
res.
|
97
|
-
res["X-Position"].
|
97
|
+
res.must_be :ok?
|
98
|
+
res["X-Position"].must_equal "default.org"
|
98
99
|
|
99
100
|
res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "bar.org")
|
100
|
-
res.
|
101
|
-
res["X-Position"].
|
101
|
+
res.must_be :ok?
|
102
|
+
res["X-Position"].must_equal "bar.org"
|
102
103
|
|
103
104
|
res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "foo.org")
|
104
|
-
res.
|
105
|
-
res["X-Position"].
|
105
|
+
res.must_be :ok?
|
106
|
+
res["X-Position"].must_equal "foo.org"
|
106
107
|
|
107
108
|
res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "subdomain.foo.org", "SERVER_NAME" => "foo.org")
|
108
|
-
res.
|
109
|
-
res["X-Position"].
|
109
|
+
res.must_be :ok?
|
110
|
+
res["X-Position"].must_equal "subdomain.foo.org"
|
110
111
|
|
111
112
|
res = Rack::MockRequest.new(map).get("http://foo.org/")
|
112
|
-
res.
|
113
|
-
res["X-Position"].
|
113
|
+
res.must_be :ok?
|
114
|
+
res["X-Position"].must_equal "foo.org"
|
114
115
|
|
115
116
|
res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "example.org")
|
116
|
-
res.
|
117
|
-
res["X-Position"].
|
117
|
+
res.must_be :ok?
|
118
|
+
res["X-Position"].must_equal "default.org"
|
118
119
|
|
119
120
|
res = Rack::MockRequest.new(map).get("/",
|
120
121
|
"HTTP_HOST" => "example.org:9292",
|
121
122
|
"SERVER_PORT" => "9292")
|
122
|
-
res.
|
123
|
-
res["X-Position"].
|
123
|
+
res.must_be :ok?
|
124
|
+
res["X-Position"].must_equal "default.org"
|
124
125
|
end
|
125
126
|
|
126
|
-
|
127
|
+
it "be nestable" do
|
127
128
|
map = Rack::Lint.new(Rack::URLMap.new("/foo" =>
|
128
129
|
Rack::URLMap.new("/bar" =>
|
129
130
|
Rack::URLMap.new("/quux" => lambda { |env|
|
@@ -136,16 +137,16 @@ describe Rack::URLMap do
|
|
136
137
|
))))
|
137
138
|
|
138
139
|
res = Rack::MockRequest.new(map).get("/foo/bar")
|
139
|
-
res.
|
140
|
+
res.must_be :not_found?
|
140
141
|
|
141
142
|
res = Rack::MockRequest.new(map).get("/foo/bar/quux")
|
142
|
-
res.
|
143
|
-
res["X-Position"].
|
144
|
-
res["X-PathInfo"].
|
145
|
-
res["X-ScriptName"].
|
143
|
+
res.must_be :ok?
|
144
|
+
res["X-Position"].must_equal "/foo/bar/quux"
|
145
|
+
res["X-PathInfo"].must_equal ""
|
146
|
+
res["X-ScriptName"].must_equal "/foo/bar/quux"
|
146
147
|
end
|
147
148
|
|
148
|
-
|
149
|
+
it "route root apps correctly" do
|
149
150
|
map = Rack::Lint.new(Rack::URLMap.new("/" => lambda { |env|
|
150
151
|
[200,
|
151
152
|
{ "Content-Type" => "text/plain",
|
@@ -163,31 +164,31 @@ describe Rack::URLMap do
|
|
163
164
|
))
|
164
165
|
|
165
166
|
res = Rack::MockRequest.new(map).get("/foo/bar")
|
166
|
-
res.
|
167
|
-
res["X-Position"].
|
168
|
-
res["X-PathInfo"].
|
169
|
-
res["X-ScriptName"].
|
167
|
+
res.must_be :ok?
|
168
|
+
res["X-Position"].must_equal "foo"
|
169
|
+
res["X-PathInfo"].must_equal "/bar"
|
170
|
+
res["X-ScriptName"].must_equal "/foo"
|
170
171
|
|
171
172
|
res = Rack::MockRequest.new(map).get("/foo")
|
172
|
-
res.
|
173
|
-
res["X-Position"].
|
174
|
-
res["X-PathInfo"].
|
175
|
-
res["X-ScriptName"].
|
173
|
+
res.must_be :ok?
|
174
|
+
res["X-Position"].must_equal "foo"
|
175
|
+
res["X-PathInfo"].must_equal ""
|
176
|
+
res["X-ScriptName"].must_equal "/foo"
|
176
177
|
|
177
178
|
res = Rack::MockRequest.new(map).get("/bar")
|
178
|
-
res.
|
179
|
-
res["X-Position"].
|
180
|
-
res["X-PathInfo"].
|
181
|
-
res["X-ScriptName"].
|
179
|
+
res.must_be :ok?
|
180
|
+
res["X-Position"].must_equal "root"
|
181
|
+
res["X-PathInfo"].must_equal "/bar"
|
182
|
+
res["X-ScriptName"].must_equal ""
|
182
183
|
|
183
184
|
res = Rack::MockRequest.new(map).get("")
|
184
|
-
res.
|
185
|
-
res["X-Position"].
|
186
|
-
res["X-PathInfo"].
|
187
|
-
res["X-ScriptName"].
|
185
|
+
res.must_be :ok?
|
186
|
+
res["X-Position"].must_equal "root"
|
187
|
+
res["X-PathInfo"].must_equal "/"
|
188
|
+
res["X-ScriptName"].must_equal ""
|
188
189
|
end
|
189
190
|
|
190
|
-
|
191
|
+
it "not squeeze slashes" do
|
191
192
|
map = Rack::Lint.new(Rack::URLMap.new("/" => lambda { |env|
|
192
193
|
[200,
|
193
194
|
{ "Content-Type" => "text/plain",
|
@@ -205,13 +206,13 @@ describe Rack::URLMap do
|
|
205
206
|
))
|
206
207
|
|
207
208
|
res = Rack::MockRequest.new(map).get("/http://example.org/bar")
|
208
|
-
res.
|
209
|
-
res["X-Position"].
|
210
|
-
res["X-PathInfo"].
|
211
|
-
res["X-ScriptName"].
|
209
|
+
res.must_be :ok?
|
210
|
+
res["X-Position"].must_equal "root"
|
211
|
+
res["X-PathInfo"].must_equal "/http://example.org/bar"
|
212
|
+
res["X-ScriptName"].must_equal ""
|
212
213
|
end
|
213
214
|
|
214
|
-
|
215
|
+
it "not be case sensitive with hosts" do
|
215
216
|
map = Rack::Lint.new(Rack::URLMap.new("http://example.org/" => lambda { |env|
|
216
217
|
[200,
|
217
218
|
{ "Content-Type" => "text/plain",
|
@@ -222,15 +223,15 @@ describe Rack::URLMap do
|
|
222
223
|
))
|
223
224
|
|
224
225
|
res = Rack::MockRequest.new(map).get("http://example.org/")
|
225
|
-
res.
|
226
|
-
res["X-Position"].
|
227
|
-
res["X-PathInfo"].
|
228
|
-
res["X-ScriptName"].
|
226
|
+
res.must_be :ok?
|
227
|
+
res["X-Position"].must_equal "root"
|
228
|
+
res["X-PathInfo"].must_equal "/"
|
229
|
+
res["X-ScriptName"].must_equal ""
|
229
230
|
|
230
231
|
res = Rack::MockRequest.new(map).get("http://EXAMPLE.ORG/")
|
231
|
-
res.
|
232
|
-
res["X-Position"].
|
233
|
-
res["X-PathInfo"].
|
234
|
-
res["X-ScriptName"].
|
232
|
+
res.must_be :ok?
|
233
|
+
res["X-Position"].must_equal "root"
|
234
|
+
res["X-PathInfo"].must_equal "/"
|
235
|
+
res["X-ScriptName"].must_equal ""
|
235
236
|
end
|
236
237
|
end
|
data/test/spec_utils.rb
CHANGED
@@ -1,288 +1,312 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
+
require 'minitest/autorun'
|
2
3
|
require 'rack/utils'
|
3
4
|
require 'rack/mock'
|
4
5
|
require 'timeout'
|
5
6
|
|
6
7
|
describe Rack::Utils do
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
def assert_sets exp, act
|
10
|
+
exp = Set.new exp.split '&'
|
11
|
+
act = Set.new act.split '&'
|
12
|
+
|
13
|
+
assert_equal exp, act
|
14
|
+
end
|
15
|
+
|
16
|
+
def assert_query exp, act
|
17
|
+
assert_sets exp, Rack::Utils.build_query(act)
|
14
18
|
end
|
15
19
|
|
16
|
-
def
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
def assert_nested_query exp, act
|
21
|
+
assert_sets exp, Rack::Utils.build_nested_query(act)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'can be mixed in and used' do
|
25
|
+
instance = Class.new {
|
26
|
+
include Rack::Utils
|
27
|
+
|
28
|
+
public :parse_nested_query
|
29
|
+
public :parse_query
|
30
|
+
}.new
|
31
|
+
|
32
|
+
assert_equal({ "foo" => "bar" }, instance.parse_nested_query("foo=bar"))
|
33
|
+
assert_equal({ "foo" => "bar" }, instance.parse_query("foo=bar"))
|
22
34
|
end
|
23
35
|
|
24
|
-
|
36
|
+
it "round trip binary data" do
|
25
37
|
r = [218, 0].pack 'CC'
|
26
|
-
if defined?(::Encoding)
|
27
38
|
z = Rack::Utils.unescape(Rack::Utils.escape(r), Encoding::BINARY)
|
28
|
-
|
29
|
-
z = Rack::Utils.unescape(Rack::Utils.escape(r))
|
30
|
-
end
|
31
|
-
r.should.equal z
|
39
|
+
r.must_equal z
|
32
40
|
end
|
33
41
|
|
34
|
-
|
35
|
-
Rack::Utils.escape("fo<o>bar").
|
36
|
-
Rack::Utils.escape("a space").
|
42
|
+
it "escape correctly" do
|
43
|
+
Rack::Utils.escape("fo<o>bar").must_equal "fo%3Co%3Ebar"
|
44
|
+
Rack::Utils.escape("a space").must_equal "a+space"
|
37
45
|
Rack::Utils.escape("q1!2\"'w$5&7/z8)?\\").
|
38
|
-
|
46
|
+
must_equal "q1%212%22%27w%245%267%2Fz8%29%3F%5C"
|
39
47
|
end
|
40
48
|
|
41
|
-
|
49
|
+
it "escape correctly for multibyte characters" do
|
42
50
|
matz_name = "\xE3\x81\xBE\xE3\x81\xA4\xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsumoto
|
43
|
-
matz_name.force_encoding(
|
44
|
-
Rack::Utils.escape(matz_name).
|
51
|
+
matz_name.force_encoding(Encoding::UTF_8)
|
52
|
+
Rack::Utils.escape(matz_name).must_equal '%E3%81%BE%E3%81%A4%E3%82%82%E3%81%A8'
|
45
53
|
matz_name_sep = "\xE3\x81\xBE\xE3\x81\xA4 \xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsu moto
|
46
54
|
matz_name_sep.force_encoding("UTF-8") if matz_name_sep.respond_to? :force_encoding
|
47
|
-
Rack::Utils.escape(matz_name_sep).
|
48
|
-
end
|
49
|
-
|
50
|
-
if RUBY_VERSION[/^\d+\.\d+/] == '1.8'
|
51
|
-
should "escape correctly for multibyte characters if $KCODE is set to 'U'" do
|
52
|
-
kcodeu do
|
53
|
-
matz_name = "\xE3\x81\xBE\xE3\x81\xA4\xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsumoto
|
54
|
-
matz_name.force_encoding("UTF-8") if matz_name.respond_to? :force_encoding
|
55
|
-
Rack::Utils.escape(matz_name).should.equal '%E3%81%BE%E3%81%A4%E3%82%82%E3%81%A8'
|
56
|
-
matz_name_sep = "\xE3\x81\xBE\xE3\x81\xA4 \xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsu moto
|
57
|
-
matz_name_sep.force_encoding("UTF-8") if matz_name_sep.respond_to? :force_encoding
|
58
|
-
Rack::Utils.escape(matz_name_sep).should.equal '%E3%81%BE%E3%81%A4+%E3%82%82%E3%81%A8'
|
59
|
-
end
|
60
|
-
end
|
55
|
+
Rack::Utils.escape(matz_name_sep).must_equal '%E3%81%BE%E3%81%A4+%E3%82%82%E3%81%A8'
|
56
|
+
end
|
61
57
|
|
62
|
-
|
63
|
-
|
64
|
-
Rack::Utils.unescape('%E3%81%BE%E3%81%A4+%E3%82%82%E3%81%A8').should.equal(
|
65
|
-
"\xE3\x81\xBE\xE3\x81\xA4 \xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0])
|
66
|
-
end
|
67
|
-
end
|
58
|
+
it "escape objects that responds to to_s" do
|
59
|
+
Rack::Utils.escape(:id).must_equal "id"
|
68
60
|
end
|
69
61
|
|
70
|
-
|
71
|
-
|
72
|
-
Rack::Utils.escape(:id).should.equal "id"
|
73
|
-
end
|
62
|
+
it "escape non-UTF8 strings" do
|
63
|
+
Rack::Utils.escape("ø".encode("ISO-8859-1")).must_equal "%F8"
|
74
64
|
end
|
75
65
|
|
76
|
-
|
77
|
-
|
78
|
-
|
66
|
+
it "not hang on escaping long strings that end in % (http://redmine.ruby-lang.org/issues/5149)" do
|
67
|
+
Timeout.timeout(1) do
|
68
|
+
lambda {
|
69
|
+
URI.decode_www_form_component "A string that causes catastrophic backtracking as it gets longer %"
|
70
|
+
}.must_raise ArgumentError
|
79
71
|
end
|
80
72
|
end
|
81
|
-
|
82
|
-
should "not hang on escaping long strings that end in % (http://redmine.ruby-lang.org/issues/5149)" do
|
83
|
-
lambda {
|
84
|
-
timeout(1) do
|
85
|
-
lambda {
|
86
|
-
URI.decode_www_form_component "A string that causes catastrophic backtracking as it gets longer %"
|
87
|
-
}.should.raise(ArgumentError)
|
88
|
-
end
|
89
|
-
}.should.not.raise(Timeout::Error)
|
90
|
-
end
|
91
73
|
|
92
|
-
|
93
|
-
Rack::Utils.escape_path("foo bar").
|
74
|
+
it "escape path spaces with %20" do
|
75
|
+
Rack::Utils.escape_path("foo bar").must_equal "foo%20bar"
|
94
76
|
end
|
95
77
|
|
96
|
-
|
97
|
-
Rack::Utils.unescape("fo%3Co%3Ebar").
|
98
|
-
Rack::Utils.unescape("a+space").
|
99
|
-
Rack::Utils.unescape("a%20space").
|
78
|
+
it "unescape correctly" do
|
79
|
+
Rack::Utils.unescape("fo%3Co%3Ebar").must_equal "fo<o>bar"
|
80
|
+
Rack::Utils.unescape("a+space").must_equal "a space"
|
81
|
+
Rack::Utils.unescape("a%20space").must_equal "a space"
|
100
82
|
Rack::Utils.unescape("q1%212%22%27w%245%267%2Fz8%29%3F%5C").
|
101
|
-
|
83
|
+
must_equal "q1!2\"'w$5&7/z8)?\\"
|
102
84
|
end
|
103
85
|
|
104
|
-
|
86
|
+
it "parse query strings correctly" do
|
105
87
|
Rack::Utils.parse_query("foo=bar").
|
106
|
-
|
88
|
+
must_equal "foo" => "bar"
|
107
89
|
Rack::Utils.parse_query("foo=\"bar\"").
|
108
|
-
|
90
|
+
must_equal "foo" => "\"bar\""
|
109
91
|
Rack::Utils.parse_query("foo=bar&foo=quux").
|
110
|
-
|
92
|
+
must_equal "foo" => ["bar", "quux"]
|
111
93
|
Rack::Utils.parse_query("foo=1&bar=2").
|
112
|
-
|
94
|
+
must_equal "foo" => "1", "bar" => "2"
|
113
95
|
Rack::Utils.parse_query("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F").
|
114
|
-
|
115
|
-
Rack::Utils.parse_query("foo%3Dbaz=bar").
|
116
|
-
Rack::Utils.parse_query("=").
|
117
|
-
Rack::Utils.parse_query("=value").
|
118
|
-
Rack::Utils.parse_query("key=").
|
119
|
-
Rack::Utils.parse_query("&key&").
|
120
|
-
Rack::Utils.parse_query(";key;", ";,").
|
121
|
-
Rack::Utils.parse_query(",key,", ";,").
|
122
|
-
Rack::Utils.parse_query(";foo=bar,;", ";,").
|
123
|
-
Rack::Utils.parse_query(",foo=bar;,", ";,").
|
124
|
-
end
|
125
|
-
|
126
|
-
|
96
|
+
must_equal "my weird field" => "q1!2\"'w$5&7/z8)?"
|
97
|
+
Rack::Utils.parse_query("foo%3Dbaz=bar").must_equal "foo=baz" => "bar"
|
98
|
+
Rack::Utils.parse_query("=").must_equal "" => ""
|
99
|
+
Rack::Utils.parse_query("=value").must_equal "" => "value"
|
100
|
+
Rack::Utils.parse_query("key=").must_equal "key" => ""
|
101
|
+
Rack::Utils.parse_query("&key&").must_equal "key" => nil
|
102
|
+
Rack::Utils.parse_query(";key;", ";,").must_equal "key" => nil
|
103
|
+
Rack::Utils.parse_query(",key,", ";,").must_equal "key" => nil
|
104
|
+
Rack::Utils.parse_query(";foo=bar,;", ";,").must_equal "foo" => "bar"
|
105
|
+
Rack::Utils.parse_query(",foo=bar;,", ";,").must_equal "foo" => "bar"
|
106
|
+
end
|
107
|
+
|
108
|
+
it "not create infinite loops with cycle structures" do
|
127
109
|
ex = { "foo" => nil }
|
128
110
|
ex["foo"] = ex
|
129
111
|
|
130
|
-
params = Rack::Utils::KeySpaceConstrainedParams.new
|
112
|
+
params = Rack::Utils::KeySpaceConstrainedParams.new(65536)
|
131
113
|
params['foo'] = params
|
132
|
-
|
133
|
-
|
134
|
-
|
114
|
+
params.to_params_hash.to_s.must_equal ex.to_s
|
115
|
+
end
|
116
|
+
|
117
|
+
it "parse nil as an empty query string" do
|
118
|
+
Rack::Utils.parse_nested_query(nil).must_equal({})
|
135
119
|
end
|
136
120
|
|
137
|
-
|
121
|
+
it "raise an exception if the params are too deep" do
|
138
122
|
len = Rack::Utils.param_depth_limit
|
139
123
|
|
140
124
|
lambda {
|
141
125
|
Rack::Utils.parse_nested_query("foo#{"[a]" * len}=bar")
|
142
|
-
}.
|
126
|
+
}.must_raise(RangeError)
|
143
127
|
|
144
|
-
|
145
|
-
Rack::Utils.parse_nested_query("foo#{"[a]" * (len - 1)}=bar")
|
146
|
-
}.should.not.raise
|
128
|
+
Rack::Utils.parse_nested_query("foo#{"[a]" * (len - 1)}=bar")
|
147
129
|
end
|
148
130
|
|
149
|
-
|
131
|
+
it "parse nested query strings correctly" do
|
150
132
|
Rack::Utils.parse_nested_query("foo").
|
151
|
-
|
133
|
+
must_equal "foo" => nil
|
152
134
|
Rack::Utils.parse_nested_query("foo=").
|
153
|
-
|
135
|
+
must_equal "foo" => ""
|
154
136
|
Rack::Utils.parse_nested_query("foo=bar").
|
155
|
-
|
137
|
+
must_equal "foo" => "bar"
|
156
138
|
Rack::Utils.parse_nested_query("foo=\"bar\"").
|
157
|
-
|
139
|
+
must_equal "foo" => "\"bar\""
|
158
140
|
|
159
141
|
Rack::Utils.parse_nested_query("foo=bar&foo=quux").
|
160
|
-
|
142
|
+
must_equal "foo" => "quux"
|
161
143
|
Rack::Utils.parse_nested_query("foo&foo=").
|
162
|
-
|
144
|
+
must_equal "foo" => ""
|
163
145
|
Rack::Utils.parse_nested_query("foo=1&bar=2").
|
164
|
-
|
146
|
+
must_equal "foo" => "1", "bar" => "2"
|
165
147
|
Rack::Utils.parse_nested_query("&foo=1&&bar=2").
|
166
|
-
|
148
|
+
must_equal "foo" => "1", "bar" => "2"
|
167
149
|
Rack::Utils.parse_nested_query("foo&bar=").
|
168
|
-
|
150
|
+
must_equal "foo" => nil, "bar" => ""
|
169
151
|
Rack::Utils.parse_nested_query("foo=bar&baz=").
|
170
|
-
|
152
|
+
must_equal "foo" => "bar", "baz" => ""
|
171
153
|
Rack::Utils.parse_nested_query("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F").
|
172
|
-
|
154
|
+
must_equal "my weird field" => "q1!2\"'w$5&7/z8)?"
|
173
155
|
|
174
156
|
Rack::Utils.parse_nested_query("a=b&pid%3D1234=1023").
|
175
|
-
|
157
|
+
must_equal "pid=1234" => "1023", "a" => "b"
|
176
158
|
|
177
159
|
Rack::Utils.parse_nested_query("foo[]").
|
178
|
-
|
160
|
+
must_equal "foo" => [nil]
|
179
161
|
Rack::Utils.parse_nested_query("foo[]=").
|
180
|
-
|
162
|
+
must_equal "foo" => [""]
|
181
163
|
Rack::Utils.parse_nested_query("foo[]=bar").
|
182
|
-
|
164
|
+
must_equal "foo" => ["bar"]
|
183
165
|
Rack::Utils.parse_nested_query("foo[]=bar&foo").
|
184
|
-
|
166
|
+
must_equal "foo" => nil
|
185
167
|
Rack::Utils.parse_nested_query("foo[]=bar&foo[").
|
186
|
-
|
168
|
+
must_equal "foo" => ["bar"], "foo[" => nil
|
187
169
|
Rack::Utils.parse_nested_query("foo[]=bar&foo[=baz").
|
188
|
-
|
170
|
+
must_equal "foo" => ["bar"], "foo[" => "baz"
|
189
171
|
Rack::Utils.parse_nested_query("foo[]=bar&foo[]").
|
190
|
-
|
172
|
+
must_equal "foo" => ["bar", nil]
|
191
173
|
Rack::Utils.parse_nested_query("foo[]=bar&foo[]=").
|
192
|
-
|
174
|
+
must_equal "foo" => ["bar", ""]
|
193
175
|
|
194
176
|
Rack::Utils.parse_nested_query("foo[]=1&foo[]=2").
|
195
|
-
|
177
|
+
must_equal "foo" => ["1", "2"]
|
196
178
|
Rack::Utils.parse_nested_query("foo=bar&baz[]=1&baz[]=2&baz[]=3").
|
197
|
-
|
179
|
+
must_equal "foo" => "bar", "baz" => ["1", "2", "3"]
|
198
180
|
Rack::Utils.parse_nested_query("foo[]=bar&baz[]=1&baz[]=2&baz[]=3").
|
199
|
-
|
181
|
+
must_equal "foo" => ["bar"], "baz" => ["1", "2", "3"]
|
200
182
|
|
201
183
|
Rack::Utils.parse_nested_query("x[y][z]=1").
|
202
|
-
|
184
|
+
must_equal "x" => {"y" => {"z" => "1"}}
|
203
185
|
Rack::Utils.parse_nested_query("x[y][z][]=1").
|
204
|
-
|
186
|
+
must_equal "x" => {"y" => {"z" => ["1"]}}
|
205
187
|
Rack::Utils.parse_nested_query("x[y][z]=1&x[y][z]=2").
|
206
|
-
|
188
|
+
must_equal "x" => {"y" => {"z" => "2"}}
|
207
189
|
Rack::Utils.parse_nested_query("x[y][z][]=1&x[y][z][]=2").
|
208
|
-
|
190
|
+
must_equal "x" => {"y" => {"z" => ["1", "2"]}}
|
209
191
|
|
210
192
|
Rack::Utils.parse_nested_query("x[y][][z]=1").
|
211
|
-
|
193
|
+
must_equal "x" => {"y" => [{"z" => "1"}]}
|
212
194
|
Rack::Utils.parse_nested_query("x[y][][z][]=1").
|
213
|
-
|
195
|
+
must_equal "x" => {"y" => [{"z" => ["1"]}]}
|
214
196
|
Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][w]=2").
|
215
|
-
|
197
|
+
must_equal "x" => {"y" => [{"z" => "1", "w" => "2"}]}
|
216
198
|
|
217
199
|
Rack::Utils.parse_nested_query("x[y][][v][w]=1").
|
218
|
-
|
200
|
+
must_equal "x" => {"y" => [{"v" => {"w" => "1"}}]}
|
219
201
|
Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][v][w]=2").
|
220
|
-
|
202
|
+
must_equal "x" => {"y" => [{"z" => "1", "v" => {"w" => "2"}}]}
|
221
203
|
|
222
204
|
Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][z]=2").
|
223
|
-
|
205
|
+
must_equal "x" => {"y" => [{"z" => "1"}, {"z" => "2"}]}
|
224
206
|
Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][w]=a&x[y][][z]=2&x[y][][w]=3").
|
225
|
-
|
207
|
+
must_equal "x" => {"y" => [{"z" => "1", "w" => "a"}, {"z" => "2", "w" => "3"}]}
|
226
208
|
|
227
209
|
lambda { Rack::Utils.parse_nested_query("x[y]=1&x[y]z=2") }.
|
228
|
-
|
229
|
-
message.
|
210
|
+
must_raise(Rack::Utils::ParameterTypeError).
|
211
|
+
message.must_equal "expected Hash (got String) for param `y'"
|
230
212
|
|
231
213
|
lambda { Rack::Utils.parse_nested_query("x[y]=1&x[]=1") }.
|
232
|
-
|
233
|
-
message.
|
214
|
+
must_raise(Rack::Utils::ParameterTypeError).
|
215
|
+
message.must_match(/expected Array \(got [^)]*\) for param `x'/)
|
234
216
|
|
235
217
|
lambda { Rack::Utils.parse_nested_query("x[y]=1&x[y][][w]=2") }.
|
236
|
-
|
237
|
-
message.
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
218
|
+
must_raise(Rack::Utils::ParameterTypeError).
|
219
|
+
message.must_equal "expected Array (got String) for param `y'"
|
220
|
+
|
221
|
+
lambda { Rack::Utils.parse_nested_query("foo%81E=1") }.
|
222
|
+
must_raise(Rack::Utils::InvalidParameterError).
|
223
|
+
message.must_equal "invalid byte sequence in UTF-8"
|
224
|
+
end
|
225
|
+
|
226
|
+
it "allow setting the params hash class to use for parsing query strings" do
|
227
|
+
begin
|
228
|
+
default_parser = Rack::Utils.default_query_parser
|
229
|
+
param_parser_class = Class.new(Rack::QueryParser::Params) do
|
230
|
+
def initialize(*)
|
231
|
+
super
|
232
|
+
@params = Hash.new{|h,k| h[k.to_s] if k.is_a?(Symbol)}
|
233
|
+
end
|
234
|
+
end
|
235
|
+
Rack::Utils.default_query_parser = Rack::QueryParser.new(param_parser_class, 65536, 100)
|
236
|
+
Rack::Utils.parse_query(",foo=bar;,", ";,")[:foo].must_equal "bar"
|
237
|
+
Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][w]=2")[:x][:y][0][:z].must_equal "1"
|
238
|
+
ensure
|
239
|
+
Rack::Utils.default_query_parser = default_parser
|
243
240
|
end
|
244
241
|
end
|
245
242
|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
Rack::Utils.build_nested_query("foo" =>
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
Rack::Utils.build_nested_query("foo" => [
|
269
|
-
|
270
|
-
Rack::Utils.build_nested_query(
|
271
|
-
|
272
|
-
Rack::Utils.build_nested_query(
|
273
|
-
|
274
|
-
Rack::Utils.build_nested_query('foo' =>
|
275
|
-
|
276
|
-
Rack::Utils.build_nested_query('foo' =>
|
277
|
-
|
278
|
-
Rack::Utils.build_nested_query('foo' => '
|
279
|
-
|
280
|
-
Rack::Utils.build_nested_query('foo' => 'bar', 'baz' =>
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
243
|
+
it "build query strings correctly" do
|
244
|
+
assert_query "foo=bar", "foo" => "bar"
|
245
|
+
assert_query "foo=bar&foo=quux", "foo" => ["bar", "quux"]
|
246
|
+
assert_query "foo=1&bar=2", "foo" => "1", "bar" => "2"
|
247
|
+
assert_query("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F",
|
248
|
+
"my weird field" => "q1!2\"'w$5&7/z8)?")
|
249
|
+
end
|
250
|
+
|
251
|
+
it "build nested query strings correctly" do
|
252
|
+
Rack::Utils.build_nested_query("foo" => nil).must_equal "foo"
|
253
|
+
Rack::Utils.build_nested_query("foo" => "").must_equal "foo="
|
254
|
+
Rack::Utils.build_nested_query("foo" => "bar").must_equal "foo=bar"
|
255
|
+
|
256
|
+
assert_nested_query("foo=1&bar=2",
|
257
|
+
"foo" => "1", "bar" => "2")
|
258
|
+
assert_nested_query("foo=1&bar=2",
|
259
|
+
"foo" => 1, "bar" => 2)
|
260
|
+
assert_nested_query("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F",
|
261
|
+
"my weird field" => "q1!2\"'w$5&7/z8)?")
|
262
|
+
|
263
|
+
Rack::Utils.build_nested_query("foo" => [nil]).must_equal "foo[]"
|
264
|
+
Rack::Utils.build_nested_query("foo" => [""]).must_equal "foo[]="
|
265
|
+
Rack::Utils.build_nested_query("foo" => ["bar"]).must_equal "foo[]=bar"
|
266
|
+
Rack::Utils.build_nested_query('foo' => []).must_equal ''
|
267
|
+
Rack::Utils.build_nested_query('foo' => {}).must_equal ''
|
268
|
+
Rack::Utils.build_nested_query('foo' => 'bar', 'baz' => []).must_equal 'foo=bar'
|
269
|
+
Rack::Utils.build_nested_query('foo' => 'bar', 'baz' => {}).must_equal 'foo=bar'
|
270
|
+
|
271
|
+
Rack::Utils.build_nested_query('foo' => nil, 'bar' => '').
|
272
|
+
must_equal 'foo&bar='
|
273
|
+
Rack::Utils.build_nested_query('foo' => 'bar', 'baz' => '').
|
274
|
+
must_equal 'foo=bar&baz='
|
275
|
+
Rack::Utils.build_nested_query('foo' => ['1', '2']).
|
276
|
+
must_equal 'foo[]=1&foo[]=2'
|
277
|
+
Rack::Utils.build_nested_query('foo' => 'bar', 'baz' => ['1', '2', '3']).
|
278
|
+
must_equal 'foo=bar&baz[]=1&baz[]=2&baz[]=3'
|
279
|
+
Rack::Utils.build_nested_query('foo' => ['bar'], 'baz' => ['1', '2', '3']).
|
280
|
+
must_equal 'foo[]=bar&baz[]=1&baz[]=2&baz[]=3'
|
281
|
+
Rack::Utils.build_nested_query('foo' => ['bar'], 'baz' => ['1', '2', '3']).
|
282
|
+
must_equal 'foo[]=bar&baz[]=1&baz[]=2&baz[]=3'
|
283
|
+
Rack::Utils.build_nested_query('x' => { 'y' => { 'z' => '1' } }).
|
284
|
+
must_equal 'x[y][z]=1'
|
285
|
+
Rack::Utils.build_nested_query('x' => { 'y' => { 'z' => ['1'] } }).
|
286
|
+
must_equal 'x[y][z][]=1'
|
287
|
+
Rack::Utils.build_nested_query('x' => { 'y' => { 'z' => ['1', '2'] } }).
|
288
|
+
must_equal 'x[y][z][]=1&x[y][z][]=2'
|
289
|
+
Rack::Utils.build_nested_query('x' => { 'y' => [{ 'z' => '1' }] }).
|
290
|
+
must_equal 'x[y][][z]=1'
|
291
|
+
Rack::Utils.build_nested_query('x' => { 'y' => [{ 'z' => ['1'] }] }).
|
292
|
+
must_equal 'x[y][][z][]=1'
|
293
|
+
Rack::Utils.build_nested_query('x' => { 'y' => [{ 'z' => '1', 'w' => '2' }] }).
|
294
|
+
must_equal 'x[y][][z]=1&x[y][][w]=2'
|
295
|
+
Rack::Utils.build_nested_query('x' => { 'y' => [{ 'v' => { 'w' => '1' } }] }).
|
296
|
+
must_equal 'x[y][][v][w]=1'
|
297
|
+
Rack::Utils.build_nested_query('x' => { 'y' => [{ 'z' => '1', 'v' => { 'w' => '2' } }] }).
|
298
|
+
must_equal 'x[y][][z]=1&x[y][][v][w]=2'
|
299
|
+
Rack::Utils.build_nested_query('x' => { 'y' => [{ 'z' => '1' }, { 'z' => '2' }] }).
|
300
|
+
must_equal 'x[y][][z]=1&x[y][][z]=2'
|
301
|
+
Rack::Utils.build_nested_query('x' => { 'y' => [{ 'z' => '1', 'w' => 'a' }, { 'z' => '2', 'w' => '3' }] }).
|
302
|
+
must_equal 'x[y][][z]=1&x[y][][w]=a&x[y][][z]=2&x[y][][w]=3'
|
303
|
+
|
304
|
+
lambda { Rack::Utils.build_nested_query("foo=bar") }.
|
305
|
+
must_raise(ArgumentError).
|
306
|
+
message.must_equal "value must be a Hash"
|
307
|
+
end
|
308
|
+
|
309
|
+
should 'perform the inverse function of #parse_nested_query' do
|
286
310
|
[{"foo" => nil, "bar" => ""},
|
287
311
|
{"foo" => "bar", "baz" => ""},
|
288
312
|
{"foo" => ["1", "2"]},
|
@@ -302,293 +326,341 @@ describe Rack::Utils do
|
|
302
326
|
{"x" => {"y" => [{"z" => "1", "w" => "a"}, {"z" => "2", "w" => "3"}]}}
|
303
327
|
].each { |params|
|
304
328
|
qs = Rack::Utils.build_nested_query(params)
|
305
|
-
Rack::Utils.parse_nested_query(qs).
|
329
|
+
Rack::Utils.parse_nested_query(qs).must_equal params
|
306
330
|
}
|
307
331
|
|
308
332
|
lambda { Rack::Utils.build_nested_query("foo=bar") }.
|
309
|
-
|
310
|
-
message.
|
333
|
+
must_raise(ArgumentError).
|
334
|
+
message.must_equal "value must be a Hash"
|
311
335
|
end
|
312
336
|
|
313
|
-
|
337
|
+
it "parse query strings that have a non-existent value" do
|
314
338
|
key = "post/2011/08/27/Deux-%22rat%C3%A9s%22-de-l-Universit"
|
315
|
-
Rack::Utils.parse_query(key).
|
339
|
+
Rack::Utils.parse_query(key).must_equal Rack::Utils.unescape(key) => nil
|
316
340
|
end
|
317
341
|
|
318
|
-
|
342
|
+
it "build query strings without = with non-existent values" do
|
319
343
|
key = "post/2011/08/27/Deux-%22rat%C3%A9s%22-de-l-Universit"
|
320
344
|
key = Rack::Utils.unescape(key)
|
321
|
-
Rack::Utils.build_query(key => nil).
|
345
|
+
Rack::Utils.build_query(key => nil).must_equal Rack::Utils.escape(key)
|
322
346
|
end
|
323
347
|
|
324
|
-
|
348
|
+
it "parse q-values" do
|
325
349
|
# XXX handle accept-extension
|
326
|
-
Rack::Utils.q_values("foo;q=0.5,bar,baz;q=0.9").
|
350
|
+
Rack::Utils.q_values("foo;q=0.5,bar,baz;q=0.9").must_equal [
|
327
351
|
[ 'foo', 0.5 ],
|
328
352
|
[ 'bar', 1.0 ],
|
329
353
|
[ 'baz', 0.9 ]
|
330
354
|
]
|
331
355
|
end
|
332
356
|
|
333
|
-
|
334
|
-
Rack::Utils.best_q_match("text/html", %w[text/html]).
|
357
|
+
it "select best quality match" do
|
358
|
+
Rack::Utils.best_q_match("text/html", %w[text/html]).must_equal "text/html"
|
335
359
|
|
336
360
|
# More specific matches are preferred
|
337
|
-
Rack::Utils.best_q_match("text/*;q=0.5,text/html;q=1.0", %w[text/html]).
|
361
|
+
Rack::Utils.best_q_match("text/*;q=0.5,text/html;q=1.0", %w[text/html]).must_equal "text/html"
|
338
362
|
|
339
363
|
# Higher quality matches are preferred
|
340
|
-
Rack::Utils.best_q_match("text/*;q=0.5,text/plain;q=1.0", %w[text/plain text/html]).
|
364
|
+
Rack::Utils.best_q_match("text/*;q=0.5,text/plain;q=1.0", %w[text/plain text/html]).must_equal "text/plain"
|
341
365
|
|
342
366
|
# Respect requested content type
|
343
|
-
Rack::Utils.best_q_match("application/json", %w[application/vnd.lotus-1-2-3 application/json]).
|
367
|
+
Rack::Utils.best_q_match("application/json", %w[application/vnd.lotus-1-2-3 application/json]).must_equal "application/json"
|
344
368
|
|
345
369
|
# All else equal, the available mimes are preferred in order
|
346
|
-
Rack::Utils.best_q_match("text/*", %w[text/html text/plain]).
|
347
|
-
Rack::Utils.best_q_match("text/plain,text/html", %w[text/html text/plain]).
|
370
|
+
Rack::Utils.best_q_match("text/*", %w[text/html text/plain]).must_equal "text/html"
|
371
|
+
Rack::Utils.best_q_match("text/plain,text/html", %w[text/html text/plain]).must_equal "text/html"
|
348
372
|
|
349
373
|
# When there are no matches, return nil:
|
350
|
-
Rack::Utils.best_q_match("application/json", %w[text/html text/plain]).
|
374
|
+
Rack::Utils.best_q_match("application/json", %w[text/html text/plain]).must_equal nil
|
351
375
|
end
|
352
376
|
|
353
|
-
|
354
|
-
Rack::Utils.escape_html("foo").
|
355
|
-
Rack::Utils.escape_html("f&o").
|
356
|
-
Rack::Utils.escape_html("f<o").
|
357
|
-
Rack::Utils.escape_html("f>o").
|
358
|
-
Rack::Utils.escape_html("f'o").
|
359
|
-
Rack::Utils.escape_html('f"o').
|
360
|
-
Rack::Utils.escape_html("f/o").
|
361
|
-
Rack::Utils.escape_html("<foo></foo>").
|
377
|
+
it "escape html entities [&><'\"/]" do
|
378
|
+
Rack::Utils.escape_html("foo").must_equal "foo"
|
379
|
+
Rack::Utils.escape_html("f&o").must_equal "f&o"
|
380
|
+
Rack::Utils.escape_html("f<o").must_equal "f<o"
|
381
|
+
Rack::Utils.escape_html("f>o").must_equal "f>o"
|
382
|
+
Rack::Utils.escape_html("f'o").must_equal "f'o"
|
383
|
+
Rack::Utils.escape_html('f"o').must_equal "f"o"
|
384
|
+
Rack::Utils.escape_html("f/o").must_equal "f/o"
|
385
|
+
Rack::Utils.escape_html("<foo></foo>").must_equal "<foo></foo>"
|
362
386
|
end
|
363
387
|
|
364
|
-
|
388
|
+
it "escape html entities even on MRI when it's bugged" do
|
365
389
|
test_escape = lambda do
|
366
|
-
|
367
|
-
Rack::Utils.escape_html("\300<").should.equal "\300<"
|
368
|
-
end
|
390
|
+
Rack::Utils.escape_html("\300<").must_equal "\300<"
|
369
391
|
end
|
370
392
|
|
371
|
-
|
372
|
-
test_escape.call
|
373
|
-
else
|
374
|
-
test_escape.should.raise(ArgumentError)
|
375
|
-
end
|
393
|
+
test_escape.must_raise ArgumentError
|
376
394
|
end
|
377
395
|
|
378
|
-
|
379
|
-
should "escape html entities in unicode strings" do
|
396
|
+
it "escape html entities in unicode strings" do
|
380
397
|
# the following will cause warnings if the regex is poorly encoded:
|
381
|
-
|
382
|
-
end
|
398
|
+
Rack::Utils.escape_html("☃").must_equal "☃"
|
383
399
|
end
|
384
400
|
|
385
|
-
|
401
|
+
it "figure out which encodings are acceptable" do
|
386
402
|
helper = lambda do |a, b|
|
387
403
|
Rack::Request.new(Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => a))
|
388
404
|
Rack::Utils.select_best_encoding(a, b)
|
389
405
|
end
|
390
406
|
|
391
|
-
helper.call(%w(), [["x", 1]]).
|
392
|
-
helper.call(%w(identity), [["identity", 0.0]]).
|
393
|
-
helper.call(%w(identity), [["*", 0.0]]).
|
407
|
+
helper.call(%w(), [["x", 1]]).must_equal nil
|
408
|
+
helper.call(%w(identity), [["identity", 0.0]]).must_equal nil
|
409
|
+
helper.call(%w(identity), [["*", 0.0]]).must_equal nil
|
394
410
|
|
395
|
-
helper.call(%w(identity), [["compress", 1.0], ["gzip", 1.0]]).
|
411
|
+
helper.call(%w(identity), [["compress", 1.0], ["gzip", 1.0]]).must_equal "identity"
|
396
412
|
|
397
|
-
helper.call(%w(compress gzip identity), [["compress", 1.0], ["gzip", 1.0]]).
|
398
|
-
helper.call(%w(compress gzip identity), [["compress", 0.5], ["gzip", 1.0]]).
|
413
|
+
helper.call(%w(compress gzip identity), [["compress", 1.0], ["gzip", 1.0]]).must_equal "compress"
|
414
|
+
helper.call(%w(compress gzip identity), [["compress", 0.5], ["gzip", 1.0]]).must_equal "gzip"
|
399
415
|
|
400
|
-
helper.call(%w(foo bar identity), []).
|
401
|
-
helper.call(%w(foo bar identity), [["*", 1.0]]).
|
402
|
-
helper.call(%w(foo bar identity), [["*", 1.0], ["foo", 0.9]]).
|
416
|
+
helper.call(%w(foo bar identity), []).must_equal "identity"
|
417
|
+
helper.call(%w(foo bar identity), [["*", 1.0]]).must_equal "foo"
|
418
|
+
helper.call(%w(foo bar identity), [["*", 1.0], ["foo", 0.9]]).must_equal "bar"
|
403
419
|
|
404
|
-
helper.call(%w(foo bar identity), [["foo", 0], ["bar", 0]]).
|
405
|
-
helper.call(%w(foo bar baz identity), [["*", 0], ["identity", 0.1]]).
|
420
|
+
helper.call(%w(foo bar identity), [["foo", 0], ["bar", 0]]).must_equal "identity"
|
421
|
+
helper.call(%w(foo bar baz identity), [["*", 0], ["identity", 0.1]]).must_equal "identity"
|
406
422
|
end
|
407
423
|
|
408
|
-
|
409
|
-
Rack::Utils.
|
424
|
+
it "should perform constant time string comparison" do
|
425
|
+
Rack::Utils.secure_compare('a', 'a').must_equal true
|
426
|
+
Rack::Utils.secure_compare('a', 'b').must_equal false
|
410
427
|
end
|
411
428
|
|
412
|
-
|
413
|
-
Rack::Utils.
|
414
|
-
Rack::Utils.secure_compare('a', 'b').should.equal false
|
429
|
+
it "return status code for integer" do
|
430
|
+
Rack::Utils.status_code(200).must_equal 200
|
415
431
|
end
|
416
432
|
|
417
|
-
|
418
|
-
Rack::Utils.status_code(200).
|
433
|
+
it "return status code for string" do
|
434
|
+
Rack::Utils.status_code("200").must_equal 200
|
419
435
|
end
|
420
436
|
|
421
|
-
|
422
|
-
Rack::Utils.status_code(
|
437
|
+
it "return status code for symbol" do
|
438
|
+
Rack::Utils.status_code(:ok).must_equal 200
|
423
439
|
end
|
424
440
|
|
425
|
-
|
426
|
-
Rack::Utils.
|
441
|
+
it "return rfc2822 format from rfc2822 helper" do
|
442
|
+
Rack::Utils.rfc2822(Time.at(0).gmtime).must_equal "Thu, 01 Jan 1970 00:00:00 -0000"
|
427
443
|
end
|
428
444
|
|
429
|
-
|
430
|
-
Rack::Utils.
|
445
|
+
it "return rfc2109 format from rfc2109 helper" do
|
446
|
+
Rack::Utils.rfc2109(Time.at(0).gmtime).must_equal "Thu, 01-Jan-1970 00:00:00 GMT"
|
431
447
|
end
|
432
448
|
|
433
|
-
|
434
|
-
Rack::Utils.
|
449
|
+
it "clean directory traversal" do
|
450
|
+
Rack::Utils.clean_path_info("/cgi/../cgi/test").must_equal "/cgi/test"
|
451
|
+
Rack::Utils.clean_path_info(".").must_be_empty
|
452
|
+
Rack::Utils.clean_path_info("test/..").must_be_empty
|
435
453
|
end
|
436
454
|
|
437
|
-
|
438
|
-
Rack::Utils.clean_path_info("
|
439
|
-
Rack::Utils.clean_path_info(".").
|
440
|
-
Rack::Utils.clean_path_info("test/..").should.empty
|
455
|
+
it "clean unsafe directory traversal to safe path" do
|
456
|
+
Rack::Utils.clean_path_info("/../README.rdoc").must_equal "/README.rdoc"
|
457
|
+
Rack::Utils.clean_path_info("../test/spec_utils.rb").must_equal "test/spec_utils.rb"
|
441
458
|
end
|
442
459
|
|
443
|
-
|
444
|
-
Rack::Utils.clean_path_info("
|
445
|
-
|
460
|
+
it "not clean directory traversal with encoded periods" do
|
461
|
+
Rack::Utils.clean_path_info("/%2E%2E/README").must_equal "/%2E%2E/README"
|
462
|
+
end
|
463
|
+
|
464
|
+
it "clean slash only paths" do
|
465
|
+
Rack::Utils.clean_path_info("/").must_equal "/"
|
466
|
+
end
|
467
|
+
end
|
468
|
+
|
469
|
+
describe Rack::Utils, "cookies" do
|
470
|
+
it "parses cookies" do
|
471
|
+
env = Rack::MockRequest.env_for("", "HTTP_COOKIE" => "zoo=m")
|
472
|
+
Rack::Utils.parse_cookies(env).must_equal({"zoo" => "m"})
|
473
|
+
|
474
|
+
env = Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=%")
|
475
|
+
Rack::Utils.parse_cookies(env).must_equal({"foo" => "%"})
|
476
|
+
|
477
|
+
env = Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar;foo=car")
|
478
|
+
Rack::Utils.parse_cookies(env).must_equal({"foo" => "bar"})
|
479
|
+
|
480
|
+
env = Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar;quux=h&m")
|
481
|
+
Rack::Utils.parse_cookies(env).must_equal({"foo" => "bar", "quux" => "h&m"})
|
482
|
+
|
483
|
+
env = Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar").freeze
|
484
|
+
Rack::Utils.parse_cookies(env).must_equal({"foo" => "bar"})
|
446
485
|
end
|
447
486
|
|
448
|
-
|
449
|
-
Rack::Utils.
|
487
|
+
it "adds new cookies to nil header" do
|
488
|
+
Rack::Utils.add_cookie_to_header(nil, 'name', 'value').must_equal 'name=value'
|
450
489
|
end
|
451
490
|
|
452
|
-
|
453
|
-
|
491
|
+
it "adds new cookies to blank header" do
|
492
|
+
header = ''
|
493
|
+
Rack::Utils.add_cookie_to_header(header, 'name', 'value').must_equal 'name=value'
|
494
|
+
header.must_equal ''
|
495
|
+
end
|
496
|
+
|
497
|
+
it "adds new cookies to string header" do
|
498
|
+
header = 'existing-cookie'
|
499
|
+
Rack::Utils.add_cookie_to_header(header, 'name', 'value').must_equal "existing-cookie\nname=value"
|
500
|
+
header.must_equal 'existing-cookie'
|
501
|
+
end
|
502
|
+
|
503
|
+
it "adds new cookies to array header" do
|
504
|
+
header = %w[ existing-cookie ]
|
505
|
+
Rack::Utils.add_cookie_to_header(header, 'name', 'value').must_equal "existing-cookie\nname=value"
|
506
|
+
header.must_equal %w[ existing-cookie ]
|
507
|
+
end
|
508
|
+
|
509
|
+
it "adds new cookies to an unrecognized header" do
|
510
|
+
lambda {
|
511
|
+
Rack::Utils.add_cookie_to_header(Object.new, 'name', 'value')
|
512
|
+
}.must_raise ArgumentError
|
454
513
|
end
|
455
514
|
end
|
456
515
|
|
457
516
|
describe Rack::Utils, "byte_range" do
|
458
|
-
|
459
|
-
Rack::Utils.byte_ranges({},500).
|
460
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "foobar"},500).
|
461
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "furlongs=123-456"},500).
|
462
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes="},500).
|
463
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-"},500).
|
464
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123,456"},500).
|
517
|
+
it "ignore missing or syntactically invalid byte ranges" do
|
518
|
+
Rack::Utils.byte_ranges({},500).must_equal nil
|
519
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "foobar"},500).must_equal nil
|
520
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "furlongs=123-456"},500).must_equal nil
|
521
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes="},500).must_equal nil
|
522
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-"},500).must_equal nil
|
523
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123,456"},500).must_equal nil
|
465
524
|
# A range of non-positive length is syntactically invalid and ignored:
|
466
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=456-123"},500).
|
467
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=456-455"},500).
|
525
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=456-123"},500).must_equal nil
|
526
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=456-455"},500).must_equal nil
|
468
527
|
end
|
469
528
|
|
470
|
-
|
471
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123-456"},500).
|
472
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123-"},500).
|
473
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-100"},500).
|
474
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=0-0"},500).
|
475
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=499-499"},500).
|
529
|
+
it "parse simple byte ranges" do
|
530
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123-456"},500).must_equal [(123..456)]
|
531
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123-"},500).must_equal [(123..499)]
|
532
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-100"},500).must_equal [(400..499)]
|
533
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=0-0"},500).must_equal [(0..0)]
|
534
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=499-499"},500).must_equal [(499..499)]
|
476
535
|
end
|
477
536
|
|
478
|
-
|
479
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=500-600,601-999"},1000).
|
537
|
+
it "parse several byte ranges" do
|
538
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=500-600,601-999"},1000).must_equal [(500..600),(601..999)]
|
480
539
|
end
|
481
540
|
|
482
|
-
|
483
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123-999"},500).
|
484
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=600-999"},500).
|
485
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-999"},500).
|
541
|
+
it "truncate byte ranges" do
|
542
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123-999"},500).must_equal [(123..499)]
|
543
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=600-999"},500).must_equal []
|
544
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-999"},500).must_equal [(0..499)]
|
486
545
|
end
|
487
546
|
|
488
|
-
|
489
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=500-501"},500).
|
490
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=500-"},500).
|
491
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=999-"},500).
|
492
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-0"},500).
|
547
|
+
it "ignore unsatisfiable byte ranges" do
|
548
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=500-501"},500).must_equal []
|
549
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=500-"},500).must_equal []
|
550
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=999-"},500).must_equal []
|
551
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-0"},500).must_equal []
|
493
552
|
end
|
494
553
|
|
495
|
-
|
496
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123-456"},0).
|
497
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=0-"},0).
|
498
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-100"},0).
|
499
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=0-0"},0).
|
500
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-0"},0).
|
554
|
+
it "handle byte ranges of empty files" do
|
555
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123-456"},0).must_equal []
|
556
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=0-"},0).must_equal []
|
557
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-100"},0).must_equal []
|
558
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=0-0"},0).must_equal []
|
559
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-0"},0).must_equal []
|
501
560
|
end
|
502
561
|
end
|
503
562
|
|
504
563
|
describe Rack::Utils::HeaderHash do
|
505
|
-
|
564
|
+
it "retain header case" do
|
506
565
|
h = Rack::Utils::HeaderHash.new("Content-MD5" => "d5ff4e2a0 ...")
|
507
566
|
h['ETag'] = 'Boo!'
|
508
|
-
h.to_hash.
|
567
|
+
h.to_hash.must_equal "Content-MD5" => "d5ff4e2a0 ...", "ETag" => 'Boo!'
|
509
568
|
end
|
510
569
|
|
511
|
-
|
570
|
+
it "check existence of keys case insensitively" do
|
512
571
|
h = Rack::Utils::HeaderHash.new("Content-MD5" => "d5ff4e2a0 ...")
|
513
|
-
h.
|
514
|
-
h.
|
572
|
+
h.must_include 'content-md5'
|
573
|
+
h.wont_include 'ETag'
|
574
|
+
end
|
575
|
+
|
576
|
+
it "create deep HeaderHash copy on dup" do
|
577
|
+
h1 = Rack::Utils::HeaderHash.new("Content-MD5" => "d5ff4e2a0 ...")
|
578
|
+
h2 = h1.dup
|
579
|
+
|
580
|
+
h1.must_include 'content-md5'
|
581
|
+
h2.must_include 'content-md5'
|
582
|
+
|
583
|
+
h2.delete("Content-MD5")
|
584
|
+
|
585
|
+
h2.wont_include 'content-md5'
|
586
|
+
h1.must_include 'content-md5'
|
515
587
|
end
|
516
588
|
|
517
|
-
|
589
|
+
it "merge case-insensitively" do
|
518
590
|
h = Rack::Utils::HeaderHash.new("ETag" => 'HELLO', "content-length" => '123')
|
519
591
|
merged = h.merge("Etag" => 'WORLD', 'Content-Length' => '321', "Foo" => 'BAR')
|
520
|
-
merged.
|
592
|
+
merged.must_equal "Etag"=>'WORLD', "Content-Length"=>'321', "Foo"=>'BAR'
|
521
593
|
end
|
522
594
|
|
523
|
-
|
595
|
+
it "overwrite case insensitively and assume the new key's case" do
|
524
596
|
h = Rack::Utils::HeaderHash.new("Foo-Bar" => "baz")
|
525
597
|
h["foo-bar"] = "bizzle"
|
526
|
-
h["FOO-BAR"].
|
527
|
-
h.length.
|
528
|
-
h.to_hash.
|
598
|
+
h["FOO-BAR"].must_equal "bizzle"
|
599
|
+
h.length.must_equal 1
|
600
|
+
h.to_hash.must_equal "foo-bar" => "bizzle"
|
529
601
|
end
|
530
602
|
|
531
|
-
|
603
|
+
it "be converted to real Hash" do
|
532
604
|
h = Rack::Utils::HeaderHash.new("foo" => "bar")
|
533
|
-
h.to_hash.
|
605
|
+
h.to_hash.must_be_instance_of Hash
|
534
606
|
end
|
535
607
|
|
536
|
-
|
608
|
+
it "convert Array values to Strings when converting to Hash" do
|
537
609
|
h = Rack::Utils::HeaderHash.new("foo" => ["bar", "baz"])
|
538
|
-
h.to_hash.
|
610
|
+
h.to_hash.must_equal({ "foo" => "bar\nbaz" })
|
539
611
|
end
|
540
612
|
|
541
|
-
|
613
|
+
it "replace hashes correctly" do
|
542
614
|
h = Rack::Utils::HeaderHash.new("Foo-Bar" => "baz")
|
543
615
|
j = {"foo" => "bar"}
|
544
616
|
h.replace(j)
|
545
|
-
h["foo"].
|
617
|
+
h["foo"].must_equal "bar"
|
546
618
|
end
|
547
619
|
|
548
|
-
|
620
|
+
it "be able to delete the given key case-sensitively" do
|
549
621
|
h = Rack::Utils::HeaderHash.new("foo" => "bar")
|
550
622
|
h.delete("foo")
|
551
|
-
h["foo"].
|
552
|
-
h["FOO"].
|
623
|
+
h["foo"].must_be_nil
|
624
|
+
h["FOO"].must_be_nil
|
553
625
|
end
|
554
626
|
|
555
|
-
|
627
|
+
it "be able to delete the given key case-insensitively" do
|
556
628
|
h = Rack::Utils::HeaderHash.new("foo" => "bar")
|
557
629
|
h.delete("FOO")
|
558
|
-
h["foo"].
|
559
|
-
h["FOO"].
|
630
|
+
h["foo"].must_be_nil
|
631
|
+
h["FOO"].must_be_nil
|
560
632
|
end
|
561
633
|
|
562
|
-
|
634
|
+
it "return the deleted value when #delete is called on an existing key" do
|
563
635
|
h = Rack::Utils::HeaderHash.new("foo" => "bar")
|
564
|
-
h.delete("Foo").
|
636
|
+
h.delete("Foo").must_equal "bar"
|
565
637
|
end
|
566
638
|
|
567
|
-
|
639
|
+
it "return nil when #delete is called on a non-existant key" do
|
568
640
|
h = Rack::Utils::HeaderHash.new("foo" => "bar")
|
569
|
-
h.delete("Hello").
|
641
|
+
h.delete("Hello").must_be_nil
|
570
642
|
end
|
571
643
|
|
572
|
-
|
644
|
+
it "avoid unnecessary object creation if possible" do
|
573
645
|
a = Rack::Utils::HeaderHash.new("foo" => "bar")
|
574
646
|
b = Rack::Utils::HeaderHash.new(a)
|
575
|
-
b.object_id.
|
576
|
-
b.
|
647
|
+
b.object_id.must_equal a.object_id
|
648
|
+
b.must_equal a
|
577
649
|
end
|
578
650
|
|
579
|
-
|
651
|
+
it "convert Array values to Strings when responding to #each" do
|
580
652
|
h = Rack::Utils::HeaderHash.new("foo" => ["bar", "baz"])
|
581
653
|
h.each do |k,v|
|
582
|
-
k.
|
583
|
-
v.
|
654
|
+
k.must_equal "foo"
|
655
|
+
v.must_equal "bar\nbaz"
|
584
656
|
end
|
585
657
|
end
|
586
658
|
|
587
|
-
|
659
|
+
it "not create headers out of thin air" do
|
588
660
|
h = Rack::Utils::HeaderHash.new
|
589
661
|
h['foo']
|
590
|
-
h['foo'].
|
591
|
-
h.
|
662
|
+
h['foo'].must_be_nil
|
663
|
+
h.wont_include 'foo'
|
592
664
|
end
|
593
665
|
end
|
594
666
|
|
@@ -605,27 +677,27 @@ describe Rack::Utils::Context do
|
|
605
677
|
test_target4 = proc{|e| [200,{'Content-Type'=>'text/plain', 'Content-Length'=>'0'},['']] }
|
606
678
|
test_app = ContextTest.new test_target4
|
607
679
|
|
608
|
-
|
609
|
-
test_app.app.
|
680
|
+
it "set context correctly" do
|
681
|
+
test_app.app.must_equal test_target4
|
610
682
|
c1 = Rack::Utils::Context.new(test_app, test_target1)
|
611
|
-
c1.for.
|
612
|
-
c1.app.
|
683
|
+
c1.for.must_equal test_app
|
684
|
+
c1.app.must_equal test_target1
|
613
685
|
c2 = Rack::Utils::Context.new(test_app, test_target2)
|
614
|
-
c2.for.
|
615
|
-
c2.app.
|
686
|
+
c2.for.must_equal test_app
|
687
|
+
c2.app.must_equal test_target2
|
616
688
|
end
|
617
689
|
|
618
|
-
|
690
|
+
it "alter app on recontexting" do
|
619
691
|
c1 = Rack::Utils::Context.new(test_app, test_target1)
|
620
692
|
c2 = c1.recontext(test_target2)
|
621
|
-
c2.for.
|
622
|
-
c2.app.
|
693
|
+
c2.for.must_equal test_app
|
694
|
+
c2.app.must_equal test_target2
|
623
695
|
c3 = c2.recontext(test_target3)
|
624
|
-
c3.for.
|
625
|
-
c3.app.
|
696
|
+
c3.for.must_equal test_app
|
697
|
+
c3.app.must_equal test_target3
|
626
698
|
end
|
627
699
|
|
628
|
-
|
700
|
+
it "run different apps" do
|
629
701
|
c1 = Rack::Utils::Context.new test_app, test_target1
|
630
702
|
c2 = c1.recontext test_target2
|
631
703
|
c3 = c2.recontext test_target3
|
@@ -633,15 +705,15 @@ describe Rack::Utils::Context do
|
|
633
705
|
a4 = Rack::Lint.new c4
|
634
706
|
a5 = Rack::Lint.new test_app
|
635
707
|
r1 = c1.call('hello')
|
636
|
-
r1.
|
708
|
+
r1.must_equal 'hello world'
|
637
709
|
r2 = c2.call(2)
|
638
|
-
r2.
|
710
|
+
r2.must_equal 4
|
639
711
|
r3 = c3.call(:misc_symbol)
|
640
|
-
r3.
|
712
|
+
r3.must_be_nil
|
641
713
|
r4 = Rack::MockRequest.new(a4).get('/')
|
642
|
-
r4.status.
|
714
|
+
r4.status.must_equal 200
|
643
715
|
r5 = Rack::MockRequest.new(a5).get('/')
|
644
|
-
r5.status.
|
645
|
-
r4.body.
|
716
|
+
r5.status.must_equal 200
|
717
|
+
r4.body.must_equal r5.body
|
646
718
|
end
|
647
719
|
end
|