@ -57,26 +57,61 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter {
@@ -57,26 +57,61 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter {
filterChain . doFilter ( request , responseWrapper ) ;
byte [ ] body = responseWrapper . toByteArray ( ) ;
String responseETag = generateETagHeaderValue ( body ) ;
response . setHeader ( HEADER_ETAG , responseETag ) ;
String requestETag = request . getHeader ( HEADER_IF_NONE_MATCH ) ;
if ( responseETag . equals ( requestETag ) ) {
if ( logger . isTraceEnabled ( ) ) {
logger . trace ( "ETag [" + responseETag + "] equal to If-None-Match, sending 304" ) ;
int statusCode = responseWrapper . getStatusCode ( ) ;
if ( isEligibleForEtag ( request , responseWrapper , statusCode , body ) ) {
String responseETag = generateETagHeaderValue ( body ) ;
response . setHeader ( HEADER_ETAG , responseETag ) ;
String requestETag = request . getHeader ( HEADER_IF_NONE_MATCH ) ;
if ( responseETag . equals ( requestETag ) ) {
if ( logger . isTraceEnabled ( ) ) {
logger . trace ( "ETag [" + responseETag + "] equal to If-None-Match, sending 304" ) ;
}
response . setContentLength ( 0 ) ;
response . setStatus ( HttpServletResponse . SC_NOT_MODIFIED ) ;
}
else {
if ( logger . isTraceEnabled ( ) ) {
logger . trace ( "ETag [" + responseETag + "] not equal to If-None-Match [" + requestETag +
"], sending normal response" ) ;
}
copyBodyToResponse ( body , response ) ;
}
response . setStatus ( HttpServletResponse . SC_NOT_MODIFIED ) ;
}
else {
if ( logger . isTraceEnabled ( ) ) {
logger . trace ( "ETag [" + responseETag + "] not equal to If-None-Match [" + requestETag +
"], sending normal response" ) ;
logger . trace ( "Response with status code [" + statusCode + "] not eligible for ETag" ) ;
}
response . setContentLength ( body . length ) ;
copyBodyToResponse ( body , response ) ;
}
}
private void copyBodyToResponse ( byte [ ] body , HttpServletResponse response ) throws IOException {
response . setContentLength ( body . length ) ;
if ( body . length > 0 ) {
FileCopyUtils . copy ( body , response . getOutputStream ( ) ) ;
}
}
/ * *
* Indicates whether the given request and response are eligible for ETag generation .
*
* < p > Default implementation returns { @code true } for response status codes in the { @code 2xx } series .
*
* @param request the HTTP request
* @param response the HTTP response
* @param responseStatusCode the HTTP response status code
* @param responseBody the response body
* @return { @code true } if eligible for ETag generation ; { @code false } otherwise
* /
protected boolean isEligibleForEtag ( HttpServletRequest request ,
HttpServletResponse response ,
int responseStatusCode ,
byte [ ] responseBody ) {
return ( responseStatusCode > = 200 & & responseStatusCode < 300 ) ;
}
/ * *
* Generate the ETag header value from the given response body byte array . < p > The default implementation generates an
* MD5 hash .
@ -105,10 +140,36 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter {
@@ -105,10 +140,36 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter {
private PrintWriter writer ;
private int statusCode = - 1 ;
private ShallowEtagResponseWrapper ( HttpServletResponse response ) {
super ( response ) ;
}
@Override
public void setStatus ( int sc ) {
super . setStatus ( sc ) ;
this . statusCode = sc ;
}
@Override
public void setStatus ( int sc , String sm ) {
super . setStatus ( sc , sm ) ;
this . statusCode = sc ;
}
@Override
public void sendError ( int sc ) throws IOException {
super . sendError ( sc ) ;
this . statusCode = sc ;
}
@Override
public void sendError ( int sc , String msg ) throws IOException {
super . sendError ( sc , msg ) ;
this . statusCode = sc ;
}
@Override
public ServletOutputStream getOutputStream ( ) {
return this . outputStream ;
@ -135,6 +196,10 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter {
@@ -135,6 +196,10 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter {
resetBuffer ( ) ;
}
private int getStatusCode ( ) {
return statusCode ;
}
private byte [ ] toByteArray ( ) {
return this . content . toByteArray ( ) ;
}