4

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?

Community
  • 1
  • 1
Dónal
  • 185,044
  • 174
  • 569
  • 824

1 Answers1

4

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.

Lari Hotari
  • 5,190
  • 1
  • 36
  • 43