hk2166 commented on issue #15851:
URL: https://github.com/apache/grails-core/issues/15851#issuecomment-6449609412

   I'd like to work on this. I traced @chrisala's reproduction on 8.0.x, and 
its cause is different from the Spring Security trace in the original report.
   
   **Cause:** since #16230 (in 8.0.0), `GrailsWebRequestTaskDecorator` gives 
each `task { }` its own copy of the `GrailsWebRequest`. That copy still points 
at the servlet request after the request has completed. Binding a Map reads the 
current request even though the source is not the request:
   
   `new Domain(name: 'x')` → `DataBindingUtils.bindObjectToDomainInstance` → 
`createDataBindingSource` → `MimeTypeUtils.resolveMimeType` → 
`DefaultMimeTypeResolver.resolveRequestMimeType()` → 
`HttpServletRequestExtension.getMimeTypes(request)` → 
`request.getAttribute(...)`
   
   Once Tomcat has recycled the request, that call throws 
`IllegalStateException`. `bindObjectToDomainInstance` catches it and records it 
as a binding error, which is what chrisala saw. On 7.x, `Promises.task` didn't 
carry the request into the task (only `WebPromises` did, and it kept the 
request open with servlet async), so binding found no request and used 
`MimeType.ALL`.
   
   **Proposal:** detach the task's copy when the originating request completes. 
The decorator would register a request destruction callback on the captured 
request that marks the copy as completed, and `GrailsWebRequest.lookup()` would 
stop returning a completed copy. The task then behaves like code running 
outside a request: binding falls back to `MimeType.ALL` and the default locale, 
as it already does on a plain background thread. It would also stop `lookup()` 
from handing a task a request object that Tomcat has reused for a later request 
when facade discarding is turned off.
   
   A narrower option is to guard only the places where binding reads the 
request (`DefaultMimeTypeResolver.resolveRequestMimeType` and 
`GrailsWebDataBinder.getLocale`), but other framework code that looks up the 
request inside a task would still fail.
   
   Tests would cover a task that binds a Map to a domain instance after the 
request completes (no binding errors), a task that still sees the request while 
it is active, and chained `then { }` callbacks.
   
   This would target 8.0.x, since the decorator only exists there. #16516 adds 
an unrelated method to `GrailsWebRequest`, and #16596 converts 
`grails-web-common` to Groovy on a side branch.
   
   I haven't found a cause for the Spring Security trace in the original report 
(7.0.0-M5, no async), so I'd leave that part open unless someone can reproduce 
it.
   
   @codeconsole @jdaugherty does detaching the task's copy fit the design of 
#16230, or would you prefer the narrower guard?
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to