Jump to content
Welcome to our new Citrix community!
  • Rate limit HTTP Web and Mobile apps based on session cookies


    Steven Wright
    • Validation Status: Validated
      Summary: Would you like to limit the number of people interacting with backend servers by session count? In this article, we show you how.
      Has Video?: No

    Sometimes it can be helpful to limit the number of people interacting with backend servers. For example, when selling festival tickets or handling purchases on the first day of a product launch, you may want to restrict the number of people talking to your servers so you can quickly complete sales.

    Usually, load balancers track the total number or rate of TCP or HTTP connections. Here, we want to rate limit another metric, the number of concurrent people, as tracked by session cookie.

     

    NetScaler can store variables such as session cookies and reference them with policy logic. In this article, we will demonstrate how to use variables and policy expressions to limit the number of people able to access your store. Thanks to Ibrahim Mubasher at DU Telecom, who helped develop this article.

     

    Note: we will assume you already have a queuing system that presents a friendly screen to waiting users and that all users will arrive with a JSESSIONID cookie.

     

     

    image.png.6ccbe0d840f22f637a57f69307639b99.png

     

     

     

     

    What is an HTTP Session?

    image.png.bb8e7f4c511883e7f96ddb09841cafea.png

     

    An HTTP session is the traffic, context, and state of a conversation between client and server. For example, a web browser opening a website may request many pages, images, and resources over several TCP connections. All these connections and requests would be a part of that session.

     

    Initially, it may seem that we can reduce the load on the NetScalers by rate-limiting TCP connections and backend servers by limiting HTTP requests. However, users will likely make repeated requests for web pages, images, etc., until they've purchased their newly released gadget or festival ticket. To be effective, it's the concurrent sessions that we need to limit.

     

    Online stores typically track users with session cookies. A session cookie is a cookie that expires once the current session ends. Stores usually use these cookies to maintain state, track shopping baskets, and bill for the correct items. Here, we will track sessions using the "JSESSIONID" cookie produced by J2EE web applications, but you could use any session cookie.

     

     

     

    How can we limit the number of HTTP sessions?

    First, we will create a variable map on the NetScaler that can store 10 32-character keys and a ulong value for each. We will set the variable to expire keys that the NetScaler hasn't requested in the last 300 seconds.

     

    add ns variable JID_Session_Map -type "map(text(32),ulong,10)" -ifFull undef -expires 300

     

    image.png.a24cae5d1ae537fec2ba00d9b5627476.png

     

     

    We will use the map variable to store the content of the "JSESSIONID" cookie as a key. We can conceptually visualise that map as a spreadsheet with ten keys and a value for each. The map needs to be large enough to store all sessions the NetScaler keeps track of; in this article, we will allow ten sessions. We don't plan to use the value for each key and will set it to "1".

     

    KeyValue
    JSESSIONID11
    JSESSIONID21
    JSESSIONID31

     

     

    Next, we will define two assignment actions that responder policies can call to add and remove keys from the variable map.

     

    add ns assignment JID_Add_Session -variable "$JID_Session_Map[http.req.cookie.value(\"JSESSIONID\")]" -set 1

     

    add ns assignment JID_Delete_Session -variable "$JID_Session_Map[http.req.cookie.value(\"JSESSIONID\")]" -clear

     

    image.png.3ec9aac7a72fd23041f2e8af8fd6478f.png

     

    image.png.a28a736512ff9c0d3d50fa6d72cdf3f5.png

     

     

    To trigger the assignment action to add a JSESSIONID cookie to our map variable, we will create a responder policy that evaluates true when the user requests a login page and their JSESSIONID cookie does not already exist as a key in the map.

     

    We will add a second responder policy that evaluates true when a logout page is requested. This second policy will trigger the assignment action to remove the cookie from our map.

     

    add responder policy JID_Add_Session_ResPol "http.req.url.contains(\"login\") && $JID_Session_Map.valueExists(http.req.cookie.value(\"JSESSIONID\")).NOT" JID_Add_Session

     

    add responder policy JID_Delete_Session_ResPol "http.req.url.contains(\"logout\")" JID_Delete_Session

     

     

    With the NetScaler now able to add and remove JSESSIONID cookie values from our map when users retrieve login and logout pages, we will create a policy that prevents new sessions from accessing the store when the existing number reaches a defined limit. In this article, we will use a limit of 10 sessions.

     

    We assume the queuing system or the normal flow of the web store will cause users to access the login page initially and to log out after their purchase is complete. That could be achieved with dedicated pages or by changing the responder policy rules to reference existing page the user will retrieve at the start and end of their purchase.

     

    We define a 'new session' as a JSESSIONID cookie value that we haven't already told the NetScaler to store within the memory map. Then, we determine the session limit has been reached by counting the number of JSESSIONID values in the memory map.

     

    The policy that we will create to limit the number of sessions will call an action to display a status message informing your queuing system.

     

    add responder action SendLimitMessage respondwith q{"HTTP/1.1 200 OK\r\n\r\nThe limit of 10 sessions has been reached and the supplied JSESSIONID value \n" + HTTP.REQ.COOKIE.VALUE("JSESSIONID") + " is not on the allowed list."}

     

    add responder policy Check_Existing_Sessions_ResPol " $JID_Session_Map.valueExists(http.req.cookie.value(\"JSESSIONID\")).NOT && $JID_Session_Map.valueCount >= 10 " SendLimitMessage

     

     

    With our policies, actions, and variables created, we can now bind the policies to our load balancing vServer.

     

    As the queuing system passes a user to the Store, the NetScaler will check if the user has an existing session and the maximum session count. If the NetScaler hasn't reached the maximum, it will allow the user access and process requests for the login and log out pages we defined earlier.

     

    bind lb vserver MyOnlineStore -policyName Check_Existing_Sessions_ResPol -priority 10 -gotoPriorityExpression END -type REQUEST

     

    bind lb vserver MyOnlineStore -policyName JID_Add_Session_ResPol -priority 20 -gotoPriorityExpression NEXT -type REQUEST

     

    bind lb vserver MyOnlineStore -policyName JID_Delete_Session_ResPol -priority 30 -gotoPriorityExpression END -type REQUEST

     

     

     

    Results

    We can see that ten users (tracked by JSESSIONID cookie) will now be able to access our web store, but the NetScaler will block other sessions until the existing sessions timeout or explicitly call the logout URL. We have also seen that we can define the session timeout by modifying the expiry time in seconds within the "add ns variable" command.

     

    image.png.7e5f5e919f7bd2f5e80122e94d34d075.png

     

    If we would prefer the NetScaler to redirect additional sessions rather than produce an error message that our queuing system can consume, we can the "add responder action SendLimitMessage" responder policy.

     

    add responder action SendLimitMessage redirect  "\"https://www.google.com\"" -responseStatusCode 302

     

     

    Extending our solution to remove the explicit login page

    Our solution relies on the user calling a specific login and logout URL. You may ask, "How can we remove the login URL?". The answer to that question has a subtlety. 

     

    We could change the expression on the " JID_Add_Session_ResPo l" responder policy, which logs in users by adding their JSESSIONID cookie to the variable map, to "true" and avoid the need for a login page.

     

    However, web browsers often display "favorite icon" representing a website. You will usually see that icon in the top left of the browser window, your website history, or your bookmarks list. The browser gains the icon by asking each website for a "favicon.png" file, usually after requesting a webpage.  

     

    The "favicon.png" file is important as if we change our logic to replace the "login" page with a request for anything, and the browser silently requests the "favicon.png" file immediately after a user visits our logout page, that additional request will cause the user to log in again.

     

    We can avoid that scenario by modifying the expression on the "JID_Add_Session_ResPol" responder policy to ignore requests for the “favicon.png” file.

     

    add responder policy JID_Add_Session_ResPol "http.req.url.contains(\"favicon.ico\").NOT && $JID_Session_Map.valueExists(http.req.cookie.value(\"JSESSIONID\")).NOT" JID_Add_Session

     

     

    Next steps

    We have seen how to limit the number of users tracked by the JSESSIONID cookie, how to provide a response a queuing system can consume, how to redirect the user to an alternative website, and how we could extend the solution to remove the need for a login URL.

     

    The NetScaler could also track other variables if desired. For example, if we replaced the references to "http.req.cookie.value(\"JSESSIONID\")" with "client.ip.src.typecase_text_t", the NetScaler would convert the client's IP address into text and track that value within the variable map.

     

    add responder policy JID_Add_Session_ResPol "http.req.url.contains(\"favicon.ico\").NOT && $JID_Session_Map.valueExists(http.req.cookie.value(\"JSESSIONID\")).NOT" JID_Add_Session

     

    add responder policy JID_Add_Session_ResPol "http.req.url.contains(\"favicon.ico\").NOT && $JID_Session_Map.valueExists(client.ip.src.typecase_text_t).NOT" JID_Add_Session

     

     

    As NetScaler has many expressions that will return text (or values that can be typecase as text using the expression above), you can expand this solution as required. More details about NetScaler policy expressions can be found here: https://developer-docs.citrix.com/projects/citrix-NetScaler-advanced-policy-expression-reference/en/latest/

     

     

     

     


    User Feedback

    Recommended Comments

    There are no comments to display.



    Create an account or sign in to comment

    You need to be a member in order to leave a comment

    Create an account

    Sign up for a new account in our community. It's easy!

    Register a new account

    Sign in

    Already have an account? Sign in here.

    Sign In Now

×
×
  • Create New...