incubator-deft-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jmee...@apache.org
Subject svn commit: r1153593 [2/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...
Date Wed, 03 Aug 2011 17:36:30 GMT
Added: incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpRequestImpl.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpRequestImpl.java?rev=1153593&view=auto
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpRequestImpl.java (added)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpRequestImpl.java Wed Aug  3 17:36:25 2011
@@ -0,0 +1,362 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+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 HttpRequestImpl implements 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("\\&|;");
+    /** 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 HttpRequestImpl(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 HttpRequestImpl(String requestLine, Map<String, String> headers, String body) {
+        this(requestLine, headers);
+        this.body = body;
+    }
+
+    public static HttpRequestImpl 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 HttpRequestImpl(requestLine, generalHeaders, body);
+        } catch (Exception t) {
+            return MalFormedHttpRequest.instance;
+        }
+    }
+
+    public static HttpRequestImpl 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 HttpRequestImpl(unfinished.getRequestLine(), unfinished.getHeaders(), unfinished.getBody());
+        }
+    }
+
+    protected void setIOLoop(IOLoop ioLoop) {
+        this.ioLoop = ioLoop;
+    }
+
+    public IOLoop getIOLoop() {
+        return ioLoop;
+    }
+
+    @Override
+    public String getRequestLine() {
+        return requestLine;
+    }
+
+    @Override
+    public String getRequestedPath() {
+        return requestedPath;
+    }
+
+    @Override
+    public String getVersion() {
+        return version;
+    }
+
+    @Override
+    public Map<String, String> getHeaders() {
+        return Collections.unmodifiableMap(headers);
+    }
+
+    @Override
+    public String getHeader(String name) {
+        return headers.get(name.toLowerCase());
+    }
+
+    @Override
+    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.
+     */
+    @Override
+    public String getParameter(String name) {
+        Collection<String> values = parameters.get(name);
+        return values.isEmpty() ? null : values.iterator().next();
+    }
+
+    @Override
+    public Map<String, Collection<String>> getParameters() {
+        return parameters.asMap();
+    }
+
+    @Override
+    public String getBody() {
+        return body;
+    }
+
+    @Override
+    public InetAddress getRemoteHost() {
+        return remoteHost;
+    }
+
+    @Override
+    public InetAddress getServerHost() {
+        return serverHost;
+    }
+
+    @Override
+    public int getRemotePort() {
+        return remotePort;
+    }
+
+    @Override
+    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
+     */
+    @Override
+    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
+     */
+    @Override
+    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.
+     */
+    @Override
+    public Collection<String> getParameterValues(String name) {
+        return parameters.get(name);
+    }
+
+    @Override
+    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;
+        }
+    }
+}

Propchange: incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpRequestImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpResponse.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpResponse.java?rev=1153593&r1=1153592&r2=1153593&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpResponse.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpResponse.java Wed Aug  3 17:36:25 2011
@@ -19,362 +19,157 @@
  */
 package org.deftserver.web.http;
 
-import static org.deftserver.web.http.HttpServerDescriptor.WRITE_BUFFER_SIZE;
-
 import java.io.File;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.nio.MappedByteBuffer;
-import java.nio.channels.ClosedChannelException;
-import java.nio.channels.FileChannel;
-import java.nio.channels.FileChannel.MapMode;
-import java.nio.channels.SelectionKey;
-import java.nio.channels.SocketChannel;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.deftserver.io.buffer.DynamicByteBuffer;
-import org.deftserver.util.Closeables;
-import org.deftserver.util.CookieUtil;
-import org.deftserver.util.DateUtil;
-import org.deftserver.util.HttpUtil;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.CharMatcher;
-import com.google.common.base.Charsets;
-import com.google.common.base.Strings;
-import com.google.common.collect.Maps;
-
-public class HttpResponse {
-	
-	private final static Logger logger = LoggerFactory.getLogger(HttpResponse.class);
-	
-	private final HttpProtocol protocol;
-	private final SelectionKey key;
-	
-	private int statusCode = 200;	// default response status code
-	
-	private final Map<String, String> headers = new HashMap<String, String>();
-	private final Map<String, String> cookies = Maps.newHashMap();
-	private boolean headersCreated = false;
-	private DynamicByteBuffer responseData = DynamicByteBuffer.allocate(WRITE_BUFFER_SIZE);
-	
-	public HttpResponse(HttpProtocol protocol, SelectionKey key, boolean keepAlive) {
-		this.protocol = protocol;
-		this.key = key;
-		headers.put("Server", "Deft/0.4.0-SNAPSHOT");
-		headers.put("Date", DateUtil.getCurrentAsString());
-		headers.put("Connection", keepAlive ? "Keep-Alive" : "Close");
-	}
-	
-	public void setStatusCode(int sc) {
-		statusCode = sc;
-	}
-	
-	public void setHeader(String header, String value) {
-		headers.put(header, value);
-	}
-	
-	/**
-	 * Add a cookie to response.
-	 * 
-	 * @see #setCookie(String, String, Integer, String, String, boolean, boolean)
-	 * @param name name of cookie
-	 * @param value value of cookie
-	 * @throws IllegalArgumentException if cookie name, or value, is not valid
-	 */
-	public void setCookie(String name, String value) {
-		setCookie(name, value, -1, null, null, false, false);
-	}
-	
-	/**
-	 * Add a cookie to response.
-	 * 
-	 * @see #setCookie(String, String, Integer, String, String, boolean, boolean)
-	 * @param name name of cookie
-	 * @param value value of cookie
-	 * @param expiration expiration of cookie in seconds
-	 * @throws IllegalArgumentException if cookie name, or value, is not valid
-	 */
-	public void setCookie(String name, String value, long expiration) {
-		setCookie(name, value, expiration, null, null, false, false);
-	}
-	
-	/**
-	 * Add a cookie to response.
-	 * 
-	 * @see #setCookie(String, String, Integer, String, String, boolean, boolean)
-	 * @param name name of cookie
-	 * @param value value of cookie
-	 * @param domain cookie domain
-	 * @throws IllegalArgumentException if cookie name, or value, is not valid
-	 */
-	public void setCookie(String name, String value, String domain) {
-		setCookie(name, value, -1, domain, null, false, false);
-	}
-	
-	/**
-	 * Add a cookie to response.
-	 * 
-	 * @see #setCookie(String, String, Integer, String, String, boolean, boolean)
-	 * @param name name of cookie
-	 * @param value value of cookie
-	 * @param domain cookie domain
-	 * @param path cookie path
-	 * @throws IllegalArgumentException if cookie name, or value, is not valid
-	 */
-	public void setCookie(String name, String value, String domain, String path) {
-		setCookie(name, value, -1, domain, path, false, false);
-	}
-		
-	/**
-	 * Add a cookie to response.
-	 * 
-	 * @see #setCookie(String, String, Integer, String, String, boolean, boolean)
-	 * @param name name of cookie
-	 * @param value value of cookie
-	 * @param expiration expiration of cookie in seconds
-	 * @param domain cookie domain
-	 * @throws IllegalArgumentException if cookie name, or value, is not valid
-	 */
-	public void setCookie(String name, String value, long expiration, String domain) {
-		setCookie(name, value, expiration, domain, null, false, false);
-	}
-	
-	/**
-	 * Add a cookie to response. 
-	 * 
-	 * @see HttpResponse#setCookie(String, String, Integer, String, String, boolean, boolean)
-	 * @param name name of cookie
-	 * @param value value of cookie
-	 * @param expiration expiration of cookie in seconds 
-	 * @param domain cookie domain
-	 * @param path cookie path
-	 * @throws IllegalArgumentException if cookie name, or value, is not valid
-	 */
-	public void setCookie(String name, String value, long expiration, String domain, String path) {
-		setCookie(name, value, expiration, domain, path, false, false);
-	}	
-	
-	/**
-	 * Add a cookie to response. 
-	 * The addition of a cookie with a name that was already inserted, causes the substitution of the previous cookie
-	 *  
-	 * @param name name of cookie
-	 * @param value value of cookie
-	 * @param expiration expiration of cookie in seconds 
-	 * @param domain cookie domain
-	 * @param path cookie path
-	 * @param secure set <code>Secure</code> property of cookie
-	 * @param httpOnly set <code>HttpOnly</code> property of cookie
-	 * @throws IllegalArgumentException if cookie name, or value, is not valid
-	 */
-	public void setCookie(String name, String value, long expiration, String domain, String path, boolean secure, boolean httpOnly) {
-		if (Strings.isNullOrEmpty(name)) {
-			throw new IllegalArgumentException("Cookie name is empty");
-		}
-		if (name.trim().startsWith("$")) {
-			throw new IllegalArgumentException("Cookie name is not valid");
-		}
-		StringBuffer sb = new StringBuffer(name.trim() + "=" + Strings.nullToEmpty(value).trim() + "; ");
-		if (CharMatcher.JAVA_ISO_CONTROL.countIn(sb) > 0) {
-			throw new IllegalArgumentException("Invalid cookie " + name + ": " + value);
-		}
-		if (expiration >= 0) {
-			if (expiration == 0)
-				sb.append("Expires=" + DateUtil.getDateAsString(new Date(0)) + "; ");
-			else 
-				sb.append("Expires=" + CookieUtil.maxAgeToExpires(expiration) + "; ");
-		} 
-		if (!Strings.isNullOrEmpty(domain)) {
-			sb.append("Domain=" + domain.trim() + "; ");
-		}
-		if (!Strings.isNullOrEmpty(path)) {
-			sb.append("Path=" + path.trim() + "; ");
-		}
-		if (secure) {
-			sb.append("Secure; ");
-		}
-		if (httpOnly) {
-			sb.append("HttpOnly; ");
-		}
-		cookies.put(name, sb.toString());
-	}
-	
-	/**
-	 * Removes a cookie. This method forces the removal of a cookie, by setting its expiration in the past.
-	 * 
-	 * @param name name of cookie to delete
-	 */
-	public void clearCookie(String name) {
-		if (Strings.emptyToNull(name) != null) {	
-			setCookie(name, null, 0);
-		}
-	}
-
-	/**
-	 * The given data data will be sent as the HTTP response upon next flush or when the response is finished.
-	 *
-	 * @return this for chaining purposes.
-	 */
-	public HttpResponse write(String data) {
-		byte[] bytes = data.getBytes(Charsets.UTF_8);
-		responseData.put(bytes);
-		return this;
-	}
-
-	/**
-	 * Explicit flush. 
-	 * 
-	 * @return the number of bytes that were actually written as the result of this flush.
-	 */
-	public long flush() {
-		if (!headersCreated) {
-			String initial = createInitalLineAndHeaders();			
-			responseData.prepend(initial);
-			headersCreated = true;
-		}
-
-		SocketChannel channel = (SocketChannel) key.channel();
-		responseData.flip();	// prepare for write
-		try {
-			channel.write(responseData.getByteBuffer());
-		} catch (IOException e) {
-			logger.error("ClosedChannelException during channel.write(): {}", e.getMessage());
-			Closeables.closeQuietly(protocol.getIOLoop(), key.channel());
-		}
-		long bytesFlushed = responseData.position();
-		if (protocol.getIOLoop().hasKeepAliveTimeout(channel)) {
-			protocol.prolongKeepAliveTimeout(channel);
-		}
-		if (responseData.hasRemaining()) { 
-			responseData.compact();	// make room for more data be "read" in
-			try {
-				key.channel().register(key.selector(), SelectionKey.OP_WRITE);	//TODO RS 110621, use IOLoop.updateHandler
-			} catch (ClosedChannelException e) {
-				logger.error("ClosedChannelException during flush(): {}", e.getMessage());
-				Closeables.closeQuietly(protocol.getIOLoop(), key.channel());
-			}
-			key.attach(responseData);
-		} else {
-			responseData.clear();
-		}
-		return bytesFlushed;
-	}
-	
-	/**
-	 * Should only be invoked by third party asynchronous request handlers 
-	 * (or by the Deft framework for synchronous request handlers). 
-	 * If no previous (explicit) flush is invoked, the "Content-Length" and "Etag" header will be calculated and 
-	 * inserted to the HTTP response.
-	 * 
-	 */
-	public long finish() {
-		long bytesWritten = 0;
-		SocketChannel clientChannel = (SocketChannel) key.channel();
-
-		if (key.attachment() instanceof MappedByteBuffer) {
-			MappedByteBuffer mbb = (MappedByteBuffer) key.attachment();
-			if (mbb.hasRemaining() && clientChannel.isOpen()) {
-				try {
-					bytesWritten = clientChannel.write(mbb);
-				} catch (IOException e) {
-					logger.warn("Could not write to channel: ", e.getMessage());					
-					Closeables.closeQuietly(key.channel());
-				}
-			}
-			if (!mbb.hasRemaining()) {
-				protocol.closeOrRegisterForRead(key);
-			}
-		} else {
-			if (clientChannel.isOpen()) {
-				if (!headersCreated) {
-					setEtagAndContentLength();
-				}
-				bytesWritten = flush();
-			}
-			// close (or register for read) if
-			// (a) DBB is attached but all data is sent to wire (hasRemaining ==
-			// false)
-			// (b) no DBB is attached (never had to register for write)
-			if (key.attachment() instanceof DynamicByteBuffer) {
-				DynamicByteBuffer dbb = (DynamicByteBuffer) key.attachment();
-				if (!(dbb).hasRemaining()) {
-					protocol.closeOrRegisterForRead(key);
-				}
-			} else {
-				protocol.closeOrRegisterForRead(key);
-			}
-		}
-		return bytesWritten;
-	}	
-	private void setEtagAndContentLength() {
-		if (responseData.position() > 0) {
-			setHeader("Etag", HttpUtil.getEtag(responseData.array()));
-		}
-		setHeader("Content-Length", String.valueOf(responseData.position()));
-	}
-	
-	private String createInitalLineAndHeaders() {
-		StringBuilder sb = new StringBuilder(HttpUtil.createInitialLine(statusCode));
-		for (Map.Entry<String, String> header : headers.entrySet()) {
-			sb.append(header.getKey());
-			sb.append(": ");
-			sb.append(header.getValue());
-			sb.append("\r\n");
-		}
-		for (String cookie : cookies.values()) {
-			sb.append("Set-Cookie: " + cookie + "\r\n");
-		}
-		
-		sb.append("\r\n");
-		return sb.toString();
-	}
-	
-	/**
-	 * Experimental support.
-	 * Before use, read https://github.com/rschildmeijer/deft/issues/75
-	 */
-	public long write(File file) {
-		//setHeader("Etag", HttpUtil.getEtag(file));
-		setHeader("Content-Length", String.valueOf(file.length()));
-		long bytesWritten = 0;
-		flush(); // write initial line + headers
-		
-		RandomAccessFile raf = null;
-		try {
-			raf = new RandomAccessFile(file, "r");
-			FileChannel fc = raf.getChannel();
-			MappedByteBuffer mbb = raf.getChannel().map(MapMode.READ_ONLY, 0L, fc.size());
-
-			if (mbb.hasRemaining()) {
-				bytesWritten = ((SocketChannel) key.channel()).write(mbb);
-				logger.debug("sent file, bytes sent: {}", bytesWritten);
-			}
-			if (mbb.hasRemaining()) {
-				try {
-					key.channel().register(key.selector(), SelectionKey.OP_WRITE); //TODO RS 110621, use IOLoop.updateHandler
-				} catch (ClosedChannelException e) {
-					logger.error("ClosedChannelException during write(File): {}", e.getMessage());
-					Closeables.closeQuietly(key.channel());
-				}
-				key.attach(mbb);
-			}
-		} catch (IOException e) {
-			logger.error("Error writing (static file) response: {}", e.getMessage());
-		} finally {
-			if (raf != null) {
-				try {
-					raf.close();
-				} catch (IOException e) {
-					logger.error("Error closing static file: ", e.getMessage());					
-				}
-			}
-		}
-
-		return bytesWritten;
-	}
-	
+
+/**
+ * An HTTP response build and sent to a client in response to a
+ * {@link HttpRequest}
+ */
+public interface HttpResponse {
+
+    /**
+     * Set this HTTP Response status (or alos called return code). 200 for OK,
+     * 404 for not found, etc..
+     * 
+     * @param sc the status code
+     */
+    void setStatusCode(int sc);
+
+    /**
+     * Set an HTTP header value. If the header doesn't exist, it will be added.
+     * 
+     * @param header the unique header key
+     * @param value the string value
+     */
+    void setHeader(String header, String value);
+
+    /**
+     * Add a cookie to response.
+     * 
+     * @see #setCookie(String, String, Integer, String, String, boolean,
+     *      boolean)
+     * @param name name of cookie
+     * @param value value of cookie
+     * @throws IllegalArgumentException if cookie name, or value, is not valid
+     */
+    void setCookie(String name, String value);
+
+    /**
+     * Add a cookie to response.
+     * 
+     * @see #setCookie(String, String, Integer, String, String, boolean,
+     *      boolean)
+     * @param name name of cookie
+     * @param value value of cookie
+     * @param expiration expiration of cookie in seconds
+     * @throws IllegalArgumentException if cookie name, or value, is not valid
+     */
+    void setCookie(String name, String value, long expiration);
+
+    /**
+     * Add a cookie to response.
+     * 
+     * @see #setCookie(String, String, Integer, String, String, boolean,
+     *      boolean)
+     * @param name name of cookie
+     * @param value value of cookie
+     * @param domain cookie domain
+     * @throws IllegalArgumentException if cookie name, or value, is not valid
+     */
+    void setCookie(String name, String value, String domain);
+
+    /**
+     * Add a cookie to response.
+     * 
+     * @see #setCookie(String, String, Integer, String, String, boolean,
+     *      boolean)
+     * @param name name of cookie
+     * @param value value of cookie
+     * @param domain cookie domain
+     * @param path cookie path
+     * @throws IllegalArgumentException if cookie name, or value, is not valid
+     */
+    void setCookie(String name, String value, String domain, String path);
+
+    /**
+     * Add a cookie to response.
+     * 
+     * @see #setCookie(String, String, Integer, String, String, boolean,
+     *      boolean)
+     * @param name name of cookie
+     * @param value value of cookie
+     * @param expiration expiration of cookie in seconds
+     * @param domain cookie domain
+     * @throws IllegalArgumentException if cookie name, or value, is not valid
+     */
+    void setCookie(String name, String value, long expiration, String domain);
+
+    /**
+     * Add a cookie to response.
+     * 
+     * @see #setCookie(String, String, Integer, String, String, boolean,
+     *      boolean)
+     * @param name name of cookie
+     * @param value value of cookie
+     * @param expiration expiration of cookie in seconds
+     * @param domain cookie domain
+     * @param path cookie path
+     * @throws IllegalArgumentException if cookie name, or value, is not valid
+     */
+    void setCookie(String name, String value, long expiration, String domain, String path);
+
+    /**
+     * Add a cookie to response. The addition of a cookie with a name that was
+     * already inserted, causes the substitution of the previous cookie
+     * 
+     * @param name name of cookie
+     * @param value value of cookie
+     * @param expiration expiration of cookie in seconds
+     * @param domain cookie domain
+     * @param path cookie path
+     * @param secure set <code>Secure</code> property of cookie
+     * @param httpOnly set <code>HttpOnly</code> property of cookie
+     * @throws IllegalArgumentException if cookie name, or value, is not valid
+     */
+    void setCookie(String name, String value, long expiration, String domain, String path, boolean secure,
+            boolean httpOnly);
+
+    /**
+     * Removes a cookie. This method forces the removal of a cookie, by setting
+     * its expiration in the past.
+     * 
+     * @param name name of cookie to delete
+     */
+    void clearCookie(String name);
+
+    /**
+     * The given data data will be sent as the HTTP response upon next flush or
+     * when the response is finished.
+     * 
+     * @return this for chaining purposes.
+     */
+    HttpResponse write(String data);
+
+    /**
+     * Explicit flush.
+     * 
+     * @return the number of bytes that were actually written as the result of
+     *         this flush.
+     */
+    long flush();
+
+    /**
+     * Experimental support. Before use, read
+     * https://github.com/rschildmeijer/deft/issues/75
+     */
+    long write(File file);
+
+    /**
+     * Should only be invoked by third party asynchronous request handlers (or
+     * by the Deft framework for synchronous request handlers). If no previous
+     * (explicit) flush is invoked, the "Content-Length" and "Etag" header will
+     * be calculated and inserted to the HTTP response.
+     * 
+     */
+    long finish();
 }

Added: incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpResponseImpl.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpResponseImpl.java?rev=1153593&view=auto
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpResponseImpl.java (added)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpResponseImpl.java Wed Aug  3 17:36:25 2011
@@ -0,0 +1,313 @@
+/*
+ *  or more contributor license agreements.  See the NOTICE file
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+package org.deftserver.web.http;
+
+import static org.deftserver.web.http.HttpServerDescriptor.WRITE_BUFFER_SIZE;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.FileChannel;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.SocketChannel;
+import java.nio.channels.FileChannel.MapMode;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.deftserver.io.buffer.DynamicByteBuffer;
+import org.deftserver.util.Closeables;
+import org.deftserver.util.CookieUtil;
+import org.deftserver.util.DateUtil;
+import org.deftserver.util.HttpUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Charsets;
+import com.google.common.base.Strings;
+import com.google.common.collect.Maps;
+
+public class HttpResponseImpl implements HttpResponse {
+
+    private final static Logger logger = LoggerFactory.getLogger(HttpResponseImpl.class);
+
+    private final HttpProtocol protocol;
+    private final SelectionKey key;
+
+    private int statusCode = 200; // default response status code
+
+    private final Map<String, String> headers = new HashMap<String, String>();
+    private final Map<String, String> cookies = Maps.newHashMap();
+    private boolean headersCreated = false;
+    private DynamicByteBuffer responseData = DynamicByteBuffer.allocate(WRITE_BUFFER_SIZE);
+
+    public HttpResponseImpl(HttpProtocol protocol, SelectionKey key, boolean keepAlive) {
+        this.protocol = protocol;
+        this.key = key;
+        headers.put("Server", "Deft/0.4.0-SNAPSHOT");
+        headers.put("Date", DateUtil.getCurrentAsString());
+        headers.put("Connection", keepAlive ? "Keep-Alive" : "Close");
+    }
+
+    @Override
+    public void setStatusCode(int sc) {
+        statusCode = sc;
+    }
+
+    @Override
+    public void setHeader(String header, String value) {
+        headers.put(header, value);
+    }
+
+    @Override
+    public void setCookie(String name, String value) {
+        setCookie(name, value, -1, null, null, false, false);
+    }
+
+    @Override
+    public void setCookie(String name, String value, long expiration) {
+        setCookie(name, value, expiration, null, null, false, false);
+    }
+
+    @Override
+    public void setCookie(String name, String value, String domain) {
+        setCookie(name, value, -1, domain, null, false, false);
+    }
+
+    @Override
+    public void setCookie(String name, String value, String domain, String path) {
+        setCookie(name, value, -1, domain, path, false, false);
+    }
+
+    @Override
+    public void setCookie(String name, String value, long expiration, String domain) {
+        setCookie(name, value, expiration, domain, null, false, false);
+    }
+
+    @Override
+    public void setCookie(String name, String value, long expiration, String domain, String path) {
+        setCookie(name, value, expiration, domain, path, false, false);
+    }
+
+    @Override
+    public void setCookie(String name, String value, long expiration, String domain, String path, boolean secure,
+            boolean httpOnly) {
+        if (Strings.isNullOrEmpty(name)) {
+            throw new IllegalArgumentException("Cookie name is empty");
+        }
+        if (name.trim().startsWith("$")) {
+            throw new IllegalArgumentException("Cookie name is not valid");
+        }
+        StringBuffer sb = new StringBuffer(name.trim() + "=" + Strings.nullToEmpty(value).trim() + "; ");
+        if (CharMatcher.JAVA_ISO_CONTROL.countIn(sb) > 0) {
+            throw new IllegalArgumentException("Invalid cookie " + name + ": " + value);
+        }
+        if (expiration >= 0) {
+            if (expiration == 0) {
+                sb.append("Expires=" + DateUtil.getDateAsString(new Date(0)) + "; ");
+            } else {
+                sb.append("Expires=" + CookieUtil.maxAgeToExpires(expiration) + "; ");
+            }
+        }
+        if (!Strings.isNullOrEmpty(domain)) {
+            sb.append("Domain=" + domain.trim() + "; ");
+        }
+        if (!Strings.isNullOrEmpty(path)) {
+            sb.append("Path=" + path.trim() + "; ");
+        }
+        if (secure) {
+            sb.append("Secure; ");
+        }
+        if (httpOnly) {
+            sb.append("HttpOnly; ");
+        }
+        cookies.put(name, sb.toString());
+    }
+
+    @Override
+    public void clearCookie(String name) {
+        if (Strings.emptyToNull(name) != null) {
+            setCookie(name, null, 0);
+        }
+    }
+
+    @Override
+    public HttpResponseImpl write(String data) {
+        byte[] bytes = data.getBytes(Charsets.UTF_8);
+        responseData.put(bytes);
+        return this;
+    }
+
+    @Override
+    public long flush() {
+        if (!headersCreated) {
+            String initial = createInitalLineAndHeaders();
+            responseData.prepend(initial);
+            headersCreated = true;
+        }
+
+        SocketChannel channel = (SocketChannel) key.channel();
+        responseData.flip(); // prepare for write
+        try {
+            channel.write(responseData.getByteBuffer());
+        } catch (IOException e) {
+            logger.error("ClosedChannelException during channel.write(): {}", e.getMessage());
+            Closeables.closeQuietly(protocol.getIOLoop(), key.channel());
+        }
+        long bytesFlushed = responseData.position();
+        if (protocol.getIOLoop().hasKeepAliveTimeout(channel)) {
+            protocol.prolongKeepAliveTimeout(channel);
+        }
+        if (responseData.hasRemaining()) {
+            responseData.compact(); // make room for more data be "read" in
+            try {
+                key.channel().register(key.selector(), SelectionKey.OP_WRITE); // TODO
+                // RS
+                // 110621,
+                // use
+                // IOLoop.updateHandler
+            } catch (ClosedChannelException e) {
+                logger.error("ClosedChannelException during flush(): {}", e.getMessage());
+                Closeables.closeQuietly(protocol.getIOLoop(), key.channel());
+            }
+            key.attach(responseData);
+        } else {
+            responseData.clear();
+        }
+        return bytesFlushed;
+    }
+
+    @Override
+    public long finish() {
+        long bytesWritten = 0;
+        SocketChannel clientChannel = (SocketChannel) key.channel();
+
+        if (key.attachment() instanceof MappedByteBuffer) {
+            MappedByteBuffer mbb = (MappedByteBuffer) key.attachment();
+            if (mbb.hasRemaining() && clientChannel.isOpen()) {
+                try {
+                    bytesWritten = clientChannel.write(mbb);
+                } catch (IOException e) {
+                    logger.warn("Could not write to channel: ", e.getMessage());
+                    Closeables.closeQuietly(key.channel());
+                }
+            }
+            if (!mbb.hasRemaining()) {
+                protocol.closeOrRegisterForRead(key);
+            }
+        } else {
+            if (clientChannel.isOpen()) {
+                if (!headersCreated) {
+                    setEtagAndContentLength();
+                }
+                bytesWritten = flush();
+            }
+            // close (or register for read) if
+            // (a) DBB is attached but all data is sent to wire (hasRemaining ==
+            // false)
+            // (b) no DBB is attached (never had to register for write)
+            if (key.attachment() instanceof DynamicByteBuffer) {
+                DynamicByteBuffer dbb = (DynamicByteBuffer) key.attachment();
+                if (!dbb.hasRemaining()) {
+                    protocol.closeOrRegisterForRead(key);
+                }
+            } else {
+                protocol.closeOrRegisterForRead(key);
+            }
+        }
+        return bytesWritten;
+    }
+
+    private void setEtagAndContentLength() {
+        if (responseData.position() > 0) {
+            setHeader("Etag", HttpUtil.getEtag(responseData.array()));
+        }
+        setHeader("Content-Length", String.valueOf(responseData.position()));
+    }
+
+    private String createInitalLineAndHeaders() {
+        StringBuilder sb = new StringBuilder(HttpUtil.createInitialLine(statusCode));
+        for (Map.Entry<String, String> header : headers.entrySet()) {
+            sb.append(header.getKey());
+            sb.append(": ");
+            sb.append(header.getValue());
+            sb.append("\r\n");
+        }
+        for (String cookie : cookies.values()) {
+            sb.append("Set-Cookie: " + cookie + "\r\n");
+        }
+
+        sb.append("\r\n");
+        return sb.toString();
+    }
+
+    /**
+     * Experimental support. Before use, read
+     * https://github.com/rschildmeijer/deft/issues/75
+     */
+    @Override
+    public long write(File file) {
+        // setHeader("Etag", HttpUtil.getEtag(file));
+        setHeader("Content-Length", String.valueOf(file.length()));
+        long bytesWritten = 0;
+        flush(); // write initial line + headers
+
+        RandomAccessFile raf = null;
+        try {
+            raf = new RandomAccessFile(file, "r");
+            FileChannel fc = raf.getChannel();
+            MappedByteBuffer mbb = raf.getChannel().map(MapMode.READ_ONLY, 0L, fc.size());
+
+            if (mbb.hasRemaining()) {
+                bytesWritten = ((SocketChannel) key.channel()).write(mbb);
+                logger.debug("sent file, bytes sent: {}", bytesWritten);
+            }
+            if (mbb.hasRemaining()) {
+                try {
+                    key.channel().register(key.selector(), SelectionKey.OP_WRITE); // TODO
+                    // RS
+                    // 110621,
+                    // use
+                    // IOLoop.updateHandler
+                } catch (ClosedChannelException e) {
+                    logger.error("ClosedChannelException during write(File): {}", e.getMessage());
+                    Closeables.closeQuietly(key.channel());
+                }
+                key.attach(mbb);
+            }
+        } catch (IOException e) {
+            logger.error("Error writing (static file) response: {}", e.getMessage());
+        } finally {
+            if (raf != null) {
+                try {
+                    raf.close();
+                } catch (IOException e) {
+                    logger.error("Error closing static file: ", e.getMessage());
+                }
+            }
+        }
+
+        return bytesWritten;
+    }
+
+}

Propchange: incubator/deft/sandbox/src/main/java/org/deftserver/web/http/HttpResponseImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/deft/sandbox/src/main/java/org/deftserver/web/http/MalFormedHttpRequest.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/web/http/MalFormedHttpRequest.java?rev=1153593&r1=1153592&r2=1153593&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/web/http/MalFormedHttpRequest.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/web/http/MalFormedHttpRequest.java Wed Aug  3 17:36:25 2011
@@ -21,14 +21,12 @@ package org.deftserver.web.http;
 
 import com.google.common.collect.Maps;
 
+public class MalFormedHttpRequest extends HttpRequestImpl {
 
-public class MalFormedHttpRequest extends HttpRequest {
-
-	public static final MalFormedHttpRequest instance = new MalFormedHttpRequest();
-	
-	/* Dummy HttpRequest that represents a malformed client HTTP request */
-	private MalFormedHttpRequest() {
-		super("GET / Mal formed request\r\n", Maps.<String, String>newHashMap());
-	}
+    public static final MalFormedHttpRequest instance = new MalFormedHttpRequest();
 
+    /* Dummy HttpRequest that represents a malformed client HTTP request */
+    private MalFormedHttpRequest() {
+        super("GET / Mal formed request\r\n", Maps.<String, String> newHashMap());
+    }
 }

Modified: incubator/deft/sandbox/src/main/java/org/deftserver/web/http/PartialHttpRequest.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/web/http/PartialHttpRequest.java?rev=1153593&r1=1153592&r2=1153593&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/web/http/PartialHttpRequest.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/web/http/PartialHttpRequest.java Wed Aug  3 17:36:25 2011
@@ -21,37 +21,34 @@ package org.deftserver.web.http;
 
 import java.util.Map;
 
-
 /**
- * Represents an unfinished "dummy" HTTP request, e.g, an HTTP POST request where the entire payload hasn't been 
- * received.
- * (E.g. because the size of the underlying (OS) socket's read buffer has a fixed size.)
+ * Represents an unfinished "dummy" HTTP request, e.g, an HTTP POST request
+ * where the entire payload hasn't been received. (E.g. because the size of the
+ * underlying (OS) socket's read buffer has a fixed size.)
  * 
  */
+public class PartialHttpRequest extends HttpRequestImpl {
+
+    private final String requestLine;
+    private String unfinishedBody;
 
-public class PartialHttpRequest extends HttpRequest {
-	
-	private final String requestLine;
-	private String unfinishedBody;
-
-	public PartialHttpRequest(String requestLine, Map<String, String> generalHeaders, String body) {
-		super("POST <> Unfinished request\r\n", generalHeaders);
-		this.requestLine = requestLine;
-		this.unfinishedBody = body;
-	}
-
-	public void appendBody(String nextChunk) {
-		unfinishedBody += nextChunk;
-	}
-	
-	@Override
-	public String getBody() {
-		return unfinishedBody;
-	}
-	
-	@Override
-	public String getRequestLine() {
-		return requestLine;
-	}
-	
+    public PartialHttpRequest(String requestLine, Map<String, String> generalHeaders, String body) {
+        super("POST <> Unfinished request\r\n", generalHeaders);
+        this.requestLine = requestLine;
+        unfinishedBody = body;
+    }
+
+    public void appendBody(String nextChunk) {
+        unfinishedBody += nextChunk;
+    }
+
+    @Override
+    public String getBody() {
+        return unfinishedBody;
+    }
+
+    @Override
+    public String getRequestLine() {
+        return requestLine;
+    }
 }

Modified: incubator/deft/sandbox/src/test/java/org/deftserver/web/ApplicationTest.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/test/java/org/deftserver/web/ApplicationTest.java?rev=1153593&r1=1153592&r2=1153593&view=diff
==============================================================================
--- incubator/deft/sandbox/src/test/java/org/deftserver/web/ApplicationTest.java (original)
+++ incubator/deft/sandbox/src/test/java/org/deftserver/web/ApplicationTest.java Wed Aug  3 17:36:25 2011
@@ -30,109 +30,115 @@ import org.deftserver.web.handler.BadReq
 import org.deftserver.web.handler.NotFoundRequestHandler;
 import org.deftserver.web.handler.RequestHandler;
 import org.deftserver.web.http.HttpRequest;
+import org.deftserver.web.http.HttpRequestImpl;
 import org.deftserver.web.http.HttpResponse;
 import org.junit.Test;
 
-
 public class ApplicationTest {
-	
-	@Test
-	public void simpleApplicationTest() {
-		Map<String, RequestHandler> handlers = new HashMap<String, RequestHandler>();
-		final RequestHandler handler1 = new RequestHandler() {
-			@Override public void get(HttpRequest request, HttpResponse response) { }
-		};
-		final RequestHandler handler2 = new RequestHandler() {
-			@Override public void get(HttpRequest request, HttpResponse response) { }
-		};
-		final RequestHandler handler3 = new RequestHandler() {
-			@Override public void get(HttpRequest request, HttpResponse response) { }
-		};
-		final RequestHandler handler4 = new RequestHandler() {
-			@Override public void get(HttpRequest request, HttpResponse response) { }
-		};
-		
-		handlers.put("/", handler1);
-		handlers.put("/persons/([0-9]+)", handler2);
-		handlers.put("/persons/phone_numbers", handler3);
-		handlers.put("/pets/([0-9]{0,3})", handler4);
-		Application app = new Application(handlers);
-		
-		
-		String requestLine = "GET / HTTP/1.1";
-		Map<String, String> headers = new HashMap<String, String>();
-		headers.put("host", "localhost");
-		HttpRequest request = new HttpRequest(requestLine, headers);
-		
-		
-		assertNotNull(app.getHandler(request));
-		
-		requestLine = "GET /persons/1911 HTTP/1.1";
-		request = new HttpRequest(requestLine, headers);
-		assertNotNull(app.getHandler(request));
-		
-		
-		requestLine = "GET /persons/phone_numbers HTTP/1.1";
-		request = new HttpRequest(requestLine, headers);
-		assertNotNull(app.getHandler(request));
-		
-		requestLine = "GET /pets/123 HTTP/1.1";
-		request = new HttpRequest(requestLine, headers);
-		assertNotNull(app.getHandler(request));
-		
-		
-		request = new HttpRequest("GET /missing HTTP/1.1", headers);
-		assertEquals(NotFoundRequestHandler.getInstance(), app.getHandler(request));
-		
-		request = new HttpRequest("GET /persons HTTP/1.1", headers);
-		assertEquals(NotFoundRequestHandler.getInstance(), app.getHandler(request));
-		
-		request = new HttpRequest("GET /persons/roger HTTP/1.1", headers);
-		assertEquals(NotFoundRequestHandler.getInstance(), app.getHandler(request));
-		
-		request = new HttpRequest("GET /persons/123a HTTP/1.1", headers);
-		assertEquals(NotFoundRequestHandler.getInstance(), app.getHandler(request));
-		
-		request = new HttpRequest("GET /persons/a123 HTTP/1.1", headers);
-		assertEquals(NotFoundRequestHandler.getInstance(), app.getHandler(request));
-		
-		request = new HttpRequest("GET /pets/a123 HTTP/1.1", headers);
-		assertEquals(NotFoundRequestHandler.getInstance(), app.getHandler(request));
-		
-		request = new HttpRequest("GET /pets/123a HTTP/1.1", headers);
-		assertEquals(NotFoundRequestHandler.getInstance(), app.getHandler(request));
-		
-		request = new HttpRequest("GET /pets/1234 HTTP/1.1", headers);
-		assertEquals(NotFoundRequestHandler.getInstance(), app.getHandler(request));
-		
-		request = new HttpRequest("GET / HTTP/1.1", headers);
-		assertEquals(handler1, app.getHandler(request));
-		
-		request = new HttpRequest("GET /persons/1911 HTTP/1.1", headers);
-		assertEquals(handler2, app.getHandler(request));
-		
-		request = new HttpRequest("GET /persons/phone_numbers HTTP/1.1", headers);
-		assertEquals(handler3, app.getHandler(request));
-		
-		request = new HttpRequest("GET /pets/123 HTTP/1.1", headers);
-		assertEquals(handler4, app.getHandler(request));
-		
-		//Verify that BadRequestRequestHandler is returned if request does not include Host header
-		headers = new HashMap<String, String>();
-		request = new HttpRequest("GET /pets/123 HTTP/1.1", headers);
-		assertEquals(BadRequestRequestHandler.getInstance(), app.getHandler(request));
-		
-	}
-	
-	@Test(expected=PatternSyntaxException.class)
-	public void malFormedRegularExpressionTest() {
-		Map<String, RequestHandler> handlers = new HashMap<String, RequestHandler>();
-		final RequestHandler handler1 = new RequestHandler() {
-			@Override public void get(HttpRequest request, HttpResponse response) { }
-		};
-		
-		handlers.put("/persons/([[0-9]{0,3})", handler1);	// path contains malformed (a '[' too much) regex
-		Application app = new Application(handlers);
 
-	}
+    @Test
+    public void simpleApplicationTest() {
+        Map<String, RequestHandler> handlers = new HashMap<String, RequestHandler>();
+        final RequestHandler handler1 = new RequestHandler() {
+            @Override
+            public void get(HttpRequest request, HttpResponse response) {
+            }
+        };
+        final RequestHandler handler2 = new RequestHandler() {
+            @Override
+            public void get(HttpRequest request, HttpResponse response) {
+            }
+        };
+        final RequestHandler handler3 = new RequestHandler() {
+            @Override
+            public void get(HttpRequest request, HttpResponse response) {
+            }
+        };
+        final RequestHandler handler4 = new RequestHandler() {
+            @Override
+            public void get(HttpRequest request, HttpResponse response) {
+            }
+        };
+
+        handlers.put("/", handler1);
+        handlers.put("/persons/([0-9]+)", handler2);
+        handlers.put("/persons/phone_numbers", handler3);
+        handlers.put("/pets/([0-9]{0,3})", handler4);
+        Application app = new Application(handlers);
+
+        String requestLine = "GET / HTTP/1.1";
+        Map<String, String> headers = new HashMap<String, String>();
+        headers.put("host", "localhost");
+        HttpRequest request = new HttpRequestImpl(requestLine, headers);
+
+        assertNotNull(app.getHandler(request));
+
+        requestLine = "GET /persons/1911 HTTP/1.1";
+        request = new HttpRequestImpl(requestLine, headers);
+        assertNotNull(app.getHandler(request));
+
+        requestLine = "GET /persons/phone_numbers HTTP/1.1";
+        request = new HttpRequestImpl(requestLine, headers);
+        assertNotNull(app.getHandler(request));
+
+        requestLine = "GET /pets/123 HTTP/1.1";
+        request = new HttpRequestImpl(requestLine, headers);
+        assertNotNull(app.getHandler(request));
+
+        request = new HttpRequestImpl("GET /missing HTTP/1.1", headers);
+        assertEquals(NotFoundRequestHandler.getInstance(), app.getHandler(request));
+
+        request = new HttpRequestImpl("GET /persons HTTP/1.1", headers);
+        assertEquals(NotFoundRequestHandler.getInstance(), app.getHandler(request));
+
+        request = new HttpRequestImpl("GET /persons/roger HTTP/1.1", headers);
+        assertEquals(NotFoundRequestHandler.getInstance(), app.getHandler(request));
+
+        request = new HttpRequestImpl("GET /persons/123a HTTP/1.1", headers);
+        assertEquals(NotFoundRequestHandler.getInstance(), app.getHandler(request));
+
+        request = new HttpRequestImpl("GET /persons/a123 HTTP/1.1", headers);
+        assertEquals(NotFoundRequestHandler.getInstance(), app.getHandler(request));
+
+        request = new HttpRequestImpl("GET /pets/a123 HTTP/1.1", headers);
+        assertEquals(NotFoundRequestHandler.getInstance(), app.getHandler(request));
+
+        request = new HttpRequestImpl("GET /pets/123a HTTP/1.1", headers);
+        assertEquals(NotFoundRequestHandler.getInstance(), app.getHandler(request));
+
+        request = new HttpRequestImpl("GET /pets/1234 HTTP/1.1", headers);
+        assertEquals(NotFoundRequestHandler.getInstance(), app.getHandler(request));
+
+        request = new HttpRequestImpl("GET / HTTP/1.1", headers);
+        assertEquals(handler1, app.getHandler(request));
+
+        request = new HttpRequestImpl("GET /persons/1911 HTTP/1.1", headers);
+        assertEquals(handler2, app.getHandler(request));
+
+        request = new HttpRequestImpl("GET /persons/phone_numbers HTTP/1.1", headers);
+        assertEquals(handler3, app.getHandler(request));
+
+        request = new HttpRequestImpl("GET /pets/123 HTTP/1.1", headers);
+        assertEquals(handler4, app.getHandler(request));
+
+        // Verify that BadRequestRequestHandler is returned if request does not
+        // include Host header
+        headers = new HashMap<String, String>();
+        request = new HttpRequestImpl("GET /pets/123 HTTP/1.1", headers);
+        assertEquals(BadRequestRequestHandler.getInstance(), app.getHandler(request));
+
+    }
+
+    @Test(expected = PatternSyntaxException.class)
+    public void malFormedRegularExpressionTest() {
+        Map<String, RequestHandler> handlers = new HashMap<String, RequestHandler>();
+        final RequestHandler handler1 = new RequestHandler() {
+            @Override
+            public void get(HttpRequest request, HttpResponse response) {
+            }
+        };
+
+        handlers.put("/persons/([[0-9]{0,3})", handler1);
+        new Application(handlers);
+    }
 }

Modified: incubator/deft/sandbox/src/test/java/org/deftserver/web/DeftSystemTest.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/test/java/org/deftserver/web/DeftSystemTest.java?rev=1153593&r1=1153592&r2=1153593&view=diff
==============================================================================
--- incubator/deft/sandbox/src/test/java/org/deftserver/web/DeftSystemTest.java (original)
+++ incubator/deft/sandbox/src/test/java/org/deftserver/web/DeftSystemTest.java Wed Aug  3 17:36:25 2011
@@ -1001,8 +1001,8 @@ public class DeftSystemTest {
         DefaultHttpClient httpclient = new DefaultHttpClient();
         HttpPost httppost = new HttpPost("http://localhost:" + PORT + "/echo");
         httppost.setEntity(new StringEntity(body)); // HTTP 1.1 says that the
-                                                    // default charset is
-                                                    // ISO-8859-1
+        // default charset is
+        // ISO-8859-1
         HttpResponse response = httpclient.execute(httppost);
 
         assertNotNull(response);
@@ -1169,8 +1169,9 @@ public class DeftSystemTest {
                     }
 
                     public void onFailure(Throwable caught) {
-                        if (caught instanceof UnresolvedAddressException)
+                        if (caught instanceof UnresolvedAddressException) {
                             latch.countDown();
+                        }
                         client.close();
                     }
                 });
@@ -1197,8 +1198,9 @@ public class DeftSystemTest {
                     }
 
                     public void onFailure(Throwable caught) {
-                        if (caught instanceof ConnectException)
+                        if (caught instanceof ConnectException) {
                             latch.countDown();
+                        }
                         client.close();
                     }
                 });
@@ -1253,8 +1255,9 @@ public class DeftSystemTest {
             }
 
             public void onFailure(Throwable e) {
-                if (e instanceof ConnectException)
+                if (e instanceof ConnectException) {
                     latch.countDown();
+                }
             }
         };
         // make sure that the http.fetch(..) is invoked from the ioloop thread

Modified: incubator/deft/sandbox/src/test/java/org/deftserver/web/handler/RequestHandlerTest.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/test/java/org/deftserver/web/handler/RequestHandlerTest.java?rev=1153593&r1=1153592&r2=1153593&view=diff
==============================================================================
--- incubator/deft/sandbox/src/test/java/org/deftserver/web/handler/RequestHandlerTest.java (original)
+++ incubator/deft/sandbox/src/test/java/org/deftserver/web/handler/RequestHandlerTest.java Wed Aug  3 17:36:25 2011
@@ -29,56 +29,53 @@ import org.deftserver.web.http.HttpReque
 import org.deftserver.web.http.HttpResponse;
 import org.junit.Test;
 
-
 public class RequestHandlerTest {
 
+    static class RequestHandler1 extends RequestHandler {
 
-	static class RequestHandler1 extends RequestHandler {
-
-		@Override
-		@Asynchronous
-		@Authenticated
-		public void get(HttpRequest request, HttpResponse response) {}
-
-	}
-
-	static class RequestHandler2 extends RequestHandler {
-
-		@Override
-		public void get(HttpRequest request, HttpResponse response) { }
-
-		
-		@Override
-		@Asynchronous
-		@Authenticated
-		public void post(HttpRequest request, HttpResponse response) { }
-
-		
-	}
-
-	@Test
-	public void testAsynchronousAnnotations() {
-		RequestHandler rh1 = new RequestHandler1();
-		RequestHandler rh2 = new RequestHandler2();
-
-		assertTrue(rh1.isMethodAsynchronous(HttpVerb.GET));
-		
-		assertFalse(rh2.isMethodAsynchronous(HttpVerb.GET));
-		assertTrue(rh2.isMethodAsynchronous(HttpVerb.POST));
-	}
-	
-	@Test
-	public void testAuthenticatedAnnotations() {
-		RequestHandler rh1 = new RequestHandler1();
-		RequestHandler rh2 = new RequestHandler2();
-
-		assertTrue(rh1.isMethodAuthenticated(HttpVerb.GET));
-		assertFalse(rh1.isMethodAuthenticated(HttpVerb.POST));
-		assertFalse(rh1.isMethodAuthenticated(HttpVerb.DELETE));
-		
-		assertFalse(rh2.isMethodAuthenticated(HttpVerb.GET));
-		assertFalse(rh2.isMethodAuthenticated(HttpVerb.PUT));
-		assertTrue(rh2.isMethodAuthenticated(HttpVerb.POST));
-	}
-
+        @Override
+        @Asynchronous
+        @Authenticated
+        public void get(HttpRequest request, HttpResponse response) {
+        }
+    }
+
+    static class RequestHandler2 extends RequestHandler {
+
+        @Override
+        public void get(HttpRequest request, HttpResponse response) {
+        }
+
+        @Override
+        @Asynchronous
+        @Authenticated
+        public void post(HttpRequest request, HttpResponse response) {
+        }
+
+    }
+
+    @Test
+    public void testAsynchronousAnnotations() {
+        RequestHandler rh1 = new RequestHandler1();
+        RequestHandler rh2 = new RequestHandler2();
+
+        assertTrue(rh1.isMethodAsynchronous(HttpVerb.GET));
+
+        assertFalse(rh2.isMethodAsynchronous(HttpVerb.GET));
+        assertTrue(rh2.isMethodAsynchronous(HttpVerb.POST));
+    }
+
+    @Test
+    public void testAuthenticatedAnnotations() {
+        RequestHandler rh1 = new RequestHandler1();
+        RequestHandler rh2 = new RequestHandler2();
+
+        assertTrue(rh1.isMethodAuthenticated(HttpVerb.GET));
+        assertFalse(rh1.isMethodAuthenticated(HttpVerb.POST));
+        assertFalse(rh1.isMethodAuthenticated(HttpVerb.DELETE));
+
+        assertFalse(rh2.isMethodAuthenticated(HttpVerb.GET));
+        assertFalse(rh2.isMethodAuthenticated(HttpVerb.PUT));
+        assertTrue(rh2.isMethodAuthenticated(HttpVerb.POST));
+    }
 }



Mime
View raw message