rack-contrib 2.0.0 → 2.2.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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: b545ac91ebc6596e463da4596aa475e98f170759
4
- data.tar.gz: 7185814a488ba9dfc59071569d9ae7175e2f5eb3
2
+ SHA256:
3
+ metadata.gz: ca58db2b7968ad4691e8bec79eea2088dabc3da5d309358f7df76d668324d824
4
+ data.tar.gz: 51816bb6b59a598fea16eb100cc783b3c0ff58eb37aa3585bb8d65141f2aae4f
5
5
  SHA512:
6
- metadata.gz: 6b8b85e0c0d7541825991235ec8b20057d9d8965d031540f056741fb99b9bd6ac524096988066ba717221ec6f6f134db76f9bf5f6325794f28d6a8f2b8831a0f
7
- data.tar.gz: 3413f89de71509f68b16b21d4e40dc0eb7fbd98c5210a5fa9e0512521f630f044a75933ce05eca29f19536f052184722c79ca37ba443d49f8161a3702054ac40
6
+ metadata.gz: 7442b5ad170ae2946f017d8ccd007d02455b7939fe42239378505b61983cd9a1f090549e87b07e08323a7cf99d04f026e8df0f326e34be166508187cca3735e7
7
+ data.tar.gz: 99985100672c65c9ee559a37281f5dddea4fcdc8481ddffd75c2f6e697197a1070339c5b70733d736f8ba33078d5f699f00097cd6ec96266c8f07edb008b727b
data/README.md CHANGED
@@ -3,7 +3,6 @@
3
3
  This package includes a variety of add-on components for Rack, a Ruby web server
4
4
  interface:
5
5
 
6
- * `Rack::AcceptFormat` - Adds a format extension at the end of the URI when there is none, corresponding to the mime-type given in the Accept HTTP header.
7
6
  * `Rack::Access` - Limits access based on IP address
8
7
  * `Rack::Backstage` - Returns content of specified file if it exists, which makes it convenient for putting up maintenance pages.
9
8
  * `Rack::BounceFavicon` - Returns a 404 for requests to `/favicon.ico`
@@ -14,6 +13,7 @@ interface:
14
13
  * `Rack::Deflect` - Helps protect against DoS attacks.
15
14
  * `Rack::Evil` - Lets the rack application return a response to the client from any place.
16
15
  * `Rack::HostMeta` - Configures `/host-meta` using a block
16
+ * `Rack::JSONBodyParser` - Adds JSON request bodies to the Rack parameters hash.
17
17
  * `Rack::JSONP` - Adds JSON-P support by stripping out the callback param and padding the response with the appropriate callback format.
18
18
  * `Rack::LazyConditionalGet` - Caches a global `Last-Modified` date and updates it each time there is a request that is not `GET` or `HEAD`.
19
19
  * `Rack::LighttpdScriptNameFix` - Fixes how lighttpd sets the `SCRIPT_NAME` and `PATH_INFO` variables in certain configurations.
@@ -21,14 +21,13 @@ interface:
21
21
  * `Rack::MailExceptions` - Rescues exceptions raised from the app and sends a useful email with the exception, stacktrace, and contents of the environment.
22
22
  * `Rack::NestedParams` - parses form params with subscripts (e.g., * "`post[title]=Hello`") into a nested/recursive Hash structure (based on Rails' implementation).
23
23
  * `Rack::NotFound` - A default 404 application.
24
- * `Rack::PostBodyContentTypeParser` - Adds support for JSON request bodies. The Rack parameter hash is populated by deserializing the JSON data provided in the request body when the Content-Type is application/json.
24
+ * `Rack::PostBodyContentTypeParser` - [Deprecated]: Adds support for JSON request bodies. The Rack parameter hash is populated by deserializing the JSON data provided in the request body when the Content-Type is application/json
25
25
  * `Rack::Printout` - Prints the environment and the response per request
26
26
  * `Rack::ProcTitle` - Displays request information in process title (`$0`) for monitoring/inspection with ps(1).
27
27
  * `Rack::Profiler` - Uses ruby-prof to measure request time.
28
28
  * `Rack::RelativeRedirect` - Transforms relative paths in redirects to absolute URLs.
29
- * `Rack::ResponseCache` - Caches responses to requests without query strings to Disk or a user provider Ruby object. Similar to Rails' page caching.
29
+ * `Rack::ResponseCache` - Caches responses to requests without query strings to Disk or a user provided Ruby object. Similar to Rails' page caching.
30
30
  * `Rack::ResponseHeaders` - Manipulates response headers object at runtime
31
- * `Rack::Sendfile` - Enables `X-Sendfile` support for bodies that can be served from file.
32
31
  * `Rack::Signals` - Installs signal handlers that are safely processed after a request
33
32
  * `Rack::SimpleEndpoint` - Creates simple endpoints with routing rules, similar to Sinatra actions
34
33
  * `Rack::StaticCache` - Modifies the response headers to facilitiate client and proxy caching for static files that minimizes http requests and improves overall load times for second time visitors.
@@ -104,8 +103,6 @@ guidelines in CONTRIBUTING.md.
104
103
 
105
104
  ### Links
106
105
 
107
- * rack-contrib on GitHub:: <http://github.com/rack/rack-contrib>
108
- * Rack:: <http://rack.rubyforge.org/>
109
- * Rack On GitHub:: <http://github.com/rack/rack>
110
- * rack-devel mailing list:: <http://groups.google.com/group/rack-devel>
111
- * [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/rack/rack-contrib?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
106
+ * rack-contrib on GitHub:: <https://github.com/rack/rack-contrib>
107
+ * Rack:: <https://rack.github.io/>
108
+ * Rack On GitHub:: <https://github.com/rack/rack>
@@ -24,6 +24,7 @@ module Rack
24
24
  autoload :HostMeta, "rack/contrib/host_meta"
25
25
  autoload :GarbageCollector, "rack/contrib/garbagecollector"
26
26
  autoload :JSONP, "rack/contrib/jsonp"
27
+ autoload :JSONBodyParser, "rack/contrib/json_body_parser"
27
28
  autoload :LazyConditionalGet, "rack/contrib/lazy_conditional_get"
28
29
  autoload :LighttpdScriptNameFix, "rack/contrib/lighttpd_script_name_fix"
29
30
  autoload :Locale, "rack/contrib/locale"
@@ -31,7 +31,7 @@ module Rack
31
31
  end
32
32
 
33
33
  def modify_headers!(headers, encoded_response)
34
- headers['Content-Length'] = encoded_response.length.to_s
34
+ headers['Content-Length'] = encoded_response.bytesize.to_s
35
35
  headers['Content-Type'] = 'text/css'
36
36
  nil
37
37
  end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Rack
6
+ # A Rack middleware that makes JSON-encoded request bodies available in the
7
+ # request.params hash. By default it parses POST, PATCH, and PUT requests
8
+ # whose media type is <tt>application/json</tt>. You can configure it to match
9
+ # any verb or media type via the <tt>:verbs</tt> and <tt>:media</tt> options.
10
+ #
11
+ #
12
+ # == Examples:
13
+ #
14
+ # === Parse POST and GET requests only
15
+ # use Rack::JSONBodyParser, verbs: ['POST', 'GET']
16
+ #
17
+ # === Parse POST|PATCH|PUT requests whose Content-Type matches 'json'
18
+ # use Rack::JSONBodyParser, media: /json/
19
+ #
20
+ # === Parse POST requests whose Content-Type is 'application/json' or 'application/vnd+json'
21
+ # use Rack::JSONBodyParser, verbs: ['POST'], media: ['application/json', 'application/vnd.api+json']
22
+ #
23
+ class JSONBodyParser
24
+ CONTENT_TYPE_MATCHERS = {
25
+ String => lambda { |option, header|
26
+ Rack::MediaType.type(header) == option
27
+ },
28
+ Array => lambda { |options, header|
29
+ media_type = Rack::MediaType.type(header)
30
+ options.any? { |opt| media_type == opt }
31
+ },
32
+ Regexp => lambda {
33
+ if //.respond_to?(:match?)
34
+ # Use Ruby's fast regex matcher when available
35
+ ->(option, header) { option.match? header }
36
+ else
37
+ # Fall back to the slower matcher for rubies older than 2.4
38
+ ->(option, header) { option.match header }
39
+ end
40
+ }.call(),
41
+ }.freeze
42
+
43
+ DEFAULT_PARSER = ->(body) { JSON.parse(body, create_additions: false) }
44
+
45
+ def initialize(
46
+ app,
47
+ verbs: %w[POST PATCH PUT],
48
+ media: 'application/json',
49
+ &block
50
+ )
51
+ @app = app
52
+ @verbs, @media = verbs, media
53
+ @matcher = CONTENT_TYPE_MATCHERS.fetch(@media.class)
54
+ @parser = block || DEFAULT_PARSER
55
+ end
56
+
57
+ def call(env)
58
+ if @verbs.include?(env[Rack::REQUEST_METHOD]) &&
59
+ @matcher.call(@media, env['CONTENT_TYPE'])
60
+
61
+ update_form_hash_with_json_body(env)
62
+ end
63
+ @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
+ end
69
+
70
+ private
71
+
72
+ def update_form_hash_with_json_body(env)
73
+ body = env[Rack::RACK_INPUT]
74
+ return unless (body_content = body.read) && !body_content.empty?
75
+
76
+ body.rewind # somebody might try to read this stream
77
+ env.update(
78
+ Rack::RACK_REQUEST_FORM_HASH => @parser.call(body_content),
79
+ Rack::RACK_REQUEST_FORM_INPUT => body
80
+ )
81
+ end
82
+ end
83
+ end
@@ -106,7 +106,7 @@ module Rack
106
106
  end
107
107
 
108
108
  def bad_request(body = "Bad Request")
109
- [ 400, { 'Content-Type' => 'text/plain', 'Content-Length' => body.size.to_s }, [body] ]
109
+ [ 400, { 'Content-Type' => 'text/plain', 'Content-Length' => body.bytesize.to_s }, [body] ]
110
110
  end
111
111
 
112
112
  end
@@ -7,43 +7,75 @@ module Rack
7
7
  end
8
8
 
9
9
  def call(env)
10
- old_locale = I18n.locale
11
-
12
- begin
13
- locale = accept_locale(env) || I18n.default_locale
14
- locale = env['rack.locale'] = I18n.locale = locale.to_s
15
- status, headers, body = @app.call(env)
16
- headers['Content-Language'] = locale unless headers['Content-Language']
17
- [status, headers, body]
18
- ensure
19
- I18n.locale = old_locale
10
+ locale_to_restore = I18n.locale
11
+
12
+ locale = user_preferred_locale(env["HTTP_ACCEPT_LANGUAGE"])
13
+ locale ||= I18n.default_locale
14
+
15
+ env['rack.locale'] = I18n.locale = locale.to_s
16
+ status, headers, body = @app.call(env)
17
+
18
+ unless headers['Content-Language']
19
+ headers['Content-Language'] = locale.to_s
20
20
  end
21
+
22
+ [status, headers, body]
23
+ ensure
24
+ I18n.locale = locale_to_restore
21
25
  end
22
26
 
23
27
  private
24
28
 
25
- # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
26
- def accept_locale(env)
27
- accept_langs = env["HTTP_ACCEPT_LANGUAGE"]
28
- return if accept_langs.nil?
29
-
30
- languages_and_qvalues = accept_langs.split(",").map { |l|
31
- l += ';q=1.0' unless l =~ /;q=\d+(?:\.\d+)?$/
32
- l.split(';q=')
33
- }
34
-
35
- language_and_qvalue = languages_and_qvalues.sort_by { |(locale, qvalue)|
36
- qvalue.to_f
37
- }.reverse.detect { |(locale, qvalue)|
38
- if I18n.enforce_available_locales
39
- locale == '*' || I18n.available_locales.include?(locale.to_sym)
40
- else
41
- true
29
+ # Accept-Language header is covered mainly by RFC 7231
30
+ # https://tools.ietf.org/html/rfc7231
31
+ #
32
+ # Related sections:
33
+ #
34
+ # * https://tools.ietf.org/html/rfc7231#section-5.3.1
35
+ # * https://tools.ietf.org/html/rfc7231#section-5.3.5
36
+ # * https://tools.ietf.org/html/rfc4647#section-3.4
37
+ #
38
+ # There is an obsolete RFC 2616 (https://tools.ietf.org/html/rfc2616)
39
+ #
40
+ # Edge cases:
41
+ #
42
+ # * Value can be a comma separated list with optional whitespaces:
43
+ # Accept-Language: da, en-gb;q=0.8, en;q=0.7
44
+ #
45
+ # * Quality value can contain optional whitespaces as well:
46
+ # Accept-Language: ru-UA, ru; q=0.8, uk; q=0.6, en-US; q=0.4, en; q=0.2
47
+ #
48
+ # * Quality prefix 'q=' can be in upper case (Q=)
49
+ #
50
+ # * Ignore case when match locale with I18n available locales
51
+ #
52
+ def user_preferred_locale(header)
53
+ return if header.nil?
54
+
55
+ locales = header.gsub(/\s+/, '').split(",").map do |language_tag|
56
+ locale, quality = language_tag.split(/;q=/i)
57
+ quality = quality ? quality.to_f : 1.0
58
+ [locale, quality]
59
+ end.reject do |(locale, quality)|
60
+ locale == '*' || quality == 0
61
+ end.sort_by do |(_, quality)|
62
+ quality
63
+ end.map(&:first)
64
+
65
+ return if locales.empty?
66
+
67
+ if I18n.enforce_available_locales
68
+ locale = locales.reverse.find { |locale| I18n.available_locales.any? { |al| match?(al, locale) } }
69
+ if locale
70
+ I18n.available_locales.find { |al| match?(al, locale) }
42
71
  end
43
- }
72
+ else
73
+ locales.last
74
+ end
75
+ end
44
76
 
45
- lang = language_and_qvalue && language_and_qvalue.first
46
- lang == '*' ? nil : lang
77
+ def match?(s1, s2)
78
+ s1.to_s.casecmp(s2.to_s) == 0
47
79
  end
48
80
  end
49
81
  end
@@ -90,7 +90,7 @@ module Rack
90
90
  mail.delivery_method :test
91
91
  elsif config[:smtp]
92
92
  smtp = config[:smtp]
93
- # for backward compability, replace the :server key with :address
93
+ # for backward compatibility, replace the :server key with :address
94
94
  address = smtp.delete :server
95
95
  smtp[:address] = address if address
96
96
  mail.delivery_method :smtp, smtp
@@ -19,7 +19,7 @@ module Rack
19
19
  else
20
20
  @content = F.read(path)
21
21
  end
22
- @length = @content.size.to_s
22
+ @length = @content.bytesize.to_s
23
23
 
24
24
  @content_type = content_type
25
25
  end
@@ -6,10 +6,51 @@ end
6
6
 
7
7
  module Rack
8
8
 
9
+ # <b>DEPRECATED:</b> <tt>JSONBodyParser</tt> is a drop-in replacement that is faster and more configurable.
10
+ #
9
11
  # A Rack middleware for parsing POST/PUT body data when Content-Type is
10
12
  # not one of the standard supported types, like <tt>application/json</tt>.
11
13
  #
12
- # TODO: Find a better name.
14
+ # === How to use the middleware
15
+ #
16
+ # Example of simple +config.ru+ file:
17
+ #
18
+ # require 'rack'
19
+ # require 'rack/contrib'
20
+ #
21
+ # use ::Rack::PostBodyContentTypeParser
22
+ #
23
+ # app = lambda do |env|
24
+ # request = Rack::Request.new(env)
25
+ # body = "Hello #{request.params['name']}"
26
+ # [200, {'Content-Type' => 'text/plain'}, [body]]
27
+ # end
28
+ #
29
+ # run app
30
+ #
31
+ # Example with passing block argument:
32
+ #
33
+ # use ::Rack::PostBodyContentTypeParser do |body|
34
+ # { 'params' => JSON.parse(body) }
35
+ # end
36
+ #
37
+ # Example with passing proc argument:
38
+ #
39
+ # parser = ->(body) { { 'params' => JSON.parse(body) } }
40
+ # use ::Rack::PostBodyContentTypeParser, &parser
41
+ #
42
+ #
43
+ # === Failed JSON parsing
44
+ #
45
+ # Returns "400 Bad request" response if invalid JSON document was sent:
46
+ #
47
+ # Raw HTTP response:
48
+ #
49
+ # HTTP/1.1 400 Bad Request
50
+ # Content-Type: text/plain
51
+ # Content-Length: 28
52
+ #
53
+ # failed to parse body as JSON
13
54
  #
14
55
  class PostBodyContentTypeParser
15
56
 
@@ -24,14 +65,16 @@ module Rack
24
65
  #
25
66
  APPLICATION_JSON = 'application/json'.freeze
26
67
 
27
- def initialize(app)
68
+ def initialize(app, &block)
69
+ warn "[DEPRECATION] `PostBodyContentTypeParser` is deprecated. Use `JSONBodyParser` as a drop-in replacement."
28
70
  @app = app
71
+ @block = block || Proc.new { |body| JSON.parse(body, :create_additions => false) }
29
72
  end
30
73
 
31
74
  def call(env)
32
75
  if Rack::Request.new(env).media_type == APPLICATION_JSON && (body = env[POST_BODY].read).length != 0
33
76
  env[POST_BODY].rewind # somebody might try to read this stream
34
- env.update(FORM_HASH => JSON.parse(body, :create_additions => false), FORM_INPUT => env[POST_BODY])
77
+ env.update(FORM_HASH => @block.call(body), FORM_INPUT => env[POST_BODY])
35
78
  end
36
79
  @app.call(env)
37
80
  rescue JSON::ParserError
@@ -39,7 +82,7 @@ module Rack
39
82
  end
40
83
 
41
84
  def bad_request(body = 'Bad Request')
42
- [ 400, { 'Content-Type' => 'text/plain', 'Content-Length' => body.size.to_s }, [body] ]
85
+ [ 400, { 'Content-Type' => 'text/plain', 'Content-Length' => body.bytesize.to_s }, [body] ]
43
86
  end
44
87
  end
45
88
  end
@@ -4,7 +4,10 @@ module Rack
4
4
  # Set the profile=process_time query parameter to download a
5
5
  # calltree profile of the request.
6
6
  #
7
- # Pass the :printer option to pick a different result format.
7
+ # Pass the :printer option to pick a different result format. Note that
8
+ # some printers (such as CallTreePrinter) have broken the
9
+ # `AbstractPrinter` API, and thus will not work. Bug reports to
10
+ # `ruby-prof`, please, not us.
8
11
  #
9
12
  # You can cause every request to be run multiple times by passing the
10
13
  # `:times` option to the `use Rack::Profiler` call. You can also run a
@@ -3,9 +3,12 @@ require 'rack'
3
3
 
4
4
  # Rack::ResponseCache is a Rack middleware that caches responses for successful
5
5
  # 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.
6
+ # []= method (so it works with memcached). As with Rails' page caching, this
7
+ # middleware only writes to the cache -- it never reads. The logic of whether a
8
+ # cached response should be served is left either to your web server, via
9
+ # something like the <tt>try_files</tt> directive in nginx, or to your
10
+ # cache-reading middleware of choice, mounted before Rack::ResponseCache in the
11
+ # stack.
9
12
  class Rack::ResponseCache
10
13
  # The default proc used if a block is not provided to .new
11
14
  # It unescapes the PATH_INFO of the environment, and makes sure that it doesn't
@@ -15,7 +18,14 @@ class Rack::ResponseCache
15
18
  # of the path to index.html.
16
19
  DEFAULT_PATH_PROC = proc do |env, res|
17
20
  path = Rack::Utils.unescape(env['PATH_INFO'])
18
- if !path.include?('..') and match = /text\/((?:x|ht)ml|css)/o.match(res[1]['Content-Type'])
21
+ 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
28
+ if !path.include?('..') and match = /text\/((?:x|ht)ml|css)/o.match(content_type)
19
29
  type = match[1]
20
30
  path = "#{path}.#{type}" unless /\.#{type}\z/.match(path)
21
31
  path = File.join(File.dirname(path), 'index.html') if type == 'html' and File.basename(path) == '.html'
@@ -23,7 +33,7 @@ class Rack::ResponseCache
23
33
  end
24
34
  end
25
35
 
26
- # Initialize a new ReponseCache object with the given arguments. Arguments:
36
+ # Initialize a new ResponseCache object with the given arguments. Arguments:
27
37
  # * app : The next middleware in the chain. This is always called.
28
38
  # * cache : The place to cache responses. If a string is provided, a disk
29
39
  # cache is used, and all cached files will use this directory as the root directory.
@@ -42,7 +52,7 @@ class Rack::ResponseCache
42
52
  # Call the next middleware with the environment. If the request was successful (response status 200),
43
53
  # was a GET request, and had an empty query string, call the block set up in initialize to get the path.
44
54
  # 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.
55
+ # subdirectory of cache. Otherwise, cache the body of the response as the value with the path as the key.
46
56
  def call(env)
47
57
  res = @app.call(env)
48
58
  if env['REQUEST_METHOD'] == 'GET' and env['QUERY_STRING'] == '' and res[0] == 200 and path = @path_proc.call(env, res)
@@ -1,3 +1,5 @@
1
+ require 'time'
2
+
1
3
  module Rack
2
4
 
3
5
  #
@@ -16,7 +18,7 @@ module Rack
16
18
  #
17
19
  # Another way to bypass the cache is adding the version number in a field-value pair in the
18
20
  # 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.
21
+ # In that case, set the option :versioning to false to avoid unnecessary regexp calculations.
20
22
  #
21
23
  # It's better to keep the current version number in some config file and use it in every static
22
24
  # content's URL. So each time we modify our static contents, we just have to change the version
@@ -40,7 +42,7 @@ module Rack
40
42
  # default headers.
41
43
  #
42
44
  # 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
45
+ # will serve all requests beginning with /images under the current directory (default for the option :root
44
46
  # is current directory). All the contents served will have cache expiration duration set to 2 years in headers
45
47
  # (default for :duration is 1 year), and StaticCache will not compute any versioning logics (default for
46
48
  # :versioning is true)
@@ -68,7 +70,6 @@ module Rack
68
70
  @version_regex = options.fetch(:version_regex, /-[\d.]+([.][a-zA-Z][\w]+)?$/)
69
71
  end
70
72
  @duration_in_seconds = self.duration_in_seconds
71
- @duration_in_words = self.duration_in_words
72
73
  end
73
74
 
74
75
  def call(env)
@@ -83,14 +84,15 @@ module Rack
83
84
  status, headers, body = @file_server.call(env)
84
85
  if @no_cache[url].nil?
85
86
  headers['Cache-Control'] ="max-age=#{@duration_in_seconds}, public"
86
- headers['Expires'] = @duration_in_words
87
+ headers['Expires'] = duration_in_words
87
88
  end
89
+ headers['Date'] = Time.now.httpdate
88
90
  [status, headers, body]
89
91
  end
90
92
  end
91
93
 
92
94
  def duration_in_words
93
- (Time.now + self.duration_in_seconds).strftime '%a, %d %b %Y %H:%M:%S GMT'
95
+ (Time.now.utc + self.duration_in_seconds).httpdate
94
96
  end
95
97
 
96
98
  def duration_in_seconds
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.0.0
4
+ version: 2.2.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: 2017-11-30 00:00:00.000000000 Z
11
+ date: 2020-03-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -28,16 +28,22 @@ dependencies:
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '1.0'
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: '3'
34
37
  type: :development
35
38
  prerelease: false
36
39
  version_requirements: !ruby/object:Gem::Requirement
37
40
  requirements:
38
- - - "~>"
41
+ - - ">="
39
42
  - !ruby/object:Gem::Version
40
43
  version: '1.0'
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: '3'
41
47
  - !ruby/object:Gem::Dependency
42
48
  name: git-version-bump
43
49
  requirement: !ruby/object:Gem::Requirement
@@ -216,14 +222,28 @@ dependencies:
216
222
  requirements:
217
223
  - - "~>"
218
224
  - !ruby/object:Gem::Version
219
- version: 0.13.0
225
+ version: '0.17'
226
+ type: :development
227
+ prerelease: false
228
+ version_requirements: !ruby/object:Gem::Requirement
229
+ requirements:
230
+ - - "~>"
231
+ - !ruby/object:Gem::Version
232
+ version: '0.17'
233
+ - !ruby/object:Gem::Dependency
234
+ name: timecop
235
+ requirement: !ruby/object:Gem::Requirement
236
+ requirements:
237
+ - - "~>"
238
+ - !ruby/object:Gem::Version
239
+ version: '0.9'
220
240
  type: :development
221
241
  prerelease: false
222
242
  version_requirements: !ruby/object:Gem::Requirement
223
243
  requirements:
224
244
  - - "~>"
225
245
  - !ruby/object:Gem::Version
226
- version: 0.13.0
246
+ version: '0.9'
227
247
  description: Contributed Rack Middleware and Utilities
228
248
  email: rack-devel@googlegroups.com
229
249
  executables: []
@@ -250,6 +270,7 @@ files:
250
270
  - lib/rack/contrib/expectation_cascade.rb
251
271
  - lib/rack/contrib/garbagecollector.rb
252
272
  - lib/rack/contrib/host_meta.rb
273
+ - lib/rack/contrib/json_body_parser.rb
253
274
  - lib/rack/contrib/jsonp.rb
254
275
  - lib/rack/contrib/lazy_conditional_get.rb
255
276
  - lib/rack/contrib/lighttpd_script_name_fix.rb
@@ -271,7 +292,7 @@ files:
271
292
  - lib/rack/contrib/static_cache.rb
272
293
  - lib/rack/contrib/time_zone.rb
273
294
  - lib/rack/contrib/try_static.rb
274
- homepage: http://github.com/rack/rack-contrib/
295
+ homepage: https://github.com/rack/rack-contrib/
275
296
  licenses:
276
297
  - MIT
277
298
  metadata: {}
@@ -296,8 +317,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
296
317
  - !ruby/object:Gem::Version
297
318
  version: '0'
298
319
  requirements: []
299
- rubyforge_project:
300
- rubygems_version: 2.6.13
320
+ rubygems_version: 3.0.3
301
321
  signing_key:
302
322
  specification_version: 2
303
323
  summary: Contributed Rack Middleware and Utilities