rack 2.0.9.4 → 2.1.0
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/{HISTORY.md → CHANGELOG.md} +214 -164
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +79 -133
- data/Rakefile +25 -18
- data/SPEC +9 -9
- data/bin/rackup +1 -0
- data/example/lobster.ru +2 -0
- data/example/protectedlobster.rb +3 -1
- data/example/protectedlobster.ru +2 -0
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +2 -0
- data/lib/rack/auth/basic.rb +4 -1
- data/lib/rack/auth/digest/md5.rb +9 -7
- data/lib/rack/auth/digest/nonce.rb +6 -3
- data/lib/rack/auth/digest/params.rb +4 -2
- data/lib/rack/auth/digest/request.rb +2 -0
- data/lib/rack/body_proxy.rb +3 -6
- data/lib/rack/builder.rb +38 -15
- data/lib/rack/cascade.rb +6 -5
- data/lib/rack/chunked.rb +29 -6
- data/lib/rack/common_logger.rb +9 -11
- data/lib/rack/conditional_get.rb +3 -1
- data/lib/rack/config.rb +2 -0
- data/lib/rack/content_length.rb +3 -1
- data/lib/rack/content_type.rb +3 -1
- data/lib/rack/core_ext/regexp.rb +14 -0
- data/lib/rack/deflater.rb +28 -17
- data/lib/rack/directory.rb +17 -14
- data/lib/rack/etag.rb +3 -1
- data/lib/rack/events.rb +5 -3
- data/lib/rack/file.rb +5 -173
- data/lib/rack/files.rb +178 -0
- data/lib/rack/handler/cgi.rb +3 -1
- data/lib/rack/handler/fastcgi.rb +4 -2
- data/lib/rack/handler/lsws.rb +3 -1
- data/lib/rack/handler/scgi.rb +9 -6
- data/lib/rack/handler/thin.rb +3 -1
- data/lib/rack/handler/webrick.rb +4 -2
- data/lib/rack/handler.rb +7 -2
- data/lib/rack/head.rb +2 -0
- data/lib/rack/lint.rb +15 -12
- data/lib/rack/lobster.rb +7 -5
- data/lib/rack/lock.rb +2 -0
- data/lib/rack/logger.rb +2 -0
- data/lib/rack/media_type.rb +10 -5
- data/lib/rack/method_override.rb +4 -2
- data/lib/rack/mime.rb +9 -1
- data/lib/rack/mock.rb +74 -15
- data/lib/rack/multipart/generator.rb +6 -7
- data/lib/rack/multipart/parser.rb +55 -62
- data/lib/rack/multipart/uploaded_file.rb +2 -0
- data/lib/rack/multipart.rb +6 -3
- data/lib/rack/null_logger.rb +2 -0
- data/lib/rack/query_parser.rb +51 -25
- data/lib/rack/recursive.rb +7 -5
- data/lib/rack/reloader.rb +10 -4
- data/lib/rack/request.rb +79 -26
- data/lib/rack/response.rb +71 -31
- data/lib/rack/rewindable_input.rb +4 -2
- data/lib/rack/runtime.rb +4 -2
- data/lib/rack/sendfile.rb +15 -8
- data/lib/rack/server.rb +88 -16
- data/lib/rack/session/abstract/id.rb +40 -22
- data/lib/rack/session/cookie.rb +10 -9
- data/lib/rack/session/memcache.rb +4 -93
- data/lib/rack/session/pool.rb +4 -2
- data/lib/rack/show_exceptions.rb +15 -9
- data/lib/rack/show_status.rb +4 -2
- data/lib/rack/static.rb +15 -10
- data/lib/rack/tempfile_reaper.rb +2 -0
- data/lib/rack/urlmap.rb +11 -2
- data/lib/rack/utils.rb +64 -93
- data/lib/rack.rb +63 -60
- data/rack.gemspec +17 -7
- metadata +33 -175
- data/test/builder/an_underscore_app.rb +0 -5
- data/test/builder/anything.rb +0 -5
- data/test/builder/comment.ru +0 -4
- data/test/builder/end.ru +0 -5
- data/test/builder/line.ru +0 -1
- data/test/builder/options.ru +0 -2
- data/test/cgi/assets/folder/test.js +0 -1
- data/test/cgi/assets/fonts/font.eot +0 -1
- data/test/cgi/assets/images/image.png +0 -1
- data/test/cgi/assets/index.html +0 -1
- data/test/cgi/assets/javascripts/app.js +0 -1
- data/test/cgi/assets/stylesheets/app.css +0 -1
- data/test/cgi/lighttpd.conf +0 -26
- data/test/cgi/rackup_stub.rb +0 -6
- data/test/cgi/sample_rackup.ru +0 -5
- data/test/cgi/test +0 -9
- data/test/cgi/test+directory/test+file +0 -1
- data/test/cgi/test.fcgi +0 -9
- data/test/cgi/test.gz +0 -0
- data/test/cgi/test.ru +0 -5
- data/test/gemloader.rb +0 -10
- data/test/helper.rb +0 -34
- data/test/multipart/bad_robots +0 -259
- data/test/multipart/binary +0 -0
- data/test/multipart/content_type_and_no_filename +0 -6
- data/test/multipart/empty +0 -10
- data/test/multipart/fail_16384_nofile +0 -814
- data/test/multipart/file1.txt +0 -1
- data/test/multipart/filename_and_modification_param +0 -7
- data/test/multipart/filename_and_no_name +0 -6
- data/test/multipart/filename_with_encoded_words +0 -7
- data/test/multipart/filename_with_escaped_quotes +0 -6
- data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
- data/test/multipart/filename_with_null_byte +0 -7
- data/test/multipart/filename_with_percent_escaped_quotes +0 -6
- data/test/multipart/filename_with_single_quote +0 -7
- data/test/multipart/filename_with_unescaped_percentages +0 -6
- data/test/multipart/filename_with_unescaped_percentages2 +0 -6
- data/test/multipart/filename_with_unescaped_percentages3 +0 -6
- data/test/multipart/filename_with_unescaped_quotes +0 -6
- data/test/multipart/ie +0 -6
- data/test/multipart/invalid_character +0 -6
- data/test/multipart/mixed_files +0 -21
- data/test/multipart/nested +0 -10
- data/test/multipart/none +0 -9
- data/test/multipart/quoted +0 -15
- data/test/multipart/rack-logo.png +0 -0
- data/test/multipart/semicolon +0 -6
- data/test/multipart/text +0 -15
- data/test/multipart/three_files_three_fields +0 -31
- data/test/multipart/unity3d_wwwform +0 -11
- data/test/multipart/webkit +0 -32
- data/test/rackup/config.ru +0 -31
- data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
- data/test/spec_auth_basic.rb +0 -89
- data/test/spec_auth_digest.rb +0 -260
- data/test/spec_body_proxy.rb +0 -85
- data/test/spec_builder.rb +0 -233
- data/test/spec_cascade.rb +0 -63
- data/test/spec_cgi.rb +0 -84
- data/test/spec_chunked.rb +0 -103
- data/test/spec_common_logger.rb +0 -107
- data/test/spec_conditional_get.rb +0 -103
- data/test/spec_config.rb +0 -23
- data/test/spec_content_length.rb +0 -86
- data/test/spec_content_type.rb +0 -46
- data/test/spec_deflater.rb +0 -375
- data/test/spec_directory.rb +0 -148
- data/test/spec_etag.rb +0 -108
- data/test/spec_events.rb +0 -133
- data/test/spec_fastcgi.rb +0 -85
- data/test/spec_file.rb +0 -264
- data/test/spec_handler.rb +0 -57
- data/test/spec_head.rb +0 -46
- data/test/spec_lint.rb +0 -520
- data/test/spec_lobster.rb +0 -59
- data/test/spec_lock.rb +0 -204
- data/test/spec_logger.rb +0 -24
- data/test/spec_media_type.rb +0 -42
- data/test/spec_method_override.rb +0 -110
- data/test/spec_mime.rb +0 -51
- data/test/spec_mock.rb +0 -359
- data/test/spec_multipart.rb +0 -721
- data/test/spec_null_logger.rb +0 -21
- data/test/spec_recursive.rb +0 -75
- data/test/spec_request.rb +0 -1423
- data/test/spec_response.rb +0 -528
- data/test/spec_rewindable_input.rb +0 -128
- data/test/spec_runtime.rb +0 -50
- data/test/spec_sendfile.rb +0 -125
- data/test/spec_server.rb +0 -193
- data/test/spec_session_abstract_id.rb +0 -31
- data/test/spec_session_abstract_session_hash.rb +0 -45
- data/test/spec_session_cookie.rb +0 -442
- data/test/spec_session_memcache.rb +0 -357
- data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
- data/test/spec_session_pool.rb +0 -247
- data/test/spec_show_exceptions.rb +0 -93
- data/test/spec_show_status.rb +0 -104
- data/test/spec_static.rb +0 -184
- data/test/spec_tempfile_reaper.rb +0 -64
- data/test/spec_thin.rb +0 -96
- data/test/spec_urlmap.rb +0 -237
- data/test/spec_utils.rb +0 -742
- data/test/spec_version.rb +0 -11
- data/test/spec_webrick.rb +0 -206
- data/test/static/another/index.html +0 -1
- data/test/static/foo.html +0 -1
- data/test/static/index.html +0 -1
- data/test/testrequest.rb +0 -78
- data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
data/lib/rack/static.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rack/files"
|
2
4
|
require "rack/utils"
|
3
5
|
|
6
|
+
require_relative 'core_ext/regexp'
|
7
|
+
|
4
8
|
module Rack
|
5
9
|
|
6
10
|
# The Rack::Static middleware intercepts requests for static files
|
7
11
|
# (javascript files, images, stylesheets, etc) based on the url prefixes or
|
8
|
-
# route mappings passed in the options, and serves them using a Rack::
|
12
|
+
# route mappings passed in the options, and serves them using a Rack::Files
|
9
13
|
# object. This allows a Rack stack to serve both static and dynamic content.
|
10
14
|
#
|
11
15
|
# Examples:
|
@@ -82,8 +86,9 @@ module Rack
|
|
82
86
|
# ]
|
83
87
|
#
|
84
88
|
class Static
|
89
|
+
using ::Rack::RegexpExtensions
|
85
90
|
|
86
|
-
def initialize(app, options={})
|
91
|
+
def initialize(app, options = {})
|
87
92
|
@app = app
|
88
93
|
@urls = options[:urls] || ["/favicon.ico"]
|
89
94
|
@index = options[:index]
|
@@ -93,13 +98,13 @@ module Rack
|
|
93
98
|
# HTTP Headers
|
94
99
|
@header_rules = options[:header_rules] || []
|
95
100
|
# Allow for legacy :cache_control option while prioritizing global header_rules setting
|
96
|
-
@header_rules.unshift([:all, {CACHE_CONTROL => options[:cache_control]}]) if options[:cache_control]
|
101
|
+
@header_rules.unshift([:all, { CACHE_CONTROL => options[:cache_control] }]) if options[:cache_control]
|
97
102
|
|
98
|
-
@file_server = Rack::
|
103
|
+
@file_server = Rack::Files.new(root)
|
99
104
|
end
|
100
105
|
|
101
106
|
def add_index_root?(path)
|
102
|
-
@index && path
|
107
|
+
@index && route_file(path) && path.end_with?('/')
|
103
108
|
end
|
104
109
|
|
105
110
|
def overwrite_file_path(path)
|
@@ -120,7 +125,7 @@ module Rack
|
|
120
125
|
if can_serve(path)
|
121
126
|
if overwrite_file_path(path)
|
122
127
|
env[PATH_INFO] = (add_index_root?(path) ? path + @index : @urls[path])
|
123
|
-
elsif @gzip && env['HTTP_ACCEPT_ENCODING']
|
128
|
+
elsif @gzip && env['HTTP_ACCEPT_ENCODING'] && /\bgzip\b/.match?(env['HTTP_ACCEPT_ENCODING'])
|
124
129
|
path = env[PATH_INFO]
|
125
130
|
env[PATH_INFO] += '.gz'
|
126
131
|
response = @file_server.call(env)
|
@@ -157,14 +162,14 @@ module Rack
|
|
157
162
|
when :all
|
158
163
|
true
|
159
164
|
when :fonts
|
160
|
-
|
165
|
+
/\.(?:ttf|otf|eot|woff2|woff|svg)\z/.match?(path)
|
161
166
|
when String
|
162
167
|
path = ::Rack::Utils.unescape(path)
|
163
168
|
path.start_with?(rule) || path.start_with?('/' + rule)
|
164
169
|
when Array
|
165
|
-
|
170
|
+
/\.(#{rule.join('|')})\z/.match?(path)
|
166
171
|
when Regexp
|
167
|
-
path
|
172
|
+
rule.match?(path)
|
168
173
|
else
|
169
174
|
false
|
170
175
|
end
|
data/lib/rack/tempfile_reaper.rb
CHANGED
data/lib/rack/urlmap.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
|
1
5
|
module Rack
|
2
6
|
# Rack::URLMap takes a hash mapping urls or paths to apps, and
|
3
7
|
# dispatches accordingly. Support for HTTP/1.1 host names exists if
|
@@ -20,9 +24,11 @@ module Rack
|
|
20
24
|
end
|
21
25
|
|
22
26
|
def remap(map)
|
27
|
+
@known_hosts = Set[]
|
23
28
|
@mapping = map.map { |location, app|
|
24
29
|
if location =~ %r{\Ahttps?://(.*?)(/.*)}
|
25
30
|
host, location = $1, $2
|
31
|
+
@known_hosts << host
|
26
32
|
else
|
27
33
|
host = nil
|
28
34
|
end
|
@@ -50,10 +56,13 @@ module Rack
|
|
50
56
|
is_same_server = casecmp?(http_host, server_name) ||
|
51
57
|
casecmp?(http_host, "#{server_name}:#{server_port}")
|
52
58
|
|
59
|
+
is_host_known = @known_hosts.include? http_host
|
60
|
+
|
53
61
|
@mapping.each do |host, location, match, app|
|
54
62
|
unless casecmp?(http_host, host) \
|
55
63
|
|| casecmp?(server_name, host) \
|
56
|
-
|| (!host && is_same_server)
|
64
|
+
|| (!host && is_same_server) \
|
65
|
+
|| (!host && !is_host_known) # If we don't have a matching host, default to the first without a specified host
|
57
66
|
next
|
58
67
|
end
|
59
68
|
|
@@ -68,7 +77,7 @@ module Rack
|
|
68
77
|
return app.call(env)
|
69
78
|
end
|
70
79
|
|
71
|
-
[404, {CONTENT_TYPE => "text/plain", "X-Cascade" => "pass"}, ["Not Found: #{path}"]]
|
80
|
+
[404, { CONTENT_TYPE => "text/plain", "X-Cascade" => "pass" }, ["Not Found: #{path}"]]
|
72
81
|
|
73
82
|
ensure
|
74
83
|
env[PATH_INFO] = path
|
data/lib/rack/utils.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
require 'uri'
|
3
5
|
require 'fileutils'
|
4
6
|
require 'set'
|
@@ -6,11 +8,15 @@ require 'tempfile'
|
|
6
8
|
require 'rack/query_parser'
|
7
9
|
require 'time'
|
8
10
|
|
11
|
+
require_relative 'core_ext/regexp'
|
12
|
+
|
9
13
|
module Rack
|
10
14
|
# Rack::Utils contains a grab-bag of useful methods for writing web
|
11
15
|
# applications adopted from all kinds of Ruby libraries.
|
12
16
|
|
13
17
|
module Utils
|
18
|
+
using ::Rack::RegexpExtensions
|
19
|
+
|
14
20
|
ParameterTypeError = QueryParser::ParameterTypeError
|
15
21
|
InvalidParameterError = QueryParser::InvalidParameterError
|
16
22
|
DEFAULT_SEP = QueryParser::DEFAULT_SEP
|
@@ -53,24 +59,13 @@ module Rack
|
|
53
59
|
module_function :unescape
|
54
60
|
|
55
61
|
class << self
|
56
|
-
attr_accessor :
|
57
|
-
|
58
|
-
attr_accessor :multipart_file_limit
|
59
|
-
|
60
|
-
# multipart_part_limit is the original name of multipart_file_limit, but
|
61
|
-
# the limit only counts parts with filenames.
|
62
|
-
alias multipart_part_limit multipart_file_limit
|
63
|
-
alias multipart_part_limit= multipart_file_limit=
|
62
|
+
attr_accessor :multipart_part_limit
|
64
63
|
end
|
65
64
|
|
66
|
-
# The maximum number of
|
67
|
-
#
|
65
|
+
# The maximum number of parts a request can contain. Accepting too many part
|
66
|
+
# can lead to the server running out of file handles.
|
68
67
|
# Set to `0` for no limit.
|
69
|
-
self.
|
70
|
-
|
71
|
-
# The maximum total number of parts a request can contain. Accepting too
|
72
|
-
# many can lead to excessive memory use and parsing time.
|
73
|
-
self.multipart_total_part_limit = (ENV['RACK_MULTIPART_TOTAL_PART_LIMIT'] || 4096).to_i
|
68
|
+
self.multipart_part_limit = (ENV['RACK_MULTIPART_PART_LIMIT'] || 128).to_i
|
74
69
|
|
75
70
|
def self.param_depth_limit
|
76
71
|
default_query_parser.param_depth_limit
|
@@ -129,7 +124,7 @@ module Rack
|
|
129
124
|
when Hash
|
130
125
|
value.map { |k, v|
|
131
126
|
build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
|
132
|
-
}.
|
127
|
+
}.delete_if(&:empty?).join('&')
|
133
128
|
when nil
|
134
129
|
prefix
|
135
130
|
else
|
@@ -140,10 +135,10 @@ module Rack
|
|
140
135
|
module_function :build_nested_query
|
141
136
|
|
142
137
|
def q_values(q_value_header)
|
143
|
-
q_value_header.to_s.split(
|
144
|
-
value, parameters = part.split(
|
138
|
+
q_value_header.to_s.split(/\s*,\s*/).map do |part|
|
139
|
+
value, parameters = part.split(/\s*;\s*/, 2)
|
145
140
|
quality = 1.0
|
146
|
-
if md = /\Aq=([\d.]+)/.match(parameters)
|
141
|
+
if parameters && (md = /\Aq=([\d.]+)/.match(parameters))
|
147
142
|
quality = md[1].to_f
|
148
143
|
end
|
149
144
|
[value, quality]
|
@@ -186,27 +181,26 @@ module Rack
|
|
186
181
|
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
187
182
|
|
188
183
|
expanded_accept_encoding =
|
189
|
-
accept_encoding.
|
184
|
+
accept_encoding.each_with_object([]) do |(m, q), list|
|
190
185
|
if m == "*"
|
191
|
-
(available_encodings - accept_encoding.map
|
186
|
+
(available_encodings - accept_encoding.map(&:first))
|
187
|
+
.each { |m2| list << [m2, q] }
|
192
188
|
else
|
193
|
-
[
|
189
|
+
list << [m, q]
|
194
190
|
end
|
195
|
-
|
196
|
-
mem + list
|
197
|
-
}
|
191
|
+
end
|
198
192
|
|
199
|
-
encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map
|
193
|
+
encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map!(&:first)
|
200
194
|
|
201
195
|
unless encoding_candidates.include?("identity")
|
202
196
|
encoding_candidates.push("identity")
|
203
197
|
end
|
204
198
|
|
205
|
-
expanded_accept_encoding.each
|
199
|
+
expanded_accept_encoding.each do |m, q|
|
206
200
|
encoding_candidates.delete(m) if q == 0.0
|
207
|
-
|
201
|
+
end
|
208
202
|
|
209
|
-
|
203
|
+
(encoding_candidates & available_encodings)[0]
|
210
204
|
end
|
211
205
|
module_function :select_best_encoding
|
212
206
|
|
@@ -222,7 +216,7 @@ module Rack
|
|
222
216
|
# precede those with less specific. Ordering with respect to other
|
223
217
|
# attributes (e.g., Domain) is unspecified.
|
224
218
|
cookies = parse_query(header, ';,') { |s| unescape(s) rescue s }
|
225
|
-
cookies.each_with_object({}) { |(k,v), hash| hash[k] = Array === v ? v.first : v }
|
219
|
+
cookies.each_with_object({}) { |(k, v), hash| hash[k] = Array === v ? v.first : v }
|
226
220
|
end
|
227
221
|
module_function :parse_cookies_header
|
228
222
|
|
@@ -232,31 +226,7 @@ module Rack
|
|
232
226
|
domain = "; domain=#{value[:domain]}" if value[:domain]
|
233
227
|
path = "; path=#{value[:path]}" if value[:path]
|
234
228
|
max_age = "; max-age=#{value[:max_age]}" if value[:max_age]
|
235
|
-
|
236
|
-
# only are there contradicting RFCs and examples within RFC text, but
|
237
|
-
# there are also numerous conflicting names of fields and partially
|
238
|
-
# cross-applicable specifications.
|
239
|
-
#
|
240
|
-
# These are best described in RFC 2616 3.3.1. This RFC text also
|
241
|
-
# specifies that RFC 822 as updated by RFC 1123 is preferred. That is a
|
242
|
-
# fixed length format with space-date delimited fields.
|
243
|
-
#
|
244
|
-
# See also RFC 1123 section 5.2.14.
|
245
|
-
#
|
246
|
-
# RFC 6265 also specifies "sane-cookie-date" as RFC 1123 date, defined
|
247
|
-
# in RFC 2616 3.3.1. RFC 6265 also gives examples that clearly denote
|
248
|
-
# the space delimited format. These formats are compliant with RFC 2822.
|
249
|
-
#
|
250
|
-
# For reference, all involved RFCs are:
|
251
|
-
# RFC 822
|
252
|
-
# RFC 1123
|
253
|
-
# RFC 2109
|
254
|
-
# RFC 2616
|
255
|
-
# RFC 2822
|
256
|
-
# RFC 2965
|
257
|
-
# RFC 6265
|
258
|
-
expires = "; expires=" +
|
259
|
-
rfc2822(value[:expires].clone.gmtime) if value[:expires]
|
229
|
+
expires = "; expires=#{value[:expires].httpdate}" if value[:expires]
|
260
230
|
secure = "; secure" if value[:secure]
|
261
231
|
httponly = "; HttpOnly" if (value.key?(:httponly) ? value[:httponly] : value[:http_only])
|
262
232
|
same_site =
|
@@ -264,11 +234,11 @@ module Rack
|
|
264
234
|
when false, nil
|
265
235
|
nil
|
266
236
|
when :none, 'None', :None
|
267
|
-
'; SameSite=None'
|
237
|
+
'; SameSite=None'
|
268
238
|
when :lax, 'Lax', :Lax
|
269
|
-
'; SameSite=Lax'
|
239
|
+
'; SameSite=Lax'
|
270
240
|
when true, :strict, 'Strict', :Strict
|
271
|
-
'; SameSite=Strict'
|
241
|
+
'; SameSite=Strict'
|
272
242
|
else
|
273
243
|
raise ArgumentError, "Invalid SameSite value: #{value[:same_site].inspect}"
|
274
244
|
end
|
@@ -308,15 +278,15 @@ module Rack
|
|
308
278
|
cookies = header
|
309
279
|
end
|
310
280
|
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
}
|
281
|
+
regexp = if value[:domain]
|
282
|
+
/\A#{escape(key)}=.*domain=#{value[:domain]}/
|
283
|
+
elsif value[:path]
|
284
|
+
/\A#{escape(key)}=.*path=#{value[:path]}/
|
285
|
+
else
|
286
|
+
/\A#{escape(key)}=/
|
287
|
+
end
|
288
|
+
|
289
|
+
cookies.reject! { |cookie| regexp.match? cookie }
|
320
290
|
|
321
291
|
cookies.join("\n")
|
322
292
|
end
|
@@ -334,9 +304,9 @@ module Rack
|
|
334
304
|
new_header = make_delete_cookie_header(header, key, value)
|
335
305
|
|
336
306
|
add_cookie_to_header(new_header, key,
|
337
|
-
{:
|
338
|
-
:
|
339
|
-
:
|
307
|
+
{ value: '', path: nil, domain: nil,
|
308
|
+
max_age: '0',
|
309
|
+
expires: Time.at(0) }.merge(value))
|
340
310
|
|
341
311
|
end
|
342
312
|
module_function :add_remove_cookie_to_header
|
@@ -376,23 +346,22 @@ module Rack
|
|
376
346
|
return nil unless http_range && http_range =~ /bytes=([^;]+)/
|
377
347
|
ranges = []
|
378
348
|
$1.split(/,\s*/).each do |range_spec|
|
379
|
-
return nil
|
380
|
-
|
381
|
-
r0
|
382
|
-
|
383
|
-
return nil if r1.nil?
|
349
|
+
return nil unless range_spec =~ /(\d*)-(\d*)/
|
350
|
+
r0, r1 = $1, $2
|
351
|
+
if r0.empty?
|
352
|
+
return nil if r1.empty?
|
384
353
|
# suffix-byte-range-spec, represents trailing suffix of file
|
385
354
|
r0 = size - r1.to_i
|
386
355
|
r0 = 0 if r0 < 0
|
387
356
|
r1 = size - 1
|
388
357
|
else
|
389
358
|
r0 = r0.to_i
|
390
|
-
if r1.
|
359
|
+
if r1.empty?
|
391
360
|
r1 = size - 1
|
392
361
|
else
|
393
362
|
r1 = r1.to_i
|
394
363
|
return nil if r1 < r0 # backwards range is syntactically invalid
|
395
|
-
r1 = size-1 if r1 >= size
|
364
|
+
r1 = size - 1 if r1 >= size
|
396
365
|
end
|
397
366
|
end
|
398
367
|
ranges << (r0..r1) if r0 <= r1
|
@@ -413,7 +382,7 @@ module Rack
|
|
413
382
|
l = a.unpack("C*")
|
414
383
|
|
415
384
|
r, i = 0, -1
|
416
|
-
b.each_byte { |v| r |= v ^ l[i+=1] }
|
385
|
+
b.each_byte { |v| r |= v ^ l[i += 1] }
|
417
386
|
r == 0
|
418
387
|
end
|
419
388
|
module_function :secure_compare
|
@@ -439,19 +408,17 @@ module Rack
|
|
439
408
|
self.class.new(@for, app)
|
440
409
|
end
|
441
410
|
|
442
|
-
def context(env, app
|
411
|
+
def context(env, app = @app)
|
443
412
|
recontext(app).call(env)
|
444
413
|
end
|
445
414
|
end
|
446
415
|
|
447
416
|
# A case-insensitive Hash that preserves the original case of a
|
448
417
|
# header when set.
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
def initialize(hash={})
|
418
|
+
#
|
419
|
+
# @api private
|
420
|
+
class HeaderHash < Hash # :nodoc:
|
421
|
+
def initialize(hash = {})
|
455
422
|
super()
|
456
423
|
@names = {}
|
457
424
|
hash.each { |k, v| self[k] = v }
|
@@ -471,7 +438,7 @@ module Rack
|
|
471
438
|
|
472
439
|
def to_hash
|
473
440
|
hash = {}
|
474
|
-
each { |k,v| hash[k] = v }
|
441
|
+
each { |k, v| hash[k] = v }
|
475
442
|
hash
|
476
443
|
end
|
477
444
|
|
@@ -524,13 +491,14 @@ module Rack
|
|
524
491
|
|
525
492
|
# Every standard HTTP code mapped to the appropriate message.
|
526
493
|
# Generated with:
|
527
|
-
#
|
528
|
-
#
|
529
|
-
#
|
494
|
+
# curl -s https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv | \
|
495
|
+
# ruby -ne 'm = /^(\d{3}),(?!Unassigned|\(Unused\))([^,]+)/.match($_) and \
|
496
|
+
# puts "#{m[1]} => \x27#{m[2].strip}\x27,"'
|
530
497
|
HTTP_STATUS_CODES = {
|
531
498
|
100 => 'Continue',
|
532
499
|
101 => 'Switching Protocols',
|
533
500
|
102 => 'Processing',
|
501
|
+
103 => 'Early Hints',
|
534
502
|
200 => 'OK',
|
535
503
|
201 => 'Created',
|
536
504
|
202 => 'Accepted',
|
@@ -547,6 +515,7 @@ module Rack
|
|
547
515
|
303 => 'See Other',
|
548
516
|
304 => 'Not Modified',
|
549
517
|
305 => 'Use Proxy',
|
518
|
+
306 => '(Unused)',
|
550
519
|
307 => 'Temporary Redirect',
|
551
520
|
308 => 'Permanent Redirect',
|
552
521
|
400 => 'Bad Request',
|
@@ -571,6 +540,7 @@ module Rack
|
|
571
540
|
422 => 'Unprocessable Entity',
|
572
541
|
423 => 'Locked',
|
573
542
|
424 => 'Failed Dependency',
|
543
|
+
425 => 'Too Early',
|
574
544
|
426 => 'Upgrade Required',
|
575
545
|
428 => 'Precondition Required',
|
576
546
|
429 => 'Too Many Requests',
|
@@ -585,12 +555,13 @@ module Rack
|
|
585
555
|
506 => 'Variant Also Negotiates',
|
586
556
|
507 => 'Insufficient Storage',
|
587
557
|
508 => 'Loop Detected',
|
558
|
+
509 => 'Bandwidth Limit Exceeded',
|
588
559
|
510 => 'Not Extended',
|
589
560
|
511 => 'Network Authentication Required'
|
590
561
|
}
|
591
562
|
|
592
563
|
# Responses with HTTP status codes that should not have an entity body
|
593
|
-
STATUS_WITH_NO_ENTITY_BODY =
|
564
|
+
STATUS_WITH_NO_ENTITY_BODY = Hash[((100..199).to_a << 204 << 304).product([true])]
|
594
565
|
|
595
566
|
SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
|
596
567
|
[message.downcase.gsub(/\s|-|'/, '_').to_sym, code]
|
@@ -598,7 +569,7 @@ module Rack
|
|
598
569
|
|
599
570
|
def status_code(status)
|
600
571
|
if status.is_a?(Symbol)
|
601
|
-
SYMBOL_TO_STATUS_CODE
|
572
|
+
SYMBOL_TO_STATUS_CODE.fetch(status) { raise ArgumentError, "Unrecognized status code #{status.inspect}" }
|
602
573
|
else
|
603
574
|
status.to_i
|
604
575
|
end
|
@@ -619,11 +590,11 @@ module Rack
|
|
619
590
|
|
620
591
|
clean.unshift '/' if parts.empty? || parts.first.empty?
|
621
592
|
|
622
|
-
::File.join
|
593
|
+
::File.join clean
|
623
594
|
end
|
624
595
|
module_function :clean_path_info
|
625
596
|
|
626
|
-
NULL_BYTE = "\0"
|
597
|
+
NULL_BYTE = "\0"
|
627
598
|
|
628
599
|
def valid_path?(path)
|
629
600
|
path.valid_encoding? && !path.include?(NULL_BYTE)
|
data/lib/rack.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (C) 2007-2019 Leah Neukirchen <http://leahneukirchen.org/infopage.html>
|
2
4
|
#
|
3
5
|
# Rack is freely distributable under the terms of an MIT-style license.
|
4
|
-
# See
|
6
|
+
# See MIT-LICENSE or https://opensource.org/licenses/MIT.
|
5
7
|
|
6
8
|
# The Rack main module, serving as a namespace for all core Rack
|
7
9
|
# modules and classes.
|
@@ -11,80 +13,80 @@
|
|
11
13
|
|
12
14
|
module Rack
|
13
15
|
# The Rack protocol version number implemented.
|
14
|
-
VERSION = [1,3]
|
16
|
+
VERSION = [1, 3]
|
15
17
|
|
16
18
|
# Return the Rack protocol version as a dotted string.
|
17
19
|
def self.version
|
18
20
|
VERSION.join(".")
|
19
21
|
end
|
20
22
|
|
21
|
-
RELEASE = "2.0
|
23
|
+
RELEASE = "2.1.0"
|
22
24
|
|
23
25
|
# Return the Rack release as a dotted string.
|
24
26
|
def self.release
|
25
27
|
RELEASE
|
26
28
|
end
|
27
29
|
|
28
|
-
HTTP_HOST = 'HTTP_HOST'
|
29
|
-
HTTP_VERSION = 'HTTP_VERSION'
|
30
|
-
HTTPS = 'HTTPS'
|
31
|
-
PATH_INFO = 'PATH_INFO'
|
32
|
-
REQUEST_METHOD = 'REQUEST_METHOD'
|
33
|
-
REQUEST_PATH = 'REQUEST_PATH'
|
34
|
-
SCRIPT_NAME = 'SCRIPT_NAME'
|
35
|
-
QUERY_STRING = 'QUERY_STRING'
|
36
|
-
SERVER_PROTOCOL = 'SERVER_PROTOCOL'
|
37
|
-
SERVER_NAME = 'SERVER_NAME'
|
38
|
-
SERVER_ADDR = 'SERVER_ADDR'
|
39
|
-
SERVER_PORT = 'SERVER_PORT'
|
40
|
-
CACHE_CONTROL = 'Cache-Control'
|
41
|
-
CONTENT_LENGTH = 'Content-Length'
|
42
|
-
CONTENT_TYPE = 'Content-Type'
|
43
|
-
SET_COOKIE = 'Set-Cookie'
|
44
|
-
TRANSFER_ENCODING = 'Transfer-Encoding'
|
45
|
-
HTTP_COOKIE = 'HTTP_COOKIE'
|
46
|
-
ETAG = 'ETag'
|
30
|
+
HTTP_HOST = 'HTTP_HOST'
|
31
|
+
HTTP_VERSION = 'HTTP_VERSION'
|
32
|
+
HTTPS = 'HTTPS'
|
33
|
+
PATH_INFO = 'PATH_INFO'
|
34
|
+
REQUEST_METHOD = 'REQUEST_METHOD'
|
35
|
+
REQUEST_PATH = 'REQUEST_PATH'
|
36
|
+
SCRIPT_NAME = 'SCRIPT_NAME'
|
37
|
+
QUERY_STRING = 'QUERY_STRING'
|
38
|
+
SERVER_PROTOCOL = 'SERVER_PROTOCOL'
|
39
|
+
SERVER_NAME = 'SERVER_NAME'
|
40
|
+
SERVER_ADDR = 'SERVER_ADDR'
|
41
|
+
SERVER_PORT = 'SERVER_PORT'
|
42
|
+
CACHE_CONTROL = 'Cache-Control'
|
43
|
+
CONTENT_LENGTH = 'Content-Length'
|
44
|
+
CONTENT_TYPE = 'Content-Type'
|
45
|
+
SET_COOKIE = 'Set-Cookie'
|
46
|
+
TRANSFER_ENCODING = 'Transfer-Encoding'
|
47
|
+
HTTP_COOKIE = 'HTTP_COOKIE'
|
48
|
+
ETAG = 'ETag'
|
47
49
|
|
48
50
|
# HTTP method verbs
|
49
|
-
GET = 'GET'
|
50
|
-
POST = 'POST'
|
51
|
-
PUT = 'PUT'
|
52
|
-
PATCH = 'PATCH'
|
53
|
-
DELETE = 'DELETE'
|
54
|
-
HEAD = 'HEAD'
|
55
|
-
OPTIONS = 'OPTIONS'
|
56
|
-
LINK = 'LINK'
|
57
|
-
UNLINK = 'UNLINK'
|
58
|
-
TRACE = 'TRACE'
|
51
|
+
GET = 'GET'
|
52
|
+
POST = 'POST'
|
53
|
+
PUT = 'PUT'
|
54
|
+
PATCH = 'PATCH'
|
55
|
+
DELETE = 'DELETE'
|
56
|
+
HEAD = 'HEAD'
|
57
|
+
OPTIONS = 'OPTIONS'
|
58
|
+
LINK = 'LINK'
|
59
|
+
UNLINK = 'UNLINK'
|
60
|
+
TRACE = 'TRACE'
|
59
61
|
|
60
62
|
# Rack environment variables
|
61
|
-
RACK_VERSION = 'rack.version'
|
62
|
-
RACK_TEMPFILES = 'rack.tempfiles'
|
63
|
-
RACK_ERRORS = 'rack.errors'
|
64
|
-
RACK_LOGGER = 'rack.logger'
|
65
|
-
RACK_INPUT = 'rack.input'
|
66
|
-
RACK_SESSION = 'rack.session'
|
67
|
-
RACK_SESSION_OPTIONS = 'rack.session.options'
|
68
|
-
RACK_SHOWSTATUS_DETAIL = 'rack.showstatus.detail'
|
69
|
-
RACK_MULTITHREAD = 'rack.multithread'
|
70
|
-
RACK_MULTIPROCESS = 'rack.multiprocess'
|
71
|
-
RACK_RUNONCE = 'rack.run_once'
|
72
|
-
RACK_URL_SCHEME = 'rack.url_scheme'
|
73
|
-
RACK_HIJACK = 'rack.hijack'
|
74
|
-
RACK_IS_HIJACK = 'rack.hijack?'
|
75
|
-
RACK_HIJACK_IO = 'rack.hijack_io'
|
76
|
-
RACK_RECURSIVE_INCLUDE = 'rack.recursive.include'
|
77
|
-
RACK_MULTIPART_BUFFER_SIZE = 'rack.multipart.buffer_size'
|
78
|
-
RACK_MULTIPART_TEMPFILE_FACTORY = 'rack.multipart.tempfile_factory'
|
79
|
-
RACK_REQUEST_FORM_INPUT = 'rack.request.form_input'
|
80
|
-
RACK_REQUEST_FORM_HASH = 'rack.request.form_hash'
|
81
|
-
RACK_REQUEST_FORM_VARS = 'rack.request.form_vars'
|
82
|
-
RACK_REQUEST_COOKIE_HASH = 'rack.request.cookie_hash'
|
83
|
-
RACK_REQUEST_COOKIE_STRING = 'rack.request.cookie_string'
|
84
|
-
RACK_REQUEST_QUERY_HASH = 'rack.request.query_hash'
|
85
|
-
RACK_REQUEST_QUERY_STRING = 'rack.request.query_string'
|
86
|
-
RACK_METHODOVERRIDE_ORIGINAL_METHOD = 'rack.methodoverride.original_method'
|
87
|
-
RACK_SESSION_UNPACKED_COOKIE_DATA = 'rack.session.unpacked_cookie_data'
|
63
|
+
RACK_VERSION = 'rack.version'
|
64
|
+
RACK_TEMPFILES = 'rack.tempfiles'
|
65
|
+
RACK_ERRORS = 'rack.errors'
|
66
|
+
RACK_LOGGER = 'rack.logger'
|
67
|
+
RACK_INPUT = 'rack.input'
|
68
|
+
RACK_SESSION = 'rack.session'
|
69
|
+
RACK_SESSION_OPTIONS = 'rack.session.options'
|
70
|
+
RACK_SHOWSTATUS_DETAIL = 'rack.showstatus.detail'
|
71
|
+
RACK_MULTITHREAD = 'rack.multithread'
|
72
|
+
RACK_MULTIPROCESS = 'rack.multiprocess'
|
73
|
+
RACK_RUNONCE = 'rack.run_once'
|
74
|
+
RACK_URL_SCHEME = 'rack.url_scheme'
|
75
|
+
RACK_HIJACK = 'rack.hijack'
|
76
|
+
RACK_IS_HIJACK = 'rack.hijack?'
|
77
|
+
RACK_HIJACK_IO = 'rack.hijack_io'
|
78
|
+
RACK_RECURSIVE_INCLUDE = 'rack.recursive.include'
|
79
|
+
RACK_MULTIPART_BUFFER_SIZE = 'rack.multipart.buffer_size'
|
80
|
+
RACK_MULTIPART_TEMPFILE_FACTORY = 'rack.multipart.tempfile_factory'
|
81
|
+
RACK_REQUEST_FORM_INPUT = 'rack.request.form_input'
|
82
|
+
RACK_REQUEST_FORM_HASH = 'rack.request.form_hash'
|
83
|
+
RACK_REQUEST_FORM_VARS = 'rack.request.form_vars'
|
84
|
+
RACK_REQUEST_COOKIE_HASH = 'rack.request.cookie_hash'
|
85
|
+
RACK_REQUEST_COOKIE_STRING = 'rack.request.cookie_string'
|
86
|
+
RACK_REQUEST_QUERY_HASH = 'rack.request.query_hash'
|
87
|
+
RACK_REQUEST_QUERY_STRING = 'rack.request.query_string'
|
88
|
+
RACK_METHODOVERRIDE_ORIGINAL_METHOD = 'rack.methodoverride.original_method'
|
89
|
+
RACK_SESSION_UNPACKED_COOKIE_DATA = 'rack.session.unpacked_cookie_data'
|
88
90
|
|
89
91
|
autoload :Builder, "rack/builder"
|
90
92
|
autoload :BodyProxy, "rack/body_proxy"
|
@@ -97,6 +99,7 @@ module Rack
|
|
97
99
|
autoload :ContentType, "rack/content_type"
|
98
100
|
autoload :ETag, "rack/etag"
|
99
101
|
autoload :File, "rack/file"
|
102
|
+
autoload :Files, "rack/files"
|
100
103
|
autoload :Deflater, "rack/deflater"
|
101
104
|
autoload :Directory, "rack/directory"
|
102
105
|
autoload :ForwardRequest, "rack/recursive"
|
data/rack.gemspec
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
Gem::Specification.new do |s|
|
2
|
-
s.name
|
4
|
+
s.name = "rack"
|
3
5
|
s.version = File.read('lib/rack.rb')[/RELEASE += +([\"\'])([\d][\w\.]+)\1/, 2]
|
4
6
|
s.platform = Gem::Platform::RUBY
|
5
7
|
s.summary = "a modular Ruby webserver interface"
|
@@ -15,20 +17,28 @@ middleware) into a single method call.
|
|
15
17
|
Also see https://rack.github.io/.
|
16
18
|
EOF
|
17
19
|
|
18
|
-
s.files = Dir['{bin/*,contrib/*,example/*,lib
|
19
|
-
%w(
|
20
|
+
s.files = Dir['{bin/*,contrib/*,example/*,lib/**/*}'] +
|
21
|
+
%w(MIT-LICENSE rack.gemspec Rakefile README.rdoc SPEC)
|
20
22
|
s.bindir = 'bin'
|
21
|
-
s.executables
|
22
|
-
s.require_path
|
23
|
-
s.extra_rdoc_files = ['README.rdoc', '
|
24
|
-
s.test_files = Dir['test/spec_*.rb']
|
23
|
+
s.executables << 'rackup'
|
24
|
+
s.require_path = 'lib'
|
25
|
+
s.extra_rdoc_files = ['README.rdoc', 'CHANGELOG.md']
|
25
26
|
|
26
27
|
s.author = 'Leah Neukirchen'
|
27
28
|
s.email = 'leah@vuxu.org'
|
28
29
|
s.homepage = 'https://rack.github.io/'
|
29
30
|
s.required_ruby_version = '>= 2.2.2'
|
31
|
+
s.metadata = {
|
32
|
+
"bug_tracker_uri" => "https://github.com/rack/rack/issues",
|
33
|
+
"changelog_uri" => "https://github.com/rack/rack/blob/master/CHANGELOG.md",
|
34
|
+
"documentation_uri" => "https://rubydoc.info/github/rack/rack",
|
35
|
+
"homepage_uri" => "https://rack.github.io",
|
36
|
+
"mailing_list_uri" => "https://groups.google.com/forum/#!forum/rack-devel",
|
37
|
+
"source_code_uri" => "https://github.com/rack/rack"
|
38
|
+
}
|
30
39
|
|
31
40
|
s.add_development_dependency 'minitest', "~> 5.0"
|
32
41
|
s.add_development_dependency 'minitest-sprint'
|
42
|
+
s.add_development_dependency 'minitest-global_expectations'
|
33
43
|
s.add_development_dependency 'rake'
|
34
44
|
end
|