rack 2.1.3 → 2.2.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +626 -1
- data/CONTRIBUTING.md +136 -0
- data/README.rdoc +83 -39
- data/Rakefile +14 -7
- data/{SPEC → SPEC.rdoc} +35 -6
- data/lib/rack.rb +7 -16
- data/lib/rack/auth/abstract/request.rb +0 -2
- data/lib/rack/auth/basic.rb +3 -3
- data/lib/rack/auth/digest/md5.rb +4 -4
- data/lib/rack/auth/digest/request.rb +3 -3
- data/lib/rack/body_proxy.rb +13 -9
- data/lib/rack/builder.rb +77 -8
- data/lib/rack/cascade.rb +23 -8
- data/lib/rack/chunked.rb +48 -23
- data/lib/rack/common_logger.rb +25 -18
- data/lib/rack/conditional_get.rb +18 -16
- data/lib/rack/content_length.rb +6 -7
- data/lib/rack/content_type.rb +3 -4
- data/lib/rack/deflater.rb +45 -35
- data/lib/rack/directory.rb +77 -59
- data/lib/rack/etag.rb +2 -3
- data/lib/rack/events.rb +15 -18
- data/lib/rack/file.rb +1 -1
- data/lib/rack/files.rb +96 -56
- data/lib/rack/handler/cgi.rb +1 -4
- data/lib/rack/handler/fastcgi.rb +1 -3
- data/lib/rack/handler/lsws.rb +1 -3
- data/lib/rack/handler/scgi.rb +1 -3
- data/lib/rack/handler/thin.rb +1 -3
- data/lib/rack/handler/webrick.rb +12 -5
- data/lib/rack/head.rb +0 -2
- data/lib/rack/lint.rb +57 -14
- data/lib/rack/lobster.rb +3 -5
- data/lib/rack/lock.rb +0 -1
- data/lib/rack/mock.rb +22 -4
- data/lib/rack/multipart.rb +1 -1
- data/lib/rack/multipart/generator.rb +11 -6
- data/lib/rack/multipart/parser.rb +7 -15
- data/lib/rack/multipart/uploaded_file.rb +13 -7
- data/lib/rack/query_parser.rb +7 -8
- data/lib/rack/recursive.rb +1 -1
- data/lib/rack/reloader.rb +1 -3
- data/lib/rack/request.rb +182 -76
- data/lib/rack/response.rb +62 -19
- data/lib/rack/rewindable_input.rb +0 -1
- data/lib/rack/runtime.rb +3 -3
- data/lib/rack/sendfile.rb +0 -3
- data/lib/rack/server.rb +9 -8
- data/lib/rack/session/abstract/id.rb +21 -18
- data/lib/rack/session/cookie.rb +1 -3
- data/lib/rack/session/pool.rb +1 -1
- data/lib/rack/show_exceptions.rb +6 -8
- data/lib/rack/show_status.rb +5 -7
- data/lib/rack/static.rb +13 -6
- data/lib/rack/tempfile_reaper.rb +0 -2
- data/lib/rack/urlmap.rb +1 -4
- data/lib/rack/utils.rb +63 -55
- data/lib/rack/version.rb +29 -0
- data/rack.gemspec +31 -29
- metadata +14 -15
data/lib/rack/runtime.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'rack/utils'
|
4
|
-
|
5
3
|
module Rack
|
6
4
|
# Sets an "X-Runtime" response header, indicating the response
|
7
5
|
# time of the request, in seconds
|
@@ -22,9 +20,11 @@ module Rack
|
|
22
20
|
def call(env)
|
23
21
|
start_time = Utils.clock_time
|
24
22
|
status, headers, body = @app.call(env)
|
23
|
+
headers = Utils::HeaderHash[headers]
|
24
|
+
|
25
25
|
request_time = Utils.clock_time - start_time
|
26
26
|
|
27
|
-
unless headers.
|
27
|
+
unless headers.key?(@header_name)
|
28
28
|
headers[@header_name] = FORMAT_STRING % request_time
|
29
29
|
end
|
30
30
|
|
data/lib/rack/sendfile.rb
CHANGED
data/lib/rack/server.rb
CHANGED
@@ -3,12 +3,10 @@
|
|
3
3
|
require 'optparse'
|
4
4
|
require 'fileutils'
|
5
5
|
|
6
|
-
require_relative 'core_ext/regexp'
|
7
|
-
|
8
6
|
module Rack
|
9
7
|
|
10
8
|
class Server
|
11
|
-
using ::Rack::RegexpExtensions
|
9
|
+
(require_relative 'core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
|
12
10
|
|
13
11
|
class Options
|
14
12
|
def parse!(args)
|
@@ -42,7 +40,7 @@ module Rack
|
|
42
40
|
|
43
41
|
opts.on("-r", "--require LIBRARY",
|
44
42
|
"require the library, before executing your script") { |library|
|
45
|
-
options[:require]
|
43
|
+
(options[:require] ||= []) << library
|
46
44
|
}
|
47
45
|
|
48
46
|
opts.separator ""
|
@@ -143,7 +141,7 @@ module Rack
|
|
143
141
|
return "" if !has_options
|
144
142
|
end
|
145
143
|
info.join("\n")
|
146
|
-
rescue NameError
|
144
|
+
rescue NameError, LoadError
|
147
145
|
return "Warning: Could not find handler specified (#{options[:server] || 'default'}) to determine handler-specific options"
|
148
146
|
end
|
149
147
|
end
|
@@ -285,7 +283,7 @@ module Rack
|
|
285
283
|
self.class.middleware
|
286
284
|
end
|
287
285
|
|
288
|
-
def start
|
286
|
+
def start(&block)
|
289
287
|
if options[:warn]
|
290
288
|
$-w = true
|
291
289
|
end
|
@@ -294,7 +292,7 @@ module Rack
|
|
294
292
|
$LOAD_PATH.unshift(*includes)
|
295
293
|
end
|
296
294
|
|
297
|
-
|
295
|
+
Array(options[:require]).each do |library|
|
298
296
|
require library
|
299
297
|
end
|
300
298
|
|
@@ -326,7 +324,7 @@ module Rack
|
|
326
324
|
end
|
327
325
|
end
|
328
326
|
|
329
|
-
server.run
|
327
|
+
server.run(wrapped_app, **options, &block)
|
330
328
|
end
|
331
329
|
|
332
330
|
def server
|
@@ -425,7 +423,10 @@ module Rack
|
|
425
423
|
end
|
426
424
|
|
427
425
|
def daemonize_app
|
426
|
+
# Cannot be covered as it forks
|
427
|
+
# :nocov:
|
428
428
|
Process.daemon
|
429
|
+
# :nocov:
|
429
430
|
end
|
430
431
|
|
431
432
|
def write_pid
|
@@ -3,10 +3,8 @@
|
|
3
3
|
# AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
|
4
4
|
# bugrep: Andreas Zehnder
|
5
5
|
|
6
|
-
|
6
|
+
require_relative '../../../rack'
|
7
7
|
require 'time'
|
8
|
-
require 'rack/request'
|
9
|
-
require 'rack/response'
|
10
8
|
require 'securerandom'
|
11
9
|
require 'digest/sha2'
|
12
10
|
|
@@ -44,18 +42,6 @@ module Rack
|
|
44
42
|
# SessionHash is responsible to lazily load the session from store.
|
45
43
|
|
46
44
|
class SessionHash
|
47
|
-
using Module.new {
|
48
|
-
refine Hash do
|
49
|
-
def transform_keys(&block)
|
50
|
-
hash = {}
|
51
|
-
each do |key, value|
|
52
|
-
hash[block.call(key)] = value
|
53
|
-
end
|
54
|
-
hash
|
55
|
-
end
|
56
|
-
end
|
57
|
-
} unless {}.respond_to?(:transform_keys)
|
58
|
-
|
59
45
|
include Enumerable
|
60
46
|
attr_writer :id
|
61
47
|
|
@@ -98,6 +84,11 @@ module Rack
|
|
98
84
|
@data[key.to_s]
|
99
85
|
end
|
100
86
|
|
87
|
+
def dig(key, *keys)
|
88
|
+
load_for_read!
|
89
|
+
@data.dig(key.to_s, *keys)
|
90
|
+
end
|
91
|
+
|
101
92
|
def fetch(key, default = Unspecified, &block)
|
102
93
|
load_for_read!
|
103
94
|
if default == Unspecified
|
@@ -201,14 +192,19 @@ module Rack
|
|
201
192
|
end
|
202
193
|
|
203
194
|
def stringify_keys(other)
|
204
|
-
|
195
|
+
# Use transform_keys after dropping Ruby 2.4 support
|
196
|
+
hash = {}
|
197
|
+
other.to_hash.each do |key, value|
|
198
|
+
hash[key.to_s] = value
|
199
|
+
end
|
200
|
+
hash
|
205
201
|
end
|
206
202
|
end
|
207
203
|
|
208
204
|
# ID sets up a basic framework for implementing an id based sessioning
|
209
205
|
# service. Cookies sent to the client for maintaining sessions will only
|
210
|
-
# contain an id reference. Only #find_session
|
211
|
-
# required to be overwritten.
|
206
|
+
# contain an id reference. Only #find_session, #write_session and
|
207
|
+
# #delete_session are required to be overwritten.
|
212
208
|
#
|
213
209
|
# All parameters are optional.
|
214
210
|
# * :key determines the name of the cookie, by default it is
|
@@ -256,6 +252,7 @@ module Rack
|
|
256
252
|
@default_options = self.class::DEFAULT_OPTIONS.merge(options)
|
257
253
|
@key = @default_options.delete(:key)
|
258
254
|
@cookie_only = @default_options.delete(:cookie_only)
|
255
|
+
@same_site = @default_options.delete(:same_site)
|
259
256
|
initialize_sid
|
260
257
|
end
|
261
258
|
|
@@ -397,6 +394,12 @@ module Rack
|
|
397
394
|
cookie[:value] = cookie_value(data)
|
398
395
|
cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after]
|
399
396
|
cookie[:expires] = Time.now + options[:max_age] if options[:max_age]
|
397
|
+
|
398
|
+
if @same_site.respond_to? :call
|
399
|
+
cookie[:same_site] = @same_site.call(req, res)
|
400
|
+
else
|
401
|
+
cookie[:same_site] = @same_site
|
402
|
+
end
|
400
403
|
set_cookie(req, res, cookie.merge!(options))
|
401
404
|
end
|
402
405
|
end
|
data/lib/rack/session/cookie.rb
CHANGED
data/lib/rack/session/pool.rb
CHANGED
data/lib/rack/show_exceptions.rb
CHANGED
@@ -2,8 +2,6 @@
|
|
2
2
|
|
3
3
|
require 'ostruct'
|
4
4
|
require 'erb'
|
5
|
-
require 'rack/request'
|
6
|
-
require 'rack/utils'
|
7
5
|
|
8
6
|
module Rack
|
9
7
|
# Rack::ShowExceptions catches all exceptions raised from the app it
|
@@ -65,12 +63,12 @@ module Rack
|
|
65
63
|
def pretty(env, exception)
|
66
64
|
req = Rack::Request.new(env)
|
67
65
|
|
68
|
-
# This double assignment is to prevent an "unused variable" warning
|
69
|
-
#
|
66
|
+
# This double assignment is to prevent an "unused variable" warning.
|
67
|
+
# Yes, it is dumb, but I don't like Ruby yelling at me.
|
70
68
|
path = path = (req.script_name + req.path_info).squeeze("/")
|
71
69
|
|
72
|
-
# This double assignment is to prevent an "unused variable" warning
|
73
|
-
#
|
70
|
+
# This double assignment is to prevent an "unused variable" warning.
|
71
|
+
# Yes, it is dumb, but I don't like Ruby yelling at me.
|
74
72
|
frames = frames = exception.backtrace.map { |line|
|
75
73
|
frame = OpenStruct.new
|
76
74
|
if line =~ /(.*?):(\d+)(:in `(.*)')?/
|
@@ -313,7 +311,7 @@ module Rack
|
|
313
311
|
<% end %>
|
314
312
|
|
315
313
|
<h3 id="post-info">POST</h3>
|
316
|
-
<% if req.POST and not req.POST.empty? %>
|
314
|
+
<% if ((req.POST and not req.POST.empty?) rescue (no_post_data = "Invalid POST data"; nil)) %>
|
317
315
|
<table class="req">
|
318
316
|
<thead>
|
319
317
|
<tr>
|
@@ -331,7 +329,7 @@ module Rack
|
|
331
329
|
</tbody>
|
332
330
|
</table>
|
333
331
|
<% else %>
|
334
|
-
<p
|
332
|
+
<p><%= no_post_data || "No POST data" %>.</p>
|
335
333
|
<% end %>
|
336
334
|
|
337
335
|
|
data/lib/rack/show_status.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'erb'
|
4
|
-
require 'rack/request'
|
5
|
-
require 'rack/utils'
|
6
4
|
|
7
5
|
module Rack
|
8
6
|
# Rack::ShowStatus catches all empty responses and replaces them
|
@@ -20,19 +18,19 @@ module Rack
|
|
20
18
|
|
21
19
|
def call(env)
|
22
20
|
status, headers, body = @app.call(env)
|
23
|
-
headers = Utils::HeaderHash
|
21
|
+
headers = Utils::HeaderHash[headers]
|
24
22
|
empty = headers[CONTENT_LENGTH].to_i <= 0
|
25
23
|
|
26
24
|
# client or server error, or explicit message
|
27
25
|
if (status.to_i >= 400 && empty) || env[RACK_SHOWSTATUS_DETAIL]
|
28
|
-
# This double assignment is to prevent an "unused variable" warning
|
29
|
-
#
|
26
|
+
# This double assignment is to prevent an "unused variable" warning.
|
27
|
+
# Yes, it is dumb, but I don't like Ruby yelling at me.
|
30
28
|
req = req = Rack::Request.new(env)
|
31
29
|
|
32
30
|
message = Rack::Utils::HTTP_STATUS_CODES[status.to_i] || status.to_s
|
33
31
|
|
34
|
-
# This double assignment is to prevent an "unused variable" warning
|
35
|
-
#
|
32
|
+
# This double assignment is to prevent an "unused variable" warning.
|
33
|
+
# Yes, it is dumb, but I don't like Ruby yelling at me.
|
36
34
|
detail = detail = env[RACK_SHOWSTATUS_DETAIL] || message
|
37
35
|
|
38
36
|
body = @template.result(binding)
|
data/lib/rack/static.rb
CHANGED
@@ -1,10 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "rack/files"
|
4
|
-
require "rack/utils"
|
5
|
-
|
6
|
-
require_relative 'core_ext/regexp'
|
7
|
-
|
8
3
|
module Rack
|
9
4
|
|
10
5
|
# The Rack::Static middleware intercepts requests for static files
|
@@ -19,6 +14,11 @@ module Rack
|
|
19
14
|
#
|
20
15
|
# use Rack::Static, :urls => ["/media"]
|
21
16
|
#
|
17
|
+
# Same as previous, but instead of returning 404 for missing files under
|
18
|
+
# /media, call the next middleware:
|
19
|
+
#
|
20
|
+
# use Rack::Static, :urls => ["/media"], :cascade => true
|
21
|
+
#
|
22
22
|
# Serve all requests beginning with /css or /images from the folder "public"
|
23
23
|
# in the current directory (ie public/css/* and public/images/*):
|
24
24
|
#
|
@@ -86,13 +86,14 @@ module Rack
|
|
86
86
|
# ]
|
87
87
|
#
|
88
88
|
class Static
|
89
|
-
using ::Rack::RegexpExtensions
|
89
|
+
(require_relative 'core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
|
90
90
|
|
91
91
|
def initialize(app, options = {})
|
92
92
|
@app = app
|
93
93
|
@urls = options[:urls] || ["/favicon.ico"]
|
94
94
|
@index = options[:index]
|
95
95
|
@gzip = options[:gzip]
|
96
|
+
@cascade = options[:cascade]
|
96
97
|
root = options[:root] || Dir.pwd
|
97
98
|
|
98
99
|
# HTTP Headers
|
@@ -133,6 +134,8 @@ module Rack
|
|
133
134
|
|
134
135
|
if response[0] == 404
|
135
136
|
response = nil
|
137
|
+
elsif response[0] == 304
|
138
|
+
# Do nothing, leave headers as is
|
136
139
|
else
|
137
140
|
if mime_type = Mime.mime_type(::File.extname(path), 'text/plain')
|
138
141
|
response[1][CONTENT_TYPE] = mime_type
|
@@ -144,6 +147,10 @@ module Rack
|
|
144
147
|
path = env[PATH_INFO]
|
145
148
|
response ||= @file_server.call(env)
|
146
149
|
|
150
|
+
if @cascade && response[0] == 404
|
151
|
+
return @app.call(env)
|
152
|
+
end
|
153
|
+
|
147
154
|
headers = response[1]
|
148
155
|
applicable_rules(path).each do |rule, new_headers|
|
149
156
|
new_headers.each { |field, content| headers[field] = content }
|
data/lib/rack/tempfile_reaper.rb
CHANGED
data/lib/rack/urlmap.rb
CHANGED
@@ -16,9 +16,6 @@ module Rack
|
|
16
16
|
# first, since they are most specific.
|
17
17
|
|
18
18
|
class URLMap
|
19
|
-
NEGATIVE_INFINITY = -1.0 / 0.0
|
20
|
-
INFINITY = 1.0 / 0.0
|
21
|
-
|
22
19
|
def initialize(map = {})
|
23
20
|
remap(map)
|
24
21
|
end
|
@@ -42,7 +39,7 @@ module Rack
|
|
42
39
|
|
43
40
|
[host, location, match, app]
|
44
41
|
}.sort_by do |(host, location, _, _)|
|
45
|
-
[host ? -host.size : INFINITY, -location.size]
|
42
|
+
[host ? -host.size : Float::INFINITY, -location.size]
|
46
43
|
end
|
47
44
|
end
|
48
45
|
|
data/lib/rack/utils.rb
CHANGED
@@ -5,17 +5,16 @@ require 'uri'
|
|
5
5
|
require 'fileutils'
|
6
6
|
require 'set'
|
7
7
|
require 'tempfile'
|
8
|
-
require 'rack/query_parser'
|
9
8
|
require 'time'
|
10
9
|
|
11
|
-
require_relative '
|
10
|
+
require_relative 'query_parser'
|
12
11
|
|
13
12
|
module Rack
|
14
13
|
# Rack::Utils contains a grab-bag of useful methods for writing web
|
15
14
|
# applications adopted from all kinds of Ruby libraries.
|
16
15
|
|
17
16
|
module Utils
|
18
|
-
using ::Rack::RegexpExtensions
|
17
|
+
(require_relative 'core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
|
19
18
|
|
20
19
|
ParameterTypeError = QueryParser::ParameterTypeError
|
21
20
|
InvalidParameterError = QueryParser::InvalidParameterError
|
@@ -30,33 +29,30 @@ module Rack
|
|
30
29
|
# This helps prevent a rogue client from flooding a Request.
|
31
30
|
self.default_query_parser = QueryParser.make_default(65536, 100)
|
32
31
|
|
32
|
+
module_function
|
33
|
+
|
33
34
|
# URI escapes. (CGI style space to +)
|
34
35
|
def escape(s)
|
35
36
|
URI.encode_www_form_component(s)
|
36
37
|
end
|
37
|
-
module_function :escape
|
38
38
|
|
39
39
|
# Like URI escaping, but with %20 instead of +. Strictly speaking this is
|
40
40
|
# true URI escaping.
|
41
41
|
def escape_path(s)
|
42
42
|
::URI::DEFAULT_PARSER.escape s
|
43
43
|
end
|
44
|
-
module_function :escape_path
|
45
44
|
|
46
45
|
# Unescapes the **path** component of a URI. See Rack::Utils.unescape for
|
47
46
|
# unescaping query parameters or form components.
|
48
47
|
def unescape_path(s)
|
49
48
|
::URI::DEFAULT_PARSER.unescape s
|
50
49
|
end
|
51
|
-
module_function :unescape_path
|
52
|
-
|
53
50
|
|
54
51
|
# Unescapes a URI escaped string with +encoding+. +encoding+ will be the
|
55
52
|
# target encoding of the string returned, and it defaults to UTF-8
|
56
53
|
def unescape(s, encoding = Encoding::UTF_8)
|
57
54
|
URI.decode_www_form_component(s, encoding)
|
58
55
|
end
|
59
|
-
module_function :unescape
|
60
56
|
|
61
57
|
class << self
|
62
58
|
attr_accessor :multipart_part_limit
|
@@ -88,21 +84,20 @@ module Rack
|
|
88
84
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
89
85
|
end
|
90
86
|
else
|
87
|
+
# :nocov:
|
91
88
|
def clock_time
|
92
89
|
Time.now.to_f
|
93
90
|
end
|
91
|
+
# :nocov:
|
94
92
|
end
|
95
|
-
module_function :clock_time
|
96
93
|
|
97
94
|
def parse_query(qs, d = nil, &unescaper)
|
98
95
|
Rack::Utils.default_query_parser.parse_query(qs, d, &unescaper)
|
99
96
|
end
|
100
|
-
module_function :parse_query
|
101
97
|
|
102
98
|
def parse_nested_query(qs, d = nil)
|
103
99
|
Rack::Utils.default_query_parser.parse_nested_query(qs, d)
|
104
100
|
end
|
105
|
-
module_function :parse_nested_query
|
106
101
|
|
107
102
|
def build_query(params)
|
108
103
|
params.map { |k, v|
|
@@ -113,7 +108,6 @@ module Rack
|
|
113
108
|
end
|
114
109
|
}.join("&")
|
115
110
|
end
|
116
|
-
module_function :build_query
|
117
111
|
|
118
112
|
def build_nested_query(value, prefix = nil)
|
119
113
|
case value
|
@@ -132,7 +126,6 @@ module Rack
|
|
132
126
|
"#{prefix}=#{escape(value)}"
|
133
127
|
end
|
134
128
|
end
|
135
|
-
module_function :build_nested_query
|
136
129
|
|
137
130
|
def q_values(q_value_header)
|
138
131
|
q_value_header.to_s.split(/\s*,\s*/).map do |part|
|
@@ -144,8 +137,11 @@ module Rack
|
|
144
137
|
[value, quality]
|
145
138
|
end
|
146
139
|
end
|
147
|
-
module_function :q_values
|
148
140
|
|
141
|
+
# Return best accept value to use, based on the algorithm
|
142
|
+
# in RFC 2616 Section 14. If there are multiple best
|
143
|
+
# matches (same specificity and quality), the value returned
|
144
|
+
# is arbitrary.
|
149
145
|
def best_q_match(q_value_header, available_mimes)
|
150
146
|
values = q_values(q_value_header)
|
151
147
|
|
@@ -158,7 +154,6 @@ module Rack
|
|
158
154
|
end.last
|
159
155
|
matches && matches.first
|
160
156
|
end
|
161
|
-
module_function :best_q_match
|
162
157
|
|
163
158
|
ESCAPE_HTML = {
|
164
159
|
"&" => "&",
|
@@ -175,22 +170,27 @@ module Rack
|
|
175
170
|
def escape_html(string)
|
176
171
|
string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] }
|
177
172
|
end
|
178
|
-
module_function :escape_html
|
179
173
|
|
180
174
|
def select_best_encoding(available_encodings, accept_encoding)
|
181
175
|
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
182
176
|
|
183
|
-
expanded_accept_encoding =
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
177
|
+
expanded_accept_encoding = []
|
178
|
+
|
179
|
+
accept_encoding.each do |m, q|
|
180
|
+
preference = available_encodings.index(m) || available_encodings.size
|
181
|
+
|
182
|
+
if m == "*"
|
183
|
+
(available_encodings - accept_encoding.map(&:first)).each do |m2|
|
184
|
+
expanded_accept_encoding << [m2, q, preference]
|
190
185
|
end
|
186
|
+
else
|
187
|
+
expanded_accept_encoding << [m, q, preference]
|
191
188
|
end
|
189
|
+
end
|
192
190
|
|
193
|
-
encoding_candidates = expanded_accept_encoding
|
191
|
+
encoding_candidates = expanded_accept_encoding
|
192
|
+
.sort_by { |_, q, p| [-q, p] }
|
193
|
+
.map!(&:first)
|
194
194
|
|
195
195
|
unless encoding_candidates.include?("identity")
|
196
196
|
encoding_candidates.push("identity")
|
@@ -202,23 +202,23 @@ module Rack
|
|
202
202
|
|
203
203
|
(encoding_candidates & available_encodings)[0]
|
204
204
|
end
|
205
|
-
module_function :select_best_encoding
|
206
205
|
|
207
206
|
def parse_cookies(env)
|
208
207
|
parse_cookies_header env[HTTP_COOKIE]
|
209
208
|
end
|
210
|
-
module_function :parse_cookies
|
211
209
|
|
212
210
|
def parse_cookies_header(header)
|
213
|
-
# According to RFC
|
214
|
-
#
|
215
|
-
#
|
216
|
-
#
|
217
|
-
|
218
|
-
|
219
|
-
|
211
|
+
# According to RFC 6265:
|
212
|
+
# The syntax for cookie headers only supports semicolons
|
213
|
+
# User Agent -> Server ==
|
214
|
+
# Cookie: SID=31d4d96e407aad42; lang=en-US
|
215
|
+
return {} unless header
|
216
|
+
header.split(/[;] */n).each_with_object({}) do |cookie, cookies|
|
217
|
+
next if cookie.empty?
|
218
|
+
key, value = cookie.split('=', 2)
|
219
|
+
cookies[key] = (unescape(value) rescue value) unless cookies.key?(key)
|
220
|
+
end
|
220
221
|
end
|
221
|
-
module_function :parse_cookies_header
|
222
222
|
|
223
223
|
def add_cookie_to_header(header, key, value)
|
224
224
|
case value
|
@@ -260,13 +260,11 @@ module Rack
|
|
260
260
|
raise ArgumentError, "Unrecognized cookie header value. Expected String, Array, or nil, got #{header.inspect}"
|
261
261
|
end
|
262
262
|
end
|
263
|
-
module_function :add_cookie_to_header
|
264
263
|
|
265
264
|
def set_cookie_header!(header, key, value)
|
266
265
|
header[SET_COOKIE] = add_cookie_to_header(header[SET_COOKIE], key, value)
|
267
266
|
nil
|
268
267
|
end
|
269
|
-
module_function :set_cookie_header!
|
270
268
|
|
271
269
|
def make_delete_cookie_header(header, key, value)
|
272
270
|
case header
|
@@ -278,25 +276,30 @@ module Rack
|
|
278
276
|
cookies = header
|
279
277
|
end
|
280
278
|
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
279
|
+
key = escape(key)
|
280
|
+
domain = value[:domain]
|
281
|
+
path = value[:path]
|
282
|
+
regexp = if domain
|
283
|
+
if path
|
284
|
+
/\A#{key}=.*(?:domain=#{domain}(?:;|$).*path=#{path}(?:;|$)|path=#{path}(?:;|$).*domain=#{domain}(?:;|$))/
|
285
|
+
else
|
286
|
+
/\A#{key}=.*domain=#{domain}(?:;|$)/
|
287
|
+
end
|
288
|
+
elsif path
|
289
|
+
/\A#{key}=.*path=#{path}(?:;|$)/
|
285
290
|
else
|
286
|
-
/\A#{
|
291
|
+
/\A#{key}=/
|
287
292
|
end
|
288
293
|
|
289
294
|
cookies.reject! { |cookie| regexp.match? cookie }
|
290
295
|
|
291
296
|
cookies.join("\n")
|
292
297
|
end
|
293
|
-
module_function :make_delete_cookie_header
|
294
298
|
|
295
299
|
def delete_cookie_header!(header, key, value = {})
|
296
300
|
header[SET_COOKIE] = add_remove_cookie_to_header(header[SET_COOKIE], key, value)
|
297
301
|
nil
|
298
302
|
end
|
299
|
-
module_function :delete_cookie_header!
|
300
303
|
|
301
304
|
# Adds a cookie that will *remove* a cookie from the client. Hence the
|
302
305
|
# strange method name.
|
@@ -309,12 +312,10 @@ module Rack
|
|
309
312
|
expires: Time.at(0) }.merge(value))
|
310
313
|
|
311
314
|
end
|
312
|
-
module_function :add_remove_cookie_to_header
|
313
315
|
|
314
316
|
def rfc2822(time)
|
315
317
|
time.rfc2822
|
316
318
|
end
|
317
|
-
module_function :rfc2822
|
318
319
|
|
319
320
|
# Modified version of stdlib time.rb Time#rfc2822 to use '%d-%b-%Y' instead
|
320
321
|
# of '% %b %Y'.
|
@@ -330,7 +331,6 @@ module Rack
|
|
330
331
|
mon = Time::RFC2822_MONTH_NAME[time.mon - 1]
|
331
332
|
time.strftime("#{wday}, %d-#{mon}-%Y %H:%M:%S GMT")
|
332
333
|
end
|
333
|
-
module_function :rfc2109
|
334
334
|
|
335
335
|
# Parses the "Range:" header, if present, into an array of Range objects.
|
336
336
|
# Returns nil if the header is missing or syntactically invalid.
|
@@ -339,7 +339,6 @@ module Rack
|
|
339
339
|
warn "`byte_ranges` is deprecated, please use `get_byte_ranges`" if $VERBOSE
|
340
340
|
get_byte_ranges env['HTTP_RANGE'], size
|
341
341
|
end
|
342
|
-
module_function :byte_ranges
|
343
342
|
|
344
343
|
def get_byte_ranges(http_range, size)
|
345
344
|
# See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
|
@@ -368,7 +367,6 @@ module Rack
|
|
368
367
|
end
|
369
368
|
ranges
|
370
369
|
end
|
371
|
-
module_function :get_byte_ranges
|
372
370
|
|
373
371
|
# Constant time string comparison.
|
374
372
|
#
|
@@ -385,7 +383,6 @@ module Rack
|
|
385
383
|
b.each_byte { |v| r |= v ^ l[i += 1] }
|
386
384
|
r == 0
|
387
385
|
end
|
388
|
-
module_function :secure_compare
|
389
386
|
|
390
387
|
# Context allows the use of a compatible middleware at different points
|
391
388
|
# in a request handling stack. A compatible middleware must define
|
@@ -418,6 +415,14 @@ module Rack
|
|
418
415
|
#
|
419
416
|
# @api private
|
420
417
|
class HeaderHash < Hash # :nodoc:
|
418
|
+
def self.[](headers)
|
419
|
+
if headers.is_a?(HeaderHash) && !headers.frozen?
|
420
|
+
return headers
|
421
|
+
else
|
422
|
+
return self.new(headers)
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
421
426
|
def initialize(hash = {})
|
422
427
|
super()
|
423
428
|
@names = {}
|
@@ -430,6 +435,12 @@ module Rack
|
|
430
435
|
@names = other.names.dup
|
431
436
|
end
|
432
437
|
|
438
|
+
# on clear, we need to clear @names hash
|
439
|
+
def clear
|
440
|
+
super
|
441
|
+
@names.clear
|
442
|
+
end
|
443
|
+
|
433
444
|
def each
|
434
445
|
super do |k, v|
|
435
446
|
yield(k, v.respond_to?(:to_ary) ? v.to_ary.join("\n") : v)
|
@@ -574,7 +585,6 @@ module Rack
|
|
574
585
|
status.to_i
|
575
586
|
end
|
576
587
|
end
|
577
|
-
module_function :status_code
|
578
588
|
|
579
589
|
PATH_SEPS = Regexp.union(*[::File::SEPARATOR, ::File::ALT_SEPARATOR].compact)
|
580
590
|
|
@@ -588,18 +598,16 @@ module Rack
|
|
588
598
|
part == '..' ? clean.pop : clean << part
|
589
599
|
end
|
590
600
|
|
591
|
-
|
592
|
-
|
593
|
-
|
601
|
+
clean_path = clean.join(::File::SEPARATOR)
|
602
|
+
clean_path.prepend("/") if parts.empty? || parts.first.empty?
|
603
|
+
clean_path
|
594
604
|
end
|
595
|
-
module_function :clean_path_info
|
596
605
|
|
597
606
|
NULL_BYTE = "\0"
|
598
607
|
|
599
608
|
def valid_path?(path)
|
600
609
|
path.valid_encoding? && !path.include?(NULL_BYTE)
|
601
610
|
end
|
602
|
-
module_function :valid_path?
|
603
611
|
|
604
612
|
end
|
605
613
|
end
|