on 20-Dec-2018 13:28
Chase bumped a question on Stack Exchange my way last night on redirects. The question was simple enough: how do you take a request for https://website1.com/user=1234 and redirect to https://website2.com/user=1234? This can (and if this is the sole logic, probably should) be done in a local traffic policy. But this is a post about iRules, so I'll explore that solution below.
At a hight level, here is what needs to happen:
If the host is website1.com and the uri begins with /user= with a user id immediately following, then redirect that request to website2.com and preserve the user id.
Now that I know the what, I can move on to the how. That’s where things get interesting. There is at least two solutions for each component that needs to be evaluated.
In my example here I am simply looking at a string in “website1.com”. Whereas with Tcl using the == operator will work, the interpreter performs a math operation (eq in the first example) instead of a string operation (streq in the second example), as you can see in the byte code below (note this is in tclsh, not in iRules.)
% set x "website1.com" website1.com % tcl::unsupported::disassemble script { if { $x == "host" } {} } ByteCode 0x0x972dc18, refCt 1, epoch 3, interp 0x0x96f78f8 (epoch 3) Source " if { $x == \"host\" } {} " Cmds 1, src 24, inst 15, litObjs 4, aux 0, stkDepth 2, code/src 0.00 Commands 1: 1: pc 0-13, src 1-23 Command 1: "if { $x == \"host\" } {} " (0) push1 0 # "x" (2) loadScalarStk (3) push1 1 # "host" (5) eq (6) jumpFalse1 +6 # pc 12 (8) push1 2 # "" (10) jump1 +4 # pc 14 (12) push1 3 # "" (14) done % tcl::unsupported::disassemble script { if { $x eq "host" } {} } ByteCode 0x0x972dea8, refCt 1, epoch 3, interp 0x0x96f78f8 (epoch 3) Source " if { $x eq \"host\" } {} " Cmds 1, src 24, inst 15, litObjs 4, aux 0, stkDepth 2, code/src 0.00 Commands 1: 1: pc 0-13, src 1-23 Command 1: "if { $x eq \"host\" } {} " (0) push1 0 # "x" (2) loadScalarStk (3) push1 1 # "host" (5) streq (6) jumpFalse1 +6 # pc 12 (8) push1 2 # "" (10) jump1 +4 # pc 14 (12) push1 3 # "" (14) done
Reconstructing these simple tests as iRules commands, I have the following:
when HTTP_REQUEST timing on { # Operation 1: eq OR == # if { [HTTP::host] eq "website1.com" } {} # if { [HTTP::host] == "website1.com" } {} HTTP::respond 200 content "ok" }
Uncommenting each if statement one at a time and running the ab command ab -n 10000 http://192.168.102.131/user=1234
, I show a 19.9% reduction in average CPU cycles by using eq over ==. You mileage may vary by platform, but should still show a reduction.
I can't evaluate the byte code here as starts_with is an iRules feature and is not part of native Tcl. But we can still use the iRules timing functionality. The two command options are:
when HTTP_REQUEST timing on { # Operation 2: starts_with OR string match # if { [HTTP::uri] starts_with "/user=" } {} # if { [string match "/user=*" [HTTP::uri]] } {} HTTP::respond 200 content "ok" }
Using the string match command here results in a reduction of 4.6% in average CPU cycles. That may not be enough of a savings for you to justify the less readable string match command, but if performance is king, there's a slight but sure victor.
I also can’t evaluate the byte code here as and is not a recognized logical operator in native Tcl. First, the commands:
when HTTP_REQUEST timing on { # Operation 3: and OR && # if { 1 and 1 } {} # if { 1 && 1 } {} HTTP::respond 200 content "ok" }
The performance results show that when comparing numbers (the 1/true or 0/false result of the previous two operations), using && over and nets a 10.1% savings in average CPU cycles.
Now that all three individual operations have been optimized, for fun I'll combine the worst options and the best options and look at the combined results. The commands:
when HTTP_REQUEST timing on { # Combined Operations # Sub-optimal choices # if { ([HTTP::host] == "website1.com") and ([HTTP::uri] starts_with "/user=") } {} # Optimal choices # if { ([HTTP::host] eq "website1.com") && ([string match "/user=*" [HTTP::uri]]) } {} HTTP::respond 200 content [HTTP::host] }
With this configuration of optimal choices, I show a reduction in average CPU cycles of 22.7%!
Before closing, I have a couple notes.
What if this gives an error saying server profile should be either fast L4 or HTTP?
Probably splitting hairs here, @Jie, but the documentation from PD on the iRules command specifies the starts_with operator as an equivalency to the string match, if not precisely the same operation.