eac-rack 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. data/COPYING +18 -0
  2. data/KNOWN-ISSUES +21 -0
  3. data/README +399 -0
  4. data/bin/rackup +4 -0
  5. data/contrib/rack_logo.svg +111 -0
  6. data/example/lobster.ru +4 -0
  7. data/example/protectedlobster.rb +14 -0
  8. data/example/protectedlobster.ru +8 -0
  9. data/lib/rack.rb +92 -0
  10. data/lib/rack/adapter/camping.rb +22 -0
  11. data/lib/rack/auth/abstract/handler.rb +37 -0
  12. data/lib/rack/auth/abstract/request.rb +37 -0
  13. data/lib/rack/auth/basic.rb +58 -0
  14. data/lib/rack/auth/digest/md5.rb +124 -0
  15. data/lib/rack/auth/digest/nonce.rb +51 -0
  16. data/lib/rack/auth/digest/params.rb +55 -0
  17. data/lib/rack/auth/digest/request.rb +40 -0
  18. data/lib/rack/builder.rb +80 -0
  19. data/lib/rack/cascade.rb +41 -0
  20. data/lib/rack/chunked.rb +49 -0
  21. data/lib/rack/commonlogger.rb +49 -0
  22. data/lib/rack/conditionalget.rb +47 -0
  23. data/lib/rack/config.rb +15 -0
  24. data/lib/rack/content_length.rb +29 -0
  25. data/lib/rack/content_type.rb +23 -0
  26. data/lib/rack/deflater.rb +96 -0
  27. data/lib/rack/directory.rb +157 -0
  28. data/lib/rack/etag.rb +23 -0
  29. data/lib/rack/file.rb +90 -0
  30. data/lib/rack/handler.rb +88 -0
  31. data/lib/rack/handler/cgi.rb +61 -0
  32. data/lib/rack/handler/evented_mongrel.rb +8 -0
  33. data/lib/rack/handler/fastcgi.rb +89 -0
  34. data/lib/rack/handler/lsws.rb +63 -0
  35. data/lib/rack/handler/mongrel.rb +90 -0
  36. data/lib/rack/handler/scgi.rb +62 -0
  37. data/lib/rack/handler/swiftiplied_mongrel.rb +8 -0
  38. data/lib/rack/handler/thin.rb +18 -0
  39. data/lib/rack/handler/webrick.rb +69 -0
  40. data/lib/rack/head.rb +19 -0
  41. data/lib/rack/lint.rb +575 -0
  42. data/lib/rack/lobster.rb +65 -0
  43. data/lib/rack/lock.rb +16 -0
  44. data/lib/rack/logger.rb +20 -0
  45. data/lib/rack/methodoverride.rb +27 -0
  46. data/lib/rack/mime.rb +206 -0
  47. data/lib/rack/mock.rb +189 -0
  48. data/lib/rack/nulllogger.rb +18 -0
  49. data/lib/rack/recursive.rb +57 -0
  50. data/lib/rack/reloader.rb +109 -0
  51. data/lib/rack/request.rb +271 -0
  52. data/lib/rack/response.rb +149 -0
  53. data/lib/rack/rewindable_input.rb +100 -0
  54. data/lib/rack/runtime.rb +27 -0
  55. data/lib/rack/sendfile.rb +142 -0
  56. data/lib/rack/server.rb +212 -0
  57. data/lib/rack/session/abstract/id.rb +140 -0
  58. data/lib/rack/session/cookie.rb +90 -0
  59. data/lib/rack/session/memcache.rb +119 -0
  60. data/lib/rack/session/pool.rb +100 -0
  61. data/lib/rack/showexceptions.rb +349 -0
  62. data/lib/rack/showstatus.rb +106 -0
  63. data/lib/rack/static.rb +38 -0
  64. data/lib/rack/urlmap.rb +56 -0
  65. data/lib/rack/utils.rb +614 -0
  66. data/rack.gemspec +38 -0
  67. data/test/spec_rack_auth_basic.rb +73 -0
  68. data/test/spec_rack_auth_digest.rb +226 -0
  69. data/test/spec_rack_builder.rb +84 -0
  70. data/test/spec_rack_camping.rb +51 -0
  71. data/test/spec_rack_cascade.rb +48 -0
  72. data/test/spec_rack_cgi.rb +89 -0
  73. data/test/spec_rack_chunked.rb +62 -0
  74. data/test/spec_rack_commonlogger.rb +61 -0
  75. data/test/spec_rack_conditionalget.rb +41 -0
  76. data/test/spec_rack_config.rb +24 -0
  77. data/test/spec_rack_content_length.rb +43 -0
  78. data/test/spec_rack_content_type.rb +30 -0
  79. data/test/spec_rack_deflater.rb +127 -0
  80. data/test/spec_rack_directory.rb +61 -0
  81. data/test/spec_rack_etag.rb +17 -0
  82. data/test/spec_rack_fastcgi.rb +89 -0
  83. data/test/spec_rack_file.rb +75 -0
  84. data/test/spec_rack_handler.rb +43 -0
  85. data/test/spec_rack_head.rb +30 -0
  86. data/test/spec_rack_lint.rb +528 -0
  87. data/test/spec_rack_lobster.rb +45 -0
  88. data/test/spec_rack_lock.rb +38 -0
  89. data/test/spec_rack_logger.rb +21 -0
  90. data/test/spec_rack_methodoverride.rb +60 -0
  91. data/test/spec_rack_mock.rb +243 -0
  92. data/test/spec_rack_mongrel.rb +189 -0
  93. data/test/spec_rack_nulllogger.rb +13 -0
  94. data/test/spec_rack_recursive.rb +77 -0
  95. data/test/spec_rack_request.rb +545 -0
  96. data/test/spec_rack_response.rb +221 -0
  97. data/test/spec_rack_rewindable_input.rb +118 -0
  98. data/test/spec_rack_runtime.rb +35 -0
  99. data/test/spec_rack_sendfile.rb +86 -0
  100. data/test/spec_rack_session_cookie.rb +73 -0
  101. data/test/spec_rack_session_memcache.rb +273 -0
  102. data/test/spec_rack_session_pool.rb +172 -0
  103. data/test/spec_rack_showexceptions.rb +21 -0
  104. data/test/spec_rack_showstatus.rb +72 -0
  105. data/test/spec_rack_static.rb +37 -0
  106. data/test/spec_rack_thin.rb +91 -0
  107. data/test/spec_rack_urlmap.rb +215 -0
  108. data/test/spec_rack_utils.rb +554 -0
  109. data/test/spec_rack_webrick.rb +130 -0
  110. data/test/spec_rackup.rb +154 -0
  111. metadata +311 -0
@@ -0,0 +1,48 @@
1
+ require 'test/spec'
2
+
3
+ require 'rack/cascade'
4
+ require 'rack/mock'
5
+
6
+ require 'rack/urlmap'
7
+ require 'rack/file'
8
+
9
+ context "Rack::Cascade" do
10
+ docroot = File.expand_path(File.dirname(__FILE__))
11
+ app1 = Rack::File.new(docroot)
12
+
13
+ app2 = Rack::URLMap.new("/crash" => lambda { |env| raise "boom" })
14
+
15
+ app3 = Rack::URLMap.new("/foo" => lambda { |env|
16
+ [200, { "Content-Type" => "text/plain"}, [""]]})
17
+
18
+ specify "should dispatch onward on 404 by default" do
19
+ cascade = Rack::Cascade.new([app1, app2, app3])
20
+ Rack::MockRequest.new(cascade).get("/cgi/test").should.be.ok
21
+ Rack::MockRequest.new(cascade).get("/foo").should.be.ok
22
+ Rack::MockRequest.new(cascade).get("/toobad").should.be.not_found
23
+ Rack::MockRequest.new(cascade).get("/cgi/../bla").should.be.forbidden
24
+ end
25
+
26
+ specify "should dispatch onward on whatever is passed" do
27
+ cascade = Rack::Cascade.new([app1, app2, app3], [404, 403])
28
+ Rack::MockRequest.new(cascade).get("/cgi/../bla").should.be.not_found
29
+ end
30
+
31
+ specify "should return 404 if empty" do
32
+ Rack::MockRequest.new(Rack::Cascade.new([])).get('/').should.be.not_found
33
+ end
34
+
35
+ specify "should append new app" do
36
+ cascade = Rack::Cascade.new([], [404, 403])
37
+ Rack::MockRequest.new(cascade).get('/').should.be.not_found
38
+ cascade << app2
39
+ Rack::MockRequest.new(cascade).get('/cgi/test').should.be.not_found
40
+ Rack::MockRequest.new(cascade).get('/cgi/../bla').should.be.not_found
41
+ cascade << app1
42
+ Rack::MockRequest.new(cascade).get('/cgi/test').should.be.ok
43
+ Rack::MockRequest.new(cascade).get('/cgi/../bla').should.be.forbidden
44
+ Rack::MockRequest.new(cascade).get('/foo').should.be.not_found
45
+ cascade << app3
46
+ Rack::MockRequest.new(cascade).get('/foo').should.be.ok
47
+ end
48
+ end
@@ -0,0 +1,89 @@
1
+ require 'test/spec'
2
+ require 'testrequest'
3
+
4
+ context "Rack::Handler::CGI" do
5
+ include TestRequest::Helpers
6
+
7
+ setup do
8
+ @host = '0.0.0.0'
9
+ @port = 9203
10
+ end
11
+
12
+ # Keep this first.
13
+ specify "startup" do
14
+ $pid = fork {
15
+ Dir.chdir(File.join(File.dirname(__FILE__), "..", "test", "cgi"))
16
+ exec "lighttpd -D -f lighttpd.conf"
17
+ }
18
+ end
19
+
20
+ specify "should respond" do
21
+ sleep 1
22
+ lambda {
23
+ GET("/test")
24
+ }.should.not.raise
25
+ end
26
+
27
+ specify "should be a lighttpd" do
28
+ GET("/test")
29
+ status.should.be 200
30
+ response["SERVER_SOFTWARE"].should =~ /lighttpd/
31
+ response["HTTP_VERSION"].should.equal "HTTP/1.1"
32
+ response["SERVER_PROTOCOL"].should.equal "HTTP/1.1"
33
+ response["SERVER_PORT"].should.equal @port.to_s
34
+ response["SERVER_NAME"].should =~ @host
35
+ end
36
+
37
+ specify "should have rack headers" do
38
+ GET("/test")
39
+ response["rack.version"].should.equal [1,1]
40
+ response["rack.multithread"].should.be false
41
+ response["rack.multiprocess"].should.be true
42
+ response["rack.run_once"].should.be true
43
+ end
44
+
45
+ specify "should have CGI headers on GET" do
46
+ GET("/test")
47
+ response["REQUEST_METHOD"].should.equal "GET"
48
+ response["SCRIPT_NAME"].should.equal "/test"
49
+ response["REQUEST_PATH"].should.equal "/"
50
+ response["PATH_INFO"].should.equal ""
51
+ response["QUERY_STRING"].should.equal ""
52
+ response["test.postdata"].should.equal ""
53
+
54
+ GET("/test/foo?quux=1")
55
+ response["REQUEST_METHOD"].should.equal "GET"
56
+ response["SCRIPT_NAME"].should.equal "/test"
57
+ response["REQUEST_PATH"].should.equal "/"
58
+ response["PATH_INFO"].should.equal "/foo"
59
+ response["QUERY_STRING"].should.equal "quux=1"
60
+ end
61
+
62
+ specify "should have CGI headers on POST" do
63
+ POST("/test", {"rack-form-data" => "23"}, {'X-test-header' => '42'})
64
+ status.should.equal 200
65
+ response["REQUEST_METHOD"].should.equal "POST"
66
+ response["SCRIPT_NAME"].should.equal "/test"
67
+ response["REQUEST_PATH"].should.equal "/"
68
+ response["QUERY_STRING"].should.equal ""
69
+ response["HTTP_X_TEST_HEADER"].should.equal "42"
70
+ response["test.postdata"].should.equal "rack-form-data=23"
71
+ end
72
+
73
+ specify "should support HTTP auth" do
74
+ GET("/test", {:user => "ruth", :passwd => "secret"})
75
+ response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ="
76
+ end
77
+
78
+ specify "should set status" do
79
+ GET("/test?secret")
80
+ status.should.equal 403
81
+ response["rack.url_scheme"].should.equal "http"
82
+ end
83
+
84
+ # Keep this last.
85
+ specify "shutdown" do
86
+ Process.kill 15, $pid
87
+ Process.wait($pid).should.equal $pid
88
+ end
89
+ end
@@ -0,0 +1,62 @@
1
+ require 'rack/mock'
2
+ require 'rack/chunked'
3
+ require 'rack/utils'
4
+
5
+ context "Rack::Chunked" do
6
+
7
+ before do
8
+ @env = Rack::MockRequest.
9
+ env_for('/', 'HTTP_VERSION' => '1.1', 'REQUEST_METHOD' => 'GET')
10
+ end
11
+
12
+ specify 'chunks responses with no Content-Length' do
13
+ app = lambda { |env| [200, {}, ['Hello', ' ', 'World!']] }
14
+ response = Rack::MockResponse.new(*Rack::Chunked.new(app).call(@env))
15
+ response.headers.should.not.include 'Content-Length'
16
+ response.headers['Transfer-Encoding'].should.equal 'chunked'
17
+ response.body.should.equal "5\r\nHello\r\n1\r\n \r\n6\r\nWorld!\r\n0\r\n\r\n"
18
+ end
19
+
20
+ specify 'chunks empty bodies properly' do
21
+ app = lambda { |env| [200, {}, []] }
22
+ response = Rack::MockResponse.new(*Rack::Chunked.new(app).call(@env))
23
+ response.headers.should.not.include 'Content-Length'
24
+ response.headers['Transfer-Encoding'].should.equal 'chunked'
25
+ response.body.should.equal "0\r\n\r\n"
26
+ end
27
+
28
+ specify 'does not modify response when Content-Length header present' do
29
+ app = lambda { |env| [200, {'Content-Length'=>'12'}, ['Hello', ' ', 'World!']] }
30
+ status, headers, body = Rack::Chunked.new(app).call(@env)
31
+ status.should.equal 200
32
+ headers.should.not.include 'Transfer-Encoding'
33
+ headers.should.include 'Content-Length'
34
+ body.join.should.equal 'Hello World!'
35
+ end
36
+
37
+ specify 'does not modify response when client is HTTP/1.0' do
38
+ app = lambda { |env| [200, {}, ['Hello', ' ', 'World!']] }
39
+ @env['HTTP_VERSION'] = 'HTTP/1.0'
40
+ status, headers, body = Rack::Chunked.new(app).call(@env)
41
+ status.should.equal 200
42
+ headers.should.not.include 'Transfer-Encoding'
43
+ body.join.should.equal 'Hello World!'
44
+ end
45
+
46
+ specify 'does not modify response when Transfer-Encoding header already present' do
47
+ app = lambda { |env| [200, {'Transfer-Encoding' => 'identity'}, ['Hello', ' ', 'World!']] }
48
+ status, headers, body = Rack::Chunked.new(app).call(@env)
49
+ status.should.equal 200
50
+ headers['Transfer-Encoding'].should.equal 'identity'
51
+ body.join.should.equal 'Hello World!'
52
+ end
53
+
54
+ [100, 204, 304].each do |status_code|
55
+ specify "does not modify response when status code is #{status_code}" do
56
+ app = lambda { |env| [status_code, {}, []] }
57
+ status, headers, body = Rack::Chunked.new(app).call(@env)
58
+ status.should.equal status_code
59
+ headers.should.not.include 'Transfer-Encoding'
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,61 @@
1
+ require 'test/spec'
2
+ require 'stringio'
3
+
4
+ require 'rack/commonlogger'
5
+ require 'rack/lobster'
6
+ require 'rack/mock'
7
+
8
+ context "Rack::CommonLogger" do
9
+ app = lambda { |env|
10
+ [200,
11
+ {"Content-Type" => "text/html", "Content-Length" => length.to_s},
12
+ [obj]]}
13
+ app_without_length = lambda { |env|
14
+ [200,
15
+ {"Content-Type" => "text/html"},
16
+ []]}
17
+ app_with_zero_length = lambda { |env|
18
+ [200,
19
+ {"Content-Type" => "text/html", "Content-Length" => "0"},
20
+ []]}
21
+
22
+ specify "should log to rack.errors by default" do
23
+ res = Rack::MockRequest.new(Rack::CommonLogger.new(app)).get("/")
24
+
25
+ res.errors.should.not.be.empty
26
+ res.errors.should =~ /"GET \/ " 200 #{length} /
27
+ end
28
+
29
+ specify "should log to anything with +write+" do
30
+ log = StringIO.new
31
+ res = Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/")
32
+
33
+ log.string.should =~ /"GET \/ " 200 #{length} /
34
+ end
35
+
36
+ specify "should log - content length if header is missing" do
37
+ res = Rack::MockRequest.new(Rack::CommonLogger.new(app_without_length)).get("/")
38
+
39
+ res.errors.should.not.be.empty
40
+ res.errors.should =~ /"GET \/ " 200 - /
41
+ end
42
+
43
+ specify "should log - content length if header is zero" do
44
+ res = Rack::MockRequest.new(Rack::CommonLogger.new(app_with_zero_length)).get("/")
45
+
46
+ res.errors.should.not.be.empty
47
+ res.errors.should =~ /"GET \/ " 200 - /
48
+ end
49
+
50
+ def length
51
+ self.class.length
52
+ end
53
+
54
+ def self.length
55
+ 123
56
+ end
57
+
58
+ def self.obj
59
+ "hello world"
60
+ end
61
+ end
@@ -0,0 +1,41 @@
1
+ require 'test/spec'
2
+ require 'time'
3
+
4
+ require 'rack/mock'
5
+ require 'rack/conditionalget'
6
+
7
+ context "Rack::ConditionalGet" do
8
+ specify "should set a 304 status and truncate body when If-Modified-Since hits" do
9
+ timestamp = Time.now.httpdate
10
+ app = Rack::ConditionalGet.new(lambda { |env|
11
+ [200, {'Last-Modified'=>timestamp}, ['TEST']] })
12
+
13
+ response = Rack::MockRequest.new(app).
14
+ get("/", 'HTTP_IF_MODIFIED_SINCE' => timestamp)
15
+
16
+ response.status.should.equal 304
17
+ response.body.should.be.empty
18
+ end
19
+
20
+ specify "should set a 304 status and truncate body when If-None-Match hits" do
21
+ app = Rack::ConditionalGet.new(lambda { |env|
22
+ [200, {'Etag'=>'1234'}, ['TEST']] })
23
+
24
+ response = Rack::MockRequest.new(app).
25
+ get("/", 'HTTP_IF_NONE_MATCH' => '1234')
26
+
27
+ response.status.should.equal 304
28
+ response.body.should.be.empty
29
+ end
30
+
31
+ specify "should not affect non-GET/HEAD requests" do
32
+ app = Rack::ConditionalGet.new(lambda { |env|
33
+ [200, {'Etag'=>'1234'}, ['TEST']] })
34
+
35
+ response = Rack::MockRequest.new(app).
36
+ post("/", 'HTTP_IF_NONE_MATCH' => '1234')
37
+
38
+ response.status.should.equal 200
39
+ response.body.should.equal 'TEST'
40
+ end
41
+ end
@@ -0,0 +1,24 @@
1
+ require 'test/spec'
2
+ require 'rack/mock'
3
+ require 'rack/builder'
4
+ require 'rack/content_length'
5
+ require 'rack/config'
6
+
7
+ context "Rack::Config" do
8
+
9
+ specify "should accept a block that modifies the environment" do
10
+ app = Rack::Builder.new do
11
+ use Rack::Lint
12
+ use Rack::ContentLength
13
+ use Rack::Config do |env|
14
+ env['greeting'] = 'hello'
15
+ end
16
+ run lambda { |env|
17
+ [200, {'Content-Type' => 'text/plain'}, [env['greeting'] || '']]
18
+ }
19
+ end
20
+ response = Rack::MockRequest.new(app).get('/')
21
+ response.body.should.equal('hello')
22
+ end
23
+
24
+ end
@@ -0,0 +1,43 @@
1
+ require 'rack/mock'
2
+ require 'rack/content_length'
3
+
4
+ context "Rack::ContentLength" do
5
+ specify "sets Content-Length on String bodies if none is set" do
6
+ app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, "Hello, World!"] }
7
+ response = Rack::ContentLength.new(app).call({})
8
+ response[1]['Content-Length'].should.equal '13'
9
+ end
10
+
11
+ specify "sets Content-Length on Array bodies if none is set" do
12
+ app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
13
+ response = Rack::ContentLength.new(app).call({})
14
+ response[1]['Content-Length'].should.equal '13'
15
+ end
16
+
17
+ specify "does not set Content-Length on variable length bodies" do
18
+ body = lambda { "Hello World!" }
19
+ def body.each ; yield call ; end
20
+
21
+ app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, body] }
22
+ response = Rack::ContentLength.new(app).call({})
23
+ response[1]['Content-Length'].should.be.nil
24
+ end
25
+
26
+ specify "does not change Content-Length if it is already set" do
27
+ app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Content-Length' => '1'}, "Hello, World!"] }
28
+ response = Rack::ContentLength.new(app).call({})
29
+ response[1]['Content-Length'].should.equal '1'
30
+ end
31
+
32
+ specify "does not set Content-Length on 304 responses" do
33
+ app = lambda { |env| [304, {'Content-Type' => 'text/plain'}, []] }
34
+ response = Rack::ContentLength.new(app).call({})
35
+ response[1]['Content-Length'].should.equal nil
36
+ end
37
+
38
+ specify "does not set Content-Length when Transfer-Encoding is chunked" do
39
+ app = lambda { |env| [200, {'Transfer-Encoding' => 'chunked'}, []] }
40
+ response = Rack::ContentLength.new(app).call({})
41
+ response[1]['Content-Length'].should.equal nil
42
+ end
43
+ end
@@ -0,0 +1,30 @@
1
+ require 'rack/mock'
2
+ require 'rack/content_type'
3
+
4
+ context "Rack::ContentType" do
5
+ specify "sets Content-Type to default text/html if none is set" do
6
+ app = lambda { |env| [200, {}, "Hello, World!"] }
7
+ status, headers, body = Rack::ContentType.new(app).call({})
8
+ headers['Content-Type'].should.equal 'text/html'
9
+ end
10
+
11
+ specify "sets Content-Type to chosen default if none is set" do
12
+ app = lambda { |env| [200, {}, "Hello, World!"] }
13
+ status, headers, body =
14
+ Rack::ContentType.new(app, 'application/octet-stream').call({})
15
+ headers['Content-Type'].should.equal 'application/octet-stream'
16
+ end
17
+
18
+ specify "does not change Content-Type if it is already set" do
19
+ app = lambda { |env| [200, {'Content-Type' => 'foo/bar'}, "Hello, World!"] }
20
+ status, headers, body = Rack::ContentType.new(app).call({})
21
+ headers['Content-Type'].should.equal 'foo/bar'
22
+ end
23
+
24
+ specify "case insensitive detection of Content-Type" do
25
+ app = lambda { |env| [200, {'CONTENT-Type' => 'foo/bar'}, "Hello, World!"] }
26
+ status, headers, body = Rack::ContentType.new(app).call({})
27
+ headers.to_a.select { |k,v| k.downcase == "content-type" }.
28
+ should.equal [["CONTENT-Type","foo/bar"]]
29
+ end
30
+ end
@@ -0,0 +1,127 @@
1
+ require 'test/spec'
2
+
3
+ require 'rack/mock'
4
+ require 'rack/deflater'
5
+ require 'stringio'
6
+ require 'time' # for Time#httpdate
7
+
8
+ context "Rack::Deflater" do
9
+ def build_response(status, body, accept_encoding, headers = {})
10
+ body = [body] if body.respond_to? :to_str
11
+ app = lambda { |env| [status, {}, body] }
12
+ request = Rack::MockRequest.env_for("", headers.merge("HTTP_ACCEPT_ENCODING" => accept_encoding))
13
+ response = Rack::Deflater.new(app).call(request)
14
+
15
+ return response
16
+ end
17
+
18
+ specify "should be able to deflate bodies that respond to each" do
19
+ body = Object.new
20
+ class << body; def each; yield("foo"); yield("bar"); end; end
21
+
22
+ response = build_response(200, body, "deflate")
23
+
24
+ response[0].should.equal(200)
25
+ response[1].should.equal({
26
+ "Content-Encoding" => "deflate",
27
+ "Vary" => "Accept-Encoding"
28
+ })
29
+ buf = ''
30
+ response[2].each { |part| buf << part }
31
+ buf.should.equal("K\313\317OJ,\002\000")
32
+ end
33
+
34
+ # TODO: This is really just a special case of the above...
35
+ specify "should be able to deflate String bodies" do
36
+ response = build_response(200, "Hello world!", "deflate")
37
+
38
+ response[0].should.equal(200)
39
+ response[1].should.equal({
40
+ "Content-Encoding" => "deflate",
41
+ "Vary" => "Accept-Encoding"
42
+ })
43
+ buf = ''
44
+ response[2].each { |part| buf << part }
45
+ buf.should.equal("\363H\315\311\311W(\317/\312IQ\004\000")
46
+ end
47
+
48
+ specify "should be able to gzip bodies that respond to each" do
49
+ body = Object.new
50
+ class << body; def each; yield("foo"); yield("bar"); end; end
51
+
52
+ response = build_response(200, body, "gzip")
53
+
54
+ response[0].should.equal(200)
55
+ response[1].should.equal({
56
+ "Content-Encoding" => "gzip",
57
+ "Vary" => "Accept-Encoding",
58
+ })
59
+
60
+ buf = ''
61
+ response[2].each { |part| buf << part }
62
+ io = StringIO.new(buf)
63
+ gz = Zlib::GzipReader.new(io)
64
+ gz.read.should.equal("foobar")
65
+ gz.close
66
+ end
67
+
68
+ specify "should be able to fallback to no deflation" do
69
+ response = build_response(200, "Hello world!", "superzip")
70
+
71
+ response[0].should.equal(200)
72
+ response[1].should.equal({ "Vary" => "Accept-Encoding" })
73
+ response[2].should.equal(["Hello world!"])
74
+ end
75
+
76
+ specify "should be able to skip when there is no response entity body" do
77
+ response = build_response(304, [], "gzip")
78
+
79
+ response[0].should.equal(304)
80
+ response[1].should.equal({})
81
+ response[2].should.equal([])
82
+ end
83
+
84
+ specify "should handle the lack of an acceptable encoding" do
85
+ response1 = build_response(200, "Hello world!", "identity;q=0", "PATH_INFO" => "/")
86
+ response1[0].should.equal(406)
87
+ response1[1].should.equal({"Content-Type" => "text/plain", "Content-Length" => "71"})
88
+ response1[2].should.equal(["An acceptable encoding for the requested resource / could not be found."])
89
+
90
+ response2 = build_response(200, "Hello world!", "identity;q=0", "SCRIPT_NAME" => "/foo", "PATH_INFO" => "/bar")
91
+ response2[0].should.equal(406)
92
+ response2[1].should.equal({"Content-Type" => "text/plain", "Content-Length" => "78"})
93
+ response2[2].should.equal(["An acceptable encoding for the requested resource /foo/bar could not be found."])
94
+ end
95
+
96
+ specify "should handle gzip response with Last-Modified header" do
97
+ last_modified = Time.now.httpdate
98
+
99
+ app = lambda { |env| [200, { "Last-Modified" => last_modified }, ["Hello World!"]] }
100
+ request = Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => "gzip")
101
+ response = Rack::Deflater.new(app).call(request)
102
+
103
+ response[0].should.equal(200)
104
+ response[1].should.equal({
105
+ "Content-Encoding" => "gzip",
106
+ "Vary" => "Accept-Encoding",
107
+ "Last-Modified" => last_modified
108
+ })
109
+
110
+ buf = ''
111
+ response[2].each { |part| buf << part }
112
+ io = StringIO.new(buf)
113
+ gz = Zlib::GzipReader.new(io)
114
+ gz.read.should.equal("Hello World!")
115
+ gz.close
116
+ end
117
+
118
+ specify "should do nothing when no-transform Cache-Control directive present" do
119
+ app = lambda { |env| [200, {'Cache-Control' => 'no-transform'}, ['Hello World!']] }
120
+ request = Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => "gzip")
121
+ response = Rack::Deflater.new(app).call(request)
122
+
123
+ response[0].should.equal(200)
124
+ response[1].should.not.include "Content-Encoding"
125
+ response[2].join.should.equal("Hello World!")
126
+ end
127
+ end