Forum Discussion

skunk69_85565's avatar
skunk69_85565
Historic F5 Account
Nov 06, 2007

need to insert new info into HTTP query or uri

Hi everyone,

 

 

I need some advice here, please.

 

 

I am currently developing an iRule which will in the end send user requests to different pools based on IP addr, user agents, an existing cookie or on a customized percentage value.

 

 

Ok, I have an idea but struggle with the following:

 

 

This is the part where I look for the cookie, if present I want to insert it into the HTTPquery or as a part of the HTTP::uri.

 

 

elseif { [HTTP::cookie exists "AB_test"] } {

 

set client_id [HTTP::cookie "AB_test"]

 

set old_uri [HTTP::uri]

 

set qstring [HTTP::query]

 

log local0. "Cookie found= $client_id"

 

log local0. "old URI= $old_uri "

 

log local0. "old query string= $qstring "

 

set qstring $client_id

 

log local0. "new query string= $qstring "

 

HTTP::query "$qstring"

 

log local0. "new query= [HTTP::query]"

 

}

 

if { [HTTP::query] starts_with "/A"} {

 

pool poolA

 

return

 

}

 

elseif { [HTTP::query] starts_with "/B"} {

 

pool poolB

 

return

 

}

 

 

Not sure what I am doing wrong here, but the new HTTP:query is empty.....

 

 

Any help is appreciated.

 

 

Patrick

 

 

 

8 Replies

  • I think the value retrieved using HTTP::query and HTTP::uri is cached for performance reasons. So even if you set it to a new value, the command will still return the original value.

     

     

    If you change your rule to check the $client_id or updated $qstring, you should get the desired result:

     

     

    if { $qstring starts_with "/A"} {

     

     

    Aaron
  • skunk69_85565's avatar
    skunk69_85565
    Historic F5 Account
    Hi Aaron,

     

     

    ;-) Yes, thanks that works

     

     

    just another small issue:

     

    Above this elseif routine there is an if-statement, and if both (if and elseif) are not true, there is an else function.

     

     

    Is my iRule correct? as requests without a cookie seams to end here

     

    if { [HTTP::query] starts_with "/A"} {

     

    pool poolA

     

    return

     

     

    and I get this log message:

     

     

    TCL error: test HTTP_REQUEST - cant read qstring: no such variable while executing if { $qstring starts_with A} { pool poolA log local0. send to poolA return } elseif { $qstring starts_with B} { pool poolB l..

     

     

     

    Thanks again,

     

     

    Patrick
  • You're setting qstring to the value of [HTTP::query] in an else block, but I would guess that block isn't always being hit. So you get an error when trying to reference $qstring when it hasn't been set. You could either set it to some default value or check that it has been set before trying to use it. Here's an example of the latter using 'info exists' (Click here😞

    if { [info exists qstring] && $qstring starts_with "/A"} {

    [edit: you might also want to handle the default condition if the qstring isn't set or is set, but doesn't start with /A or /B.]

    
    ...
    if { [info exists qstring] && $qstring starts_with "/A"} {
       pool poolA
    } elseif { [info exists qstring] && $qstring starts_with "/B"} {
       pool poolB
    } else {
       pool default_pool
    }

    Aaron
  • skunk69_85565's avatar
    skunk69_85565
    Historic F5 Account
    Aaron,

     

     

    good point and thanks for the hint.

     

    Actually I have no default pool, just site A or B.

     

     

    I solved the problem just by including my if { $qstring starts_with "/A"} {.... routine into the big elseif routine:

     

     

    ...

     

    elseif { [HTTP::cookie exists "AB_test"] } {

     

    not a bad ip addr and no crawler, look for existing cookie

     

    set client_id [HTTP::cookie "AB_test"]

     

    this is used to direct the traffic

     

    set qstring [HTTP::query]

     

    log local0. "Cookie found= $client_id"

     

    log local0. "old query string= $qstring "

     

    set qstring $client_id

     

    log local0. "new query string= $qstring "

     

    now the load balancing is done

     

    if { $qstring starts_with "A"} {

     

    pool poolA

     

    log local0. "send to poolA"

     

    return

     

    }

     

    elseif { $qstring starts_with "B"} {

     

    pool poolB

     

    log local0. "send to poolB"

     

    return

     

    }

     

    }

     

    else {

     

    .....

     

     

     

    so, now it is working and I am thinking of a good way to get my requests to persist as I do not see a unique value for each (not ip, not the cookie) maybe a hash?

     

     

    Patrick

     

     

  • If you're setting the pool, you will need to get the user back to the same pool in your rule in order for persistence based on the pool members to work. Based on what you've posted so far, I assume the AB_test cookie will be sent in all requests.

    Once you select the correct pool, you have two options. Deb posted some good info on this (Click here).

    - You can configure a cookie insert profile with a default cookie name and not do anything with persistence in your rule.

    - Or you can create a custom cookie insert persistence profile for the two pools. You could then specify a separate persistence profile per pool using the persist command (Click here) inside the block where you set the pool.

    
    if { $qstring starts_with "A"} {
       pool poolA
       persist cookie insert poolA_cookie_persist
       log local0. "send to poolA"
       return
    }
    elseif { $qstring starts_with "B"} {
       pool poolB
       persist cookie insert poolB_cookie_persist
       log local0. "send to poolB"
       return
    }

    Aaron
  • skunk69_85565's avatar
    skunk69_85565
    Historic F5 Account
    Aaron,

     

     

    you pointed me in the right way, and finally I got it working :-). Sure this is not the best way, but it seams to work - not tested with customer right now.

     

     

    At the moment I am not sure how I can fulfill the requirement of setting the query parameter with the value of the cookie (e.g. &AB_test=A-3)? This is needed for analytics. Any idea here?

     

     

    For your info: I have a VS with no default pool, no persistence profile, just this irule:

     

     

    when RULE_INIT {

     

    Value defines how many new requests will end up at siteA "

     

    set ::DefaultPerc 30

     

    Global Test ID which will manually changed "

     

    set ::TestID 7

     

    Reset of sample_id

     

    set ::sample_id 0

     

    }

     

    look for bad ip addr and send straight to poolA

     

    when CLIENT_ACCEPTED {

     

    if { [matchclass [IP::remote_addr] equals $::BlackList] } {

     

    log local0. "Client IP is [IP::remote_addr] and send to site A"

     

    pool poolA

     

    }

     

    log local0. "Client IP is [IP::remote_addr]"

     

    }

     

    when HTTP_REQUEST {

     

    set UA [HTTP::header User-Agent]

     

    log local0. "User Agent is $UA "

     

    look if user agent is a crawler and send straight to poolA

     

    if { [matchclass [string tolower $UA] contains $::SearchEngineCrawler] } {

     

    log local0. "Crawler found: $UA and send to site A"

     

    pool poolA

     

    return

     

    }

     

    elseif { [HTTP::cookie exists "AB_test"] } {

     

    not a bad ip addr and no crawler, look for existing cookie

     

    set client_id [HTTP::cookie "AB_test"]

     

    this is used to direct the traffic

     

    set qstring [HTTP::query]

     

    log local0. "Cookie found= $client_id"

     

    log local0. "old query string= $qstring "

     

    set qstring $client_id

     

    log local0. "new query string= $qstring "

     

    now the load balancing is done

     

    if { $qstring starts_with "A"} {

     

    pool poolA

     

    log local0. "send to poolA"

     

    return

     

    }

     

    elseif { $qstring starts_with "B"} {

     

    pool poolB

     

    log local0. "send to poolB"

     

    return

     

    }

     

    }

     

    else {

     

    no bad ip, no crawler and no cookie set or new request

     

    log local0. "no cookie need to sample!"

     

    set id [expr { int(100 * rand()) }]

     

    log local0. "id ist $id"

     

    this is where the decision is made where to send traffic to

     

    if {$id <= $::DefaultPerc } {

     

    set ::sample_id A-$::TestID

     

    }

     

    else {

     

    set ::sample_id B-$::TestID

     

    }

     

    log local0. "sample id is= $::sample_id "

     

    set qstring [HTTP::query]

     

    log local0. "old query string= $qstring "

     

    set qstring $::sample_id

     

    log local0. "new query string= $qstring "

     

    loadbalance new requests

     

    if { $qstring starts_with "A"} {

     

    pool poolA

     

    log local0. "send to poolA "

     

    return

     

    }

     

    elseif { $qstring starts_with "B"} {

     

    pool poolB

     

    log local0. "send to poolB "

     

    return

     

    }

     

    }

     

    log local0. "END"

     

    }

     

    when HTTP_RESPONSE {

     

    HTTP::cookie insert name AB_test value $::sample_id

     

    }

     

     

     

    Any idea how to get the query parameter set is welcome.

     

     

    Thanks for your help so far.

     

     

    Patrick

     

  • Hi Patrick,

    Sorry, I didn't realize that HTTP::query only allows you to get the query string value, and not set it. HTTP::uri will allow you to get or set the URI, including the query string.

    If you know there will never be a pre-existing query string in the original client request, you could just append the updated URI to the existing URI. However, I expect there would be requests with existing query strings for most apps. To handle that, you could use string map to replace the query string with the new query string within the entire URI. Here is an example:

    
    ...
     original query string is returned by [HTTP::query]
     check if query string is empty
    if {[string length [HTTP::query]] > 0}{
        query string wasn't empty, so we have to preserve what was there
        make a change to the query string
       set updated_qs "[HTTP::query]&someparameter=somevalue"
        update the URI by replacing the old query string with the new
       HTTP::uri [string map "[HTTP::query] $updated_qs" [HTTP::uri]]
    } else {
        query string wasn't set, so we can just append to the existing URI
       HTTP::uri "[HTTP::uri]?someparameter=somevalue"
    }
    ...

    You won't be able to log the change to the URI using HTTP::uri, as the value is cached. But you could log the string map output to see the change:

    
    log local0. "Updated URI: [string map \"[HTTP::query] $updated_qs\" [HTTP::uri]]"

    Aaron
  • skunk69_85565's avatar
    skunk69_85565
    Historic F5 Account
    Aaron,

     

     

    Thank you so much :-)

     

    I will test it tomorrow, onsite!

     

     

    Patrick