Forum Discussion

Gus's avatar
Gus
Icon for Nimbostratus rankNimbostratus
Oct 31, 2023

Persistence on value present in one of 3 places

Hello! 

Looking to force persistence on a single value found in either the URI of a request, in a header (present in a header but is not the header itself) or the payload.


When a request is redirected to this VIP depending on the type of request it has a unique reference in the URI such as the below:

ref=7c974593-b5e1-4885-a1e1-0a33403bb094

If the reference above is not present in the URI of the request its present in a header but the header is not called "ref" it can be a value in the referred header (after the URL) or present in the payload of the request as something slightly different like xref=7c974593-b5e1-4885-a1e1-0a33403bb094

 

Payload data is seperated by & (key) and value is seprated by the = 
Example -  somedata=1234&xref=7c974593-b5e1-4885-a1e1-0a33403bb094&moredata=5678

Below is what I currently have yet its logging the initial request on the URI but nothing on the body (because the below is looking for a specific header) and the BODY part seems to log nothing either just empty. 

proc key2value {l k} {
  foreach elm [split $l &] {
    set kv [split $elm =]
    if { $k eq [string trim [lindex $kv 0]] } {
      return [string trim [lindex $kv 1]]
    }
  }
}

when HTTP_REQUEST {

    if {[string tolower [URI::query [HTTP::uri] "ref"]] != ""}{
        set req_trvalue [string tolower [URI::query [HTTP::uri] "ref"]]
        log local0. "REF URI: $req_trvalue"
        persist uie $req_trvalue 43200
#########This part works as i can see the ref logged on the F5##########
    } elseif { [HTTP::header exists ref] } {
        set req_trvalue [string tolower [HTTP::header ref]]
        log local0. "REF HEADER: $req_trvalue"
        persist uie $req_trvalue 43200

#########The above doesn't work as I can't seem to search for a "ref=" in the headers can only find the option to search for a specific header based on its name####

    } else {

        HTTP::collect [HTTP::header Content-Length]
    }
}

 

when HTTP_REQUEST_DATA {
    set payload [HTTP::payload]
    set req_trvalue [call key2value [lindex $payload 0] \"xref\"]
    log local0. "XREF BODY: [call key2value [lindex $payload 0] \"xref\"]"
    persist uie $req_trvalue 43200
    HTTP::release
}

 

This last part if I just log after the "HTTTP::collect" command I can cleary see the entire payload listed with the xref=value but the actual log file doesn't seem to log it so either im doing something wrong here or the proc at the top is not right.

The goal is just to scan through all 3, if the ref is present in the first it logs it and persists on it, if not does a value exist in the headers , if not the payload. 

4 Replies

  • Don't know if this works for you, but the iRule below uses a regular expression to match the ref. If the format of the ref is unique in the traffic that is passing this iRule, then it should be okay. Otherwise you might need to tweak the regular expression a bit.

    when HTTP_REQUEST {
        if {[string tolower [URI::query [HTTP::uri] "ref"]] != ""}{
            set req_trvalue [string tolower [URI::query [HTTP::uri] "ref"]]
            log local0. "REF URI: $req_trvalue"
            persist uie $req_trvalue 43200
    	} else {
    	    # match strings like 7c974593-b5e1-4885-a1e1-0a33403bb094
    	    set lref [regexp -inline {[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-\S{4}-[a-z0-9]{12}} [HTTP::request]]
    		if { !($lref == "") } {
    		    set req_trvalue [join $lref]
    			log local0. "REF HEADER: $req_trvalue"
    			persist uie $req_trvalue 43200
    		} else {
    		    HTTP::collect [HTTP::header Content-Length]
    		}
    	}
    }
    
    when HTTP_REQUEST_DATA {
        # match strings like 7c974593-b5e1-4885-a1e1-0a33403bb094
        set lref [regexp -inline {[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-\S{4}-[a-z0-9]{12}} [HTTP::payload]]
        if { !($lref == "") } {
    	    set req_trvalue [join $lref]
    		log local0. "XREF BODY: $req_trvalue"
    		persist uie $req_trvalue 43200
        }
        HTTP::release
    }

    Have fun,

         --Niels

  • Gus's avatar
    Gus
    Icon for Nimbostratus rankNimbostratus

    Hello! 

    Thank you for this. This works really well and logs the value for each of the 3 parts. From the screenshot below you can see the results of this:

    Something however wasn't working well and some transactions were failing and this I believe is due to the request not "persisting" and being sent to the other Pool Member. (the setup is testing a round robin accross 2 seprate servers) 

    I then added the LB_SELECTED LB::SERVER to see exactly where this traffic is going to and it seems as though the logging of LB::SERVER isn't really logging each HTTP request. Each transaction / connection attempts generates multiple requests as you can see from the above yet for some requests the server is only logged once. 

     

    I then decided to use BIG IQ and can see that some are being sent to the wrong pool member and I have no idea why. I can see the reference is present in the header or payload and is always there.

     

    Some requests don't even log the Pool Member;

    I'm also unable to get the LB::enable_decisionlog working to see the logic behind choosing said pool member. 

     

     

     

     

     

  • Gus's avatar
    Gus
    Icon for Nimbostratus rankNimbostratus

    Looking into it a little further one that failed as you can see from the below on BIG IQ (bottom is oldest) the last request which is a POST request all others are GETs has a different pool member;

     

    The post request I believe is also the request that has the value in the payload and it seems to be the one request that goes to a different server. I also can't see the reference from the blody logged anywhere at all and I've sent well over 200 requests where the same value above is in the payload yet it doesn't log. The persistence table shows the last destination server as the one where the final POST message is sent to so I assume it changes as it reads the requests 

    Does the last part that scans the payload work like an elseif statement rather than a statement on its own? 
    Would it be worth also looking for the values from the sever response as I believe the value can be in there as well.