Get HTTP Response size from Grails plugin

grails, servlet-filters

Solution

Grails has this feature built in. It's undocumented. Set the system property "GSPResponseWriter.enableContentLength" to true by adding "-DGSPResponseWriter.enableContentLength=true" option to the JVM args. See the source code of GSPResponseWriter for more details.

The reason this isn't enabled by default is that calculating the content length is not simple because some characters are multi-byte in UTF-8 encoding. BoundedCharsAsEncodedBytesCounter is the class doing the calculation.

Problem

I'm trying to write a Grails plugin that records the size of each HTTP response sent by the application it's installed in. The approach I've tried so far is: (1) Write a servlet filter that wraps the `response`. The response wrapper decorates the `OutputStream` with an `OutputStream` that counts the number of bytes in the response. These classes are shown here. (2) Register the servlet filter in the plugin descriptor ``` def doWithWebDescriptor = { webXml -> def contextParam = webXml.'context-param' contextParam[contextParam.size() - 1] + { 'filter' { 'filter-name'('countingFilter') 'filter-class'(CountingFilter.name) } } def filter = webXml.'filter' filter[filter.size() - 1] + { 'filter-mapping' { 'filter-name'('countingFilter') 'url-pattern'('/*') } } } ``` (3) In a Grails filter, try to retrieve the number of bytes written like so: ``` afterView = { // countingFilter registers the response wrapper as an attribute of the request // named 'counter' request.getAttribute('counter').byteCount } ``` But a value of 0 is always returned. However, I discovered that if I add the code above to both the `after` and `afterView` closures of the Grails filter ``` after = { // returns 0 request.getAttribute('counter').byteCount } afterView = { // returns the correct response size request.getAttribute('counter').byteCount } ``` It now returns the correct response size, but it seems the content generated by the sitemesh layout has disappeared, leaving only the content in the GSP itself. So it seems I'm close to a solution. My guess is that I'm flushing buffers at the wrong time, and/or my filter is not running at the correct point in the filter chain. This is proving to be much more difficult that I expected. Is there a simpler way?

Original source

Related problems