rack-contrib 2.0.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
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