Skip to content

Commit 5f2e6bd

Browse files
committed
improved rack-compatibility for our "pure" servlet-env request env parsing impl
... we'll now raise a TypeError just like rack does when it detects invalid parameters
1 parent ff145fe commit 5f2e6bd

File tree

2 files changed

+80
-6
lines changed

2 files changed

+80
-6
lines changed

src/main/ruby/rack/handler/servlet/servlet_env.rb

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ def load_env_key(env, key)
5050
# @private
5151
POST_PARAM_METHODS = [ 'POST', 'PUT', 'DELETE' ].freeze
5252

53+
if defined? Rack::Utils::ParameterTypeError
54+
ParameterTypeError = Rack::Utils::ParameterTypeError
55+
else
56+
ParameterTypeError = TypeError
57+
end
58+
5359
# Load parameters into the (Rack) env from the Servlet API.
5460
# using javax.servlet.http.HttpServletRequest#getParameterMap
5561
def load_parameters
@@ -65,7 +71,7 @@ def load_parameters
6571
@servlet_env.getParameterMap.each do |key, val| # String, String[]
6672
val = [''] if val.nil? # e.g. buggy Jetty 6
6773
val = [''] if val.length == 1 && val[0].nil?
68-
74+
6975
if ( q_vals = query_values(key) ) || get_only
7076
if q_vals.length != val.length
7177
# some are GET params some POST params
@@ -93,6 +99,20 @@ def load_parameters
9399
@env[ FORM_HASH ] = form_hash
94100
end
95101

102+
def [](key)
103+
value = super(key)
104+
if key.eql? QUERY_HASH
105+
if @parameter_error ||= nil
106+
raise @parameter_error
107+
end
108+
end
109+
value
110+
end
111+
public :[]
112+
113+
# @private
114+
KEY_SEP = /([^\[\]]*)(?:\[(.*?)\])?/
115+
96116
# Store the parameter into the given Hash.
97117
# By default this is performed in a Rack compatible way and thus
98118
# some parameter values might get "lost" - it only accepts multiple
@@ -102,12 +122,33 @@ def load_parameters
102122
# @param val the value(s) in a array-like structure
103123
# @param hash the Hash to store the name, value pair
104124
def store_parameter(key, val, hash)
105-
# Rack::Utils.parse_nested_query behaviour
106-
# for 'foo=bad&foo=bar' does { 'foo' => 'bar' }
107-
if key[-2, 2] == '[]' # foo[]=f1&foo[]=f2
108-
hash[ key[0...-2] ] = val.to_a # String[]
125+
# emulating Rack::Utils.parse_nested_query behavior
126+
127+
if match = key.match(KEY_SEP)
128+
n_key = match[1]; sub = match[2]
109129
else
110-
hash[ key ] = val[ val.length - 1 ] # last
130+
n_key = key; sub = nil # normalized-key[ sub-key ]
131+
end
132+
133+
if sub
134+
if sub.empty? # e.g. foo[]=1&foo[]=2
135+
if arr = hash[ n_key ]
136+
return mark_parameter_error "expected Array (got #{arr.class}) for param `#{n_key}'" unless arr.is_a?(Array)
137+
hash[ n_key ] = arr + val.to_a; return
138+
end
139+
hash[ n_key ] = val.to_a # String[]
140+
else # foo[bar]=rrr&foo[baz]=zzz
141+
v = val[ val.length - 1 ] # last
142+
if hsh = hash[ n_key ]
143+
return mark_parameter_error "expected Hash (got #{hsh.class}) for param `#{n_key}'" unless hsh.is_a?(Hash)
144+
hsh[ sub ] = v
145+
else
146+
hash[ n_key ] = { sub => v }
147+
end
148+
end
149+
else
150+
# for 'foo=bad&foo=bar' does { 'foo' => 'bar' }
151+
hash[ n_key ] = val[ val.length - 1 ] # last
111152
end
112153
end
113154

@@ -149,6 +190,12 @@ def parse_query_string
149190
Java::JavaxServletHttp::HttpUtils.parseQueryString(query_string)
150191
end
151192

193+
def mark_parameter_error(msg)
194+
raise ParameterTypeError, msg
195+
rescue ParameterTypeError => e
196+
@parameter_error = e
197+
end
198+
152199
end
153200
end
154201
end

src/spec/ruby/rack/handler/servlet_spec.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,33 @@ def getAttributeNames
368368
expect( env['org.apache.internal'] ).to be true
369369
end
370370

371+
it "raises if nested request parameters are broken (Rack-compat)" do
372+
servlet_request = @servlet_request
373+
servlet_request.setMethod 'GET'
374+
servlet_request.setContextPath '/'
375+
servlet_request.setPathInfo '/path'
376+
servlet_request.setRequestURI '/home/path'
377+
servlet_request.setQueryString 'foo[]=0&foo[bar]=1'
378+
servlet_request.addParameter('foo[]', '0')
379+
servlet_request.addParameter('foo[bar]', '1')
380+
381+
env = servlet.create_env(@servlet_env)
382+
rack_request = Rack::Request.new(env)
383+
384+
# Rack::Utils::ParameterTypeError (< TypeError) since 1.6.0
385+
if Rack::Utils.const_defined? :ParameterTypeError
386+
error = Rack::Utils::ParameterTypeError
387+
else
388+
error = TypeError
389+
end
390+
391+
expect { rack_request.GET }.to raise_error(error, "expected Hash (got Array) for param `foo'")
392+
rack_request.POST.should == {}
393+
expect { rack_request.params }.to raise_error(error, "expected Hash (got Array) for param `foo'") if rack_release('1.6')
394+
395+
rack_request.query_string.should == 'foo[]=0&foo[bar]=1'
396+
end
397+
371398
end
372399

373400
shared_examples "(eager)rack-env" do

0 commit comments

Comments
 (0)