rack 2.0.8 → 2.1.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 +69 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +77 -117
- data/Rakefile +25 -18
- data/SPEC +3 -4
- 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.rb +63 -60
- 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 +39 -15
- data/lib/rack/cascade.rb +6 -5
- data/lib/rack/chunked.rb +29 -6
- data/lib/rack/common_logger.rb +9 -8
- 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 +32 -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 +4 -173
- data/lib/rack/files.rb +178 -0
- data/lib/rack/handler.rb +7 -2
- 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/head.rb +2 -0
- data/lib/rack/lint.rb +14 -11
- 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.rb +5 -3
- data/lib/rack/multipart/generator.rb +6 -7
- data/lib/rack/multipart/parser.rb +51 -45
- data/lib/rack/multipart/uploaded_file.rb +2 -0
- 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 -18
- data/lib/rack/session/abstract/id.rb +32 -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 +55 -70
- data/rack.gemspec +17 -7
- metadata +29 -169
- data/HISTORY.md +0 -505
- 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 -95
- 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 -515
- 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 -722
- data/test/spec_null_logger.rb +0 -21
- data/test/spec_recursive.rb +0 -75
- data/test/spec_request.rb +0 -1407
- data/test/spec_response.rb +0 -510
- 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_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
@@ -1,99 +1,10 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'rack/session/
|
4
|
-
require 'memcache'
|
3
|
+
require 'rack/session/dalli'
|
5
4
|
|
6
5
|
module Rack
|
7
6
|
module Session
|
8
|
-
|
9
|
-
|
10
|
-
# maintained in the cookie.
|
11
|
-
# You may treat Session::Memcache as you would Session::Pool with the
|
12
|
-
# following caveats.
|
13
|
-
#
|
14
|
-
# * Setting :expire_after to 0 would note to the Memcache server to hang
|
15
|
-
# onto the session data until it would drop it according to it's own
|
16
|
-
# specifications. However, the cookie sent to the client would expire
|
17
|
-
# immediately.
|
18
|
-
#
|
19
|
-
# Note that memcache does drop data before it may be listed to expire. For
|
20
|
-
# a full description of behaviour, please see memcache's documentation.
|
21
|
-
|
22
|
-
class Memcache < Abstract::PersistedSecure
|
23
|
-
attr_reader :mutex, :pool
|
24
|
-
|
25
|
-
DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \
|
26
|
-
:namespace => 'rack:session',
|
27
|
-
:memcache_server => 'localhost:11211'
|
28
|
-
|
29
|
-
def initialize(app, options={})
|
30
|
-
super
|
31
|
-
|
32
|
-
@mutex = Mutex.new
|
33
|
-
mserv = @default_options[:memcache_server]
|
34
|
-
mopts = @default_options.reject{|k,v| !MemCache::DEFAULT_OPTIONS.include? k }
|
35
|
-
|
36
|
-
@pool = options[:cache] || MemCache.new(mserv, mopts)
|
37
|
-
unless @pool.active? and @pool.servers.any?(&:alive?)
|
38
|
-
raise 'No memcache servers'
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def generate_sid
|
43
|
-
loop do
|
44
|
-
sid = super
|
45
|
-
break sid unless @pool.get(sid.private_id, true)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def find_session(req, sid)
|
50
|
-
with_lock(req) do
|
51
|
-
unless sid and session = get_session_with_fallback(sid)
|
52
|
-
sid, session = generate_sid, {}
|
53
|
-
unless /^STORED/ =~ @pool.add(sid.private_id, session)
|
54
|
-
raise "Session collision on '#{sid.inspect}'"
|
55
|
-
end
|
56
|
-
end
|
57
|
-
[sid, session]
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def write_session(req, session_id, new_session, options)
|
62
|
-
expiry = options[:expire_after]
|
63
|
-
expiry = expiry.nil? ? 0 : expiry + 1
|
64
|
-
|
65
|
-
with_lock(req) do
|
66
|
-
@pool.set session_id.private_id, new_session, expiry
|
67
|
-
session_id
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def delete_session(req, session_id, options)
|
72
|
-
with_lock(req) do
|
73
|
-
@pool.delete(session_id.public_id)
|
74
|
-
@pool.delete(session_id.private_id)
|
75
|
-
generate_sid unless options[:drop]
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def with_lock(req)
|
80
|
-
@mutex.lock if req.multithread?
|
81
|
-
yield
|
82
|
-
rescue MemCache::MemCacheError, Errno::ECONNREFUSED
|
83
|
-
if $VERBOSE
|
84
|
-
warn "#{self} is unable to find memcached server."
|
85
|
-
warn $!.inspect
|
86
|
-
end
|
87
|
-
raise
|
88
|
-
ensure
|
89
|
-
@mutex.unlock if @mutex.locked?
|
90
|
-
end
|
91
|
-
|
92
|
-
private
|
93
|
-
|
94
|
-
def get_session_with_fallback(sid)
|
95
|
-
@pool.get(sid.private_id) || @pool.get(sid.public_id)
|
96
|
-
end
|
97
|
-
end
|
7
|
+
warn "Rack::Session::Memcache is deprecated, please use Rack::Session::Dalli from 'dalli' gem instead."
|
8
|
+
Memcache = Dalli
|
98
9
|
end
|
99
10
|
end
|
data/lib/rack/session/pool.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
|
2
4
|
# THANKS:
|
3
5
|
# apeiros, for session id generation, expiry setup, and threadiness
|
@@ -26,9 +28,9 @@ module Rack
|
|
26
28
|
|
27
29
|
class Pool < Abstract::PersistedSecure
|
28
30
|
attr_reader :mutex, :pool
|
29
|
-
DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :
|
31
|
+
DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge drop: false
|
30
32
|
|
31
|
-
def initialize(app, options={})
|
33
|
+
def initialize(app, options = {})
|
32
34
|
super
|
33
35
|
@pool = Hash.new
|
34
36
|
@mutex = Mutex.new
|
data/lib/rack/show_exceptions.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'ostruct'
|
2
4
|
require 'erb'
|
3
5
|
require 'rack/request'
|
@@ -55,7 +57,7 @@ module Rack
|
|
55
57
|
private :accepts_html?
|
56
58
|
|
57
59
|
def dump_exception(exception)
|
58
|
-
string = "#{exception.class}: #{exception.message}\n"
|
60
|
+
string = "#{exception.class}: #{exception.message}\n".dup
|
59
61
|
string << exception.backtrace.map { |l| "\t#{l}" }.join("\n")
|
60
62
|
string
|
61
63
|
end
|
@@ -77,13 +79,13 @@ module Rack
|
|
77
79
|
frame.function = $4
|
78
80
|
|
79
81
|
begin
|
80
|
-
lineno = frame.lineno-1
|
82
|
+
lineno = frame.lineno - 1
|
81
83
|
lines = ::File.readlines(frame.filename)
|
82
|
-
frame.pre_context_lineno = [lineno-CONTEXT, 0].max
|
84
|
+
frame.pre_context_lineno = [lineno - CONTEXT, 0].max
|
83
85
|
frame.pre_context = lines[frame.pre_context_lineno...lineno]
|
84
86
|
frame.context_line = lines[lineno].chomp
|
85
|
-
frame.post_context_lineno = [lineno+CONTEXT, lines.size].min
|
86
|
-
frame.post_context = lines[lineno+1..frame.post_context_lineno]
|
87
|
+
frame.post_context_lineno = [lineno + CONTEXT, lines.size].min
|
88
|
+
frame.post_context = lines[lineno + 1..frame.post_context_lineno]
|
87
89
|
rescue
|
88
90
|
end
|
89
91
|
|
@@ -93,7 +95,11 @@ module Rack
|
|
93
95
|
end
|
94
96
|
}.compact
|
95
97
|
|
96
|
-
|
98
|
+
template.result(binding)
|
99
|
+
end
|
100
|
+
|
101
|
+
def template
|
102
|
+
TEMPLATE
|
97
103
|
end
|
98
104
|
|
99
105
|
def h(obj) # :nodoc:
|
@@ -107,8 +113,8 @@ module Rack
|
|
107
113
|
|
108
114
|
# :stopdoc:
|
109
115
|
|
110
|
-
# adapted from Django <djangoproject.com>
|
111
|
-
# Copyright (c)
|
116
|
+
# adapted from Django <www.djangoproject.com>
|
117
|
+
# Copyright (c) Django Software Foundation and individual contributors.
|
112
118
|
# Used under the modified BSD license:
|
113
119
|
# http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
|
114
120
|
TEMPLATE = ERB.new(<<-'HTML'.gsub(/^ /, ''))
|
@@ -363,7 +369,7 @@ module Rack
|
|
363
369
|
<% env.sort_by { |k, v| k.to_s }.each { |key, val| %>
|
364
370
|
<tr>
|
365
371
|
<td><%=h key %></td>
|
366
|
-
<td class="code"><div><%=h val %></div></td>
|
372
|
+
<td class="code"><div><%=h val.inspect %></div></td>
|
367
373
|
</tr>
|
368
374
|
<% } %>
|
369
375
|
</tbody>
|
data/lib/rack/show_status.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'erb'
|
2
4
|
require 'rack/request'
|
3
5
|
require 'rack/utils'
|
@@ -52,8 +54,8 @@ module Rack
|
|
52
54
|
|
53
55
|
# :stopdoc:
|
54
56
|
|
55
|
-
# adapted from Django <djangoproject.com>
|
56
|
-
# Copyright (c)
|
57
|
+
# adapted from Django <www.djangoproject.com>
|
58
|
+
# Copyright (c) Django Software Foundation and individual contributors.
|
57
59
|
# Used under the modified BSD license:
|
58
60
|
# http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
|
59
61
|
TEMPLATE = <<'HTML'
|
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
|
@@ -118,7 +124,7 @@ module Rack
|
|
118
124
|
when Hash
|
119
125
|
value.map { |k, v|
|
120
126
|
build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
|
121
|
-
}.
|
127
|
+
}.delete_if(&:empty?).join('&')
|
122
128
|
when nil
|
123
129
|
prefix
|
124
130
|
else
|
@@ -132,7 +138,7 @@ module Rack
|
|
132
138
|
q_value_header.to_s.split(/\s*,\s*/).map do |part|
|
133
139
|
value, parameters = part.split(/\s*;\s*/, 2)
|
134
140
|
quality = 1.0
|
135
|
-
if md = /\Aq=([\d.]+)/.match(parameters)
|
141
|
+
if parameters && (md = /\Aq=([\d.]+)/.match(parameters))
|
136
142
|
quality = md[1].to_f
|
137
143
|
end
|
138
144
|
[value, quality]
|
@@ -175,27 +181,26 @@ module Rack
|
|
175
181
|
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
176
182
|
|
177
183
|
expanded_accept_encoding =
|
178
|
-
accept_encoding.
|
184
|
+
accept_encoding.each_with_object([]) do |(m, q), list|
|
179
185
|
if m == "*"
|
180
|
-
(available_encodings - accept_encoding.map
|
186
|
+
(available_encodings - accept_encoding.map(&:first))
|
187
|
+
.each { |m2| list << [m2, q] }
|
181
188
|
else
|
182
|
-
[
|
189
|
+
list << [m, q]
|
183
190
|
end
|
184
|
-
|
185
|
-
mem + list
|
186
|
-
}
|
191
|
+
end
|
187
192
|
|
188
|
-
encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map
|
193
|
+
encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map!(&:first)
|
189
194
|
|
190
195
|
unless encoding_candidates.include?("identity")
|
191
196
|
encoding_candidates.push("identity")
|
192
197
|
end
|
193
198
|
|
194
|
-
expanded_accept_encoding.each
|
199
|
+
expanded_accept_encoding.each do |m, q|
|
195
200
|
encoding_candidates.delete(m) if q == 0.0
|
196
|
-
|
201
|
+
end
|
197
202
|
|
198
|
-
|
203
|
+
(encoding_candidates & available_encodings)[0]
|
199
204
|
end
|
200
205
|
module_function :select_best_encoding
|
201
206
|
|
@@ -211,7 +216,7 @@ module Rack
|
|
211
216
|
# precede those with less specific. Ordering with respect to other
|
212
217
|
# attributes (e.g., Domain) is unspecified.
|
213
218
|
cookies = parse_query(header, ';,') { |s| unescape(s) rescue s }
|
214
|
-
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 }
|
215
220
|
end
|
216
221
|
module_function :parse_cookies_header
|
217
222
|
|
@@ -221,41 +226,19 @@ module Rack
|
|
221
226
|
domain = "; domain=#{value[:domain]}" if value[:domain]
|
222
227
|
path = "; path=#{value[:path]}" if value[:path]
|
223
228
|
max_age = "; max-age=#{value[:max_age]}" if value[:max_age]
|
224
|
-
|
225
|
-
# only are there contradicting RFCs and examples within RFC text, but
|
226
|
-
# there are also numerous conflicting names of fields and partially
|
227
|
-
# cross-applicable specifications.
|
228
|
-
#
|
229
|
-
# These are best described in RFC 2616 3.3.1. This RFC text also
|
230
|
-
# specifies that RFC 822 as updated by RFC 1123 is preferred. That is a
|
231
|
-
# fixed length format with space-date delimited fields.
|
232
|
-
#
|
233
|
-
# See also RFC 1123 section 5.2.14.
|
234
|
-
#
|
235
|
-
# RFC 6265 also specifies "sane-cookie-date" as RFC 1123 date, defined
|
236
|
-
# in RFC 2616 3.3.1. RFC 6265 also gives examples that clearly denote
|
237
|
-
# the space delimited format. These formats are compliant with RFC 2822.
|
238
|
-
#
|
239
|
-
# For reference, all involved RFCs are:
|
240
|
-
# RFC 822
|
241
|
-
# RFC 1123
|
242
|
-
# RFC 2109
|
243
|
-
# RFC 2616
|
244
|
-
# RFC 2822
|
245
|
-
# RFC 2965
|
246
|
-
# RFC 6265
|
247
|
-
expires = "; expires=" +
|
248
|
-
rfc2822(value[:expires].clone.gmtime) if value[:expires]
|
229
|
+
expires = "; expires=#{value[:expires].httpdate}" if value[:expires]
|
249
230
|
secure = "; secure" if value[:secure]
|
250
231
|
httponly = "; HttpOnly" if (value.key?(:httponly) ? value[:httponly] : value[:http_only])
|
251
232
|
same_site =
|
252
233
|
case value[:same_site]
|
253
234
|
when false, nil
|
254
235
|
nil
|
236
|
+
when :none, 'None', :None
|
237
|
+
'; SameSite=None'
|
255
238
|
when :lax, 'Lax', :Lax
|
256
|
-
'; SameSite=Lax'
|
239
|
+
'; SameSite=Lax'
|
257
240
|
when true, :strict, 'Strict', :Strict
|
258
|
-
'; SameSite=Strict'
|
241
|
+
'; SameSite=Strict'
|
259
242
|
else
|
260
243
|
raise ArgumentError, "Invalid SameSite value: #{value[:same_site].inspect}"
|
261
244
|
end
|
@@ -295,15 +278,15 @@ module Rack
|
|
295
278
|
cookies = header
|
296
279
|
end
|
297
280
|
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
}
|
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 }
|
307
290
|
|
308
291
|
cookies.join("\n")
|
309
292
|
end
|
@@ -321,9 +304,9 @@ module Rack
|
|
321
304
|
new_header = make_delete_cookie_header(header, key, value)
|
322
305
|
|
323
306
|
add_cookie_to_header(new_header, key,
|
324
|
-
{:
|
325
|
-
:
|
326
|
-
:
|
307
|
+
{ value: '', path: nil, domain: nil,
|
308
|
+
max_age: '0',
|
309
|
+
expires: Time.at(0) }.merge(value))
|
327
310
|
|
328
311
|
end
|
329
312
|
module_function :add_remove_cookie_to_header
|
@@ -364,7 +347,7 @@ module Rack
|
|
364
347
|
ranges = []
|
365
348
|
$1.split(/,\s*/).each do |range_spec|
|
366
349
|
return nil unless range_spec =~ /(\d*)-(\d*)/
|
367
|
-
r0,r1 = $1, $2
|
350
|
+
r0, r1 = $1, $2
|
368
351
|
if r0.empty?
|
369
352
|
return nil if r1.empty?
|
370
353
|
# suffix-byte-range-spec, represents trailing suffix of file
|
@@ -378,7 +361,7 @@ module Rack
|
|
378
361
|
else
|
379
362
|
r1 = r1.to_i
|
380
363
|
return nil if r1 < r0 # backwards range is syntactically invalid
|
381
|
-
r1 = size-1 if r1 >= size
|
364
|
+
r1 = size - 1 if r1 >= size
|
382
365
|
end
|
383
366
|
end
|
384
367
|
ranges << (r0..r1) if r0 <= r1
|
@@ -399,7 +382,7 @@ module Rack
|
|
399
382
|
l = a.unpack("C*")
|
400
383
|
|
401
384
|
r, i = 0, -1
|
402
|
-
b.each_byte { |v| r |= v ^ l[i+=1] }
|
385
|
+
b.each_byte { |v| r |= v ^ l[i += 1] }
|
403
386
|
r == 0
|
404
387
|
end
|
405
388
|
module_function :secure_compare
|
@@ -425,19 +408,17 @@ module Rack
|
|
425
408
|
self.class.new(@for, app)
|
426
409
|
end
|
427
410
|
|
428
|
-
def context(env, app
|
411
|
+
def context(env, app = @app)
|
429
412
|
recontext(app).call(env)
|
430
413
|
end
|
431
414
|
end
|
432
415
|
|
433
416
|
# A case-insensitive Hash that preserves the original case of a
|
434
417
|
# header when set.
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
def initialize(hash={})
|
418
|
+
#
|
419
|
+
# @api private
|
420
|
+
class HeaderHash < Hash # :nodoc:
|
421
|
+
def initialize(hash = {})
|
441
422
|
super()
|
442
423
|
@names = {}
|
443
424
|
hash.each { |k, v| self[k] = v }
|
@@ -457,7 +438,7 @@ module Rack
|
|
457
438
|
|
458
439
|
def to_hash
|
459
440
|
hash = {}
|
460
|
-
each { |k,v| hash[k] = v }
|
441
|
+
each { |k, v| hash[k] = v }
|
461
442
|
hash
|
462
443
|
end
|
463
444
|
|
@@ -510,13 +491,14 @@ module Rack
|
|
510
491
|
|
511
492
|
# Every standard HTTP code mapped to the appropriate message.
|
512
493
|
# Generated with:
|
513
|
-
#
|
514
|
-
#
|
515
|
-
#
|
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,"'
|
516
497
|
HTTP_STATUS_CODES = {
|
517
498
|
100 => 'Continue',
|
518
499
|
101 => 'Switching Protocols',
|
519
500
|
102 => 'Processing',
|
501
|
+
103 => 'Early Hints',
|
520
502
|
200 => 'OK',
|
521
503
|
201 => 'Created',
|
522
504
|
202 => 'Accepted',
|
@@ -533,6 +515,7 @@ module Rack
|
|
533
515
|
303 => 'See Other',
|
534
516
|
304 => 'Not Modified',
|
535
517
|
305 => 'Use Proxy',
|
518
|
+
306 => '(Unused)',
|
536
519
|
307 => 'Temporary Redirect',
|
537
520
|
308 => 'Permanent Redirect',
|
538
521
|
400 => 'Bad Request',
|
@@ -557,6 +540,7 @@ module Rack
|
|
557
540
|
422 => 'Unprocessable Entity',
|
558
541
|
423 => 'Locked',
|
559
542
|
424 => 'Failed Dependency',
|
543
|
+
425 => 'Too Early',
|
560
544
|
426 => 'Upgrade Required',
|
561
545
|
428 => 'Precondition Required',
|
562
546
|
429 => 'Too Many Requests',
|
@@ -571,12 +555,13 @@ module Rack
|
|
571
555
|
506 => 'Variant Also Negotiates',
|
572
556
|
507 => 'Insufficient Storage',
|
573
557
|
508 => 'Loop Detected',
|
558
|
+
509 => 'Bandwidth Limit Exceeded',
|
574
559
|
510 => 'Not Extended',
|
575
560
|
511 => 'Network Authentication Required'
|
576
561
|
}
|
577
562
|
|
578
563
|
# Responses with HTTP status codes that should not have an entity body
|
579
|
-
STATUS_WITH_NO_ENTITY_BODY =
|
564
|
+
STATUS_WITH_NO_ENTITY_BODY = Hash[((100..199).to_a << 204 << 304).product([true])]
|
580
565
|
|
581
566
|
SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
|
582
567
|
[message.downcase.gsub(/\s|-|'/, '_').to_sym, code]
|
@@ -584,7 +569,7 @@ module Rack
|
|
584
569
|
|
585
570
|
def status_code(status)
|
586
571
|
if status.is_a?(Symbol)
|
587
|
-
SYMBOL_TO_STATUS_CODE
|
572
|
+
SYMBOL_TO_STATUS_CODE.fetch(status) { raise ArgumentError, "Unrecognized status code #{status.inspect}" }
|
588
573
|
else
|
589
574
|
status.to_i
|
590
575
|
end
|
@@ -605,11 +590,11 @@ module Rack
|
|
605
590
|
|
606
591
|
clean.unshift '/' if parts.empty? || parts.first.empty?
|
607
592
|
|
608
|
-
::File.join
|
593
|
+
::File.join clean
|
609
594
|
end
|
610
595
|
module_function :clean_path_info
|
611
596
|
|
612
|
-
NULL_BYTE = "\0"
|
597
|
+
NULL_BYTE = "\0"
|
613
598
|
|
614
599
|
def valid_path?(path)
|
615
600
|
path.valid_encoding? && !path.include?(NULL_BYTE)
|