rack-contrib 2.2.0 → 2.3.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.

Potentially problematic release.


This version of rack-contrib might be problematic. Click here for more details.

Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -1
  3. data/lib/rack/contrib.rb +2 -1
  4. data/lib/rack/contrib/access.rb +6 -4
  5. data/lib/rack/contrib/backstage.rb +3 -1
  6. data/lib/rack/contrib/bounce_favicon.rb +2 -0
  7. data/lib/rack/contrib/callbacks.rb +2 -0
  8. data/lib/rack/contrib/common_cookies.rb +16 -11
  9. data/lib/rack/contrib/config.rb +3 -15
  10. data/lib/rack/contrib/cookies.rb +2 -0
  11. data/lib/rack/contrib/csshttprequest.rb +9 -5
  12. data/lib/rack/contrib/deflect.rb +34 -32
  13. data/lib/rack/contrib/enforce_valid_encoding.rb +2 -0
  14. data/lib/rack/contrib/evil.rb +2 -0
  15. data/lib/rack/contrib/expectation_cascade.rb +3 -1
  16. data/lib/rack/contrib/garbagecollector.rb +2 -0
  17. data/lib/rack/contrib/host_meta.rb +2 -0
  18. data/lib/rack/contrib/json_body_parser.rb +9 -7
  19. data/lib/rack/contrib/jsonp.rb +7 -5
  20. data/lib/rack/contrib/lazy_conditional_get.rb +9 -0
  21. data/lib/rack/contrib/lighttpd_script_name_fix.rb +2 -0
  22. data/lib/rack/contrib/locale.rb +3 -0
  23. data/lib/rack/contrib/mailexceptions.rb +3 -1
  24. data/lib/rack/contrib/nested_params.rb +3 -1
  25. data/lib/rack/contrib/not_found.rb +2 -0
  26. data/lib/rack/contrib/post_body_content_type_parser.rb +2 -0
  27. data/lib/rack/contrib/printout.rb +2 -0
  28. data/lib/rack/contrib/proctitle.rb +2 -0
  29. data/lib/rack/contrib/profiler.rb +2 -0
  30. data/lib/rack/contrib/relative_redirect.rb +10 -5
  31. data/lib/rack/contrib/response_cache.rb +12 -11
  32. data/lib/rack/contrib/response_headers.rb +2 -0
  33. data/lib/rack/contrib/route_exceptions.rb +2 -0
  34. data/lib/rack/contrib/runtime.rb +3 -30
  35. data/lib/rack/contrib/signals.rb +6 -0
  36. data/lib/rack/contrib/simple_endpoint.rb +3 -1
  37. data/lib/rack/contrib/static_cache.rb +10 -3
  38. data/lib/rack/contrib/time_zone.rb +2 -0
  39. data/lib/rack/contrib/try_static.rb +2 -0
  40. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ca58db2b7968ad4691e8bec79eea2088dabc3da5d309358f7df76d668324d824
4
- data.tar.gz: 51816bb6b59a598fea16eb100cc783b3c0ff58eb37aa3585bb8d65141f2aae4f
3
+ metadata.gz: cf14e9cb70d9701bc1f3fe5ce036a0667f4ba6c9411e0e6ded73ef958228ec45
4
+ data.tar.gz: d0c2abe31908dd0d49dec3635cca17c720c94d098a04e9485adf917c469e0ee5
5
5
  SHA512:
6
- metadata.gz: 7442b5ad170ae2946f017d8ccd007d02455b7939fe42239378505b61983cd9a1f090549e87b07e08323a7cf99d04f026e8df0f326e34be166508187cca3735e7
7
- data.tar.gz: 99985100672c65c9ee559a37281f5dddea4fcdc8481ddffd75c2f6e697197a1070339c5b70733d736f8ba33078d5f699f00097cd6ec96266c8f07edb008b727b
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.
@@ -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"
@@ -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
- @original_request = Request.new(env)
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(@original_request.ip))
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 = "".respond_to?(:bytesize) ? content.bytesize.to_s : content.size.to_s
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
  # Bounce those annoying favicon.ico requests
3
5
  class BounceFavicon
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  class Callbacks
3
5
  def initialize(&block)
@@ -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).tap do |(status, headers, response)|
14
- @host = env['HTTP_HOST'].sub PORT, ''
15
- share_cookie headers
16
- end
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
- @host =~ DOMAIN_REGEXP
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 @host !~ LOCALHOST_OR_IP_REGEXP
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
@@ -1,16 +1,4 @@
1
- module Rack
1
+ # frozen_string_literal: true
2
2
 
3
- # Rack::Config modifies the environment using the block given during
4
- # initialization.
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'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  class Cookies
3
5
  class CookieJar < Hash
@@ -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
- response = encode(response)
18
- modify_headers!(headers, response)
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(response, assembled_body="")
29
- response.each { |s| assembled_body << s.to_s } # call down the stack
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)
@@ -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
- @remote_addr = env['REMOTE_ADDR']
71
- return false if options[:whitelist].include? @remote_addr
72
- return true if options[:blacklist].include? @remote_addr
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[@remote_addr] ||= {
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 #{@remote_addr}"
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? @remote_addr
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 #{@remote_addr}" if blocked?
121
- @remote_addr_map.delete @remote_addr
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  # Ensure that the path and query string presented to the application
3
5
  # contains only valid characters. If the validation fails, then a
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  class Evil
3
5
  # Lets you return a response to the client immediately from anywhere ( M V or C ) in the code.
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  class ExpectationCascade
3
- Expect = "Expect".freeze
5
+ Expect = "HTTP_EXPECT".freeze
4
6
  ContinueExpectation = "100-continue".freeze
5
7
 
6
8
  ExpectationFailed = [417, {"Content-Type" => "text/html"}, []].freeze
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  # Forces garbage collection after each request.
3
5
  class GarbageCollector
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
 
3
5
  # Rack middleware implementing the IETF draft: "Host Metadata for the Web"
@@ -55,16 +55,18 @@ module Rack
55
55
  end
56
56
 
57
57
  def call(env)
58
- if @verbs.include?(env[Rack::REQUEST_METHOD]) &&
59
- @matcher.call(@media, env['CONTENT_TYPE'])
58
+ begin
59
+ if @verbs.include?(env[Rack::REQUEST_METHOD]) &&
60
+ @matcher.call(@media, env['CONTENT_TYPE'])
60
61
 
61
- update_form_hash_with_json_body(env)
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
@@ -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.to_ary.inject(0) { |len, part| len + part.bytesize }
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, body = "")
91
- response.each do |s|
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
- body << s.to_s.gsub(U2028, '\u2028').gsub(U2029, '\u2029')
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]
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  # Lighttpd sets the wrong SCRIPT_NAME and PATH_INFO if you mount your
3
5
  # FastCGI app at "/". This middleware fixes this issue.
@@ -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 if io.respond_to?(: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 if post_body.respond_to?(: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
  module Rack
2
4
  # Rack::NotFound is a default endpoint. Optionally initialize with the
3
5
  # path to a custom 404 page, to override the standard response body.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'json'
3
5
  rescue LoadError => e
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  #prints the environment and request for simple debugging
3
5
  class Printout
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  # Middleware to update the process title ($0) with information about the
3
5
  # current request. Based loosely on:
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'ruby-prof'
2
4
 
3
5
  module Rack
@@ -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
- 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 = 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
- res
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
- # Content-Type is almost always at headers['Content-Type'], but to fully
23
- # comply with HTTP RFC 7230, we fall back to a case-insensitive lookup
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
- res = @app.call(env)
58
- 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 = 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| res[2].each{|c| f.write(c)}}
62
+ File.open(path, 'wb'){|f| body.each{|c| f.write(c)}}
63
63
  else
64
- @cache[path] = res[2]
64
+ @cache[path] = body
65
65
  end
66
66
  end
67
- res
67
+
68
+ [status, headers, body]
68
69
  end
69
70
  end
@@ -1,3 +1,5 @@
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
5
  # of current response headers to the block. Example:
@@ -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,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[url] = 1
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
@@ -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
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.2.0
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-03-29 00:00:00.000000000 Z
11
+ date: 2020-10-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack