rack 2.2.17 → 3.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 might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +501 -70
- data/CONTRIBUTING.md +63 -55
- data/MIT-LICENSE +1 -1
- data/README.md +376 -0
- data/SPEC.rdoc +243 -277
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +5 -1
- data/lib/rack/auth/basic.rb +1 -3
- data/lib/rack/bad_request.rb +8 -0
- data/lib/rack/body_proxy.rb +21 -3
- data/lib/rack/builder.rb +108 -69
- data/lib/rack/cascade.rb +2 -3
- data/lib/rack/common_logger.rb +22 -17
- data/lib/rack/conditional_get.rb +20 -16
- data/lib/rack/constants.rb +68 -0
- data/lib/rack/content_length.rb +12 -16
- data/lib/rack/content_type.rb +8 -5
- data/lib/rack/deflater.rb +40 -26
- data/lib/rack/directory.rb +9 -3
- data/lib/rack/etag.rb +17 -23
- data/lib/rack/events.rb +4 -0
- data/lib/rack/files.rb +15 -17
- data/lib/rack/head.rb +8 -8
- data/lib/rack/headers.rb +238 -0
- data/lib/rack/lint.rb +817 -648
- data/lib/rack/lock.rb +2 -5
- data/lib/rack/media_type.rb +6 -7
- data/lib/rack/method_override.rb +5 -1
- data/lib/rack/mime.rb +14 -5
- data/lib/rack/mock.rb +1 -300
- data/lib/rack/mock_request.rb +161 -0
- data/lib/rack/mock_response.rb +147 -0
- data/lib/rack/multipart/generator.rb +7 -5
- data/lib/rack/multipart/parser.rb +241 -95
- data/lib/rack/multipart/uploaded_file.rb +45 -4
- data/lib/rack/multipart.rb +53 -40
- data/lib/rack/null_logger.rb +9 -0
- data/lib/rack/query_parser.rb +116 -121
- data/lib/rack/recursive.rb +2 -0
- data/lib/rack/reloader.rb +0 -2
- data/lib/rack/request.rb +269 -141
- data/lib/rack/response.rb +151 -66
- data/lib/rack/rewindable_input.rb +27 -5
- data/lib/rack/runtime.rb +7 -6
- data/lib/rack/sendfile.rb +30 -25
- data/lib/rack/show_exceptions.rb +25 -6
- data/lib/rack/show_status.rb +17 -9
- data/lib/rack/static.rb +8 -8
- data/lib/rack/tempfile_reaper.rb +15 -4
- data/lib/rack/urlmap.rb +3 -1
- data/lib/rack/utils.rb +228 -238
- data/lib/rack/version.rb +3 -15
- data/lib/rack.rb +13 -90
- metadata +15 -41
- data/README.rdoc +0 -347
- data/Rakefile +0 -130
- 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 -53
- data/lib/rack/auth/digest/params.rb +0 -54
- data/lib/rack/auth/digest/request.rb +0 -43
- data/lib/rack/chunked.rb +0 -117
- data/lib/rack/core_ext/regexp.rb +0 -14
- data/lib/rack/file.rb +0 -7
- data/lib/rack/handler/cgi.rb +0 -59
- data/lib/rack/handler/fastcgi.rb +0 -100
- data/lib/rack/handler/lsws.rb +0 -61
- data/lib/rack/handler/scgi.rb +0 -71
- data/lib/rack/handler/thin.rb +0 -36
- data/lib/rack/handler/webrick.rb +0 -129
- data/lib/rack/handler.rb +0 -104
- data/lib/rack/lobster.rb +0 -70
- data/lib/rack/logger.rb +0 -20
- data/lib/rack/server.rb +0 -466
- data/lib/rack/session/abstract/id.rb +0 -523
- data/lib/rack/session/cookie.rb +0 -203
- data/lib/rack/session/memcache.rb +0 -10
- data/lib/rack/session/pool.rb +0 -90
- data/rack.gemspec +0 -46
data/lib/rack/body_proxy.rb
CHANGED
@@ -15,7 +15,12 @@ module Rack
|
|
15
15
|
|
16
16
|
# Return whether the wrapped body responds to the method.
|
17
17
|
def respond_to_missing?(method_name, include_all = false)
|
18
|
-
|
18
|
+
case method_name
|
19
|
+
when :to_str
|
20
|
+
false
|
21
|
+
else
|
22
|
+
super or @body.respond_to?(method_name, include_all)
|
23
|
+
end
|
19
24
|
end
|
20
25
|
|
21
26
|
# If not already closed, close the wrapped body and
|
@@ -24,7 +29,7 @@ module Rack
|
|
24
29
|
return if @closed
|
25
30
|
@closed = true
|
26
31
|
begin
|
27
|
-
@body.close if @body.respond_to?
|
32
|
+
@body.close if @body.respond_to?(:close)
|
28
33
|
ensure
|
29
34
|
@block.call
|
30
35
|
end
|
@@ -38,8 +43,21 @@ module Rack
|
|
38
43
|
|
39
44
|
# Delegate missing methods to the wrapped body.
|
40
45
|
def method_missing(method_name, *args, &block)
|
41
|
-
|
46
|
+
case method_name
|
47
|
+
when :to_str
|
48
|
+
super
|
49
|
+
when :to_ary
|
50
|
+
begin
|
51
|
+
@body.__send__(method_name, *args, &block)
|
52
|
+
ensure
|
53
|
+
close
|
54
|
+
end
|
55
|
+
else
|
56
|
+
@body.__send__(method_name, *args, &block)
|
57
|
+
end
|
42
58
|
end
|
59
|
+
# :nocov:
|
43
60
|
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
61
|
+
# :nocov:
|
44
62
|
end
|
45
63
|
end
|
data/lib/rack/builder.rb
CHANGED
@@ -1,35 +1,38 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'urlmap'
|
4
|
+
|
5
|
+
module Rack; end
|
6
|
+
Rack::BUILDER_TOPLEVEL_BINDING = ->(builder){builder.instance_eval{binding}}
|
7
|
+
|
3
8
|
module Rack
|
4
|
-
# Rack::Builder
|
5
|
-
# applications.
|
9
|
+
# Rack::Builder provides a domain-specific language (DSL) to construct Rack
|
10
|
+
# applications. It is primarily used to parse +config.ru+ files which
|
11
|
+
# instantiate several middleware and a final application which are hosted
|
12
|
+
# by a Rack-compatible web server.
|
6
13
|
#
|
7
14
|
# Example:
|
8
15
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
# run Rack::Lobster.new
|
16
|
-
# end
|
17
|
-
# end
|
16
|
+
# app = Rack::Builder.new do
|
17
|
+
# use Rack::CommonLogger
|
18
|
+
# map "/ok" do
|
19
|
+
# run lambda { |env| [200, {'content-type' => 'text/plain'}, ['OK']] }
|
20
|
+
# end
|
21
|
+
# end
|
18
22
|
#
|
19
|
-
#
|
23
|
+
# run app
|
20
24
|
#
|
21
25
|
# Or
|
22
26
|
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
+
# app = Rack::Builder.app do
|
28
|
+
# use Rack::CommonLogger
|
29
|
+
# run lambda { |env| [200, {'content-type' => 'text/plain'}, ['OK']] }
|
30
|
+
# end
|
27
31
|
#
|
28
|
-
#
|
32
|
+
# run app
|
29
33
|
#
|
30
34
|
# +use+ adds middleware to the stack, +run+ dispatches to an application.
|
31
35
|
# You can use +map+ to construct a Rack::URLMap in a convenient way.
|
32
|
-
|
33
36
|
class Builder
|
34
37
|
|
35
38
|
# https://stackoverflow.com/questions/2223882/whats-the-difference-between-utf-8-and-utf-8-without-bom
|
@@ -39,13 +42,11 @@ module Rack
|
|
39
42
|
#
|
40
43
|
# If the config file ends in +.ru+, it is treated as a
|
41
44
|
# rackup file and the contents will be treated as if
|
42
|
-
# specified inside a Rack::Builder block
|
43
|
-
# options.
|
45
|
+
# specified inside a Rack::Builder block.
|
44
46
|
#
|
45
47
|
# If the config file does not end in +.ru+, it is
|
46
48
|
# required and Rack will use the basename of the file
|
47
49
|
# to guess which constant will be the Rack application to run.
|
48
|
-
# The options given will be ignored in this case.
|
49
50
|
#
|
50
51
|
# Examples:
|
51
52
|
#
|
@@ -55,29 +56,24 @@ module Rack
|
|
55
56
|
# Rack::Builder.parse_file('app.rb')
|
56
57
|
# # requires app.rb, which can be anywhere in Ruby's
|
57
58
|
# # load path. After requiring, assumes App constant
|
58
|
-
# #
|
59
|
+
# # is a Rack application
|
59
60
|
#
|
60
61
|
# Rack::Builder.parse_file('./my_app.rb')
|
61
62
|
# # requires ./my_app.rb, which should be in the
|
62
63
|
# # process's current directory. After requiring,
|
63
|
-
# # assumes MyApp constant
|
64
|
-
def self.parse_file(
|
65
|
-
if
|
66
|
-
return self.load_file(
|
64
|
+
# # assumes MyApp constant is a Rack application
|
65
|
+
def self.parse_file(path, **options)
|
66
|
+
if path.end_with?('.ru')
|
67
|
+
return self.load_file(path, **options)
|
67
68
|
else
|
68
|
-
require
|
69
|
-
|
70
|
-
return app, {}
|
69
|
+
require path
|
70
|
+
return Object.const_get(::File.basename(path, '.rb').split('_').map(&:capitalize).join(''))
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
74
74
|
# Load the given file as a rackup file, treating the
|
75
75
|
# contents as if specified inside a Rack::Builder block.
|
76
76
|
#
|
77
|
-
# Treats the first comment at the beginning of a line
|
78
|
-
# that starts with a backslash as options similar to
|
79
|
-
# options passed on a rackup command line.
|
80
|
-
#
|
81
77
|
# Ignores content in the file after +__END__+, so that
|
82
78
|
# use of +__END__+ will not result in a syntax error.
|
83
79
|
#
|
@@ -85,46 +81,56 @@ module Rack
|
|
85
81
|
#
|
86
82
|
# $ cat config.ru
|
87
83
|
#
|
88
|
-
# #\ -p 9393
|
89
|
-
#
|
90
84
|
# use Rack::ContentLength
|
91
85
|
# require './app.rb'
|
92
86
|
# run App
|
93
|
-
def self.load_file(path,
|
94
|
-
|
95
|
-
|
96
|
-
cfgfile = ::File.read(path)
|
97
|
-
cfgfile.slice!(/\A#{UTF_8_BOM}/) if cfgfile.encoding == Encoding::UTF_8
|
87
|
+
def self.load_file(path, **options)
|
88
|
+
config = ::File.read(path)
|
89
|
+
config.slice!(/\A#{UTF_8_BOM}/) if config.encoding == Encoding::UTF_8
|
98
90
|
|
99
|
-
if
|
100
|
-
|
101
|
-
options = opts.parse! $1.split(/\s+/)
|
91
|
+
if config[/^#\\(.*)/]
|
92
|
+
fail "Parsing options from the first comment line is no longer supported: #{path}"
|
102
93
|
end
|
103
94
|
|
104
|
-
|
105
|
-
app = new_from_string cfgfile, path
|
95
|
+
config.sub!(/^__END__\n.*\Z/m, '')
|
106
96
|
|
107
|
-
return
|
97
|
+
return new_from_string(config, path, **options)
|
108
98
|
end
|
109
99
|
|
110
100
|
# Evaluate the given +builder_script+ string in the context of
|
111
101
|
# a Rack::Builder block, returning a Rack application.
|
112
|
-
def self.new_from_string(builder_script,
|
102
|
+
def self.new_from_string(builder_script, path = "(rackup)", **options)
|
103
|
+
builder = self.new(**options)
|
104
|
+
|
113
105
|
# We want to build a variant of TOPLEVEL_BINDING with self as a Rack::Builder instance.
|
114
106
|
# We cannot use instance_eval(String) as that would resolve constants differently.
|
115
|
-
binding
|
116
|
-
eval
|
117
|
-
|
107
|
+
binding = BUILDER_TOPLEVEL_BINDING.call(builder)
|
108
|
+
eval(builder_script, binding, path)
|
109
|
+
|
110
|
+
return builder.to_app
|
118
111
|
end
|
119
112
|
|
120
113
|
# Initialize a new Rack::Builder instance. +default_app+ specifies the
|
121
114
|
# default application if +run+ is not called later. If a block
|
122
|
-
# is given, it is
|
123
|
-
def initialize(default_app = nil, &block)
|
124
|
-
@use
|
115
|
+
# is given, it is evaluated in the context of the instance.
|
116
|
+
def initialize(default_app = nil, **options, &block)
|
117
|
+
@use = []
|
118
|
+
@map = nil
|
119
|
+
@run = default_app
|
120
|
+
@warmup = nil
|
121
|
+
@freeze_app = false
|
122
|
+
@options = options
|
123
|
+
|
125
124
|
instance_eval(&block) if block_given?
|
126
125
|
end
|
127
126
|
|
127
|
+
# Any options provided to the Rack::Builder instance at initialization.
|
128
|
+
# These options can be server-specific. Some general options are:
|
129
|
+
#
|
130
|
+
# * +:isolation+: One of +process+, +thread+ or +fiber+. The execution
|
131
|
+
# isolation model to use.
|
132
|
+
attr :options
|
133
|
+
|
128
134
|
# Create a new Rack::Builder instance and return the Rack application
|
129
135
|
# generated from it.
|
130
136
|
def self.app(default_app = nil, &block)
|
@@ -145,7 +151,7 @@ module Rack
|
|
145
151
|
# end
|
146
152
|
#
|
147
153
|
# use Middleware
|
148
|
-
# run lambda { |env| [200, { "
|
154
|
+
# run lambda { |env| [200, { "content-type" => "text/plain" }, ["OK"]] }
|
149
155
|
#
|
150
156
|
# All requests through to this application will first be processed by the middleware class.
|
151
157
|
# The +call+ method in this example sets an additional environment key which then can be
|
@@ -156,25 +162,42 @@ module Rack
|
|
156
162
|
@use << proc { |app| generate_map(app, mapping) }
|
157
163
|
end
|
158
164
|
@use << proc { |app| middleware.new(app, *args, &block) }
|
165
|
+
|
166
|
+
nil
|
159
167
|
end
|
168
|
+
# :nocov:
|
160
169
|
ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true)
|
170
|
+
# :nocov:
|
161
171
|
|
162
|
-
# Takes
|
163
|
-
#
|
172
|
+
# Takes a block or argument that is an object that responds to #call and
|
173
|
+
# returns a Rack response.
|
174
|
+
#
|
175
|
+
# You can use a block:
|
176
|
+
#
|
177
|
+
# run do |env|
|
178
|
+
# [200, { "content-type" => "text/plain" }, ["Hello World!"]]
|
179
|
+
# end
|
180
|
+
#
|
181
|
+
# You can also provide a lambda:
|
164
182
|
#
|
165
|
-
# run lambda { |env| [200, { "
|
183
|
+
# run lambda { |env| [200, { "content-type" => "text/plain" }, ["OK"]] }
|
166
184
|
#
|
167
|
-
#
|
185
|
+
# You can also provide a class instance:
|
168
186
|
#
|
169
187
|
# class Heartbeat
|
170
|
-
# def
|
171
|
-
# [200, { "
|
188
|
+
# def call(env)
|
189
|
+
# [200, { "content-type" => "text/plain" }, ["OK"]]
|
172
190
|
# end
|
173
191
|
# end
|
174
192
|
#
|
175
|
-
# run Heartbeat
|
176
|
-
|
177
|
-
|
193
|
+
# run Heartbeat.new
|
194
|
+
#
|
195
|
+
def run(app = nil, &block)
|
196
|
+
raise ArgumentError, "Both app and block given!" if app && block_given?
|
197
|
+
|
198
|
+
@run = app || block
|
199
|
+
|
200
|
+
nil
|
178
201
|
end
|
179
202
|
|
180
203
|
# Takes a lambda or block that is used to warm-up the application. This block is called
|
@@ -195,21 +218,35 @@ module Rack
|
|
195
218
|
# the Rack application specified by run inside the block. Other requests will be sent to the
|
196
219
|
# default application specified by run outside the block.
|
197
220
|
#
|
198
|
-
#
|
221
|
+
# class App
|
222
|
+
# def call(env)
|
223
|
+
# [200, {'content-type' => 'text/plain'}, ["Hello World"]]
|
224
|
+
# end
|
225
|
+
# end
|
226
|
+
#
|
227
|
+
# class Heartbeat
|
228
|
+
# def call(env)
|
229
|
+
# [200, { "content-type" => "text/plain" }, ["OK"]]
|
230
|
+
# end
|
231
|
+
# end
|
232
|
+
#
|
233
|
+
# app = Rack::Builder.app do
|
199
234
|
# map '/heartbeat' do
|
200
|
-
# run Heartbeat
|
235
|
+
# run Heartbeat.new
|
201
236
|
# end
|
202
|
-
# run App
|
237
|
+
# run App.new
|
203
238
|
# end
|
204
239
|
#
|
240
|
+
# run app
|
241
|
+
#
|
205
242
|
# The +use+ method can also be used inside the block to specify middleware to run under a specific path:
|
206
243
|
#
|
207
|
-
# Rack::Builder.app do
|
244
|
+
# app = Rack::Builder.app do
|
208
245
|
# map '/heartbeat' do
|
209
246
|
# use Middleware
|
210
|
-
# run Heartbeat
|
247
|
+
# run Heartbeat.new
|
211
248
|
# end
|
212
|
-
# run App
|
249
|
+
# run App.new
|
213
250
|
# end
|
214
251
|
#
|
215
252
|
# This example includes a piece of middleware which will run before +/heartbeat+ requests hit +Heartbeat+.
|
@@ -219,6 +256,8 @@ module Rack
|
|
219
256
|
def map(path, &block)
|
220
257
|
@map ||= {}
|
221
258
|
@map[path] = block
|
259
|
+
|
260
|
+
nil
|
222
261
|
end
|
223
262
|
|
224
263
|
# Freeze the app (set using run) and all middleware instances when building the application
|
data/lib/rack/cascade.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'constants'
|
4
|
+
|
3
5
|
module Rack
|
4
6
|
# Rack::Cascade tries a request on several apps, and returns the
|
5
7
|
# first response that is not 404 or 405 (or in a list of configured
|
@@ -7,9 +9,6 @@ module Rack
|
|
7
9
|
# status codes, return the last response.
|
8
10
|
|
9
11
|
class Cascade
|
10
|
-
# deprecated, no longer used
|
11
|
-
NotFound = [404, { CONTENT_TYPE => "text/plain" }, []]
|
12
|
-
|
13
12
|
# An array of applications to try in order.
|
14
13
|
attr_reader :apps
|
15
14
|
|
data/lib/rack/common_logger.rb
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'constants'
|
4
|
+
require_relative 'utils'
|
5
|
+
require_relative 'body_proxy'
|
6
|
+
require_relative 'request'
|
7
|
+
|
3
8
|
module Rack
|
4
9
|
# Rack::CommonLogger forwards every request to the given +app+, and
|
5
10
|
# logs a line in the
|
@@ -35,36 +40,36 @@ module Rack
|
|
35
40
|
# cause the request not to be logged.
|
36
41
|
def call(env)
|
37
42
|
began_at = Utils.clock_time
|
38
|
-
status, headers, body = @app.call(env)
|
39
|
-
|
40
|
-
|
41
|
-
|
43
|
+
status, headers, body = response = @app.call(env)
|
44
|
+
|
45
|
+
response[2] = BodyProxy.new(body) { log(env, status, headers, began_at) }
|
46
|
+
response
|
42
47
|
end
|
43
48
|
|
44
49
|
private
|
45
50
|
|
46
51
|
# Log the request to the configured logger.
|
47
|
-
def log(env, status,
|
48
|
-
|
52
|
+
def log(env, status, response_headers, began_at)
|
53
|
+
request = Rack::Request.new(env)
|
54
|
+
length = extract_content_length(response_headers)
|
49
55
|
|
50
|
-
msg = FORMAT
|
51
|
-
|
52
|
-
|
56
|
+
msg = sprintf(FORMAT,
|
57
|
+
request.ip || "-",
|
58
|
+
request.get_header("REMOTE_USER") || "-",
|
53
59
|
Time.now.strftime("%d/%b/%Y:%H:%M:%S %z"),
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
60
|
+
request.request_method,
|
61
|
+
request.script_name,
|
62
|
+
request.path_info,
|
63
|
+
request.query_string.empty? ? "" : "?#{request.query_string}",
|
64
|
+
request.get_header(SERVER_PROTOCOL),
|
59
65
|
status.to_s[0..3],
|
60
66
|
length,
|
61
|
-
Utils.clock_time - began_at
|
67
|
+
Utils.clock_time - began_at)
|
62
68
|
|
63
69
|
msg.gsub!(/[^[:print:]]/) { |c| sprintf("\\x%x", c.ord) }
|
64
70
|
msg[-1] = "\n"
|
65
71
|
|
66
|
-
logger = @logger ||
|
67
|
-
|
72
|
+
logger = @logger || request.get_header(RACK_ERRORS)
|
68
73
|
# Standard library logger doesn't support write but it supports << which actually
|
69
74
|
# calls to write on the log device without formatting
|
70
75
|
if logger.respond_to?(:write)
|
data/lib/rack/conditional_get.rb
CHANGED
@@ -1,10 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'constants'
|
4
|
+
require_relative 'utils'
|
5
|
+
require_relative 'body_proxy'
|
6
|
+
|
3
7
|
module Rack
|
4
8
|
|
5
|
-
# Middleware that enables conditional GET using
|
6
|
-
#
|
7
|
-
#
|
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
|
8
12
|
# either of the conditions is met, the response body is set to be zero
|
9
13
|
# length and the response status is set to 304 Not Modified.
|
10
14
|
#
|
@@ -24,18 +28,18 @@ module Rack
|
|
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
|
-
|
36
|
-
|
37
|
+
|
38
|
+
# We are done with the body:
|
39
|
+
body.close if body.respond_to?(:close)
|
40
|
+
response[2] = []
|
37
41
|
end
|
38
|
-
|
42
|
+
response
|
39
43
|
else
|
40
44
|
@app.call(env)
|
41
45
|
end
|
@@ -46,7 +50,7 @@ module Rack
|
|
46
50
|
# Return whether the response has not been modified since the
|
47
51
|
# last request.
|
48
52
|
def fresh?(env, headers)
|
49
|
-
#
|
53
|
+
# if-none-match has priority over if-modified-since per RFC 7232
|
50
54
|
if none_match = env['HTTP_IF_NONE_MATCH']
|
51
55
|
etag_matches?(none_match, headers)
|
52
56
|
elsif (modified_since = env['HTTP_IF_MODIFIED_SINCE']) && (modified_since = to_rfc2822(modified_since))
|
@@ -54,16 +58,16 @@ module Rack
|
|
54
58
|
end
|
55
59
|
end
|
56
60
|
|
57
|
-
# Whether the
|
61
|
+
# Whether the etag response header matches the if-none-match request header.
|
58
62
|
# If so, the request has not been modified.
|
59
63
|
def etag_matches?(none_match, headers)
|
60
|
-
headers[
|
64
|
+
headers[ETAG] == none_match
|
61
65
|
end
|
62
66
|
|
63
|
-
# Whether the
|
67
|
+
# Whether the last-modified response header matches the if-modified-since
|
64
68
|
# request header. If so, the request has not been modified.
|
65
69
|
def modified_since?(modified_since, headers)
|
66
|
-
last_modified = to_rfc2822(headers['
|
70
|
+
last_modified = to_rfc2822(headers['last-modified']) and
|
67
71
|
modified_since >= last_modified
|
68
72
|
end
|
69
73
|
|
@@ -0,0 +1,68 @@
|
|
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
|
+
TRANSFER_ENCODING = 'transfer-encoding'
|
26
|
+
|
27
|
+
# HTTP method verbs
|
28
|
+
GET = 'GET'
|
29
|
+
POST = 'POST'
|
30
|
+
PUT = 'PUT'
|
31
|
+
PATCH = 'PATCH'
|
32
|
+
DELETE = 'DELETE'
|
33
|
+
HEAD = 'HEAD'
|
34
|
+
OPTIONS = 'OPTIONS'
|
35
|
+
CONNECT = 'CONNECT'
|
36
|
+
LINK = 'LINK'
|
37
|
+
UNLINK = 'UNLINK'
|
38
|
+
TRACE = 'TRACE'
|
39
|
+
|
40
|
+
# Rack environment variables
|
41
|
+
RACK_VERSION = 'rack.version'
|
42
|
+
RACK_TEMPFILES = 'rack.tempfiles'
|
43
|
+
RACK_EARLY_HINTS = 'rack.early_hints'
|
44
|
+
RACK_ERRORS = 'rack.errors'
|
45
|
+
RACK_LOGGER = 'rack.logger'
|
46
|
+
RACK_INPUT = 'rack.input'
|
47
|
+
RACK_SESSION = 'rack.session'
|
48
|
+
RACK_SESSION_OPTIONS = 'rack.session.options'
|
49
|
+
RACK_SHOWSTATUS_DETAIL = 'rack.showstatus.detail'
|
50
|
+
RACK_URL_SCHEME = 'rack.url_scheme'
|
51
|
+
RACK_HIJACK = 'rack.hijack'
|
52
|
+
RACK_IS_HIJACK = 'rack.hijack?'
|
53
|
+
RACK_RECURSIVE_INCLUDE = 'rack.recursive.include'
|
54
|
+
RACK_MULTIPART_BUFFER_SIZE = 'rack.multipart.buffer_size'
|
55
|
+
RACK_MULTIPART_TEMPFILE_FACTORY = 'rack.multipart.tempfile_factory'
|
56
|
+
RACK_RESPONSE_FINISHED = 'rack.response_finished'
|
57
|
+
RACK_PROTOCOL = 'rack.protocol'
|
58
|
+
RACK_REQUEST_FORM_INPUT = 'rack.request.form_input'
|
59
|
+
RACK_REQUEST_FORM_HASH = 'rack.request.form_hash'
|
60
|
+
RACK_REQUEST_FORM_PAIRS = 'rack.request.form_pairs'
|
61
|
+
RACK_REQUEST_FORM_VARS = 'rack.request.form_vars'
|
62
|
+
RACK_REQUEST_FORM_ERROR = 'rack.request.form_error'
|
63
|
+
RACK_REQUEST_COOKIE_HASH = 'rack.request.cookie_hash'
|
64
|
+
RACK_REQUEST_COOKIE_STRING = 'rack.request.cookie_string'
|
65
|
+
RACK_REQUEST_QUERY_HASH = 'rack.request.query_hash'
|
66
|
+
RACK_REQUEST_QUERY_STRING = 'rack.request.query_string'
|
67
|
+
RACK_METHODOVERRIDE_ORIGINAL_METHOD = 'rack.methodoverride.original_method'
|
68
|
+
end
|
data/lib/rack/content_length.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'constants'
|
4
|
+
require_relative 'utils'
|
5
|
+
|
3
6
|
module Rack
|
4
7
|
|
5
|
-
# Sets the
|
6
|
-
# a
|
7
|
-
# does not fix responses that have an invalid
|
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
|
8
11
|
# header specified.
|
9
12
|
class ContentLength
|
10
13
|
include Rack::Utils
|
@@ -14,25 +17,18 @@ module Rack
|
|
14
17
|
end
|
15
18
|
|
16
19
|
def call(env)
|
17
|
-
status, headers, body = @app.call(env)
|
18
|
-
headers = HeaderHash[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
|
-
obody = body
|
25
|
-
body, length = [], 0
|
26
|
-
obody.each { |part| body << part; length += part.bytesize }
|
27
|
-
|
28
|
-
body = BodyProxy.new(body) do
|
29
|
-
obody.close if obody.respond_to?(:close)
|
30
|
-
end
|
24
|
+
!headers[TRANSFER_ENCODING] &&
|
25
|
+
body.respond_to?(:to_ary)
|
31
26
|
|
32
|
-
|
27
|
+
response[2] = body = body.to_ary
|
28
|
+
headers[CONTENT_LENGTH] = body.sum(&:bytesize).to_s
|
33
29
|
end
|
34
30
|
|
35
|
-
|
31
|
+
response
|
36
32
|
end
|
37
33
|
end
|
38
34
|
end
|
data/lib/rack/content_type.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'constants'
|
4
|
+
require_relative 'utils'
|
5
|
+
|
3
6
|
module Rack
|
4
7
|
|
5
|
-
# Sets the
|
8
|
+
# Sets the content-type header on responses which don't have one.
|
6
9
|
#
|
7
10
|
# Builder Usage:
|
8
11
|
# use Rack::ContentType, "text/plain"
|
@@ -13,18 +16,18 @@ module Rack
|
|
13
16
|
include Rack::Utils
|
14
17
|
|
15
18
|
def initialize(app, content_type = "text/html")
|
16
|
-
@app
|
19
|
+
@app = app
|
20
|
+
@content_type = content_type
|
17
21
|
end
|
18
22
|
|
19
23
|
def call(env)
|
20
|
-
status, headers,
|
21
|
-
headers = Utils::HeaderHash[headers]
|
24
|
+
status, headers, _ = response = @app.call(env)
|
22
25
|
|
23
26
|
unless STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i)
|
24
27
|
headers[CONTENT_TYPE] ||= @content_type
|
25
28
|
end
|
26
29
|
|
27
|
-
|
30
|
+
response
|
28
31
|
end
|
29
32
|
end
|
30
33
|
end
|