Merge pull request #40457 from HParker/specify-per-param-encoding

add ability to set per param encoding
This commit is contained in:
Eileen M. Uchitelle 2020-10-27 09:43:55 -04:00 committed by GitHub
commit c9ddceab6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 49 additions and 23 deletions

@ -135,7 +135,7 @@ def self.make_response!(request)
end
end
def self.binary_params_for?(action) # :nodoc:
def self.custom_encoding_for(action, param) # :nodoc:
false
end

@ -12,11 +12,11 @@ def inherited(klass) # :nodoc:
end
def setup_param_encode # :nodoc:
@_parameter_encodings = {}
@_parameter_encodings = Hash.new { |h, k| h[k] = {} }
end
def binary_params_for?(action) # :nodoc:
@_parameter_encodings[action.to_s]
def custom_encoding_for(action, param) # :nodoc:
@_parameter_encodings[action.to_s][param.to_s]
end
# Specify that a given action's parameters should all be encoded as
@ -44,7 +44,35 @@ def binary_params_for?(action) # :nodoc:
# encoded as ASCII-8BIT. This is useful in the case where an application
# must handle data but encoding of the data is unknown, like file system data.
def skip_parameter_encoding(action)
@_parameter_encodings[action.to_s] = true
@_parameter_encodings[action.to_s] = Hash.new { Encoding::ASCII_8BIT }
end
# Specify the encoding for a a parameter on an action
# ASCII-8BIT (it "skips" the encoding default of UTF-8).
#
# For example, a controller would use it like this:
#
# class RepositoryController < ActionController::Base
# param_encoding :show, :file_path, Encoding::ASCII_8BIT
#
# def show
# @repo = Repository.find_by_filesystem_path params[:file_path]
#
# # `repo_name` is guaranteed to be UTF-8, but was ASCII-8BIT, so
# # tag it as such
# @repo_name = params[:repo_name].force_encoding 'UTF-8'
# end
#
# def index
# @repositories = Repository.all
# end
# end
#
# The file_path parameter on the show action would be encoded as ASCII-8BIT.
# This is useful in the case where an application must handle data
# but encoding of the data is unknown, like file system data.
def param_encoding(action, param, encoding)
@_parameter_encodings[action.to_s][param.to_s] = encoding
end
end
end

@ -76,7 +76,7 @@ def commit_cookie_jar! # :nodoc:
PASS_NOT_FOUND = Class.new { # :nodoc:
def self.action(_); self; end
def self.call(_); [404, { "X-Cascade" => "pass" }, []]; end
def self.binary_params_for?(action); false; end
def self.custom_encoding_for(action, param); nil; end
}
def controller_class

@ -42,7 +42,7 @@ def self.check_param_encoding(params)
end
def self.set_binary_encoding(request, params, controller, action)
BinaryParamEncoder.encode(request, params, controller, action)
CustomParamEncoder.encode(request, params, controller, action)
end
class ParamEncoder # :nodoc:
@ -78,23 +78,25 @@ def self.handle_array(params)
end
end
class BinaryParamEncoder # :nodoc:
class CustomParamEncoder # :nodoc:
def self.encode(request, params, controller, action)
return params unless controller && controller.valid_encoding?
if binary_params_for?(request, controller, action)
ActionDispatch::Request::Utils.each_param_value(params.except(:controller, :action)) do |param|
param.force_encoding ::Encoding::ASCII_8BIT
params.except(:controller, :action).each do |key, value|
# next if key == :controller || key == :action
ActionDispatch::Request::Utils.each_param_value(value) do |param|
if desired_encoding = custom_encoding_for(request, controller, action, key)
param.force_encoding(desired_encoding)
end
end
end
params
end
def self.binary_params_for?(request, controller, action)
request.controller_class_for(controller).binary_params_for?(action)
def self.custom_encoding_for(request, controller, action, param)
request.controller_class_for(controller).custom_encoding_for(action, param)
rescue MissingController
false
nil
end
end
end

@ -4,12 +4,10 @@
class MultipartParamsParsingTest < ActionDispatch::IntegrationTest
class TestController < ActionController::Base
skip_parameter_encoding("parse_binary")
class << self
attr_accessor :last_request_parameters, :last_parameters
def binary_params_for?(action)
action == "parse_binary"
end
end
def parse_binary

@ -4559,9 +4559,7 @@ def app; APP end
class TestInvalidUrls < ActionDispatch::IntegrationTest
class FooController < ActionController::Base
def self.binary_params_for?(action)
action == "show"
end
param_encoding :show, :id, Encoding::ASCII_8BIT
def show
render plain: "foo#show"
@ -4595,7 +4593,7 @@ def show
end
end
test "params encoded with binary_params_for? are treated as ASCII 8bit" do
test "params param_encoding uses ASCII 8bit" do
with_routing do |set|
set.draw do
get "/foo/show(/:id)", to: "test_invalid_urls/foo#show"