Forum Discussion
Using switch statements on HTTP::headers
I'm trying to optimize some code that I've pulled from a couple of places and I was wondering if there's a way to use switch statements to check for the existence of headers, since I've read the switch statements are faster than if statements.
I'm mostly interested in combining my insertions into one switch statement (if possible) and my removal statements.
This iRule combines Combine_cache_Control_Headers, Cookie_Encryption and removes some additional headers
that might be useful to an attacker.
when RULE_INIT {
This is for the BigIP cookie encryption
Cookie name prefix
set static::ck_pattern "BIGipServer*"
Cookie encryption passphrase
Change this to a custom string!
set static::ck_pass "REMOVED"
This is for the Cache header combiner
set static::headers { "Cache-Control" "WWW-Authenticate" }
set static::CacheHeaders { "Cache-Control" }
Headers for removal
set static::InfoHeaders { "Server" "X-Powered-By" }
}
when HTTP_REQUEST {
Check if the cookie names in the request match our string glob pattern
if {[set cookie_names [lsearch -all -inline [HTTP::cookie names] $static::ck_pattern]] ne ""}{
We have at least one match so loop through the cookie(s) by name
foreach cookie_name $cookie_names {
Decrypt the cookie value and check if the decryption failed (null return value)
if {[HTTP::cookie decrypt $cookie_name $static::ck_pass] eq ""}{
HTTP::cookie remove $cookie_name
}
}
}
}
when HTTP_RESPONSE {
Remove headers that provide information about the backend
foreach header $static::InfoHeaders {
if {[HTTP::header exists $header]} {
HTTP::header remove $header
}
}
Clickjacking mitigation
if {![HTTP::header exists "Content-Security-Policy"] } {
HTTP::header insert "Content-Security-Policy" "frame-ancestors 'self' example.com *.example.com"
}
MIME sniffing header
if {![HTTP::header exists "X-Content-Type-Options"] } {
HTTP::header insert "X-Content-Type-Options" "nosniff"
}
XSS header
if {![HTTP::header exists "X-XSS-Protection"] } {
HTTP::header insert "X-XSS-Protection" "1; mode=block"
}
Combine Cache headers so the acceleration profiles work correctly
foreach header $static::CacheHeaders {
if {[HTTP::header exists $header]} {
Grab the values as a list, remove the existing headers, and
replace with the values, joined as a comma-delimited string
set from_values [HTTP::header values $header]
set to_values {}
foreach value $from_values {
set value [string trim $value]
if { $value ne ""} { lappend to_values $value }
}
set to_values [join $to_values ", "]
HTTP::header remove $header
HTTP::header insert "lws" $header $to_values
}
}
Check if the cookie names in the request match our string glob pattern
if {[set cookie_names [lsearch -all -inline [HTTP::cookie names] $static::ck_pattern]] ne ""}{
We have at least one match so loop through the cookie(s) by name
foreach cookie_name $cookie_names {
Encrypt the cookie value
HTTP::cookie encrypt $cookie_name $static::ck_pass
}
}
}
Hi Sam,
when comparing just a few conditions (like you do), then [if] outperforms [switch] very much.
The very simplyfied guideline would be to use [if] for less that 10 contions, to use use [switch] for more than 10 but less than 50 condition and to use [class] for more than 50 conditions. But only individual [timing on] tests would provide reliable data.
Your code looks already very clean and straightforward. The only optimizations I could spot are within your [foreach] cookie encryption and [HTTP::header remove] syntax.
[Foreach] Optimization:
foreach cookie_name [lsearch -all -inline [HTTP::cookie names] $static::ck_pattern] { Decrypt the cookie value and check if the decryption failed (null return value) if {[HTTP::cookie decrypt $cookie_name $static::ck_pass] eq ""}{ HTTP::cookie remove $cookie_name } } foreach cookie_name [lsearch -all -inline [HTTP::cookie names] $static::ck_pattern] { Encrypt the cookie value HTTP::cookie encrypt $cookie_name $static::ck_pass }
Background: [foreach] has a build-in ne "" check.
[HTTP::header remove] Optimization:
foreach header $static::InfoHeaders { HTTP::header remove $header }
Background: The required cpu cycles for remove and exists are almost identiacal and remove wouldn't trow an error if the header is not in place.
Additional Note: You could free up some memory by unsetting the variable after usage. I would recommend to migrate your code to $temp(your_var_name) variables and then just issue an unset -nocomplain temp at the of HTTP_RESPONSE to be able to flush every variable in one step.
Cheers, Kai
Recent Discussions
Related Content
* 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