Forum Discussion
Ricardo_Marinho
Nimbostratus
Jan 28, 2010HTTPRequestThrottle Issues
Hi,
We are using iRule HTTPRequestThrottle from (http://devcentral.f5.com/wiki/default.aspx/iRules/HTTPRequestThrottle.html).
Sometimes I get following errors on ltm log:
TCL error: HTTPRequestThrottle - can't use empty string as operand of "-" while executing "expr {$curr_time - $start_time}"
TCL error: HTTPRequestThrottle - Operation not supported (line 1) invoked from within "HTTP::header insert "X-RequestLimit-Class" "normal""
We have made some adjustments, but nothing really serious only log statements.
Our code is:
when RULE_INIT {
Expiration for tracking IPs not yet throttled (seconds)
set ::standard_expiration_time 360
Expiration for tracking throttled clients (seconds)
set ::throttled_expiration_time 3600
Throttle clients that exceed this number of requests/min
set ::throttle_activate_rate 400
Once throttled, limit clients to this number of requests/min
set ::throttle_limit_rate 40
Limit blacklisted clients to this number of requests/min
set ::blacklist_limit_rate 1
Message to be displayed when requests are dropped
set ::drop_msg "Too many requests, please try again later!"
}
when HTTP_REQUEST {
HTTP::header insert "X-RequestLimit-Class" "normal"
set client_ip [IP::remote_addr]
if {[matchclass [IP::remote_addr] equals $::RequestLimit_Whitelist]} {
Remote IP is in the whitelist, do not throttle.
HTTP::header replace "X-RequestLimit-Class" "whitelisted"
return
}
If we are still here, the client is not whitelisted
Initialize variables
set curr_time [clock seconds]
set blacklisted 0
Define the keys that we use to store the data in the session table
The key throttle_starttime_W.X.Y.Z tracks the start time (in seconds) when we started throttling the requests from the client
set throttle_timekey throttle_starttime_$client_ip
The key throttle_reqcount_W.X.Y.Z tracks the number of restricted requests received since the start throttling tracking time
set throttle_reqkey throttle_reqcount_$client_ip
set throttle_request_count [session lookup uie $throttle_reqkey]
log local0.info "[IP::remote_addr] $throttle_request_count"
if {[matchclass [HTTP::host] equals $::RequestLimit_Blacklist]} {
Remote IP is in the blacklist, note that the client is blacklisted and start tracking their request rate.
set blacklisted 1
if {$throttle_request_count <= 0} {
session add uie $throttle_timekey [expr {$curr_time - 2}] [expr {$::throttled_expiration_time + 2}]
set throttle_request_count $::blacklist_limit_rate
}
}
if {$throttle_request_count > 0} {
If this is greater than 0, we are already throttling this client.
Increment and re-store the throttled request count
incr throttle_request_count
session add uie $throttle_reqkey $throttle_request_count $::throttled_expiration_time
Calculate elapsed time
set throttle_start_time [session lookup uie $throttle_timekey]
session add uie $throttle_timekey $throttle_start_time $::throttled_expiration_time
set elapsed_time [expr {$curr_time - $throttle_start_time}]
if {$elapsed_time < 60} {
set elapsed_time 60
}
Calculate number of requests/minute currently
set curr_rate [expr {$throttle_request_count / ($elapsed_time/60)} ]
Determine if this client is blacklisted or just throttled and set limit
rate, header, stats variable appropriately
if {$blacklisted} {
HTTP::header replace "X-RequestLimit-Class" "blacklisted"
set limit_rate $::blacklist_limit_rate
set statsvar "blacklisted_dropped_requests"
} else {
HTTP::header replace "X-RequestLimit-Class" "throttled"
set limit_rate $::throttle_limit_rate
set statsvar "throttled_dropped_requests"
}
Now see if they are still over their throttled rate. If so, respond with an error.
if {$curr_rate > $limit_rate} {
Throttle this request
HTTP::respond 500 content $::drop_msg
STATS::incr Request_Limit_Stats $statsvar
log local0. " BLACK LIST ******** Site : [HTTP::host] RequestLimit: $client_ip: rate=$curr_rate, limit_rate=$limit_rate, dropping request!"
}
return
}
If we are still here, the client is not whitelisted, blacklisted, or
throttled. Now we need to track them to make sure we don't need to start
thottling. Are they accessing a restricted URL?
log local0.info "Antes do if"
log local0.info [HTTP::host]
if {[matchclass [HTTP::host] contains $::RequestLimit_URLlist]} {
log local0.info "Depois do if"
Define the keys to track the start time and request count for this client
set timekey starttime_$client_ip
set reqkey reqcount_$client_ip
Lookup the current request count
set request_count [session lookup uie $reqkey]
if {$request_count > 0} {
We have seen this client before. Increment and re-store the request count.
incr request_count
session add uie $reqkey $request_count $::standard_expiration_time
HTTP::header insert "X-Request-Count" $request_count
Now calculate the elapsed time
set start_time [session lookup uie $timekey]
session add uie $timekey $start_time $::standard_expiration_time
set elapsed_time [expr {$curr_time - $start_time}]
if {$elapsed_time < 60} {
set elapsed_time 60
}
HTTP::header insert "X-Elapsed-Time" $elapsed_time
Now calculate the current request rate (for the restricted objects only)
set curr_rate [expr $request_count / ($elapsed_time / 60)]
HTTP::header insert "X-Current-Rate" $curr_rate
log local0. "[HTTP::host]([IP::remote_addr]): request count=$request_count, elapsed time=$elapsed_time, curr rate=$curr_rate"
if {$curr_rate >= $::throttle_activate_rate} {
Throttle this client after this request (add them to throttle list)
session add uie $throttle_timekey $start_time $::throttled_expiration_time
session add uie $throttle_reqkey $request_count $::throttled_expiration_time
log local0. "LimitReached **** : Site : [HTTP::host] RequestLimit: !THROTTLED! $client_ip: rate=$curr_rate, request_count=$request_count, elapsed_time=$elapsed_time"
STATS::incr Request_Limit_Stats throttled_clients
}
} else {
This is a new client, start tracking them for potential throttling
session add uie $timekey $curr_time [expr {$::standard_expiration_time + 2}]
session add uie $reqkey 1 $::standard_expiration_time
}
}
}
Our big ip are running version 9.4.6 Build 401.0 Final.
Thanks in advance!
Regards,
- hoolio
Cirrostratus
Hi Ricardo, - Ricardo_Marinho
Nimbostratus
Hi Aaron, - hoolio
Cirrostratus
I think one scenario I've seen the 'operation not supported' error on a header insert is when a request is being redirected in one part of iRule code and another part is trying to do a header insert. Do you have any other iRules enabled on the same VIP? - Ricardo_Marinho
Nimbostratus
Yep. - hoolio
Cirrostratus
You would want to add some kind of logic to either or both iRules to ensure that if you're redirecting the client to the maintenance page that you don't try to insert an HTTP header in the request. One option is to use a local variable to track that you're sent a redirect:Maint page lite rule when HTTP_REQUEST priority 400 { Run this rule event before other HTTP_REQUEST events so we can allow this rule to send a redirect set redirected 0 ... If the pool_testLB is down, redirect to the maintenance page if { [active_members pool_testLB] < 1 } { set redirected 1 HTTP::redirect "http://$host/maintenance" return }
if {$redirected==0} Insert HTTP header or send client a redirect }
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