Forum Discussion
smp_86112
Cirrostratus
Feb 08, 2008STREAM::replace
I'm trying to write a simple iRule using a STREAM profile on an LTM running 9.1.2. According to the iRules wiki, the STREAM::replace was introduced in 9.0.0. However this simple iRule generates the error:
line 2: [wrong args] [STREAM::replace @string1@string2@]
when HTTP_RESPONSE {
STREAM::replace @string1@string2@
}
I've tried lots of different permutations, like changing the character delimiter, adding quotes, etc... What's going on here?
8 Replies
- hoolio
Cirrostratus
- smp_86112
Cirrostratus
So you're telling me this doc is inaccurate?
http://devcentral.f5.com/wiki/default.aspx/iRules/STREAM__replace.html - hoolio
Cirrostratus
Sorry, I thought you'd gotten the answer in the other thread and was just updating this one to indicate the answer was in that one.
The STREAM::replace command just allows you to change the string which is inserted in place of the matched string in the stream profile. It won't accept multiple tokens. The syntax you're using is accepted in the STREAM::expression command. However, it looks like that command wasn't added until 9.2.
What are you trying to accomplish with the stream profile? Can you configure the find/replace strings in the stream profile attached to the VIP? Else, if you need more dynamic control over the stream expression, can you upgrade to 9.3.x?
Aaron - smp_86112
Cirrostratus
What I'm trying to do is replace all occurrences of the string "about:blank" with a different string in any HTTP_RESPONSE_DATA if { HTTP::header "Content-Type"] contains "application/x-javascript"}. My LTMs are running 9.1.2, and it appeared the only STREAM command available to me was STREAM::replace. I was hoping I could apply a blank stream profile to the VIP, then use the STREAM::replace command to accomplish the replacement. I misunderstood the explanation of the command.
Based on your explanation of the stream profile, plus the requirement of only applying the stream profile under certain conditions, it appears must either upgrade or to try this using HTTP::payload. I think we've tried HTTP::payload in the past with pretty horrible consequences, so I'm a hesitant about using it since I'm not yet confident in my iRules expertise. But since an upgrade is not possible in the time constraint I'm under, then I guess that's my only option. I'm now pouring over the forums to find a good example. - hoolio
Cirrostratus
If you're trying to collect response content and replace multiple instances of one string with another string of a different length, it gets pretty complicated. You need to update the indices for subsequent matches based on the length of the current replacement string.
Fixing the app or upgrading the BIG-IP and using the newer stream commands would be ideal options. If you try the payload option and need to replace multiple instances of the search string, let me know and I'll see if I can find a past rule we used.
Aaron - smp_86112
Cirrostratus
If you're trying to collect response content and replace multiple instances of one string with another string of a different length
That is exactly what I am trying to do. If you drum up an example, I'd love to take a look. - hoolio
Cirrostratus
No warranty on this, but it worked in tests I tried. I tested by using a few different 'find regexes' and replacement strings. I tested to see that variable length matches were replaced correctly. Note that I only tested with a single global replacement string. The rule would need to be tweaked if you want to dynamically set the replacement text per response.
I tried to add comments to help you follow. There is an exorbitant amount of debug logging which will hopefully help. You could remove this altogether once you're done testing.when RULE_INIT { Log debug messages to /var/log/ltm? 1=yes, 0=no. set ::collect_debug 1 A regular expression which describes what strings to replace (wrap the regex in curly braces). set ::find_regex {about:blank} set ::find_regex {[a-zA-Z]+} set ::find_regex {[0-9]+} The string to insert in replacement of the matched string(s) set ::replacement_string "abc" set ::replacement_string "x" } when HTTP_REQUEST { Don't allow response data to be chunked if { [HTTP::version] eq "1.1" } { Check if this is a keep alive connection if { [HTTP::header is_keepalive] } { Replace the Connection header with Keep-Alive HTTP::header replace "Connection" "Keep-Alive" } Set the serverside version to 1.0 HTTP::version "1.0" } } when HTTP_RESPONSE { Only check responses that are a javascript content type if {[HTTP::header "Content-Type"] eq "application/x-javascript"} { if {$::collect_debug}{log local0. "Received javascript response"} Get the content length to collect if { [HTTP::header exists "Content-Length"] } { set content_length [HTTP::header "Content-Length"] } else { set content_length 1000000000 } if { $content_length > 0 } { if {$::collect_debug}{log local0. "Collecting $content_length"} Collect the content HTTP::collect $content_length } } } when HTTP_RESPONSE_DATA { if {$::collect_debug}{log local0. "original payload: [HTTP::payload]"} Find all the match strings in one pass (-indices saves the start and end index of each match to a list) set match_indices [regexp -all -inline -indices $::find_regex [HTTP::payload]] if {$::collect_debug}{log local0. "match_indices = $match_indices"} Initialize a variable to track the offset of the match set match_offset_len 0 Loop through the indices and replace the matching strings with the replacement string foreach match_index $match_indices { Take the first match's start index and adjust it if the payload has already been changed by a previous match set match_start [expr [lindex $match_index 0] - $match_offset_len] if {$::collect_debug}{log local0. "match_start = $match_start"} Take the first match's end index and adjust it if the payload has already been changed by a previous match set match_end [expr [lindex $match_index 1] - $match_offset_len + 1] if {$::collect_debug}{log local0. "match_end = $match_end"} Calculate the length of the match set match_len [expr $match_end - $match_start] if {$::collect_debug}{log local0. "match_len = $match_len"} Calculate the length of the offset based on the past offset plus the difference between the current match and the replacement string set match_offset_len [expr $match_offset_len + $match_len - [string length $::replacement_string]] if {$::collect_debug}{log local0. "match_offset_len = $match_offset_len"} For debug purposes, save and log the string which was matched set match_string [string range [HTTP::payload] $match_start [expr $match_end - 1]] if {$::collect_debug}{log local0. "match string = $match_string"} Replace the match with the replacement string HTTP::payload replace $match_start $match_len $::replacement_string Log the updated payload if {$::collect_debug}{log local0. "updated payload: [HTTP::payload]"} } }
Aaron - smp_86112
Cirrostratus
I'm sorry I never got around to thanking you for this post. Very thorough and nicely written. Worked great!
Recent Discussions
Related Content
DevCentral Quicklinks
* Getting Started on DevCentral
* Community Guidelines
* Community Terms of Use / EULA
* Community Ranking Explained
* Community Resources
* Contact the DevCentral Team
* Update MFA on account.f5.com
Discover DevCentral Connects
