You are viewing a plain text version of this content. The canonical link for it is here.
Posted to awf-commits@incubator.apache.org by jm...@apache.org on 2011/08/03 17:36:30 UTC

svn commit: r1153593 [1/3] - in /incubator/deft/sandbox/src: main/java/org/deftserver/example/handler/ main/java/org/deftserver/example/keyvalue/ main/java/org/deftserver/util/ main/java/org/deftserver/web/ main/java/org/deftserver/web/handler/ main/ja...

Author: jmeehan
Date: Wed Aug  3 17:36:25 2011
New Revision: 1153593

URL: http://svn.apache.org/viewvc?rev=1153593&view=rev
Log:
DEFT-178: Extract HttpRequest and HttpResponse interfaces; sundry clean-up.

Added:
    incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpRequestImpl.java   (with props)
    incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpResponseImpl.java   (with props)
Modified:
    incubator/deft/sandbox/src/main/java/org/deftserver/example/handler/BasicHandlerExample.java
    incubator/deft/sandbox/src/main/java/org/deftserver/example/handler/CookieHandlerExample.java
    incubator/deft/sandbox/src/main/java/org/deftserver/example/handler/PatternHandlerExample.java
    incubator/deft/sandbox/src/main/java/org/deftserver/example/keyvalue/KeyValueStoreExample.java
    incubator/deft/sandbox/src/main/java/org/deftserver/util/HttpUtil.java
    incubator/deft/sandbox/src/main/java/org/deftserver/web/Application.java
    incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/BadRequestRequestHandler.java
    incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/ForbiddenRequestHandler.java
    incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/NotFoundRequestHandler.java
    incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/RequestHandler.java
    incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/StaticContentHandler.java
    incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpProtocol.java
    incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpRequest.java
    incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpRequestDispatcher.java
    incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpResponse.java
    incubator/deft/sandbox/src/main/java/org/deftserver/web/http/MalFormedHttpRequest.java
    incubator/deft/sandbox/src/main/java/org/deftserver/web/http/PartialHttpRequest.java
    incubator/deft/sandbox/src/test/java/org/deftserver/web/ApplicationTest.java
    incubator/deft/sandbox/src/test/java/org/deftserver/web/DeftSystemTest.java
    incubator/deft/sandbox/src/test/java/org/deftserver/web/handler/RequestHandlerTest.java
    incubator/deft/sandbox/src/test/java/org/deftserver/web/http/HttpRequestTest.java

Modified: incubator/deft/sandbox/src/main/java/org/deftserver/example/handler/BasicHandlerExample.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/example/handler/BasicHandlerExample.java?rev=1153593&r1=1153592&r2=1153593&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/example/handler/BasicHandlerExample.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/example/handler/BasicHandlerExample.java Wed Aug  3 17:36:25 2011
@@ -26,10 +26,8 @@ import org.deftserver.web.http.HttpReque
 import org.deftserver.web.http.HttpResponse;
 
 /**
- * This is a basic handler, which does nothing more than write output and
- * associate itself with a path. The {@link Path} annotation below defines the
- * path on which this handler is invoked. When executed as part of the server
- * example:
+ * This is a basic handler, which does nothing more than write output and associate itself with a path. The {@link Path}
+ * annotation below defines the path on which this handler is invoked. When executed as part of the server example:
  * <p>
  * <code>http://localhost:8080/</code>
  * </p>

Modified: incubator/deft/sandbox/src/main/java/org/deftserver/example/handler/CookieHandlerExample.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/example/handler/CookieHandlerExample.java?rev=1153593&r1=1153592&r2=1153593&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/example/handler/CookieHandlerExample.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/example/handler/CookieHandlerExample.java Wed Aug  3 17:36:25 2011
@@ -28,15 +28,14 @@ import org.deftserver.web.http.HttpReque
 import org.deftserver.web.http.HttpResponse;
 
 /**
- * This handler demonstrates usage of cookie-related functionality. The
- * {@link Path} annotation below defines the path on which this handler is
- * invoked. When executed as part of the server example:
+ * This handler demonstrates usage of cookie-related functionality. The {@link Path} annotation below defines the path
+ * on which this handler is invoked. When executed as part of the server example:
  * <p>
  * <code>http://localhost:8080/cookie</code>
  * </p>
  * <p>
- * This handler will set a cookie when a page is first viewed, with a limited
- * lifetime and unique identifier. Upon refresh, this cookie will be cleared.
+ * This handler will set a cookie when a page is first viewed, with a limited lifetime and unique identifier. Upon
+ * refresh, this cookie will be cleared.
  * </p>
  * 
  * @see Path

Modified: incubator/deft/sandbox/src/main/java/org/deftserver/example/handler/PatternHandlerExample.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/example/handler/PatternHandlerExample.java?rev=1153593&r1=1153592&r2=1153593&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/example/handler/PatternHandlerExample.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/example/handler/PatternHandlerExample.java Wed Aug  3 17:36:25 2011
@@ -28,16 +28,15 @@ import org.deftserver.web.http.HttpReque
 import org.deftserver.web.http.HttpResponse;
 
 /**
- * This handler is invoked as a result of the combination of path and Regular
- * Expression. When executed as part of the server example:
+ * This handler is invoked as a result of the combination of path and Regular Expression. When executed as part of the
+ * server example:
  * <p>
- * <code>http://localhost:8080/pattern/123</code> will be found.
- * <code>http://localhost:8080/pattern/abc</code> will not be found.
+ * <code>http://localhost:8080/pattern/123</code> will be found. <code>http://localhost:8080/pattern/abc</code> will not
+ * be found.
  * </p>
  * <p>
- * In the annotation below, the regular expression is "[0-9]+" which indicates
- * that any number will be matched. Any pattern as understood by {@link Pattern}
- * is suitable, and the handler will be invoked only when this pattern is
+ * In the annotation below, the regular expression is "[0-9]+" which indicates that any number will be matched. Any
+ * pattern as understood by {@link Pattern} is suitable, and the handler will be invoked only when this pattern is
  * matched.
  * </p>
  * 

Modified: incubator/deft/sandbox/src/main/java/org/deftserver/example/keyvalue/KeyValueStoreExample.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/example/keyvalue/KeyValueStoreExample.java?rev=1153593&r1=1153592&r2=1153593&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/example/keyvalue/KeyValueStoreExample.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/example/keyvalue/KeyValueStoreExample.java Wed Aug  3 17:36:25 2011
@@ -49,10 +49,13 @@ public class KeyValueStoreExample {
         public void get(HttpRequest request, final HttpResponse response) {
             client.get("deft", new AsyncResult<byte[]>() {
                 @Override
-                public void onFailure(Throwable caught) { /* ignore */ }
+                public void onFailure(Throwable caught) { /* ignore */
+                }
 
                 @Override
-                public void onSuccess(byte[] result) { response.write(new String(result)).finish(); }
+                public void onSuccess(byte[] result) {
+                    response.write(new String(result)).finish();
+                }
             });
         }
     }

Modified: incubator/deft/sandbox/src/main/java/org/deftserver/util/HttpUtil.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/util/HttpUtil.java?rev=1153593&r1=1153592&r2=1153593&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/util/HttpUtil.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/util/HttpUtil.java Wed Aug  3 17:36:25 2011
@@ -28,166 +28,168 @@ import org.deftserver.web.http.HttpReque
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-
 public class HttpUtil {
 
-	private static final Logger logger = LoggerFactory.getLogger(HttpUtil.class);
-
-	/* MessageDigest are not thread-safe and are expensive to create. 
-	 * Do it lazily for each thread that need access to one.*/
-	private static final ThreadLocal<MessageDigest> md = new ThreadLocal<MessageDigest>();
-	
-	private static final String _200_OK 		 			= "HTTP/1.1 200 OK\r\n"; 
-	private static final String _201_CREATED 		 		= "HTTP/1.1 201 Created\r\n"; 
-	private static final String _202_ACCEPTED 		 		= "HTTP/1.1 202 Accepted\r\n"; 
-	private static final String _203_NON_AUTHORITATIVE_INFO = "HTTP/1.1 203 Non-Authoritative Information\r\n"; 
-	private static final String _204_NO_CONTENT 			= "HTTP/1.1 204 No Content\r\n"; 
-	private static final String _205_RESET_CONTENT 			= "HTTP/1.1 205 Reset Content\r\n"; 
-	private static final String _206_PARTIAL_CONTENT 		= "HTTP/1.1 206 Partial Content\r\n"; 
-	private static final String _300_MULTIPLE_CHOICES 		= "HTTP/1.1 300 Multiple Choices\r\n"; 
-	private static final String _301_MOVED_PERMANENTLY 		= "HTTP/1.1 301 Moved Permanently\r\n"; 
-	private static final String _302_NOT_FOUND		 		= "HTTP/1.1 302 Found\r\n"; 
-	private static final String _303_SEE_OTHER		 		= "HTTP/1.1 303 See Other\r\n"; 
-	private static final String _304_NOT_MODIFIED 		 	= "HTTP/1.1 304 Not Modified\r\n"; 
-	private static final String _305_USE_PROXY 		 		= "HTTP/1.1 305 Use Proxy\r\n"; 
-	private static final String _307_TEMPORARY_REDIRECT 	= "HTTP/1.1 307 Temporary Redirect\r\n"; 
-	private static final String _400_BAD_REQUEST			= "HTTP/1.1 400 Bad Request\r\n"; 
-	private static final String _401_UNAUTHORIZED			= "HTTP/1.1 401 Unauthorized\r\n"; 
-	private static final String _403_FORBIDDEN 			 	= "HTTP/1.1 403 Forbidden\r\n"; 
-	private static final String _404_NOT_FOUND 	 			= "HTTP/1.1 404 Not Found\r\n"; 
-	private static final String _405_METHOD_NOT_ALLOWED 	= "HTTP/1.1 405 Method Not Allowed\r\n"; 
-	private static final String _406_NOT_ACCEPTABLE		 	= "HTTP/1.1 406 Not Acceptable\r\n"; 
-	private static final String _407_PROXY_AUTH_REQUIRED	= "HTTP/1.1 407 Proxy Authentication Required\r\n"; 
-	private static final String _408_REQUEST_TIMEOUT		= "HTTP/1.1 408 Request Timeout\r\n"; 
-	private static final String _409_CONFLICT				= "HTTP/1.1 409 Conflict\r\n"; 
-	private static final String _410_GONE					= "HTTP/1.1 410 Gone\r\n"; 
-	private static final String _411_LENGTH_REQUIRED		= "HTTP/1.1 411 Length Required\r\n"; 
-	private static final String _412_PRECONDITION_FAILED	= "HTTP/1.1 412 Precondition Failed\r\n"; 
-	private static final String _413_REQUEST_ENTITY_LARGE	= "HTTP/1.1 413 Request Entity Too Large\r\n"; 
-	private static final String _414_REQUEST_URI_TOO_LONG	= "HTTP/1.1 414 Request-URI Too Long\r\n"; 
-	private static final String _415_UNSUPPORTED_MEDIA_TYPE	= "HTTP/1.1 415 Unsupported Media Type\r\n"; 
-	private static final String _416_REQUEST_RANGE_NOT_SAT	= "HTTP/1.1 416 Requested Range Not Satisfiable\r\n"; 
-	private static final String _417_EXPECTATION_FAILED		= "HTTP/1.1 417 Expectation Failed\r\n"; 
-	private static final String _500_INTERNAL_SERVER_ERROR	= "HTTP/1.1 500 Internal Server Error\r\n"; 
-	private static final String _501_NOT_IMPLEMENTED		= "HTTP/1.1 501 Not Implemented\r\n"; 
-	private static final String _502_BAD_GATEWAY			= "HTTP/1.1 502 Bad Gateway\r\n"; 
-	private static final String _503_SERVICE_UNAVAILABLE	= "HTTP/1.1 503 Service Unavailable\r\n"; 
-	private static final String _504_GATEWAY_TIMEOUT		= "HTTP/1.1 504 Gateway Timeout\r\n"; 
-	private static final String _505_VERSION_NOT_SUPPORTED	= "HTTP/1.1 505 HTTP Version Not Supported\r\n"; 
-
-	// e.g. HTTP/1.0 200 OK or HTTP/1.0 404 Not Found (HTTP version + response status code + reason phrase)
-	public static String createInitialLine(int statusCode) {
-
-		switch (statusCode) {
-		case 200:
-			return _200_OK;
-		case 201:
-			return _201_CREATED;
-		case 202:
-			return _202_ACCEPTED;
-		case 203:
-			return _203_NON_AUTHORITATIVE_INFO;
-		case 204:
-			return _204_NO_CONTENT;
-		case 205:
-			return _205_RESET_CONTENT;
-		case 206:
-			return _206_PARTIAL_CONTENT;
-		case 300:
-			return _300_MULTIPLE_CHOICES;
-		case 301:
-			return _301_MOVED_PERMANENTLY;
-		case 302:
-			return _302_NOT_FOUND;
-		case 303:
-			return _303_SEE_OTHER;
-		case 304:
-			return _304_NOT_MODIFIED;
-		case 305:
-			return _305_USE_PROXY;
-		case 307:
-			return _307_TEMPORARY_REDIRECT;
-		case 400:
-			return _400_BAD_REQUEST;
-		case 401:
-			return _401_UNAUTHORIZED;
-		case 403:
-			return _403_FORBIDDEN;
-		case 404:
-			return _404_NOT_FOUND;
-		case 405:
-			return _405_METHOD_NOT_ALLOWED;
-		case 406:
-			return _406_NOT_ACCEPTABLE;
-		case 407:
-			return _407_PROXY_AUTH_REQUIRED;
-		case 408:
-			return _408_REQUEST_TIMEOUT;
-		case 409:
-			return _409_CONFLICT;
-		case 410:
-			return _410_GONE;
-		case 411:
-			return _411_LENGTH_REQUIRED;
-		case 412:
-			return _412_PRECONDITION_FAILED;
-		case 413:
-			return _413_REQUEST_ENTITY_LARGE;
-		case 414:
-			return _414_REQUEST_URI_TOO_LONG;
-		case 415:
-			return _415_UNSUPPORTED_MEDIA_TYPE;
-		case 416:
-			return _416_REQUEST_RANGE_NOT_SAT;
-		case 417:
-			return _417_EXPECTATION_FAILED;
-		case 500:
-			return _500_INTERNAL_SERVER_ERROR;
-		case 501:
-			return _501_NOT_IMPLEMENTED;
-		case 502:
-			return _502_BAD_GATEWAY;
-		case 503:
-			return _503_SERVICE_UNAVAILABLE;
-		case 504:
-			return _504_GATEWAY_TIMEOUT;
-		case 505:
-			return _505_VERSION_NOT_SUPPORTED;
-		default:
-			logger.error("Uknonwn Http status code: " + statusCode);
-			throw new IllegalArgumentException("Unknow Http status code: " + statusCode);
-		}
-	}
-
-
-	public static boolean verifyRequest(HttpRequest request) {
-		String version = request.getVersion();
-		boolean requestOk = true;
-		if (version.equals("HTTP/1.1")) { //TODO might be optimized? Could do version.endsWith("1"), or similar
-			requestOk =  (request.getHeader("host") != null);
-		}
-
-		return requestOk;
-	}
-
-
-	public static String getEtag(byte[] bytes) {
-		if (md.get() == null) {
-			try {
-				md.set(MessageDigest.getInstance("MD5"));
-			} catch (NoSuchAlgorithmException e) {
-				throw new RuntimeException("MD5 cryptographic algorithm is not available.", e);
-			}
-		}
-		byte[] digest = md.get().digest(bytes);
-		BigInteger number = new BigInteger(1, digest);
-		return '0' + number.toString(16);	// prepend a '0' to get a proper MD5 hash 
-	}
-
-
-	public static String getEtag(File file) {
-		//	TODO RS 101011 Implement if etag response header should be present while static file serving.
-		return "";
-	}
+    private static final Logger logger = LoggerFactory.getLogger(HttpUtil.class);
 
+    /*
+     * MessageDigest are not thread-safe and are expensive to create. Do it
+     * lazily for each thread that need access to one.
+     */
+    private static final ThreadLocal<MessageDigest> md = new ThreadLocal<MessageDigest>();
+
+    private static final String _200_OK = "HTTP/1.1 200 OK\r\n";
+    private static final String _201_CREATED = "HTTP/1.1 201 Created\r\n";
+    private static final String _202_ACCEPTED = "HTTP/1.1 202 Accepted\r\n";
+    private static final String _203_NON_AUTHORITATIVE_INFO = "HTTP/1.1 203 Non-Authoritative Information\r\n";
+    private static final String _204_NO_CONTENT = "HTTP/1.1 204 No Content\r\n";
+    private static final String _205_RESET_CONTENT = "HTTP/1.1 205 Reset Content\r\n";
+    private static final String _206_PARTIAL_CONTENT = "HTTP/1.1 206 Partial Content\r\n";
+    private static final String _300_MULTIPLE_CHOICES = "HTTP/1.1 300 Multiple Choices\r\n";
+    private static final String _301_MOVED_PERMANENTLY = "HTTP/1.1 301 Moved Permanently\r\n";
+    private static final String _302_NOT_FOUND = "HTTP/1.1 302 Found\r\n";
+    private static final String _303_SEE_OTHER = "HTTP/1.1 303 See Other\r\n";
+    private static final String _304_NOT_MODIFIED = "HTTP/1.1 304 Not Modified\r\n";
+    private static final String _305_USE_PROXY = "HTTP/1.1 305 Use Proxy\r\n";
+    private static final String _307_TEMPORARY_REDIRECT = "HTTP/1.1 307 Temporary Redirect\r\n";
+    private static final String _400_BAD_REQUEST = "HTTP/1.1 400 Bad Request\r\n";
+    private static final String _401_UNAUTHORIZED = "HTTP/1.1 401 Unauthorized\r\n";
+    private static final String _403_FORBIDDEN = "HTTP/1.1 403 Forbidden\r\n";
+    private static final String _404_NOT_FOUND = "HTTP/1.1 404 Not Found\r\n";
+    private static final String _405_METHOD_NOT_ALLOWED = "HTTP/1.1 405 Method Not Allowed\r\n";
+    private static final String _406_NOT_ACCEPTABLE = "HTTP/1.1 406 Not Acceptable\r\n";
+    private static final String _407_PROXY_AUTH_REQUIRED = "HTTP/1.1 407 Proxy Authentication Required\r\n";
+    private static final String _408_REQUEST_TIMEOUT = "HTTP/1.1 408 Request Timeout\r\n";
+    private static final String _409_CONFLICT = "HTTP/1.1 409 Conflict\r\n";
+    private static final String _410_GONE = "HTTP/1.1 410 Gone\r\n";
+    private static final String _411_LENGTH_REQUIRED = "HTTP/1.1 411 Length Required\r\n";
+    private static final String _412_PRECONDITION_FAILED = "HTTP/1.1 412 Precondition Failed\r\n";
+    private static final String _413_REQUEST_ENTITY_LARGE = "HTTP/1.1 413 Request Entity Too Large\r\n";
+    private static final String _414_REQUEST_URI_TOO_LONG = "HTTP/1.1 414 Request-URI Too Long\r\n";
+    private static final String _415_UNSUPPORTED_MEDIA_TYPE = "HTTP/1.1 415 Unsupported Media Type\r\n";
+    private static final String _416_REQUEST_RANGE_NOT_SAT = "HTTP/1.1 416 Requested Range Not Satisfiable\r\n";
+    private static final String _417_EXPECTATION_FAILED = "HTTP/1.1 417 Expectation Failed\r\n";
+    private static final String _500_INTERNAL_SERVER_ERROR = "HTTP/1.1 500 Internal Server Error\r\n";
+    private static final String _501_NOT_IMPLEMENTED = "HTTP/1.1 501 Not Implemented\r\n";
+    private static final String _502_BAD_GATEWAY = "HTTP/1.1 502 Bad Gateway\r\n";
+    private static final String _503_SERVICE_UNAVAILABLE = "HTTP/1.1 503 Service Unavailable\r\n";
+    private static final String _504_GATEWAY_TIMEOUT = "HTTP/1.1 504 Gateway Timeout\r\n";
+    private static final String _505_VERSION_NOT_SUPPORTED = "HTTP/1.1 505 HTTP Version Not Supported\r\n";
+
+    // e.g. HTTP/1.0 200 OK or HTTP/1.0 404 Not Found (HTTP version + response
+    // status code + reason phrase)
+    public static String createInitialLine(int statusCode) {
+
+        switch (statusCode) {
+        case 200:
+            return _200_OK;
+        case 201:
+            return _201_CREATED;
+        case 202:
+            return _202_ACCEPTED;
+        case 203:
+            return _203_NON_AUTHORITATIVE_INFO;
+        case 204:
+            return _204_NO_CONTENT;
+        case 205:
+            return _205_RESET_CONTENT;
+        case 206:
+            return _206_PARTIAL_CONTENT;
+        case 300:
+            return _300_MULTIPLE_CHOICES;
+        case 301:
+            return _301_MOVED_PERMANENTLY;
+        case 302:
+            return _302_NOT_FOUND;
+        case 303:
+            return _303_SEE_OTHER;
+        case 304:
+            return _304_NOT_MODIFIED;
+        case 305:
+            return _305_USE_PROXY;
+        case 307:
+            return _307_TEMPORARY_REDIRECT;
+        case 400:
+            return _400_BAD_REQUEST;
+        case 401:
+            return _401_UNAUTHORIZED;
+        case 403:
+            return _403_FORBIDDEN;
+        case 404:
+            return _404_NOT_FOUND;
+        case 405:
+            return _405_METHOD_NOT_ALLOWED;
+        case 406:
+            return _406_NOT_ACCEPTABLE;
+        case 407:
+            return _407_PROXY_AUTH_REQUIRED;
+        case 408:
+            return _408_REQUEST_TIMEOUT;
+        case 409:
+            return _409_CONFLICT;
+        case 410:
+            return _410_GONE;
+        case 411:
+            return _411_LENGTH_REQUIRED;
+        case 412:
+            return _412_PRECONDITION_FAILED;
+        case 413:
+            return _413_REQUEST_ENTITY_LARGE;
+        case 414:
+            return _414_REQUEST_URI_TOO_LONG;
+        case 415:
+            return _415_UNSUPPORTED_MEDIA_TYPE;
+        case 416:
+            return _416_REQUEST_RANGE_NOT_SAT;
+        case 417:
+            return _417_EXPECTATION_FAILED;
+        case 500:
+            return _500_INTERNAL_SERVER_ERROR;
+        case 501:
+            return _501_NOT_IMPLEMENTED;
+        case 502:
+            return _502_BAD_GATEWAY;
+        case 503:
+            return _503_SERVICE_UNAVAILABLE;
+        case 504:
+            return _504_GATEWAY_TIMEOUT;
+        case 505:
+            return _505_VERSION_NOT_SUPPORTED;
+        default:
+            logger.error("Uknonwn Http status code: " + statusCode);
+            throw new IllegalArgumentException("Unknow Http status code: " + statusCode);
+        }
+    }
+
+    public static boolean verifyRequest(HttpRequest request) {
+        String version = request.getVersion();
+        boolean requestOk = true;
+        if (version.equals("HTTP/1.1")) { // TODO might be optimized? Could do
+            // version.endsWith("1"), or similar
+            requestOk = request.getHeader("host") != null;
+        }
+
+        return requestOk;
+    }
+
+    public static String getEtag(byte[] bytes) {
+        if (md.get() == null) {
+            try {
+                md.set(MessageDigest.getInstance("MD5"));
+            } catch (NoSuchAlgorithmException e) {
+                throw new RuntimeException("MD5 cryptographic algorithm is not available.", e);
+            }
+        }
+        byte[] digest = md.get().digest(bytes);
+        BigInteger number = new BigInteger(1, digest);
+        // prepend a '0' to get a proper MD5 hash
+        return '0' + number.toString(16);
+
+    }
+
+    public static String getEtag(File file) {
+        // TODO RS 101011 Implement if etag response header should be present
+        // while static file serving.
+        return "";
+    }
 }

Modified: incubator/deft/sandbox/src/main/java/org/deftserver/web/Application.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/web/Application.java?rev=1153593&r1=1153592&r2=1153593&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/web/Application.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/web/Application.java Wed Aug  3 17:36:25 2011
@@ -33,119 +33,128 @@ import org.deftserver.web.http.HttpReque
 import com.google.common.collect.ImmutableMap;
 
 public class Application {
-	
-	/**
-	 * "Normal/Absolute" (non group capturing) RequestHandlers
-	 * e.g. "/", "/persons"
-	 */
-	private final ImmutableMap<String, RequestHandler> absoluteHandlers;
-
-	/**
-	 * Group capturing RequestHandlers
-	 * e.g. "/persons/([0-9]+)", "/persons/(\\d{1,3})"  
-	 */
-	private final ImmutableMap<String, RequestHandler> capturingHandlers;
-	
-	/**
-	 * A mapping between group capturing RequestHandlers and their corresponding pattern ( e.g. "([0-9]+)" )
-	 */
-	private final ImmutableMap<RequestHandler, Pattern> patterns;
-	
-	/**
-	 * The directory where static content (files) will be served from.
-	 */
-	private String staticContentDir;
-	
-	public Application(Map<String, RequestHandler> handlers) {
-		ImmutableMap.Builder<String, RequestHandler> builder = new ImmutableMap.Builder<String, RequestHandler>();
-		ImmutableMap.Builder<String, RequestHandler> capturingBuilder = new ImmutableMap.Builder<String, RequestHandler>();
-		ImmutableMap.Builder<RequestHandler, Pattern> patternsBuilder = new ImmutableMap.Builder<RequestHandler, Pattern>();
-
-		for (String path : handlers.keySet()) {
-			int index = path.lastIndexOf("/");
-			String group = path.substring(index+1, path.length());
-			if (containsCapturingGroup(group)) {
-				// path ends with capturing group, e.g path == "/person/([0-9]+)"
-				capturingBuilder.put(path.substring(0, index+1), handlers.get(path));
-				patternsBuilder.put(handlers.get(path), Pattern.compile(group));
-			} else {
-				// "normal" path, e.g. path == "/"
-				builder.put(path, handlers.get(path));
-			}
-		}
-		absoluteHandlers = builder.build();
-		capturingHandlers = capturingBuilder.build();
-		patterns = patternsBuilder.build();
-	}
-
-	/**
-	 * 
-	 * @param path Requested path
-	 * @return Returns the {@link RequestHandler} associated with the given path. If no mapping exists a 
-	 * {@link NotFoundRequestHandler} is returned.
-	 */
-	private RequestHandler getHandler(String path) {
-		RequestHandler rh = absoluteHandlers.get(path);
-		if (rh == null) {
-			// path could contain capturing groups which we could have a handler associated with.
-			rh = getCapturingHandler(path);
-			if (rh == null) {
-				// path could be prefixed with the 'static content directory'
-				rh = getStaticContentHandler(path);
-			}
-		} 
-		return rh != null ? rh : NotFoundRequestHandler.getInstance();	// TODO RS store in a final field for improved performance?
-	}
-	
-	public RequestHandler getHandler(HttpRequest request) {
-		if (!HttpUtil.verifyRequest(request)) {
-			return BadRequestRequestHandler.getInstance(); 
-		}
-		// if @Authenticated annotation is present, make sure that the request/user is authenticated 
-		// (i.e RequestHandler.getCurrentUser() != null).
-		RequestHandler rh = getHandler(request.getRequestedPath());;
-		if (rh.isMethodAuthenticated(request.getMethod()) && rh.getCurrentUser(request) == null) {
-			return ForbiddenRequestHandler.getInstance();
-		}
-		return rh;
-	}
-	
-	private boolean containsCapturingGroup(String group) {
-		boolean containsGroup =  group.matches("^\\(.*\\)$");
-		Pattern.compile(group);	// throws PatternSyntaxException if group is malformed regular expression
-		return containsGroup;
-	}
-	
-	private RequestHandler getCapturingHandler(String path) {
-		int index = path.lastIndexOf("/");
-		if (index != -1) {
-			String init = path.substring(0, index+1);	// path without its last segment
-			String group = path.substring(index+1, path.length()); 
-			RequestHandler handler = capturingHandlers.get(init);
-			if (handler != null) {
-				Pattern regex = patterns.get(handler);
-				if (regex.matcher(group).matches()) {
-					return handler;
-				}
-			}
-		}
-		return null;
-	}
-	
-	private RequestHandler getStaticContentHandler(String path) {
-		if (staticContentDir == null || path.length() <= staticContentDir.length()) {
-			return null;	// quick reject (no static dir or simple contradiction)
-		}
-		
-		if (path.substring(1).startsWith(staticContentDir)) {
-			return StaticContentHandler.getInstance();
-		} else {
-			return null;
-		}
-	}
-	
-	public void setStaticContentDir(String scd) {
-		this.staticContentDir = scd;
-	}
 
+    /**
+     * "Normal/Absolute" (non group capturing) RequestHandlers e.g. "/",
+     * "/persons"
+     */
+    private final ImmutableMap<String, RequestHandler> absoluteHandlers;
+
+    /**
+     * Group capturing RequestHandlers e.g. "/persons/([0-9]+)",
+     * "/persons/(\\d{1,3})"
+     */
+    private final ImmutableMap<String, RequestHandler> capturingHandlers;
+
+    /**
+     * A mapping between group capturing RequestHandlers and their corresponding
+     * pattern ( e.g. "([0-9]+)" )
+     */
+    private final ImmutableMap<RequestHandler, Pattern> patterns;
+
+    /**
+     * The directory where static content (files) will be served from.
+     */
+    private String staticContentDir;
+
+    public Application(Map<String, RequestHandler> handlers) {
+        ImmutableMap.Builder<String, RequestHandler> builder = new ImmutableMap.Builder<String, RequestHandler>();
+        ImmutableMap.Builder<String, RequestHandler> capturingBuilder = new ImmutableMap.Builder<String, RequestHandler>();
+        ImmutableMap.Builder<RequestHandler, Pattern> patternsBuilder = new ImmutableMap.Builder<RequestHandler, Pattern>();
+
+        for (String path : handlers.keySet()) {
+            int index = path.lastIndexOf("/");
+            String group = path.substring(index + 1, path.length());
+            if (containsCapturingGroup(group)) {
+                // path ends with capturing group, e.g path ==
+                // "/person/([0-9]+)"
+                capturingBuilder.put(path.substring(0, index + 1), handlers.get(path));
+                patternsBuilder.put(handlers.get(path), Pattern.compile(group));
+            } else {
+                // "normal" path, e.g. path == "/"
+                builder.put(path, handlers.get(path));
+            }
+        }
+        absoluteHandlers = builder.build();
+        capturingHandlers = capturingBuilder.build();
+        patterns = patternsBuilder.build();
+    }
+
+    /**
+     * 
+     * @param path Requested path
+     * @return Returns the {@link RequestHandler} associated with the given
+     *         path. If no mapping exists a {@link NotFoundRequestHandler} is
+     *         returned.
+     */
+    private RequestHandler getHandler(String path) {
+        RequestHandler rh = absoluteHandlers.get(path);
+        if (rh == null) {
+            // path could contain capturing groups which we could have a handler
+            // associated with.
+            rh = getCapturingHandler(path);
+            if (rh == null) {
+                // path could be prefixed with the 'static content directory'
+                rh = getStaticContentHandler(path);
+            }
+        }
+
+        // TODO RS store in a final field for improved performance?
+        return rh != null ? rh : NotFoundRequestHandler.getInstance();
+    }
+
+    public RequestHandler getHandler(HttpRequest request) {
+        if (!HttpUtil.verifyRequest(request)) {
+            return BadRequestRequestHandler.getInstance();
+        }
+        // if @Authenticated annotation is present, make sure that the
+        // request/user is authenticated
+        // (i.e RequestHandler.getCurrentUser() != null).
+        RequestHandler rh = getHandler(request.getRequestedPath());
+        ;
+        if (rh.isMethodAuthenticated(request.getMethod()) && rh.getCurrentUser(request) == null) {
+            return ForbiddenRequestHandler.getInstance();
+        }
+        return rh;
+    }
+
+    private boolean containsCapturingGroup(String group) {
+        boolean containsGroup = group.matches("^\\(.*\\)$");
+        Pattern.compile(group); // throws PatternSyntaxException if group is
+        // malformed regular expression
+        return containsGroup;
+    }
+
+    private RequestHandler getCapturingHandler(String path) {
+        int index = path.lastIndexOf("/");
+        if (index != -1) {
+            String init = path.substring(0, index + 1); // path without its last
+            // segment
+            String group = path.substring(index + 1, path.length());
+            RequestHandler handler = capturingHandlers.get(init);
+            if (handler != null) {
+                Pattern regex = patterns.get(handler);
+                if (regex.matcher(group).matches()) {
+                    return handler;
+                }
+            }
+        }
+        return null;
+    }
+
+    private RequestHandler getStaticContentHandler(String path) {
+        if (staticContentDir == null || path.length() <= staticContentDir.length()) {
+            return null; // quick reject (no static dir or simple contradiction)
+        }
+
+        if (path.substring(1).startsWith(staticContentDir)) {
+            return StaticContentHandler.getInstance();
+        } else {
+            return null;
+        }
+    }
+
+    public void setStaticContentDir(String scd) {
+        staticContentDir = scd;
+    }
 }

Modified: incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/BadRequestRequestHandler.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/BadRequestRequestHandler.java?rev=1153593&r1=1153592&r2=1153593&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/BadRequestRequestHandler.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/BadRequestRequestHandler.java Wed Aug  3 17:36:25 2011
@@ -24,18 +24,19 @@ import org.deftserver.web.http.HttpRespo
 
 public class BadRequestRequestHandler extends RequestHandler {
 
-private final static BadRequestRequestHandler instance = new BadRequestRequestHandler();
-	
-	private BadRequestRequestHandler() { }
-	
-	public static final BadRequestRequestHandler getInstance() {
-		return instance;
-	}
-	
-	@Override
-	public void get(HttpRequest request, HttpResponse response) {
-		response.setStatusCode(400);
-		response.setHeader("Connection", "close");
-		response.write("HTTP 1.1 requests must include the Host: header");
-	}
+    private final static BadRequestRequestHandler instance = new BadRequestRequestHandler();
+
+    private BadRequestRequestHandler() {
+    }
+
+    public static final BadRequestRequestHandler getInstance() {
+        return instance;
+    }
+
+    @Override
+    public void get(HttpRequest request, HttpResponse response) {
+        response.setStatusCode(400);
+        response.setHeader("Connection", "close");
+        response.write("HTTP 1.1 requests must include the Host: header");
+    }
 }

Modified: incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/ForbiddenRequestHandler.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/ForbiddenRequestHandler.java?rev=1153593&r1=1153592&r2=1153593&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/ForbiddenRequestHandler.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/ForbiddenRequestHandler.java Wed Aug  3 17:36:25 2011
@@ -24,18 +24,19 @@ import org.deftserver.web.http.HttpRespo
 
 public class ForbiddenRequestHandler extends RequestHandler {
 
-private final static ForbiddenRequestHandler instance = new ForbiddenRequestHandler();
-	
-	private ForbiddenRequestHandler() { }
-	
-	public static final ForbiddenRequestHandler getInstance() {
-		return instance;
-	}
-	
-	@Override
-	public void get(HttpRequest request, HttpResponse response) {
-		response.setStatusCode(403);
-		response.setHeader("Connection", "close");
-		response.write("Authentication failed");
-	}
-}
\ No newline at end of file
+    private final static ForbiddenRequestHandler instance = new ForbiddenRequestHandler();
+
+    private ForbiddenRequestHandler() {
+    }
+
+    public static final ForbiddenRequestHandler getInstance() {
+        return instance;
+    }
+
+    @Override
+    public void get(HttpRequest request, HttpResponse response) {
+        response.setStatusCode(403);
+        response.setHeader("Connection", "close");
+        response.write("Authentication failed");
+    }
+}

Modified: incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/NotFoundRequestHandler.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/NotFoundRequestHandler.java?rev=1153593&r1=1153592&r2=1153593&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/NotFoundRequestHandler.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/NotFoundRequestHandler.java Wed Aug  3 17:36:25 2011
@@ -22,21 +22,21 @@ package org.deftserver.web.handler;
 import org.deftserver.web.http.HttpRequest;
 import org.deftserver.web.http.HttpResponse;
 
-
 public class NotFoundRequestHandler extends RequestHandler {
 
-	private final static NotFoundRequestHandler instance = new NotFoundRequestHandler();
-	
-	private NotFoundRequestHandler() { }
-	
-	public static final NotFoundRequestHandler getInstance() {
-		return instance;
-	}
-	
-	@Override
-	public void get(HttpRequest request, HttpResponse response) {
-		response.setStatusCode(404);
-		response.setHeader("Connection", "close");
-		response.write("Requested URL: " + request.getRequestedPath() + " was not found");
-	}
+    private final static NotFoundRequestHandler instance = new NotFoundRequestHandler();
+
+    private NotFoundRequestHandler() {
+    }
+
+    public static final NotFoundRequestHandler getInstance() {
+        return instance;
+    }
+
+    @Override
+    public void get(HttpRequest request, HttpResponse response) {
+        response.setStatusCode(404);
+        response.setHeader("Connection", "close");
+        response.write("Requested URL: " + request.getRequestedPath() + " was not found");
+    }
 }

Modified: incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/RequestHandler.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/RequestHandler.java?rev=1153593&r1=1153592&r2=1153593&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/RequestHandler.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/RequestHandler.java Wed Aug  3 17:36:25 2011
@@ -33,65 +33,64 @@ import com.google.common.collect.Maps;
 
 public abstract class RequestHandler {
 
-	private final ImmutableMap<HttpVerb, Boolean> asynchVerbs;
-	private final ImmutableMap<HttpVerb, Boolean> authVerbs;
-
-	public RequestHandler() {
-		Map<HttpVerb, Boolean> asyncV = Maps.newHashMap();
-		Map<HttpVerb, Boolean> authV = Maps.newHashMap();
-		for (HttpVerb verb : HttpVerb.values()) {
-			authV.put(verb, isMethodAnnotated(verb, Authenticated.class));
-			asyncV.put(verb, isMethodAnnotated(verb, Asynchronous.class));
-		}
-		asynchVerbs = ImmutableMap.copyOf(asyncV);
-		authVerbs = ImmutableMap.copyOf(authV);
-	}
-
-	private boolean isMethodAnnotated(HttpVerb verb, Class<? extends Annotation> annotation) {
-		try {
-			Class<?>[] parameterTypes = {HttpRequest.class, HttpResponse.class};
-			return getClass().getMethod(verb.toString().toLowerCase(), parameterTypes).getAnnotation(annotation) != null; 
-		} catch (NoSuchMethodException nsme) {
-			return false;
-		}
-	}
-	
-	public boolean isMethodAsynchronous(HttpVerb verb) {
-		return asynchVerbs.get(verb);
-	}
-	
-	public boolean isMethodAuthenticated(HttpVerb verb) {
-		return authVerbs.get(verb);
-	}
-
-	//Default implementation of HttpMethods return a 501 page
-	public void get(HttpRequest request, HttpResponse response) {
-		response.setStatusCode(501);
-		response.write("");
-	}
-
-	public void post(HttpRequest request, HttpResponse response) { 
-		response.setStatusCode(501);
-		response.write("");
-	}
-
-	public void put(HttpRequest request, HttpResponse response) { 
-		response.setStatusCode(501);
-		response.write("");
-	}
-
-	public void delete(HttpRequest request, HttpResponse response) { 
-		response.setStatusCode(501);
-		response.write("");
-	}
-
-	public void head(HttpRequest request, HttpResponse response) { 
-		response.setStatusCode(501);
-		response.write("");
-	}
-	
-	public String getCurrentUser(HttpRequest request) { 
-		return null; 
-	}
+    private final ImmutableMap<HttpVerb, Boolean> asynchVerbs;
+    private final ImmutableMap<HttpVerb, Boolean> authVerbs;
 
+    public RequestHandler() {
+        Map<HttpVerb, Boolean> asyncV = Maps.newHashMap();
+        Map<HttpVerb, Boolean> authV = Maps.newHashMap();
+        for (HttpVerb verb : HttpVerb.values()) {
+            authV.put(verb, isMethodAnnotated(verb, Authenticated.class));
+            asyncV.put(verb, isMethodAnnotated(verb, Asynchronous.class));
+        }
+        asynchVerbs = ImmutableMap.copyOf(asyncV);
+        authVerbs = ImmutableMap.copyOf(authV);
+    }
+
+    private boolean isMethodAnnotated(HttpVerb verb, Class<? extends Annotation> annotation) {
+        try {
+            Class<?>[] parameterTypes = { HttpRequest.class, HttpResponse.class };
+            return getClass().getMethod(verb.toString().toLowerCase(), parameterTypes).getAnnotation(annotation) != null;
+        } catch (NoSuchMethodException nsme) {
+            return false;
+        }
+    }
+
+    public boolean isMethodAsynchronous(HttpVerb verb) {
+        return asynchVerbs.get(verb);
+    }
+
+    public boolean isMethodAuthenticated(HttpVerb verb) {
+        return authVerbs.get(verb);
+    }
+
+    // Default implementation of HttpMethods return a 501 page
+    public void get(HttpRequest request, HttpResponse response) {
+        response.setStatusCode(501);
+        response.write("");
+    }
+
+    public void post(HttpRequest request, HttpResponse response) {
+        response.setStatusCode(501);
+        response.write("");
+    }
+
+    public void put(HttpRequest request, HttpResponse response) {
+        response.setStatusCode(501);
+        response.write("");
+    }
+
+    public void delete(HttpRequest request, HttpResponse response) {
+        response.setStatusCode(501);
+        response.write("");
+    }
+
+    public void head(HttpRequest request, HttpResponse response) {
+        response.setStatusCode(501);
+        response.write("");
+    }
+
+    public String getCurrentUser(HttpRequest request) {
+        return null;
+    }
 }

Modified: incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/StaticContentHandler.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/StaticContentHandler.java?rev=1153593&r1=1153592&r2=1153593&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/StaticContentHandler.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/web/handler/StaticContentHandler.java Wed Aug  3 17:36:25 2011
@@ -27,7 +27,6 @@ package org.deftserver.web.handler;
 import java.io.File;
 
 import javax.activation.FileTypeMap;
-import javax.activation.MimetypesFileTypeMap;
 
 import org.deftserver.util.DateUtil;
 import org.deftserver.web.http.HttpException;
@@ -37,71 +36,73 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- *	A RequestHandler that serves static content (files) from a predefined directory. 
- *
- *	"Cache-Control: public" indicates that the response MAY be cached by any cache, even if it would normally be 
- *  non-cacheable or cacheable only within a non- shared cache.
- *
+ * A RequestHandler that serves static content (files) from a predefined
+ * directory.
+ * 
+ * "Cache-Control: public" indicates that the response MAY be cached by any
+ * cache, even if it would normally be non-cacheable or cacheable only within a
+ * non- shared cache.
  */
 public class StaticContentHandler extends RequestHandler {
 
-	private final static Logger logger = LoggerFactory.getLogger(StaticContentHandler.class);
+    private final static Logger logger = LoggerFactory.getLogger(StaticContentHandler.class);
 
-	private final static StaticContentHandler instance = new StaticContentHandler();
+    private final static StaticContentHandler instance = new StaticContentHandler();
 
-	private final FileTypeMap mimeTypeMap =  MimetypesFileTypeMap.getDefaultFileTypeMap();
+    private final FileTypeMap mimeTypeMap = FileTypeMap.getDefaultFileTypeMap();
 
-	public static StaticContentHandler getInstance() {
-		return instance;
-	}
-
-	/** {inheritDoc} */
-	@Override
-	public void get(HttpRequest request, HttpResponse response) {
-		this.perform(request, response, true);
-	}
-	
-	/** {inheritDoc} */
-	@Override
-	public void head(final HttpRequest request, final HttpResponse response) {
-		this.perform(request, response, false);
-	}
-
-	/**
-	 * @param request the <code>HttpRequest</code>
-	 * @param response the <code>HttpResponse</code> 
-	 * @param hasBody <code>true</code> to write the message body; <code>false</code> otherwise.
-	 */
-	private void perform(final HttpRequest request, final HttpResponse response, boolean hasBody) {
-		
-		final String path = request.getRequestedPath();
-		final File file = new File(path.substring(1));	// remove the leading '/'
-		if (!file.exists()) {
-			throw new HttpException(404);
-		} else if (!file.isFile()) {
-			throw new HttpException(403, path + "is not a file");
-		}
-
-		final long lastModified = file.lastModified();
-		response.setHeader("Last-Modified", DateUtil.parseToRFC1123(lastModified));
-		response.setHeader("Cache-Control", "public");
-		String mimeType = mimeTypeMap.getContentType(file);
-		if ("text/plain".equals(mimeType)) {
-			mimeType += "; charset=utf-8";
-		}
-		response.setHeader("Content-Type", mimeType);
-		final String ifModifiedSince = request.getHeader("If-Modified-Since");
-		if (ifModifiedSince != null) {
-			final long ims = DateUtil.parseToMilliseconds(ifModifiedSince);
-			if (lastModified <= ims) {
-				response.setStatusCode(304);	//Not Modified
-				logger.debug("not modified");
-				return;
-			}
-		}
-		
-		if(hasBody) {
-			response.write(file);
-		}
-	}
+    public static StaticContentHandler getInstance() {
+        return instance;
+    }
+
+    /** {inheritDoc} */
+    @Override
+    public void get(HttpRequest request, HttpResponse response) {
+        perform(request, response, true);
+    }
+
+    /** {inheritDoc} */
+    @Override
+    public void head(final HttpRequest request, final HttpResponse response) {
+        perform(request, response, false);
+    }
+
+    /**
+     * @param request the <code>HttpRequest</code>
+     * @param response the <code>HttpResponse</code>
+     * @param hasBody <code>true</code> to write the message body;
+     *            <code>false</code> otherwise.
+     */
+    private void perform(final HttpRequest request, final HttpResponse response, boolean hasBody) {
+
+        final String path = request.getRequestedPath();
+        final File file = new File(path.substring(1)); // remove the leading '/'
+        if (!file.exists()) {
+            throw new HttpException(404);
+        } else if (!file.isFile()) {
+            throw new HttpException(403, path + "is not a file");
+        }
+
+        final long lastModified = file.lastModified();
+        response.setHeader("Last-Modified", DateUtil.parseToRFC1123(lastModified));
+        response.setHeader("Cache-Control", "public");
+        String mimeType = mimeTypeMap.getContentType(file);
+        if ("text/plain".equals(mimeType)) {
+            mimeType += "; charset=utf-8";
+        }
+        response.setHeader("Content-Type", mimeType);
+        final String ifModifiedSince = request.getHeader("If-Modified-Since");
+        if (ifModifiedSince != null) {
+            final long ims = DateUtil.parseToMilliseconds(ifModifiedSince);
+            if (lastModified <= ims) {
+                response.setStatusCode(304); // Not Modified
+                logger.debug("not modified");
+                return;
+            }
+        }
+
+        if (hasBody) {
+            response.write(file);
+        }
+    }
 }

Modified: incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpProtocol.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpProtocol.java?rev=1153593&r1=1153592&r2=1153593&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpProtocol.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpProtocol.java Wed Aug  3 17:36:25 2011
@@ -45,201 +45,204 @@ import org.slf4j.LoggerFactory;
 import com.google.common.collect.Maps;
 
 public class HttpProtocol implements IOHandler {
-	
-	private final static Logger logger = LoggerFactory.getLogger(HttpProtocol.class);
 
-	private final IOLoop ioLoop;
-	private final Application application;
+    private final static Logger logger = LoggerFactory.getLogger(HttpProtocol.class);
 
-	
-	// a queue of half-baked (pending/unfinished) HTTP post request
-	private final Map<SelectableChannel, PartialHttpRequest> partials = Maps.newHashMap();
- 	
-	public HttpProtocol(Application app) {
-		this(IOLoop.INSTANCE, app);
-	}
-	
-	public HttpProtocol(IOLoop ioLoop, Application app) {
-		this.ioLoop = ioLoop;
-		application = app;
-	}
-	
-	@Override
-	public void handleAccept(SelectionKey key) throws IOException {
-		logger.debug("handle accept...");
-		SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();
-		if (clientChannel != null) {
-			// could be null in a multithreaded deft environment because another ioloop was "faster" to accept()
-			clientChannel.configureBlocking(false);
-			ioLoop.addHandler(clientChannel, this, SelectionKey.OP_READ, ByteBuffer.allocate(READ_BUFFER_SIZE));
-		}
-	}
-	
-	@Override
-	public void handleConnect(SelectionKey key) throws IOException {
-		logger.error("handle connect in HttpProcotol...");
-	}
-
-	@Override
-	public void handleRead(SelectionKey key) throws IOException {
-		logger.debug("handle read...");
-		SocketChannel clientChannel = (SocketChannel) key.channel();
-		HttpRequest request = getHttpRequest(key, clientChannel);
-		
-		if (request.isKeepAlive()) {
-			ioLoop.addKeepAliveTimeout(
-					clientChannel, 
-					Timeout.newKeepAliveTimeout(ioLoop, clientChannel, KEEP_ALIVE_TIMEOUT)
-			);
-		}
-		HttpResponse response = new HttpResponse(this, key, request.isKeepAlive());
-		RequestHandler rh = application.getHandler(request);
-		HttpRequestDispatcher.dispatch(rh, request, response);
-		
-		//Only close if not async. In that case its up to RH to close it (+ don't close if it's a partial request).
-		if (!rh.isMethodAsynchronous(request.getMethod()) && ! (request instanceof PartialHttpRequest)) {
-			response.finish();
-		}
-	}
-
-	@Override
-	public void handleWrite(SelectionKey key) {
-		logger.debug("handle write...");
-		SocketChannel channel = ((SocketChannel) key.channel());
-
-		if (key.attachment() instanceof MappedByteBuffer) {
-			writeMappedByteBuffer(key, channel);
-		} else if (key.attachment() instanceof DynamicByteBuffer) {
-			writeDynamicByteBuffer(key, channel);
-		}
-		if (ioLoop.hasKeepAliveTimeout(channel)) {
-			prolongKeepAliveTimeout(channel);
-		}
-
-	}
-
-	private void writeMappedByteBuffer(SelectionKey key, SocketChannel channel) {
-		MappedByteBuffer mbb = (MappedByteBuffer) key.attachment();
-		if (mbb.hasRemaining()) {
-			try {
-				channel.write(mbb);
-			} catch (IOException e) {
-				logger.error("Failed to send data to client: {}", e.getMessage());
-				Closeables.closeQuietly(channel);
-			}
-		}
-		if (!mbb.hasRemaining()) {
-			closeOrRegisterForRead(key);
-		}
-	}
-	
-	private void writeDynamicByteBuffer(SelectionKey key, SocketChannel channel) {
-		DynamicByteBuffer dbb = (DynamicByteBuffer) key.attachment();
-		logger.debug("pending data about to be written");
-		ByteBuffer toSend = dbb.getByteBuffer();
-		toSend.flip(); // prepare for write
-		long bytesWritten = 0;
-		try {
-			bytesWritten = channel.write(toSend);
-		} catch (IOException e) {
-			logger.error("Failed to send data to client: {}", e.getMessage());
-			Closeables.closeQuietly(channel);
-		}
-		logger.debug("sent {} bytes to wire", bytesWritten);
-		if (!toSend.hasRemaining()) {
-			logger.debug("sent all data in toSend buffer");
-			closeOrRegisterForRead(key); // should probably only be done if the HttpResponse is finished
-		} else {
-			toSend.compact(); // make room for more data be "read" in
-		}
-	}
-
-	public void closeOrRegisterForRead(SelectionKey key) {
-		if (key.isValid() && ioLoop.hasKeepAliveTimeout(key.channel())) {
-			try {
-				key.channel().register(key.selector(), SelectionKey.OP_READ, reuseAttachment(key));
-				prolongKeepAliveTimeout(key.channel());
-				logger.debug("keep-alive connection. registrating for read.");
-			} catch (ClosedChannelException e) {
-				logger.debug("ClosedChannelException while registrating key for read: {}", e.getMessage());
-				Closeables.closeQuietly(ioLoop, key.channel());
-			}		
-		} else {
-			// http request should be finished and no 'keep-alive' => close connection
-			logger.debug("Closing finished (non keep-alive) http connection"); 
-			Closeables.closeQuietly(ioLoop, key.channel());
-		}
-	}
-	
-	public void prolongKeepAliveTimeout(SelectableChannel channel) {
-		ioLoop.addKeepAliveTimeout(
-				channel, 
-				Timeout.newKeepAliveTimeout(ioLoop, channel, KEEP_ALIVE_TIMEOUT)
-		);
-	}
-	
-	public IOLoop getIOLoop() {
-		return ioLoop;
-	}
-	
-	/**
-	 * Clears the buffer (prepares for reuse) attached to the given SelectionKey.
-	 * @return A cleared (position=0, limit=capacity) ByteBuffer which is ready for new reads
-	 */
-	private ByteBuffer reuseAttachment(SelectionKey key) {
-		Object o = key.attachment();
-		ByteBuffer attachment = null;
-		if (o instanceof MappedByteBuffer) {
-			attachment = ByteBuffer.allocate(READ_BUFFER_SIZE);
-		} else if (o instanceof DynamicByteBuffer) {
-			attachment = ((DynamicByteBuffer) o).getByteBuffer();
-		} else {
-			attachment = (ByteBuffer) o;
-		}
-
-		if (attachment.capacity() < READ_BUFFER_SIZE) {
-			attachment = ByteBuffer.allocate(READ_BUFFER_SIZE);
-		}
-		attachment.clear(); // prepare for reuse
-		return attachment;
-	}
-
-	private HttpRequest getHttpRequest(SelectionKey key, SocketChannel clientChannel) {
-		ByteBuffer buffer = (ByteBuffer) key.attachment();
-		try {
-			clientChannel.read(buffer);
-		} catch (IOException e) {
-			logger.warn("Could not read buffer: {}", e.getMessage());
-			Closeables.closeQuietly(ioLoop, clientChannel);
-		}
-		buffer.flip();
-		
-		return doGetHttpRequest(key, clientChannel, buffer);
-	}
-	
-	private HttpRequest doGetHttpRequest(SelectionKey key, SocketChannel clientChannel, ByteBuffer buffer) {
-		//do we have any unfinished http post requests for this channel?
-		HttpRequest request = null;
-		if (partials.containsKey(clientChannel)) {
-			request = HttpRequest.continueParsing(buffer, partials.get(clientChannel));
-			if (! (request instanceof PartialHttpRequest)) {	// received the entire payload/body
-				partials.remove(clientChannel);
-			}
-		} else {
-			request = HttpRequest.of(buffer);
-			if (request instanceof PartialHttpRequest) {
-				partials.put(key.channel(), (PartialHttpRequest) request);
-			}
-		}
-		//set extra request info
-		request.setRemoteHost(clientChannel.socket().getInetAddress());
-		request.setRemotePort(clientChannel.socket().getPort());
-		request.setServerHost(clientChannel.socket().getLocalAddress());
-		request.setServerPort(clientChannel.socket().getLocalPort());
-		return request;
-	}
-	
-	@Override
-	public String toString() { return "HttpProtocol"; }
-	
+    private final IOLoop ioLoop;
+    private final Application application;
+
+    // a queue of half-baked (pending/unfinished) HTTP post request
+    private final Map<SelectableChannel, PartialHttpRequest> partials = Maps.newHashMap();
+
+    public HttpProtocol(Application app) {
+        this(IOLoop.INSTANCE, app);
+    }
+
+    public HttpProtocol(IOLoop ioLoop, Application app) {
+        this.ioLoop = ioLoop;
+        application = app;
+    }
+
+    @Override
+    public void handleAccept(SelectionKey key) throws IOException {
+        logger.debug("handle accept...");
+        SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();
+        if (clientChannel != null) {
+            // could be null in a multithreaded deft environment because another
+            // ioloop was "faster" to accept()
+            clientChannel.configureBlocking(false);
+            ioLoop.addHandler(clientChannel, this, SelectionKey.OP_READ, ByteBuffer.allocate(READ_BUFFER_SIZE));
+        }
+    }
+
+    @Override
+    public void handleConnect(SelectionKey key) throws IOException {
+        logger.error("handle connect in HttpProcotol...");
+    }
+
+    @Override
+    public void handleRead(SelectionKey key) throws IOException {
+        logger.debug("handle read...");
+        SocketChannel clientChannel = (SocketChannel) key.channel();
+        HttpRequest request = getHttpRequest(key, clientChannel);
+
+        if (request.isKeepAlive()) {
+            ioLoop.addKeepAliveTimeout(clientChannel, Timeout.newKeepAliveTimeout(ioLoop, clientChannel,
+                    KEEP_ALIVE_TIMEOUT));
+        }
+        HttpResponse response = new HttpResponseImpl(this, key, request.isKeepAlive());
+        RequestHandler rh = application.getHandler(request);
+        HttpRequestDispatcher.dispatch(rh, request, response);
+
+        // Only close if not async. In that case its up to RH to close it (+
+        // don't close if it's a partial request).
+        if (!rh.isMethodAsynchronous(request.getMethod()) && !(request instanceof PartialHttpRequest)) {
+            response.finish();
+        }
+    }
+
+    @Override
+    public void handleWrite(SelectionKey key) {
+        logger.debug("handle write...");
+        SocketChannel channel = (SocketChannel) key.channel();
+
+        if (key.attachment() instanceof MappedByteBuffer) {
+            writeMappedByteBuffer(key, channel);
+        } else if (key.attachment() instanceof DynamicByteBuffer) {
+            writeDynamicByteBuffer(key, channel);
+        }
+        if (ioLoop.hasKeepAliveTimeout(channel)) {
+            prolongKeepAliveTimeout(channel);
+        }
+
+    }
+
+    private void writeMappedByteBuffer(SelectionKey key, SocketChannel channel) {
+        MappedByteBuffer mbb = (MappedByteBuffer) key.attachment();
+        if (mbb.hasRemaining()) {
+            try {
+                channel.write(mbb);
+            } catch (IOException e) {
+                logger.error("Failed to send data to client: {}", e.getMessage());
+                Closeables.closeQuietly(channel);
+            }
+        }
+        if (!mbb.hasRemaining()) {
+            closeOrRegisterForRead(key);
+        }
+    }
+
+    private void writeDynamicByteBuffer(SelectionKey key, SocketChannel channel) {
+        DynamicByteBuffer dbb = (DynamicByteBuffer) key.attachment();
+        logger.debug("pending data about to be written");
+        ByteBuffer toSend = dbb.getByteBuffer();
+        toSend.flip(); // prepare for write
+        long bytesWritten = 0;
+        try {
+            bytesWritten = channel.write(toSend);
+        } catch (IOException e) {
+            logger.error("Failed to send data to client: {}", e.getMessage());
+            Closeables.closeQuietly(channel);
+        }
+        logger.debug("sent {} bytes to wire", bytesWritten);
+        if (!toSend.hasRemaining()) {
+            logger.debug("sent all data in toSend buffer");
+            closeOrRegisterForRead(key); // should probably only be done if the
+            // HttpResponse is finished
+        } else {
+            toSend.compact(); // make room for more data be "read" in
+        }
+    }
+
+    public void closeOrRegisterForRead(SelectionKey key) {
+        if (key.isValid() && ioLoop.hasKeepAliveTimeout(key.channel())) {
+            try {
+                key.channel().register(key.selector(), SelectionKey.OP_READ, reuseAttachment(key));
+                prolongKeepAliveTimeout(key.channel());
+                logger.debug("keep-alive connection. registrating for read.");
+            } catch (ClosedChannelException e) {
+                logger.debug("ClosedChannelException while registrating key for read: {}", e.getMessage());
+                Closeables.closeQuietly(ioLoop, key.channel());
+            }
+        } else {
+            // http request should be finished and no 'keep-alive' => close
+            // connection
+            logger.debug("Closing finished (non keep-alive) http connection");
+            Closeables.closeQuietly(ioLoop, key.channel());
+        }
+    }
+
+    public void prolongKeepAliveTimeout(SelectableChannel channel) {
+        ioLoop.addKeepAliveTimeout(channel, Timeout.newKeepAliveTimeout(ioLoop, channel, KEEP_ALIVE_TIMEOUT));
+    }
+
+    public IOLoop getIOLoop() {
+        return ioLoop;
+    }
+
+    /**
+     * Clears the buffer (prepares for reuse) attached to the given
+     * SelectionKey.
+     * 
+     * @return A cleared (position=0, limit=capacity) ByteBuffer which is ready
+     *         for new reads
+     */
+    private ByteBuffer reuseAttachment(SelectionKey key) {
+        Object o = key.attachment();
+        ByteBuffer attachment = null;
+        if (o instanceof MappedByteBuffer) {
+            attachment = ByteBuffer.allocate(READ_BUFFER_SIZE);
+        } else if (o instanceof DynamicByteBuffer) {
+            attachment = ((DynamicByteBuffer) o).getByteBuffer();
+        } else {
+            attachment = (ByteBuffer) o;
+        }
+
+        if (attachment.capacity() < READ_BUFFER_SIZE) {
+            attachment = ByteBuffer.allocate(READ_BUFFER_SIZE);
+        }
+        attachment.clear(); // prepare for reuse
+        return attachment;
+    }
+
+    private HttpRequest getHttpRequest(SelectionKey key, SocketChannel clientChannel) {
+        ByteBuffer buffer = (ByteBuffer) key.attachment();
+        try {
+            clientChannel.read(buffer);
+        } catch (IOException e) {
+            logger.warn("Could not read buffer: {}", e.getMessage());
+            Closeables.closeQuietly(ioLoop, clientChannel);
+        }
+        buffer.flip();
+
+        return doGetHttpRequest(key, clientChannel, buffer);
+    }
+
+    private HttpRequest doGetHttpRequest(SelectionKey key, SocketChannel clientChannel, ByteBuffer buffer) {
+        // do we have any unfinished http post requests for this channel?
+        HttpRequestImpl request = null;
+        if (partials.containsKey(clientChannel)) {
+            request = HttpRequestImpl.continueParsing(buffer, partials.get(clientChannel));
+            if (!(request instanceof PartialHttpRequest)) {
+                // received the entire payload/body
+                partials.remove(clientChannel);
+            }
+        } else {
+            request = HttpRequestImpl.of(buffer);
+            if (request instanceof PartialHttpRequest) {
+                partials.put(key.channel(), (PartialHttpRequest) request);
+            }
+        }
+        // set extra request info
+        request.setRemoteHost(clientChannel.socket().getInetAddress());
+        request.setRemotePort(clientChannel.socket().getPort());
+        request.setServerHost(clientChannel.socket().getLocalAddress());
+        request.setServerPort(clientChannel.socket().getLocalPort());
+        return request;
+    }
+
+    @Override
+    public String toString() {
+        return "HttpProtocol";
+    }
 }

Modified: incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpRequest.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpRequest.java?rev=1153593&r1=1153592&r2=1153593&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpRequest.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpRequest.java Wed Aug  3 17:36:25 2011
@@ -20,319 +20,164 @@
 package org.deftserver.web.http;
 
 import java.net.InetAddress;
-import java.nio.ByteBuffer;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
 import java.util.Map;
-import java.util.regex.Pattern;
 
-import org.deftserver.io.IOLoop;
-import org.deftserver.util.ArrayUtil;
 import org.deftserver.web.HttpVerb;
 
-import com.google.common.base.Charsets;
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.Maps;
-
-public class HttpRequest {
-	
-	private IOLoop ioLoop;
-	
-	private final String requestLine;
-	private final HttpVerb method;
-	private final String requestedPath;	// correct name?
-	private final String version; 
-	private Map<String, String> headers;
-	private ImmutableMultimap<String, String> parameters;
-	private String body;
-	private boolean keepAlive;
-	private InetAddress remoteHost;
-	private InetAddress serverHost;
-	private int remotePort;
-	private int serverPort;
-	private Map<String, String> cookies = null;
-
-	/** Regex to parse HttpRequest Request Line */
-	public static final Pattern REQUEST_LINE_PATTERN = Pattern.compile(" ") ;
-	/** Regex to parse out QueryString from HttpRequest */
-	public static final Pattern QUERY_STRING_PATTERN = Pattern.compile("\\?") ;
-	/** Regex to parse out parameters from query string */
-	public static final Pattern PARAM_STRING_PATTERN = Pattern.compile("\\&|;");  //Delimiter is either & or ;
-	/** Regex to parse out key/value pairs */
-	public static final Pattern KEY_VALUE_PATTERN = Pattern.compile("=");
-	/** Regex to parse raw headers and body */
-	public static final Pattern RAW_VALUE_PATTERN = Pattern.compile("\\r\\n\\r\\n"); //TODO fix a better regexp for this
-	/** Regex to parse raw headers from body */
-	public static final Pattern HEADERS_BODY_PATTERN = Pattern.compile("\\r\\n");
-	/** Regex to parse header name and value */
-	public static final Pattern HEADER_VALUE_PATTERN = Pattern.compile(": ");
-	/** Regex to split cookie header following RFC6265 Section 5.4 */
-	public static final Pattern COOKIE_SEPARATOR_PATTERN = Pattern.compile(";");	
-	
-	/**
-	 * Creates a new HttpRequest 
-	 * @param requestLine The Http request text line
-	 * @param headers The Http request headers
-	 */
-	public HttpRequest(String requestLine, Map<String, String> headers) {
-		this.requestLine = requestLine;
-		String[] elements = REQUEST_LINE_PATTERN.split(requestLine);
-		method = HttpVerb.valueOf(elements[0]);
-		String[] pathFrags = QUERY_STRING_PATTERN.split(elements[1]);
-		requestedPath = pathFrags[0];
-		version = elements[2];
-		this.headers = headers;	
-		body = null;
-		initKeepAlive();
-		parameters = parseParameters(elements[1]);
-	}
-	
-	/**
-	 * Creates a new HttpRequest
-	 * @param requestLine The Http request text line
-	 * @param headers The Http request headers
-	 * @param body The Http request posted body
-	 */
-	public HttpRequest(String requestLine, Map<String, String> headers, String body) {
-		this(requestLine, headers);
-		this.body = body;
-	}
-	
-	public static HttpRequest of(ByteBuffer buffer) {
-		try {
-			String raw = new String(buffer.array(), 0, buffer.limit(), Charsets.ISO_8859_1);
-			String[] headersAndBody = RAW_VALUE_PATTERN.split(raw); 
-			String[] headerFields = HEADERS_BODY_PATTERN.split(headersAndBody[0]);
-			headerFields = ArrayUtil.dropFromEndWhile(headerFields, "");
-
-			String requestLine = headerFields[0];
-			Map<String, String> generalHeaders = new HashMap<String, String>();
-			for (int i = 1; i < headerFields.length; i++) {
-				String[] header = HEADER_VALUE_PATTERN.split(headerFields[i]);
-				generalHeaders.put(header[0].toLowerCase(), header[1]);
-			}
-
-			String body = "";
-			for (int i = 1; i < headersAndBody.length; ++i) { //First entry contains headers
-				body += headersAndBody[i];
-			}
-			
-			if (requestLine.contains("POST")) {
-				int contentLength = Integer.parseInt(generalHeaders.get("content-length"));
-				if (contentLength > body.length()) {
-					return new PartialHttpRequest(requestLine, generalHeaders, body);
-				}
-			}
-			return new HttpRequest(requestLine, generalHeaders, body);
-		} catch (Exception t) {
-			return MalFormedHttpRequest.instance;
-		}
-	}
-	
-	public static HttpRequest continueParsing(ByteBuffer buffer, PartialHttpRequest unfinished) {
-		String nextChunk = new String(buffer.array(), 0, buffer.limit(), Charsets.US_ASCII);
-		unfinished.appendBody(nextChunk);
-		
-		int contentLength = Integer.parseInt(unfinished.getHeader("Content-Length"));
-		if (contentLength > unfinished.getBody().length()) {
-			return unfinished;
-		} else {
-			return new HttpRequest(unfinished.getRequestLine(), unfinished.getHeaders(), unfinished.getBody());
-		}
-	}
-	
-	protected void setIOLoop(IOLoop ioLoop) {
-		this.ioLoop = ioLoop;
-	}
-	
-	public IOLoop getIOLoop() {
-		return ioLoop;
-	}
-	
-	public String getRequestLine() {
-		return requestLine;
-	}
-	
-	public String getRequestedPath() {
-		return requestedPath;
-	}
-
-	public String getVersion() {
-		return version;
-	}
-	
-	public Map<String, String> getHeaders() {
-		return Collections.unmodifiableMap(headers);
-	}
-	
-	public String getHeader(String name) {
-		return headers.get(name.toLowerCase());
-	}
-	
-	public HttpVerb getMethod() {
-		return method;
-	}
-	
-	/**
-	 * Returns the value of a request parameter as a String, or null if the parameter does not exist. 
-	 *
-	 * You should only use this method when you are sure the parameter has only one value. If the parameter 
-	 * might have more than one value, use getParameterValues(java.lang.String).
-     * If you use this method with a multi-valued parameter, the value returned is equal to the first value in
-     * the array returned by getParameterValues. 
-	 */
-	public String getParameter(String name) {
-		Collection<String> values = parameters.get(name);		
-		return values.isEmpty() ? null : values.iterator().next();
-	}
-	
-	public Map<String, Collection<String>> getParameters() {
-		return parameters.asMap();
-	}	
-	
-	public String getBody() {
-		return body;
-	}	
-	
-	public InetAddress getRemoteHost() {
-		return remoteHost;
-	}
-
-	public InetAddress getServerHost() {
-		return serverHost;
-	}
-
-	public int getRemotePort() {
-		return remotePort;
-	}
-
-	public int getServerPort() {
-		return serverPort;
-	}
-
-	protected void setRemoteHost(InetAddress host) {
-		remoteHost = host;
-	}
-
-	protected void setServerHost(InetAddress host) {
-		serverHost = host;
-	}
-
-	protected void setRemotePort(int port) {
-		remotePort = port;
-	}
-
-	protected void setServerPort(int port) {
-		serverPort = port;
-	}
-	
-	/**
-	 * Returns a map with all cookies contained in the request. Cookies are represented as strings, 
-	 * and are parsed at the first invocation of this method
-	 * 
-	 * @return a map containing all cookies of request
-	 */
-	public Map<String, String> getCookies() {
-		if (cookies == null) {
-			parseCookies();
-		}
-		return Collections.unmodifiableMap(cookies);
-	}
-	
-	/**
-	 * Returns a given cookie. Cookies are represented as strings, 
-	 * and are parsed at the first invocation of this method
-	 * 
-	 * @param name the name of cookie
-	 * @return the corresponding cookie, or null if the cookie does not exist
-	 */
-	public String getCookie(String name) {
-		if (cookies == null) {
-			parseCookies();
-		}
-		return cookies.get(name);
-	}
-
-	/**
-	 * Returns a collection of all values associated with the provided parameter.
-	 * If no values are found and empty collection is returned.
-	 */
-	public Collection<String> getParameterValues(String name) {
-		return parameters.get(name);
-	}
-	
-	public boolean isKeepAlive() {
-		return keepAlive;
-	}
-	
-	@Override
-	public String toString() {
-		String result = "METHOD: " + method + "\n";
-		result += "VERSION: " + version + "\n";
-		result += "PATH: " + requestedPath + "\n";
-		
-		result += "--- HEADER --- \n";
-		for (String key : headers.keySet()) {
-			String value = headers.get(key);
-			result += key + ":" + value + "\n";
-		}
-		
-		result += "--- PARAMETERS --- \n";
-		for (String key : parameters.keySet()) {
-			Collection<String> values = parameters.get(key);
-			for (String value : values) {
-				result += key + ":" + value + "\n";
-			}
-		}
-		return result;
-	}
-
-
-	
-	private ImmutableMultimap<String, String> parseParameters(String requestLine) {
-		ImmutableMultimap.Builder<String, String> builder = ImmutableMultimap.builder();
-		String[] str = QUERY_STRING_PATTERN.split(requestLine);
-		
-		//Parameters exist
-		if (str.length > 1) {
-			String[] paramArray = PARAM_STRING_PATTERN.split(str[1]); 
-			for (String keyValue : paramArray) {
-				String[] keyValueArray = KEY_VALUE_PATTERN.split(keyValue);				
-				//We need to check if the parameter has a value associated with it.
-				if (keyValueArray.length > 1) {
-					builder.put(keyValueArray[0], keyValueArray[1]); //name, value
-				}
-			}
-		}
-		return builder.build();
-	}
-	
-	/**
-	* Parse the cookie's http header (RFC6265 Section 5.4)
-	*/
-	private void parseCookies() {
-		String cookiesHeader = Strings.nullToEmpty(getHeader("Cookie")).trim();
-		cookies = Maps.newHashMap();
-		if (!cookiesHeader.equals("")) {
-			String[] cookiesStrings = COOKIE_SEPARATOR_PATTERN.split(cookiesHeader);
-			for (String cookieString : cookiesStrings) {
-				String[] cookie = KEY_VALUE_PATTERN.split(cookieString, 2);
-				cookies.put(cookie[0].trim(), cookie[1].trim());
-			}
-		}
-	}	
-	
-	private void initKeepAlive() {
-		String connection = getHeader("Connection");
-		if ("keep-alive".equalsIgnoreCase(connection)) { 
-			keepAlive = true;
-		} else if ("close".equalsIgnoreCase(connection) || requestLine.contains("1.0")) {
-			keepAlive = false;
-		} else {
-			keepAlive = true;
-		}
-	}
+/**
+ * An HTTP request received from a client
+ */
+public interface HttpRequest {
+
+    /**
+     * Get the HTTP request line for the request.
+     * <p>
+     * Ex :
+     * 
+     * <pre>
+     * GET http://www.w3.org/pub/WWW/TheProject.html HTTP/1.1
+     * </pre>
+     * 
+     * @return
+     */
+    public String getRequestLine();
+
+    /**
+     * The path of this request
+     * <p>
+     * Ex :
+     * 
+     * <pre>
+     * http://www.w3.org/pub/WWW/TheProject.html
+     * </pre>
+     * 
+     * @return the path requested
+     */
+    public String getRequestedPath();
+
+    /**
+     * The version of the HTTP protocol.
+     * <p>
+     * Can be <tt>HTTP/1.0</tt> or <tt>HTTP/1.1</tt>.
+     * 
+     * @return the HTTP version
+     */
+    public String getVersion();
+
+    /**
+     * Get the read-only header of this request.
+     * 
+     * @see HttpRequest#getHeader(String)
+     * 
+     * @return the header.
+     */
+    public Map<String, String> getHeaders();
+
+    /**
+     * Get the value of a given HTTP header.
+     * 
+     * @see HttpRequest#getHeaders()
+     * @param name the name of the requested header
+     * @return the value or <code>null</code> if the header is not found.
+     */
+    public String getHeader(String name);
+
+    /**
+     * The method (POST,GET ..) used for this request.
+     * 
+     * @see HttpVerb
+     * 
+     * @return the method
+     */
+    public HttpVerb getMethod();
+
+    /**
+     * Returns the value of a request parameter as a String, or null if the
+     * parameter does not exist.
+     * 
+     * You should only use this method when you are sure the parameter has only
+     * one value. If the parameter might have more than one value, use
+     * getParameterValues(java.lang.String). If you use this method with a
+     * multi-valued parameter, the value returned is equal to the first value in
+     * the array returned by getParameterValues.
+     */
+    public String getParameter(String name);
+
+    /**
+     * TODO: JAVADOC, it's unclear to me (jvermillard)
+     * 
+     * @return all the request parameters
+     */
+    public Map<String, Collection<String>> getParameters();
+
+    /**
+     * Returns a collection of all values associated with the provided
+     * parameter. If no values are found and empty collection is returned.
+     */
+    public Collection<String> getParameterValues(String name);
+
+    /**
+     * The body of this request
+     * 
+     * @return the body as a {@link String}
+     */
+    public String getBody();
+
+    /**
+     * The address of the client which issued this request.
+     * 
+     * @return the address
+     */
+    public InetAddress getRemoteHost();
+
+    /**
+     * The TCP port of the client which issued this request.
+     * 
+     * @return
+     */
+    public int getRemotePort();
+
+    /**
+     * The address of the server which received this request.
+     * 
+     * @return
+     */
+    public InetAddress getServerHost();
+
+    /**
+     * The TCP port of the server which received this request.
+     * 
+     * @return
+     */
+    public int getServerPort();
+
+    /**
+     * Returns a map with all cookies contained in the request. Cookies are
+     * represented as strings, and are parsed at the first invocation of this
+     * method
+     * 
+     * @return a map containing all cookies of request
+     */
+    public Map<String, String> getCookies();
+
+    /**
+     * Returns a given cookie. Cookies are represented as strings, and are
+     * parsed at the first invocation of this method
+     * 
+     * @param name the name of cookie
+     * @return the corresponding cookie, or null if the cookie does not exist
+     */
+    public String getCookie(String name);
+
+    /**
+     * Does keep-alive was requested.
+     * 
+     * @see <a
+     *      href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html">HTTP/1.1
+     *      persistent connections</a>
+     * @return <code>true</code> if keep-alive requested
+     */
+    public boolean isKeepAlive();
 
 }

Modified: incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpRequestDispatcher.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpRequestDispatcher.java?rev=1153593&r1=1153592&r2=1153593&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpRequestDispatcher.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpRequestDispatcher.java Wed Aug  3 17:36:25 2011
@@ -26,42 +26,42 @@ import org.slf4j.LoggerFactory;
 
 public class HttpRequestDispatcher {
 
-	private static final Logger logger = LoggerFactory.getLogger(HttpRequestDispatcher.class);
+    private static final Logger logger = LoggerFactory.getLogger(HttpRequestDispatcher.class);
 
-	public static void dispatch(RequestHandler rh, HttpRequest request, HttpResponse response) {
-		if (rh != null) {
-			HttpVerb method = request.getMethod();
-			try {
-				switch (method) {
-				case GET:
-					rh.get(request, response);
-					break;
-				case POST:
-					rh.post(request, response);
-					break;
-				case HEAD:
-					rh.head(request, response);
-					break;
-				case PUT:
-					rh.put(request, response);
-					break;
-				case DELETE:
-					rh.delete(request, response);
-					break;
-				case OPTIONS: //Fall through
-				case TRACE:
-				case CONNECT:
-				default:
-					logger.warn("Unimplemented Http metod received: {}", method);
-					//TODO send "not supported page (501) back to client"
-				}
-			} catch (HttpException he) {
-				response.setStatusCode(he.getStatusCode());
-				response.write(he.getMessage());
-				if (rh.isMethodAsynchronous(request.getMethod())) {
-					response.finish();
-				}
-			}
-		}
-	}
+    public static void dispatch(RequestHandler rh, HttpRequest request, HttpResponse response) {
+        if (rh != null) {
+            HttpVerb method = request.getMethod();
+            try {
+                switch (method) {
+                case GET:
+                    rh.get(request, response);
+                    break;
+                case POST:
+                    rh.post(request, response);
+                    break;
+                case HEAD:
+                    rh.head(request, response);
+                    break;
+                case PUT:
+                    rh.put(request, response);
+                    break;
+                case DELETE:
+                    rh.delete(request, response);
+                    break;
+                case OPTIONS: // Fall through
+                case TRACE:
+                case CONNECT:
+                default:
+                    logger.warn("Unimplemented Http metod received: {}", method);
+                    // TODO send "not supported page (501) back to client"
+                }
+            } catch (HttpException he) {
+                response.setStatusCode(he.getStatusCode());
+                response.write(he.getMessage());
+                if (rh.isMethodAsynchronous(request.getMethod())) {
+                    response.finish();
+                }
+            }
+        }
+    }
 }