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

Was this article helpful?

No CommentsBe the first to comment