rack-contrib-with-working-jsonp 0.9.2.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 (56) hide show
  1. data/COPYING +18 -0
  2. data/README.rdoc +78 -0
  3. data/Rakefile +97 -0
  4. data/lib/rack/contrib.rb +37 -0
  5. data/lib/rack/contrib/accept_format.rb +46 -0
  6. data/lib/rack/contrib/backstage.rb +20 -0
  7. data/lib/rack/contrib/bounce_favicon.rb +16 -0
  8. data/lib/rack/contrib/callbacks.rb +37 -0
  9. data/lib/rack/contrib/config.rb +16 -0
  10. data/lib/rack/contrib/cookies.rb +50 -0
  11. data/lib/rack/contrib/csshttprequest.rb +39 -0
  12. data/lib/rack/contrib/deflect.rb +137 -0
  13. data/lib/rack/contrib/etag.rb +20 -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_backstage.rb +26 -0
  37. data/test/spec_rack_callbacks.rb +65 -0
  38. data/test/spec_rack_config.rb +22 -0
  39. data/test/spec_rack_contrib.rb +8 -0
  40. data/test/spec_rack_csshttprequest.rb +66 -0
  41. data/test/spec_rack_deflect.rb +107 -0
  42. data/test/spec_rack_etag.rb +23 -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 +37 -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,18 @@
1
+ module Rack
2
+ # Rack::NotFound is a default endpoint. Initialize with the path to
3
+ # your 404 page.
4
+
5
+ class NotFound
6
+ F = ::File
7
+
8
+ def initialize(path)
9
+ file = F.expand_path(path)
10
+ @content = F.read(file)
11
+ @length = @content.size.to_s
12
+ end
13
+
14
+ def call(env)
15
+ [404, {'Content-Type' => 'text/html', 'Content-Length' => @length}, [@content]]
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,40 @@
1
+ begin
2
+ require 'json'
3
+ rescue LoadError => e
4
+ require 'json/pure'
5
+ end
6
+
7
+ module Rack
8
+
9
+ # A Rack middleware for parsing POST/PUT body data when Content-Type is
10
+ # not one of the standard supported types, like <tt>application/json</tt>.
11
+ #
12
+ # TODO: Find a better name.
13
+ #
14
+ class PostBodyContentTypeParser
15
+
16
+ # Constants
17
+ #
18
+ CONTENT_TYPE = 'CONTENT_TYPE'.freeze
19
+ POST_BODY = 'rack.input'.freeze
20
+ FORM_INPUT = 'rack.request.form_input'.freeze
21
+ FORM_HASH = 'rack.request.form_hash'.freeze
22
+
23
+ # Supported Content-Types
24
+ #
25
+ APPLICATION_JSON = 'application/json'.freeze
26
+
27
+ def initialize(app)
28
+ @app = app
29
+ end
30
+
31
+ def call(env)
32
+ case env[CONTENT_TYPE]
33
+ when APPLICATION_JSON
34
+ env.update(FORM_HASH => JSON.parse(env[POST_BODY].read), FORM_INPUT => env[POST_BODY])
35
+ end
36
+ @app.call(env)
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,30 @@
1
+ module Rack
2
+ # Middleware to update the process title ($0) with information about the
3
+ # current request. Based loosely on:
4
+ # - http://purefiction.net/mongrel_proctitle/
5
+ # - http://github.com/grempe/thin-proctitle/tree/master
6
+ #
7
+ # NOTE: This will not work properly in a multi-threaded environment.
8
+ class ProcTitle
9
+ F = ::File
10
+ PROGNAME = F.basename($0)
11
+
12
+ def initialize(app)
13
+ @app = app
14
+ @appname = Dir.pwd.split('/').reverse.
15
+ find { |name| name !~ /^(\d+|current|releases)$/ } || PROGNAME
16
+ @requests = 0
17
+ $0 = "#{PROGNAME} [#{@appname}] init ..."
18
+ end
19
+
20
+ def call(env)
21
+ host, port = env['SERVER_NAME'], env['SERVER_PORT']
22
+ meth, path = env['REQUEST_METHOD'], env['PATH_INFO']
23
+ @requests += 1
24
+ $0 = "#{PROGNAME} [#{@appname}/#{port}] (#{@requests}) " \
25
+ "#{meth} #{path}"
26
+
27
+ @app.call(env)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,108 @@
1
+ require 'ruby-prof'
2
+
3
+ module Rack
4
+ # Set the profile=process_time query parameter to download a
5
+ # calltree profile of the request.
6
+ #
7
+ # Pass the :printer option to pick a different result format.
8
+ class Profiler
9
+ MODES = %w(
10
+ process_time
11
+ wall_time
12
+ cpu_time
13
+ allocations
14
+ memory
15
+ gc_runs
16
+ gc_time
17
+ )
18
+
19
+ DEFAULT_PRINTER = RubyProf::CallTreePrinter
20
+ DEFAULT_CONTENT_TYPE = 'application/octet-stream'
21
+
22
+ PRINTER_CONTENT_TYPE = {
23
+ RubyProf::FlatPrinter => 'text/plain',
24
+ RubyProf::GraphPrinter => 'text/plain',
25
+ RubyProf::GraphHtmlPrinter => 'text/html'
26
+ }
27
+
28
+ # Accepts a :printer => [:call_tree|:graph_html|:graph|:flat] option
29
+ # defaulting to :call_tree.
30
+ def initialize(app, options = {})
31
+ @app = app
32
+ @printer = parse_printer(options[:printer])
33
+ @times = (options[:times] || 1).to_i
34
+ end
35
+
36
+ def call(env)
37
+ if mode = profiling?(env)
38
+ profile(env, mode)
39
+ else
40
+ @app.call(env)
41
+ end
42
+ end
43
+
44
+ private
45
+ def profiling?(env)
46
+ unless RubyProf.running?
47
+ request = Rack::Request.new(env)
48
+ if mode = request.params.delete('profile')
49
+ if RubyProf.const_defined?(mode.upcase)
50
+ mode
51
+ else
52
+ env['rack.errors'].write "Invalid RubyProf measure_mode: " +
53
+ "#{mode}. Use one of #{MODES.to_a.join(', ')}"
54
+ false
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ def profile(env, mode)
61
+ RubyProf.measure_mode = RubyProf.const_get(mode.upcase)
62
+
63
+ GC.enable_stats if GC.respond_to?(:enable_stats)
64
+ result = RubyProf.profile do
65
+ @times.times { @app.call(env) }
66
+ end
67
+ GC.disable_stats if GC.respond_to?(:disable_stats)
68
+
69
+ [200, headers(@printer, env, mode), print(@printer, result)]
70
+ end
71
+
72
+ def print(printer, result)
73
+ body = StringIO.new
74
+ printer.new(result).print(body, :min_percent => 0.01)
75
+ body.rewind
76
+ body
77
+ end
78
+
79
+ def headers(printer, env, mode)
80
+ headers = { 'Content-Type' => PRINTER_CONTENT_TYPE[printer] || DEFAULT_CONTENT_TYPE }
81
+ if printer == RubyProf::CallTreePrinter
82
+ filename = ::File.basename(env['PATH_INFO'])
83
+ headers['Content-Disposition'] =
84
+ %(attachment; filename="#{filename}.#{mode}.tree")
85
+ end
86
+ headers
87
+ end
88
+
89
+ def parse_printer(printer)
90
+ if printer.nil?
91
+ DEFAULT_PRINTER
92
+ elsif printer.is_a?(Class)
93
+ printer
94
+ else
95
+ name = "#{camel_case(printer)}Printer"
96
+ if RubyProf.const_defined?(name)
97
+ RubyProf.const_get(name)
98
+ else
99
+ DEFAULT_PRINTER
100
+ end
101
+ end
102
+ end
103
+
104
+ def camel_case(word)
105
+ word.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }
106
+ end
107
+ end
108
+ end
@@ -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,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