From c94caf05a7a06c342cb804386301de0b07a3c23e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=CC=88rg=20Prante?= Date: Sat, 24 Nov 2012 22:22:06 +0100 Subject: [PATCH 1/2] simple ETag handling --- .../http/netty/NettyHttpChannel.java | 26 ++++++++++++++++++- .../rest/AbstractRestResponse.java | 25 ++++++++++++++++++ .../org/elasticsearch/rest/RestResponse.java | 2 ++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/elasticsearch/http/netty/NettyHttpChannel.java b/src/main/java/org/elasticsearch/http/netty/NettyHttpChannel.java index 5e3508a84fa7e..ba309ad8cdbb9 100644 --- a/src/main/java/org/elasticsearch/http/netty/NettyHttpChannel.java +++ b/src/main/java/org/elasticsearch/http/netty/NettyHttpChannel.java @@ -36,12 +36,21 @@ import org.jboss.netty.handler.codec.http.*; import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.Locale; import java.util.Set; +import java.util.TimeZone; /** * */ public class NettyHttpChannel implements HttpChannel { + + public static final String HTTP_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz"; + public static final String HTTP_DATE_GMT_TIMEZONE = "GMT"; + private final NettyHttpServerTransport transport; private final Channel channel; private final org.jboss.netty.handler.codec.http.HttpRequest request; @@ -131,6 +140,21 @@ public void sendResponse(RestResponse response) { resp.setHeader(HttpHeaders.Names.CONTENT_TYPE, response.contentType()); resp.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(buf.readableBytes())); + + // ETag handling, check if checksums matches, and deliver timestamped HTTP 304 "not modified" if so + if (transport.settings().getAsBoolean("http.cache.etag", false)) { + long checksum = response.contentChecksum(); + resp.addHeader(HttpHeaders.Names.ETAG, checksum); + String etag = request.getHeader("If-None-Match"); + if (etag != null && Long.parseLong(etag) == checksum) { + resp.setContent(null); + resp.setStatus(HttpResponseStatus.NOT_MODIFIED); + SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US); + dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE)); + Calendar time = new GregorianCalendar(); + resp.setHeader(HttpHeaders.Names.DATE, dateFormatter.format(time.getTime())); + } + } if (transport.resetCookies) { String cookieString = request.getHeader(HttpHeaders.Names.COOKIE); @@ -147,7 +171,7 @@ public void sendResponse(RestResponse response) { } } } - + // Write the response. ChannelFuture future = channel.write(resp); if (releaseContentListener != null) { diff --git a/src/main/java/org/elasticsearch/rest/AbstractRestResponse.java b/src/main/java/org/elasticsearch/rest/AbstractRestResponse.java index 72592b255ce19..7c6578e58749b 100644 --- a/src/main/java/org/elasticsearch/rest/AbstractRestResponse.java +++ b/src/main/java/org/elasticsearch/rest/AbstractRestResponse.java @@ -19,11 +19,16 @@ package org.elasticsearch.rest; +import java.io.IOException; +import java.util.zip.Adler32; + /** * */ public abstract class AbstractRestResponse implements RestResponse { + private long checksum = -1L; + @Override public byte[] prefixContent() { return null; @@ -53,4 +58,24 @@ public int suffixContentLength() { public int suffixContentOffset() { return 0; } + + @Override + public long contentChecksum() { + if (checksum == -1L) { + createChecksum(); + } + return checksum; + } + + private long createChecksum() { + Adler32 adler = new Adler32(); + try { + byte[] b = content(); + adler.update(b,0,b.length); + } catch (IOException e) { + } + return adler.getValue(); + } + + } \ No newline at end of file diff --git a/src/main/java/org/elasticsearch/rest/RestResponse.java b/src/main/java/org/elasticsearch/rest/RestResponse.java index 15bba82932103..5c30e42ee139e 100644 --- a/src/main/java/org/elasticsearch/rest/RestResponse.java +++ b/src/main/java/org/elasticsearch/rest/RestResponse.java @@ -45,6 +45,8 @@ public interface RestResponse { int contentLength() throws IOException; int contentOffset() throws IOException; + + long contentChecksum(); byte[] prefixContent(); From 07d41cd77d36fd5230b5e0499676aa407539ca22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=CC=88rg=20Prante?= Date: Sun, 25 Nov 2012 01:16:10 +0100 Subject: [PATCH 2/2] ETag caching per REST action class --- .../org/elasticsearch/http/netty/NettyHttpChannel.java | 7 ++++++- .../java/org/elasticsearch/rest/AbstractRestResponse.java | 3 +-- src/main/java/org/elasticsearch/rest/RestChannel.java | 2 ++ src/main/java/org/elasticsearch/rest/RestController.java | 1 + 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/elasticsearch/http/netty/NettyHttpChannel.java b/src/main/java/org/elasticsearch/http/netty/NettyHttpChannel.java index ba309ad8cdbb9..c1b67d152c6a6 100644 --- a/src/main/java/org/elasticsearch/http/netty/NettyHttpChannel.java +++ b/src/main/java/org/elasticsearch/http/netty/NettyHttpChannel.java @@ -54,6 +54,7 @@ public class NettyHttpChannel implements HttpChannel { private final NettyHttpServerTransport transport; private final Channel channel; private final org.jboss.netty.handler.codec.http.HttpRequest request; + private boolean cacheable; public NettyHttpChannel(NettyHttpServerTransport transport, Channel channel, org.jboss.netty.handler.codec.http.HttpRequest request) { this.transport = transport; @@ -61,6 +62,10 @@ public NettyHttpChannel(NettyHttpServerTransport transport, Channel channel, org this.request = request; } + public void setCacheable(boolean cacheable) { + this.cacheable = cacheable; + } + @Override public void sendResponse(RestResponse response) { @@ -142,7 +147,7 @@ public void sendResponse(RestResponse response) { resp.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(buf.readableBytes())); // ETag handling, check if checksums matches, and deliver timestamped HTTP 304 "not modified" if so - if (transport.settings().getAsBoolean("http.cache.etag", false)) { + if (cacheable) { long checksum = response.contentChecksum(); resp.addHeader(HttpHeaders.Names.ETAG, checksum); String etag = request.getHeader("If-None-Match"); diff --git a/src/main/java/org/elasticsearch/rest/AbstractRestResponse.java b/src/main/java/org/elasticsearch/rest/AbstractRestResponse.java index 7c6578e58749b..a40d408381f0f 100644 --- a/src/main/java/org/elasticsearch/rest/AbstractRestResponse.java +++ b/src/main/java/org/elasticsearch/rest/AbstractRestResponse.java @@ -62,7 +62,7 @@ public int suffixContentOffset() { @Override public long contentChecksum() { if (checksum == -1L) { - createChecksum(); + checksum = createChecksum(); } return checksum; } @@ -77,5 +77,4 @@ private long createChecksum() { return adler.getValue(); } - } \ No newline at end of file diff --git a/src/main/java/org/elasticsearch/rest/RestChannel.java b/src/main/java/org/elasticsearch/rest/RestChannel.java index 5bfe750819c76..0e0ed1826a7c3 100644 --- a/src/main/java/org/elasticsearch/rest/RestChannel.java +++ b/src/main/java/org/elasticsearch/rest/RestChannel.java @@ -25,4 +25,6 @@ public interface RestChannel { void sendResponse(RestResponse response); + + void setCacheable(boolean enabled); } \ No newline at end of file diff --git a/src/main/java/org/elasticsearch/rest/RestController.java b/src/main/java/org/elasticsearch/rest/RestController.java index 620c8229b25e6..13ca66e478ee6 100644 --- a/src/main/java/org/elasticsearch/rest/RestController.java +++ b/src/main/java/org/elasticsearch/rest/RestController.java @@ -156,6 +156,7 @@ public void dispatchRequest(final RestRequest request, final RestChannel channel void executeHandler(RestRequest request, RestChannel channel) { final RestHandler handler = getHandler(request); if (handler != null) { + channel.setCacheable(settings.getAsBoolean("http.cache.etag."+ handler.getClass().getSimpleName().toLowerCase(), false)); handler.handleRequest(request, channel); } else { if (request.method() == RestRequest.Method.OPTIONS) {