Skip to content
This repository

correct handling for incomplete hash/array parameters #615

Merged
merged 2 commits into from 7 months ago

4 participants

Victor Bilyk Aaron Patterson Hasnain Bukhari James Tucker
Victor Bilyk

Two problems:
1. incompete parameters with single bracket caused an exception "Expected Hash, ...", while it is pretty much valid parameter name
2. rack.request.query_hash should be only set after query is parsed. Otherwise, in case of thrown exception, client will get exception only first time, next time execution goes inside first if block and nil is being returned. This leads to absolutely weird and non-descriptive exception in params method (No method merge for Nil)

Victor Bilyk

Is there something wrong with this pull request?

Hasnain Bukhari

Why have you added this variable?

There are two ways to prevent @env["rack.request.query_string"] written before actually parsing query. One is to parse it beforehand as I did it, another is to flip @env["rack.request.query_string"]=… and @env["rack.request.query_hash"]=....

I just think that this way it is more implicit that parsing is done before caching query string. But I don't mind to just flip following two lines, if you think it would be better.

As I (maybe very briefly) explained in pull request, this whole situation of writing rack.request.query_string before actually parsing query leads to weird, non-descriptive error messages in case parsing itself ends with throwing some exception. This is especially important when you using rack behind some web-framework. I was using rails when I encountered this bug, and what I have seen - first exception were caught by something inside the rails, but when I tried to access parameters again @env["rack.request.query_string"] == query_string was true, obviously, but @env["rack.request.query_hash"] was nil, so it failed somewhere here, in request.rb trying to merge something into query hash, which was nil.

Aaron Patterson
Collaborator

@vspy can you add a test for the second behavior you desire? I see tests for the first behavior, but I'm afraid if we don't have tests for the second behavior, people will just say "why do we have this extra variable?" and there will be a regression.

Victor Bilyk

done!

James Tucker raggi merged commit 23dfded into from
James Tucker raggi closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
3  lib/rack/request.rb
@@ -192,8 +192,9 @@ def GET
192 192 if @env["rack.request.query_string"] == query_string
193 193 @env["rack.request.query_hash"]
194 194 else
  195 + p = parse_query(query_string)
195 196 @env["rack.request.query_string"] = query_string
196   - @env["rack.request.query_hash"] = parse_query(query_string)
  197 + @env["rack.request.query_hash"] = p
197 198 end
198 199 end
199 200
2  lib/rack/utils.rb
@@ -109,6 +109,8 @@ def normalize_params(params, name, v = nil)
109 109
110 110 if after == ""
111 111 params[k] = v
  112 + elsif after == "["
  113 + params[name] = v
112 114 elsif after == "[]"
113 115 params[k] ||= []
114 116 raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
7 test/spec_request.rb
@@ -1098,6 +1098,13 @@ def params
1098 1098 req2.params.should.equal "foo" => "bar"
1099 1099 end
1100 1100
  1101 + should "raise TypeError every time if request parameters are broken" do
  1102 + broken_query = Rack::MockRequest.env_for("/?foo[]=0&foo[bar]=1")
  1103 + req = Rack::Request.new(broken_query)
  1104 + lambda{req.GET}.should.raise(TypeError)
  1105 + lambda{req.params}.should.raise(TypeError)
  1106 + end
  1107 +
1101 1108 (0x20...0x7E).collect { |a|
1102 1109 b = a.chr
1103 1110 c = CGI.escape(b)
10 test/spec_utils.rb
@@ -157,6 +157,16 @@ def kcodeu
157 157 should.equal "foo" => [""]
158 158 Rack::Utils.parse_nested_query("foo[]=bar").
159 159 should.equal "foo" => ["bar"]
  160 + Rack::Utils.parse_nested_query("foo[]=bar&foo").
  161 + should.equal "foo" => nil
  162 + Rack::Utils.parse_nested_query("foo[]=bar&foo[").
  163 + should.equal "foo" => ["bar"], "foo[" => nil
  164 + Rack::Utils.parse_nested_query("foo[]=bar&foo[=baz").
  165 + should.equal "foo" => ["bar"], "foo[" => "baz"
  166 + Rack::Utils.parse_nested_query("foo[]=bar&foo[]").
  167 + should.equal "foo" => ["bar", nil]
  168 + Rack::Utils.parse_nested_query("foo[]=bar&foo[]=").
  169 + should.equal "foo" => ["bar", ""]
160 170
161 171 Rack::Utils.parse_nested_query("foo[]=1&foo[]=2").
162 172 should.equal "foo" => ["1", "2"]

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.