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.
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