Forum Discussion

Lukasz_01_15307's avatar
Lukasz_01_15307
Icon for Nimbostratus rankNimbostratus
Sep 13, 2016

irule executes twice when refreshing a website

Hello.. I have a really odd problem, my irule script works exactly every second time. I'm working with APIs and on the backend, they are setup to use apiName.example.com/api/id. For the real world, We want to use api.example.com/api/apiName/id. I know this can be done with tools like TYK, however, at the moment we need a workaround using irules... My iRule:

 

when HTTP_REQUEST {
    log local0. "-------------------------------------------------------------"
    log local0. "Path [HTTP::path]"
    if { not ([HTTP::path] starts_with "/api/") } { return }
    set apiType [lindex [split [HTTP::path] "/"] 2]
    log local0. "api type $apiType"
    append newSubdomain $apiType .uat.
    log local0. "New Subdomain $newSubdomain"
HTTP::header replace "Host" [string map [list "api-uat." $newSubdomain ] [HTTP::host]]
log local0. "-------True"
HTTP::path [string map [list "/api/$apiType" "/api"] [HTTP::path]]
    log local0. "New Path [HTTP::host][HTTP::path]"
    return
}

 

The above code works every second time... in the browser, I'm going to https://api-uat.example.com/api/inventory/demo and it should change into inventory.uat.example.com/api/demo which it does in the first request, but not the second one.. and every time I refresh it goes bad, good, bad, good.... no idea what I'm doing wrong, please help.

 

Path /api/inventory/demo
api type inventory
New Subdomain inventory.uat.
-------True
New Path inventory.uat.example.com/api/demo
-------------------------------------------------------------
Path /api/inventory/demo
api type inventory
New Subdomain inventory.uat.inventory.uat.
-------True
New Path inventory.uat.inventory.uat.example.com/api/demo

 

  • Try unsetting your variables at the end of the iRule? Specifically...

     

    unset $newSubdomain

     

  • Try unsetting your variables at the end of the iRule? Specifically...

     

    unset $newSubdomain

     

    • Lukasz_01_15307's avatar
      Lukasz_01_15307
      Icon for Nimbostratus rankNimbostratus

      Hi ekaleido, this actually made my website unavailable for some reason...

       

      error "connection reset" in chrome...

       

    • ekaleido's avatar
      ekaleido
      Icon for Cirrus rankCirrus

      Using the following modifications? What is being logged?

       

      when HTTP_REQUEST {
        log local0. "-------------------------------------------------------------"
        log local0. "Path [HTTP::path]"
        if { not ([HTTP::path] starts_with "/api/") } { return }
        set apiType [lindex [split [HTTP::path] "/"] 2]
        log local0. "api type $apiType"
        append newSubdomain $apiType .uat.
        log local0. "New Subdomain $newSubdomain"
        HTTP::header replace "Host" [string map [list "api-uat." $newSubdomain ] [HTTP::host]]
        log local0. "-------True"
        HTTP::path [string map [list "/api/$apiType" "/api"] [HTTP::path]]
        log local0. "New Path [HTTP::host][HTTP::path]"
        unset $newSubdomain
      }
      

       

    • Lukasz_01_15307's avatar
      Lukasz_01_15307
      Icon for Nimbostratus rankNimbostratus

      the error was

      warning: [variable reference used where variable name expected][$newSubdomain]

      so I changed

      unset $newSubdomain

      to

      unset newSubdomain

      and it seems to be working.

      Thanks! 🙂

      a question... Why would irule require a variable to be unset? shouldn't all local variable be unset at the end of the script? as far as I know this is a local variable...

  • Hi Lukasz,

    the best approach to fix your iRule be to change the append newSubdomain $apiType .uat. syntax to set newSubdomain "${apiType}.uat.". In this case you don't need to manally issue a unset newSubdomain after each single web request. And no, this is not a bug. Its more a very important feature to have persistent session variables for the duration of an existing TCP connection.

    As a general guideline I always recommend to use as little $variables in iRules as possibile. Try to set $variables only in the case you have to parse something more or less complex - but only if you need the results a second time or if you need those data in other (incompatible) iRule events.

    Below is an example how you could improve the syntax of your code to use as little CPU overhead as possible. Its a combination of well selected commands and a consequent avoidance of unneccessary variables.

     

    when HTTP_REQUEST {
         log local0.debug "-------------------------------------------------------------"
         log local0.debug "Incomming Request for: [HTTP::host][HTTP::path]"
        if { ( [HTTP::path] starts_with "/api/" ) and ( [set apiType [getfield [HTTP::path] "/" 3]] ne "" ) } then {
             log local0.debug "Set Host-Header to: ${apiType}.uat.[domain [HTTP::host] 2]"
            HTTP::header replace "Host" "${apiType}.uat.[domain [HTTP::host] 2]"
             log local0.debug "Set HTTP-Path to: /api[string range [HTTP::path] [string length "/api/$apiType"] end]"
            HTTP::path "/api[string range [HTTP::path] [string length "/api/$apiType"] end]"
        }
         log local0.debug "Outgoing Request to: [HTTP::host][HTTP::path]"
         log local0.debug "-------------------------------------------------------------"
    }
    

     

    Note: The iRule above uses a slightly different syntax which consumes much less CPU ressources (e.g. using [getfield [HTTP::path] "/" 3] instead of [lindex [split [HTTP::path] "/"] 2], using a substituted ${apiType}.uat.[domain [HTTP::host] 2] instead of [string map [list oldHost newHost] [HTTP::host]] and using a substituted /newpath[string range [HTTP::path] [string length /somepath] end] instead of [string map [list /somepath /newpath] [HTTP::path]]).

    Cheers, Kai