rack 2.1.0 → 3.1.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 might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +377 -16
- data/CONTRIBUTING.md +144 -0
- data/MIT-LICENSE +1 -1
- data/README.md +328 -0
- data/SPEC.rdoc +365 -0
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +2 -2
- data/lib/rack/auth/basic.rb +4 -7
- data/lib/rack/bad_request.rb +8 -0
- data/lib/rack/body_proxy.rb +34 -12
- data/lib/rack/builder.rb +162 -59
- data/lib/rack/cascade.rb +24 -10
- data/lib/rack/common_logger.rb +43 -28
- data/lib/rack/conditional_get.rb +30 -25
- data/lib/rack/constants.rb +66 -0
- data/lib/rack/content_length.rb +10 -16
- data/lib/rack/content_type.rb +9 -7
- data/lib/rack/deflater.rb +78 -50
- data/lib/rack/directory.rb +86 -63
- data/lib/rack/etag.rb +14 -22
- data/lib/rack/events.rb +18 -17
- data/lib/rack/files.rb +99 -61
- data/lib/rack/head.rb +8 -9
- data/lib/rack/headers.rb +238 -0
- data/lib/rack/lint.rb +868 -642
- data/lib/rack/lock.rb +2 -6
- data/lib/rack/logger.rb +3 -0
- data/lib/rack/media_type.rb +9 -4
- data/lib/rack/method_override.rb +6 -2
- data/lib/rack/mime.rb +14 -5
- data/lib/rack/mock.rb +1 -253
- data/lib/rack/mock_request.rb +171 -0
- data/lib/rack/mock_response.rb +124 -0
- data/lib/rack/multipart/generator.rb +15 -8
- data/lib/rack/multipart/parser.rb +238 -107
- data/lib/rack/multipart/uploaded_file.rb +17 -7
- data/lib/rack/multipart.rb +54 -42
- data/lib/rack/null_logger.rb +9 -0
- data/lib/rack/query_parser.rb +87 -105
- data/lib/rack/recursive.rb +3 -1
- data/lib/rack/reloader.rb +0 -4
- data/lib/rack/request.rb +366 -135
- data/lib/rack/response.rb +186 -68
- data/lib/rack/rewindable_input.rb +24 -6
- data/lib/rack/runtime.rb +8 -7
- data/lib/rack/sendfile.rb +29 -27
- data/lib/rack/show_exceptions.rb +27 -12
- data/lib/rack/show_status.rb +21 -13
- data/lib/rack/static.rb +19 -12
- data/lib/rack/tempfile_reaper.rb +14 -5
- data/lib/rack/urlmap.rb +5 -6
- data/lib/rack/utils.rb +274 -260
- data/lib/rack/version.rb +21 -0
- data/lib/rack.rb +18 -103
- metadata +25 -52
- data/README.rdoc +0 -262
- data/Rakefile +0 -123
- data/SPEC +0 -263
- data/bin/rackup +0 -5
- data/contrib/rack.png +0 -0
- data/contrib/rack.svg +0 -150
- data/contrib/rack_logo.svg +0 -164
- data/contrib/rdoc.css +0 -412
- data/example/lobster.ru +0 -6
- data/example/protectedlobster.rb +0 -16
- data/example/protectedlobster.ru +0 -10
- data/lib/rack/auth/digest/md5.rb +0 -131
- data/lib/rack/auth/digest/nonce.rb +0 -54
- data/lib/rack/auth/digest/params.rb +0 -54
- data/lib/rack/auth/digest/request.rb +0 -43
- data/lib/rack/chunked.rb +0 -92
- data/lib/rack/core_ext/regexp.rb +0 -14
- data/lib/rack/file.rb +0 -8
- data/lib/rack/handler/cgi.rb +0 -62
- data/lib/rack/handler/fastcgi.rb +0 -102
- data/lib/rack/handler/lsws.rb +0 -63
- data/lib/rack/handler/scgi.rb +0 -73
- data/lib/rack/handler/thin.rb +0 -38
- data/lib/rack/handler/webrick.rb +0 -122
- data/lib/rack/handler.rb +0 -104
- data/lib/rack/lobster.rb +0 -72
- data/lib/rack/server.rb +0 -467
- data/lib/rack/session/abstract/id.rb +0 -528
- data/lib/rack/session/cookie.rb +0 -205
- data/lib/rack/session/memcache.rb +0 -10
- data/lib/rack/session/pool.rb +0 -85
- data/rack.gemspec +0 -44
data/lib/rack/conditional_get.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require_relative 'constants'
|
4
|
+
require_relative 'utils'
|
5
|
+
require_relative 'body_proxy'
|
4
6
|
|
5
7
|
module Rack
|
6
8
|
|
7
|
-
# Middleware that enables conditional GET using
|
8
|
-
#
|
9
|
-
#
|
9
|
+
# Middleware that enables conditional GET using if-none-match and
|
10
|
+
# if-modified-since. The application should set either or both of the
|
11
|
+
# last-modified or etag response headers according to RFC 2616. When
|
10
12
|
# either of the conditions is met, the response body is set to be zero
|
11
13
|
# length and the response status is set to 304 Not Modified.
|
12
14
|
#
|
@@ -21,21 +23,22 @@ module Rack
|
|
21
23
|
@app = app
|
22
24
|
end
|
23
25
|
|
26
|
+
# Return empty 304 response if the response has not been
|
27
|
+
# modified since the last request.
|
24
28
|
def call(env)
|
25
29
|
case env[REQUEST_METHOD]
|
26
30
|
when "GET", "HEAD"
|
27
|
-
status, headers, body = @app.call(env)
|
28
|
-
|
31
|
+
status, headers, body = response = @app.call(env)
|
32
|
+
|
29
33
|
if status == 200 && fresh?(env, headers)
|
30
|
-
|
34
|
+
response[0] = 304
|
31
35
|
headers.delete(CONTENT_TYPE)
|
32
36
|
headers.delete(CONTENT_LENGTH)
|
33
|
-
|
34
|
-
|
35
|
-
original_body.close if original_body.respond_to?(:close)
|
37
|
+
response[2] = Rack::BodyProxy.new([]) do
|
38
|
+
body.close if body.respond_to?(:close)
|
36
39
|
end
|
37
40
|
end
|
38
|
-
|
41
|
+
response
|
39
42
|
else
|
40
43
|
@app.call(env)
|
41
44
|
end
|
@@ -43,28 +46,32 @@ module Rack
|
|
43
46
|
|
44
47
|
private
|
45
48
|
|
49
|
+
# Return whether the response has not been modified since the
|
50
|
+
# last request.
|
46
51
|
def fresh?(env, headers)
|
47
|
-
|
48
|
-
none_match
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
success &&= modified_since?(to_rfc2822(modified_since), headers) if modified_since
|
54
|
-
success &&= etag_matches?(none_match, headers) if none_match
|
55
|
-
success
|
52
|
+
# if-none-match has priority over if-modified-since per RFC 7232
|
53
|
+
if none_match = env['HTTP_IF_NONE_MATCH']
|
54
|
+
etag_matches?(none_match, headers)
|
55
|
+
elsif (modified_since = env['HTTP_IF_MODIFIED_SINCE']) && (modified_since = to_rfc2822(modified_since))
|
56
|
+
modified_since?(modified_since, headers)
|
57
|
+
end
|
56
58
|
end
|
57
59
|
|
60
|
+
# Whether the etag response header matches the if-none-match request header.
|
61
|
+
# If so, the request has not been modified.
|
58
62
|
def etag_matches?(none_match, headers)
|
59
|
-
|
63
|
+
headers[ETAG] == none_match
|
60
64
|
end
|
61
65
|
|
66
|
+
# Whether the last-modified response header matches the if-modified-since
|
67
|
+
# request header. If so, the request has not been modified.
|
62
68
|
def modified_since?(modified_since, headers)
|
63
|
-
last_modified = to_rfc2822(headers['
|
64
|
-
modified_since and
|
69
|
+
last_modified = to_rfc2822(headers['last-modified']) and
|
65
70
|
modified_since >= last_modified
|
66
71
|
end
|
67
72
|
|
73
|
+
# Return a Time object for the given string (which should be in RFC2822
|
74
|
+
# format), or nil if the string cannot be parsed.
|
68
75
|
def to_rfc2822(since)
|
69
76
|
# shortest possible valid date is the obsolete: 1 Nov 97 09:55 A
|
70
77
|
# anything shorter is invalid, this avoids exceptions for common cases
|
@@ -73,8 +80,6 @@ module Rack
|
|
73
80
|
# NOTE: there is no trivial way to write this in a non exception way
|
74
81
|
# _rfc2822 returns a hash but is not that usable
|
75
82
|
Time.rfc2822(since) rescue nil
|
76
|
-
else
|
77
|
-
nil
|
78
83
|
end
|
79
84
|
end
|
80
85
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
# Request env keys
|
5
|
+
HTTP_HOST = 'HTTP_HOST'
|
6
|
+
HTTP_PORT = 'HTTP_PORT'
|
7
|
+
HTTPS = 'HTTPS'
|
8
|
+
PATH_INFO = 'PATH_INFO'
|
9
|
+
REQUEST_METHOD = 'REQUEST_METHOD'
|
10
|
+
REQUEST_PATH = 'REQUEST_PATH'
|
11
|
+
SCRIPT_NAME = 'SCRIPT_NAME'
|
12
|
+
QUERY_STRING = 'QUERY_STRING'
|
13
|
+
SERVER_PROTOCOL = 'SERVER_PROTOCOL'
|
14
|
+
SERVER_NAME = 'SERVER_NAME'
|
15
|
+
SERVER_PORT = 'SERVER_PORT'
|
16
|
+
HTTP_COOKIE = 'HTTP_COOKIE'
|
17
|
+
|
18
|
+
# Response Header Keys
|
19
|
+
CACHE_CONTROL = 'cache-control'
|
20
|
+
CONTENT_LENGTH = 'content-length'
|
21
|
+
CONTENT_TYPE = 'content-type'
|
22
|
+
ETAG = 'etag'
|
23
|
+
EXPIRES = 'expires'
|
24
|
+
SET_COOKIE = 'set-cookie'
|
25
|
+
|
26
|
+
# HTTP method verbs
|
27
|
+
GET = 'GET'
|
28
|
+
POST = 'POST'
|
29
|
+
PUT = 'PUT'
|
30
|
+
PATCH = 'PATCH'
|
31
|
+
DELETE = 'DELETE'
|
32
|
+
HEAD = 'HEAD'
|
33
|
+
OPTIONS = 'OPTIONS'
|
34
|
+
CONNECT = 'CONNECT'
|
35
|
+
LINK = 'LINK'
|
36
|
+
UNLINK = 'UNLINK'
|
37
|
+
TRACE = 'TRACE'
|
38
|
+
|
39
|
+
# Rack environment variables
|
40
|
+
RACK_VERSION = 'rack.version'
|
41
|
+
RACK_TEMPFILES = 'rack.tempfiles'
|
42
|
+
RACK_EARLY_HINTS = 'rack.early_hints'
|
43
|
+
RACK_ERRORS = 'rack.errors'
|
44
|
+
RACK_LOGGER = 'rack.logger'
|
45
|
+
RACK_INPUT = 'rack.input'
|
46
|
+
RACK_SESSION = 'rack.session'
|
47
|
+
RACK_SESSION_OPTIONS = 'rack.session.options'
|
48
|
+
RACK_SHOWSTATUS_DETAIL = 'rack.showstatus.detail'
|
49
|
+
RACK_URL_SCHEME = 'rack.url_scheme'
|
50
|
+
RACK_HIJACK = 'rack.hijack'
|
51
|
+
RACK_IS_HIJACK = 'rack.hijack?'
|
52
|
+
RACK_RECURSIVE_INCLUDE = 'rack.recursive.include'
|
53
|
+
RACK_MULTIPART_BUFFER_SIZE = 'rack.multipart.buffer_size'
|
54
|
+
RACK_MULTIPART_TEMPFILE_FACTORY = 'rack.multipart.tempfile_factory'
|
55
|
+
RACK_RESPONSE_FINISHED = 'rack.response_finished'
|
56
|
+
RACK_REQUEST_FORM_INPUT = 'rack.request.form_input'
|
57
|
+
RACK_REQUEST_FORM_HASH = 'rack.request.form_hash'
|
58
|
+
RACK_REQUEST_FORM_PAIRS = 'rack.request.form_pairs'
|
59
|
+
RACK_REQUEST_FORM_VARS = 'rack.request.form_vars'
|
60
|
+
RACK_REQUEST_FORM_ERROR = 'rack.request.form_error'
|
61
|
+
RACK_REQUEST_COOKIE_HASH = 'rack.request.cookie_hash'
|
62
|
+
RACK_REQUEST_COOKIE_STRING = 'rack.request.cookie_string'
|
63
|
+
RACK_REQUEST_QUERY_HASH = 'rack.request.query_hash'
|
64
|
+
RACK_REQUEST_QUERY_STRING = 'rack.request.query_string'
|
65
|
+
RACK_METHODOVERRIDE_ORIGINAL_METHOD = 'rack.methodoverride.original_method'
|
66
|
+
end
|
data/lib/rack/content_length.rb
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
require_relative 'constants'
|
4
|
+
require_relative 'utils'
|
5
5
|
|
6
6
|
module Rack
|
7
7
|
|
8
|
-
# Sets the
|
8
|
+
# Sets the content-length header on responses that do not specify
|
9
|
+
# a content-length or transfer-encoding header. Note that this
|
10
|
+
# does not fix responses that have an invalid content-length
|
11
|
+
# header specified.
|
9
12
|
class ContentLength
|
10
13
|
include Rack::Utils
|
11
14
|
|
@@ -14,26 +17,17 @@ module Rack
|
|
14
17
|
end
|
15
18
|
|
16
19
|
def call(env)
|
17
|
-
status, headers, body = @app.call(env)
|
18
|
-
headers = HeaderHash.new(headers)
|
20
|
+
status, headers, body = response = @app.call(env)
|
19
21
|
|
20
22
|
if !STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i) &&
|
21
23
|
!headers[CONTENT_LENGTH] &&
|
22
|
-
!headers[TRANSFER_ENCODING] &&
|
23
24
|
body.respond_to?(:to_ary)
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
obody.each { |part| body << part; length += part.bytesize }
|
28
|
-
|
29
|
-
body = BodyProxy.new(body) do
|
30
|
-
obody.close if obody.respond_to?(:close)
|
31
|
-
end
|
32
|
-
|
33
|
-
headers[CONTENT_LENGTH] = length.to_s
|
26
|
+
response[2] = body = body.to_ary
|
27
|
+
headers[CONTENT_LENGTH] = body.sum(&:bytesize).to_s
|
34
28
|
end
|
35
29
|
|
36
|
-
|
30
|
+
response
|
37
31
|
end
|
38
32
|
end
|
39
33
|
end
|
data/lib/rack/content_type.rb
CHANGED
@@ -1,31 +1,33 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require_relative 'constants'
|
4
|
+
require_relative 'utils'
|
4
5
|
|
5
6
|
module Rack
|
6
7
|
|
7
|
-
# Sets the
|
8
|
+
# Sets the content-type header on responses which don't have one.
|
8
9
|
#
|
9
10
|
# Builder Usage:
|
10
11
|
# use Rack::ContentType, "text/plain"
|
11
12
|
#
|
12
|
-
# When no content type argument is provided, "text/html" is
|
13
|
+
# When no content type argument is provided, "text/html" is the
|
14
|
+
# default.
|
13
15
|
class ContentType
|
14
16
|
include Rack::Utils
|
15
17
|
|
16
18
|
def initialize(app, content_type = "text/html")
|
17
|
-
@app
|
19
|
+
@app = app
|
20
|
+
@content_type = content_type
|
18
21
|
end
|
19
22
|
|
20
23
|
def call(env)
|
21
|
-
status, headers,
|
22
|
-
headers = Utils::HeaderHash.new(headers)
|
24
|
+
status, headers, _ = response = @app.call(env)
|
23
25
|
|
24
26
|
unless STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i)
|
25
27
|
headers[CONTENT_TYPE] ||= @content_type
|
26
28
|
end
|
27
29
|
|
28
|
-
|
30
|
+
response
|
29
31
|
end
|
30
32
|
end
|
31
33
|
end
|
data/lib/rack/deflater.rb
CHANGED
@@ -2,51 +2,52 @@
|
|
2
2
|
|
3
3
|
require "zlib"
|
4
4
|
require "time" # for Time.httpdate
|
5
|
-
require 'rack/utils'
|
6
5
|
|
7
|
-
require_relative '
|
6
|
+
require_relative 'constants'
|
7
|
+
require_relative 'utils'
|
8
|
+
require_relative 'request'
|
9
|
+
require_relative 'body_proxy'
|
8
10
|
|
9
11
|
module Rack
|
10
|
-
# This middleware enables
|
12
|
+
# This middleware enables content encoding of http responses,
|
13
|
+
# usually for purposes of compression.
|
11
14
|
#
|
12
|
-
# Currently supported
|
15
|
+
# Currently supported encodings:
|
13
16
|
#
|
14
|
-
#
|
15
|
-
#
|
17
|
+
# * gzip
|
18
|
+
# * identity (no transformation)
|
16
19
|
#
|
17
|
-
#
|
18
|
-
# and allowed. For example no
|
19
|
-
# directive of 'no-transform' is present,
|
20
|
-
# code is one that doesn't allow an entity body
|
20
|
+
# This middleware automatically detects when encoding is supported
|
21
|
+
# and allowed. For example no encoding is made when a cache
|
22
|
+
# directive of 'no-transform' is present, when the response status
|
23
|
+
# code is one that doesn't allow an entity body, or when the body
|
24
|
+
# is empty.
|
25
|
+
#
|
26
|
+
# Note that despite the name, Deflater does not support the +deflate+
|
27
|
+
# encoding.
|
21
28
|
class Deflater
|
22
|
-
|
23
|
-
|
24
|
-
##
|
25
|
-
# Creates Rack::Deflater middleware.
|
29
|
+
# Creates Rack::Deflater middleware. Options:
|
26
30
|
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
# compression and throughput. Defaults to `true'.
|
31
|
+
# :if :: a lambda enabling / disabling deflation based on returned boolean value
|
32
|
+
# (e.g <tt>use Rack::Deflater, :if => lambda { |*, body| sum=0; body.each { |i| sum += i.length }; sum > 512 }</tt>).
|
33
|
+
# However, be aware that calling `body.each` inside the block will break cases where `body.each` is not idempotent,
|
34
|
+
# such as when it is an +IO+ instance.
|
35
|
+
# :include :: a list of content types that should be compressed. By default, all content types are compressed.
|
36
|
+
# :sync :: determines if the stream is going to be flushed after every chunk. Flushing after every chunk reduces
|
37
|
+
# latency for time-sensitive streaming applications, but hurts compression and throughput.
|
38
|
+
# Defaults to +true+.
|
36
39
|
def initialize(app, options = {})
|
37
40
|
@app = app
|
38
|
-
|
39
41
|
@condition = options[:if]
|
40
42
|
@compressible_types = options[:include]
|
41
|
-
@sync = options
|
43
|
+
@sync = options.fetch(:sync, true)
|
42
44
|
end
|
43
45
|
|
44
46
|
def call(env)
|
45
|
-
status, headers, body = @app.call(env)
|
46
|
-
headers = Utils::HeaderHash.new(headers)
|
47
|
+
status, headers, body = response = @app.call(env)
|
47
48
|
|
48
49
|
unless should_deflate?(env, status, headers, body)
|
49
|
-
return
|
50
|
+
return response
|
50
51
|
end
|
51
52
|
|
52
53
|
request = Request.new(env)
|
@@ -55,75 +56,102 @@ module Rack
|
|
55
56
|
request.accept_encoding)
|
56
57
|
|
57
58
|
# Set the Vary HTTP header.
|
58
|
-
vary = headers["
|
59
|
-
unless vary.include?("*") || vary.
|
60
|
-
headers["
|
59
|
+
vary = headers["vary"].to_s.split(",").map(&:strip)
|
60
|
+
unless vary.include?("*") || vary.any?{|v| v.downcase == 'accept-encoding'}
|
61
|
+
headers["vary"] = vary.push("Accept-Encoding").join(",")
|
61
62
|
end
|
62
63
|
|
63
64
|
case encoding
|
64
65
|
when "gzip"
|
65
|
-
headers['
|
66
|
-
headers.delete(
|
67
|
-
mtime = headers["
|
66
|
+
headers['content-encoding'] = "gzip"
|
67
|
+
headers.delete(CONTENT_LENGTH)
|
68
|
+
mtime = headers["last-modified"]
|
68
69
|
mtime = Time.httpdate(mtime).to_i if mtime
|
69
|
-
[
|
70
|
+
response[2] = GzipStream.new(body, mtime, @sync)
|
71
|
+
response
|
70
72
|
when "identity"
|
71
|
-
|
72
|
-
when nil
|
73
|
+
response
|
74
|
+
else # when nil
|
75
|
+
# Only possible encoding values here are 'gzip', 'identity', and nil
|
73
76
|
message = "An acceptable encoding for the requested resource #{request.fullpath} could not be found."
|
74
77
|
bp = Rack::BodyProxy.new([message]) { body.close if body.respond_to?(:close) }
|
75
|
-
[406, {
|
78
|
+
[406, { CONTENT_TYPE => "text/plain", CONTENT_LENGTH => message.length.to_s }, bp]
|
76
79
|
end
|
77
80
|
end
|
78
81
|
|
82
|
+
# Body class used for gzip encoded responses.
|
79
83
|
class GzipStream
|
84
|
+
|
85
|
+
BUFFER_LENGTH = 128 * 1_024
|
86
|
+
|
87
|
+
# Initialize the gzip stream. Arguments:
|
88
|
+
# body :: Response body to compress with gzip
|
89
|
+
# mtime :: The modification time of the body, used to set the
|
90
|
+
# modification time in the gzip header.
|
91
|
+
# sync :: Whether to flush each gzip chunk as soon as it is ready.
|
80
92
|
def initialize(body, mtime, sync)
|
81
|
-
@sync = sync
|
82
93
|
@body = body
|
83
94
|
@mtime = mtime
|
95
|
+
@sync = sync
|
84
96
|
end
|
85
97
|
|
98
|
+
# Yield gzip compressed strings to the given block.
|
86
99
|
def each(&block)
|
87
100
|
@writer = block
|
88
101
|
gzip = ::Zlib::GzipWriter.new(self)
|
89
102
|
gzip.mtime = @mtime if @mtime
|
90
|
-
@body.each
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
103
|
+
# @body.each is equivalent to @body.gets (slow)
|
104
|
+
if @body.is_a? ::File # XXX: Should probably be ::IO
|
105
|
+
while part = @body.read(BUFFER_LENGTH)
|
106
|
+
gzip.write(part)
|
107
|
+
gzip.flush if @sync
|
108
|
+
end
|
109
|
+
else
|
110
|
+
@body.each { |part|
|
111
|
+
# Skip empty strings, as they would result in no output,
|
112
|
+
# and flushing empty parts would raise Zlib::BufError.
|
113
|
+
next if part.empty?
|
114
|
+
gzip.write(part)
|
115
|
+
gzip.flush if @sync
|
116
|
+
}
|
117
|
+
end
|
95
118
|
ensure
|
96
|
-
gzip.
|
97
|
-
@writer = nil
|
119
|
+
gzip.finish
|
98
120
|
end
|
99
121
|
|
122
|
+
# Call the block passed to #each with the gzipped data.
|
100
123
|
def write(data)
|
101
124
|
@writer.call(data)
|
102
125
|
end
|
103
126
|
|
127
|
+
# Close the original body if possible.
|
104
128
|
def close
|
105
129
|
@body.close if @body.respond_to?(:close)
|
106
|
-
@body = nil
|
107
130
|
end
|
108
131
|
end
|
109
132
|
|
110
133
|
private
|
111
134
|
|
135
|
+
# Whether the body should be compressed.
|
112
136
|
def should_deflate?(env, status, headers, body)
|
113
137
|
# Skip compressing empty entity body responses and responses with
|
114
138
|
# no-transform set.
|
115
139
|
if Utils::STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i) ||
|
116
|
-
/\bno-transform\b/.match?(headers[
|
117
|
-
|
140
|
+
/\bno-transform\b/.match?(headers[CACHE_CONTROL].to_s) ||
|
141
|
+
headers['content-encoding']&.!~(/\bidentity\b/)
|
118
142
|
return false
|
119
143
|
end
|
120
144
|
|
121
145
|
# Skip if @compressible_types are given and does not include request's content type
|
122
|
-
return false if @compressible_types && !(headers.has_key?(
|
146
|
+
return false if @compressible_types && !(headers.has_key?(CONTENT_TYPE) && @compressible_types.include?(headers[CONTENT_TYPE][/[^;]*/]))
|
123
147
|
|
124
148
|
# Skip if @condition lambda is given and evaluates to false
|
125
149
|
return false if @condition && !@condition.call(env, status, headers, body)
|
126
150
|
|
151
|
+
# No point in compressing empty body, also handles usage with
|
152
|
+
# Rack::Sendfile.
|
153
|
+
return false if headers[CONTENT_LENGTH] == '0'
|
154
|
+
|
127
155
|
true
|
128
156
|
end
|
129
157
|
end
|