Learn how easy it is to sync an existing GitHub or Google Code repo to a SourceForge project! See Demo

Close

#207 generated script mishandles multi-valued request parameters

3.12
open
Philip Aston
TCPProxy (41)
5
2015-01-10
2012-04-05
Anonymous
No

My app has a form post with a multi-valued parameter The generated Clojure script erroneously uses the same value for all instances of the parameter. See for example the references to token_login2 in the script fragment below:

; Worker thread state is stored in a map using a dynamic var.
(def ^:dynamic *tokens*)
(defn set-token [k v] (set! *tokens* (assoc *tokens* k v)))
(defn token [k] (*tokens* k))

[...]

(defpage page4 "POST login (requests 401-408)." (Test. 400 "Page 4") []
  (set-token :token_login2 "Login")
  (set-token :token_login2 "")
  (set-token :token__sourcePage "HQduFOCJZk6Fw7gPHLMv3_9ey7vdLQ8IuRf--7h2p03EenTfDNNtuw==")
  (set-token :token___fp "RbQxcD5ZA1A=")

  ; Expecting 302 'Found'
  (.POST request401 "/t/a/login"
    (nvpairs [
      "__session_id__" (token :token___session_id__)
      "operator.password", "XXXXXXXX"
      "login2" (token :token_login2)
      "targetURL" (token :token_targetURL)
      "operator.username" (token :token_operatorusername)
      "login2" (token :token_login2)
      "_sourcePage" (token :token__sourcePage)
      "__fp" (token :token___fp)])
    (nvpairs [
      "Content-Type", "application/x-www-form-urlencoded"]))

  (.sleep grinder 12)

In the above example, the body of the form post should contain two values for "login2": the string "Login" and the empty string "". Instead, both instances will be the empty string.

Observed using the Grinder 3.9 snapshot, Sun Java 1.6.0_26, and Firefox 11.

Philip Aston remarked to me that the generated Jython scripts may also have the same problem.

Discussion

  • Philip Aston
    Philip Aston
    2012-10-13

    • assigned_to: Philip Aston
    • milestone: --> 3.11
     
  • Philip Aston
    Philip Aston
    2012-10-14

    • Description has changed:

    Diff:

    --- old
    +++ new
    @@ -1,33 +1,33 @@
     My app has a form post with a multi-valued parameter  The generated Clojure script erroneously uses the same value for all instances of the parameter.  See for example the references to token_login2 in the script fragment below:
    
    -; Worker thread state is stored in a map using a dynamic var.
    -(def ^:dynamic *tokens*)
    -(defn set-token [k v] (set! *tokens* (assoc *tokens* k v)))
    -(defn token [k] (*tokens* k))
    +    ; Worker thread state is stored in a map using a dynamic var.
    +    (def ^:dynamic *tokens*)
    +    (defn set-token [k v] (set! *tokens* (assoc *tokens* k v)))
    +    (defn token [k] (*tokens* k))
    
    -[...]
    +    [...]
    
    -(defpage page4 "POST login (requests 401-408)." (Test. 400 "Page 4") []
    -  (set-token :token_login2 "Login")
    -  (set-token :token_login2 "")
    -  (set-token :token__sourcePage "HQduFOCJZk6Fw7gPHLMv3_9ey7vdLQ8IuRf--7h2p03EenTfDNNtuw==")
    -  (set-token :token___fp "RbQxcD5ZA1A=")
    +    (defpage page4 "POST login (requests 401-408)." (Test. 400 "Page 4") []
    +      (set-token :token_login2 "Login")
    +      (set-token :token_login2 "")
    +      (set-token :token__sourcePage "HQduFOCJZk6Fw7gPHLMv3_9ey7vdLQ8IuRf--7h2p03EenTfDNNtuw==")
    +      (set-token :token___fp "RbQxcD5ZA1A=")
    
    -  ; Expecting 302 'Found'
    -  (.POST request401 "/t/a/login"
    -    (nvpairs [
    -      "__session_id__" (token :token___session_id__)
    -      "operator.password", "XXXXXXXX"
    -      "login2" (token :token_login2)
    -      "targetURL" (token :token_targetURL)
    -      "operator.username" (token :token_operatorusername)
    -      "login2" (token :token_login2)
    -      "_sourcePage" (token :token__sourcePage)
    -      "__fp" (token :token___fp)])
    -    (nvpairs [
    -      "Content-Type", "application/x-www-form-urlencoded"]))
    +      ; Expecting 302 'Found'
    +      (.POST request401 "/t/a/login"
    +        (nvpairs [
    +          "__session_id__" (token :token___session_id__)
    +          "operator.password", "XXXXXXXX"
    +          "login2" (token :token_login2)
    +          "targetURL" (token :token_targetURL)
    +          "operator.username" (token :token_operatorusername)
    +          "login2" (token :token_login2)
    +          "_sourcePage" (token :token__sourcePage)
    +          "__fp" (token :token___fp)])
    +        (nvpairs [
    +          "Content-Type", "application/x-www-form-urlencoded"]))
    
    -  (.sleep grinder 12)
    +      (.sleep grinder 12)
    
     In the above example, the body of the form post should contain two values for "login2": the string "Login" and the empty string "".  Instead, both instances will be the empty string.
    
     
  • Philip Aston
    Philip Aston
    2012-10-14

    This is tricky. The form is something like this:

    <form>
     <input name="login2" type="hidden" value="Login" />
     <input name="login2" type="hidden" value="" />
     ...
    </form>
    

    So when parsing the response containing the form, The Grinder has to
    do something special to give the second input a different token ID,
    and also has to use the right values in the subsequent POST.

    The Grinder currently maintains a simple map of the last value seen
    for each particular token.

    To handle this case, we need to extend the token map so that forms are
    handled in a more sophisticated manner. We could proceed as follows.

    1. When looking for hidden inputs, also find with their parent form

    2. For each hidden inputs, hash (form-action, name, count) into the token key, where count is number of hidden inputs before this one in the form.

    3. Add HTTPRecording.getHiddenFormTokens(name, formAction) that
      returns a collection of tokens, if they exist. Use this in
      ConnectionHandlerImplementation.end()

    Adding the form action as key information allows us to distinguish
    between cases such as:

    <form action="/foo">
     <input name="login2" type="hidden" value="Login" />
     ...
    </form>
    
    <form action="/bah">
     <input name="login2" type="hidden" value="Blah" />
     ...
    </form>
    

    Taken together, I think a solution that addresses both of these
    problems is worth implementing.

    • milestone: 3.11 --> 3.12
     
    Last edit: Philip Aston 2012-10-14