cancel
Showing results for 
Search instead for 
Did you mean: 

URL/URI rewrite without changing in Client browser

NathPras
Nimbostratus
Nimbostratus

Hello All Experts,

Greetings!

 

I have an issue where I need to rewrite URL and as well as URIs from https://abc.com/xyz to https://pqr.com/mnp.

I found two ways to do that one I can use a profile first to match the URL and /path or URIs placed on in a data group list under iRule.

Other way looks like something following what I found in different DevCentral technical forum discussion.

 

if {[string tolower [HTTP::Host]] starts_with "www.abc.com" && [HTTP::path] eq "/"} { HTTP::header replace Host "partner.abc.com" HTTP::uri "/xyz" }

 

Now my problem is there are around 1200 URIs or paths which need to be translated from one One URL+URI to another URL+URI where the catch is URL+URI must not change in user's browser.

What would be correct way to do it? With proflies or with iRule.

In profile I saw that there is an option where I can create a rule where it states is any URL+URI matches it can be send done with HTTP_Header_replace and then I can add one more rule with HTTP_URI_Replace.

Challenge is how to match all those 1200 URL(where URL is constant) + URi(which is changing) from (example  https://abc.com/xyz to https://pqr.com/mnphttps://abc.com/xyz1 to https://pqr.com/mnp1 ...and the list grows)

Also to add one more point https://abc.com is on one F5 and https://pqr.com is on another F5.

Thanks,

Prasenjit

10 REPLIES 10

CA_Valli
Cumulonimbus
Cumulonimbus

In my opinion, with such a large amount of items you should use an iRule to perform this task, as it will be more beneficial for scalability and easier to manage.

How much will the URI paths change? Make sure to use [HTTP::uri] , [HTTP::path], [HTTP::query] parameters properly in order to isolate the parts of your old URL that you want to change/keep. For example you could save the full querystring in a variable so that you can add it back to the rewritten url. 

If you have a large list of paths that all need to be rewritten on a single new path, you can also use data groups for even better scalability and maintenance. 

 

This might give you a couple ideas

when HTTP_REQUEST {
  set uri [HTTP::uri]; set path [HTTP::path]; set query [HTTP::query]
  HTTP::header replace Host "pqr.com"
  if { $path starts_with "/xyz" } {
        set newpath [string map -nocase {"/xyz" "/mnp"} $path]
        HTTP::uri "$newpath$query"
    }
}

 

Hi CA_Valli,

 

How to incoroprate the different paths with the use of datagroup in the iRule provided above. Also when the response would come back doesn't it need to be changed to original URL+URI/path as well?

Thanks,

Prasenjit

I've tried to modify my syntax, haven't had the chance to test it in a lab so please be careful with this and don't run it in prod without testing. 

Code below is supposed to replace anything that matches /aaa/ /bbb/ or /ccc/ at depth1 in your path (this is driven by "starts_with" condition)  with static /mnp/ 

so /aaa/path/to/index.jsp will change into /mnp/path/to/index.jsp/

and i believe /bbb/bbb/file.txt should change to /mnp/bbb/file.txt 

 

Please also integrate knowledge shared by @Kevin_Stewart in his comments which is very detailed and much more reliable than my attempt 🙂 

Lastly, I don't think you will need to change anything in HTTP response. 

 

 

when HTTP_REQUEST {
  set uri [HTTP::uri]; set path [HTTP::path]; set query [HTTP::query]
  HTTP::header replace Host "pqr.com"
  
  if {[class match $path starts_with datagroup_rewriteURL]} {
		
		set dgValue [class match -value $path eq datagroup_rewriteURL] ; #i believe this should return /xyz/ if you've set as a string entry in the data group  
		log local0. "replacing $dgValue with /mnp/"
		
        set newpath [string map -nocase { $dgValue "/mnp/"} $path]
        HTTP::uri "$newpath$query"
    }
}

 

 

ltm object will be something like 

 

ltm data-group internal datagroup_rewriteURL {
    records {
        /aaa/ {}
        /bbb/ {}
        /ccc/ {}
    }
    type string
}

 

 

Kevin_Stewart
F5 Employee
F5 Employee

It would depend on how statuc or variable the host/uri strings are. If somewhat static, you could simply do a class lookup to a datagroup using the host/uri combination as a single string match. Something like this:

when HTTP_REQUEST {
    if { [set match [class match -value -- "[HTTP::host][HTTP::uri]" equals url_datagroup]] ne "" } {
        ... match is true if the match is made
        ... value will be the matched data group value to be used in replacing HTTP:host and HTTP::uri]
    }
}

But curious, if abc and pqr are on separate BIG-IPs, AND you need the user's URL to not change in the browser, then one BIG-IP would have to be behind the other BIG-IP. 

Hi Kevin,

Thank you for your kind response. Here actually the existing applications of Akana is hosted on a F5 and customer wants to move from Akana to Mulesoft. Mulesoft being a cluster has virtual IP which is hosted on a different F5. These are actually API calls which presently being done on current F5 hosting the virtual server of Akana. Now customer wants to migrate these API calls from Akana to mulesoft with a phase wise migration where the primary need is till the migration is completly done URL+URI in user end should not change as it would drop the response.

customer does not have licesne of APM so they are trying to achieve it with LTM.

I was thinking in the following way which may not be effective at all 

When HTTP_REQUEST {
if {[string tolower [HTTP::host]] starts_with "https://abc.com"}{
if {[HTTP::path] eq "/xyz"}{
HTTP::header replace Host "https://pqr.com" HTTP::uri "/mnp"}
}
}

When HTTP_RESPONSE {
if {[string tolower [HTTP::host]] starts_with "https://pqr.com"}{
if {[HTTP::path] eq "/mnp"}{
HTTP::header replace host "https://abc.com" HTTP::uri "/xyz"}
}
}

But If the above works at all I am struggling if there are more URI paths which need to be matched and switched with each other like "xyz1" to replace "mnp1" where the URL https://abc.com and https://pqr.com are static/constant.

From the above solution you have provided, would be kind enough to elaborate how would I be achieve the solution?

Thanks,

Prasenjit

 

 

 

 

Okay, so let's say you have a string datagroup that looks like this:

ltm data-group internal url_datagroup {
    records {
        www.blah.com:/blah {
            data www.test.com:/test
        }
        www.foo.com:/foo {
            data www.bar.com:/bar
        }
    }
    type string
}

I've intentionally added a colon ":" between the host and uri to make it easier to separate them later. So then you're matching on "[HTTP::host]:[HTTP::uri]" in the following iRule. If you have a match, you'll split the corresponding value into host and uri (split on the ":" to make a list object). You'll then, on separate lines, update the HTTP Host header and replace the HTTP URI.

when HTTP_REQUEST {
    if { [set match [class match -value -- "[HTTP::host]:[HTTP::uri]" equals url_datagroup]] ne "" } {
        log local0. "match = $match"
        set matchlist [split $match ":"]
        log local0. "match host = [lindex $matchlist 0]"
        log local0. "match uri = [lindex $matchlist 1]"
        
        HTTP::host [lindex $matchlist 0]
        HTTP::uri [lindex $matchlist 1]
    }
}

You can now just add all of these HOST:URI patterns to a single datagroup. Now as I was mentioning earlier, it also depends on how static or dynamic the URLs are. For example, if the URI is statically "/foo", then the above works fine. But if /foo is just the start of the URI (ex. /foo/blah?this=that...), then the above won't completely address the problem. It'll change the HTTP Host, but then you need to do a string map to replace the portion of the URI while (presumably) keeping the rest. Example:

/foo/blah?this=that

would be changed to:

/bar/blah?this=that

You're doing all of this in the HTTP request. There's no URI, path or URI values in an HTTP response.

Point is, if you need to keep the user's browser on http://abc.com, you need to keep that traffic on the abc.com VIP. You can change the HTTP Host and URI on the abc VIP and forward the traffic to the pqr VIP, but there's no scenario where the abc VIP would change these values if the user was able to go directly to the pqr VIP directly.

 

 

Hi Kevin_Stewart,

 

The data group which you shared can also be created in GUI right? 

String would be www.blah.com:/blah and value for the string would be www.test.com:/test.

 

Yes. This is the tmsh representation of a data group that I created in the UI.

Hi Kevin,

 

Thank your response. If the resource path is wild card (path (/xyz or /abc/12 or any instead of /test)) for say after www.blah.com/blah/anything can that be translated to www.test.com/test/(anything)

How this can be matched in data group creation?

 

Thanks,

Prasenjit

Okay, so small modification. This assumes the path to be replaced is the first path in the URI:

when HTTP_REQUEST {
    if { [set match [class match -value -- "[HTTP::host]:[HTTP::uri]" starts_with url_datagroup]] ne "" } {
        ## Get first URI path in client request
        set FIRSTPATH [getfield [HTTP::path] "/" 2]
        
        ## Split match into host and path
        set matchlist [split $match ":"]
        set MATCHHOST [lindex $matchlist 0]
        set MATCHPATH [lindex $matchlist 1]
        
        ## Replace FIRSTPATH with MATCHPATH (from full HTTP::uri)
        set UPDATEDPATH [string map [list "/$FIRSTPATH" "$MATCHPATH"] [HTTP::uri]]
        log local0. $UPDATEDPATH
        
        HTTP::host $MATCHHOST
        HTTP::uri $UPDATEDPATH
    }
}