For more information regarding the security incident at F5, the actions we are taking to address it, and our ongoing efforts to protect our customers, click here.

Redirect Trapper

Problem this snippet solves:

iRule to trap server redirect response, follow the redirect on the BIG-IP and return the followed response to the client as a result of original request

Code :

## REDIRECT TRAPPER - 1.1
## c.jenison@f5.com (Chad Jenison)
## More advanced and more expensive iRule to trap server redirect response, follow the redirect on the BIG-IP and return the followed response to the client as a result of original request
## advantages over simple approach: 
##  + sends original browser headers and cookies along with retried request
##  + handles cookie sets on redirect; sends them on followed request; stores them for setting on the followed response
##  + insertion of base href tag with computed URL (base href must be absolute) of location header - can fix broken embedded objects
##  + attempts to prevent loops - should not recurse - assumption is to follow a single redirect; after that just pass redirect to browser
##  + detects redirects to external sites (where the hostname portion of the Location field doesn't match Host header of request) and leaves them alone

when RULE_INIT {
  #if HTTP for this virtual server
  set static::proto http
  #if HTTPS for this virtual server
  #set static::proto https
  # to perform basehref tag insertion on text/html using STREAM profile set below to 1
  set static::dobasehref 1
}

when HTTP_REQUEST {
  STREAM::disable
  if {![info exists requestisredirectfollow]} {
    set uri [HTTP::uri]
    set httpver [HTTP::version]
    set headers [HTTP::header names]
    array unset request
    array set request {uri $uri}
    foreach header $headers {
      set request($header) [HTTP::header $header]
    }
  } else {
    #this request results from a followed redirect that was done using HTTP::retry
    if {[info exists gotnewcookie]} {
      HTTP::cookie insert $newcookiename $newcookievalue
    }
    unset requestisredirectfollow
    set responseresultsfromretry 1
  }
}
 
when HTTP_RESPONSE {
  if {[HTTP::is_redirect]} {
    if {![info exists responseresultsfromretry]} {
      set location [HTTP::header Location]
      if {[HTTP::header exists "Set-Cookie"]} {
        set gotnewcookie 1 
        set newcookiename [HTTP::cookie names]
        set newcookievalue [HTTP::cookie value $newcookiename]
        set newsetcookieheader [HTTP::header value "Set-Cookie"]
      }
      if {$location starts_with "http:" || $location starts_with "https:"} {
        set locationhost [URI::host $location]
        set locationuri [URI::path $location]
        append locationuri [URI::query $location]
      } else {
        #TBD: special handling for "relative" Location headers
        set locationhost $request(Host)
        set locationuri $location
      }
      if {[string tolower $request(Host)] == [string tolower $locationhost]} {
        set newrequest "GET $locationuri HTTP/$httpver{BR}"
        foreach header $headers {
          if {$header == "Referer"} {
            append newrequest "$header: http://$request(Host)$request(uri){BR}"
          } else {
            append newrequest "$header: $request($header){BR}"
          }
        }
        append newrequest "x-irule-trapped-redirect: true{BR}"
        append newrequest "{BR}"
        set requestisredirectfollow 1
        HTTP::retry $newrequest
      } else {
        #Redirect must be to an external host; not trapping
      }

    } else {
      #this must be a redirect following a redirect; don't follow any further - don't recurse
      #expected behavior is that the 2nd redirect response will pass through to client unaltered
      unset responseresultsfromretry
      if {[info exists gotnewcookie]} {
        HTTP::header insert "Set-Cookie" $newsetcookieheader
        unset gotnewcookie
      }
    }
  } elseif {[info exists responseresultsfromretry]} {
    if {[info exists gotnewcookie]} {
      HTTP::header insert "Set-Cookie" $newsetcookieheader
      unset gotnewcookie
      #must be a normal response following a trapped redirect; need to add cookie to response
    }
    unset responseresultsfromretry
    HTTP::header remove ETag
    HTTP::header insert x-irule-trapped-redirect true
    if {$static::dobasehref} {
      if {[HTTP::header value Content-Type] == "text/html" && ![HTTP::header exists Content-Encoding]} {
        STREAM::expression "@@@"
        STREAM::enable
      }
    }
  }
}
Published Mar 18, 2015
Version 1.0
No CommentsBe the first to comment