edgar-rack 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +18 -0
- data/KNOWN-ISSUES +21 -0
- data/README +401 -0
- data/Rakefile +101 -0
- data/SPEC +171 -0
- data/bin/rackup +4 -0
- data/contrib/rack_logo.svg +111 -0
- data/example/lobster.ru +4 -0
- data/example/protectedlobster.rb +14 -0
- data/example/protectedlobster.ru +8 -0
- data/lib/rack.rb +81 -0
- data/lib/rack/auth/abstract/handler.rb +37 -0
- data/lib/rack/auth/abstract/request.rb +43 -0
- data/lib/rack/auth/basic.rb +58 -0
- data/lib/rack/auth/digest/md5.rb +124 -0
- data/lib/rack/auth/digest/nonce.rb +51 -0
- data/lib/rack/auth/digest/params.rb +53 -0
- data/lib/rack/auth/digest/request.rb +40 -0
- data/lib/rack/builder.rb +80 -0
- data/lib/rack/cascade.rb +41 -0
- data/lib/rack/chunked.rb +52 -0
- data/lib/rack/commonlogger.rb +49 -0
- data/lib/rack/conditionalget.rb +63 -0
- data/lib/rack/config.rb +15 -0
- data/lib/rack/content_length.rb +29 -0
- data/lib/rack/content_type.rb +23 -0
- data/lib/rack/deflater.rb +96 -0
- data/lib/rack/directory.rb +157 -0
- data/lib/rack/etag.rb +59 -0
- data/lib/rack/file.rb +118 -0
- data/lib/rack/handler.rb +88 -0
- data/lib/rack/handler/cgi.rb +61 -0
- data/lib/rack/handler/evented_mongrel.rb +8 -0
- data/lib/rack/handler/fastcgi.rb +90 -0
- data/lib/rack/handler/lsws.rb +61 -0
- data/lib/rack/handler/mongrel.rb +90 -0
- data/lib/rack/handler/scgi.rb +59 -0
- data/lib/rack/handler/swiftiplied_mongrel.rb +8 -0
- data/lib/rack/handler/thin.rb +17 -0
- data/lib/rack/handler/webrick.rb +73 -0
- data/lib/rack/head.rb +19 -0
- data/lib/rack/lint.rb +567 -0
- data/lib/rack/lobster.rb +65 -0
- data/lib/rack/lock.rb +44 -0
- data/lib/rack/logger.rb +18 -0
- data/lib/rack/methodoverride.rb +27 -0
- data/lib/rack/mime.rb +210 -0
- data/lib/rack/mock.rb +185 -0
- data/lib/rack/nulllogger.rb +18 -0
- data/lib/rack/recursive.rb +61 -0
- data/lib/rack/reloader.rb +109 -0
- data/lib/rack/request.rb +307 -0
- data/lib/rack/response.rb +151 -0
- data/lib/rack/rewindable_input.rb +104 -0
- data/lib/rack/runtime.rb +27 -0
- data/lib/rack/sendfile.rb +139 -0
- data/lib/rack/server.rb +289 -0
- data/lib/rack/session/abstract/id.rb +348 -0
- data/lib/rack/session/cookie.rb +152 -0
- data/lib/rack/session/memcache.rb +93 -0
- data/lib/rack/session/pool.rb +79 -0
- data/lib/rack/showexceptions.rb +378 -0
- data/lib/rack/showstatus.rb +113 -0
- data/lib/rack/static.rb +53 -0
- data/lib/rack/urlmap.rb +55 -0
- data/lib/rack/utils.rb +698 -0
- data/rack.gemspec +39 -0
- data/test/cgi/lighttpd.conf +25 -0
- data/test/cgi/rackup_stub.rb +6 -0
- data/test/cgi/sample_rackup.ru +5 -0
- data/test/cgi/test +9 -0
- data/test/cgi/test.fcgi +8 -0
- data/test/cgi/test.ru +5 -0
- data/test/gemloader.rb +6 -0
- data/test/multipart/bad_robots +259 -0
- data/test/multipart/binary +0 -0
- data/test/multipart/empty +10 -0
- data/test/multipart/fail_16384_nofile +814 -0
- data/test/multipart/file1.txt +1 -0
- data/test/multipart/filename_and_modification_param +7 -0
- data/test/multipart/filename_with_escaped_quotes +6 -0
- data/test/multipart/filename_with_escaped_quotes_and_modification_param +7 -0
- data/test/multipart/filename_with_percent_escaped_quotes +6 -0
- data/test/multipart/filename_with_unescaped_quotes +6 -0
- data/test/multipart/ie +6 -0
- data/test/multipart/nested +10 -0
- data/test/multipart/none +9 -0
- data/test/multipart/semicolon +6 -0
- data/test/multipart/text +15 -0
- data/test/rackup/config.ru +31 -0
- data/test/spec_auth_basic.rb +70 -0
- data/test/spec_auth_digest.rb +241 -0
- data/test/spec_builder.rb +123 -0
- data/test/spec_cascade.rb +45 -0
- data/test/spec_cgi.rb +102 -0
- data/test/spec_chunked.rb +60 -0
- data/test/spec_commonlogger.rb +56 -0
- data/test/spec_conditionalget.rb +86 -0
- data/test/spec_config.rb +23 -0
- data/test/spec_content_length.rb +36 -0
- data/test/spec_content_type.rb +29 -0
- data/test/spec_deflater.rb +125 -0
- data/test/spec_directory.rb +57 -0
- data/test/spec_etag.rb +75 -0
- data/test/spec_fastcgi.rb +107 -0
- data/test/spec_file.rb +92 -0
- data/test/spec_handler.rb +49 -0
- data/test/spec_head.rb +30 -0
- data/test/spec_lint.rb +515 -0
- data/test/spec_lobster.rb +43 -0
- data/test/spec_lock.rb +142 -0
- data/test/spec_logger.rb +28 -0
- data/test/spec_methodoverride.rb +58 -0
- data/test/spec_mock.rb +241 -0
- data/test/spec_mongrel.rb +182 -0
- data/test/spec_nulllogger.rb +12 -0
- data/test/spec_recursive.rb +69 -0
- data/test/spec_request.rb +774 -0
- data/test/spec_response.rb +245 -0
- data/test/spec_rewindable_input.rb +118 -0
- data/test/spec_runtime.rb +39 -0
- data/test/spec_sendfile.rb +83 -0
- data/test/spec_server.rb +8 -0
- data/test/spec_session_abstract_id.rb +43 -0
- data/test/spec_session_cookie.rb +171 -0
- data/test/spec_session_memcache.rb +289 -0
- data/test/spec_session_pool.rb +200 -0
- data/test/spec_showexceptions.rb +87 -0
- data/test/spec_showstatus.rb +79 -0
- data/test/spec_static.rb +48 -0
- data/test/spec_thin.rb +86 -0
- data/test/spec_urlmap.rb +213 -0
- data/test/spec_utils.rb +678 -0
- data/test/spec_webrick.rb +141 -0
- data/test/testrequest.rb +78 -0
- data/test/unregistered_handler/rack/handler/unregistered.rb +7 -0
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
- metadata +329 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rack/cascade'
|
2
|
+
require 'rack/file'
|
3
|
+
require 'rack/urlmap'
|
4
|
+
require 'rack/mock'
|
5
|
+
|
6
|
+
describe Rack::Cascade do
|
7
|
+
docroot = File.expand_path(File.dirname(__FILE__))
|
8
|
+
app1 = Rack::File.new(docroot)
|
9
|
+
|
10
|
+
app2 = Rack::URLMap.new("/crash" => lambda { |env| raise "boom" })
|
11
|
+
|
12
|
+
app3 = Rack::URLMap.new("/foo" => lambda { |env|
|
13
|
+
[200, { "Content-Type" => "text/plain"}, [""]]})
|
14
|
+
|
15
|
+
should "dispatch onward on 404 by default" do
|
16
|
+
cascade = Rack::Cascade.new([app1, app2, app3])
|
17
|
+
Rack::MockRequest.new(cascade).get("/cgi/test").should.be.ok
|
18
|
+
Rack::MockRequest.new(cascade).get("/foo").should.be.ok
|
19
|
+
Rack::MockRequest.new(cascade).get("/toobad").should.be.not_found
|
20
|
+
Rack::MockRequest.new(cascade).get("/cgi/../bla").should.be.forbidden
|
21
|
+
end
|
22
|
+
|
23
|
+
should "dispatch onward on whatever is passed" do
|
24
|
+
cascade = Rack::Cascade.new([app1, app2, app3], [404, 403])
|
25
|
+
Rack::MockRequest.new(cascade).get("/cgi/../bla").should.be.not_found
|
26
|
+
end
|
27
|
+
|
28
|
+
should "return 404 if empty" do
|
29
|
+
Rack::MockRequest.new(Rack::Cascade.new([])).get('/').should.be.not_found
|
30
|
+
end
|
31
|
+
|
32
|
+
should "append new app" do
|
33
|
+
cascade = Rack::Cascade.new([], [404, 403])
|
34
|
+
Rack::MockRequest.new(cascade).get('/').should.be.not_found
|
35
|
+
cascade << app2
|
36
|
+
Rack::MockRequest.new(cascade).get('/cgi/test').should.be.not_found
|
37
|
+
Rack::MockRequest.new(cascade).get('/cgi/../bla').should.be.not_found
|
38
|
+
cascade << app1
|
39
|
+
Rack::MockRequest.new(cascade).get('/cgi/test').should.be.ok
|
40
|
+
Rack::MockRequest.new(cascade).get('/cgi/../bla').should.be.forbidden
|
41
|
+
Rack::MockRequest.new(cascade).get('/foo').should.be.not_found
|
42
|
+
cascade << app3
|
43
|
+
Rack::MockRequest.new(cascade).get('/foo').should.be.ok
|
44
|
+
end
|
45
|
+
end
|
data/test/spec_cgi.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
begin
|
2
|
+
require File.expand_path('../testrequest', __FILE__)
|
3
|
+
require 'rack/handler/cgi'
|
4
|
+
|
5
|
+
describe Rack::Handler::CGI do
|
6
|
+
extend TestRequest::Helpers
|
7
|
+
|
8
|
+
@host = '0.0.0.0'
|
9
|
+
@port = 9203
|
10
|
+
|
11
|
+
if `which lighttpd` && !$?.success?
|
12
|
+
raise "lighttpd not found"
|
13
|
+
end
|
14
|
+
|
15
|
+
# Keep this first.
|
16
|
+
$pid = fork {
|
17
|
+
ENV['RACK_ENV'] = 'deployment'
|
18
|
+
ENV['RUBYLIB'] = [
|
19
|
+
File.expand_path('../../lib', __FILE__),
|
20
|
+
ENV['RUBYLIB'],
|
21
|
+
].compact.join(':')
|
22
|
+
|
23
|
+
Dir.chdir(File.expand_path("../cgi", __FILE__)) do
|
24
|
+
exec "lighttpd -D -f lighttpd.conf"
|
25
|
+
end
|
26
|
+
}
|
27
|
+
|
28
|
+
should "respond" do
|
29
|
+
sleep 1
|
30
|
+
GET("/test")
|
31
|
+
response.should.not.be.nil
|
32
|
+
end
|
33
|
+
|
34
|
+
should "be a lighttpd" do
|
35
|
+
GET("/test")
|
36
|
+
status.should.equal 200
|
37
|
+
response["SERVER_SOFTWARE"].should =~ /lighttpd/
|
38
|
+
response["HTTP_VERSION"].should.equal "HTTP/1.1"
|
39
|
+
response["SERVER_PROTOCOL"].should.equal "HTTP/1.1"
|
40
|
+
response["SERVER_PORT"].should.equal @port.to_s
|
41
|
+
response["SERVER_NAME"].should.equal @host
|
42
|
+
end
|
43
|
+
|
44
|
+
should "have rack headers" do
|
45
|
+
GET("/test")
|
46
|
+
response["rack.version"].should.equal([1,1])
|
47
|
+
response["rack.multithread"].should.be.false
|
48
|
+
response["rack.multiprocess"].should.be.true
|
49
|
+
response["rack.run_once"].should.be.true
|
50
|
+
end
|
51
|
+
|
52
|
+
should "have CGI headers on GET" do
|
53
|
+
GET("/test")
|
54
|
+
response["REQUEST_METHOD"].should.equal "GET"
|
55
|
+
response["SCRIPT_NAME"].should.equal "/test"
|
56
|
+
response["REQUEST_PATH"].should.equal "/"
|
57
|
+
response["PATH_INFO"].should.be.nil
|
58
|
+
response["QUERY_STRING"].should.equal ""
|
59
|
+
response["test.postdata"].should.equal ""
|
60
|
+
|
61
|
+
GET("/test/foo?quux=1")
|
62
|
+
response["REQUEST_METHOD"].should.equal "GET"
|
63
|
+
response["SCRIPT_NAME"].should.equal "/test"
|
64
|
+
response["REQUEST_PATH"].should.equal "/"
|
65
|
+
response["PATH_INFO"].should.equal "/foo"
|
66
|
+
response["QUERY_STRING"].should.equal "quux=1"
|
67
|
+
end
|
68
|
+
|
69
|
+
should "have CGI headers on POST" do
|
70
|
+
POST("/test", {"rack-form-data" => "23"}, {'X-test-header' => '42'})
|
71
|
+
status.should.equal 200
|
72
|
+
response["REQUEST_METHOD"].should.equal "POST"
|
73
|
+
response["SCRIPT_NAME"].should.equal "/test"
|
74
|
+
response["REQUEST_PATH"].should.equal "/"
|
75
|
+
response["QUERY_STRING"].should.equal ""
|
76
|
+
response["HTTP_X_TEST_HEADER"].should.equal "42"
|
77
|
+
response["test.postdata"].should.equal "rack-form-data=23"
|
78
|
+
end
|
79
|
+
|
80
|
+
should "support HTTP auth" do
|
81
|
+
GET("/test", {:user => "ruth", :passwd => "secret"})
|
82
|
+
response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ="
|
83
|
+
end
|
84
|
+
|
85
|
+
should "set status" do
|
86
|
+
GET("/test?secret")
|
87
|
+
status.should.equal 403
|
88
|
+
response["rack.url_scheme"].should.equal "http"
|
89
|
+
end
|
90
|
+
|
91
|
+
# Keep this last.
|
92
|
+
should "shutdown" do
|
93
|
+
Process.kill 15, $pid
|
94
|
+
Process.wait($pid).should == $pid
|
95
|
+
end
|
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
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'rack/chunked'
|
2
|
+
require 'rack/mock'
|
3
|
+
|
4
|
+
describe Rack::Chunked do
|
5
|
+
before do
|
6
|
+
@env = Rack::MockRequest.
|
7
|
+
env_for('/', 'HTTP_VERSION' => '1.1', 'REQUEST_METHOD' => 'GET')
|
8
|
+
end
|
9
|
+
|
10
|
+
should 'chunk responses with no Content-Length' do
|
11
|
+
app = lambda { |env| [200, {}, ['Hello', ' ', 'World!']] }
|
12
|
+
response = Rack::MockResponse.new(*Rack::Chunked.new(app).call(@env))
|
13
|
+
response.headers.should.not.include 'Content-Length'
|
14
|
+
response.headers['Transfer-Encoding'].should.equal 'chunked'
|
15
|
+
response.body.should.equal "5\r\nHello\r\n1\r\n \r\n6\r\nWorld!\r\n0\r\n\r\n"
|
16
|
+
end
|
17
|
+
|
18
|
+
should 'chunks empty bodies properly' do
|
19
|
+
app = lambda { |env| [200, {}, []] }
|
20
|
+
response = Rack::MockResponse.new(*Rack::Chunked.new(app).call(@env))
|
21
|
+
response.headers.should.not.include 'Content-Length'
|
22
|
+
response.headers['Transfer-Encoding'].should.equal 'chunked'
|
23
|
+
response.body.should.equal "0\r\n\r\n"
|
24
|
+
end
|
25
|
+
|
26
|
+
should 'not modify response when Content-Length header present' do
|
27
|
+
app = lambda { |env| [200, {'Content-Length'=>'12'}, ['Hello', ' ', 'World!']] }
|
28
|
+
status, headers, body = Rack::Chunked.new(app).call(@env)
|
29
|
+
status.should.equal 200
|
30
|
+
headers.should.not.include 'Transfer-Encoding'
|
31
|
+
headers.should.include 'Content-Length'
|
32
|
+
body.join.should.equal 'Hello World!'
|
33
|
+
end
|
34
|
+
|
35
|
+
should 'not modify response when client is HTTP/1.0' do
|
36
|
+
app = lambda { |env| [200, {}, ['Hello', ' ', 'World!']] }
|
37
|
+
@env['HTTP_VERSION'] = 'HTTP/1.0'
|
38
|
+
status, headers, body = Rack::Chunked.new(app).call(@env)
|
39
|
+
status.should.equal 200
|
40
|
+
headers.should.not.include 'Transfer-Encoding'
|
41
|
+
body.join.should.equal 'Hello World!'
|
42
|
+
end
|
43
|
+
|
44
|
+
should 'not modify response when Transfer-Encoding header already present' do
|
45
|
+
app = lambda { |env| [200, {'Transfer-Encoding' => 'identity'}, ['Hello', ' ', 'World!']] }
|
46
|
+
status, headers, body = Rack::Chunked.new(app).call(@env)
|
47
|
+
status.should.equal 200
|
48
|
+
headers['Transfer-Encoding'].should.equal 'identity'
|
49
|
+
body.join.should.equal 'Hello World!'
|
50
|
+
end
|
51
|
+
|
52
|
+
[100, 204, 304].each do |status_code|
|
53
|
+
should "not modify response when status code is #{status_code}" do
|
54
|
+
app = lambda { |env| [status_code, {}, []] }
|
55
|
+
status, headers, _ = Rack::Chunked.new(app).call(@env)
|
56
|
+
status.should.equal status_code
|
57
|
+
headers.should.not.include 'Transfer-Encoding'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rack/commonlogger'
|
2
|
+
require 'rack/mock'
|
3
|
+
|
4
|
+
describe Rack::CommonLogger do
|
5
|
+
obj = 'foobar'
|
6
|
+
length = obj.size
|
7
|
+
|
8
|
+
app = lambda { |env|
|
9
|
+
[200,
|
10
|
+
{"Content-Type" => "text/html", "Content-Length" => length.to_s},
|
11
|
+
[obj]]}
|
12
|
+
app_without_length = lambda { |env|
|
13
|
+
[200,
|
14
|
+
{"Content-Type" => "text/html"},
|
15
|
+
[]]}
|
16
|
+
app_with_zero_length = lambda { |env|
|
17
|
+
[200,
|
18
|
+
{"Content-Type" => "text/html", "Content-Length" => "0"},
|
19
|
+
[]]}
|
20
|
+
|
21
|
+
should "log to rack.errors by default" do
|
22
|
+
res = Rack::MockRequest.new(Rack::CommonLogger.new(app)).get("/")
|
23
|
+
|
24
|
+
res.errors.should.not.be.empty
|
25
|
+
res.errors.should =~ /"GET \/ " 200 #{length} /
|
26
|
+
end
|
27
|
+
|
28
|
+
should "log to anything with +write+" do
|
29
|
+
log = StringIO.new
|
30
|
+
Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/")
|
31
|
+
|
32
|
+
log.string.should =~ /"GET \/ " 200 #{length} /
|
33
|
+
end
|
34
|
+
|
35
|
+
should "log - content length if header is missing" do
|
36
|
+
res = Rack::MockRequest.new(Rack::CommonLogger.new(app_without_length)).get("/")
|
37
|
+
|
38
|
+
res.errors.should.not.be.empty
|
39
|
+
res.errors.should =~ /"GET \/ " 200 - /
|
40
|
+
end
|
41
|
+
|
42
|
+
should "log - content length if header is zero" do
|
43
|
+
res = Rack::MockRequest.new(Rack::CommonLogger.new(app_with_zero_length)).get("/")
|
44
|
+
|
45
|
+
res.errors.should.not.be.empty
|
46
|
+
res.errors.should =~ /"GET \/ " 200 - /
|
47
|
+
end
|
48
|
+
|
49
|
+
def length
|
50
|
+
123
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.obj
|
54
|
+
"hello world"
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'rack/conditionalget'
|
3
|
+
require 'rack/mock'
|
4
|
+
|
5
|
+
describe Rack::ConditionalGet do
|
6
|
+
should "set a 304 status and truncate body when If-Modified-Since hits" do
|
7
|
+
timestamp = Time.now.httpdate
|
8
|
+
app = Rack::ConditionalGet.new(lambda { |env|
|
9
|
+
[200, {'Last-Modified'=>timestamp}, ['TEST']] })
|
10
|
+
|
11
|
+
response = Rack::MockRequest.new(app).
|
12
|
+
get("/", 'HTTP_IF_MODIFIED_SINCE' => timestamp)
|
13
|
+
|
14
|
+
response.status.should.equal 304
|
15
|
+
response.body.should.be.empty
|
16
|
+
end
|
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
|
+
|
29
|
+
should "set a 304 status and truncate body when If-None-Match hits" do
|
30
|
+
app = Rack::ConditionalGet.new(lambda { |env|
|
31
|
+
[200, {'Etag'=>'1234'}, ['TEST']] })
|
32
|
+
|
33
|
+
response = Rack::MockRequest.new(app).
|
34
|
+
get("/", 'HTTP_IF_NONE_MATCH' => '1234')
|
35
|
+
|
36
|
+
response.status.should.equal 304
|
37
|
+
response.body.should.be.empty
|
38
|
+
end
|
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
|
+
|
64
|
+
should "not affect non-GET/HEAD requests" do
|
65
|
+
app = Rack::ConditionalGet.new(lambda { |env|
|
66
|
+
[200, {'Etag'=>'1234'}, ['TEST']] })
|
67
|
+
|
68
|
+
response = Rack::MockRequest.new(app).
|
69
|
+
post("/", 'HTTP_IF_NONE_MATCH' => '1234')
|
70
|
+
|
71
|
+
response.status.should.equal 200
|
72
|
+
response.body.should.equal 'TEST'
|
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
|
+
|
86
|
+
end
|
data/test/spec_config.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rack/builder'
|
2
|
+
require 'rack/config'
|
3
|
+
require 'rack/content_length'
|
4
|
+
require 'rack/lint'
|
5
|
+
require 'rack/mock'
|
6
|
+
|
7
|
+
describe Rack::Config do
|
8
|
+
should "accept a block that modifies the environment" do
|
9
|
+
app = Rack::Builder.new do
|
10
|
+
use Rack::Lint
|
11
|
+
use Rack::ContentLength
|
12
|
+
use Rack::Config do |env|
|
13
|
+
env['greeting'] = 'hello'
|
14
|
+
end
|
15
|
+
run lambda { |env|
|
16
|
+
[200, {'Content-Type' => 'text/plain'}, [env['greeting'] || '']]
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
response = Rack::MockRequest.new(app).get('/')
|
21
|
+
response.body.should.equal('hello')
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rack/content_length'
|
2
|
+
|
3
|
+
describe Rack::ContentLength do
|
4
|
+
should "set Content-Length on Array 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
|
+
should "not set Content-Length on variable length bodies" do
|
11
|
+
body = lambda { "Hello World!" }
|
12
|
+
def body.each ; yield call ; end
|
13
|
+
|
14
|
+
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, body] }
|
15
|
+
response = Rack::ContentLength.new(app).call({})
|
16
|
+
response[1]['Content-Length'].should.be.nil
|
17
|
+
end
|
18
|
+
|
19
|
+
should "not change Content-Length if it is already set" do
|
20
|
+
app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Content-Length' => '1'}, "Hello, World!"] }
|
21
|
+
response = Rack::ContentLength.new(app).call({})
|
22
|
+
response[1]['Content-Length'].should.equal '1'
|
23
|
+
end
|
24
|
+
|
25
|
+
should "not set Content-Length on 304 responses" do
|
26
|
+
app = lambda { |env| [304, {'Content-Type' => 'text/plain'}, []] }
|
27
|
+
response = Rack::ContentLength.new(app).call({})
|
28
|
+
response[1]['Content-Length'].should.equal nil
|
29
|
+
end
|
30
|
+
|
31
|
+
should "not set Content-Length when Transfer-Encoding is chunked" do
|
32
|
+
app = lambda { |env| [200, {'Transfer-Encoding' => 'chunked'}, []] }
|
33
|
+
response = Rack::ContentLength.new(app).call({})
|
34
|
+
response[1]['Content-Length'].should.equal nil
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'rack/content_type'
|
2
|
+
|
3
|
+
describe Rack::ContentType do
|
4
|
+
should "set Content-Type to default text/html if none is set" do
|
5
|
+
app = lambda { |env| [200, {}, "Hello, World!"] }
|
6
|
+
headers = Rack::ContentType.new(app).call({})[1]
|
7
|
+
headers['Content-Type'].should.equal 'text/html'
|
8
|
+
end
|
9
|
+
|
10
|
+
should "set Content-Type to chosen default if none is set" do
|
11
|
+
app = lambda { |env| [200, {}, "Hello, World!"] }
|
12
|
+
headers =
|
13
|
+
Rack::ContentType.new(app, 'application/octet-stream').call({})[1]
|
14
|
+
headers['Content-Type'].should.equal 'application/octet-stream'
|
15
|
+
end
|
16
|
+
|
17
|
+
should "not change Content-Type if it is already set" do
|
18
|
+
app = lambda { |env| [200, {'Content-Type' => 'foo/bar'}, "Hello, World!"] }
|
19
|
+
headers = Rack::ContentType.new(app).call({})[1]
|
20
|
+
headers['Content-Type'].should.equal 'foo/bar'
|
21
|
+
end
|
22
|
+
|
23
|
+
should "detect Content-Type case insensitive" do
|
24
|
+
app = lambda { |env| [200, {'CONTENT-Type' => 'foo/bar'}, "Hello, World!"] }
|
25
|
+
headers = Rack::ContentType.new(app).call({})[1]
|
26
|
+
headers.to_a.select { |k,v| k.downcase == "content-type" }.
|
27
|
+
should.equal [["CONTENT-Type","foo/bar"]]
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'time' # for Time#httpdate
|
3
|
+
require 'rack/deflater'
|
4
|
+
require 'rack/mock'
|
5
|
+
|
6
|
+
describe Rack::Deflater do
|
7
|
+
def build_response(status, body, accept_encoding, headers = {})
|
8
|
+
body = [body] if body.respond_to? :to_str
|
9
|
+
app = lambda { |env| [status, {}, body] }
|
10
|
+
request = Rack::MockRequest.env_for("", headers.merge("HTTP_ACCEPT_ENCODING" => accept_encoding))
|
11
|
+
response = Rack::Deflater.new(app).call(request)
|
12
|
+
|
13
|
+
return response
|
14
|
+
end
|
15
|
+
|
16
|
+
should "be able to deflate bodies that respond to each" do
|
17
|
+
body = Object.new
|
18
|
+
class << body; def each; yield("foo"); yield("bar"); end; end
|
19
|
+
|
20
|
+
response = build_response(200, body, "deflate")
|
21
|
+
|
22
|
+
response[0].should.equal(200)
|
23
|
+
response[1].should.equal({
|
24
|
+
"Content-Encoding" => "deflate",
|
25
|
+
"Vary" => "Accept-Encoding"
|
26
|
+
})
|
27
|
+
buf = ''
|
28
|
+
response[2].each { |part| buf << part }
|
29
|
+
buf.should.equal("K\313\317OJ,\002\000")
|
30
|
+
end
|
31
|
+
|
32
|
+
# TODO: This is really just a special case of the above...
|
33
|
+
should "be able to deflate String bodies" do
|
34
|
+
response = build_response(200, "Hello world!", "deflate")
|
35
|
+
|
36
|
+
response[0].should.equal(200)
|
37
|
+
response[1].should.equal({
|
38
|
+
"Content-Encoding" => "deflate",
|
39
|
+
"Vary" => "Accept-Encoding"
|
40
|
+
})
|
41
|
+
buf = ''
|
42
|
+
response[2].each { |part| buf << part }
|
43
|
+
buf.should.equal("\363H\315\311\311W(\317/\312IQ\004\000")
|
44
|
+
end
|
45
|
+
|
46
|
+
should "be able to gzip bodies that respond to each" do
|
47
|
+
body = Object.new
|
48
|
+
class << body; def each; yield("foo"); yield("bar"); end; end
|
49
|
+
|
50
|
+
response = build_response(200, body, "gzip")
|
51
|
+
|
52
|
+
response[0].should.equal(200)
|
53
|
+
response[1].should.equal({
|
54
|
+
"Content-Encoding" => "gzip",
|
55
|
+
"Vary" => "Accept-Encoding",
|
56
|
+
})
|
57
|
+
|
58
|
+
buf = ''
|
59
|
+
response[2].each { |part| buf << part }
|
60
|
+
io = StringIO.new(buf)
|
61
|
+
gz = Zlib::GzipReader.new(io)
|
62
|
+
gz.read.should.equal("foobar")
|
63
|
+
gz.close
|
64
|
+
end
|
65
|
+
|
66
|
+
should "be able to fallback to no deflation" do
|
67
|
+
response = build_response(200, "Hello world!", "superzip")
|
68
|
+
|
69
|
+
response[0].should.equal(200)
|
70
|
+
response[1].should.equal({ "Vary" => "Accept-Encoding" })
|
71
|
+
response[2].should.equal(["Hello world!"])
|
72
|
+
end
|
73
|
+
|
74
|
+
should "be able to skip when there is no response entity body" do
|
75
|
+
response = build_response(304, [], "gzip")
|
76
|
+
|
77
|
+
response[0].should.equal(304)
|
78
|
+
response[1].should.equal({})
|
79
|
+
response[2].should.equal([])
|
80
|
+
end
|
81
|
+
|
82
|
+
should "handle the lack of an acceptable encoding" do
|
83
|
+
response1 = build_response(200, "Hello world!", "identity;q=0", "PATH_INFO" => "/")
|
84
|
+
response1[0].should.equal(406)
|
85
|
+
response1[1].should.equal({"Content-Type" => "text/plain", "Content-Length" => "71"})
|
86
|
+
response1[2].should.equal(["An acceptable encoding for the requested resource / could not be found."])
|
87
|
+
|
88
|
+
response2 = build_response(200, "Hello world!", "identity;q=0", "SCRIPT_NAME" => "/foo", "PATH_INFO" => "/bar")
|
89
|
+
response2[0].should.equal(406)
|
90
|
+
response2[1].should.equal({"Content-Type" => "text/plain", "Content-Length" => "78"})
|
91
|
+
response2[2].should.equal(["An acceptable encoding for the requested resource /foo/bar could not be found."])
|
92
|
+
end
|
93
|
+
|
94
|
+
should "handle gzip response with Last-Modified header" do
|
95
|
+
last_modified = Time.now.httpdate
|
96
|
+
|
97
|
+
app = lambda { |env| [200, { "Last-Modified" => last_modified }, ["Hello World!"]] }
|
98
|
+
request = Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => "gzip")
|
99
|
+
response = Rack::Deflater.new(app).call(request)
|
100
|
+
|
101
|
+
response[0].should.equal(200)
|
102
|
+
response[1].should.equal({
|
103
|
+
"Content-Encoding" => "gzip",
|
104
|
+
"Vary" => "Accept-Encoding",
|
105
|
+
"Last-Modified" => last_modified
|
106
|
+
})
|
107
|
+
|
108
|
+
buf = ''
|
109
|
+
response[2].each { |part| buf << part }
|
110
|
+
io = StringIO.new(buf)
|
111
|
+
gz = Zlib::GzipReader.new(io)
|
112
|
+
gz.read.should.equal("Hello World!")
|
113
|
+
gz.close
|
114
|
+
end
|
115
|
+
|
116
|
+
should "do nothing when no-transform Cache-Control directive present" do
|
117
|
+
app = lambda { |env| [200, {'Cache-Control' => 'no-transform'}, ['Hello World!']] }
|
118
|
+
request = Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => "gzip")
|
119
|
+
response = Rack::Deflater.new(app).call(request)
|
120
|
+
|
121
|
+
response[0].should.equal(200)
|
122
|
+
response[1].should.not.include "Content-Encoding"
|
123
|
+
response[2].join.should.equal("Hello World!")
|
124
|
+
end
|
125
|
+
end
|