edgar-rack 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. data/COPYING +18 -0
  2. data/KNOWN-ISSUES +21 -0
  3. data/README +401 -0
  4. data/Rakefile +101 -0
  5. data/SPEC +171 -0
  6. data/bin/rackup +4 -0
  7. data/contrib/rack_logo.svg +111 -0
  8. data/example/lobster.ru +4 -0
  9. data/example/protectedlobster.rb +14 -0
  10. data/example/protectedlobster.ru +8 -0
  11. data/lib/rack.rb +81 -0
  12. data/lib/rack/auth/abstract/handler.rb +37 -0
  13. data/lib/rack/auth/abstract/request.rb +43 -0
  14. data/lib/rack/auth/basic.rb +58 -0
  15. data/lib/rack/auth/digest/md5.rb +124 -0
  16. data/lib/rack/auth/digest/nonce.rb +51 -0
  17. data/lib/rack/auth/digest/params.rb +53 -0
  18. data/lib/rack/auth/digest/request.rb +40 -0
  19. data/lib/rack/builder.rb +80 -0
  20. data/lib/rack/cascade.rb +41 -0
  21. data/lib/rack/chunked.rb +52 -0
  22. data/lib/rack/commonlogger.rb +49 -0
  23. data/lib/rack/conditionalget.rb +63 -0
  24. data/lib/rack/config.rb +15 -0
  25. data/lib/rack/content_length.rb +29 -0
  26. data/lib/rack/content_type.rb +23 -0
  27. data/lib/rack/deflater.rb +96 -0
  28. data/lib/rack/directory.rb +157 -0
  29. data/lib/rack/etag.rb +59 -0
  30. data/lib/rack/file.rb +118 -0
  31. data/lib/rack/handler.rb +88 -0
  32. data/lib/rack/handler/cgi.rb +61 -0
  33. data/lib/rack/handler/evented_mongrel.rb +8 -0
  34. data/lib/rack/handler/fastcgi.rb +90 -0
  35. data/lib/rack/handler/lsws.rb +61 -0
  36. data/lib/rack/handler/mongrel.rb +90 -0
  37. data/lib/rack/handler/scgi.rb +59 -0
  38. data/lib/rack/handler/swiftiplied_mongrel.rb +8 -0
  39. data/lib/rack/handler/thin.rb +17 -0
  40. data/lib/rack/handler/webrick.rb +73 -0
  41. data/lib/rack/head.rb +19 -0
  42. data/lib/rack/lint.rb +567 -0
  43. data/lib/rack/lobster.rb +65 -0
  44. data/lib/rack/lock.rb +44 -0
  45. data/lib/rack/logger.rb +18 -0
  46. data/lib/rack/methodoverride.rb +27 -0
  47. data/lib/rack/mime.rb +210 -0
  48. data/lib/rack/mock.rb +185 -0
  49. data/lib/rack/nulllogger.rb +18 -0
  50. data/lib/rack/recursive.rb +61 -0
  51. data/lib/rack/reloader.rb +109 -0
  52. data/lib/rack/request.rb +307 -0
  53. data/lib/rack/response.rb +151 -0
  54. data/lib/rack/rewindable_input.rb +104 -0
  55. data/lib/rack/runtime.rb +27 -0
  56. data/lib/rack/sendfile.rb +139 -0
  57. data/lib/rack/server.rb +289 -0
  58. data/lib/rack/session/abstract/id.rb +348 -0
  59. data/lib/rack/session/cookie.rb +152 -0
  60. data/lib/rack/session/memcache.rb +93 -0
  61. data/lib/rack/session/pool.rb +79 -0
  62. data/lib/rack/showexceptions.rb +378 -0
  63. data/lib/rack/showstatus.rb +113 -0
  64. data/lib/rack/static.rb +53 -0
  65. data/lib/rack/urlmap.rb +55 -0
  66. data/lib/rack/utils.rb +698 -0
  67. data/rack.gemspec +39 -0
  68. data/test/cgi/lighttpd.conf +25 -0
  69. data/test/cgi/rackup_stub.rb +6 -0
  70. data/test/cgi/sample_rackup.ru +5 -0
  71. data/test/cgi/test +9 -0
  72. data/test/cgi/test.fcgi +8 -0
  73. data/test/cgi/test.ru +5 -0
  74. data/test/gemloader.rb +6 -0
  75. data/test/multipart/bad_robots +259 -0
  76. data/test/multipart/binary +0 -0
  77. data/test/multipart/empty +10 -0
  78. data/test/multipart/fail_16384_nofile +814 -0
  79. data/test/multipart/file1.txt +1 -0
  80. data/test/multipart/filename_and_modification_param +7 -0
  81. data/test/multipart/filename_with_escaped_quotes +6 -0
  82. data/test/multipart/filename_with_escaped_quotes_and_modification_param +7 -0
  83. data/test/multipart/filename_with_percent_escaped_quotes +6 -0
  84. data/test/multipart/filename_with_unescaped_quotes +6 -0
  85. data/test/multipart/ie +6 -0
  86. data/test/multipart/nested +10 -0
  87. data/test/multipart/none +9 -0
  88. data/test/multipart/semicolon +6 -0
  89. data/test/multipart/text +15 -0
  90. data/test/rackup/config.ru +31 -0
  91. data/test/spec_auth_basic.rb +70 -0
  92. data/test/spec_auth_digest.rb +241 -0
  93. data/test/spec_builder.rb +123 -0
  94. data/test/spec_cascade.rb +45 -0
  95. data/test/spec_cgi.rb +102 -0
  96. data/test/spec_chunked.rb +60 -0
  97. data/test/spec_commonlogger.rb +56 -0
  98. data/test/spec_conditionalget.rb +86 -0
  99. data/test/spec_config.rb +23 -0
  100. data/test/spec_content_length.rb +36 -0
  101. data/test/spec_content_type.rb +29 -0
  102. data/test/spec_deflater.rb +125 -0
  103. data/test/spec_directory.rb +57 -0
  104. data/test/spec_etag.rb +75 -0
  105. data/test/spec_fastcgi.rb +107 -0
  106. data/test/spec_file.rb +92 -0
  107. data/test/spec_handler.rb +49 -0
  108. data/test/spec_head.rb +30 -0
  109. data/test/spec_lint.rb +515 -0
  110. data/test/spec_lobster.rb +43 -0
  111. data/test/spec_lock.rb +142 -0
  112. data/test/spec_logger.rb +28 -0
  113. data/test/spec_methodoverride.rb +58 -0
  114. data/test/spec_mock.rb +241 -0
  115. data/test/spec_mongrel.rb +182 -0
  116. data/test/spec_nulllogger.rb +12 -0
  117. data/test/spec_recursive.rb +69 -0
  118. data/test/spec_request.rb +774 -0
  119. data/test/spec_response.rb +245 -0
  120. data/test/spec_rewindable_input.rb +118 -0
  121. data/test/spec_runtime.rb +39 -0
  122. data/test/spec_sendfile.rb +83 -0
  123. data/test/spec_server.rb +8 -0
  124. data/test/spec_session_abstract_id.rb +43 -0
  125. data/test/spec_session_cookie.rb +171 -0
  126. data/test/spec_session_memcache.rb +289 -0
  127. data/test/spec_session_pool.rb +200 -0
  128. data/test/spec_showexceptions.rb +87 -0
  129. data/test/spec_showstatus.rb +79 -0
  130. data/test/spec_static.rb +48 -0
  131. data/test/spec_thin.rb +86 -0
  132. data/test/spec_urlmap.rb +213 -0
  133. data/test/spec_utils.rb +678 -0
  134. data/test/spec_webrick.rb +141 -0
  135. data/test/testrequest.rb +78 -0
  136. data/test/unregistered_handler/rack/handler/unregistered.rb +7 -0
  137. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
  138. 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
@@ -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
@@ -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