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,49 @@
1
+ module Rack
2
+ class RouteExceptions
3
+ ROUTES = [
4
+ [Exception, '/error/internal']
5
+ ]
6
+
7
+ PATH_INFO = 'rack.route_exceptions.path_info'.freeze
8
+ EXCEPTION = 'rack.route_exceptions.exception'.freeze
9
+ RETURNED = 'rack.route_exceptions.returned'.freeze
10
+
11
+ class << self
12
+ def route(exception, to)
13
+ ROUTES.delete_if{|k,v| k == exception }
14
+ ROUTES << [exception, to]
15
+ end
16
+
17
+ alias []= route
18
+ end
19
+
20
+ def initialize(app)
21
+ @app = app
22
+ end
23
+
24
+ def call(env, try_again = true)
25
+ returned = @app.call(env)
26
+ rescue Exception => exception
27
+ raise(exception) unless try_again
28
+
29
+ ROUTES.each do |klass, to|
30
+ next unless klass === exception
31
+ return route(to, env, returned, exception)
32
+ end
33
+
34
+ raise(exception)
35
+ end
36
+
37
+ def route(to, env, returned, exception)
38
+ env.merge!(
39
+ PATH_INFO => env['PATH_INFO'],
40
+ EXCEPTION => exception,
41
+ RETURNED => returned
42
+ )
43
+
44
+ env['PATH_INFO'] = to
45
+
46
+ call(env, try_again = false)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,142 @@
1
+ require 'rack/file'
2
+
3
+ module Rack
4
+ class File #:nodoc:
5
+ alias :to_path :path
6
+ end
7
+
8
+ # = Sendfile
9
+ #
10
+ # The Sendfile middleware intercepts responses whose body is being
11
+ # served from a file and replaces it with a server specific X-Sendfile
12
+ # header. The web server is then responsible for writing the file contents
13
+ # to the client. This can dramatically reduce the amount of work required
14
+ # by the Ruby backend and takes advantage of the web servers optimized file
15
+ # delivery code.
16
+ #
17
+ # In order to take advantage of this middleware, the response body must
18
+ # respond to +to_path+ and the request must include an X-Sendfile-Type
19
+ # header. Rack::File and other components implement +to_path+ so there's
20
+ # rarely anything you need to do in your application. The X-Sendfile-Type
21
+ # header is typically set in your web servers configuration. The following
22
+ # sections attempt to document
23
+ #
24
+ # === Nginx
25
+ #
26
+ # Nginx supports the X-Accel-Redirect header. This is similar to X-Sendfile
27
+ # but requires parts of the filesystem to be mapped into a private URL
28
+ # hierarachy.
29
+ #
30
+ # The following example shows the Nginx configuration required to create
31
+ # a private "/files/" area, enable X-Accel-Redirect, and pass the special
32
+ # X-Sendfile-Type and X-Accel-Mapping headers to the backend:
33
+ #
34
+ # location /files/ {
35
+ # internal;
36
+ # alias /var/www/;
37
+ # }
38
+ #
39
+ # location / {
40
+ # proxy_redirect false;
41
+ #
42
+ # proxy_set_header Host $host;
43
+ # proxy_set_header X-Real-IP $remote_addr;
44
+ # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
45
+ #
46
+ # proxy_set_header X-Sendfile-Type X-Accel-Redirect
47
+ # proxy_set_header X-Accel-Mapping /files/=/var/www/;
48
+ #
49
+ # proxy_pass http://127.0.0.1:8080/;
50
+ # }
51
+ #
52
+ # Note that the X-Sendfile-Type header must be set exactly as shown above. The
53
+ # X-Accel-Mapping header should specify the name of the private URL pattern,
54
+ # followed by an equals sign (=), followed by the location on the file system
55
+ # that it maps to. The middleware performs a simple substitution on the
56
+ # resulting path.
57
+ #
58
+ # See Also: http://wiki.codemongers.com/NginxXSendfile
59
+ #
60
+ # === lighttpd
61
+ #
62
+ # Lighttpd has supported some variation of the X-Sendfile header for some
63
+ # time, although only recent version support X-Sendfile in a reverse proxy
64
+ # configuration.
65
+ #
66
+ # $HTTP["host"] == "example.com" {
67
+ # proxy-core.protocol = "http"
68
+ # proxy-core.balancer = "round-robin"
69
+ # proxy-core.backends = (
70
+ # "127.0.0.1:8000",
71
+ # "127.0.0.1:8001",
72
+ # ...
73
+ # )
74
+ #
75
+ # proxy-core.allow-x-sendfile = "enable"
76
+ # proxy-core.rewrite-request = (
77
+ # "X-Sendfile-Type" => (".*" => "X-Sendfile")
78
+ # )
79
+ # }
80
+ #
81
+ # See Also: http://redmine.lighttpd.net/wiki/lighttpd/Docs:ModProxyCore
82
+ #
83
+ # === Apache
84
+ #
85
+ # X-Sendfile is supported under Apache 2.x using a separate module:
86
+ #
87
+ # http://tn123.ath.cx/mod_xsendfile/
88
+ #
89
+ # Once the module is compiled and installed, you can enable it using
90
+ # XSendFile config directive:
91
+ #
92
+ # RequestHeader Set X-Sendfile-Type X-Sendfile
93
+ # ProxyPassReverse / http://localhost:8001/
94
+ # XSendFile on
95
+
96
+ class Sendfile
97
+ F = ::File
98
+
99
+ def initialize(app, variation=nil)
100
+ @app = app
101
+ @variation = variation
102
+ end
103
+
104
+ def call(env)
105
+ status, headers, body = @app.call(env)
106
+ if body.respond_to?(:to_path)
107
+ case type = variation(env)
108
+ when 'X-Accel-Redirect'
109
+ path = F.expand_path(body.to_path)
110
+ if url = map_accel_path(env, path)
111
+ headers[type] = url
112
+ body = []
113
+ else
114
+ env['rack.errors'] << "X-Accel-Mapping header missing"
115
+ end
116
+ when 'X-Sendfile', 'X-Lighttpd-Send-File'
117
+ path = F.expand_path(body.to_path)
118
+ headers[type] = path
119
+ body = []
120
+ when '', nil
121
+ else
122
+ env['rack.errors'] << "Unknown x-sendfile variation: '#{variation}'.\n"
123
+ end
124
+ end
125
+ [status, headers, body]
126
+ end
127
+
128
+ private
129
+ def variation(env)
130
+ @variation ||
131
+ env['sendfile.type'] ||
132
+ env['HTTP_X_SENDFILE_TYPE']
133
+ end
134
+
135
+ def map_accel_path(env, file)
136
+ if mapping = env['HTTP_X_ACCEL_MAPPING']
137
+ internal, external = mapping.split('=', 2).map{ |p| p.strip }
138
+ file.sub(/^#{internal}/i, external)
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,63 @@
1
+ module Rack
2
+ # Installs signal handlers that are safely processed after a request
3
+ #
4
+ # NOTE: This middleware should not be used in a threaded environment
5
+ #
6
+ # use Rack::Signals.new do
7
+ # trap 'INT', lambda {
8
+ # puts "Exiting now"
9
+ # exit
10
+ # }
11
+ #
12
+ # trap_when_ready 'USR1', lambda {
13
+ # puts "Exiting when ready"
14
+ # exit
15
+ # }
16
+ # end
17
+ class Signals
18
+ class BodyWithCallback
19
+ def initialize(body, callback)
20
+ @body, @callback = body, callback
21
+ end
22
+
23
+ def each(&block)
24
+ @body.each(&block)
25
+ @callback.call
26
+ end
27
+ end
28
+
29
+ def initialize(app, &block)
30
+ @app = app
31
+ @processing = false
32
+ @when_ready = nil
33
+ instance_eval(&block)
34
+ end
35
+
36
+ def call(env)
37
+ begin
38
+ @processing, @when_ready = true, nil
39
+ status, headers, body = @app.call(env)
40
+
41
+ if handler = @when_ready
42
+ body = BodyWithCallback.new(body, handler)
43
+ @when_ready = nil
44
+ end
45
+ ensure
46
+ @processing = false
47
+ end
48
+
49
+ [status, headers, body]
50
+ end
51
+
52
+ def trap_when_ready(signal, handler)
53
+ when_ready_handler = lambda { |signal|
54
+ if @processing
55
+ @when_ready = lambda { handler.call(signal) }
56
+ else
57
+ handler.call(signal)
58
+ end
59
+ }
60
+ trap(signal, when_ready_handler)
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,25 @@
1
+ module Rack
2
+ class TimeZone
3
+ Javascript = <<-EOJ
4
+ function setTimezoneCookie() {
5
+ var offset = (new Date()).getTimezoneOffset()
6
+ var date = new Date();
7
+ date.setTime(date.getTime()+3600000);
8
+ document.cookie = "utc_offset="+offset+"; expires="+date.toGMTString();+"; path=/";
9
+ }
10
+ EOJ
11
+
12
+ def initialize(app)
13
+ @app = app
14
+ end
15
+
16
+ def call(env)
17
+ request = Rack::Request.new(env)
18
+ if utc_offset = request.cookies["utc_offset"]
19
+ env["rack.timezone.utc_offset"] = -(utc_offset.to_i * 60)
20
+ end
21
+
22
+ @app.call(env)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,88 @@
1
+ Gem::Specification.new do |s|
2
+ s.specification_version = 2 if s.respond_to? :specification_version=
3
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
4
+
5
+ s.name = 'devver-rack-contrib'
6
+ s.version = '0.9.3'
7
+ s.date = '2010-01-10'
8
+
9
+ s.description = "The Devver fork of rack-contrib"
10
+ s.summary = "This gem is the Devver fork of rack-contrib (http://github.com/rack/rack-contrib) so we can play around with new features we're building. You should not use this version and use the official rack-contrib gem instead."
11
+
12
+ s.authors = ["rack-devel"]
13
+ s.email = "rack-devel@googlegroups.com"
14
+
15
+ # = MANIFEST =
16
+ s.files = %w[
17
+ COPYING
18
+ README.rdoc
19
+ Rakefile
20
+ lib/rack/contrib.rb
21
+ lib/rack/contrib/accept_format.rb
22
+ lib/rack/contrib/access.rb
23
+ lib/rack/contrib/backstage.rb
24
+ lib/rack/contrib/bounce_favicon.rb
25
+ lib/rack/contrib/callbacks.rb
26
+ lib/rack/contrib/config.rb
27
+ lib/rack/contrib/cookies.rb
28
+ lib/rack/contrib/csshttprequest.rb
29
+ lib/rack/contrib/deflect.rb
30
+ lib/rack/contrib/evil.rb
31
+ lib/rack/contrib/garbagecollector.rb
32
+ lib/rack/contrib/jsonp.rb
33
+ lib/rack/contrib/lighttpd_script_name_fix.rb
34
+ lib/rack/contrib/locale.rb
35
+ lib/rack/contrib/mailexceptions.rb
36
+ lib/rack/contrib/nested_params.rb
37
+ lib/rack/contrib/not_found.rb
38
+ lib/rack/contrib/post_body_content_type_parser.rb
39
+ lib/rack/contrib/proctitle.rb
40
+ lib/rack/contrib/profiler.rb
41
+ lib/rack/contrib/relative_redirect.rb
42
+ lib/rack/contrib/response_cache.rb
43
+ lib/rack/contrib/route_exceptions.rb
44
+ lib/rack/contrib/sendfile.rb
45
+ lib/rack/contrib/signals.rb
46
+ lib/rack/contrib/time_zone.rb
47
+ rack-contrib.gemspec
48
+ test/404.html
49
+ test/Maintenance.html
50
+ test/mail_settings.rb
51
+ test/spec_rack_accept_format.rb
52
+ test/spec_rack_access.rb
53
+ test/spec_rack_backstage.rb
54
+ test/spec_rack_callbacks.rb
55
+ test/spec_rack_config.rb
56
+ test/spec_rack_contrib.rb
57
+ test/spec_rack_csshttprequest.rb
58
+ test/spec_rack_deflect.rb
59
+ test/spec_rack_evil.rb
60
+ test/spec_rack_garbagecollector.rb
61
+ test/spec_rack_jsonp.rb
62
+ test/spec_rack_lighttpd_script_name_fix.rb
63
+ test/spec_rack_mailexceptions.rb
64
+ test/spec_rack_nested_params.rb
65
+ test/spec_rack_not_found.rb
66
+ test/spec_rack_post_body_content_type_parser.rb
67
+ test/spec_rack_proctitle.rb
68
+ test/spec_rack_profiler.rb
69
+ test/spec_rack_relative_redirect.rb
70
+ test/spec_rack_response_cache.rb
71
+ test/spec_rack_sendfile.rb
72
+ ]
73
+ # = MANIFEST =
74
+
75
+ s.test_files = s.files.select {|path| path =~ /^test\/spec_.*\.rb/}
76
+
77
+ s.extra_rdoc_files = %w[README.rdoc COPYING]
78
+ s.add_dependency 'rack', '>= 0.9.1'
79
+ s.add_development_dependency 'test-spec', '~> 0.9.0'
80
+ s.add_development_dependency 'tmail', '>= 1.2'
81
+ s.add_development_dependency 'json', '>= 1.1'
82
+
83
+ s.has_rdoc = true
84
+ s.homepage = "http://github.com/rack/rack-contrib/"
85
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "rack-contrib", "--main", "README"]
86
+ s.require_paths = %w[lib]
87
+ s.rubygems_version = '1.1.1'
88
+ end
data/test/404.html ADDED
@@ -0,0 +1 @@
1
+ Not Found
@@ -0,0 +1 @@
1
+ Under maintenance.
@@ -0,0 +1,12 @@
1
+ TEST_SMTP = nil
2
+
3
+ # Enable SMTP tests by providing the following for your SMTP server.
4
+ #
5
+ # TEST_SMTP = {
6
+ # :server => 'localhost',
7
+ # :domain => 'localhost',
8
+ # :port => 25,
9
+ # :authentication => :login,
10
+ # :user_name => nil,
11
+ # :password => nil
12
+ # }
@@ -0,0 +1,72 @@
1
+ require 'test/spec'
2
+ require 'rack/mock'
3
+ require 'rack/contrib/accept_format'
4
+ require 'rack/mime'
5
+
6
+ context "Rack::AcceptFormat" do
7
+ app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, env['PATH_INFO']] }
8
+
9
+ specify "should do nothing when a format extension is already provided" do
10
+ request = Rack::MockRequest.env_for("/resource.json")
11
+ body = Rack::AcceptFormat.new(app).call(request).last
12
+ body.should == "/resource.json"
13
+ end
14
+
15
+ context "default extention" do
16
+ specify "should allow custom default" do
17
+ request = Rack::MockRequest.env_for("/resource")
18
+ body = Rack::AcceptFormat.new(app, '.xml').call(request).last
19
+ body.should == "/resource.xml"
20
+ end
21
+
22
+ specify "should default to html" do
23
+ request = Rack::MockRequest.env_for("/resource")
24
+ body = Rack::AcceptFormat.new(app).call(request).last
25
+ body.should == "/resource.html"
26
+ end
27
+
28
+ specify "should notmalize custom extention" do
29
+ request = Rack::MockRequest.env_for("/resource")
30
+
31
+ body = Rack::AcceptFormat.new(app,'xml').call(request).last #no dot prefix
32
+ body.should == "/resource.xml"
33
+
34
+ body = Rack::AcceptFormat.new(app, :xml).call(request).last
35
+ body.should == "/resource.xml"
36
+ end
37
+ end
38
+
39
+ context "there is no format extension" do
40
+ Rack::Mime::MIME_TYPES.clear
41
+
42
+ def mime(ext, type)
43
+ ext = ".#{ext}" unless ext.to_s[0] == ?.
44
+ Rack::Mime::MIME_TYPES[ext.to_s] = type
45
+ end
46
+
47
+ specify "should add the default extension if no Accept header" do
48
+ request = Rack::MockRequest.env_for("/resource")
49
+ body = Rack::AcceptFormat.new(app).call(request).last
50
+ body.should == "/resource.html"
51
+ end
52
+
53
+ specify "should add the default extension if the Accept header is not registered in the Mime::Types" do
54
+ request = Rack::MockRequest.env_for("/resource", 'HTTP_ACCEPT' => 'application/json;q=1.0, text/html;q=0.8, */*;q=0.1')
55
+ body = Rack::AcceptFormat.new(app).call(request).last
56
+ body.should == "/resource.html"
57
+ end
58
+
59
+ specify "should add the correct extension if the Accept header is registered in the Mime::Types" do
60
+ mime :json, 'application/json'
61
+ request = Rack::MockRequest.env_for("/resource", 'HTTP_ACCEPT' => 'application/json;q=1.0, text/html;q=0.8, */*;q=0.1')
62
+ body = Rack::AcceptFormat.new(app).call(request).last
63
+ body.should == "/resource.json"
64
+ end
65
+ end
66
+
67
+ specify "shouldn't confuse extention when there are dots in path" do
68
+ request = Rack::MockRequest.env_for("/parent.resource/resource")
69
+ body = Rack::AcceptFormat.new(app, '.html').call(request).last
70
+ body.should == "/parent.resource/resource.html"
71
+ end
72
+ end