rack 2.0.6 → 2.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +694 -0
- data/CONTRIBUTING.md +136 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +152 -148
- data/Rakefile +37 -23
- data/{SPEC → SPEC.rdoc} +38 -10
- 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 +67 -73
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +1 -1
- data/lib/rack/auth/basic.rb +7 -4
- data/lib/rack/auth/digest/md5.rb +13 -11
- 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 +5 -3
- data/lib/rack/body_proxy.rb +15 -14
- data/lib/rack/builder.rb +116 -23
- data/lib/rack/cascade.rb +28 -12
- data/lib/rack/chunked.rb +68 -20
- data/lib/rack/common_logger.rb +33 -25
- data/lib/rack/conditional_get.rb +20 -16
- data/lib/rack/config.rb +2 -0
- data/lib/rack/content_length.rb +8 -7
- data/lib/rack/content_type.rb +5 -4
- data/lib/rack/core_ext/regexp.rb +14 -0
- data/lib/rack/deflater.rb +59 -34
- data/lib/rack/directory.rb +84 -64
- data/lib/rack/etag.rb +5 -4
- data/lib/rack/events.rb +19 -20
- data/lib/rack/file.rb +4 -173
- data/lib/rack/files.rb +218 -0
- data/lib/rack/handler.rb +7 -2
- data/lib/rack/handler/cgi.rb +2 -3
- data/lib/rack/handler/fastcgi.rb +4 -4
- data/lib/rack/handler/lsws.rb +3 -3
- data/lib/rack/handler/scgi.rb +9 -8
- data/lib/rack/handler/thin.rb +3 -3
- data/lib/rack/handler/webrick.rb +15 -6
- data/lib/rack/head.rb +1 -1
- data/lib/rack/lint.rb +71 -25
- data/lib/rack/lobster.rb +10 -10
- data/lib/rack/lock.rb +2 -1
- 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 +97 -20
- data/lib/rack/multipart.rb +6 -4
- data/lib/rack/multipart/generator.rb +17 -13
- data/lib/rack/multipart/parser.rb +57 -62
- data/lib/rack/multipart/uploaded_file.rb +15 -7
- data/lib/rack/null_logger.rb +2 -0
- data/lib/rack/query_parser.rb +53 -28
- data/lib/rack/recursive.rb +7 -5
- data/lib/rack/reloader.rb +8 -4
- data/lib/rack/request.rb +220 -61
- data/lib/rack/response.rb +127 -44
- data/lib/rack/rewindable_input.rb +4 -3
- data/lib/rack/runtime.rb +6 -4
- data/lib/rack/sendfile.rb +13 -9
- data/lib/rack/server.rb +95 -24
- data/lib/rack/session/abstract/id.rb +100 -22
- data/lib/rack/session/cookie.rb +22 -14
- data/lib/rack/session/memcache.rb +4 -87
- data/lib/rack/session/pool.rb +18 -9
- data/lib/rack/show_exceptions.rb +21 -17
- data/lib/rack/show_status.rb +9 -9
- data/lib/rack/static.rb +23 -11
- data/lib/rack/tempfile_reaper.rb +1 -1
- data/lib/rack/urlmap.rb +12 -6
- data/lib/rack/utils.rb +102 -109
- data/lib/rack/version.rb +29 -0
- data/rack.gemspec +40 -28
- metadata +39 -181
- 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 -1398
- 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 -320
- data/test/spec_session_pool.rb +0 -210
- 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/show_exceptions.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'ostruct'
|
|
2
4
|
require 'erb'
|
|
3
|
-
require 'rack/request'
|
|
4
|
-
require 'rack/utils'
|
|
5
5
|
|
|
6
6
|
module Rack
|
|
7
7
|
# Rack::ShowExceptions catches all exceptions raised from the app it
|
|
@@ -55,7 +55,7 @@ module Rack
|
|
|
55
55
|
private :accepts_html?
|
|
56
56
|
|
|
57
57
|
def dump_exception(exception)
|
|
58
|
-
string = "#{exception.class}: #{exception.message}\n"
|
|
58
|
+
string = "#{exception.class}: #{exception.message}\n".dup
|
|
59
59
|
string << exception.backtrace.map { |l| "\t#{l}" }.join("\n")
|
|
60
60
|
string
|
|
61
61
|
end
|
|
@@ -63,12 +63,12 @@ module Rack
|
|
|
63
63
|
def pretty(env, exception)
|
|
64
64
|
req = Rack::Request.new(env)
|
|
65
65
|
|
|
66
|
-
# This double assignment is to prevent an "unused variable" warning
|
|
67
|
-
#
|
|
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.
|
|
68
68
|
path = path = (req.script_name + req.path_info).squeeze("/")
|
|
69
69
|
|
|
70
|
-
# This double assignment is to prevent an "unused variable" warning
|
|
71
|
-
#
|
|
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.
|
|
72
72
|
frames = frames = exception.backtrace.map { |line|
|
|
73
73
|
frame = OpenStruct.new
|
|
74
74
|
if line =~ /(.*?):(\d+)(:in `(.*)')?/
|
|
@@ -77,13 +77,13 @@ module Rack
|
|
|
77
77
|
frame.function = $4
|
|
78
78
|
|
|
79
79
|
begin
|
|
80
|
-
lineno = frame.lineno-1
|
|
80
|
+
lineno = frame.lineno - 1
|
|
81
81
|
lines = ::File.readlines(frame.filename)
|
|
82
|
-
frame.pre_context_lineno = [lineno-CONTEXT, 0].max
|
|
82
|
+
frame.pre_context_lineno = [lineno - CONTEXT, 0].max
|
|
83
83
|
frame.pre_context = lines[frame.pre_context_lineno...lineno]
|
|
84
84
|
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]
|
|
85
|
+
frame.post_context_lineno = [lineno + CONTEXT, lines.size].min
|
|
86
|
+
frame.post_context = lines[lineno + 1..frame.post_context_lineno]
|
|
87
87
|
rescue
|
|
88
88
|
end
|
|
89
89
|
|
|
@@ -93,7 +93,11 @@ module Rack
|
|
|
93
93
|
end
|
|
94
94
|
}.compact
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
template.result(binding)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def template
|
|
100
|
+
TEMPLATE
|
|
97
101
|
end
|
|
98
102
|
|
|
99
103
|
def h(obj) # :nodoc:
|
|
@@ -107,8 +111,8 @@ module Rack
|
|
|
107
111
|
|
|
108
112
|
# :stopdoc:
|
|
109
113
|
|
|
110
|
-
# adapted from Django <djangoproject.com>
|
|
111
|
-
# Copyright (c)
|
|
114
|
+
# adapted from Django <www.djangoproject.com>
|
|
115
|
+
# Copyright (c) Django Software Foundation and individual contributors.
|
|
112
116
|
# Used under the modified BSD license:
|
|
113
117
|
# http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
|
|
114
118
|
TEMPLATE = ERB.new(<<-'HTML'.gsub(/^ /, ''))
|
|
@@ -307,7 +311,7 @@ module Rack
|
|
|
307
311
|
<% end %>
|
|
308
312
|
|
|
309
313
|
<h3 id="post-info">POST</h3>
|
|
310
|
-
<% 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)) %>
|
|
311
315
|
<table class="req">
|
|
312
316
|
<thead>
|
|
313
317
|
<tr>
|
|
@@ -325,7 +329,7 @@ module Rack
|
|
|
325
329
|
</tbody>
|
|
326
330
|
</table>
|
|
327
331
|
<% else %>
|
|
328
|
-
<p
|
|
332
|
+
<p><%= no_post_data || "No POST data" %>.</p>
|
|
329
333
|
<% end %>
|
|
330
334
|
|
|
331
335
|
|
|
@@ -363,7 +367,7 @@ module Rack
|
|
|
363
367
|
<% env.sort_by { |k, v| k.to_s }.each { |key, val| %>
|
|
364
368
|
<tr>
|
|
365
369
|
<td><%=h key %></td>
|
|
366
|
-
<td class="code"><div><%=h val %></div></td>
|
|
370
|
+
<td class="code"><div><%=h val.inspect %></div></td>
|
|
367
371
|
</tr>
|
|
368
372
|
<% } %>
|
|
369
373
|
</tbody>
|
data/lib/rack/show_status.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'erb'
|
|
2
|
-
require 'rack/request'
|
|
3
|
-
require 'rack/utils'
|
|
4
4
|
|
|
5
5
|
module Rack
|
|
6
6
|
# Rack::ShowStatus catches all empty responses and replaces them
|
|
@@ -18,19 +18,19 @@ module Rack
|
|
|
18
18
|
|
|
19
19
|
def call(env)
|
|
20
20
|
status, headers, body = @app.call(env)
|
|
21
|
-
headers = Utils::HeaderHash
|
|
21
|
+
headers = Utils::HeaderHash[headers]
|
|
22
22
|
empty = headers[CONTENT_LENGTH].to_i <= 0
|
|
23
23
|
|
|
24
24
|
# client or server error, or explicit message
|
|
25
25
|
if (status.to_i >= 400 && empty) || env[RACK_SHOWSTATUS_DETAIL]
|
|
26
|
-
# This double assignment is to prevent an "unused variable" warning
|
|
27
|
-
#
|
|
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.
|
|
28
28
|
req = req = Rack::Request.new(env)
|
|
29
29
|
|
|
30
30
|
message = Rack::Utils::HTTP_STATUS_CODES[status.to_i] || status.to_s
|
|
31
31
|
|
|
32
|
-
# This double assignment is to prevent an "unused variable" warning
|
|
33
|
-
#
|
|
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.
|
|
34
34
|
detail = detail = env[RACK_SHOWSTATUS_DETAIL] || message
|
|
35
35
|
|
|
36
36
|
body = @template.result(binding)
|
|
@@ -52,8 +52,8 @@ module Rack
|
|
|
52
52
|
|
|
53
53
|
# :stopdoc:
|
|
54
54
|
|
|
55
|
-
# adapted from Django <djangoproject.com>
|
|
56
|
-
# Copyright (c)
|
|
55
|
+
# adapted from Django <www.djangoproject.com>
|
|
56
|
+
# Copyright (c) Django Software Foundation and individual contributors.
|
|
57
57
|
# Used under the modified BSD license:
|
|
58
58
|
# http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
|
|
59
59
|
TEMPLATE = <<'HTML'
|
data/lib/rack/static.rb
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
require "rack/utils"
|
|
1
|
+
# frozen_string_literal: true
|
|
3
2
|
|
|
4
3
|
module Rack
|
|
5
4
|
|
|
6
5
|
# The Rack::Static middleware intercepts requests for static files
|
|
7
6
|
# (javascript files, images, stylesheets, etc) based on the url prefixes or
|
|
8
|
-
# route mappings passed in the options, and serves them using a Rack::
|
|
7
|
+
# route mappings passed in the options, and serves them using a Rack::Files
|
|
9
8
|
# object. This allows a Rack stack to serve both static and dynamic content.
|
|
10
9
|
#
|
|
11
10
|
# Examples:
|
|
@@ -15,6 +14,11 @@ module Rack
|
|
|
15
14
|
#
|
|
16
15
|
# use Rack::Static, :urls => ["/media"]
|
|
17
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
|
+
#
|
|
18
22
|
# Serve all requests beginning with /css or /images from the folder "public"
|
|
19
23
|
# in the current directory (ie public/css/* and public/images/*):
|
|
20
24
|
#
|
|
@@ -82,24 +86,26 @@ module Rack
|
|
|
82
86
|
# ]
|
|
83
87
|
#
|
|
84
88
|
class Static
|
|
89
|
+
(require_relative 'core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
|
|
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]
|
|
90
95
|
@gzip = options[:gzip]
|
|
96
|
+
@cascade = options[:cascade]
|
|
91
97
|
root = options[:root] || Dir.pwd
|
|
92
98
|
|
|
93
99
|
# HTTP Headers
|
|
94
100
|
@header_rules = options[:header_rules] || []
|
|
95
101
|
# 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]
|
|
102
|
+
@header_rules.unshift([:all, { CACHE_CONTROL => options[:cache_control] }]) if options[:cache_control]
|
|
97
103
|
|
|
98
|
-
@file_server = Rack::
|
|
104
|
+
@file_server = Rack::Files.new(root)
|
|
99
105
|
end
|
|
100
106
|
|
|
101
107
|
def add_index_root?(path)
|
|
102
|
-
@index && path
|
|
108
|
+
@index && route_file(path) && path.end_with?('/')
|
|
103
109
|
end
|
|
104
110
|
|
|
105
111
|
def overwrite_file_path(path)
|
|
@@ -120,7 +126,7 @@ module Rack
|
|
|
120
126
|
if can_serve(path)
|
|
121
127
|
if overwrite_file_path(path)
|
|
122
128
|
env[PATH_INFO] = (add_index_root?(path) ? path + @index : @urls[path])
|
|
123
|
-
elsif @gzip && env['HTTP_ACCEPT_ENCODING']
|
|
129
|
+
elsif @gzip && env['HTTP_ACCEPT_ENCODING'] && /\bgzip\b/.match?(env['HTTP_ACCEPT_ENCODING'])
|
|
124
130
|
path = env[PATH_INFO]
|
|
125
131
|
env[PATH_INFO] += '.gz'
|
|
126
132
|
response = @file_server.call(env)
|
|
@@ -128,6 +134,8 @@ module Rack
|
|
|
128
134
|
|
|
129
135
|
if response[0] == 404
|
|
130
136
|
response = nil
|
|
137
|
+
elsif response[0] == 304
|
|
138
|
+
# Do nothing, leave headers as is
|
|
131
139
|
else
|
|
132
140
|
if mime_type = Mime.mime_type(::File.extname(path), 'text/plain')
|
|
133
141
|
response[1][CONTENT_TYPE] = mime_type
|
|
@@ -139,6 +147,10 @@ module Rack
|
|
|
139
147
|
path = env[PATH_INFO]
|
|
140
148
|
response ||= @file_server.call(env)
|
|
141
149
|
|
|
150
|
+
if @cascade && response[0] == 404
|
|
151
|
+
return @app.call(env)
|
|
152
|
+
end
|
|
153
|
+
|
|
142
154
|
headers = response[1]
|
|
143
155
|
applicable_rules(path).each do |rule, new_headers|
|
|
144
156
|
new_headers.each { |field, content| headers[field] = content }
|
|
@@ -157,14 +169,14 @@ module Rack
|
|
|
157
169
|
when :all
|
|
158
170
|
true
|
|
159
171
|
when :fonts
|
|
160
|
-
|
|
172
|
+
/\.(?:ttf|otf|eot|woff2|woff|svg)\z/.match?(path)
|
|
161
173
|
when String
|
|
162
174
|
path = ::Rack::Utils.unescape(path)
|
|
163
175
|
path.start_with?(rule) || path.start_with?('/' + rule)
|
|
164
176
|
when Array
|
|
165
|
-
|
|
177
|
+
/\.(#{rule.join('|')})\z/.match?(path)
|
|
166
178
|
when Regexp
|
|
167
|
-
path
|
|
179
|
+
rule.match?(path)
|
|
168
180
|
else
|
|
169
181
|
false
|
|
170
182
|
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
|
|
@@ -12,17 +16,16 @@ module Rack
|
|
|
12
16
|
# first, since they are most specific.
|
|
13
17
|
|
|
14
18
|
class URLMap
|
|
15
|
-
NEGATIVE_INFINITY = -1.0 / 0.0
|
|
16
|
-
INFINITY = 1.0 / 0.0
|
|
17
|
-
|
|
18
19
|
def initialize(map = {})
|
|
19
20
|
remap(map)
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
def remap(map)
|
|
24
|
+
@known_hosts = Set[]
|
|
23
25
|
@mapping = map.map { |location, app|
|
|
24
26
|
if location =~ %r{\Ahttps?://(.*?)(/.*)}
|
|
25
27
|
host, location = $1, $2
|
|
28
|
+
@known_hosts << host
|
|
26
29
|
else
|
|
27
30
|
host = nil
|
|
28
31
|
end
|
|
@@ -36,7 +39,7 @@ module Rack
|
|
|
36
39
|
|
|
37
40
|
[host, location, match, app]
|
|
38
41
|
}.sort_by do |(host, location, _, _)|
|
|
39
|
-
[host ? -host.size : INFINITY, -location.size]
|
|
42
|
+
[host ? -host.size : Float::INFINITY, -location.size]
|
|
40
43
|
end
|
|
41
44
|
end
|
|
42
45
|
|
|
@@ -50,10 +53,13 @@ module Rack
|
|
|
50
53
|
is_same_server = casecmp?(http_host, server_name) ||
|
|
51
54
|
casecmp?(http_host, "#{server_name}:#{server_port}")
|
|
52
55
|
|
|
56
|
+
is_host_known = @known_hosts.include? http_host
|
|
57
|
+
|
|
53
58
|
@mapping.each do |host, location, match, app|
|
|
54
59
|
unless casecmp?(http_host, host) \
|
|
55
60
|
|| casecmp?(server_name, host) \
|
|
56
|
-
|| (!host && is_same_server)
|
|
61
|
+
|| (!host && is_same_server) \
|
|
62
|
+
|| (!host && !is_host_known) # If we don't have a matching host, default to the first without a specified host
|
|
57
63
|
next
|
|
58
64
|
end
|
|
59
65
|
|
|
@@ -68,7 +74,7 @@ module Rack
|
|
|
68
74
|
return app.call(env)
|
|
69
75
|
end
|
|
70
76
|
|
|
71
|
-
[404, {CONTENT_TYPE => "text/plain", "X-Cascade" => "pass"}, ["Not Found: #{path}"]]
|
|
77
|
+
[404, { CONTENT_TYPE => "text/plain", "X-Cascade" => "pass" }, ["Not Found: #{path}"]]
|
|
72
78
|
|
|
73
79
|
ensure
|
|
74
80
|
env[PATH_INFO] = path
|
data/lib/rack/utils.rb
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
# -*- encoding: binary -*-
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
2
4
|
require 'uri'
|
|
3
5
|
require 'fileutils'
|
|
4
6
|
require 'set'
|
|
5
7
|
require 'tempfile'
|
|
6
|
-
require 'rack/query_parser'
|
|
7
8
|
require 'time'
|
|
8
9
|
|
|
10
|
+
require_relative 'query_parser'
|
|
11
|
+
|
|
9
12
|
module Rack
|
|
10
13
|
# Rack::Utils contains a grab-bag of useful methods for writing web
|
|
11
14
|
# applications adopted from all kinds of Ruby libraries.
|
|
12
15
|
|
|
13
16
|
module Utils
|
|
17
|
+
(require_relative 'core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
|
|
18
|
+
|
|
14
19
|
ParameterTypeError = QueryParser::ParameterTypeError
|
|
15
20
|
InvalidParameterError = QueryParser::InvalidParameterError
|
|
16
21
|
DEFAULT_SEP = QueryParser::DEFAULT_SEP
|
|
@@ -24,33 +29,30 @@ module Rack
|
|
|
24
29
|
# This helps prevent a rogue client from flooding a Request.
|
|
25
30
|
self.default_query_parser = QueryParser.make_default(65536, 100)
|
|
26
31
|
|
|
32
|
+
module_function
|
|
33
|
+
|
|
27
34
|
# URI escapes. (CGI style space to +)
|
|
28
35
|
def escape(s)
|
|
29
36
|
URI.encode_www_form_component(s)
|
|
30
37
|
end
|
|
31
|
-
module_function :escape
|
|
32
38
|
|
|
33
39
|
# Like URI escaping, but with %20 instead of +. Strictly speaking this is
|
|
34
40
|
# true URI escaping.
|
|
35
41
|
def escape_path(s)
|
|
36
42
|
::URI::DEFAULT_PARSER.escape s
|
|
37
43
|
end
|
|
38
|
-
module_function :escape_path
|
|
39
44
|
|
|
40
45
|
# Unescapes the **path** component of a URI. See Rack::Utils.unescape for
|
|
41
46
|
# unescaping query parameters or form components.
|
|
42
47
|
def unescape_path(s)
|
|
43
48
|
::URI::DEFAULT_PARSER.unescape s
|
|
44
49
|
end
|
|
45
|
-
module_function :unescape_path
|
|
46
|
-
|
|
47
50
|
|
|
48
51
|
# Unescapes a URI escaped string with +encoding+. +encoding+ will be the
|
|
49
52
|
# target encoding of the string returned, and it defaults to UTF-8
|
|
50
53
|
def unescape(s, encoding = Encoding::UTF_8)
|
|
51
54
|
URI.decode_www_form_component(s, encoding)
|
|
52
55
|
end
|
|
53
|
-
module_function :unescape
|
|
54
56
|
|
|
55
57
|
class << self
|
|
56
58
|
attr_accessor :multipart_part_limit
|
|
@@ -82,21 +84,20 @@ module Rack
|
|
|
82
84
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
83
85
|
end
|
|
84
86
|
else
|
|
87
|
+
# :nocov:
|
|
85
88
|
def clock_time
|
|
86
89
|
Time.now.to_f
|
|
87
90
|
end
|
|
91
|
+
# :nocov:
|
|
88
92
|
end
|
|
89
|
-
module_function :clock_time
|
|
90
93
|
|
|
91
94
|
def parse_query(qs, d = nil, &unescaper)
|
|
92
95
|
Rack::Utils.default_query_parser.parse_query(qs, d, &unescaper)
|
|
93
96
|
end
|
|
94
|
-
module_function :parse_query
|
|
95
97
|
|
|
96
98
|
def parse_nested_query(qs, d = nil)
|
|
97
99
|
Rack::Utils.default_query_parser.parse_nested_query(qs, d)
|
|
98
100
|
end
|
|
99
|
-
module_function :parse_nested_query
|
|
100
101
|
|
|
101
102
|
def build_query(params)
|
|
102
103
|
params.map { |k, v|
|
|
@@ -107,7 +108,6 @@ module Rack
|
|
|
107
108
|
end
|
|
108
109
|
}.join("&")
|
|
109
110
|
end
|
|
110
|
-
module_function :build_query
|
|
111
111
|
|
|
112
112
|
def build_nested_query(value, prefix = nil)
|
|
113
113
|
case value
|
|
@@ -118,7 +118,7 @@ module Rack
|
|
|
118
118
|
when Hash
|
|
119
119
|
value.map { |k, v|
|
|
120
120
|
build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
|
|
121
|
-
}.
|
|
121
|
+
}.delete_if(&:empty?).join('&')
|
|
122
122
|
when nil
|
|
123
123
|
prefix
|
|
124
124
|
else
|
|
@@ -126,20 +126,22 @@ module Rack
|
|
|
126
126
|
"#{prefix}=#{escape(value)}"
|
|
127
127
|
end
|
|
128
128
|
end
|
|
129
|
-
module_function :build_nested_query
|
|
130
129
|
|
|
131
130
|
def q_values(q_value_header)
|
|
132
131
|
q_value_header.to_s.split(/\s*,\s*/).map do |part|
|
|
133
132
|
value, parameters = part.split(/\s*;\s*/, 2)
|
|
134
133
|
quality = 1.0
|
|
135
|
-
if md = /\Aq=([\d.]+)/.match(parameters)
|
|
134
|
+
if parameters && (md = /\Aq=([\d.]+)/.match(parameters))
|
|
136
135
|
quality = md[1].to_f
|
|
137
136
|
end
|
|
138
137
|
[value, quality]
|
|
139
138
|
end
|
|
140
139
|
end
|
|
141
|
-
module_function :q_values
|
|
142
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.
|
|
143
145
|
def best_q_match(q_value_header, available_mimes)
|
|
144
146
|
values = q_values(q_value_header)
|
|
145
147
|
|
|
@@ -152,7 +154,6 @@ module Rack
|
|
|
152
154
|
end.last
|
|
153
155
|
matches && matches.first
|
|
154
156
|
end
|
|
155
|
-
module_function :best_q_match
|
|
156
157
|
|
|
157
158
|
ESCAPE_HTML = {
|
|
158
159
|
"&" => "&",
|
|
@@ -169,51 +170,55 @@ module Rack
|
|
|
169
170
|
def escape_html(string)
|
|
170
171
|
string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] }
|
|
171
172
|
end
|
|
172
|
-
module_function :escape_html
|
|
173
173
|
|
|
174
174
|
def select_best_encoding(available_encodings, accept_encoding)
|
|
175
175
|
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
|
176
176
|
|
|
177
|
-
expanded_accept_encoding =
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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]
|
|
183
185
|
end
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
186
|
+
else
|
|
187
|
+
expanded_accept_encoding << [m, q, preference]
|
|
188
|
+
end
|
|
189
|
+
end
|
|
187
190
|
|
|
188
|
-
encoding_candidates = expanded_accept_encoding
|
|
191
|
+
encoding_candidates = expanded_accept_encoding
|
|
192
|
+
.sort_by { |_, q, p| [-q, p] }
|
|
193
|
+
.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
|
-
module_function :select_best_encoding
|
|
201
205
|
|
|
202
206
|
def parse_cookies(env)
|
|
203
207
|
parse_cookies_header env[HTTP_COOKIE]
|
|
204
208
|
end
|
|
205
|
-
module_function :parse_cookies
|
|
206
209
|
|
|
207
210
|
def parse_cookies_header(header)
|
|
208
|
-
# According to RFC
|
|
209
|
-
#
|
|
210
|
-
#
|
|
211
|
-
#
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
|
215
221
|
end
|
|
216
|
-
module_function :parse_cookies_header
|
|
217
222
|
|
|
218
223
|
def add_cookie_to_header(header, key, value)
|
|
219
224
|
case value
|
|
@@ -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
|
|
@@ -277,13 +260,11 @@ module Rack
|
|
|
277
260
|
raise ArgumentError, "Unrecognized cookie header value. Expected String, Array, or nil, got #{header.inspect}"
|
|
278
261
|
end
|
|
279
262
|
end
|
|
280
|
-
module_function :add_cookie_to_header
|
|
281
263
|
|
|
282
264
|
def set_cookie_header!(header, key, value)
|
|
283
265
|
header[SET_COOKIE] = add_cookie_to_header(header[SET_COOKIE], key, value)
|
|
284
266
|
nil
|
|
285
267
|
end
|
|
286
|
-
module_function :set_cookie_header!
|
|
287
268
|
|
|
288
269
|
def make_delete_cookie_header(header, key, value)
|
|
289
270
|
case header
|
|
@@ -295,25 +276,30 @@ module Rack
|
|
|
295
276
|
cookies = header
|
|
296
277
|
end
|
|
297
278
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
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}(?:;|$)/
|
|
290
|
+
else
|
|
291
|
+
/\A#{key}=/
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
cookies.reject! { |cookie| regexp.match? cookie }
|
|
307
295
|
|
|
308
296
|
cookies.join("\n")
|
|
309
297
|
end
|
|
310
|
-
module_function :make_delete_cookie_header
|
|
311
298
|
|
|
312
299
|
def delete_cookie_header!(header, key, value = {})
|
|
313
300
|
header[SET_COOKIE] = add_remove_cookie_to_header(header[SET_COOKIE], key, value)
|
|
314
301
|
nil
|
|
315
302
|
end
|
|
316
|
-
module_function :delete_cookie_header!
|
|
317
303
|
|
|
318
304
|
# Adds a cookie that will *remove* a cookie from the client. Hence the
|
|
319
305
|
# strange method name.
|
|
@@ -321,17 +307,15 @@ module Rack
|
|
|
321
307
|
new_header = make_delete_cookie_header(header, key, value)
|
|
322
308
|
|
|
323
309
|
add_cookie_to_header(new_header, key,
|
|
324
|
-
{:
|
|
325
|
-
:
|
|
326
|
-
:
|
|
310
|
+
{ value: '', path: nil, domain: nil,
|
|
311
|
+
max_age: '0',
|
|
312
|
+
expires: Time.at(0) }.merge(value))
|
|
327
313
|
|
|
328
314
|
end
|
|
329
|
-
module_function :add_remove_cookie_to_header
|
|
330
315
|
|
|
331
316
|
def rfc2822(time)
|
|
332
317
|
time.rfc2822
|
|
333
318
|
end
|
|
334
|
-
module_function :rfc2822
|
|
335
319
|
|
|
336
320
|
# Modified version of stdlib time.rb Time#rfc2822 to use '%d-%b-%Y' instead
|
|
337
321
|
# of '% %b %Y'.
|
|
@@ -347,7 +331,6 @@ module Rack
|
|
|
347
331
|
mon = Time::RFC2822_MONTH_NAME[time.mon - 1]
|
|
348
332
|
time.strftime("#{wday}, %d-#{mon}-%Y %H:%M:%S GMT")
|
|
349
333
|
end
|
|
350
|
-
module_function :rfc2109
|
|
351
334
|
|
|
352
335
|
# Parses the "Range:" header, if present, into an array of Range objects.
|
|
353
336
|
# Returns nil if the header is missing or syntactically invalid.
|
|
@@ -356,7 +339,6 @@ module Rack
|
|
|
356
339
|
warn "`byte_ranges` is deprecated, please use `get_byte_ranges`" if $VERBOSE
|
|
357
340
|
get_byte_ranges env['HTTP_RANGE'], size
|
|
358
341
|
end
|
|
359
|
-
module_function :byte_ranges
|
|
360
342
|
|
|
361
343
|
def get_byte_ranges(http_range, size)
|
|
362
344
|
# See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
|
|
@@ -364,7 +346,7 @@ module Rack
|
|
|
364
346
|
ranges = []
|
|
365
347
|
$1.split(/,\s*/).each do |range_spec|
|
|
366
348
|
return nil unless range_spec =~ /(\d*)-(\d*)/
|
|
367
|
-
r0,r1 = $1, $2
|
|
349
|
+
r0, r1 = $1, $2
|
|
368
350
|
if r0.empty?
|
|
369
351
|
return nil if r1.empty?
|
|
370
352
|
# suffix-byte-range-spec, represents trailing suffix of file
|
|
@@ -378,14 +360,13 @@ module Rack
|
|
|
378
360
|
else
|
|
379
361
|
r1 = r1.to_i
|
|
380
362
|
return nil if r1 < r0 # backwards range is syntactically invalid
|
|
381
|
-
r1 = size-1 if r1 >= size
|
|
363
|
+
r1 = size - 1 if r1 >= size
|
|
382
364
|
end
|
|
383
365
|
end
|
|
384
366
|
ranges << (r0..r1) if r0 <= r1
|
|
385
367
|
end
|
|
386
368
|
ranges
|
|
387
369
|
end
|
|
388
|
-
module_function :get_byte_ranges
|
|
389
370
|
|
|
390
371
|
# Constant time string comparison.
|
|
391
372
|
#
|
|
@@ -399,10 +380,9 @@ module Rack
|
|
|
399
380
|
l = a.unpack("C*")
|
|
400
381
|
|
|
401
382
|
r, i = 0, -1
|
|
402
|
-
b.each_byte { |v| r |= v ^ l[i+=1] }
|
|
383
|
+
b.each_byte { |v| r |= v ^ l[i += 1] }
|
|
403
384
|
r == 0
|
|
404
385
|
end
|
|
405
|
-
module_function :secure_compare
|
|
406
386
|
|
|
407
387
|
# Context allows the use of a compatible middleware at different points
|
|
408
388
|
# in a request handling stack. A compatible middleware must define
|
|
@@ -425,19 +405,25 @@ module Rack
|
|
|
425
405
|
self.class.new(@for, app)
|
|
426
406
|
end
|
|
427
407
|
|
|
428
|
-
def context(env, app
|
|
408
|
+
def context(env, app = @app)
|
|
429
409
|
recontext(app).call(env)
|
|
430
410
|
end
|
|
431
411
|
end
|
|
432
412
|
|
|
433
413
|
# A case-insensitive Hash that preserves the original case of a
|
|
434
414
|
# header when set.
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
415
|
+
#
|
|
416
|
+
# @api private
|
|
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
|
|
438
424
|
end
|
|
439
425
|
|
|
440
|
-
def initialize(hash={})
|
|
426
|
+
def initialize(hash = {})
|
|
441
427
|
super()
|
|
442
428
|
@names = {}
|
|
443
429
|
hash.each { |k, v| self[k] = v }
|
|
@@ -449,6 +435,12 @@ module Rack
|
|
|
449
435
|
@names = other.names.dup
|
|
450
436
|
end
|
|
451
437
|
|
|
438
|
+
# on clear, we need to clear @names hash
|
|
439
|
+
def clear
|
|
440
|
+
super
|
|
441
|
+
@names.clear
|
|
442
|
+
end
|
|
443
|
+
|
|
452
444
|
def each
|
|
453
445
|
super do |k, v|
|
|
454
446
|
yield(k, v.respond_to?(:to_ary) ? v.to_ary.join("\n") : v)
|
|
@@ -457,7 +449,7 @@ module Rack
|
|
|
457
449
|
|
|
458
450
|
def to_hash
|
|
459
451
|
hash = {}
|
|
460
|
-
each { |k,v| hash[k] = v }
|
|
452
|
+
each { |k, v| hash[k] = v }
|
|
461
453
|
hash
|
|
462
454
|
end
|
|
463
455
|
|
|
@@ -510,13 +502,14 @@ module Rack
|
|
|
510
502
|
|
|
511
503
|
# Every standard HTTP code mapped to the appropriate message.
|
|
512
504
|
# Generated with:
|
|
513
|
-
#
|
|
514
|
-
#
|
|
515
|
-
#
|
|
505
|
+
# curl -s https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv | \
|
|
506
|
+
# ruby -ne 'm = /^(\d{3}),(?!Unassigned|\(Unused\))([^,]+)/.match($_) and \
|
|
507
|
+
# puts "#{m[1]} => \x27#{m[2].strip}\x27,"'
|
|
516
508
|
HTTP_STATUS_CODES = {
|
|
517
509
|
100 => 'Continue',
|
|
518
510
|
101 => 'Switching Protocols',
|
|
519
511
|
102 => 'Processing',
|
|
512
|
+
103 => 'Early Hints',
|
|
520
513
|
200 => 'OK',
|
|
521
514
|
201 => 'Created',
|
|
522
515
|
202 => 'Accepted',
|
|
@@ -533,6 +526,7 @@ module Rack
|
|
|
533
526
|
303 => 'See Other',
|
|
534
527
|
304 => 'Not Modified',
|
|
535
528
|
305 => 'Use Proxy',
|
|
529
|
+
306 => '(Unused)',
|
|
536
530
|
307 => 'Temporary Redirect',
|
|
537
531
|
308 => 'Permanent Redirect',
|
|
538
532
|
400 => 'Bad Request',
|
|
@@ -557,6 +551,7 @@ module Rack
|
|
|
557
551
|
422 => 'Unprocessable Entity',
|
|
558
552
|
423 => 'Locked',
|
|
559
553
|
424 => 'Failed Dependency',
|
|
554
|
+
425 => 'Too Early',
|
|
560
555
|
426 => 'Upgrade Required',
|
|
561
556
|
428 => 'Precondition Required',
|
|
562
557
|
429 => 'Too Many Requests',
|
|
@@ -571,12 +566,13 @@ module Rack
|
|
|
571
566
|
506 => 'Variant Also Negotiates',
|
|
572
567
|
507 => 'Insufficient Storage',
|
|
573
568
|
508 => 'Loop Detected',
|
|
569
|
+
509 => 'Bandwidth Limit Exceeded',
|
|
574
570
|
510 => 'Not Extended',
|
|
575
571
|
511 => 'Network Authentication Required'
|
|
576
572
|
}
|
|
577
573
|
|
|
578
574
|
# Responses with HTTP status codes that should not have an entity body
|
|
579
|
-
STATUS_WITH_NO_ENTITY_BODY =
|
|
575
|
+
STATUS_WITH_NO_ENTITY_BODY = Hash[((100..199).to_a << 204 << 304).product([true])]
|
|
580
576
|
|
|
581
577
|
SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
|
|
582
578
|
[message.downcase.gsub(/\s|-|'/, '_').to_sym, code]
|
|
@@ -584,12 +580,11 @@ module Rack
|
|
|
584
580
|
|
|
585
581
|
def status_code(status)
|
|
586
582
|
if status.is_a?(Symbol)
|
|
587
|
-
SYMBOL_TO_STATUS_CODE
|
|
583
|
+
SYMBOL_TO_STATUS_CODE.fetch(status) { raise ArgumentError, "Unrecognized status code #{status.inspect}" }
|
|
588
584
|
else
|
|
589
585
|
status.to_i
|
|
590
586
|
end
|
|
591
587
|
end
|
|
592
|
-
module_function :status_code
|
|
593
588
|
|
|
594
589
|
PATH_SEPS = Regexp.union(*[::File::SEPARATOR, ::File::ALT_SEPARATOR].compact)
|
|
595
590
|
|
|
@@ -603,18 +598,16 @@ module Rack
|
|
|
603
598
|
part == '..' ? clean.pop : clean << part
|
|
604
599
|
end
|
|
605
600
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
601
|
+
clean_path = clean.join(::File::SEPARATOR)
|
|
602
|
+
clean_path.prepend("/") if parts.empty? || parts.first.empty?
|
|
603
|
+
clean_path
|
|
609
604
|
end
|
|
610
|
-
module_function :clean_path_info
|
|
611
605
|
|
|
612
|
-
NULL_BYTE = "\0"
|
|
606
|
+
NULL_BYTE = "\0"
|
|
613
607
|
|
|
614
608
|
def valid_path?(path)
|
|
615
609
|
path.valid_encoding? && !path.include?(NULL_BYTE)
|
|
616
610
|
end
|
|
617
|
-
module_function :valid_path?
|
|
618
611
|
|
|
619
612
|
end
|
|
620
613
|
end
|