devver-rack-contrib 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/COPYING +18 -0
  2. data/README.rdoc +80 -0
  3. data/Rakefile +90 -0
  4. data/lib/rack/contrib.rb +40 -0
  5. data/lib/rack/contrib/accept_format.rb +46 -0
  6. data/lib/rack/contrib/access.rb +85 -0
  7. data/lib/rack/contrib/backstage.rb +20 -0
  8. data/lib/rack/contrib/bounce_favicon.rb +16 -0
  9. data/lib/rack/contrib/callbacks.rb +37 -0
  10. data/lib/rack/contrib/config.rb +16 -0
  11. data/lib/rack/contrib/cookies.rb +50 -0
  12. data/lib/rack/contrib/csshttprequest.rb +39 -0
  13. data/lib/rack/contrib/deflect.rb +137 -0
  14. data/lib/rack/contrib/evil.rb +12 -0
  15. data/lib/rack/contrib/garbagecollector.rb +14 -0
  16. data/lib/rack/contrib/jsonp.rb +41 -0
  17. data/lib/rack/contrib/lighttpd_script_name_fix.rb +16 -0
  18. data/lib/rack/contrib/locale.rb +31 -0
  19. data/lib/rack/contrib/mailexceptions.rb +120 -0
  20. data/lib/rack/contrib/nested_params.rb +143 -0
  21. data/lib/rack/contrib/not_found.rb +18 -0
  22. data/lib/rack/contrib/post_body_content_type_parser.rb +40 -0
  23. data/lib/rack/contrib/proctitle.rb +30 -0
  24. data/lib/rack/contrib/profiler.rb +108 -0
  25. data/lib/rack/contrib/relative_redirect.rb +44 -0
  26. data/lib/rack/contrib/response_cache.rb +59 -0
  27. data/lib/rack/contrib/route_exceptions.rb +49 -0
  28. data/lib/rack/contrib/sendfile.rb +142 -0
  29. data/lib/rack/contrib/signals.rb +63 -0
  30. data/lib/rack/contrib/time_zone.rb +25 -0
  31. data/rack-contrib.gemspec +88 -0
  32. data/test/404.html +1 -0
  33. data/test/Maintenance.html +1 -0
  34. data/test/mail_settings.rb +12 -0
  35. data/test/spec_rack_accept_format.rb +72 -0
  36. data/test/spec_rack_access.rb +154 -0
  37. data/test/spec_rack_backstage.rb +26 -0
  38. data/test/spec_rack_callbacks.rb +65 -0
  39. data/test/spec_rack_config.rb +22 -0
  40. data/test/spec_rack_contrib.rb +8 -0
  41. data/test/spec_rack_csshttprequest.rb +66 -0
  42. data/test/spec_rack_deflect.rb +107 -0
  43. data/test/spec_rack_evil.rb +19 -0
  44. data/test/spec_rack_garbagecollector.rb +13 -0
  45. data/test/spec_rack_jsonp.rb +34 -0
  46. data/test/spec_rack_lighttpd_script_name_fix.rb +16 -0
  47. data/test/spec_rack_mailexceptions.rb +97 -0
  48. data/test/spec_rack_nested_params.rb +46 -0
  49. data/test/spec_rack_not_found.rb +17 -0
  50. data/test/spec_rack_post_body_content_type_parser.rb +32 -0
  51. data/test/spec_rack_proctitle.rb +26 -0
  52. data/test/spec_rack_profiler.rb +41 -0
  53. data/test/spec_rack_relative_redirect.rb +78 -0
  54. data/test/spec_rack_response_cache.rb +137 -0
  55. data/test/spec_rack_sendfile.rb +86 -0
  56. metadata +174 -0
@@ -0,0 +1,154 @@
1
+ require 'test/spec'
2
+ require 'rack/mock'
3
+ require 'rack/contrib/access'
4
+
5
+ context "Rack::Access" do
6
+
7
+ setup do
8
+ @app = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, 'hello'] }
9
+ @mock_addr_1 = '111.111.111.111'
10
+ @mock_addr_2 = '192.168.1.222'
11
+ @mock_addr_localhost = '127.0.0.1'
12
+ @mock_addr_range = '192.168.1.0/24'
13
+ end
14
+
15
+ def mock_env(remote_addr, path = '/')
16
+ Rack::MockRequest.env_for(path, { 'REMOTE_ADDR' => remote_addr })
17
+ end
18
+
19
+ def middleware(options = {})
20
+ Rack::Access.new(@app, options)
21
+ end
22
+
23
+ specify "default configuration should deny non-local requests" do
24
+ app = middleware
25
+ status, headers, body = app.call(mock_env(@mock_addr_1))
26
+ status.should.equal 403
27
+ body.should.equal ''
28
+ end
29
+
30
+ specify "default configuration should allow requests from 127.0.0.1" do
31
+ app = middleware
32
+ status, headers, body = app.call(mock_env(@mock_addr_localhost))
33
+ status.should.equal 200
34
+ body.should.equal 'hello'
35
+ end
36
+
37
+ specify "should allow remote addresses in allow_ipmasking" do
38
+ app = middleware('/' => [@mock_addr_1])
39
+ status, headers, body = app.call(mock_env(@mock_addr_1))
40
+ status.should.equal 200
41
+ body.should.equal 'hello'
42
+ end
43
+
44
+ specify "should deny remote addresses not in allow_ipmasks" do
45
+ app = middleware('/' => [@mock_addr_1])
46
+ status, headers, body = app.call(mock_env(@mock_addr_2))
47
+ status.should.equal 403
48
+ body.should.equal ''
49
+ end
50
+
51
+ specify "should allow remote addresses in allow_ipmasks range" do
52
+ app = middleware('/' => [@mock_addr_range])
53
+ status, headers, body = app.call(mock_env(@mock_addr_2))
54
+ status.should.equal 200
55
+ body.should.equal 'hello'
56
+ end
57
+
58
+ specify "should deny remote addresses not in allow_ipmasks range" do
59
+ app = middleware('/' => [@mock_addr_range])
60
+ status, headers, body = app.call(mock_env(@mock_addr_1))
61
+ status.should.equal 403
62
+ body.should.equal ''
63
+ end
64
+
65
+ specify "should allow remote addresses in one of allow_ipmasking" do
66
+ app = middleware('/' => [@mock_addr_range, @mock_addr_localhost])
67
+
68
+ status, headers, body = app.call(mock_env(@mock_addr_2))
69
+ status.should.equal 200
70
+ body.should.equal 'hello'
71
+
72
+ status, headers, body = app.call(mock_env(@mock_addr_localhost))
73
+ status.should.equal 200
74
+ body.should.equal 'hello'
75
+ end
76
+
77
+ specify "should deny remote addresses not in one of allow_ipmasks" do
78
+ app = middleware('/' => [@mock_addr_range, @mock_addr_localhost])
79
+ status, headers, body = app.call(mock_env(@mock_addr_1))
80
+ status.should.equal 403
81
+ body.should.equal ''
82
+ end
83
+
84
+ specify "handles paths correctly" do
85
+ app = middleware({
86
+ 'http://foo.org/bar' => [@mock_addr_localhost],
87
+ '/foo' => [@mock_addr_localhost],
88
+ '/foo/bar' => [@mock_addr_range, @mock_addr_localhost]
89
+ })
90
+
91
+ status, headers, body = app.call(mock_env(@mock_addr_1, "/"))
92
+ status.should.equal 200
93
+ body.should.equal 'hello'
94
+
95
+ status, headers, body = app.call(mock_env(@mock_addr_1, "/qux"))
96
+ status.should.equal 200
97
+ body.should.equal 'hello'
98
+
99
+ status, headers, body = app.call(mock_env(@mock_addr_1, "/foo"))
100
+ status.should.equal 403
101
+ body.should.equal ''
102
+ status, headers, body = app.call(mock_env(@mock_addr_localhost, "/foo"))
103
+ status.should.equal 200
104
+ body.should.equal 'hello'
105
+
106
+ status, headers, body = app.call(mock_env(@mock_addr_1, "/foo/"))
107
+ status.should.equal 403
108
+ body.should.equal ''
109
+ status, headers, body = app.call(mock_env(@mock_addr_localhost, "/foo/"))
110
+ status.should.equal 200
111
+ body.should.equal 'hello'
112
+
113
+ status, headers, body = app.call(mock_env(@mock_addr_1, "/foo/bar"))
114
+ status.should.equal 403
115
+ body.should.equal ''
116
+ status, headers, body = app.call(mock_env(@mock_addr_localhost, "/foo/bar"))
117
+ status.should.equal 200
118
+ body.should.equal 'hello'
119
+ status, headers, body = app.call(mock_env(@mock_addr_2, "/foo/bar"))
120
+ status.should.equal 200
121
+ body.should.equal 'hello'
122
+
123
+ status, headers, body = app.call(mock_env(@mock_addr_1, "/foo/bar/"))
124
+ status.should.equal 403
125
+ body.should.equal ''
126
+ status, headers, body = app.call(mock_env(@mock_addr_localhost, "/foo/bar/"))
127
+ status.should.equal 200
128
+ body.should.equal 'hello'
129
+
130
+ status, headers, body = app.call(mock_env(@mock_addr_1, "/foo///bar//quux"))
131
+ status.should.equal 403
132
+ body.should.equal ''
133
+ status, headers, body = app.call(mock_env(@mock_addr_localhost, "/foo///bar//quux"))
134
+ status.should.equal 200
135
+ body.should.equal 'hello'
136
+
137
+ status, headers, body = app.call(mock_env(@mock_addr_1, "/foo/quux"))
138
+ status.should.equal 403
139
+ body.should.equal ''
140
+ status, headers, body = app.call(mock_env(@mock_addr_localhost, "/foo/quux"))
141
+ status.should.equal 200
142
+ body.should.equal 'hello'
143
+
144
+ status, headers, body = app.call(mock_env(@mock_addr_1, "/bar"))
145
+ status.should.equal 200
146
+ body.should.equal 'hello'
147
+ status, headers, body = app.call(mock_env(@mock_addr_1, "/bar").merge('HTTP_HOST' => 'foo.org'))
148
+ status.should.equal 403
149
+ body.should.equal ''
150
+ status, headers, body = app.call(mock_env(@mock_addr_localhost, "/bar").merge('HTTP_HOST' => 'foo.org'))
151
+ status.should.equal 200
152
+ body.should.equal 'hello'
153
+ end
154
+ end
@@ -0,0 +1,26 @@
1
+ require 'test/spec'
2
+ require 'rack/builder'
3
+ require 'rack/mock'
4
+ require 'rack/contrib/backstage'
5
+
6
+ context "Rack::Backstage" do
7
+ specify "shows maintenances page if present" do
8
+ app = Rack::Builder.new do
9
+ use Rack::Backstage, 'test/Maintenance.html'
10
+ run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
11
+ end
12
+ response = Rack::MockRequest.new(app).get('/')
13
+ response.body.should.equal('Under maintenance.')
14
+ response.status.should.equal(503)
15
+ end
16
+
17
+ specify "passes on request if page is not present" do
18
+ app = Rack::Builder.new do
19
+ use Rack::Backstage, 'test/Nonsense.html'
20
+ run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
21
+ end
22
+ response = Rack::MockRequest.new(app).get('/')
23
+ response.body.should.equal('Hello, World!')
24
+ response.status.should.equal(200)
25
+ end
26
+ end
@@ -0,0 +1,65 @@
1
+ require 'test/spec'
2
+ require 'rack/mock'
3
+
4
+ class Flame
5
+ def call(env)
6
+ env['flame'] = 'F Lifo..'
7
+ end
8
+ end
9
+
10
+ class Pacify
11
+ def initialize(with)
12
+ @with = with
13
+ end
14
+
15
+ def call(env)
16
+ env['peace'] = @with
17
+ end
18
+ end
19
+
20
+ class Finale
21
+ def call(response)
22
+ status, headers, body = response
23
+
24
+ headers['last'] = 'Finale'
25
+ $old_status = status
26
+
27
+ [201, headers, body]
28
+ end
29
+ end
30
+
31
+ class TheEnd
32
+ def call(response)
33
+ status, headers, body = response
34
+
35
+ headers['last'] = 'TheEnd'
36
+ [201, headers, body]
37
+ end
38
+ end
39
+
40
+ context "Rack::Callbacks" do
41
+ specify "works for love and small stack trace" do
42
+ callback_app = Rack::Callbacks.new do
43
+ before Flame
44
+ before Pacify, "with love"
45
+
46
+ run lambda {|env| [200, {}, [env['flame'], env['peace']]] }
47
+
48
+ after Finale
49
+ after TheEnd
50
+ end
51
+
52
+ app = Rack::Builder.new do
53
+ run callback_app
54
+ end.to_app
55
+
56
+ response = Rack::MockRequest.new(app).get("/")
57
+
58
+ response.body.should.equal 'F Lifo..with love'
59
+
60
+ $old_status.should.equal 200
61
+ response.status.should.equal 201
62
+
63
+ response.headers['last'].should.equal 'TheEnd'
64
+ end
65
+ end
@@ -0,0 +1,22 @@
1
+ require 'test/spec'
2
+ require 'rack/mock'
3
+ require 'rack/contrib/config'
4
+
5
+ context "Rack::Config" do
6
+
7
+ specify "should accept a block that modifies the environment" do
8
+ app = Rack::Builder.new do
9
+ use Rack::Lint
10
+ use Rack::ContentLength
11
+ use Rack::Config do |env|
12
+ env['greeting'] = 'hello'
13
+ end
14
+ run lambda { |env|
15
+ [200, {'Content-Type' => 'text/plain'}, [env['greeting'] || '']]
16
+ }
17
+ end
18
+ response = Rack::MockRequest.new(app).get('/')
19
+ response.body.should.equal('hello')
20
+ end
21
+
22
+ end
@@ -0,0 +1,8 @@
1
+ require 'test/spec'
2
+ require 'rack/contrib'
3
+
4
+ context "Rack::Contrib" do
5
+ specify "should expose release" do
6
+ Rack::Contrib.should.respond_to :release
7
+ end
8
+ end
@@ -0,0 +1,66 @@
1
+ require 'test/spec'
2
+ require 'rack/mock'
3
+
4
+ begin
5
+ require 'csshttprequest'
6
+ require 'rack/contrib/csshttprequest'
7
+
8
+ context "Rack::CSSHTTPRequest" do
9
+
10
+ before(:each) do
11
+ @test_body = '{"bar":"foo"}'
12
+ @test_headers = {'Content-Type' => 'text/plain'}
13
+ @encoded_body = CSSHTTPRequest.encode(@test_body)
14
+ @app = lambda { |env| [200, @test_headers, [@test_body]] }
15
+ end
16
+
17
+ specify "env['csshttprequest.chr'] should be set to true when \
18
+ PATH_INFO ends with '.chr'" do
19
+ request = Rack::MockRequest.env_for("/blah.chr", :lint => true, :fatal => true)
20
+ Rack::CSSHTTPRequest.new(@app).call(request)
21
+ request['csshttprequest.chr'].should.equal true
22
+ end
23
+
24
+ specify "env['csshttprequest.chr'] should be set to true when \
25
+ request parameter _format == 'chr'" do
26
+ request = Rack::MockRequest.env_for("/?_format=chr", :lint => true, :fatal => true)
27
+ Rack::CSSHTTPRequest.new(@app).call(request)
28
+ request['csshttprequest.chr'].should.equal true
29
+ end
30
+
31
+ specify "should not change the headers or response when !env['csshttprequest.chr']" do
32
+ request = Rack::MockRequest.env_for("/", :lint => true, :fatal => true)
33
+ status, headers, response = Rack::CSSHTTPRequest.new(@app).call(request)
34
+ headers.should.equal @test_headers
35
+ response.join.should.equal @test_body
36
+ end
37
+
38
+ context "when env['csshttprequest.chr']" do
39
+ before(:each) do
40
+ @request = Rack::MockRequest.env_for("/",
41
+ 'csshttprequest.chr' => true, :lint => true, :fatal => true)
42
+ end
43
+
44
+ specify "should modify the content length to the correct value" do
45
+ headers = Rack::CSSHTTPRequest.new(@app).call(@request)[1]
46
+ headers['Content-Length'].should.equal @encoded_body.length.to_s
47
+ end
48
+
49
+ specify "should modify the content type to the correct value" do
50
+ headers = Rack::CSSHTTPRequest.new(@app).call(@request)[1]
51
+ headers['Content-Type'].should.equal 'text/css'
52
+ end
53
+
54
+ specify "should not modify any other headers" do
55
+ headers = Rack::CSSHTTPRequest.new(@app).call(@request)[1]
56
+ headers.should.equal @test_headers.merge({
57
+ 'Content-Type' => 'text/css',
58
+ 'Content-Length' => @encoded_body.length.to_s
59
+ })
60
+ end
61
+ end
62
+
63
+ end
64
+ rescue LoadError => boom
65
+ STDERR.puts "WARN: Skipping Rack::CSSHTTPRequest tests (nbio-csshttprequest not installed)"
66
+ end
@@ -0,0 +1,107 @@
1
+ require 'test/spec'
2
+ require 'rack/mock'
3
+ require 'rack/contrib/deflect'
4
+
5
+ context "Rack::Deflect" do
6
+
7
+ setup do
8
+ @app = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, 'cookies'] }
9
+ @mock_addr_1 = '111.111.111.111'
10
+ @mock_addr_2 = '222.222.222.222'
11
+ @mock_addr_3 = '333.333.333.333'
12
+ end
13
+
14
+ def mock_env remote_addr, path = '/'
15
+ Rack::MockRequest.env_for path, { 'REMOTE_ADDR' => remote_addr }
16
+ end
17
+
18
+ def mock_deflect options = {}
19
+ Rack::Deflect.new @app, options
20
+ end
21
+
22
+ specify "should allow regular requests to follow through" do
23
+ app = mock_deflect
24
+ status, headers, body = app.call mock_env(@mock_addr_1)
25
+ status.should.equal 200
26
+ body.should.equal 'cookies'
27
+ end
28
+
29
+ specify "should deflect requests exceeding the request threshold" do
30
+ log = StringIO.new
31
+ app = mock_deflect :request_threshold => 5, :interval => 10, :block_duration => 10, :log => log
32
+ env = mock_env @mock_addr_1
33
+
34
+ # First 5 should be fine
35
+ 5.times do
36
+ status, headers, body = app.call env
37
+ status.should.equal 200
38
+ body.should.equal 'cookies'
39
+ end
40
+
41
+ # Remaining requests should fail for 10 seconds
42
+ 10.times do
43
+ status, headers, body = app.call env
44
+ status.should.equal 403
45
+ body.should.equal ''
46
+ end
47
+
48
+ # Log should reflect that we have blocked an address
49
+ log.string.should.match(/^deflect\(\d+\/\d+\/\d+\): blocked 111.111.111.111\n/)
50
+ end
51
+
52
+ specify "should expire blocking" do
53
+ log = StringIO.new
54
+ app = mock_deflect :request_threshold => 5, :interval => 2, :block_duration => 2, :log => log
55
+ env = mock_env @mock_addr_1
56
+
57
+ # First 5 should be fine
58
+ 5.times do
59
+ status, headers, body = app.call env
60
+ status.should.equal 200
61
+ body.should.equal 'cookies'
62
+ end
63
+
64
+ # Exceeds request threshold
65
+ status, headers, body = app.call env
66
+ status.should.equal 403
67
+ body.should.equal ''
68
+
69
+ # Allow block to expire
70
+ sleep 3
71
+
72
+ # Another 5 is fine now
73
+ 5.times do
74
+ status, headers, body = app.call env
75
+ status.should.equal 200
76
+ body.should.equal 'cookies'
77
+ end
78
+
79
+ # Log should reflect block and release
80
+ log.string.should.match(/deflect.*: blocked 111\.111\.111\.111\ndeflect.*: released 111\.111\.111\.111\n/)
81
+ end
82
+
83
+ specify "should allow whitelisting of remote addresses" do
84
+ app = mock_deflect :whitelist => [@mock_addr_1], :request_threshold => 5, :interval => 2
85
+ env = mock_env @mock_addr_1
86
+
87
+ # Whitelisted addresses are always fine
88
+ 10.times do
89
+ status, headers, body = app.call env
90
+ status.should.equal 200
91
+ body.should.equal 'cookies'
92
+ end
93
+ end
94
+
95
+ specify "should allow blacklisting of remote addresses" do
96
+ app = mock_deflect :blacklist => [@mock_addr_2]
97
+
98
+ status, headers, body = app.call mock_env(@mock_addr_1)
99
+ status.should.equal 200
100
+ body.should.equal 'cookies'
101
+
102
+ status, headers, body = app.call mock_env(@mock_addr_2)
103
+ status.should.equal 403
104
+ body.should.equal ''
105
+ end
106
+
107
+ end