1. Problem description: Cookie parsing failed when request reuse is caused by asynchronous thread operation
1. Scene background
In a web application, there is usually one for each requestHttpServletRequest
Object to save the context information of the request. For example,HttpServletRequest
Stored the requestedCookie
information. To improve performance and reduce memory usage, web containers (such as Tomcat) will workHttpServletRequest
Objects are reused. That is, when a request is completed, Tomcat willHttpServletRequest
The object is put back into the pool for use on the next request.
To avoid repeated parsing of certain information every time the request (e.g.Cookie
), the developer may parse and mark the state of the requested object in the main thread, for example by setting acookieParsed
The flag indicatesCookie
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
-
Asynchronous thread operation request object: When the main thread is parsed
HttpServletRequest
In-houseCookie
After the information,markcookieParsed
for "parsed", then start an asynchronous thread to execute some long-term tasks, and then the main thread completes execution and performs a Request recycling operation (For example: clear context information,cookieParsed
Set to unresolved state). becauseHttpServletRequest
is a shared object (shared between the main thread and the asynchronous thread), and the asynchronous thread may modify the state of the requested object, for examplecookieParsed
Set to "Resolved". -
Request multiplexing mechanism: After the current request is completed,
HttpServletRequest
It 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 objectcookieParsed
Marked 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. -
Flag bit is not reset: Since after the main thread ends,
cookieParsed
The flag is set to "Resolved", but the asynchronous thread does not reset the flag 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 cookies,It 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
- Main thread acquisition
HttpServletRequest
ofCookie
: When the main thread processes HTTP requests, it first starts fromHttpServletRequest
In the parsingCookie
and mark its parsing status. Typically, Tomcat will recycle the request object after the request is completed. -
Asynchronous thread startup: After the main thread ends, asynchronous tasks (for example, long-term export tasks) will continue to be executed. During this process, the asynchronous thread will continue to access the same
HttpServletRequest
Object. -
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 an asynchronous thread modifies some status flags of the request (e.g. markup).
Cookie
has been parsed), the next request may reuse the modified onesHttpServletRequest
Object. -
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 skip
Cookie
Analytical logic, resulting in the acquisition ofCookie
fornull
, 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,
request
Has been recycled,()
ReturnedCookie
It might be oneEmpty 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 time
request
Has been marked as "Cookies have been resolved”,The request that causes the next request to be reused is skippedCookie
Analytical logic, causing the next request to obtainCookie
is empty.
2. Problem analysis
Tomcat request multiplexing mechanism:
- Tomcat will not be destroyed immediately after the request is processed
HttpServletRequest
Object, instead put it in the object pool for reuse of the next request. When the request is completed, if the asynchronous thread has accessed itHttpServletRequest
, the request object of the main thread will continue to be used. - If the main thread has processed the request, it has already
HttpServletRequest
Tagged "Cookies have been resolved”, this state may be reused, causing the next request to skipCookie
Analysis of .
Asynchronous thread conflicts with request object status:
- Although the asynchronous thread and the main thread share the same
HttpServletRequest
Object, but the asynchronous thread modifies the requested state (e.g.cookieParsed
flags), 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.
Request context delivery failed:
- In asynchronous threads, due to thread isolation, the
HttpServletRequest
Cannot be automatically passed to asynchronous thread. Even if usedAsyncContext
to delay cleaning requests,HttpServletRequest
The data in it may also not be properly passed to the asynchronous thread.
Request flags and cleaning mechanisms:
- Tomcat uses the request flag (e.g.
cookieParsed
orrequestCompleted
) 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. Solution
To avoidHttpServletRequest
The state of the ,is modified and correctly passes the request context to the asynchronous thread, are the recommended solutions.
useHttpServletRequestWrapper
Create a request copy
Create a copy of the request in an asynchronous thread to avoid directly manipulating the original request object, thereby solving the request reuse problem.
public String handleRequest(HttpServletRequest request, HttpServletResponse response) { // Create a request copy HttpServletRequest requestCopy = new HttpServletRequestWrapper(request) { @Override public Cookie[] getCookies() { Cookie[] cookies = (); // parse cookies or create copy return cookies; } }; AsyncContext asyncContext = (request, response); new Thread(() -> { try { // Use replicas in asynchronous threads String cookieValueFromAsync = ()[0].getValue(); ("Cookies in asynchronous threads: " + cookieValueFromAsync); (); } catch (InterruptedException e) { (); } }).start(); return "success"; }
advantage:passHttpServletRequestWrapper
The created replica ensures that the asynchronous thread does not directly modify the original request object, thus avoiding data contamination during request reuse.
Manually pass request context
passRequestContextHolder
Manually pass the request context to the asynchronous thread, ensuring that the asynchronous thread can access the request data of the main thread.
public String handleRequest(HttpServletRequest request, HttpServletResponse response) { AsyncContext asyncContext = (request, response); // Manually pass the request context to the asynchronous thread new Thread(() -> { try { // Set the current request context ServletRequestAttributes attributes = new ServletRequestAttributes(request, response); (attributes, true); // Get request parameters in asynchronous thread String cookieValueFromAsync = ()[0].getValue(); ("Cookies in asynchronous threads: " + cookieValueFromAsync); (); } catch (InterruptedException e) { (); } finally { // Clean up the request context (); } }).start(); return "success"; }
advantage: Manually passing the request context allows asynchronous threads to access the request information of the main thread, avoiding the context isolation problem between the asynchronous thread and the main thread.
Delaying the cleaning of request objects
pass()
Delay the cleanup of requests, avoiding the request object being recycled during asynchronous thread execution, thereby maintaining the validity of the requested data.
public String handleRequest(HttpServletRequest request, HttpServletResponse response) { AsyncContext asyncContext = (request, response); new Thread(() -> { try { // Execute asynchronous tasks (5000); // Simulate long-term tasks (); // Delay request cleaning } catch (InterruptedException e) { (); } }).start(); return "success"; }
advantage: By delaying cleaning of the request object, ensure that asynchronous threads can access valid request data, avoiding the request data being accidentally cleaned during asynchronous task execution.
4. Summary
When dealing with asynchronous threads, especially when dealing withHttpServletRequest
When you wait for a request object, you may encounter request reuse and context delivery issues. By rationally using request replicas, manually passing request contexts, delaying request cleaning, etc., we can effectively avoid data contamination and request object reuse issues, thereby ensuring the correctness of request 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:
- use
HttpServletRequestWrapper
Create a copy of the request. - Manually pass the request context to the asynchronous thread.
- Delays the cleanup of request objects, ensuring that asynchronous threads can access request data during execution.
This is the article about the HttpServletRequest reuse problem when using asynchronous threads in Spring Boot. This is the end of this article. For more related contents of Spring Boot asynchronous threads, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!