mcardle 2005/11/08 11:58:49 CET
Modified files:
src/net/sf/j2ep/responsehandlers ResponseHandlerBase.java
Log:
* better policy for cookie caching
* supports Last-Modified header
Revision Changes Path
1.10 +87 -126
esi_server/src/net/sf/j2ep/responsehandlers/ResponseHandlerBase.java
http://jahia.mine.nu:8080/cgi-bin/cvsweb.cgi/esi_server/src/net/sf/j2ep/responsehandlers/ResponseHandlerBase.java.diff?r1=1.9&r2=1.10&f=h
Index: ResponseHandlerBase.java
===================================================================
RCS file:
/home/cvs/repository/esi_server/src/net/sf/j2ep/responsehandlers/ResponseHandlerBase.java,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -r1.9 -r1.10
--- ResponseHandlerBase.java 4 Nov 2005 16:56:30 -0000 1.9
+++ ResponseHandlerBase.java 8 Nov 2005 10:58:49 -0000 1.10
@@ -25,8 +25,6 @@
import java.util.Vector;
import java.util.Enumeration;
import java.util.Date;
-import java.text.DateFormat;
-import java.text.ParseException;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
@@ -40,6 +38,8 @@
import net.sf.j2ep.ProxyFilter;
import org.apache.commons.httpclient.*;
+import org.apache.commons.httpclient.util.DateUtil;
+import org.apache.commons.httpclient.util.DateParseException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.Log;
@@ -56,7 +56,7 @@
*
* @author Anders Nyman
*/
-public abstract class ResponseHandlerBase implements ResponseHandler{
+public abstract class ResponseHandlerBase implements ResponseHandler {
private static Log log = LogFactory.getLog(ResponseHandlerBase.class);
@@ -163,12 +163,7 @@
ByteArrayOutputStream cacheStream = new ByteArrayOutputStream(1000);
- /*private Locale locale = null;
- private String contentEncoding = null;
- private String contentType = null;
- private long lastModified = -1;*/
-
- if (streamFromServer != null) {
+ if (streamFromServer != null) {
byte[] buffer = new byte[1024];
int read = streamFromServer.read(buffer);
while (read > 0) {
@@ -199,38 +194,8 @@
public void sendStreamToClientFromCache(ServletResponse clientResponse,
String cacheKey, byte[] cachedByteContent) throws IOException {
-
- //****************************************** MEGA HACK
*************************************
- String ifnonematch = "";
- try {
- if (methodToServer.getRequestHeader("if-none-match").getValue()
!=null) {
- log.debug("removed if-none-match
["+methodToServer.getRequestHeader("if-none-match")+"] ");
- methodToServer.removeRequestHeader("if-none-match");
- }
-
- } catch (Exception ex) {
- log.debug(" no if-none-match
["+methodToServer.getResponseHeader("if-none-match")+"] ex: "+ex);
- }
- String ifmodifiedsince = "";
- try {
- if
(methodToServer.getRequestHeader("if-modified-since").getValue() !=null) {
- log.debug("removed if-modified-since
["+methodToServer.getRequestHeader("if-modified-since")+"] ");
- methodToServer.removeRequestHeader("if-modified-since");
- }
-
- } catch (Exception ex) {
- log.debug(" no if-modified-since
["+methodToServer.getResponseHeader("if-modified-since")+"] ex: "+ex);
- }
- //****************************************** MEGA HACK
*************************************
-
-
OutputStream clientResponseStream = clientResponse.getOutputStream();
- /*private Locale locale = null;
- private String contentEncoding = null;
- private String contentType = null;
- private long lastModified = -1;*/
-
if (cachedByteContent==null) {
log.error("sendStreamToClientFromCache : cachedByteContent is
NULL. so not sending anything");
}
@@ -263,9 +228,17 @@
String name = header.getName();
boolean contentLength = name.equalsIgnoreCase("content-length");
boolean connection = name.equalsIgnoreCase("connection");
- boolean cookie = name.equalsIgnoreCase("set-cookie");
+ boolean setcookie = name.equalsIgnoreCase("set-cookie");
if (!contentLength && !connection ) {
+ //optionally ignore Set-Cookie header
+ if (setcookie && ignoreCookies) {
+ log.debug("Ignored Set-Cookie header because this is a
response returned from Cache. Header:"+header);
+ continue;
+ }
+ else if (setcookie) {
+ log.debug("Keeping Set-Cookie header because this is a
response returned directly from Server. Header:"+header);
+ }
clientResponse.addHeader(name, header.getValue());
}
}
@@ -351,11 +324,13 @@
else {
CacheObject cacheObject = null;
+ boolean freshCopy = false;
//Case 2: Not in cache so fetch any necessary bits
if (!fragmentCache.contains(cacheKey)) {
log.debug(" * Case 2: Not in cache so fetch any necessary
bits *");
fetch ( clientResponse, clientRequest, cacheKey ,
clientRequestUrl);
+ freshCopy = true;
}
else {
log.debug (" * Case 3: in cache => return from cache *");
@@ -367,16 +342,25 @@
//reset the method to the cached one so we can take the settings
from that one
this.methodToServer =
cacheObject.getUrlCacheObject().getMethod();
- copyHeadersFromServerResponseToClientResponse(clientResponse,
true); //doesn't copy over saved cookie
- //copy the Cookie in the client request to the one sent back to
the client
- //and not the old one stored in the cache as they might be for
different sessions
- clientResponse.addHeader("Set-Cookie",
clientRequest.getHeader("Cookie"));
+ //copy all server response headers to the client
+ // but not necessarily for the Set-Cookie header:
+ //the Set-Cookie header is only forwarded back to the client if
+ //this is a fresh copy of the object we are returning.
+ //So we can guarantee that the returned Set-Cookie is the one
targetted for the current client
+ copyHeadersFromServerResponseToClientResponse(clientResponse,
!freshCopy); //doesn't copy over saved cookie for responses returned from the
cache
+ //TODO: might be inconsistancies
+ //TODO: 1/ when simply a fragment of a skeleton is fetched
+ //TODO: and we return the Set-Cookie of the cached skeleton and
not of the newly
+ //TODO: fetched fragment. Not sure what behaviour to adopt here.
+ //TODO: 2/ when a Set-Cookie should be sent back to the client
+ //TODO: but isn't because the object is already cached. Might
not be important since
+ //TODO: original GET URL contains an ESI identifier which has a
similar role to cookies. Need to check
boolean return304NotModified =
checkIfConditionalHtppHeaders(clientRequest, cacheObject);
if (return304NotModified) {
log.debug("Returning a HTPP 304 for
"+cacheObject.getUrlCacheObject().getUrl());
- clientResponse.setStatus(304); //HTTP 304: the document was
not modified
+
clientResponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED); //HTTP 304: the
document was not modified
//no data sent since request object hasn't changed
}
else {
@@ -386,6 +370,7 @@
}
}
+
}
@@ -479,125 +464,101 @@
private boolean checkIfConditionalHtppHeaders(HttpServletRequest
clientRequest, CacheObject cacheObject) {
boolean return304NotModified = false;
+ printRequestHeaders(clientRequest);
+
//check Entity Tag Revalidation
- String eTagHeader = clientRequest.getHeader("If-None-Match");
- if (eTagHeader !=null) {
+ String IfNoneMatchHeader = clientRequest.getHeader("If-None-Match");
+ if (IfNoneMatchHeader !=null) {
Header eTagRespHeader =
this.methodToServer.getResponseHeader("ETag");
if (eTagRespHeader!=null) {
String[] eTag = eTagRespHeader.getValue().split(",");
for (int i = 0; i<eTag.length; i++) { //since there can be
more than one ETag e.g. If-None-March: "foobar","A34FAC0095","Profiles in
Courage"
- if (eTagHeader.equals(eTag[i])) {
- log.debug("Detected a HTPP 304 for "+eTagHeader);
+ if (IfNoneMatchHeader.equals(eTag[i])) {
+ log.debug("Detected a HTPP 304 for
["+IfNoneMatchHeader+"] because of ETag header ["+eTagRespHeader+"] from
server");
return304NotModified = true;
}
}
}
+ else
+ log.debug("client submitted a
IfNoneMatch:"+IfNoneMatchHeader +" but cached server response did not contain
an ETag");
}
- String modSinceHeader = clientRequest.getHeader("If-Modified-Since");
- if ((eTagHeader!=null ^ return304NotModified==false) //If an
HTTP/1.1 cache or server receives a request with both If-Modified-Since and
entity tag conditional headers, it must not return a 304 Not Modified response
unless doing so is consistent with all of the conditional header fields in the
request.
- && modSinceHeader !=null) {
-
- try {
- //First check if the object has been changed in the cache
since If-Modified-Since date
- Date modSinceHeaderDate =
DateFormat.getDateInstance().parse(modSinceHeader);
- if
(cacheObject.getUrlCacheObject().getLastUpdateDate().after(modSinceHeaderDate)
) {
- log.debug("Detected a HTPP 304 for "+modSinceHeader);
- return304NotModified = true;
+ long modSinceHeader =
clientRequest.getDateHeader("If-Modified-Since");
+ //if ((IfNoneMatchHeader!=null ^ return304NotModified==false) //If
an HTTP/1.1 cache or server receives a request with both If-Modified-Since and
entity tag conditional headers, it must not return a 304 Not Modified response
unless doing so is consistent with all of the conditional header fields in the
request.
+ // && modSinceHeader !=null) {
+ if (return304NotModified==false && modSinceHeader !=-1) {
+ //First check if the object has been changed in the cache since
If-Modified-Since date
+ Date modSinceHeaderDate = new Date(modSinceHeader);
+ if
(cacheObject.getUrlCacheObject().getLastUpdateDate().after(modSinceHeaderDate)
) {
+ log.debug("Detected a HTTP 304 for ["+new
Date(modSinceHeader)+"] because object has been changed/added in the cache at
"+cacheObject.getUrlCacheObject().getLastUpdateDate());
+ return304NotModified = true;
+ }
+ //if it hasn't, then still check if the stored Expires header
date is after the If-Modified-Since date
+ else {
+ Header expiresRespHeader =
this.methodToServer.getResponseHeader("Expires");
+ if (expiresRespHeader!=null) {
+ try {
+ //TODO: move this date parsing to UrlCacheKey object
initialization
+ Date expiresHeaderDate = DateUtil.parseDate(
expiresRespHeader.getValue() ) ;
+ if (modSinceHeaderDate.after(expiresHeaderDate)) {
+ log.debug("Detected a HTTP 304 for
["+modSinceHeader+"] because of Expires Header ["+expiresRespHeader+"] from
Server");
+ return304NotModified = true;
+ }
+ } catch (DateParseException e) {
+ log.error("Error trying to parse Expires Header date
["+expiresRespHeader.getValue()+"] : "+e);
+ }
}
- //if it hasn't, then still check if the stored Expires
header date is after the If-Modified-Since date
- else {
- //TODO: rename vars to something more meaningful
- Header expiresRespHeader =
this.methodToServer.getResponseHeader("Expires");
- if (expiresRespHeader!=null) {
- String expiresHeader = expiresRespHeader.getValue();
- try {
- Date expiresHeaderDate =
DateFormat.getDateInstance().parse(expiresHeader);
- if (modSinceHeaderDate.after(expiresHeaderDate))
{
- log.debug("Detected a HTPP 304 for
"+expiresRespHeader);
- return304NotModified = true;
- }
- } catch (ParseException e) {
- log.debug("Error trying to parse Expires Header
date ["+expiresHeader+"] : "+e);
+ Header lastModRespHeader =
this.methodToServer.getResponseHeader("Last-Modified");
+ if (return304NotModified==false && lastModRespHeader !=null)
{
+ try {
+ //TODO: move this date parsing to UrlCacheKey object
initialization
+ Date lastModHeaderDate = DateUtil.parseDate(
lastModRespHeader.getValue() ) ;
+ if (modSinceHeaderDate.after(lastModHeaderDate)) {
+ log.debug("Detected a HTTP 304 for
["+modSinceHeader+"] because of Last-Modified Header ["+lastModRespHeader+"]
from Server");
+ return304NotModified = true;
}
+ } catch (DateParseException e) {
+ log.error("Error trying to parse Last-Modified
Header date ["+lastModRespHeader+"] : "+e);
}
}
- } catch (ParseException e) {
- log.debug("Badly formatted If-Modified-Since Header date
["+modSinceHeader+"] in client Request : "+e);
- }
- }
- return return304NotModified;
- }
- public static void getRidOfDodgyRequestHeaders(HttpMethod method) {
- //****************************************** MEGA HACK
*************************************
- String ifnonematch = "";
- try {
- if (method.getRequestHeader("if-none-match").getValue() !=null) {
- log.debug("removed if-none-match
["+method.getRequestHeader("if-none-match")+"] ");
- method.removeRequestHeader("if-none-match");
- }
- } catch (Exception ex) {
- //log.debug(" no if-none-match
["+method.getResponseHeader("if-none-match")+"] ex: "+ex);
- }
- String ifmodifiedsince = "";
- try {
- if (method.getRequestHeader("if-modified-since").getValue()
!=null) {
- log.debug("removed if-modified-since
["+method.getRequestHeader("if-modified-since")+"] ");
- method.removeRequestHeader("if-modified-since");
}
-
- } catch (Exception ex) {
- //log.debug(" no if-modified-since
["+method.getResponseHeader("if-modified-since")+"] ex: "+ex);
}
-
- //****************************************** MEGA HACK
*************************************
+ return return304NotModified;
}
- public static void getRidOfDodgyResponseHeaders(HttpMethod method) {
- //****************************************** MEGA HACK
*************************************
- String ETag = "";
- try {
- if (method.getRequestHeader("ETag").getValue() !=null) {
- log.debug("removed ETag
["+method.getRequestHeader("ETag")+"] ");
- method.removeRequestHeader("ETag");
- }
-
- } catch (Exception ex) {
- //log.debug(" no ETag ["+method.getResponseHeader("ETag")+"]
ex: "+ex);
- }
-
- String LastModified = "";
- try {
- if (method.getRequestHeader("Last-Modified").getValue() !=null) {
- log.debug("removed Last-Modified
["+method.getRequestHeader("Last-Modified")+"] ");
- method.removeRequestHeader("Last-Modified");
+ public static void printRequestHeaders(HttpServletRequest clientRequest)
{
+ StringBuffer buffer = new StringBuffer();
+ Enumeration headerNamesEnum = clientRequest.getHeaderNames();
+ while (headerNamesEnum.hasMoreElements()) {
+ String hName = (String) headerNamesEnum.nextElement();
+ Enumeration hValEnum = clientRequest.getHeaders(hName);
+ while (hValEnum.hasMoreElements()) {
+ String hVal = (String) hValEnum.nextElement();
+ buffer.append(hName +":"+ hVal+"\n");
}
-
- } catch (Exception ex) {
- //log.debug(" no ETag
["+method.getResponseHeader("Last-Modified")+"] ex: "+ex);
}
-
- //****************************************** MEGA HACK
*************************************
+ log.debug("HTTP client REQUEST Headers : \n" + buffer.toString());
}
-
public static void printRequestHeaders(HttpMethod method) {
Header[] headers = method.getRequestHeaders();
StringBuffer buffer = new StringBuffer();
for (int i=0; i<headers.length; i++){
buffer.append(headers[i]);
}
- log.debug("HTTP REQUEST Headers : \n" + buffer.toString());
+ log.debug("HTTP Server REQUEST Headers : \n" + buffer.toString());
}
+
+
public static void printResponseHeaders(HttpMethod method) {
Header[] headers = method.getResponseHeaders();
StringBuffer buffer = new StringBuffer();
for (int i=0; i<headers.length; i++){
buffer.append(headers[i]);
}
- log.debug("HTTP RESPONSE Headers : \n" + buffer.toString());
+ log.debug("HTTP Server RESPONSE Headers : \n" + buffer.toString());
}