actionpack 5.2.8.1 → 6.0.6
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +270 -347
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -3
- data/lib/abstract_controller/base.rb +4 -3
- data/lib/abstract_controller/caching/fragments.rb +6 -22
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +12 -0
- data/lib/abstract_controller/collector.rb +1 -2
- data/lib/abstract_controller/helpers.rb +7 -6
- data/lib/abstract_controller/railties/routes_helpers.rb +1 -1
- data/lib/abstract_controller/translation.rb +4 -4
- data/lib/action_controller/api.rb +2 -1
- data/lib/action_controller/base.rb +2 -7
- data/lib/action_controller/caching.rb +1 -2
- data/lib/action_controller/log_subscriber.rb +8 -5
- data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
- data/lib/action_controller/metal/conditional_get.rb +9 -3
- data/lib/action_controller/metal/content_security_policy.rb +0 -1
- data/lib/action_controller/metal/data_streaming.rb +5 -6
- data/lib/action_controller/metal/default_headers.rb +17 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +1 -1
- data/lib/action_controller/metal/exceptions.rb +23 -2
- data/lib/action_controller/metal/flash.rb +5 -5
- data/lib/action_controller/metal/force_ssl.rb +15 -56
- data/lib/action_controller/metal/head.rb +1 -1
- data/lib/action_controller/metal/helpers.rb +3 -4
- data/lib/action_controller/metal/http_authentication.rb +20 -21
- data/lib/action_controller/metal/implicit_render.rb +4 -14
- data/lib/action_controller/metal/instrumentation.rb +3 -6
- data/lib/action_controller/metal/live.rb +29 -31
- data/lib/action_controller/metal/mime_responds.rb +13 -2
- data/lib/action_controller/metal/params_wrapper.rb +18 -14
- data/lib/action_controller/metal/redirecting.rb +5 -5
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +2 -3
- data/lib/action_controller/metal/request_forgery_protection.rb +25 -48
- data/lib/action_controller/metal/streaming.rb +0 -1
- data/lib/action_controller/metal/strong_parameters.rb +65 -44
- data/lib/action_controller/metal/url_for.rb +1 -1
- data/lib/action_controller/metal.rb +8 -6
- data/lib/action_controller/railties/helpers.rb +1 -1
- data/lib/action_controller/renderer.rb +17 -3
- data/lib/action_controller/template_assertions.rb +1 -1
- data/lib/action_controller/test_case.rb +7 -8
- data/lib/action_controller.rb +5 -1
- data/lib/action_dispatch/http/cache.rb +14 -11
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +28 -17
- data/lib/action_dispatch/http/filter_parameters.rb +8 -7
- data/lib/action_dispatch/http/filter_redirect.rb +1 -2
- data/lib/action_dispatch/http/headers.rb +1 -2
- data/lib/action_dispatch/http/mime_negotiation.rb +13 -6
- data/lib/action_dispatch/http/mime_type.rb +14 -8
- data/lib/action_dispatch/http/parameter_filter.rb +5 -79
- data/lib/action_dispatch/http/parameters.rb +15 -6
- data/lib/action_dispatch/http/request.rb +21 -14
- data/lib/action_dispatch/http/response.rb +40 -21
- data/lib/action_dispatch/http/upload.rb +9 -1
- data/lib/action_dispatch/http/url.rb +81 -82
- data/lib/action_dispatch/journey/formatter.rb +2 -3
- data/lib/action_dispatch/journey/gtg/builder.rb +0 -1
- data/lib/action_dispatch/journey/gtg/transition_table.rb +0 -1
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -2
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -1
- data/lib/action_dispatch/journey/nodes/node.rb +9 -8
- data/lib/action_dispatch/journey/path/pattern.rb +6 -3
- data/lib/action_dispatch/journey/route.rb +5 -4
- data/lib/action_dispatch/journey/router/utils.rb +10 -10
- data/lib/action_dispatch/journey/router.rb +0 -4
- data/lib/action_dispatch/journey/routes.rb +0 -2
- data/lib/action_dispatch/journey/scanner.rb +10 -4
- data/lib/action_dispatch/journey/visitors.rb +1 -4
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -4
- data/lib/action_dispatch/middleware/cookies.rb +62 -78
- data/lib/action_dispatch/middleware/debug_exceptions.rb +45 -61
- data/lib/action_dispatch/middleware/debug_locks.rb +5 -5
- data/lib/action_dispatch/middleware/debug_view.rb +66 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +49 -16
- data/lib/action_dispatch/middleware/flash.rb +1 -1
- data/lib/action_dispatch/middleware/host_authorization.rb +121 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +9 -12
- data/lib/action_dispatch/middleware/request_id.rb +2 -2
- data/lib/action_dispatch/middleware/session/abstract_store.rb +0 -1
- data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -7
- data/lib/action_dispatch/middleware/show_exceptions.rb +1 -2
- data/lib/action_dispatch/middleware/ssl.rb +8 -8
- data/lib/action_dispatch/middleware/stack.rb +38 -2
- data/lib/action_dispatch/middleware/static.rb +6 -7
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +3 -1
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +26 -4
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +7 -4
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +5 -2
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +4 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +3 -0
- data/lib/action_dispatch/railtie.rb +7 -2
- data/lib/action_dispatch/request/session.rb +9 -2
- data/lib/action_dispatch/routing/inspector.rb +97 -50
- data/lib/action_dispatch/routing/mapper.rb +63 -42
- data/lib/action_dispatch/routing/polymorphic_routes.rb +3 -6
- data/lib/action_dispatch/routing/route_set.rb +25 -31
- data/lib/action_dispatch/routing/url_for.rb +2 -2
- data/lib/action_dispatch/routing.rb +21 -20
- data/lib/action_dispatch/system_test_case.rb +44 -6
- data/lib/action_dispatch/system_testing/browser.rb +38 -7
- data/lib/action_dispatch/system_testing/driver.rb +11 -2
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +6 -5
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +7 -6
- data/lib/action_dispatch/testing/assertion_response.rb +0 -1
- data/lib/action_dispatch/testing/assertions/response.rb +2 -3
- data/lib/action_dispatch/testing/assertions/routing.rb +15 -3
- data/lib/action_dispatch/testing/assertions.rb +1 -1
- data/lib/action_dispatch/testing/integration.rb +33 -12
- data/lib/action_dispatch/testing/request_encoder.rb +2 -2
- data/lib/action_dispatch/testing/test_process.rb +2 -2
- data/lib/action_dispatch/testing/test_response.rb +4 -32
- data/lib/action_dispatch.rb +7 -2
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack.rb +1 -1
- metadata +29 -15
- data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
@@ -67,7 +67,7 @@ module ActionDispatch
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def path_for(options)
|
70
|
-
path = options[:script_name].to_s.chomp("/"
|
70
|
+
path = options[:script_name].to_s.chomp("/")
|
71
71
|
path << options[:path] if options.key?(:path)
|
72
72
|
|
73
73
|
add_trailing_slash(path) if options[:trailing_slash]
|
@@ -78,109 +78,108 @@ module ActionDispatch
|
|
78
78
|
end
|
79
79
|
|
80
80
|
private
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
path << "?#{query}" unless query.empty?
|
87
|
-
end
|
88
|
-
|
89
|
-
def add_anchor(path, anchor)
|
90
|
-
if anchor
|
91
|
-
path << "##{Journey::Router::Utils.escape_fragment(anchor.to_param)}"
|
81
|
+
def add_params(path, params)
|
82
|
+
params = { params: params } unless params.is_a?(Hash)
|
83
|
+
params.reject! { |_, v| v.to_param.nil? }
|
84
|
+
query = params.to_query
|
85
|
+
path << "?#{query}" unless query.empty?
|
92
86
|
end
|
93
|
-
end
|
94
87
|
|
95
|
-
|
96
|
-
|
97
|
-
|
88
|
+
def add_anchor(path, anchor)
|
89
|
+
if anchor
|
90
|
+
path << "##{Journey::Router::Utils.escape_fragment(anchor.to_param)}"
|
91
|
+
end
|
92
|
+
end
|
98
93
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
end
|
94
|
+
def extract_domain_from(host, tld_length)
|
95
|
+
host.split(".").last(1 + tld_length).join(".")
|
96
|
+
end
|
103
97
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
elsif !path.include?(".")
|
108
|
-
path.sub!(/[^\/]\z|\A\z/, '\&/')
|
98
|
+
def extract_subdomains_from(host, tld_length)
|
99
|
+
parts = host.split(".")
|
100
|
+
parts[0..-(tld_length + 2)]
|
109
101
|
end
|
110
|
-
end
|
111
102
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
103
|
+
def add_trailing_slash(path)
|
104
|
+
if path.include?("?")
|
105
|
+
path.sub!(/\?/, '/\&')
|
106
|
+
elsif !path.include?(".")
|
107
|
+
path.sub!(/[^\/]\z|\A\z/, '\&/')
|
108
|
+
end
|
117
109
|
end
|
118
110
|
|
119
|
-
|
120
|
-
|
111
|
+
def build_host_url(host, port, protocol, options, path)
|
112
|
+
if match = host.match(HOST_REGEXP)
|
113
|
+
protocol ||= match[1] unless protocol == false
|
114
|
+
host = match[2]
|
115
|
+
port = match[3] unless options.key? :port
|
116
|
+
end
|
121
117
|
|
122
|
-
|
118
|
+
protocol = normalize_protocol protocol
|
119
|
+
host = normalize_host(host, options)
|
123
120
|
|
124
|
-
|
125
|
-
result << "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
|
126
|
-
end
|
121
|
+
result = protocol.dup
|
127
122
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
}
|
123
|
+
if options[:user] && options[:password]
|
124
|
+
result << "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
|
125
|
+
end
|
132
126
|
|
133
|
-
|
134
|
-
|
127
|
+
result << host
|
128
|
+
normalize_port(port, protocol) { |normalized_port|
|
129
|
+
result << ":#{normalized_port}"
|
130
|
+
}
|
135
131
|
|
136
|
-
|
137
|
-
|
138
|
-
end
|
132
|
+
result.concat path
|
133
|
+
end
|
139
134
|
|
140
|
-
|
141
|
-
|
142
|
-
when nil
|
143
|
-
"http://"
|
144
|
-
when false, "//"
|
145
|
-
"//"
|
146
|
-
when PROTOCOL_REGEXP
|
147
|
-
"#{$1}://"
|
148
|
-
else
|
149
|
-
raise ArgumentError, "Invalid :protocol option: #{protocol.inspect}"
|
135
|
+
def named_host?(host)
|
136
|
+
IP_HOST_REGEXP !~ host
|
150
137
|
end
|
151
|
-
end
|
152
138
|
|
153
|
-
|
154
|
-
|
139
|
+
def normalize_protocol(protocol)
|
140
|
+
case protocol
|
141
|
+
when nil
|
142
|
+
"http://"
|
143
|
+
when false, "//"
|
144
|
+
"//"
|
145
|
+
when PROTOCOL_REGEXP
|
146
|
+
"#{$1}://"
|
147
|
+
else
|
148
|
+
raise ArgumentError, "Invalid :protocol option: #{protocol.inspect}"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def normalize_host(_host, options)
|
153
|
+
return _host unless named_host?(_host)
|
155
154
|
|
156
|
-
|
157
|
-
|
158
|
-
|
155
|
+
tld_length = options[:tld_length] || @@tld_length
|
156
|
+
subdomain = options.fetch :subdomain, true
|
157
|
+
domain = options[:domain]
|
159
158
|
|
160
|
-
|
161
|
-
|
162
|
-
|
159
|
+
host = +""
|
160
|
+
if subdomain == true
|
161
|
+
return _host if domain.nil?
|
163
162
|
|
164
|
-
|
165
|
-
|
166
|
-
|
163
|
+
host << extract_subdomains_from(_host, tld_length).join(".")
|
164
|
+
elsif subdomain
|
165
|
+
host << subdomain.to_param
|
166
|
+
end
|
167
|
+
host << "." unless host.empty?
|
168
|
+
host << (domain || extract_domain_from(_host, tld_length))
|
169
|
+
host
|
167
170
|
end
|
168
|
-
host << "." unless host.empty?
|
169
|
-
host << (domain || extract_domain_from(_host, tld_length))
|
170
|
-
host
|
171
|
-
end
|
172
171
|
|
173
|
-
|
174
|
-
|
172
|
+
def normalize_port(port, protocol)
|
173
|
+
return unless port
|
175
174
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
175
|
+
case protocol
|
176
|
+
when "//" then yield port
|
177
|
+
when "https://"
|
178
|
+
yield port unless port.to_i == 443
|
179
|
+
else
|
180
|
+
yield port unless port.to_i == 80
|
181
|
+
end
|
182
182
|
end
|
183
|
-
end
|
184
183
|
end
|
185
184
|
|
186
185
|
def initialize
|
@@ -231,7 +230,7 @@ module ActionDispatch
|
|
231
230
|
# req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
|
232
231
|
# req.host # => "example.com"
|
233
232
|
def host
|
234
|
-
raw_host_with_port.sub(/:\d+$/, ""
|
233
|
+
raw_host_with_port.sub(/:\d+$/, "")
|
235
234
|
end
|
236
235
|
|
237
236
|
# Returns a \host:\port string for this request, such as "example.com" or
|
@@ -50,7 +50,7 @@ module ActionDispatch
|
|
50
50
|
unmatched_keys = (missing_keys || []) & constraints.keys
|
51
51
|
missing_keys = (missing_keys || []) - unmatched_keys
|
52
52
|
|
53
|
-
message = "No route matches #{Hash[constraints.sort_by { |k, v| k.to_s }].inspect}"
|
53
|
+
message = +"No route matches #{Hash[constraints.sort_by { |k, v| k.to_s }].inspect}"
|
54
54
|
message << ", missing required keys: #{missing_keys.sort.inspect}" if missing_keys && !missing_keys.empty?
|
55
55
|
message << ", possible unmatched constraints: #{unmatched_keys.sort.inspect}" if unmatched_keys && !unmatched_keys.empty?
|
56
56
|
|
@@ -62,12 +62,11 @@ module ActionDispatch
|
|
62
62
|
end
|
63
63
|
|
64
64
|
private
|
65
|
-
|
66
65
|
def extract_parameterized_parts(route, options, recall, parameterize = nil)
|
67
66
|
parameterized_parts = recall.merge(options)
|
68
67
|
|
69
68
|
keys_to_keep = route.parts.reverse_each.drop_while { |part|
|
70
|
-
!options.key?(part) || (options[part] || recall[part]).nil?
|
69
|
+
!(options.key?(part) || route.scope_options.key?(part)) || (options[part] || recall[part]).nil?
|
71
70
|
} | route.required_parts
|
72
71
|
|
73
72
|
parameterized_parts.delete_if do |bad_key, _|
|
@@ -32,7 +32,7 @@ module ActionDispatch
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def name
|
35
|
-
left.tr
|
35
|
+
-left.tr("*:", "")
|
36
36
|
end
|
37
37
|
|
38
38
|
def type
|
@@ -65,12 +65,12 @@ module ActionDispatch
|
|
65
65
|
def literal?; false; end
|
66
66
|
end
|
67
67
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
68
|
+
class Slash < Terminal # :nodoc:
|
69
|
+
def type; :SLASH; end
|
70
|
+
end
|
71
|
+
|
72
|
+
class Dot < Terminal # :nodoc:
|
73
|
+
def type; :DOT; end
|
74
74
|
end
|
75
75
|
|
76
76
|
class Symbol < Terminal # :nodoc:
|
@@ -82,13 +82,14 @@ module ActionDispatch
|
|
82
82
|
def initialize(left)
|
83
83
|
super
|
84
84
|
@regexp = DEFAULT_EXP
|
85
|
-
@name = left.tr
|
85
|
+
@name = -left.tr("*:", "")
|
86
86
|
end
|
87
87
|
|
88
88
|
def default_regexp?
|
89
89
|
regexp == DEFAULT_EXP
|
90
90
|
end
|
91
91
|
|
92
|
+
def type; :SYMBOL; end
|
92
93
|
def symbol?; true; end
|
93
94
|
end
|
94
95
|
|
@@ -90,7 +90,7 @@ module ActionDispatch
|
|
90
90
|
return @separator_re unless @matchers.key?(node)
|
91
91
|
|
92
92
|
re = @matchers[node]
|
93
|
-
"(#{re})"
|
93
|
+
"(#{Regexp.union(re)})"
|
94
94
|
end
|
95
95
|
|
96
96
|
def visit_GROUP(node)
|
@@ -137,6 +137,10 @@ module ActionDispatch
|
|
137
137
|
Array.new(length - 1) { |i| self[i + 1] }
|
138
138
|
end
|
139
139
|
|
140
|
+
def named_captures
|
141
|
+
@names.zip(captures).to_h
|
142
|
+
end
|
143
|
+
|
140
144
|
def [](x)
|
141
145
|
idx = @offsets[x - 1] + x
|
142
146
|
@match[idx]
|
@@ -170,7 +174,6 @@ module ActionDispatch
|
|
170
174
|
end
|
171
175
|
|
172
176
|
private
|
173
|
-
|
174
177
|
def regexp_visitor
|
175
178
|
@anchored ? AnchoredRegexp : UnanchoredRegexp
|
176
179
|
end
|
@@ -184,7 +187,7 @@ module ActionDispatch
|
|
184
187
|
node = node.to_sym
|
185
188
|
|
186
189
|
if @requirements.key?(node)
|
187
|
-
re = /#{@requirements[node]}|/
|
190
|
+
re = /#{Regexp.union(@requirements[node])}|/
|
188
191
|
@offsets.push((re.match("").length - 1) + @offsets.last)
|
189
192
|
else
|
190
193
|
@offsets << @offsets.last
|
@@ -4,9 +4,9 @@ module ActionDispatch
|
|
4
4
|
# :stopdoc:
|
5
5
|
module Journey
|
6
6
|
class Route
|
7
|
-
attr_reader :app, :path, :defaults, :name, :precedence
|
7
|
+
attr_reader :app, :path, :defaults, :name, :precedence, :constraints,
|
8
|
+
:internal, :scope_options
|
8
9
|
|
9
|
-
attr_reader :constraints, :internal
|
10
10
|
alias :conditions :constraints
|
11
11
|
|
12
12
|
module VerbMatchers
|
@@ -51,13 +51,13 @@ module ActionDispatch
|
|
51
51
|
|
52
52
|
def self.build(name, app, path, constraints, required_defaults, defaults)
|
53
53
|
request_method_match = verb_matcher(constraints.delete(:request_method))
|
54
|
-
new name, app, path, constraints, required_defaults, defaults, request_method_match, 0
|
54
|
+
new name, app, path, constraints, required_defaults, defaults, request_method_match, 0, {}
|
55
55
|
end
|
56
56
|
|
57
57
|
##
|
58
58
|
# +path+ is a path constraint.
|
59
59
|
# +constraints+ is a hash of constraints to be applied to this route.
|
60
|
-
def initialize(name, app, path, constraints, required_defaults, defaults, request_method_match, precedence, internal = false)
|
60
|
+
def initialize(name, app, path, constraints, required_defaults, defaults, request_method_match, precedence, scope_options, internal = false)
|
61
61
|
@name = name
|
62
62
|
@app = app
|
63
63
|
@path = path
|
@@ -72,6 +72,7 @@ module ActionDispatch
|
|
72
72
|
@decorated_ast = nil
|
73
73
|
@precedence = precedence
|
74
74
|
@path_formatter = @path.build_formatter
|
75
|
+
@scope_options = scope_options
|
75
76
|
@internal = internal
|
76
77
|
end
|
77
78
|
|
@@ -17,11 +17,11 @@ module ActionDispatch
|
|
17
17
|
def self.normalize_path(path)
|
18
18
|
path ||= ""
|
19
19
|
encoding = path.encoding
|
20
|
-
path = "/#{path}"
|
21
|
-
path.squeeze!("/"
|
22
|
-
path.sub!(%r{/+\Z}, ""
|
20
|
+
path = +"/#{path}"
|
21
|
+
path.squeeze!("/")
|
22
|
+
path.sub!(%r{/+\Z}, "")
|
23
23
|
path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
|
24
|
-
path = "/"
|
24
|
+
path = +"/" if path == ""
|
25
25
|
path.force_encoding(encoding)
|
26
26
|
path
|
27
27
|
end
|
@@ -29,16 +29,16 @@ module ActionDispatch
|
|
29
29
|
# URI path and fragment escaping
|
30
30
|
# https://tools.ietf.org/html/rfc3986
|
31
31
|
class UriEncoder # :nodoc:
|
32
|
-
ENCODE = "%%%02X"
|
32
|
+
ENCODE = "%%%02X"
|
33
33
|
US_ASCII = Encoding::US_ASCII
|
34
34
|
UTF_8 = Encoding::UTF_8
|
35
|
-
EMPTY = "".
|
35
|
+
EMPTY = (+"").force_encoding(US_ASCII).freeze
|
36
36
|
DEC2HEX = (0..255).to_a.map { |i| ENCODE % i }.map { |s| s.force_encoding(US_ASCII) }
|
37
37
|
|
38
|
-
ALPHA = "a-zA-Z"
|
39
|
-
DIGIT = "0-9"
|
40
|
-
UNRESERVED = "#{ALPHA}#{DIGIT}\\-\\._~"
|
41
|
-
SUB_DELIMS = "!\\$&'\\(\\)\\*\\+,;="
|
38
|
+
ALPHA = "a-zA-Z"
|
39
|
+
DIGIT = "0-9"
|
40
|
+
UNRESERVED = "#{ALPHA}#{DIGIT}\\-\\._~"
|
41
|
+
SUB_DELIMS = "!\\$&'\\(\\)\\*\\+,;="
|
42
42
|
|
43
43
|
ESCAPED = /%[a-zA-Z0-9]{2}/.freeze
|
44
44
|
|
@@ -15,9 +15,6 @@ require "action_dispatch/journey/path/pattern"
|
|
15
15
|
module ActionDispatch
|
16
16
|
module Journey # :nodoc:
|
17
17
|
class Router # :nodoc:
|
18
|
-
class RoutingError < ::StandardError # :nodoc:
|
19
|
-
end
|
20
|
-
|
21
18
|
attr_accessor :routes
|
22
19
|
|
23
20
|
def initialize(routes)
|
@@ -84,7 +81,6 @@ module ActionDispatch
|
|
84
81
|
end
|
85
82
|
|
86
83
|
private
|
87
|
-
|
88
84
|
def partitioned_routes
|
89
85
|
routes.partition { |r|
|
90
86
|
r.path.anchored && r.ast.grep(Nodes::Symbol).all? { |n| n.default_regexp? }
|
@@ -56,7 +56,6 @@ module ActionDispatch
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def simulator
|
59
|
-
return if ast.nil?
|
60
59
|
@simulator ||= begin
|
61
60
|
gtg = GTG::Builder.new(ast).transition_table
|
62
61
|
GTG::Simulator.new(gtg)
|
@@ -72,7 +71,6 @@ module ActionDispatch
|
|
72
71
|
end
|
73
72
|
|
74
73
|
private
|
75
|
-
|
76
74
|
def clear_cache!
|
77
75
|
@ast = nil
|
78
76
|
@simulator = nil
|
@@ -33,6 +33,12 @@ module ActionDispatch
|
|
33
33
|
end
|
34
34
|
|
35
35
|
private
|
36
|
+
# takes advantage of String @- deduping capabilities in Ruby 2.5 upwards
|
37
|
+
# see: https://bugs.ruby-lang.org/issues/13077
|
38
|
+
def dedup_scan(regex)
|
39
|
+
r = @ss.scan(regex)
|
40
|
+
r ? -r : nil
|
41
|
+
end
|
36
42
|
|
37
43
|
def scan
|
38
44
|
case
|
@@ -47,15 +53,15 @@ module ActionDispatch
|
|
47
53
|
[:OR, "|"]
|
48
54
|
when @ss.skip(/\./)
|
49
55
|
[:DOT, "."]
|
50
|
-
when text =
|
56
|
+
when text = dedup_scan(/:\w+/)
|
51
57
|
[:SYMBOL, text]
|
52
|
-
when text =
|
58
|
+
when text = dedup_scan(/\*\w+/)
|
53
59
|
[:STAR, text]
|
54
60
|
when text = @ss.scan(/(?:[\w%\-~!$&'*+,;=@]|\\[:()])+/)
|
55
61
|
text.tr! "\\", ""
|
56
|
-
[:LITERAL, text]
|
62
|
+
[:LITERAL, -text]
|
57
63
|
# any char
|
58
|
-
when text =
|
64
|
+
when text = dedup_scan(/./)
|
59
65
|
[:LITERAL, text]
|
60
66
|
end
|
61
67
|
end
|
@@ -40,7 +40,7 @@ module ActionDispatch
|
|
40
40
|
@parameters.each do |index|
|
41
41
|
param = parts[index]
|
42
42
|
value = hash[param.name]
|
43
|
-
return ""
|
43
|
+
return "" unless value
|
44
44
|
parts[index] = param.escape value
|
45
45
|
end
|
46
46
|
|
@@ -59,7 +59,6 @@ module ActionDispatch
|
|
59
59
|
end
|
60
60
|
|
61
61
|
private
|
62
|
-
|
63
62
|
def visit(node)
|
64
63
|
send(DISPATCH_CACHE[node.type], node)
|
65
64
|
end
|
@@ -168,7 +167,6 @@ module ActionDispatch
|
|
168
167
|
|
169
168
|
class String < FunctionalVisitor # :nodoc:
|
170
169
|
private
|
171
|
-
|
172
170
|
def binary(node, seed)
|
173
171
|
visit(node.right, visit(node.left, seed))
|
174
172
|
end
|
@@ -214,7 +212,6 @@ module ActionDispatch
|
|
214
212
|
end
|
215
213
|
|
216
214
|
private
|
217
|
-
|
218
215
|
def binary(node, seed)
|
219
216
|
seed.last.concat node.children.map { |c|
|
220
217
|
"#{node.object_id} -> #{c.object_id};"
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "erb"
|
4
|
+
require "uri"
|
5
|
+
require "action_dispatch/http/request"
|
6
|
+
require "active_support/actionable_error"
|
7
|
+
|
8
|
+
module ActionDispatch
|
9
|
+
class ActionableExceptions # :nodoc:
|
10
|
+
cattr_accessor :endpoint, default: "/rails/actions"
|
11
|
+
|
12
|
+
def initialize(app)
|
13
|
+
@app = app
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(env)
|
17
|
+
request = ActionDispatch::Request.new(env)
|
18
|
+
return @app.call(env) unless actionable_request?(request)
|
19
|
+
|
20
|
+
ActiveSupport::ActionableError.dispatch(request.params[:error].to_s.safe_constantize, request.params[:action])
|
21
|
+
|
22
|
+
redirect_to request.params[:location]
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def actionable_request?(request)
|
27
|
+
request.get_header("action_dispatch.show_detailed_exceptions") && request.post? && request.path == endpoint
|
28
|
+
end
|
29
|
+
|
30
|
+
def redirect_to(location)
|
31
|
+
uri = URI.parse location
|
32
|
+
|
33
|
+
if uri.relative? || uri.scheme == "http" || uri.scheme == "https"
|
34
|
+
body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(location)}\">redirected</a>.</body></html>"
|
35
|
+
else
|
36
|
+
return [400, { "Content-Type" => "text/plain" }, ["Invalid redirection URI"]]
|
37
|
+
end
|
38
|
+
|
39
|
+
[302, {
|
40
|
+
"Content-Type" => "text/html; charset=#{Response.default_charset}",
|
41
|
+
"Content-Length" => body.bytesize.to_s,
|
42
|
+
"Location" => location,
|
43
|
+
}, [body]]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|