Forum Discussion

Eric_Frankenfie's avatar
Eric_Frankenfie
Icon for Nimbostratus rankNimbostratus
Nov 13, 2017

HTTP Path Modification and Response Rewrite

I have a VIP which is hosting multiple versions of the same application: the current version (/APP_APP1153/APP1153.ashx)is sent to the pool configured on the VS, /G1/APP_APP1153/APP1153.ashx is sent to a G1 pool, and /G2/APP_APP1153/APP1153.ashx is sent to a G2 pool. The issue I am having is the response arriving at the client has /APP_APP1153/APP1153.ashx in the path, so subsequent requests are being send to the pool configured on the VS rather the G1 or G2 pools.

The VS is configured with both a client and server SSL profile (SSL offloading). I am trying to rewrite the HTTP_RESPONSE_DATA, but I have not yet been successful.

The rewrite should be as follows:

  1. Client sends /G1/APP_APP1153/APP1153.ashx, the iRule modifies the path to /APP_APP1153/APP1153.ashx, the server responds with /APP_APP1153/APP1153.ashx, and the iRule replaces /APP_APP1153/APP1153.ashx to /G1/APP_APP1153/APP1153.ashx before sending the data to the client.
  2. Client sends /G2/APP_APP1153/APP1153.ashx, the iRule modifies the path to /APP_APP1153/APP1153.ashx, the server responds with /APP_APP1153/APP1153.ashx, and the iRule replaces /APP_APP1153/APP1153.ashx to /G2/APP_APP1153/APP1153.ashx before sending the data to the client.

Currently, the page is not loading and Firefox is reporting "Secure Connection Failed". I see the GET request in Firefox Developer tools, but I do not see a response. I suspect the issue is in the rewrite, but cannot pinpoint the root cause.

Any help would be appreciated.

This is the iRule I have configured on the VS.

    when HTTP_REQUEST {
    switch [HTTP::path] {
        "/G1/APP_APP1153/APP1153.ashx" {
            HTTP::cookie remove "app1153"
            HTTP::cookie remove "ASP.NET_SessionId"
            pool app-g1_443_pool
            persist cookie insert app1153
            HTTP::path "/TCM_TCM1153/TCM1153.ashx"
            set ::group1 1
        }
        "/G2/TCM_TCM1153/TCM1153.ashx" {
            HTTP::cookie remove "app1153"
            HTTP::cookie remove "ASP.NET_SessionId"
            pool app-g2_443_pool
            persist cookie insert app1153
            HTTP::path "/APP_APP1153/APP1153.ashx"
            set ::group2 1
        }
    }
}

when HTTP_RESPONSE {
if { $::group1 } {  
     collect response data
    if { [HTTP::header exists "Content-Length"] } {
    set content_length [HTTP::header "Content-Length"]
    } else {
    set content_length 4294967295
    }
    if { $content_length > 0 } {
    HTTP::collect $content_length
    }
}
elseif { $::group2 } {  
     collect response data
    if { [HTTP::header exists "Content-Length"] } {
    set content_length [HTTP::header "Content-Length"]
    } else {
    set content_length 4294967295
    }
    if { $content_length > 0 } {
    HTTP::collect $content_length
    }
}
}

when HTTP_RESPONSE_DATA {
if { $::group1 } {
    set find "/APP_APP1153/APP1153.ashx"
    set replace "/G1/APP_APP1153/APP1153.ashx"
    set offset 0
    set diff [expr [string length $replace] - [string length $find]]

     Get indices of all instances of find string in the payload
    set indices [regexp -all -inline -indices $find [HTTP::payload]]  
    foreach idx $indices {
    set start [expr [lindex $idx 0] + $offset]
    set end [expr [lindex $idx 1] + $offset]
    set len [expr {$end - $start + 1}]

     replace the instance of find with the contents of replace
    HTTP::payload replace $start $len $replace

     modify offset if the replace string is larger or smaller
     than find.
    incr offset $diff
    }
}
elseif { $::group2 } {
    set find "/APP_APP1153/APP1153.ashx"
    set replace "/G2/APP_APP1153/APP1153.ashx"
    set offset 0
    set diff [expr [string length $replace] - [string length $find]]

     Get indices of all instances of find string in the payload
    set indices [regexp -all -inline -indices $find [HTTP::payload]]  
    foreach idx $indices {
    set start [expr [lindex $idx 0] + $offset]
    set end [expr [lindex $idx 1] + $offset]
    set len [expr {$end - $start + 1}]

     replace the instance of find with the contents of replace
    HTTP::payload replace $start $len $replace

     modify offset if the replace string is larger or smaller
     than find.
    incr offset $diff
    }
}
}
  • Good evening..

    Seems to me you just want to rewrite URI between client and server. This can be done with [HTTP::uri] no need to collect the HTTP payload and complicated string replacement.

    I've just put this very basic iRule together with should illustrate my thinking. I'm just setting a flag on HTTP request so that we know what to change the URI back to on response. Hopefully it might give you some ideas:

    when HTTP_REQUEST {
      set uri "/APP_APP1153/APP1153.ashx"
      set g1 0
      set g2 0
    
      if {[HTTP::uri] equals "/G1/$uri" } {
        [HTTP::uri] $uri
        set g1 1
      }
    
      if {[HTTP::uri] equals "/G2/$uri" } {
        [HTTP::uri] $uri
        set g2 1
      }  
    }
    
    when HTTP_RESPONSE {
      if {$g1} {
        [HTTP::uri] "/G1/$uri"    
      }
      if {$g2} {
        [HTTP::uri] "/G2/$uri"    
      }  
    }
    
    PS - I've not tested this (making the most of a delayed train! )
    
  • You can try this code (not tested) with a stream profile assigned to the virtual server

     

    when HTTP_REQUEST {
        set uri "/APP_APP1153/APP1153.ashx"
        set g1 0
        set g2 0
         disable compression to support stream profile
        HTTP::header remove "Accept-Encoding"
        if {[HTTP::uri] equals "/G1$uri" } {
            [HTTP::uri] $uri
            set stream_expression "@$uri@/G2$uri@"
            set baseURI /G1
        }
    
        if {[HTTP::uri] equals "/G2$uri" } {
            [HTTP::uri] $uri
            set stream_expression "@$uri@/G2$uri@"
            set baseURI /G2
        }  
    }
    
    when HTTP_RESPONSE {
        if { [HTTP::status] == 302  {
             This is a 302 redirect with a absolute Location URI
            if {[scan [HTTP::header Location] {%[^:]://%[^/]%s} location_proto location_host location_uri] == 3} {
                 The Location header is absolute URI
                HTTP::header replace Location $location_proto://$location_host$baseURI$uri
            } else {
                 The Location header is absolute URI
                HTTP::header replace Location $baseURI[HTTP::header Location]
            }
        } elseif {[HTTP::header value Content-Type] starts_with "text"} {
             Apply stream expression stored in RULE_INIT event
            if {$stream_profile_enabled} {
                STREAM::expression $stream_expression
    
                 Enable the stream filter for this response only
                STREAM::enable
            }
        }
    }