rack-contrib 1.4.0 → 2.5.0

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 (43) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +27 -13
  3. data/lib/rack/contrib/access.rb +8 -6
  4. data/lib/rack/contrib/backstage.rb +5 -3
  5. data/lib/rack/contrib/bounce_favicon.rb +3 -1
  6. data/lib/rack/contrib/callbacks.rb +2 -0
  7. data/lib/rack/contrib/common_cookies.rb +19 -11
  8. data/lib/rack/contrib/config.rb +3 -15
  9. data/lib/rack/contrib/cookies.rb +2 -0
  10. data/lib/rack/contrib/csshttprequest.rb +12 -6
  11. data/lib/rack/contrib/deflect.rb +35 -33
  12. data/lib/rack/contrib/enforce_valid_encoding.rb +3 -1
  13. data/lib/rack/contrib/evil.rb +2 -0
  14. data/lib/rack/contrib/expectation_cascade.rb +5 -3
  15. data/lib/rack/contrib/garbagecollector.rb +2 -0
  16. data/lib/rack/contrib/host_meta.rb +3 -1
  17. data/lib/rack/contrib/json_body_parser.rb +94 -0
  18. data/lib/rack/contrib/jsonp.rb +21 -17
  19. data/lib/rack/contrib/lazy_conditional_get.rb +13 -4
  20. data/lib/rack/contrib/lighttpd_script_name_fix.rb +2 -0
  21. data/lib/rack/contrib/locale.rb +74 -22
  22. data/lib/rack/contrib/mailexceptions.rb +3 -1
  23. data/lib/rack/contrib/nested_params.rb +6 -115
  24. data/lib/rack/contrib/not_found.rb +22 -7
  25. data/lib/rack/contrib/post_body_content_type_parser.rb +54 -4
  26. data/lib/rack/contrib/printout.rb +3 -1
  27. data/lib/rack/contrib/proctitle.rb +2 -0
  28. data/lib/rack/contrib/profiler.rb +44 -17
  29. data/lib/rack/contrib/relative_redirect.rb +11 -5
  30. data/lib/rack/contrib/response_cache.rb +23 -11
  31. data/lib/rack/contrib/response_headers.rb +8 -2
  32. data/lib/rack/contrib/route_exceptions.rb +2 -0
  33. data/lib/rack/contrib/runtime.rb +3 -30
  34. data/lib/rack/contrib/signals.rb +6 -0
  35. data/lib/rack/contrib/simple_endpoint.rb +3 -1
  36. data/lib/rack/contrib/static_cache.rb +20 -9
  37. data/lib/rack/contrib/time_zone.rb +2 -0
  38. data/lib/rack/contrib/try_static.rb +2 -0
  39. data/lib/rack/contrib/version.rb +5 -0
  40. data/lib/rack/contrib.rb +10 -4
  41. metadata +14 -189
  42. data/lib/rack/contrib/accept_format.rb +0 -66
  43. data/lib/rack/contrib/sendfile.rb +0 -140
@@ -1,10 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'ruby-prof'
2
4
 
3
5
  module Rack
4
6
  # Set the profile=process_time query parameter to download a
5
7
  # calltree profile of the request.
6
8
  #
7
- # Pass the :printer option to pick a different result format.
9
+ # Pass the :printer option to pick a different result format. Note that
10
+ # some printers (such as CallTreePrinter) have broken the
11
+ # `AbstractPrinter` API, and thus will not work. Bug reports to
12
+ # `ruby-prof`, please, not us.
8
13
  #
9
14
  # You can cause every request to be run multiple times by passing the
10
15
  # `:times` option to the `use Rack::Profiler` call. You can also run a
@@ -27,10 +32,14 @@ module Rack
27
32
  # option defaulting to :call_stack.
28
33
  def initialize(app, options = {})
29
34
  @app = app
35
+ @profile = nil
30
36
  @printer = parse_printer(options[:printer] || DEFAULT_PRINTER)
31
37
  @times = (options[:times] || 1).to_i
38
+ @maximum_runs = options.fetch(:maximum_runs, 10)
32
39
  end
33
40
 
41
+ attr :maximum_runs
42
+
34
43
  def call(env)
35
44
  if mode = profiling?(env)
36
45
  profile(env, mode)
@@ -41,28 +50,46 @@ module Rack
41
50
 
42
51
  private
43
52
  def profiling?(env)
44
- unless ::RubyProf.running?
45
- request = Rack::Request.new(env.clone)
46
- if mode = request.params.delete('profile')
47
- if ::RubyProf.const_defined?(mode.upcase)
48
- mode
49
- else
50
- env['rack.errors'].write "Invalid RubyProf measure_mode: " +
51
- "#{mode}. Use one of #{MODES.to_a.join(', ')}"
52
- false
53
- end
53
+ return if @profile && @profile.running?
54
+
55
+ request = Rack::Request.new(env.clone)
56
+ if mode = request.params.delete('profile')
57
+ if ::RubyProf.const_defined?(mode.upcase)
58
+ mode
59
+ else
60
+ env['rack.errors'].write "Invalid RubyProf measure_mode: " +
61
+ "#{mode}. Use one of #{MODES.to_a.join(', ')}"
62
+ false
54
63
  end
55
64
  end
56
65
  end
57
66
 
67
+ # How many times to run the request within the profiler.
68
+ # If the profiler_runs query parameter is set, use that.
69
+ # Otherwise, use the :times option passed to `#initialize`.
70
+ # If the profiler_runs query parameter is greater than the
71
+ # :maximum option passed to `#initialize`, use the :maximum
72
+ # option.
73
+ def runs(request)
74
+ if profiler_runs = request.params['profiler_runs']
75
+ profiler_runs = profiler_runs.to_i
76
+ if profiler_runs > @maximum_runs
77
+ return @maximum_runs
78
+ else
79
+ return profiler_runs
80
+ end
81
+ else
82
+ return @times
83
+ end
84
+ end
85
+
58
86
  def profile(env, mode)
59
- ::RubyProf.measure_mode = ::RubyProf.const_get(mode.upcase)
87
+ @profile = ::RubyProf::Profile.new(measure_mode: ::RubyProf.const_get(mode.upcase))
60
88
 
61
89
  GC.enable_stats if GC.respond_to?(:enable_stats)
62
90
  request = Rack::Request.new(env.clone)
63
- runs = (request.params['profiler_runs'] || @times).to_i
64
- result = ::RubyProf.profile do
65
- runs.times { @app.call(env) }
91
+ result = @profile.profile do
92
+ runs(request).times { @app.call(env) }
66
93
  end
67
94
  GC.disable_stats if GC.respond_to?(:disable_stats)
68
95
 
@@ -77,10 +104,10 @@ module Rack
77
104
  end
78
105
 
79
106
  def headers(printer, env, mode)
80
- headers = { 'Content-Type' => CONTENT_TYPES[printer.name] }
107
+ headers = { 'content-type' => CONTENT_TYPES[printer.name] }
81
108
  if printer == ::RubyProf::CallTreePrinter
82
109
  filename = ::File.basename(env['PATH_INFO'])
83
- headers['Content-Disposition'] =
110
+ headers['content-disposition'] =
84
111
  %(attachment; filename="#{filename}.#{mode}.tree")
85
112
  end
86
113
  headers
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack'
2
4
 
3
5
  # Rack::RelativeRedirect is a simple middleware that converts relative paths in
@@ -30,15 +32,19 @@ class Rack::RelativeRedirect
30
32
  # and use that to make the Location header an absolute url. If the Location
31
33
  # does not start with a slash, make location relative to the path requested.
32
34
  def call(env)
33
- res = @app.call(env)
34
- if [301,302,303, 307,308].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)
35
+ status, headers, body = @app.call(env)
36
+ headers_klass = Rack.release < "3" ? Rack::Utils::HeaderHash : Rack::Headers
37
+ headers = headers_klass.new.merge(headers)
38
+
39
+ if [301,302,303, 307,308].include?(status) and loc = headers['Location'] and !%r{\Ahttps?://}o.match(loc)
40
+ absolute = @absolute_proc.call(env, [status, headers, body])
41
+ headers['Location'] = if %r{\A/}.match(loc)
37
42
  "#{absolute}#{loc}"
38
43
  else
39
44
  "#{absolute}#{File.dirname(Rack::Utils.unescape(env['PATH_INFO']))}/#{loc}"
40
45
  end
41
46
  end
42
- res
47
+
48
+ [status, headers, body]
43
49
  end
44
50
  end
@@ -1,11 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'fileutils'
2
4
  require 'rack'
3
5
 
4
6
  # Rack::ResponseCache is a Rack middleware that caches responses for successful
5
7
  # 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.
8
+ # []= method (so it works with memcached). As with Rails' page caching, this
9
+ # middleware only writes to the cache -- it never reads. The logic of whether a
10
+ # cached response should be served is left either to your web server, via
11
+ # something like the <tt>try_files</tt> directive in nginx, or to your
12
+ # cache-reading middleware of choice, mounted before Rack::ResponseCache in the
13
+ # stack.
9
14
  class Rack::ResponseCache
10
15
  # The default proc used if a block is not provided to .new
11
16
  # It unescapes the PATH_INFO of the environment, and makes sure that it doesn't
@@ -15,7 +20,10 @@ class Rack::ResponseCache
15
20
  # of the path to index.html.
16
21
  DEFAULT_PATH_PROC = proc do |env, res|
17
22
  path = Rack::Utils.unescape(env['PATH_INFO'])
18
- if !path.include?('..') and match = /text\/((?:x|ht)ml|css)/o.match(res[1]['Content-Type'])
23
+ headers = res[1]
24
+ content_type = headers['Content-Type']
25
+
26
+ if !path.include?('..') and match = /text\/((?:x|ht)ml|css)/o.match(content_type)
19
27
  type = match[1]
20
28
  path = "#{path}.#{type}" unless /\.#{type}\z/.match(path)
21
29
  path = File.join(File.dirname(path), 'index.html') if type == 'html' and File.basename(path) == '.html'
@@ -23,7 +31,7 @@ class Rack::ResponseCache
23
31
  end
24
32
  end
25
33
 
26
- # Initialize a new ReponseCache object with the given arguments. Arguments:
34
+ # Initialize a new ResponseCache object with the given arguments. Arguments:
27
35
  # * app : The next middleware in the chain. This is always called.
28
36
  # * cache : The place to cache responses. If a string is provided, a disk
29
37
  # cache is used, and all cached files will use this directory as the root directory.
@@ -42,18 +50,22 @@ class Rack::ResponseCache
42
50
  # Call the next middleware with the environment. If the request was successful (response status 200),
43
51
  # was a GET request, and had an empty query string, call the block set up in initialize to get the path.
44
52
  # 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.
53
+ # subdirectory of cache. Otherwise, cache the body of the response as the value with the path as the key.
46
54
  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)
55
+ status, headers, body = @app.call(env)
56
+ headers_klass = Rack.release < "3" ? Rack::Utils::HeaderHash : Rack::Headers
57
+ headers = headers_klass.new.merge(headers)
58
+
59
+ if env['REQUEST_METHOD'] == 'GET' and env['QUERY_STRING'] == '' and status == 200 and path = @path_proc.call(env, [status, headers, body])
49
60
  if @cache.is_a?(String)
50
61
  path = File.join(@cache, path) if @cache
51
62
  FileUtils.mkdir_p(File.dirname(path))
52
- File.open(path, 'wb'){|f| res[2].each{|c| f.write(c)}}
63
+ File.open(path, 'wb'){|f| body.each{|c| f.write(c)}}
53
64
  else
54
- @cache[path] = res[2]
65
+ @cache[path] = body
55
66
  end
56
67
  end
57
- res
68
+
69
+ [status, headers, body]
58
70
  end
59
71
  end
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  # Allows you to tap into the response headers. Yields a Rack::Utils::HeaderHash
3
- # of current response headers to the block. Example:
5
+ # (Rack 2) or a Rack::Headers (Rack 3) of current response headers to the block.
6
+ # Example:
4
7
  #
5
8
  # use Rack::ResponseHeaders do |headers|
6
9
  # headers['X-Foo'] = 'bar'
@@ -8,6 +11,9 @@ module Rack
8
11
  # end
9
12
  #
10
13
  class ResponseHeaders
14
+ HEADERS_KLASS = Rack.release < "3" ? Utils::HeaderHash : Headers
15
+ private_constant :HEADERS_KLASS
16
+
11
17
  def initialize(app, &block)
12
18
  @app = app
13
19
  @block = block
@@ -15,7 +21,7 @@ module Rack
15
21
 
16
22
  def call(env)
17
23
  response = @app.call(env)
18
- headers = Utils::HeaderHash.new(response[1])
24
+ headers = HEADERS_KLASS.new.merge(response[1])
19
25
  @block.call(headers)
20
26
  response[1] = headers
21
27
  response
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  class RouteExceptions
3
5
  ROUTES = [
@@ -1,31 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
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
-
3
+ require 'rack'
4
+ require 'rack/runtime'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  # Installs signal handlers that are safely processed after a request
3
5
  #
@@ -24,6 +26,10 @@ module Rack
24
26
  @body.each(&block)
25
27
  @callback.call
26
28
  end
29
+
30
+ def close
31
+ @body.close if @body.respond_to?(:close)
32
+ end
27
33
  end
28
34
 
29
35
  def initialize(app, &block)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  # Create simple endpoints with routing rules, similar to Sinatra actions.
3
5
  #
@@ -78,4 +80,4 @@ module Rack
78
80
  @verbs.empty? || @verbs.include?(method)
79
81
  end
80
82
  end
81
- end
83
+ end
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+
1
5
  module Rack
2
6
 
3
7
  #
@@ -16,7 +20,7 @@ module Rack
16
20
  #
17
21
  # Another way to bypass the cache is adding the version number in a field-value pair in the
18
22
  # 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.
23
+ # In that case, set the option :versioning to false to avoid unnecessary regexp calculations.
20
24
  #
21
25
  # It's better to keep the current version number in some config file and use it in every static
22
26
  # content's URL. So each time we modify our static contents, we just have to change the version
@@ -40,7 +44,7 @@ module Rack
40
44
  # default headers.
41
45
  #
42
46
  # use Rack::StaticCache, :urls => ["/images"], :duration => 2, :versioning => false
43
- # will serve all requests begining with /images under the current directory (default for the option :root
47
+ # will serve all requests beginning with /images under the current directory (default for the option :root
44
48
  # is current directory). All the contents served will have cache expiration duration set to 2 years in headers
45
49
  # (default for :duration is 1 year), and StaticCache will not compute any versioning logics (default for
46
50
  # :versioning is true)
@@ -48,6 +52,8 @@ module Rack
48
52
 
49
53
 
50
54
  class StaticCache
55
+ HEADERS_KLASS = Rack.release < "3" ? Utils::HeaderHash : Headers
56
+ private_constant :HEADERS_KLASS
51
57
 
52
58
  def initialize(app, options={})
53
59
  @app = app
@@ -55,20 +61,21 @@ module Rack
55
61
  @no_cache = {}
56
62
  @urls.collect! do |url|
57
63
  if url =~ /\*$/
58
- url.sub!(/\*$/, '')
59
- @no_cache[url] = 1
64
+ url_prefix = url.sub(/\*$/, '')
65
+ @no_cache[url_prefix] = 1
66
+ url_prefix
67
+ else
68
+ url
60
69
  end
61
- url
62
70
  end
63
71
  root = options[:root] || Dir.pwd
64
- @file_server = Rack::File.new(root)
72
+ @file_server = Rack::Files.new(root)
65
73
  @cache_duration = options[:duration] || 1
66
74
  @versioning_enabled = options.fetch(:versioning, true)
67
75
  if @versioning_enabled
68
76
  @version_regex = options.fetch(:version_regex, /-[\d.]+([.][a-zA-Z][\w]+)?$/)
69
77
  end
70
78
  @duration_in_seconds = self.duration_in_seconds
71
- @duration_in_words = self.duration_in_words
72
79
  end
73
80
 
74
81
  def call(env)
@@ -80,17 +87,21 @@ module Rack
80
87
  if @versioning_enabled
81
88
  path.sub!(@version_regex, '\1')
82
89
  end
90
+
83
91
  status, headers, body = @file_server.call(env)
92
+ headers = HEADERS_KLASS.new.merge(headers)
93
+
84
94
  if @no_cache[url].nil?
85
95
  headers['Cache-Control'] ="max-age=#{@duration_in_seconds}, public"
86
- headers['Expires'] = @duration_in_words
96
+ headers['Expires'] = duration_in_words
87
97
  end
98
+ headers['Date'] = Time.now.httpdate
88
99
  [status, headers, body]
89
100
  end
90
101
  end
91
102
 
92
103
  def duration_in_words
93
- (Time.now + self.duration_in_seconds).strftime '%a, %d %b %Y %H:%M:%S GMT'
104
+ (Time.now.utc + self.duration_in_seconds).httpdate
94
105
  end
95
106
 
96
107
  def duration_in_seconds
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  class TimeZone
3
5
  Javascript = <<-EOJ
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
 
3
5
  # The Rack::TryStatic middleware delegates requests to Rack::Static middleware
@@ -0,0 +1,5 @@
1
+ module Rack
2
+ module Contrib
3
+ VERSION = '2.5.0'
4
+ end
5
+ end
data/lib/rack/contrib.rb CHANGED
@@ -1,14 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack'
2
- require 'git-version-bump'
3
4
 
4
5
  module Rack
5
6
  module Contrib
6
7
  def self.release
8
+ require "git-version-bump"
7
9
  GVB.version
10
+ rescue LoadError
11
+ begin
12
+ Gem::Specification.find_by_name("rack-contrib").version.to_s
13
+ rescue Gem::LoadError
14
+ "0.0.0.1.ENOGEM"
15
+ end
8
16
  end
9
17
  end
10
18
 
11
- autoload :AcceptFormat, "rack/contrib/accept_format"
12
19
  autoload :Access, "rack/contrib/access"
13
20
  autoload :BounceFavicon, "rack/contrib/bounce_favicon"
14
21
  autoload :Cookies, "rack/contrib/cookies"
@@ -19,6 +26,7 @@ module Rack
19
26
  autoload :HostMeta, "rack/contrib/host_meta"
20
27
  autoload :GarbageCollector, "rack/contrib/garbagecollector"
21
28
  autoload :JSONP, "rack/contrib/jsonp"
29
+ autoload :JSONBodyParser, "rack/contrib/json_body_parser"
22
30
  autoload :LazyConditionalGet, "rack/contrib/lazy_conditional_get"
23
31
  autoload :LighttpdScriptNameFix, "rack/contrib/lighttpd_script_name_fix"
24
32
  autoload :Locale, "rack/contrib/locale"
@@ -27,8 +35,6 @@ module Rack
27
35
  autoload :ProcTitle, "rack/contrib/proctitle"
28
36
  autoload :Profiler, "rack/contrib/profiler"
29
37
  autoload :ResponseHeaders, "rack/contrib/response_headers"
30
- autoload :Runtime, "rack/contrib/runtime"
31
- autoload :Sendfile, "rack/contrib/sendfile"
32
38
  autoload :Signals, "rack/contrib/signals"
33
39
  autoload :SimpleEndpoint, "rack/contrib/simple_endpoint"
34
40
  autoload :TimeZone, "rack/contrib/time_zone"