SoFunction
Updated on 2025-04-18

Solution to the problem of cookie acquisition exception when a short-term continuous request in SpringBoot

1. Problem description: Cookie parsing failed when request reuse is caused by asynchronous thread operation

In Spring Boot Web Applications, each request carries an HttpServletRequest, which contains key information such as cookies. However, due to Tomcat's multiplexing mechanism of HttpServletRequest, if the cookieParsed tag of a request object is wrongly modified in an asynchronous thread, subsequent requests in a short period of time may not be properly parsed by the cookie.

1. Scene background

In a web application, usually each request has an HttpServletRequest object to hold the context information of the request. For example, HttpServletRequest stores cookie information in the request. To improve performance and reduce memory usage, web containers (such as Tomcat) reuse the HttpServletRequest object. That is, when a request is completed, Tomcat will put the HttpServletRequest object back into the pool for use on the next request.

To avoid repeated parsing of certain information (such as cookies) for each request, the developer may parse and mark the status of the request object in the main thread, for example by setting a cookieParsed flag to indicate that the cookie has been parsed. This process was originally intended to avoid duplicate parsing operations, but if the requested flag bit is modified in the asynchronous thread, it may affect the behavior during request reuse, resulting in problems during the next request reuse.

2. The root of the problem

  1. Asynchronous thread operation request object: When the main thread is parsedHttpServletRequestInCookieAfter the information,TagcookieParsedIt is "resolved", then start an asynchronous thread to execute some long-term tasks, and then the main thread completes execution and performsHttpServletRequestRecycling operation (For example: clear context information,cookieParsedSet to unresolved state). becauseHttpServletRequestis a shared object (shared between the main thread and the asynchronous thread), and the asynchronous thread may modify the state of the request object, for example,cookieParsedSet to "Resolved".

  2. Request multiplexing mechanism: After the current request is completed,HttpServletRequestIt will be recycled and returned to the request pool for reuse of the next request. During multiplexing, Tomcat checks the status of the current requested object. If the previous request objectcookieParsedMarked as "resolved", the next request will be reused when the request object isSkip the parsing steps of cookies, resulting in the next request not being able to obtain cookie information correctly.

  3. Flag bit is not reset: Since after the main thread ends,cookieParsedThe flag bit is set to "Resolved", but the asynchronous thread does not reset the flag bit after the task is completed, resulting in the request object being incorrectly marked as having been resolved cookies when reused. This will directly affect the processing of the next request.Causes to resolve cookiesIt will not be normal until the Request is recycled again and the Request recycling operation is performed again.

2. Detailed analysis of the problem

1. Scene reappears

  1. Main thread acquisitionHttpServletRequestofCookie: When the main thread processes HTTP requests, it first starts fromHttpServletRequestIn-parseCookieInformation and mark its parsing status. Typically, Tomcat will recycle the request object after the request is completed.

  2. Asynchronous thread startup: After the main thread ends, asynchronous tasks will continue to be executed (for example, long-term export tasks). During this process, the asynchronous thread will continue to access the same one.HttpServletRequestObject.

  3. Request reuse: Since Tomcat reuses the request object, when a request is processed, it returns the request object to the pool for the next request to be reused. If the asynchronous thread modifies some status flags of the request (such as markingCookie Has been parsed),The next request may reuse the modified ones HttpServletRequestObject.

  4. Data pollution issues: Since the multiplexed request object has been marked as "Cookies have been resolved”, this state may be reused,Causes the next request to be skippedCookieAnalytical logic, resulting in the acquisitionCookiefornull, thereby affecting the processing of the requested data.

Code example:

public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
    // The main thread starts executing and parsing cookie information    String cookieValue = null;
    Cookie[] cookies = ();
    if (cookies != null) {
        for (Cookie cookie : cookies) {
            if ("UID".equals(())) {
                cookieValue = ();
                break;
            }
        }
    }

    // Start the asynchronous thread after the main thread is completed    AsyncContext asyncContext = (request, response);
    new Thread(() -> {
        try {
            // Simulate delay tasks            (5000);

            // Asynchronous thread tries to read the cookie again, setting the `cookieParsed` in the recycled request to "parsed"            String cookieValueFromAsync = ()[0].getValue();  
            
            ("Cookies in asynchronous threads: " + cookieValueFromAsync);

            ();
        } catch (InterruptedException e) {
            ();
        }
    }).start();

    return "success";
}

question:

  • When an asynchronous thread executes,requestHas been recycled,()ReturnedCookieIt might be aEmpty arrayOrWrong Cookies. At this time, even if there is a valid one in the requestCookie, asynchronous threads still cannot obtain the correct value.

  • Recycled at the same timerequestHas been marked as "Cookies have been resolved”,The request that causes the next request to be reused is skippedCookieAnalytical logic, causing the next request to obtainCookieis empty.

2. Problem analysis

  1. Tomcat request multiplexing mechanism
  • Tomcat will not be destroyed immediately after the request is processed.HttpServletRequestObject, butAfter initializationPut it into the object pool for reuse of the next request. When the request is completed, if the asynchronous thread accesses itHttpServletRequest, the request object of the main thread will continue to be used.

  • If the main thread has processed the request, it has alreadyHttpServletRequestTagged "Cookies have been resolved”, this state may be reused, causing the next request to be skippedCookieAnalysis of  .

  1. Asynchronous thread conflicts with request object status
  • Although the asynchronous thread and the main thread share the sameHttpServletRequestObject, but the asynchronous thread modifies the requested state (e.g.cookieParsedflag), which will affect other threads' ability to access requested data.

  • In this case, the next request uses the already marked "Cookie analysis completed” request object, resulting in parsing failure.

  1. Request context delivery failed
  • In asynchronous threads, due to thread isolation, theHttpServletRequestCannot be automatically passed to asynchronous thread. Even if usedAsyncContextTo delay cleaning requests,HttpServletRequestThe data in it may also not be properly passed to the asynchronous thread.
  1. Request flags and cleaning mechanisms
  • Tomcat uses request flags (such ascookieParsedorrequestCompleted) to track the status of the request and clean up the requested resource after the request processing is completed. When the asynchronous thread and the main thread share the same request object, these flags may be modified unexpectedly, affecting the correctness of the multiplexed request.

  • Once the request enters asynchronous mode, Tomcat marks its status as "processing is completed" and passes()Delay cleaning of the request object. This delayed cleaning mechanism will allow the asynchronous thread to continue to hold the original request object, causing conflicts in request flags and data pollution.

3. How to avoid affecting the next request?

To avoidHttpServletRequestThe state of  is modified and the request context is correctly passed to the asynchronous thread, here are several recommended solutions.

Method 1: Copy cookies in advance on the main thread (recommended)

Avoid asynchronous threads access requests, obtain a copy of the cookie in the main thread and pass it to the asynchronous thread:

Cookie[] cookiesCopy = ((), ().length);

AsyncContext asyncContext = ();
new Thread(() -> {
    try {
        // Access the copy to avoid modifying the original request        String cookieValue = cookiesCopy[0].getValue();
        ("Async thread's cookies:" + cookieValue);
    } finally {
        ();
    }
}).start();

advantage:

  • Getting cookies in the main thread will not affect the internal state of the request.
  • Avoid cookieParsed being set to true in advance.

Method 2: Use HttpServletRequestWrapper to wrap the request (avoid modifying the original request)

If you need to keep request availability, you can use HttpServletRequestWrapper to intercept getCookies() to prevent it from affecting the cookieParsed status of the request:

class SafeRequestWrapper extends HttpServletRequestWrapper {
    private final Cookie[] cookiesCopy;

    public SafeRequestWrapper(HttpServletRequest request) {
        super(request);
        // Copy cookies in advance to avoid affecting the original request         = () != null ?
                ((), ().length) : new Cookie[0];
    }

    @Override
    public Cookie[] getCookies() {
        return cookiesCopy;
    }
}

public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
    HttpServletRequest safeRequest = new SafeRequestWrapper(request);
    AsyncContext asyncContext = ();

    new Thread(() -> {
        try {
            String cookieValue = ()[0].getValue();
            ("Async thread's cookies:" + cookieValue);
        } finally {
            ();
        }
    }).start();

    return "success";
}

advantage:

  • SafeRequestWrapperInterceptgetCookies(),preventcookieParsedStatus changes.
  • Asynchronous threads can still behave like normalrequestGet the sameCookie, but it will not pollute the mainrequest

Method 3: Use ThreadLocal to pass cookies (for complex scenarios)

If an asynchronous thread may be accessed in multiple placesrequest, can be usedThreadLocalPre-cacheCookie

private static final ThreadLocal<Cookie[]> threadLocalCookies = new ThreadLocal<>();

public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
    (()); // Copy cookies    AsyncContext asyncContext = ();

    new Thread(() -> {
        try {
            Cookie[] cookies = ();
            if (cookies != null) {
                String cookieValue = cookies[0].getValue();
                ("Async thread's cookies:" + cookieValue);
            }
        } finally {
            (); // Avoid memory leaks            ();
        }
    }).start();

    return "success";
}

advantage:

  • Avoid asynchronous thread accessrequest, but still availableCookieCopy.
  • avoidcookieParsedThe status is modified and will not pollute subsequent requests.
  • Suitable for situations where asynchronous tasks are complex and may be called across multiple method.

4. Summary

When dealing with asynchronous threads, especially when it comes toHttpServletRequestWhen you are requesting objects, you may encounter request reuse and context delivery issues. Copy in advance in the main thread through reasonable useCookie,useHttpServletRequestWrapperPackagerequest, using ThreadLocal to pass cookies or directly pass parameters, can effectively avoid data contamination and request object reuse issues, thereby ensuring the correctness of requested data in asynchronous tasks.

Core issues

  • Request reuse: Tomcat will reuse the request object, causing the asynchronous thread to access the modified request.

  • Asynchronous thread cannot access the requested data: The requested data cannot be accessed because the request object may have been cleaned or marked as "complete" when executing asynchronous thread.

Solution

plan Applicable scenarios Advantages Possible disadvantages
Copy cookies in advance (recommended) Simple scene Thread safe and good performance Suitable forCookieScenarios with fewer access
HttpServletRequestWrapper Need completerequestFunction Transparent userequest Requires additional packaging
ThreadLocalPassingCookie Complex asynchronous tasks Suitable for cross-threading and cross-methods Need to be manually cleanedThreadLocal

Best Practices:

  • If it's just readingCookie, It is recommended to pass the data after the main thread copyes (Method 1).
  • If asynchronous threads require multiplerequestMethod, it is recommended to useHttpServletRequestWrapper(Method 2).
  • If the asynchronous task is complicated, you can useThreadLocalMaintain a copy (Method 3).

This ensures asynchronous thread accessCookieWithout affectingrequestReuse of  !

The above is the detailed content of the solution to the problem of Cookie acquisition exceptions when a short-term continuous request is made in SpringBoot. For more information about SpringBoot Cookie acquisition exceptions, please follow my other related articles!