rack 1.2.8 → 1.3.0.beta
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.
- data/README +9 -177
- data/Rakefile +2 -1
- data/SPEC +2 -2
- data/lib/rack.rb +2 -13
- data/lib/rack/auth/abstract/request.rb +7 -5
- data/lib/rack/auth/digest/md5.rb +6 -2
- data/lib/rack/auth/digest/params.rb +5 -7
- data/lib/rack/auth/digest/request.rb +1 -1
- data/lib/rack/backports/uri/common.rb +64 -0
- data/lib/rack/builder.rb +60 -3
- data/lib/rack/chunked.rb +29 -22
- data/lib/rack/conditionalget.rb +35 -16
- data/lib/rack/content_length.rb +3 -3
- data/lib/rack/deflater.rb +5 -2
- data/lib/rack/etag.rb +38 -10
- data/lib/rack/file.rb +76 -43
- data/lib/rack/handler.rb +13 -7
- data/lib/rack/handler/cgi.rb +0 -2
- data/lib/rack/handler/fastcgi.rb +13 -4
- data/lib/rack/handler/lsws.rb +0 -2
- data/lib/rack/handler/mongrel.rb +12 -2
- data/lib/rack/handler/scgi.rb +9 -1
- data/lib/rack/handler/thin.rb +7 -1
- data/lib/rack/handler/webrick.rb +12 -5
- data/lib/rack/lint.rb +2 -2
- data/lib/rack/lock.rb +29 -3
- data/lib/rack/methodoverride.rb +1 -1
- data/lib/rack/mime.rb +2 -2
- data/lib/rack/mock.rb +28 -33
- data/lib/rack/multipart.rb +34 -0
- data/lib/rack/multipart/generator.rb +93 -0
- data/lib/rack/multipart/parser.rb +164 -0
- data/lib/rack/multipart/uploaded_file.rb +30 -0
- data/lib/rack/request.rb +55 -19
- data/lib/rack/response.rb +10 -8
- data/lib/rack/sendfile.rb +14 -18
- data/lib/rack/server.rb +55 -8
- data/lib/rack/session/abstract/id.rb +233 -22
- data/lib/rack/session/cookie.rb +99 -46
- data/lib/rack/session/memcache.rb +30 -56
- data/lib/rack/session/pool.rb +22 -43
- data/lib/rack/showexceptions.rb +40 -11
- data/lib/rack/showstatus.rb +9 -2
- data/lib/rack/static.rb +29 -9
- data/lib/rack/urlmap.rb +6 -1
- data/lib/rack/utils.rb +67 -326
- data/rack.gemspec +2 -3
- data/test/builder/anything.rb +5 -0
- data/test/builder/comment.ru +4 -0
- data/test/builder/end.ru +3 -0
- data/test/builder/options.ru +2 -0
- data/test/cgi/lighttpd.conf +1 -1
- data/test/cgi/lighttpd.errors +412 -0
- data/test/multipart/content_type_and_no_filename +6 -0
- data/test/multipart/text +5 -0
- data/test/multipart/webkit +32 -0
- data/test/registering_handler/rack/handler/registering_myself.rb +8 -0
- data/test/spec_auth_digest.rb +20 -5
- data/test/spec_builder.rb +29 -0
- data/test/spec_cgi.rb +11 -0
- data/test/spec_chunked.rb +1 -1
- data/test/spec_commonlogger.rb +1 -1
- data/test/spec_conditionalget.rb +47 -0
- data/test/spec_content_length.rb +0 -6
- data/test/spec_content_type.rb +5 -5
- data/test/spec_deflater.rb +46 -2
- data/test/spec_etag.rb +68 -1
- data/test/spec_fastcgi.rb +11 -0
- data/test/spec_file.rb +54 -3
- data/test/spec_handler.rb +23 -5
- data/test/spec_lint.rb +2 -2
- data/test/spec_lock.rb +111 -5
- data/test/spec_methodoverride.rb +2 -2
- data/test/spec_mock.rb +3 -3
- data/test/spec_mongrel.rb +1 -2
- data/test/spec_multipart.rb +279 -0
- data/test/spec_request.rb +222 -38
- data/test/spec_response.rb +9 -3
- data/test/spec_server.rb +74 -0
- data/test/spec_session_abstract_id.rb +43 -0
- data/test/spec_session_cookie.rb +97 -15
- data/test/spec_session_memcache.rb +60 -50
- data/test/spec_session_pool.rb +63 -40
- data/test/spec_showexceptions.rb +64 -0
- data/test/spec_static.rb +23 -0
- data/test/spec_utils.rb +65 -351
- data/test/spec_webrick.rb +23 -4
- metadata +35 -15
- data/test/spec_auth.rb +0 -57
data/test/multipart/text
CHANGED
@@ -3,6 +3,11 @@ Content-Disposition: form-data; name="submit-name"
|
|
3
3
|
|
4
4
|
Larry
|
5
5
|
--AaB03x
|
6
|
+
Content-Disposition: form-data; name="submit-name-with-content"
|
7
|
+
Content-Type: text/plain
|
8
|
+
|
9
|
+
Berry
|
10
|
+
--AaB03x
|
6
11
|
Content-Disposition: form-data; name="files"; filename="file1.txt"
|
7
12
|
Content-Type: text/plain
|
8
13
|
|
@@ -0,0 +1,32 @@
|
|
1
|
+
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
2
|
+
Content-Disposition: form-data; name="_method"
|
3
|
+
|
4
|
+
put
|
5
|
+
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
6
|
+
Content-Disposition: form-data; name="profile[blog]"
|
7
|
+
|
8
|
+
|
9
|
+
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
10
|
+
Content-Disposition: form-data; name="profile[public_email]"
|
11
|
+
|
12
|
+
|
13
|
+
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
14
|
+
Content-Disposition: form-data; name="profile[interests]"
|
15
|
+
|
16
|
+
|
17
|
+
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
18
|
+
Content-Disposition: form-data; name="profile[bio]"
|
19
|
+
|
20
|
+
hello
|
21
|
+
|
22
|
+
"quote"
|
23
|
+
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
24
|
+
Content-Disposition: form-data; name="media"; filename=""
|
25
|
+
Content-Type: application/octet-stream
|
26
|
+
|
27
|
+
|
28
|
+
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
29
|
+
Content-Disposition: form-data; name="commit"
|
30
|
+
|
31
|
+
Save
|
32
|
+
------WebKitFormBoundaryWLHCs9qmcJJoyjKR--
|
data/test/spec_auth_digest.rb
CHANGED
@@ -8,17 +8,15 @@ describe Rack::Auth::Digest::MD5 do
|
|
8
8
|
|
9
9
|
def unprotected_app
|
10
10
|
lambda do |env|
|
11
|
-
|
11
|
+
friend = Rack::Utils.parse_query(env["QUERY_STRING"])["friend"]
|
12
|
+
[ 200, {'Content-Type' => 'text/plain'}, ["Hi #{env['REMOTE_USER']}#{friend ? " and #{friend}" : ''}"] ]
|
12
13
|
end
|
13
14
|
end
|
14
15
|
|
15
16
|
def protected_app
|
16
|
-
|
17
|
+
Rack::Auth::Digest::MD5.new(unprotected_app, :realm => realm, :opaque => 'this-should-be-secret') do |username|
|
17
18
|
{ 'Alice' => 'correct-password' }[username]
|
18
19
|
end
|
19
|
-
app.realm = realm
|
20
|
-
app.opaque = 'this-should-be-secret'
|
21
|
-
app
|
22
20
|
end
|
23
21
|
|
24
22
|
def protected_app_with_hashed_passwords
|
@@ -207,6 +205,23 @@ describe Rack::Auth::Digest::MD5 do
|
|
207
205
|
end
|
208
206
|
end
|
209
207
|
|
208
|
+
should 'return application output when used with a query string and path as uri' do
|
209
|
+
@request = Rack::MockRequest.new(partially_protected_app)
|
210
|
+
request_with_digest_auth 'GET', '/protected?friend=Mike', 'Alice', 'correct-password' do |response|
|
211
|
+
response.status.should.equal 200
|
212
|
+
response.body.to_s.should.equal 'Hi Alice and Mike'
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
should 'return application output when used with a query string and fullpath as uri' do
|
217
|
+
@request = Rack::MockRequest.new(partially_protected_app)
|
218
|
+
qs_uri = '/protected?friend=Mike'
|
219
|
+
request_with_digest_auth 'GET', qs_uri, 'Alice', 'correct-password', 'uri' => qs_uri do |response|
|
220
|
+
response.status.should.equal 200
|
221
|
+
response.body.to_s.should.equal 'Hi Alice and Mike'
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
210
225
|
should 'return application output if correct credentials given for POST' do
|
211
226
|
request_with_digest_auth 'POST', '/', 'Alice', 'correct-password' do |response|
|
212
227
|
response.status.should.equal 200
|
data/test/spec_builder.rb
CHANGED
@@ -120,4 +120,33 @@ describe Rack::Builder do
|
|
120
120
|
Rack::MockRequest.new(app).get("/").should.be.server_error
|
121
121
|
end
|
122
122
|
|
123
|
+
describe "parse_file" do
|
124
|
+
def config_file(name)
|
125
|
+
File.join(File.dirname(__FILE__), 'builder', name)
|
126
|
+
end
|
127
|
+
|
128
|
+
it "parses commented options" do
|
129
|
+
app, options = Rack::Builder.parse_file config_file('options.ru')
|
130
|
+
options[:debug].should.be.true
|
131
|
+
Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'OK'
|
132
|
+
end
|
133
|
+
|
134
|
+
it "removes __END__ before evaluating app" do
|
135
|
+
app, options = Rack::Builder.parse_file config_file('end.ru')
|
136
|
+
Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'OK'
|
137
|
+
end
|
138
|
+
|
139
|
+
it "supports multi-line comments" do
|
140
|
+
lambda {
|
141
|
+
Rack::Builder.parse_file config_file('comment.ru')
|
142
|
+
}.should.not.raise(SyntaxError)
|
143
|
+
end
|
144
|
+
|
145
|
+
it "requires anything not ending in .ru" do
|
146
|
+
$: << File.dirname(__FILE__)
|
147
|
+
app, options = Rack::Builder.parse_file 'builder/anything'
|
148
|
+
Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'OK'
|
149
|
+
$:.pop
|
150
|
+
end
|
151
|
+
end
|
123
152
|
end
|
data/test/spec_cgi.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
begin
|
1
2
|
require File.expand_path('../testrequest', __FILE__)
|
2
3
|
require 'rack/handler/cgi'
|
3
4
|
|
@@ -7,6 +8,10 @@ describe Rack::Handler::CGI do
|
|
7
8
|
@host = '0.0.0.0'
|
8
9
|
@port = 9203
|
9
10
|
|
11
|
+
if `which lighttpd` && !$?.success?
|
12
|
+
raise "lighttpd not found"
|
13
|
+
end
|
14
|
+
|
10
15
|
# Keep this first.
|
11
16
|
$pid = fork {
|
12
17
|
ENV['RACK_ENV'] = 'deployment'
|
@@ -89,3 +94,9 @@ describe Rack::Handler::CGI do
|
|
89
94
|
Process.wait($pid).should == $pid
|
90
95
|
end
|
91
96
|
end
|
97
|
+
|
98
|
+
rescue RuntimeError
|
99
|
+
$stderr.puts "Skipping Rack::Handler::CGI tests (lighttpd is required). Install lighttpd and try again."
|
100
|
+
rescue NotImplementedError
|
101
|
+
$stderr.puts "Your Ruby implemenation or platform does not support fork. Skipping Rack::Handler::CGI tests."
|
102
|
+
end
|
data/test/spec_chunked.rb
CHANGED
@@ -52,7 +52,7 @@ describe Rack::Chunked do
|
|
52
52
|
[100, 204, 304].each do |status_code|
|
53
53
|
should "not modify response when status code is #{status_code}" do
|
54
54
|
app = lambda { |env| [status_code, {}, []] }
|
55
|
-
status, headers,
|
55
|
+
status, headers, _ = Rack::Chunked.new(app).call(@env)
|
56
56
|
status.should.equal status_code
|
57
57
|
headers.should.not.include 'Transfer-Encoding'
|
58
58
|
end
|
data/test/spec_commonlogger.rb
CHANGED
@@ -27,7 +27,7 @@ describe Rack::CommonLogger do
|
|
27
27
|
|
28
28
|
should "log to anything with +write+" do
|
29
29
|
log = StringIO.new
|
30
|
-
|
30
|
+
Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/")
|
31
31
|
|
32
32
|
log.string.should =~ /"GET \/ " 200 #{length} /
|
33
33
|
end
|
data/test/spec_conditionalget.rb
CHANGED
@@ -15,6 +15,17 @@ describe Rack::ConditionalGet do
|
|
15
15
|
response.body.should.be.empty
|
16
16
|
end
|
17
17
|
|
18
|
+
should "set a 304 status and truncate body when If-Modified-Since hits and is higher than current time" do
|
19
|
+
app = Rack::ConditionalGet.new(lambda { |env|
|
20
|
+
[200, {'Last-Modified'=>(Time.now - 3600).httpdate}, ['TEST']] })
|
21
|
+
|
22
|
+
response = Rack::MockRequest.new(app).
|
23
|
+
get("/", 'HTTP_IF_MODIFIED_SINCE' => Time.now.httpdate)
|
24
|
+
|
25
|
+
response.status.should.equal 304
|
26
|
+
response.body.should.be.empty
|
27
|
+
end
|
28
|
+
|
18
29
|
should "set a 304 status and truncate body when If-None-Match hits" do
|
19
30
|
app = Rack::ConditionalGet.new(lambda { |env|
|
20
31
|
[200, {'Etag'=>'1234'}, ['TEST']] })
|
@@ -26,6 +37,30 @@ describe Rack::ConditionalGet do
|
|
26
37
|
response.body.should.be.empty
|
27
38
|
end
|
28
39
|
|
40
|
+
should "not set a 304 status if If-Modified-Since hits but Etag does not" do
|
41
|
+
timestamp = Time.now.httpdate
|
42
|
+
app = Rack::ConditionalGet.new(lambda { |env|
|
43
|
+
[200, {'Last-Modified'=>timestamp, 'Etag'=>'1234'}, ['TEST']] })
|
44
|
+
|
45
|
+
response = Rack::MockRequest.new(app).
|
46
|
+
get("/", 'HTTP_IF_MODIFIED_SINCE' => timestamp, 'HTTP_IF_NONE_MATCH' => '4321')
|
47
|
+
|
48
|
+
response.status.should.equal 200
|
49
|
+
response.body.should.equal 'TEST'
|
50
|
+
end
|
51
|
+
|
52
|
+
should "set a 304 status and truncate body when both If-None-Match and If-Modified-Since hits" do
|
53
|
+
timestamp = Time.now.httpdate
|
54
|
+
app = Rack::ConditionalGet.new(lambda { |env|
|
55
|
+
[200, {'Last-Modified'=>timestamp, 'Etag'=>'1234'}, ['TEST']] })
|
56
|
+
|
57
|
+
response = Rack::MockRequest.new(app).
|
58
|
+
get("/", 'HTTP_IF_MODIFIED_SINCE' => timestamp, 'HTTP_IF_NONE_MATCH' => '1234')
|
59
|
+
|
60
|
+
response.status.should.equal 304
|
61
|
+
response.body.should.be.empty
|
62
|
+
end
|
63
|
+
|
29
64
|
should "not affect non-GET/HEAD requests" do
|
30
65
|
app = Rack::ConditionalGet.new(lambda { |env|
|
31
66
|
[200, {'Etag'=>'1234'}, ['TEST']] })
|
@@ -36,4 +71,16 @@ describe Rack::ConditionalGet do
|
|
36
71
|
response.status.should.equal 200
|
37
72
|
response.body.should.equal 'TEST'
|
38
73
|
end
|
74
|
+
|
75
|
+
should "not affect non-200 requests" do
|
76
|
+
app = Rack::ConditionalGet.new(lambda { |env|
|
77
|
+
[302, {'Etag'=>'1234'}, ['TEST']] })
|
78
|
+
|
79
|
+
response = Rack::MockRequest.new(app).
|
80
|
+
get("/", 'HTTP_IF_NONE_MATCH' => '1234')
|
81
|
+
|
82
|
+
response.status.should.equal 302
|
83
|
+
response.body.should.equal 'TEST'
|
84
|
+
end
|
85
|
+
|
39
86
|
end
|
data/test/spec_content_length.rb
CHANGED
@@ -1,12 +1,6 @@
|
|
1
1
|
require 'rack/content_length'
|
2
2
|
|
3
3
|
describe Rack::ContentLength do
|
4
|
-
should "set Content-Length on String bodies if none is set" do
|
5
|
-
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, "Hello, World!"] }
|
6
|
-
response = Rack::ContentLength.new(app).call({})
|
7
|
-
response[1]['Content-Length'].should.equal '13'
|
8
|
-
end
|
9
|
-
|
10
4
|
should "set Content-Length on Array bodies if none is set" do
|
11
5
|
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
|
12
6
|
response = Rack::ContentLength.new(app).call({})
|
data/test/spec_content_type.rb
CHANGED
@@ -3,26 +3,26 @@ require 'rack/content_type'
|
|
3
3
|
describe Rack::ContentType do
|
4
4
|
should "set Content-Type to default text/html if none is set" do
|
5
5
|
app = lambda { |env| [200, {}, "Hello, World!"] }
|
6
|
-
|
6
|
+
headers = Rack::ContentType.new(app).call({})[1]
|
7
7
|
headers['Content-Type'].should.equal 'text/html'
|
8
8
|
end
|
9
9
|
|
10
10
|
should "set Content-Type to chosen default if none is set" do
|
11
11
|
app = lambda { |env| [200, {}, "Hello, World!"] }
|
12
|
-
|
13
|
-
Rack::ContentType.new(app, 'application/octet-stream').call({})
|
12
|
+
headers =
|
13
|
+
Rack::ContentType.new(app, 'application/octet-stream').call({})[1]
|
14
14
|
headers['Content-Type'].should.equal 'application/octet-stream'
|
15
15
|
end
|
16
16
|
|
17
17
|
should "not change Content-Type if it is already set" do
|
18
18
|
app = lambda { |env| [200, {'Content-Type' => 'foo/bar'}, "Hello, World!"] }
|
19
|
-
|
19
|
+
headers = Rack::ContentType.new(app).call({})[1]
|
20
20
|
headers['Content-Type'].should.equal 'foo/bar'
|
21
21
|
end
|
22
22
|
|
23
23
|
should "detect Content-Type case insensitive" do
|
24
24
|
app = lambda { |env| [200, {'CONTENT-Type' => 'foo/bar'}, "Hello, World!"] }
|
25
|
-
|
25
|
+
headers = Rack::ContentType.new(app).call({})[1]
|
26
26
|
headers.to_a.select { |k,v| k.downcase == "content-type" }.
|
27
27
|
should.equal [["CONTENT-Type","foo/bar"]]
|
28
28
|
end
|
data/test/spec_deflater.rb
CHANGED
@@ -2,6 +2,7 @@ require 'stringio'
|
|
2
2
|
require 'time' # for Time#httpdate
|
3
3
|
require 'rack/deflater'
|
4
4
|
require 'rack/mock'
|
5
|
+
require 'zlib'
|
5
6
|
|
6
7
|
describe Rack::Deflater do
|
7
8
|
def build_response(status, body, accept_encoding, headers = {})
|
@@ -13,6 +14,11 @@ describe Rack::Deflater do
|
|
13
14
|
return response
|
14
15
|
end
|
15
16
|
|
17
|
+
def inflate(buf)
|
18
|
+
inflater = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
19
|
+
inflater.inflate(buf) << inflater.finish
|
20
|
+
end
|
21
|
+
|
16
22
|
should "be able to deflate bodies that respond to each" do
|
17
23
|
body = Object.new
|
18
24
|
class << body; def each; yield("foo"); yield("bar"); end; end
|
@@ -26,7 +32,26 @@ describe Rack::Deflater do
|
|
26
32
|
})
|
27
33
|
buf = ''
|
28
34
|
response[2].each { |part| buf << part }
|
29
|
-
buf.should.equal("
|
35
|
+
inflate(buf).should.equal("foobar")
|
36
|
+
end
|
37
|
+
|
38
|
+
should "flush deflated chunks to the client as they become ready" do
|
39
|
+
body = Object.new
|
40
|
+
class << body; def each; yield("foo"); yield("bar"); end; end
|
41
|
+
|
42
|
+
response = build_response(200, body, "deflate")
|
43
|
+
|
44
|
+
response[0].should.equal(200)
|
45
|
+
response[1].should.equal({
|
46
|
+
"Content-Encoding" => "deflate",
|
47
|
+
"Vary" => "Accept-Encoding"
|
48
|
+
})
|
49
|
+
buf = []
|
50
|
+
inflater = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
51
|
+
response[2].each { |part| buf << inflater.inflate(part) }
|
52
|
+
buf << inflater.finish
|
53
|
+
buf.delete_if { |part| part.empty? }
|
54
|
+
buf.should.equal(%w(foo bar))
|
30
55
|
end
|
31
56
|
|
32
57
|
# TODO: This is really just a special case of the above...
|
@@ -40,7 +65,7 @@ describe Rack::Deflater do
|
|
40
65
|
})
|
41
66
|
buf = ''
|
42
67
|
response[2].each { |part| buf << part }
|
43
|
-
buf.should.equal("
|
68
|
+
inflate(buf).should.equal("Hello world!")
|
44
69
|
end
|
45
70
|
|
46
71
|
should "be able to gzip bodies that respond to each" do
|
@@ -63,6 +88,25 @@ describe Rack::Deflater do
|
|
63
88
|
gz.close
|
64
89
|
end
|
65
90
|
|
91
|
+
should "flush gzipped chunks to the client as they become ready" do
|
92
|
+
body = Object.new
|
93
|
+
class << body; def each; yield("foo"); yield("bar"); end; end
|
94
|
+
|
95
|
+
response = build_response(200, body, "gzip")
|
96
|
+
|
97
|
+
response[0].should.equal(200)
|
98
|
+
response[1].should.equal({
|
99
|
+
"Content-Encoding" => "gzip",
|
100
|
+
"Vary" => "Accept-Encoding"
|
101
|
+
})
|
102
|
+
buf = []
|
103
|
+
inflater = Zlib::Inflate.new(Zlib::MAX_WBITS + 32)
|
104
|
+
response[2].each { |part| buf << inflater.inflate(part) }
|
105
|
+
buf << inflater.finish
|
106
|
+
buf.delete_if { |part| part.empty? }
|
107
|
+
buf.should.equal(%w(foo bar))
|
108
|
+
end
|
109
|
+
|
66
110
|
should "be able to fallback to no deflation" do
|
67
111
|
response = build_response(200, "Hello world!", "superzip")
|
68
112
|
|
data/test/spec_etag.rb
CHANGED
@@ -1,15 +1,82 @@
|
|
1
1
|
require 'rack/etag'
|
2
|
+
require 'time'
|
2
3
|
|
3
4
|
describe Rack::ETag do
|
4
|
-
|
5
|
+
def sendfile_body
|
6
|
+
res = ['Hello World']
|
7
|
+
def res.to_path ; "/tmp/hello.txt" ; end
|
8
|
+
res
|
9
|
+
end
|
10
|
+
|
11
|
+
should "set ETag if none is set if status is 200" do
|
5
12
|
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
|
6
13
|
response = Rack::ETag.new(app).call({})
|
7
14
|
response[1]['ETag'].should.equal "\"65a8e27d8879283831b664bd8b7f0ad4\""
|
8
15
|
end
|
9
16
|
|
17
|
+
should "set ETag if none is set if status is 201" do
|
18
|
+
app = lambda { |env| [201, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
|
19
|
+
response = Rack::ETag.new(app).call({})
|
20
|
+
response[1]['ETag'].should.equal "\"65a8e27d8879283831b664bd8b7f0ad4\""
|
21
|
+
end
|
22
|
+
|
23
|
+
should "set Cache-Control to 'max-age=0, private, must-revalidate' (default) if none is set" do
|
24
|
+
app = lambda { |env| [201, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
|
25
|
+
response = Rack::ETag.new(app).call({})
|
26
|
+
response[1]['Cache-Control'].should.equal 'max-age=0, private, must-revalidate'
|
27
|
+
end
|
28
|
+
|
29
|
+
should "set Cache-Control to chosen one if none is set" do
|
30
|
+
app = lambda { |env| [201, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
|
31
|
+
response = Rack::ETag.new(app, nil, 'public').call({})
|
32
|
+
response[1]['Cache-Control'].should.equal 'public'
|
33
|
+
end
|
34
|
+
|
35
|
+
should "set a given Cache-Control even if digest could not be calculated" do
|
36
|
+
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, []] }
|
37
|
+
response = Rack::ETag.new(app, 'no-cache').call({})
|
38
|
+
response[1]['Cache-Control'].should.equal 'no-cache'
|
39
|
+
end
|
40
|
+
|
41
|
+
should "not set Cache-Control if it is already set" do
|
42
|
+
app = lambda { |env| [201, {'Content-Type' => 'text/plain', 'Cache-Control' => 'public'}, ["Hello, World!"]] }
|
43
|
+
response = Rack::ETag.new(app).call({})
|
44
|
+
response[1]['Cache-Control'].should.equal 'public'
|
45
|
+
end
|
46
|
+
|
10
47
|
should "not change ETag if it is already set" do
|
11
48
|
app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'ETag' => '"abc"'}, ["Hello, World!"]] }
|
12
49
|
response = Rack::ETag.new(app).call({})
|
13
50
|
response[1]['ETag'].should.equal "\"abc\""
|
14
51
|
end
|
52
|
+
|
53
|
+
should "not set ETag if body is empty" do
|
54
|
+
app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Last-Modified' => Time.now.httpdate}, []] }
|
55
|
+
response = Rack::ETag.new(app).call({})
|
56
|
+
response[1]['ETag'].should.be.nil
|
57
|
+
end
|
58
|
+
|
59
|
+
should "not set ETag if Last-Modified is set" do
|
60
|
+
app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Last-Modified' => Time.now.httpdate}, ["Hello, World!"]] }
|
61
|
+
response = Rack::ETag.new(app).call({})
|
62
|
+
response[1]['ETag'].should.be.nil
|
63
|
+
end
|
64
|
+
|
65
|
+
should "not set ETag if a sendfile_body is given" do
|
66
|
+
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, sendfile_body] }
|
67
|
+
response = Rack::ETag.new(app).call({})
|
68
|
+
response[1]['ETag'].should.be.nil
|
69
|
+
end
|
70
|
+
|
71
|
+
should "not set ETag if a status is not 200 or 201" do
|
72
|
+
app = lambda { |env| [401, {'Content-Type' => 'text/plain'}, ['Access denied.']] }
|
73
|
+
response = Rack::ETag.new(app).call({})
|
74
|
+
response[1]['ETag'].should.be.nil
|
75
|
+
end
|
76
|
+
|
77
|
+
should "not set ETag if no-cache is given" do
|
78
|
+
app = lambda { |env| [200, {'Cache-Control' => 'no-cache'}, ['Hello, World!']] }
|
79
|
+
response = Rack::ETag.new(app).call({})
|
80
|
+
response[1]['ETag'].should.be.nil
|
81
|
+
end
|
15
82
|
end
|