rack-contrib 2.2.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rack-contrib might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +0 -1
- data/lib/rack/contrib.rb +2 -1
- data/lib/rack/contrib/access.rb +6 -4
- data/lib/rack/contrib/backstage.rb +3 -1
- data/lib/rack/contrib/bounce_favicon.rb +2 -0
- data/lib/rack/contrib/callbacks.rb +2 -0
- data/lib/rack/contrib/common_cookies.rb +16 -11
- data/lib/rack/contrib/config.rb +3 -15
- data/lib/rack/contrib/cookies.rb +2 -0
- data/lib/rack/contrib/csshttprequest.rb +9 -5
- data/lib/rack/contrib/deflect.rb +34 -32
- data/lib/rack/contrib/enforce_valid_encoding.rb +2 -0
- data/lib/rack/contrib/evil.rb +2 -0
- data/lib/rack/contrib/expectation_cascade.rb +3 -1
- data/lib/rack/contrib/garbagecollector.rb +2 -0
- data/lib/rack/contrib/host_meta.rb +2 -0
- data/lib/rack/contrib/json_body_parser.rb +9 -7
- data/lib/rack/contrib/jsonp.rb +7 -5
- data/lib/rack/contrib/lazy_conditional_get.rb +9 -0
- data/lib/rack/contrib/lighttpd_script_name_fix.rb +2 -0
- data/lib/rack/contrib/locale.rb +3 -0
- data/lib/rack/contrib/mailexceptions.rb +3 -1
- data/lib/rack/contrib/nested_params.rb +3 -1
- data/lib/rack/contrib/not_found.rb +2 -0
- data/lib/rack/contrib/post_body_content_type_parser.rb +2 -0
- data/lib/rack/contrib/printout.rb +2 -0
- data/lib/rack/contrib/proctitle.rb +2 -0
- data/lib/rack/contrib/profiler.rb +2 -0
- data/lib/rack/contrib/relative_redirect.rb +10 -5
- data/lib/rack/contrib/response_cache.rb +12 -11
- data/lib/rack/contrib/response_headers.rb +2 -0
- 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 +10 -3
- data/lib/rack/contrib/time_zone.rb +2 -0
- data/lib/rack/contrib/try_static.rb +2 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cf14e9cb70d9701bc1f3fe5ce036a0667f4ba6c9411e0e6ded73ef958228ec45
|
4
|
+
data.tar.gz: d0c2abe31908dd0d49dec3635cca17c720c94d098a04e9485adf917c469e0ee5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f352f5eac5008e79d645460cd9b17dcc7231fe55edb63a0540c6c0991c06ba5f989cd70c23453e854a8189bcd02ad1be96366bb4d24efdaccefc5fe823a9eed5
|
7
|
+
data.tar.gz: 379d6d0190a63d1df9c615e2fdd53bfebcac0cf506407e1e8a11c734ffc955f066a2aa56e79db21458b62efbb6603e1749f1bdddaed1c35fb84a69cd494dba8f
|
data/README.md
CHANGED
@@ -8,7 +8,6 @@ interface:
|
|
8
8
|
* `Rack::BounceFavicon` - Returns a 404 for requests to `/favicon.ico`
|
9
9
|
* `Rack::CSSHTTPRequest` - Adds CSSHTTPRequest support by encoding responses as CSS for cross-site AJAX-style data loading
|
10
10
|
* `Rack::Callbacks` - Implements DSL for pure before/after filter like Middlewares.
|
11
|
-
* `Rack::Config` - Shared configuration for cooperative middleware.
|
12
11
|
* `Rack::Cookies` - Adds simple cookie jar hash to env
|
13
12
|
* `Rack::Deflect` - Helps protect against DoS attacks.
|
14
13
|
* `Rack::Evil` - Lets the rack application return a response to the client from any place.
|
data/lib/rack/contrib.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rack'
|
2
4
|
|
3
5
|
module Rack
|
@@ -33,7 +35,6 @@ module Rack
|
|
33
35
|
autoload :ProcTitle, "rack/contrib/proctitle"
|
34
36
|
autoload :Profiler, "rack/contrib/profiler"
|
35
37
|
autoload :ResponseHeaders, "rack/contrib/response_headers"
|
36
|
-
autoload :Runtime, "rack/contrib/runtime"
|
37
38
|
autoload :Signals, "rack/contrib/signals"
|
38
39
|
autoload :SimpleEndpoint, "rack/contrib/simple_endpoint"
|
39
40
|
autoload :TimeZone, "rack/contrib/time_zone"
|
data/lib/rack/contrib/access.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "ipaddr"
|
2
4
|
|
3
5
|
module Rack
|
@@ -48,9 +50,9 @@ module Rack
|
|
48
50
|
end
|
49
51
|
|
50
52
|
def call(env)
|
51
|
-
|
53
|
+
request = Request.new(env)
|
52
54
|
ipmasks = ipmasks_for_path(env)
|
53
|
-
return forbidden! unless ip_authorized?(ipmasks)
|
55
|
+
return forbidden! unless ip_authorized?(request, ipmasks)
|
54
56
|
status, headers, body = @app.call(env)
|
55
57
|
[status, headers, body]
|
56
58
|
end
|
@@ -73,11 +75,11 @@ module Rack
|
|
73
75
|
[403, { 'Content-Type' => 'text/html', 'Content-Length' => '0' }, []]
|
74
76
|
end
|
75
77
|
|
76
|
-
def ip_authorized?(ipmasks)
|
78
|
+
def ip_authorized?(request, ipmasks)
|
77
79
|
return true if ipmasks.nil?
|
78
80
|
|
79
81
|
ipmasks.any? do |ip_mask|
|
80
|
-
ip_mask.include?(IPAddr.new(
|
82
|
+
ip_mask.include?(IPAddr.new(request.ip))
|
81
83
|
end
|
82
84
|
end
|
83
85
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
class Backstage
|
3
5
|
File = ::File
|
@@ -10,7 +12,7 @@ module Rack
|
|
10
12
|
def call(env)
|
11
13
|
if File.exists?(@file)
|
12
14
|
content = File.read(@file)
|
13
|
-
length =
|
15
|
+
length = content.bytesize.to_s
|
14
16
|
[503, {'Content-Type' => 'text/html', 'Content-Length' => length}, [content]]
|
15
17
|
else
|
16
18
|
@app.call(env)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
# Rack middleware to use common cookies across domain and subdomains.
|
3
5
|
class CommonCookies
|
@@ -10,21 +12,24 @@ module Rack
|
|
10
12
|
end
|
11
13
|
|
12
14
|
def call(env)
|
13
|
-
@app.call(env)
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
status, headers, body = @app.call(env)
|
16
|
+
headers = Utils::HeaderHash.new(headers)
|
17
|
+
|
18
|
+
host = env['HTTP_HOST'].sub PORT, ''
|
19
|
+
share_cookie(headers, host)
|
20
|
+
|
21
|
+
[status, headers, body]
|
17
22
|
end
|
18
23
|
|
19
24
|
private
|
20
25
|
|
21
|
-
def domain
|
22
|
-
|
26
|
+
def domain(host)
|
27
|
+
host =~ DOMAIN_REGEXP
|
23
28
|
".#{$1}.#{$2}"
|
24
29
|
end
|
25
30
|
|
26
|
-
def share_cookie(headers)
|
27
|
-
headers['Set-Cookie'] &&= common_cookie(headers) if
|
31
|
+
def share_cookie(headers, host)
|
32
|
+
headers['Set-Cookie'] &&= common_cookie(headers, host) if host !~ LOCALHOST_OR_IP_REGEXP
|
28
33
|
end
|
29
34
|
|
30
35
|
def cookie(headers)
|
@@ -32,8 +37,8 @@ module Rack
|
|
32
37
|
cookies.is_a?(Array) ? cookies.join("\n") : cookies
|
33
38
|
end
|
34
39
|
|
35
|
-
def common_cookie(headers)
|
36
|
-
cookie(headers).gsub(/; domain=[^;]*/, '').gsub(/$/, "; domain=#{domain}")
|
40
|
+
def common_cookie(headers, host)
|
41
|
+
cookie(headers).gsub(/; domain=[^;]*/, '').gsub(/$/, "; domain=#{domain(host)}")
|
37
42
|
end
|
38
43
|
end
|
39
|
-
end
|
44
|
+
end
|
data/lib/rack/contrib/config.rb
CHANGED
@@ -1,16 +1,4 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
class Config
|
6
|
-
def initialize(app, &block)
|
7
|
-
@app = app
|
8
|
-
@block = block
|
9
|
-
end
|
10
|
-
|
11
|
-
def call(env)
|
12
|
-
@block.call(env)
|
13
|
-
@app.call(env)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
3
|
+
require 'rack'
|
4
|
+
require 'rack/config'
|
data/lib/rack/contrib/cookies.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'csshttprequest'
|
2
4
|
|
3
5
|
module Rack
|
@@ -13,9 +15,12 @@ module Rack
|
|
13
15
|
# the CSSHTTPRequest encoder
|
14
16
|
def call(env)
|
15
17
|
status, headers, response = @app.call(env)
|
18
|
+
headers = Utils::HeaderHash.new(headers)
|
19
|
+
|
16
20
|
if chr_request?(env)
|
17
|
-
|
18
|
-
modify_headers!(headers,
|
21
|
+
encoded_response = encode(response)
|
22
|
+
modify_headers!(headers, encoded_response)
|
23
|
+
response = [encoded_response]
|
19
24
|
end
|
20
25
|
[status, headers, response]
|
21
26
|
end
|
@@ -25,9 +30,8 @@ module Rack
|
|
25
30
|
!(/\.chr$/.match(env['PATH_INFO'])).nil? || Rack::Request.new(env).params['_format'] == 'chr'
|
26
31
|
end
|
27
32
|
|
28
|
-
def encode(
|
29
|
-
|
30
|
-
return ::CSSHTTPRequest.encode(assembled_body)
|
33
|
+
def encode(body)
|
34
|
+
::CSSHTTPRequest.encode(body.to_enum.to_a.join)
|
31
35
|
end
|
32
36
|
|
33
37
|
def modify_headers!(headers, encoded_response)
|
data/lib/rack/contrib/deflect.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'thread'
|
2
4
|
|
3
5
|
# TODO: optional stats
|
@@ -67,10 +69,10 @@ module Rack
|
|
67
69
|
end
|
68
70
|
|
69
71
|
def deflect? env
|
70
|
-
|
71
|
-
return false if options[:whitelist].include?
|
72
|
-
return true if options[:blacklist].include?
|
73
|
-
sync { watch }
|
72
|
+
remote_addr = env['REMOTE_ADDR']
|
73
|
+
return false if options[:whitelist].include? remote_addr
|
74
|
+
return true if options[:blacklist].include? remote_addr
|
75
|
+
sync { watch(remote_addr) }
|
74
76
|
end
|
75
77
|
|
76
78
|
def log message
|
@@ -82,55 +84,55 @@ module Rack
|
|
82
84
|
@mutex.synchronize(&block)
|
83
85
|
end
|
84
86
|
|
85
|
-
def map
|
86
|
-
@remote_addr_map[
|
87
|
+
def map(remote_addr)
|
88
|
+
@remote_addr_map[remote_addr] ||= {
|
87
89
|
:expires => Time.now + options[:interval],
|
88
90
|
:requests => 0
|
89
91
|
}
|
90
92
|
end
|
91
93
|
|
92
|
-
def watch
|
93
|
-
increment_requests
|
94
|
-
clear! if watch_expired? and not blocked?
|
95
|
-
clear! if blocked? and block_expired?
|
96
|
-
block! if watching? and exceeded_request_threshold?
|
97
|
-
blocked?
|
94
|
+
def watch(remote_addr)
|
95
|
+
increment_requests(remote_addr)
|
96
|
+
clear!(remote_addr) if watch_expired?(remote_addr) and not blocked?(remote_addr)
|
97
|
+
clear!(remote_addr) if blocked?(remote_addr) and block_expired?(remote_addr)
|
98
|
+
block!(remote_addr) if watching?(remote_addr) and exceeded_request_threshold?(remote_addr)
|
99
|
+
blocked?(remote_addr)
|
98
100
|
end
|
99
101
|
|
100
|
-
def block!
|
101
|
-
return if blocked?
|
102
|
-
log "blocked #{
|
103
|
-
map[:block_expires] = Time.now + options[:block_duration]
|
102
|
+
def block!(remote_addr)
|
103
|
+
return if blocked?(remote_addr)
|
104
|
+
log "blocked #{remote_addr}"
|
105
|
+
map(remote_addr)[:block_expires] = Time.now + options[:block_duration]
|
104
106
|
end
|
105
107
|
|
106
|
-
def blocked?
|
107
|
-
map.has_key? :block_expires
|
108
|
+
def blocked?(remote_addr)
|
109
|
+
map(remote_addr).has_key? :block_expires
|
108
110
|
end
|
109
111
|
|
110
|
-
def block_expired?
|
111
|
-
map[:block_expires] < Time.now rescue false
|
112
|
+
def block_expired?(remote_addr)
|
113
|
+
map(remote_addr)[:block_expires] < Time.now rescue false
|
112
114
|
end
|
113
115
|
|
114
|
-
def watching?
|
115
|
-
@remote_addr_map.has_key?
|
116
|
+
def watching?(remote_addr)
|
117
|
+
@remote_addr_map.has_key? remote_addr
|
116
118
|
end
|
117
119
|
|
118
|
-
def clear!
|
119
|
-
return unless watching?
|
120
|
-
log "released #{
|
121
|
-
@remote_addr_map.delete
|
120
|
+
def clear!(remote_addr)
|
121
|
+
return unless watching?(remote_addr)
|
122
|
+
log "released #{remote_addr}" if blocked?(remote_addr)
|
123
|
+
@remote_addr_map.delete remote_addr
|
122
124
|
end
|
123
125
|
|
124
|
-
def increment_requests
|
125
|
-
map[:requests] += 1
|
126
|
+
def increment_requests(remote_addr)
|
127
|
+
map(remote_addr)[:requests] += 1
|
126
128
|
end
|
127
129
|
|
128
|
-
def exceeded_request_threshold?
|
129
|
-
map[:requests] > options[:request_threshold]
|
130
|
+
def exceeded_request_threshold?(remote_addr)
|
131
|
+
map(remote_addr)[:requests] > options[:request_threshold]
|
130
132
|
end
|
131
133
|
|
132
|
-
def watch_expired?
|
133
|
-
map[:expires] <= Time.now
|
134
|
+
def watch_expired?(remote_addr)
|
135
|
+
map(remote_addr)[:expires] <= Time.now
|
134
136
|
end
|
135
137
|
|
136
138
|
end
|
data/lib/rack/contrib/evil.rb
CHANGED
@@ -55,16 +55,18 @@ module Rack
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def call(env)
|
58
|
-
|
59
|
-
|
58
|
+
begin
|
59
|
+
if @verbs.include?(env[Rack::REQUEST_METHOD]) &&
|
60
|
+
@matcher.call(@media, env['CONTENT_TYPE'])
|
60
61
|
|
61
|
-
|
62
|
+
update_form_hash_with_json_body(env)
|
63
|
+
end
|
64
|
+
rescue JSON::ParserError
|
65
|
+
body = { error: 'Failed to parse body as JSON' }.to_json
|
66
|
+
header = { 'Content-Type' => 'application/json' }
|
67
|
+
return Rack::Response.new(body, 400, header).finish
|
62
68
|
end
|
63
69
|
@app.call(env)
|
64
|
-
rescue JSON::ParserError
|
65
|
-
body = { error: 'Failed to parse body as JSON' }.to_json
|
66
|
-
header = { 'Content-Type' => 'application/json' }
|
67
|
-
Rack::Response.new(body, 400, header).finish
|
68
70
|
end
|
69
71
|
|
70
72
|
private
|
data/lib/rack/contrib/jsonp.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
|
3
5
|
# A Rack middleware for providing JSON-P support.
|
@@ -53,7 +55,7 @@ module Rack
|
|
53
55
|
|
54
56
|
# Set new Content-Length, if it was set before we mutated the response body
|
55
57
|
if headers['Content-Length']
|
56
|
-
length = response.
|
58
|
+
length = response.map(&:bytesize).reduce(0, :+)
|
57
59
|
headers['Content-Length'] = length.to_s
|
58
60
|
end
|
59
61
|
end
|
@@ -87,8 +89,8 @@ module Rack
|
|
87
89
|
# method of combining all of the data into a single string makes sense
|
88
90
|
# since JSON is returned as a full string.
|
89
91
|
#
|
90
|
-
def pad(callback, response
|
91
|
-
response.
|
92
|
+
def pad(callback, response)
|
93
|
+
body = response.to_enum.map do |s|
|
92
94
|
# U+2028 and U+2029 are allowed inside strings in JSON (as all literal
|
93
95
|
# Unicode characters) but JavaScript defines them as newline
|
94
96
|
# seperators. Because no literal newlines are allowed in a string, this
|
@@ -96,8 +98,8 @@ module Rack
|
|
96
98
|
# replacing them with the escaped version. This should be safe because
|
97
99
|
# according to the JSON spec, these characters are *only* valid inside
|
98
100
|
# a string and should therefore not be present any other places.
|
99
|
-
|
100
|
-
end
|
101
|
+
s.gsub(U2028, '\u2028').gsub(U2029, '\u2029')
|
102
|
+
end.join
|
101
103
|
|
102
104
|
# https://github.com/rack/rack-contrib/issues/46
|
103
105
|
response.close if response.respond_to?(:close)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
|
3
5
|
##
|
@@ -43,6 +45,10 @@ module Rack
|
|
43
45
|
# know for sure that it does not modify the cached content, you can set the
|
44
46
|
# `Rack-Lazy-Conditional-Get` on response to `skip`. This will not update the
|
45
47
|
# global modification date.
|
48
|
+
#
|
49
|
+
# NOTE: This will not work properly in a multi-threaded environment with
|
50
|
+
# default cache object. A provided cache object should ensure thread-safety
|
51
|
+
# of the `get`/`set`/`[]`/`[]=` methods.
|
46
52
|
|
47
53
|
class LazyConditionalGet
|
48
54
|
|
@@ -70,7 +76,10 @@ module Rack
|
|
70
76
|
if reading? env and fresh? env
|
71
77
|
return [304, {'Last-Modified' => env['HTTP_IF_MODIFIED_SINCE']}, []]
|
72
78
|
end
|
79
|
+
|
73
80
|
status, headers, body = @app.call env
|
81
|
+
headers = Utils::HeaderHash.new(headers)
|
82
|
+
|
74
83
|
update_cache unless (reading?(env) or skipping?(headers))
|
75
84
|
headers['Last-Modified'] = cached_value if stampable? headers
|
76
85
|
[status, headers, body]
|
data/lib/rack/contrib/locale.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'i18n'
|
2
4
|
|
3
5
|
module Rack
|
@@ -14,6 +16,7 @@ module Rack
|
|
14
16
|
|
15
17
|
env['rack.locale'] = I18n.locale = locale.to_s
|
16
18
|
status, headers, body = @app.call(env)
|
19
|
+
headers = Utils::HeaderHash.new(headers)
|
17
20
|
|
18
21
|
unless headers['Content-Language']
|
19
22
|
headers['Content-Language'] = locale.to_s
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'net/smtp'
|
2
4
|
require 'mail'
|
3
5
|
require 'erb'
|
@@ -104,7 +106,7 @@ module Rack
|
|
104
106
|
|
105
107
|
def extract_body(env)
|
106
108
|
if io = env['rack.input']
|
107
|
-
io.rewind
|
109
|
+
io.rewind
|
108
110
|
io.read
|
109
111
|
end
|
110
112
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rack/utils'
|
2
4
|
|
3
5
|
module Rack
|
@@ -24,7 +26,7 @@ module Rack
|
|
24
26
|
post_body = env[POST_BODY]
|
25
27
|
env[FORM_INPUT] = post_body
|
26
28
|
env[FORM_HASH] = Rack::Utils.parse_nested_query(post_body.read)
|
27
|
-
post_body.rewind
|
29
|
+
post_body.rewind
|
28
30
|
end
|
29
31
|
@app.call(env)
|
30
32
|
end
|
@@ -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,18 @@ 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 = Rack::Utils::HeaderHash.new(headers)
|
37
|
+
|
38
|
+
if [301,302,303, 307,308].include?(status) and loc = headers['Location'] and !%r{\Ahttps?://}o.match(loc)
|
39
|
+
absolute = @absolute_proc.call(env, [status, headers, body])
|
40
|
+
headers['Location'] = if %r{\A/}.match(loc)
|
37
41
|
"#{absolute}#{loc}"
|
38
42
|
else
|
39
43
|
"#{absolute}#{File.dirname(Rack::Utils.unescape(env['PATH_INFO']))}/#{loc}"
|
40
44
|
end
|
41
45
|
end
|
42
|
-
|
46
|
+
|
47
|
+
[status, headers, body]
|
43
48
|
end
|
44
49
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'fileutils'
|
2
4
|
require 'rack'
|
3
5
|
|
@@ -19,12 +21,8 @@ class Rack::ResponseCache
|
|
19
21
|
DEFAULT_PATH_PROC = proc do |env, res|
|
20
22
|
path = Rack::Utils.unescape(env['PATH_INFO'])
|
21
23
|
headers = res[1]
|
22
|
-
|
23
|
-
|
24
|
-
content_type = headers.fetch('Content-Type') do |titlecase_key|
|
25
|
-
_, val = headers.find { |key, _| key.casecmp(titlecase_key) == 0 }
|
26
|
-
val
|
27
|
-
end
|
24
|
+
content_type = headers['Content-Type']
|
25
|
+
|
28
26
|
if !path.include?('..') and match = /text\/((?:x|ht)ml|css)/o.match(content_type)
|
29
27
|
type = match[1]
|
30
28
|
path = "#{path}.#{type}" unless /\.#{type}\z/.match(path)
|
@@ -54,16 +52,19 @@ class Rack::ResponseCache
|
|
54
52
|
# If the cache is a string, create any necessary middle directories, and cache the file in the appropriate
|
55
53
|
# subdirectory of cache. Otherwise, cache the body of the response as the value with the path as the key.
|
56
54
|
def call(env)
|
57
|
-
|
58
|
-
|
55
|
+
status, headers, body = @app.call(env)
|
56
|
+
headers = Rack::Utils::HeaderHash.new(headers)
|
57
|
+
|
58
|
+
if env['REQUEST_METHOD'] == 'GET' and env['QUERY_STRING'] == '' and status == 200 and path = @path_proc.call(env, [status, headers, body])
|
59
59
|
if @cache.is_a?(String)
|
60
60
|
path = File.join(@cache, path) if @cache
|
61
61
|
FileUtils.mkdir_p(File.dirname(path))
|
62
|
-
File.open(path, 'wb'){|f|
|
62
|
+
File.open(path, 'wb'){|f| body.each{|c| f.write(c)}}
|
63
63
|
else
|
64
|
-
@cache[path] =
|
64
|
+
@cache[path] = body
|
65
65
|
end
|
66
66
|
end
|
67
|
-
|
67
|
+
|
68
|
+
[status, headers, body]
|
68
69
|
end
|
69
70
|
end
|
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,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'time'
|
2
4
|
|
3
5
|
module Rack
|
@@ -57,10 +59,12 @@ module Rack
|
|
57
59
|
@no_cache = {}
|
58
60
|
@urls.collect! do |url|
|
59
61
|
if url =~ /\*$/
|
60
|
-
url.sub
|
61
|
-
@no_cache[
|
62
|
+
url_prefix = url.sub(/\*$/, '')
|
63
|
+
@no_cache[url_prefix] = 1
|
64
|
+
url_prefix
|
65
|
+
else
|
66
|
+
url
|
62
67
|
end
|
63
|
-
url
|
64
68
|
end
|
65
69
|
root = options[:root] || Dir.pwd
|
66
70
|
@file_server = Rack::File.new(root)
|
@@ -81,7 +85,10 @@ module Rack
|
|
81
85
|
if @versioning_enabled
|
82
86
|
path.sub!(@version_regex, '\1')
|
83
87
|
end
|
88
|
+
|
84
89
|
status, headers, body = @file_server.call(env)
|
90
|
+
headers = Utils::HeaderHash.new(headers)
|
91
|
+
|
85
92
|
if @no_cache[url].nil?
|
86
93
|
headers['Cache-Control'] ="max-age=#{@duration_in_seconds}, public"
|
87
94
|
headers['Expires'] = duration_in_words
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-contrib
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- rack-devel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-10-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|