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.
- checksums.yaml +5 -5
- data/README.md +27 -13
- data/lib/rack/contrib/access.rb +8 -6
- data/lib/rack/contrib/backstage.rb +5 -3
- data/lib/rack/contrib/bounce_favicon.rb +3 -1
- data/lib/rack/contrib/callbacks.rb +2 -0
- data/lib/rack/contrib/common_cookies.rb +19 -11
- data/lib/rack/contrib/config.rb +3 -15
- data/lib/rack/contrib/cookies.rb +2 -0
- data/lib/rack/contrib/csshttprequest.rb +12 -6
- data/lib/rack/contrib/deflect.rb +35 -33
- data/lib/rack/contrib/enforce_valid_encoding.rb +3 -1
- data/lib/rack/contrib/evil.rb +2 -0
- data/lib/rack/contrib/expectation_cascade.rb +5 -3
- data/lib/rack/contrib/garbagecollector.rb +2 -0
- data/lib/rack/contrib/host_meta.rb +3 -1
- data/lib/rack/contrib/json_body_parser.rb +94 -0
- data/lib/rack/contrib/jsonp.rb +21 -17
- data/lib/rack/contrib/lazy_conditional_get.rb +13 -4
- data/lib/rack/contrib/lighttpd_script_name_fix.rb +2 -0
- data/lib/rack/contrib/locale.rb +74 -22
- data/lib/rack/contrib/mailexceptions.rb +3 -1
- data/lib/rack/contrib/nested_params.rb +6 -115
- data/lib/rack/contrib/not_found.rb +22 -7
- data/lib/rack/contrib/post_body_content_type_parser.rb +54 -4
- data/lib/rack/contrib/printout.rb +3 -1
- data/lib/rack/contrib/proctitle.rb +2 -0
- data/lib/rack/contrib/profiler.rb +44 -17
- data/lib/rack/contrib/relative_redirect.rb +11 -5
- data/lib/rack/contrib/response_cache.rb +23 -11
- data/lib/rack/contrib/response_headers.rb +8 -2
- data/lib/rack/contrib/route_exceptions.rb +2 -0
- data/lib/rack/contrib/runtime.rb +3 -30
- data/lib/rack/contrib/signals.rb +6 -0
- data/lib/rack/contrib/simple_endpoint.rb +3 -1
- data/lib/rack/contrib/static_cache.rb +20 -9
- data/lib/rack/contrib/time_zone.rb +2 -0
- data/lib/rack/contrib/try_static.rb +2 -0
- data/lib/rack/contrib/version.rb +5 -0
- data/lib/rack/contrib.rb +10 -4
- metadata +14 -189
- data/lib/rack/contrib/accept_format.rb +0 -66
- 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
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
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
|
-
|
64
|
-
|
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 = { '
|
107
|
+
headers = { 'content-type' => CONTENT_TYPES[printer.name] }
|
81
108
|
if printer == ::RubyProf::CallTreePrinter
|
82
109
|
filename = ::File.basename(env['PATH_INFO'])
|
83
|
-
headers['
|
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
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
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).
|
7
|
-
#
|
8
|
-
# be served
|
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
|
-
|
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
|
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
|
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
|
-
|
48
|
-
|
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|
|
63
|
+
File.open(path, 'wb'){|f| body.each{|c| f.write(c)}}
|
53
64
|
else
|
54
|
-
@cache[path] =
|
65
|
+
@cache[path] = body
|
55
66
|
end
|
56
67
|
end
|
57
|
-
|
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.
|
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 =
|
24
|
+
headers = HEADERS_KLASS.new.merge(response[1])
|
19
25
|
@block.call(headers)
|
20
26
|
response[1] = headers
|
21
27
|
response
|
data/lib/rack/contrib/runtime.rb
CHANGED
@@ -1,31 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
|
-
|
3
|
-
|
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'
|
data/lib/rack/contrib/signals.rb
CHANGED
@@ -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,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
|
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
|
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[
|
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::
|
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'] =
|
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).
|
104
|
+
(Time.now.utc + self.duration_in_seconds).httpdate
|
94
105
|
end
|
95
106
|
|
96
107
|
def duration_in_seconds
|
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"
|