Concurrent Requests and Session State in ASP.NET Core 6.0

Viewed 73

I am keeping track of the number of times a client calls a particular controller action in a single session. The requirement is to limit the total number of requests to the action method to some value (e.g. 10). If the client makes more than 10 request, I reject the request with 403 Forbidden.

The action method (simplified) looks like below:

private readonly static object _lockObj = new();

public IActionResult Subscribe()
{
        lock(_lockObj){ //lockObj is declared in the controller as private static readonly lockObj = new ();    
            int? subscriptionCount = HttpContext.Session.GetInt32("SubscriptionCount");

            if (subscriptionCount == null)
            {
                HttpContext.Session.SetInt32("SubscriptionCount", 1);
            }
            else
            {
                if (subscriptionCount > 10)
                {
                    return StatusCode(StatusCodes.Status403Forbidden, {"Result":"Max subscriptions per user/session reached"});
                }else{
                    subscriptionCount++;
                    HttpContext.Session.SetInt32("SubscriptionCount", subscriptionCount.Value);
                    Console.Writeline($"SubscriptionCount={subscriptionCount}");
                    return Ok({"Result": "Created"});
                }
            }
        }
}

I have used lock to synchronize access in action method but still the count goes haywire when a client makes concurrent requests. The above works as expected if the client makes the requests serially.

Client sending concurrent requests:

function sendParallelRequests(){
    const host = "localhost:5001";
    const options = {
          method: "POST",          
          credentials: "include",          
    };
    const reqs = [];
    for(let i=0;i < 15;i++){
         reqs.push(fetch(`http://${host}/subscription`, options).then((res) => res.json()));
    }
    const allData = Promise.all(reqs);
    allData.then((res) => console.log(res));       
}

When the above client is run, I can see multiple requests having access to the same session value i.e. the Console.Writeline() prints the same value multiple times.

The following client sends requests serially and this works as expected.

async function sendSerialRequests(){
    const host = "localhost:5001";
    const options = {
          method: "POST",          
          credentials: "include",          
    };  
    for(let i=0;i < 15;i++){
        const res = await fetch(`http://${host}/subscription`, options);
        console.log(await res.json());
    }
}

Note: In both the above cases, the session id is same.

0 Answers
Related