corntrace-rack-contrib 1.0.2

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 (72) hide show
  1. data/AUTHORS +26 -0
  2. data/COPYING +18 -0
  3. data/README.rdoc +87 -0
  4. data/Rakefile +89 -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/expectation_cascade.rb +32 -0
  16. data/lib/rack/contrib/garbagecollector.rb +14 -0
  17. data/lib/rack/contrib/host_meta.rb +47 -0
  18. data/lib/rack/contrib/jsonp.rb +78 -0
  19. data/lib/rack/contrib/lighttpd_script_name_fix.rb +16 -0
  20. data/lib/rack/contrib/locale.rb +31 -0
  21. data/lib/rack/contrib/mailexceptions.rb +120 -0
  22. data/lib/rack/contrib/nested_params.rb +143 -0
  23. data/lib/rack/contrib/not_found.rb +18 -0
  24. data/lib/rack/contrib/post_body_content_type_parser.rb +40 -0
  25. data/lib/rack/contrib/proctitle.rb +30 -0
  26. data/lib/rack/contrib/profiler.rb +108 -0
  27. data/lib/rack/contrib/relative_redirect.rb +44 -0
  28. data/lib/rack/contrib/response_cache.rb +59 -0
  29. data/lib/rack/contrib/response_headers.rb +24 -0
  30. data/lib/rack/contrib/route_exceptions.rb +49 -0
  31. data/lib/rack/contrib/runtime.rb +31 -0
  32. data/lib/rack/contrib/sendfile.rb +142 -0
  33. data/lib/rack/contrib/signals.rb +63 -0
  34. data/lib/rack/contrib/simple_endpoint.rb +81 -0
  35. data/lib/rack/contrib/static_cache.rb +93 -0
  36. data/lib/rack/contrib/time_zone.rb +25 -0
  37. data/lib/rack/contrib.rb +39 -0
  38. data/rack-contrib.gemspec +104 -0
  39. data/test/404.html +1 -0
  40. data/test/Maintenance.html +1 -0
  41. data/test/documents/test +1 -0
  42. data/test/mail_settings.rb +12 -0
  43. data/test/spec_rack_accept_format.rb +72 -0
  44. data/test/spec_rack_access.rb +154 -0
  45. data/test/spec_rack_backstage.rb +26 -0
  46. data/test/spec_rack_callbacks.rb +65 -0
  47. data/test/spec_rack_config.rb +22 -0
  48. data/test/spec_rack_contrib.rb +8 -0
  49. data/test/spec_rack_cookies.rb +56 -0
  50. data/test/spec_rack_csshttprequest.rb +66 -0
  51. data/test/spec_rack_deflect.rb +107 -0
  52. data/test/spec_rack_evil.rb +19 -0
  53. data/test/spec_rack_expectation_cascade.rb +72 -0
  54. data/test/spec_rack_garbagecollector.rb +13 -0
  55. data/test/spec_rack_host_meta.rb +50 -0
  56. data/test/spec_rack_jsonp.rb +83 -0
  57. data/test/spec_rack_lighttpd_script_name_fix.rb +16 -0
  58. data/test/spec_rack_mailexceptions.rb +97 -0
  59. data/test/spec_rack_nested_params.rb +46 -0
  60. data/test/spec_rack_not_found.rb +17 -0
  61. data/test/spec_rack_post_body_content_type_parser.rb +32 -0
  62. data/test/spec_rack_proctitle.rb +26 -0
  63. data/test/spec_rack_profiler.rb +37 -0
  64. data/test/spec_rack_relative_redirect.rb +78 -0
  65. data/test/spec_rack_response_cache.rb +137 -0
  66. data/test/spec_rack_response_headers.rb +35 -0
  67. data/test/spec_rack_runtime.rb +35 -0
  68. data/test/spec_rack_sendfile.rb +86 -0
  69. data/test/spec_rack_simple_endpoint.rb +95 -0
  70. data/test/spec_rack_static_cache.rb +91 -0
  71. data/test/statics/test +1 -0
  72. metadata +234 -0
@@ -0,0 +1,44 @@
1
+ require 'rack'
2
+
3
+ # Rack::RelativeRedirect is a simple middleware that converts relative paths in
4
+ # redirects in absolute urls, so they conform to RFC2616. It allows the user to
5
+ # specify the absolute path to use (with a sensible default), and handles
6
+ # relative paths (those that don't start with a slash) as well.
7
+ class Rack::RelativeRedirect
8
+ SCHEME_MAP = {'http'=>'80', 'https'=>'443'}
9
+ # The default proc used if a block is not provided to .new
10
+ # Just uses the url scheme of the request and the server name.
11
+ DEFAULT_ABSOLUTE_PROC = proc do |env, res|
12
+ port = env['SERVER_PORT']
13
+ scheme = env['rack.url_scheme']
14
+ "#{scheme}://#{env['SERVER_NAME']}#{":#{port}" unless SCHEME_MAP[scheme] == port}"
15
+ end
16
+
17
+ # Initialize a new RelativeRedirect object with the given arguments. Arguments:
18
+ # * app : The next middleware in the chain. This is always called.
19
+ # * &block : If provided, it is called with the environment and the response
20
+ # from the next middleware. It should return a string representing the scheme
21
+ # and server name (such as 'http://example.org').
22
+ def initialize(app, &block)
23
+ @app = app
24
+ @absolute_proc = block || DEFAULT_ABSOLUTE_PROC
25
+ end
26
+
27
+ # Call the next middleware with the environment. If the request was a
28
+ # redirect (response status 301, 302, or 303), and the location header does
29
+ # not start with an http or https url scheme, call the block provided by new
30
+ # and use that to make the Location header an absolute url. If the Location
31
+ # does not start with a slash, make location relative to the path requested.
32
+ def call(env)
33
+ res = @app.call(env)
34
+ if [301,302,303].include?(res[0]) and loc = res[1]['Location'] and !%r{\Ahttps?://}o.match(loc)
35
+ absolute = @absolute_proc.call(env, res)
36
+ res[1]['Location'] = if %r{\A/}.match(loc)
37
+ "#{absolute}#{loc}"
38
+ else
39
+ "#{absolute}#{File.dirname(Rack::Utils.unescape(env['PATH_INFO']))}/#{loc}"
40
+ end
41
+ end
42
+ res
43
+ end
44
+ end
@@ -0,0 +1,59 @@
1
+ require 'fileutils'
2
+ require 'rack'
3
+
4
+ # Rack::ResponseCache is a Rack middleware that caches responses for successful
5
+ # GET requests with no query string to disk or any ruby object that has an
6
+ # []= method (so it works with memcached). When caching to disk, it works similar to
7
+ # Rails' page caching, allowing you to cache dynamic pages to static files that can
8
+ # be served directly by a front end webserver.
9
+ class Rack::ResponseCache
10
+ # The default proc used if a block is not provided to .new
11
+ # It unescapes the PATH_INFO of the environment, and makes sure that it doesn't
12
+ # include '..'. If the Content-Type of the response is text/(html|css|xml),
13
+ # return a path with the appropriate extension (.html, .css, or .xml).
14
+ # If the path ends with a / and the Content-Type is text/html, change the basename
15
+ # of the path to index.html.
16
+ DEFAULT_PATH_PROC = proc do |env, res|
17
+ path = Rack::Utils.unescape(env['PATH_INFO'])
18
+ if !path.include?('..') and match = /text\/((?:x|ht)ml|css)/o.match(res[1]['Content-Type'])
19
+ type = match[1]
20
+ path = "#{path}.#{type}" unless /\.#{type}\z/.match(path)
21
+ path = File.join(File.dirname(path), 'index.html') if type == 'html' and File.basename(path) == '.html'
22
+ path
23
+ end
24
+ end
25
+
26
+ # Initialize a new ReponseCache object with the given arguments. Arguments:
27
+ # * app : The next middleware in the chain. This is always called.
28
+ # * cache : The place to cache responses. If a string is provided, a disk
29
+ # cache is used, and all cached files will use this directory as the root directory.
30
+ # If anything other than a string is provided, it should respond to []=, which will
31
+ # be called with a path string and a body value (the 3rd element of the response).
32
+ # * &block : If provided, it is called with the environment and the response from the next middleware.
33
+ # It should return nil or false if the path should not be cached, and should return
34
+ # the pathname to use as a string if the result should be cached.
35
+ # If not provided, the DEFAULT_PATH_PROC is used.
36
+ def initialize(app, cache, &block)
37
+ @app = app
38
+ @cache = cache
39
+ @path_proc = block || DEFAULT_PATH_PROC
40
+ end
41
+
42
+ # Call the next middleware with the environment. If the request was successful (response status 200),
43
+ # was a GET request, and had an empty query string, call the block set up in initialize to get the path.
44
+ # If the cache is a string, create any necessary middle directories, and cache the file in the appropriate
45
+ # subdirectory of cache. Otherwise, cache the body of the reponse as the value with the path as the key.
46
+ def call(env)
47
+ res = @app.call(env)
48
+ if env['REQUEST_METHOD'] == 'GET' and env['QUERY_STRING'] == '' and res[0] == 200 and path = @path_proc.call(env, res)
49
+ if @cache.is_a?(String)
50
+ path = File.join(@cache, path) if @cache
51
+ FileUtils.mkdir_p(File.dirname(path))
52
+ File.open(path, 'wb'){|f| res[2].each{|c| f.write(c)}}
53
+ else
54
+ @cache[path] = res[2]
55
+ end
56
+ end
57
+ res
58
+ end
59
+ end
@@ -0,0 +1,24 @@
1
+ module Rack
2
+ # Allows you to tap into the response headers. Yields a Rack::Utils::HeaderHash
3
+ # of current response headers to the block. Example:
4
+ #
5
+ # use Rack::ResponseHeaders do |headers|
6
+ # headers['X-Foo'] = 'bar'
7
+ # headers.delete('X-Baz')
8
+ # end
9
+ #
10
+ class ResponseHeaders
11
+ def initialize(app, &block)
12
+ @app = app
13
+ @block = block
14
+ end
15
+
16
+ def call(env)
17
+ response = @app.call(env)
18
+ headers = Utils::HeaderHash.new(response[1])
19
+ @block.call(headers)
20
+ response[1] = headers
21
+ response
22
+ end
23
+ end
24
+ end
@@ -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,31 @@
1
+
2
+ module Rack
3
+ # Sets an "X-Runtime" response header, indicating the response
4
+ # time of the request, in seconds
5
+ #
6
+ # You can put it right before the application to see the processing
7
+ # time, or before all the other middlewares to include time for them,
8
+ # too.
9
+ class Runtime
10
+ def initialize(app, name = nil)
11
+ @app = app
12
+ @header_name = "X-Runtime"
13
+ @header_name << "-#{name}" if name
14
+ end
15
+
16
+ def call(env)
17
+ start_time = Time.now
18
+ status, headers, body = @app.call(env)
19
+ request_time = Time.now - start_time
20
+
21
+ if !headers.has_key?(@header_name)
22
+ headers[@header_name] = "%0.6f" % request_time
23
+ end
24
+
25
+ [status, headers, body]
26
+ end
27
+ end
28
+ end
29
+
30
+
31
+
@@ -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,81 @@
1
+ module Rack
2
+ # Create simple endpoints with routing rules, similar to Sinatra actions.
3
+ #
4
+ # Simplest example:
5
+ #
6
+ # use Rack::SimpleEndpoint, '/ping_monitor' do
7
+ # 'pong'
8
+ # end
9
+ #
10
+ # The value returned from the block will be written to the response body, so
11
+ # the above example will return "pong" when the request path is /ping_monitor.
12
+ #
13
+ # HTTP verb requirements can optionally be specified:
14
+ #
15
+ # use Rack::SimpleEndpoint, '/foo' => :get do
16
+ # 'only GET requests will match'
17
+ # end
18
+ #
19
+ # use Rack::SimpleEndpoint, '/bar' => [:get, :post] do
20
+ # 'only GET and POST requests will match'
21
+ # end
22
+ #
23
+ # Rack::Request and Rack::Response objects are yielded to block:
24
+ #
25
+ # use Rack::SimpleEndpoint, '/json' do |req, res|
26
+ # res['Content-Type'] = 'application/json'
27
+ # %({"foo": "#{req[:foo]}"})
28
+ # end
29
+ #
30
+ # When path is a Regexp, match data object is yielded as third argument to block
31
+ #
32
+ # use Rack::SimpleEndpoint, %r{^/(john|paul|george|ringo)} do |req, res, match|
33
+ # "Hello, #{match[1]}"
34
+ # end
35
+ #
36
+ # A :pass symbol returned from block will not return a response; control will continue down the
37
+ # Rack stack:
38
+ #
39
+ # use Rack::SimpleEndpoint, '/api_key' do |req, res|
40
+ # req.env['myapp.user'].authorized? ? '12345' : :pass
41
+ # end
42
+ #
43
+ # # Unauthorized access to /api_key will be handled by PublicApp
44
+ # run PublicApp
45
+ class SimpleEndpoint
46
+ def initialize(app, arg, &block)
47
+ @app = app
48
+ @path = extract_path(arg)
49
+ @verbs = extract_verbs(arg)
50
+ @block = block
51
+ end
52
+
53
+ def call(env)
54
+ match = match_path(env['PATH_INFO'])
55
+ if match && valid_method?(env['REQUEST_METHOD'])
56
+ req, res = Request.new(env), Response.new
57
+ body = @block.call(req, res, (match unless match == true))
58
+ body == :pass ? @app.call(env) : (res.write(body); res.finish)
59
+ else
60
+ @app.call(env)
61
+ end
62
+ end
63
+
64
+ private
65
+ def extract_path(arg)
66
+ arg.is_a?(Hash) ? arg.keys.first : arg
67
+ end
68
+
69
+ def extract_verbs(arg)
70
+ arg.is_a?(Hash) ? [arg.values.first].flatten.map {|verb| verb.to_s.upcase} : []
71
+ end
72
+
73
+ def match_path(path)
74
+ @path.is_a?(Regexp) ? @path.match(path.to_s) : @path == path.to_s
75
+ end
76
+
77
+ def valid_method?(method)
78
+ @verbs.empty? || @verbs.include?(method)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,93 @@
1
+ module Rack
2
+
3
+ #
4
+ # The Rack::StaticCache middleware automatically adds, removes and modifies
5
+ # stuffs in response headers to facilitiate client and proxy caching for static files
6
+ # that minimizes http requests and improves overall load times for second time visitors.
7
+ #
8
+ # Once a static content is stored in a client/proxy the only way to enforce the browser
9
+ # to fetch the latest content and ignore the cache is to rename the static file.
10
+ #
11
+ # Alternatively, we can add a version number into the URL to the content to bypass
12
+ # the caches. Rack::StaticCache by default handles version numbers in the filename.
13
+ # As an example,
14
+ # http://yoursite.com/images/test-1.0.0.png and http://yoursite.com/images/test-2.0.0.png
15
+ # both reffers to the same image file http://yoursite.com/images/test.png
16
+ #
17
+ # Another way to bypass the cache is adding the version number in a field-value pair in the
18
+ # URL query string. As an example, http://yoursite.com/images/test.png?v=1.0.0
19
+ # In that case, set the option :versioning to false to avoid unneccessary regexp calculations.
20
+ #
21
+ # It's better to keep the current version number in some config file and use it in every static
22
+ # content's URL. So each time we modify our static contents, we just have to change the version
23
+ # number to enforce the browser to fetch the latest content.
24
+ #
25
+ # You can use Rack::Deflater along with Rack::StaticCache for further improvements in page loading time.
26
+ #
27
+ # Examples:
28
+ # use Rack::StaticCache, :urls => ["/images", "/css", "/js", "/documents*"], :root => "statics"
29
+ # will serve all requests beginning with /images, /csss or /js from the
30
+ # directory "statics/images", "statics/css", "statics/js".
31
+ # All the files from these directories will have modified headers to enable client/proxy caching,
32
+ # except the files from the directory "documents". Append a * (star) at the end of the pattern
33
+ # if you want to disable caching for any pattern . In that case, plain static contents will be served with
34
+ # default headers.
35
+ #
36
+ # use Rack::StaticCache, :urls => ["/images"], :duration => 2, :versioning => false
37
+ # will serve all requests begining with /images under the current directory (default for the option :root
38
+ # is current directory). All the contents served will have cache expiration duration set to 2 years in headers
39
+ # (default for :duration is 1 year), and StaticCache will not compute any versioning logics (default for
40
+ # :versioning is true)
41
+ #
42
+
43
+
44
+ class StaticCache
45
+
46
+ def initialize(app, options={})
47
+ @app = app
48
+ @urls = options[:urls]
49
+ @no_cache = {}
50
+ @urls.collect! do |url|
51
+ if url =~ /\*$/
52
+ url.sub!(/\*$/, '')
53
+ @no_cache[url] = 1
54
+ end
55
+ url
56
+ end
57
+ root = options[:root] || Dir.pwd
58
+ @file_server = Rack::File.new(root)
59
+ @cache_duration = options[:duration] || 1
60
+ @versioning_enabled = true
61
+ @versioning_enabled = options[:versioning] unless options[:versioning].nil?
62
+ @duration_in_seconds = self.duration_in_seconds
63
+ @duration_in_words = self.duration_in_words
64
+ end
65
+
66
+ def call(env)
67
+ path = env["PATH_INFO"]
68
+ url = @urls.detect{ |u| path.index(u) == 0 }
69
+ unless url.nil?
70
+ path.sub!(/-[\d.]+([.][a-zA-Z][\w]+)?$/, '\1') if @versioning_enabled
71
+ status, headers, body = @file_server.call(env)
72
+ if @no_cache[url].nil?
73
+ headers['Cache-Control'] ="max-age=#{@duration_in_seconds}, public"
74
+ headers['Expires'] = @duration_in_words
75
+ headers.delete 'Etag'
76
+ headers.delete 'Pragma'
77
+ headers.delete 'Last-Modified'
78
+ end
79
+ [status, headers, body]
80
+ else
81
+ @app.call(env)
82
+ end
83
+ end
84
+
85
+ def duration_in_words
86
+ (Time.now + self.duration_in_seconds).strftime '%a, %d %b %Y %H:%M:%S GMT'
87
+ end
88
+
89
+ def duration_in_seconds
90
+ 60 * 60 * 24 * 365 * @cache_duration
91
+ end
92
+ end
93
+ 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,39 @@
1
+ require 'rack'
2
+
3
+ module Rack
4
+ module Contrib
5
+ def self.release
6
+ "1.0.2"
7
+ end
8
+ end
9
+
10
+ autoload :AcceptFormat, "rack/contrib/accept_format"
11
+ autoload :Access, "rack/contrib/access"
12
+ autoload :BounceFavicon, "rack/contrib/bounce_favicon"
13
+ autoload :Cookies, "rack/contrib/cookies"
14
+ autoload :CSSHTTPRequest, "rack/contrib/csshttprequest"
15
+ autoload :Deflect, "rack/contrib/deflect"
16
+ autoload :ExpectationCascade, "rack/contrib/expectation_cascade"
17
+ autoload :GarbageCollector, "rack/contrib/garbagecollector"
18
+ autoload :JSONP, "rack/contrib/jsonp"
19
+ autoload :LighttpdScriptNameFix, "rack/contrib/lighttpd_script_name_fix"
20
+ autoload :Locale, "rack/contrib/locale"
21
+ autoload :MailExceptions, "rack/contrib/mailexceptions"
22
+ autoload :PostBodyContentTypeParser, "rack/contrib/post_body_content_type_parser"
23
+ autoload :ProcTitle, "rack/contrib/proctitle"
24
+ autoload :Profiler, "rack/contrib/profiler"
25
+ autoload :ResponseHeaders, "rack/contrib/response_headers"
26
+ autoload :Runtime, "rack/contrib/runtime"
27
+ autoload :Sendfile, "rack/contrib/sendfile"
28
+ autoload :Signals, "rack/contrib/signals"
29
+ autoload :SimpleEndpoint, "rack/contrib/simple_endpoint"
30
+ autoload :TimeZone, "rack/contrib/time_zone"
31
+ autoload :Evil, "rack/contrib/evil"
32
+ autoload :Callbacks, "rack/contrib/callbacks"
33
+ autoload :NestedParams, "rack/contrib/nested_params"
34
+ autoload :Config, "rack/contrib/config"
35
+ autoload :NotFound, "rack/contrib/not_found"
36
+ autoload :ResponseCache, "rack/contrib/response_cache"
37
+ autoload :RelativeRedirect, "rack/contrib/relative_redirect"
38
+ autoload :StaticCache, "rack/contrib/static_cache"
39
+ end