Session Limiting

Problem this snippet solves:

This iRule limits the amount of sessions. The limits of the Session command in version 9.x forces us to use a global array so we know the count of sessions. There is also a reaper function to ensure that stale sessions don't continue to eat up sessions This iRule is tailored for an application which uses JSessionID cookies or tokens in the URI. If the application does not use a JSessionID, you'll need to remove the URI checking logic and customize the name of the application's session cookie. If the application doesn't use a session cookie, you could modify this iRule to set one in the response if the request doesn't contain one already.

Code :

#timing on 
when RULE_INIT {

#Defines session limit
set ::limit 4

#Defines Debug Level (0=no logging 1=logging)
set ::session_debug 1

#Defines Session Timeout
set ::timeout 300

#Defines the session array
array set ::sessionar ""

#Defines the lastrun global variable used by the reaping process
set ::lastrun 0

#Defines the timeout in seconds that the reaper is to run
set ::sessreap 60

#Defines the redirect webpage
set ::busy_redirect "http://www.example.com/busypage.html"
}

when HTTP_REQUEST {
#if {$::session_debug}{log  local0.  "Got an http request..."}
#Sets the current time in seconds.
set currtime [clock seconds]
#if { $::session_debug }{ log local0. "Session List: [array names ::sessionar]"}
#if { $::session_debug }{ log local0. "Value of lastrun: $::lastrun"}
#if {$::session_debug}{log local0. "Value of currtime: $currtime"}
if { [info exists ::sessionar(loggedoutcontinue)] }{
unset ::sessionar(loggedoutcontinue)
}
#This is the reaping process. This checks upon every connection the amount of time since the last reap
#and if that reap is greater than the value of the $::sessreap global it executes. The reap process will
#remove sessions that have been inactive for 301 seconds or more and leave any sessions 300 or lower. 
set since [expr {$currtime - $::lastrun}]
if {$::session_debug}{log local0. "Seconds since last reap: $since"}
if { $since >= $::sessreap }{
set ::lastrun $currtime
if {$::session_debug}{log local0.  "At least one minute has passed. Reaping Session Array"}
foreach sesskey [array names ::sessionar] {
#if {$::session_debug}{log local0. "SessionID: $sesskey"}
if {$::session_debug}{log local0. "Value of $sesskey: $::sessionar($sesskey)"}
set lastconn $::sessionar($sesskey)
set elapsedtime [expr {$currtime - $lastconn}]
if { $elapsedtime > $::timeout }{
unset ::sessionar($sesskey)
if { $::session_debug }{ log local0. "Session: $sesskey exceeded timeout. Removed from session table."}
}
}
}
#Since the array contains unique sessions the following variable provides for an accurate count
#of the current sessions. The "array size" command gives use the amount of elements within the array
#in the form of an integer
set currsess [array size ::sessionar]
if {$::session_debug}{log  local0. "Current Sessions: $currsess"}
#Here we check that the HTTP URI starts with "/licensemgmt" as this rule only pertains to 
#the license management application
if { [HTTP::uri] starts_with "/licensemgmt" } {
if { $::session_debug }{ log  local0. "URL received: [HTTP::uri]"}
#reap away session on logout
if { [HTTP::uri] contains "/invalidateSession.lic" } {
if {$::session_debug}{log local0. "sessions before reaping: $currsess"}
set sesscookie [URI::query [HTTP::uri] "ID"]
unset ::sessionar($sesscookie)
if { $::session_debug }{ log local0. "session reaped away due to logout: $sesscookie"}
set currsess_new [array size ::sessionar]
if {$::session_debug}{log local0. "sessions after reaping: $currsess_new"}
}
#Check for the existence of the ObSSOCookie and extract the unique value of said cookie
if { [HTTP::cookie exists "JSESSIONID"] }{
if { $::session_debug }{ log local0. "has cookie..."}
set sesscookie [HTTP::cookie "JSESSIONID"]
if { $::session_debug }{ log local0. "Value of JSESSIONID: $sesscookie"}
#Check whether this cookie's value is contained as an element of the array. If it is
#the iRule updates the last accessed time which is the data of each element. This is
#in clock seconds. If it doesn't exist we treat is as a new session. This involves a check
#of whether the threshold has been reach and if so a redirect, otherwise we add the unique
#id to the array with its time in clock seconds
if { [info exists ::sessionar($sesscookie)] } {
if { $::session_debug }{ log local0. "Session Already Exists"}
set ::sessionar($sesscookie) $currtime
return
} else {
if { $currsess >= $::limit }{
#if {$::session_debug}{loglocal0. "Redirected to: [HTTP::header "Host"]$::busy_redirect"}
#HTTP::busy_redirect [HTTP::header "Host"]$::busy_redirect
HTTP::busy_redirect $::busy_redirect
if {$::session_debug}{log local0. "Over Threshold and not an existing session"}
if {$::session_debug}{log local0. "List of Sessions:"}
foreach sesslist [array names ::sessionar] {
if {$::session_debug}{log local0. "[IP::client_addr]: $sesslist"}
}
STATS::incr throttle "Rejected Sessions"
} else {
set ::sessionar($sesscookie) $currtime
STATS::incr throttle "Allowed Sessions"
return
}
}
#If the client didn't have the JSESSIONID than we treat it as a new client and only allow through
#if the threshold has not been met.
} else {
STATS::incr throttle "Total Sessions"
if { $currsess <= $::limit }{
STATS::incr "throttle" "Allowed Sessions"
} else {
if { $::session_debug }{ log local0. "[IP::client_addr] was denied. Over Threshold" }
HTTP::busy_redirect $::busy_redirect
STATS::incr "throttle" "Rejected Sessions"
}
}
}
}

Tested this on version:

9.0
Published Jan 30, 2015
Version 1.0

Was this article helpful?

2 Comments

  • I followed the link from the codeshare that said: HTTP session limit - HTTP Session limiting for LTM v10.1 using tables.

     

    but this is for version 9...

     

    bad link or am i not finding something for version 10? thanks