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 [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...
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));
+ }
}