I recently had to do some major debugging of a hard to reproduce issue. The issue occurred mainly when rendering grails templates in the controller and retrieving them by AJAX. Sometimes (it seemed pretty random) the response to exactly the same request didn't contain any Content-Type header, which resulted in messed up pages (mainly because of missing charset).
I finally managed to reproduce the issue and discovered that the source of the problem was caused by the mail plugin's HTML rendering.
Detailed description
the class WrappedResponseHolder from grails is involved, which holds a reference to a response in a ThreadLocal
Because of the ThreadLocal the occurrence of the issue depends on the current worker-thread handling the request
mail plugin restores the current response object in the WrappedResponseHolder after rendering a HTML mail, instead of the value before doing the rendering
in the grails class WebOutputContext the method setContentType() uses the the wrapped response if available
subsequent requests in the same worker-thread will now always have the same response object available in the WrappedResponseHolder. A render(template: ...) call in a controller will now result in a response with no Content-Type set, because the method setContentType() will be called on the old (already expired) response object
Correct usage of WrappedResponseHolder
I searched the usage of WrappedResponseHolder inside of grails. The correct pattern to use this class seems to be like this:
The code is found in the class MailMessageContentRenderer.RenderEnvironment:
WrappedResponseHolder.wrappedResponse = originalRequestAttributes?.currentResponse
This line of code sets the wrapped response after the rendering. But is uses the current response instead of the previously stored value before doing the rendering.
I recently had to do some major debugging of a hard to reproduce issue. The issue occurred mainly when rendering grails templates in the controller and retrieving them by AJAX. Sometimes (it seemed pretty random) the response to exactly the same request didn't contain any Content-Type header, which resulted in messed up pages (mainly because of missing charset).
I finally managed to reproduce the issue and discovered that the source of the problem was caused by the mail plugin's HTML rendering.
Detailed description
WrappedResponseHolder
from grails is involved, which holds a reference to a response in aThreadLocal
ThreadLocal
the occurrence of the issue depends on the current worker-thread handling the requestWrappedResponseHolder
after rendering a HTML mail, instead of the value before doing the renderingWebOutputContext
the methodsetContentType()
uses the the wrapped response if availableWrappedResponseHolder
. Arender(template: ...)
call in a controller will now result in a response with no Content-Type set, because the methodsetContentType()
will be called on the old (already expired) response objectCorrect usage of WrappedResponseHolder
I searched the usage of
WrappedResponseHolder
inside of grails. The correct pattern to use this class seems to be like this:Wrong usage in mail plugin
The code is found in the class
MailMessageContentRenderer.RenderEnvironment
:WrappedResponseHolder.wrappedResponse = originalRequestAttributes?.currentResponse
This line of code sets the wrapped response after the rendering. But is uses the current response instead of the previously stored value before doing the rendering.
Example project
I made an example project to reproduce and demonstrate the issue: https://github.com/davidkron/mail-plugin-renderissue
Details of the project:
To see the issue, run the application, call the following URLs and see the DEBUG messages in the console: