Forum Discussion

Ivan_383's avatar
Ivan_383
Icon for Nimbostratus rankNimbostratus
Jun 18, 2021

iRule to insert header ntCoent-Length based in URI

Hello everyone, this time I ask for your help because I am trying to find the way to insert a header named ntCoent-Length which contains the total length in bytes of the content requested. I am balancing the pools based with URIs, which are contained in datagroups, so this insert just should be with certain URIs, contained in this example in the datagroup_C. I designed this iRule but when I tried to log the value it doesn't return nothing, so I am pretty sure it is not inserting the header and the value.

 

when HTTP_REQUEST {

 

if { [class match [HTTP::uri] starts_with "datag_A"] } {

           pool Pool_A

 

       } elseif { [class match [HTTP::uri] starts_with "datag_B"] } {

 

 pool Pool_B

 

 

       } elseif { [class match [HTTP::uri] starts_with "datag_C"] } {

 HTTP::collect

 set urimatch 1

 pool Pool_C

 

 

       } elseif { 

 

 pool Pool_default

 

 

       } 

 

   }

 

when HTTP_RESPONSE_DATA {

  if { [info exists urimatch] } {

    set ntCoent [HTTP::payload length]

    HTTP::release

  }

}

 

when HTTP_RESPONSE {

if { [info exists urimatch] } {

 

HTTP::header insert ntCoent-Length $ntCoent

 

log local0. "HTTP ntCoent-Length header = [HTTP::header value "ntCoent-Length"]"

}

}

 

 

I have tried a lot of iRules, but it seems this is closer, I also tried to do it by a Traffic Policy but I don't know how to trigger the HTTP::collect within the policy, my policy was moreless:

 

Match the following conditions:

 

HTTP URI PATH starts with /example at request

 

Don the following:

 

Insert HTTP Header named= ntCoent-Length with value [HTTP::payload length] at response time

 

 

Log message The ntCoent-Lenght value is [HTTP::header value ntCoent-Length] at response time

 

 

But it doesn't return any value, can you help me please or give me a hint.

 

Thanks a lot!!!!!

  • Hamish's avatar
    Hamish
    Icon for Cirrocumulus rankCirrocumulus

    Do you really want to include the total length in the header? It's just not scalable. Admittedly you could possibly have a situation where all your responses are small enough to do this, but in reality... Probably not

     

    is there a reason you can't simply enable chunking on the client side?

     

    H

  • Hi Hamish, thanks a lot for your answer, the reason why I must include that header is because I am migrating this configuration from a Citrix netscaler, which balances to a legacy application server, I offered to enable the chunking within the http profile, , I am not sure why they need this header within the application, but unfortunatelly I must meet the requierement exactly as they asked. Thanks a lot!!!

  • I spotted several issues in your iRule. First, HTTP_RESPONSE occurs before HTTP_RESPONSE_DATA. (See https://devcentral.f5.com/s/question/0D51T00006i7X94/irule-event-order-http for more info.) That means, the HTTP::header insert for ntCoent needs to occur in the HTTP_RESPONSE_DATA event rather than in the HTTP_RESPONSE event. Second, HTTP_RESPONSE_DATA occurs after an HTTP::collect command. You have one HTTP::collect command but it is in the HTTP_REQUEST event, so it applies to the client-side request payload, not the server-side response payload.

    I tested an alternative scenario below that might work for you or at least get you started, since I don't have the benefit of testing with your application. I decided to log the Content-Length header as sent by the server, too. I could not get the iRule to trigger the HTTP_RESPONSE_DATA event unless I specified a value on the HTTP::collect command:

    when HTTP_REQUEST {
        # Set default pool and no URI match
        set urimatch 0
        pool pool_default
        if { [class match [HTTP::uri] starts_with "datatag_A"] } {
            pool pool_A
        } elseif { [class match [HTTP::uri] starts_with "datatag_B"] } {
            pool pool_B
        } elseif { [class match [HTTP::uri] starts_with "datatag_C"] } {
            pool pool_C
            # This request needs an ntCoent header inserted in the response
            set urimatch 1
        }
    }
     
    when HTTP_RESPONSE {
        if { $urimatch } {
            log local0. "HTTP RESPONSE Content-Length header is [HTTP::header Content-Length]"
            # Adjust as needed. Will not work with no value
            HTTP::collect 100000
        }
    }
     
    when HTTP_RESPONSE_DATA {
        if { $urimatch } {
            log local0. "HTTP RESPONSE DATA payload length is [HTTP::payload length]"
            HTTP::header insert ntCoent [HTTP::payload length]
            HTTP::release
        }
    }

    ...and here is the log output it produced when just 1 of the URI's I requested matched my Data Group C:

    Jun 21 10:48:49 bigip4 info tmm[9908]: Rule /Common/test_devcentral_irule <HTTP_RESPONSE>: HTTP RESPONSE Content-Length header is 22666
    Jun 21 10:48:49 bigip4 info tmm[9908]: Rule /Common/test_devcentral_irule <HTTP_RESPONSE_DATA>: HTTP RESPONSE DATA payload length is 22666

     So, at that point, you might as well just pass the value contained in the Content-Length header. If I used a lower value, such as HTTP::collect 1, the HTTP_RESPONSE_DATA event still only triggered once, and there was a discrepancy between the Content-Length header and the HTTP::payload length, as expected. I tested on BIG-IP v14.1.0.