rack 2.1.2 → 2.2.2
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 +622 -1
- data/CONTRIBUTING.md +136 -0
- data/README.rdoc +83 -39
- data/Rakefile +14 -7
- data/{SPEC → SPEC.rdoc} +26 -1
- 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 -60
- 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 +58 -54
- data/lib/rack/version.rb +29 -0
- data/rack.gemspec +31 -29
- metadata +11 -12
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,19 @@ 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
|
-
cookies = parse_query(header, ';,') { |s| unescape(s) rescue s }
|
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
|
+
cookies = parse_query(header, ';') { |s| unescape(s) rescue s }
|
219
216
|
cookies.each_with_object({}) { |(k, v), hash| hash[k] = Array === v ? v.first : v }
|
220
217
|
end
|
221
|
-
module_function :parse_cookies_header
|
222
218
|
|
223
219
|
def add_cookie_to_header(header, key, value)
|
224
220
|
case value
|
@@ -260,13 +256,11 @@ module Rack
|
|
260
256
|
raise ArgumentError, "Unrecognized cookie header value. Expected String, Array, or nil, got #{header.inspect}"
|
261
257
|
end
|
262
258
|
end
|
263
|
-
module_function :add_cookie_to_header
|
264
259
|
|
265
260
|
def set_cookie_header!(header, key, value)
|
266
261
|
header[SET_COOKIE] = add_cookie_to_header(header[SET_COOKIE], key, value)
|
267
262
|
nil
|
268
263
|
end
|
269
|
-
module_function :set_cookie_header!
|
270
264
|
|
271
265
|
def make_delete_cookie_header(header, key, value)
|
272
266
|
case header
|
@@ -278,25 +272,30 @@ module Rack
|
|
278
272
|
cookies = header
|
279
273
|
end
|
280
274
|
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
275
|
+
key = escape(key)
|
276
|
+
domain = value[:domain]
|
277
|
+
path = value[:path]
|
278
|
+
regexp = if domain
|
279
|
+
if path
|
280
|
+
/\A#{key}=.*(?:domain=#{domain}(?:;|$).*path=#{path}(?:;|$)|path=#{path}(?:;|$).*domain=#{domain}(?:;|$))/
|
281
|
+
else
|
282
|
+
/\A#{key}=.*domain=#{domain}(?:;|$)/
|
283
|
+
end
|
284
|
+
elsif path
|
285
|
+
/\A#{key}=.*path=#{path}(?:;|$)/
|
285
286
|
else
|
286
|
-
/\A#{
|
287
|
+
/\A#{key}=/
|
287
288
|
end
|
288
289
|
|
289
290
|
cookies.reject! { |cookie| regexp.match? cookie }
|
290
291
|
|
291
292
|
cookies.join("\n")
|
292
293
|
end
|
293
|
-
module_function :make_delete_cookie_header
|
294
294
|
|
295
295
|
def delete_cookie_header!(header, key, value = {})
|
296
296
|
header[SET_COOKIE] = add_remove_cookie_to_header(header[SET_COOKIE], key, value)
|
297
297
|
nil
|
298
298
|
end
|
299
|
-
module_function :delete_cookie_header!
|
300
299
|
|
301
300
|
# Adds a cookie that will *remove* a cookie from the client. Hence the
|
302
301
|
# strange method name.
|
@@ -309,12 +308,10 @@ module Rack
|
|
309
308
|
expires: Time.at(0) }.merge(value))
|
310
309
|
|
311
310
|
end
|
312
|
-
module_function :add_remove_cookie_to_header
|
313
311
|
|
314
312
|
def rfc2822(time)
|
315
313
|
time.rfc2822
|
316
314
|
end
|
317
|
-
module_function :rfc2822
|
318
315
|
|
319
316
|
# Modified version of stdlib time.rb Time#rfc2822 to use '%d-%b-%Y' instead
|
320
317
|
# of '% %b %Y'.
|
@@ -330,7 +327,6 @@ module Rack
|
|
330
327
|
mon = Time::RFC2822_MONTH_NAME[time.mon - 1]
|
331
328
|
time.strftime("#{wday}, %d-#{mon}-%Y %H:%M:%S GMT")
|
332
329
|
end
|
333
|
-
module_function :rfc2109
|
334
330
|
|
335
331
|
# Parses the "Range:" header, if present, into an array of Range objects.
|
336
332
|
# Returns nil if the header is missing or syntactically invalid.
|
@@ -339,7 +335,6 @@ module Rack
|
|
339
335
|
warn "`byte_ranges` is deprecated, please use `get_byte_ranges`" if $VERBOSE
|
340
336
|
get_byte_ranges env['HTTP_RANGE'], size
|
341
337
|
end
|
342
|
-
module_function :byte_ranges
|
343
338
|
|
344
339
|
def get_byte_ranges(http_range, size)
|
345
340
|
# See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
|
@@ -368,7 +363,6 @@ module Rack
|
|
368
363
|
end
|
369
364
|
ranges
|
370
365
|
end
|
371
|
-
module_function :get_byte_ranges
|
372
366
|
|
373
367
|
# Constant time string comparison.
|
374
368
|
#
|
@@ -385,7 +379,6 @@ module Rack
|
|
385
379
|
b.each_byte { |v| r |= v ^ l[i += 1] }
|
386
380
|
r == 0
|
387
381
|
end
|
388
|
-
module_function :secure_compare
|
389
382
|
|
390
383
|
# Context allows the use of a compatible middleware at different points
|
391
384
|
# in a request handling stack. A compatible middleware must define
|
@@ -418,6 +411,14 @@ module Rack
|
|
418
411
|
#
|
419
412
|
# @api private
|
420
413
|
class HeaderHash < Hash # :nodoc:
|
414
|
+
def self.[](headers)
|
415
|
+
if headers.is_a?(HeaderHash) && !headers.frozen?
|
416
|
+
return headers
|
417
|
+
else
|
418
|
+
return self.new(headers)
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
421
422
|
def initialize(hash = {})
|
422
423
|
super()
|
423
424
|
@names = {}
|
@@ -430,6 +431,12 @@ module Rack
|
|
430
431
|
@names = other.names.dup
|
431
432
|
end
|
432
433
|
|
434
|
+
# on clear, we need to clear @names hash
|
435
|
+
def clear
|
436
|
+
super
|
437
|
+
@names.clear
|
438
|
+
end
|
439
|
+
|
433
440
|
def each
|
434
441
|
super do |k, v|
|
435
442
|
yield(k, v.respond_to?(:to_ary) ? v.to_ary.join("\n") : v)
|
@@ -574,7 +581,6 @@ module Rack
|
|
574
581
|
status.to_i
|
575
582
|
end
|
576
583
|
end
|
577
|
-
module_function :status_code
|
578
584
|
|
579
585
|
PATH_SEPS = Regexp.union(*[::File::SEPARATOR, ::File::ALT_SEPARATOR].compact)
|
580
586
|
|
@@ -588,18 +594,16 @@ module Rack
|
|
588
594
|
part == '..' ? clean.pop : clean << part
|
589
595
|
end
|
590
596
|
|
591
|
-
|
592
|
-
|
593
|
-
|
597
|
+
clean_path = clean.join(::File::SEPARATOR)
|
598
|
+
clean_path.prepend("/") if parts.empty? || parts.first.empty?
|
599
|
+
clean_path
|
594
600
|
end
|
595
|
-
module_function :clean_path_info
|
596
601
|
|
597
602
|
NULL_BYTE = "\0"
|
598
603
|
|
599
604
|
def valid_path?(path)
|
600
605
|
path.valid_encoding? && !path.include?(NULL_BYTE)
|
601
606
|
end
|
602
|
-
module_function :valid_path?
|
603
607
|
|
604
608
|
end
|
605
609
|
end
|