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 el...@apache.org on 2011/07/26 21:45:19 UTC
svn commit: r1151259 - in /incubator/deft/sandbox: ./
src/main/java/org/deftserver/example/
src/main/java/org/deftserver/example/kv/ src/main/java/org/deftserver/io/
src/main/java/org/deftserver/io/buffer/ src/main/java/org/deftserver/util/
src/main/ja...
Author: elecharny
Date: Tue Jul 26 21:45:16 2011
New Revision: 1151259
URL: http://svn.apache.org/viewvc?rev=1151259&view=rev
Log:
Applied patch for DEFT-117
Added:
incubator/deft/sandbox/src/main/java/org/deftserver/example/AsynchronousHttpClientGetExample.java
incubator/deft/sandbox/src/main/java/org/deftserver/example/AsynchronousHttpClientPostExample.java
incubator/deft/sandbox/src/main/java/org/deftserver/util/KnuthMorrisPrattAlgorithm.java
incubator/deft/sandbox/src/main/java/org/deftserver/web/http/ContentType.java
incubator/deft/sandbox/src/test/java/org/deftserver/web/HttpVerbTest.java
incubator/deft/sandbox/src/test/java/org/deftserver/web/http/ContentTypeTest.java
incubator/deft/sandbox/src/test/java/org/deftserver/web/http/client/
incubator/deft/sandbox/src/test/java/org/deftserver/web/http/client/AsynchronousHttpClientTest.java
incubator/deft/sandbox/src/test/java/org/deftserver/web/http/client/RequestTest.java
Modified:
incubator/deft/sandbox/NEWS.txt
incubator/deft/sandbox/src/main/java/org/deftserver/example/AsynchronousHttpClientExample.java
incubator/deft/sandbox/src/main/java/org/deftserver/example/kv/KeyValueStoreClient.java
incubator/deft/sandbox/src/main/java/org/deftserver/example/kv/KeyValueStoreExample.java
incubator/deft/sandbox/src/main/java/org/deftserver/io/AsynchronousSocket.java
incubator/deft/sandbox/src/main/java/org/deftserver/io/buffer/DynamicByteBuffer.java
incubator/deft/sandbox/src/main/java/org/deftserver/web/HttpVerb.java
incubator/deft/sandbox/src/main/java/org/deftserver/web/http/client/AsynchronousHttpClient.java
incubator/deft/sandbox/src/main/java/org/deftserver/web/http/client/Request.java
incubator/deft/sandbox/src/test/java/org/deftserver/io/AsynchronousSocketTest.java
incubator/deft/sandbox/src/test/java/org/deftserver/web/DeftSystemTest.java
Modified: incubator/deft/sandbox/NEWS.txt
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/NEWS.txt?rev=1151259&r1=1151258&r2=1151259&view=diff
==============================================================================
--- incubator/deft/sandbox/NEWS.txt (original)
+++ incubator/deft/sandbox/NEWS.txt Tue Jul 26 21:45:16 2011
@@ -13,6 +13,7 @@ Features / Improvements
- AsynchronousSocket accepts SocketChannel instead of SelectableChannel (DEFT-162)
- Improved error handling in AsynchronousSocket (DEFT-154)
- Bug fix. NPE when using AsynchronousSocket.write (DEFT-155)
+ - Asynchronous HTTP client supports POST and PUT (DEFT-117)
Configuration
-------------
Modified: incubator/deft/sandbox/src/main/java/org/deftserver/example/AsynchronousHttpClientExample.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/example/AsynchronousHttpClientExample.java?rev=1151259&r1=1151258&r2=1151259&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/example/AsynchronousHttpClientExample.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/example/AsynchronousHttpClientExample.java Tue Jul 26 21:45:16 2011
@@ -1,51 +0,0 @@
-/*
- * 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.example;
-
-import static java.lang.System.out;
-
-import org.deftserver.io.IOLoop;
-import org.deftserver.io.timeout.Timeout;
-import org.deftserver.web.AsyncCallback;
-import org.deftserver.web.AsyncResult;
-import org.deftserver.web.http.client.AsynchronousHttpClient;
-import org.deftserver.web.http.client.Response;
-
-public class AsynchronousHttpClientExample {
-
- public static void main(String[] args) {
- AsynchronousHttpClient client = new AsynchronousHttpClient();
- client.fetch("http://sunet.se/",
- new AsyncResult<Response>() {
- public void onFailure(Throwable caught) { out.println("exception:\n" + caught);}
- public void onSuccess(Response response) { out.println("http resonse:\n" + response);}
- }
- );
- IOLoop.INSTANCE.addTimeout(
- new Timeout(
- System.currentTimeMillis() + 1000,
- new AsyncCallback() { public void onCallback() { IOLoop.INSTANCE.stop(); }}
- )
- );
- IOLoop.INSTANCE.start();
- }
-
-
-}
Added: incubator/deft/sandbox/src/main/java/org/deftserver/example/AsynchronousHttpClientGetExample.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/example/AsynchronousHttpClientGetExample.java?rev=1151259&view=auto
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/example/AsynchronousHttpClientGetExample.java (added)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/example/AsynchronousHttpClientGetExample.java Tue Jul 26 21:45:16 2011
@@ -0,0 +1,61 @@
+package org.deftserver.example;
+
+import org.deftserver.io.IOLoop;
+import org.deftserver.io.timeout.Timeout;
+import org.deftserver.web.AsyncCallback;
+import org.deftserver.web.AsyncResult;
+import org.deftserver.web.http.client.AsynchronousHttpClient;
+import org.deftserver.web.http.client.Response;
+
+/**
+ * Example class to demonstrate usage of <code>AsynchronousHttpClient</code>.
+ */
+public class AsynchronousHttpClientGetExample {
+ /**
+ * A simple example of how to use the <code>AsynchronousHttpClient</code>,
+ * taking the following steps:
+ * <ol>
+ * <li>Create an instance of <code>AsyncResult<Response></code> and
+ * define the methods <code>onSuccess</code> and <code>onFailure</code>.
+ * This type will become the callback for our operation, and the actions
+ * defined in the method will be executed when that operation succeeds or
+ * fails respectively. In this case, we will detail the exception on failure
+ * and show the response body on success.
+ * <li>Create an instance of <code>AsynchronousHttpClient</code> and perform
+ * a GET action passing the URL to hit and our previously defined callback
+ * type.
+ * <li>Add time-out value to the current <code>IOLoop</code> of one second,
+ * and in the definition of the <code>onCallback</code> method tell the
+ * instance to stop listening when this time limit has been reached.
+ * <li>Start the instance.
+ * </ol>
+ *
+ * @param args
+ * the arguments to pass; unused.
+ */
+ public static void main(final String[] args) {
+
+ final AsyncResult<Response> callback = new AsyncResult<Response>() {
+
+ @Override
+ public void onSuccess(final Response response) { System.out.println("RESPONSE: " + response); }
+
+ @Override
+ public void onFailure(final Throwable caught) { System.out.println("EXCEPTION: " + caught); }
+ };
+
+ final AsynchronousHttpClient client = new AsynchronousHttpClient();
+ client.get("http://incubator.apache.org/deft/", callback);
+
+ IOLoop.INSTANCE.addTimeout(new Timeout(System.currentTimeMillis() + 1000, new AsyncCallback() {
+ @Override
+ public void onCallback() {
+ System.out.println("INSTANCE.stop()");
+ IOLoop.INSTANCE.stop();
+ }
+ }));
+
+ System.out.println("INSTANCE.start()");
+ IOLoop.INSTANCE.start();
+ }
+ }
\ No newline at end of file
Added: incubator/deft/sandbox/src/main/java/org/deftserver/example/AsynchronousHttpClientPostExample.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/example/AsynchronousHttpClientPostExample.java?rev=1151259&view=auto
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/example/AsynchronousHttpClientPostExample.java (added)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/example/AsynchronousHttpClientPostExample.java Tue Jul 26 21:45:16 2011
@@ -0,0 +1,89 @@
+/*
+ * 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.example;
+
+import org.deftserver.io.IOLoop;
+import org.deftserver.io.timeout.Timeout;
+import org.deftserver.web.AsyncCallback;
+import org.deftserver.web.AsyncResult;
+import org.deftserver.web.HttpVerb;
+import org.deftserver.web.http.ContentType;
+import org.deftserver.web.http.client.AsynchronousHttpClient;
+import org.deftserver.web.http.client.Request;
+import org.deftserver.web.http.client.Response;
+
+/**
+ * Example class to demonstrate usage of <code>AsynchronousHttpClient</code>.
+ */
+public class AsynchronousHttpClientPostExample {
+
+ /**
+ * A simple example of how to use the <code>AsynchronousHttpClient</code>,
+ * taking the following steps:
+ * <ol>
+ * <li>Create an instance of <code>AsyncResult<Response></code> and
+ * define the methods <code>onSuccess</code> and <code>onFailure</code>.
+ * This type will become the callback for our operation, and the actions
+ * defined in the method will be executed when that operation succeeds or
+ * fails respectively. In this case, we will detail the exception on failure
+ * and show the response body on success.
+ * <li>Create a <code>Request</code>, providing target URL and HTTP method
+ * as well as body and content type information.
+ * <li>Create an instance of <code>AsynchronousHttpClient</code> and pass
+ * this <code>Request</code> with our previously defined callback.
+ * <li>Add time-out value to the current <code>IOLoop</code> of one second,
+ * and in the definition of the <code>onCallback</code> method tell the
+ * instance to stop listening when this time limit has been reached.
+ * <li>Start the instance.
+ * </ol>
+ *
+ * @param args
+ * the arguments to pass; unused.
+ */
+ public static void main(final String[] args) {
+
+ final AsyncResult<Response> callback = new AsyncResult<Response>() {
+
+ @Override
+ public void onSuccess(final Response response) { System.out.println("RESPONSE: " + response); }
+
+ @Override
+ public void onFailure(final Throwable caught) { System.out.println("EXCEPTION: " + caught); }
+ };
+
+ Request request = new Request("http://incubator.apache.org/deft/", HttpVerb.POST);
+ request.setBody("paramName=paramValue");
+ request.setContentType(ContentType.APPLICATION_FORM_URLENCODED);
+
+ final AsynchronousHttpClient client = new AsynchronousHttpClient();
+ client.fetch(request, callback);
+
+ IOLoop.INSTANCE.addTimeout(new Timeout(System.currentTimeMillis() + 1000, new AsyncCallback() {
+ @Override
+ public void onCallback() {
+ System.out.println("INSTANCE.stop()");
+ IOLoop.INSTANCE.stop();
+ }
+ }));
+
+ System.out.println("INSTANCE.start()");
+ IOLoop.INSTANCE.start();
+ }
+}
Modified: incubator/deft/sandbox/src/main/java/org/deftserver/example/kv/KeyValueStoreClient.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/example/kv/KeyValueStoreClient.java?rev=1151259&r1=1151258&r2=1151259&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/example/kv/KeyValueStoreClient.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/example/kv/KeyValueStoreClient.java Tue Jul 26 21:45:16 2011
@@ -55,15 +55,15 @@ public class KeyValueStoreClient {
IOLoop.INSTANCE.addHandler(channel, socket, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
}
- public void get(String value, AsyncResult<String> cb) {
- socket.write("GET deft\r\n", new WriteCallback(cb));
+ public void get(String value, AsyncResult<byte[]> cb) {
+ socket.write("GET deft\r\n".getBytes(), new WriteCallback(cb));
}
private class WriteCallback implements AsyncCallback {
- private final AsyncResult<String> cb;
+ private final AsyncResult<byte[]> cb;
- public WriteCallback(AsyncResult<String> cb) {
+ public WriteCallback(AsyncResult<byte[]> cb) {
this.cb = cb;
}
@@ -71,7 +71,7 @@ public class KeyValueStoreClient {
public void onCallback() {
// write is finished. read response from server
logger.debug("readUntil: \r\n");
- socket.readUntil("\r\n", cb);
+ socket.readUntil("\r\n".getBytes(), cb);
}
}
Modified: incubator/deft/sandbox/src/main/java/org/deftserver/example/kv/KeyValueStoreExample.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/example/kv/KeyValueStoreExample.java?rev=1151259&r1=1151258&r2=1151259&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/example/kv/KeyValueStoreExample.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/example/kv/KeyValueStoreExample.java Tue Jul 26 21:45:16 2011
@@ -50,9 +50,9 @@ public class KeyValueStoreExample {
@Override
@Asynchronous
public void get(HttpRequest request, final HttpResponse response) {
- client.get("deft", new AsyncResult<String>() {
+ client.get("deft", new AsyncResult<byte[]>() {
@Override public void onFailure(Throwable caught) { /* ignore */}
- @Override public void onSuccess(String result) { response.write(result).finish(); }
+ @Override public void onSuccess(byte[] result) { response.write(new String(result)).finish(); }
});
}
Modified: incubator/deft/sandbox/src/main/java/org/deftserver/io/AsynchronousSocket.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/io/AsynchronousSocket.java?rev=1151259&r1=1151258&r2=1151259&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/io/AsynchronousSocket.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/io/AsynchronousSocket.java Tue Jul 26 21:45:16 2011
@@ -30,7 +30,9 @@ import java.nio.channels.ServerSocketCha
import java.nio.channels.SocketChannel;
import java.nio.channels.UnresolvedAddressException;
+import org.deftserver.io.buffer.DynamicByteBuffer;
import org.deftserver.util.Closeables;
+import org.deftserver.util.KnuthMorrisPrattAlgorithm;
import org.deftserver.util.NopAsyncResult;
import org.deftserver.web.AsyncCallback;
import org.deftserver.web.AsyncResult;
@@ -45,24 +47,26 @@ public class AsynchronousSocket implemen
private final IOLoop ioLoop;
- private final int DEFAULT_BYTEBUFFER_SIZE = 1024;
+ private static final int DEFAULT_BYTEBUFFER_SIZE = 1024;
+ private static final int DEFAULT_INITIAL_READ_BYTEBUFFER_SIZE = 1024;
+ private static final int DEFAULT_INITIAL_WRITE_BYTEBUFFER_SIZE = 1024;
- private final AsyncResult<String> nopAsyncStringResult = NopAsyncResult.of(String.class).nopAsyncResult;
+ private final AsyncResult<byte[]> nopAsyncByteArrayResult = NopAsyncResult.of(byte[].class).nopAsyncResult;
private final AsyncResult<Boolean> nopAsyncBooleanResult = NopAsyncResult.of(Boolean.class).nopAsyncResult;
private final SocketChannel channel;
private int interestOps;
- private String readDelimiter = "";
+ private byte[] readDelimiter = "".getBytes();
private int readBytes = Integer.MAX_VALUE;
private AsyncResult<Boolean> connectCallback = nopAsyncBooleanResult;
private AsyncCallback closeCallback = AsyncCallback.nopCb;
- private AsyncResult<String> readCallback = nopAsyncStringResult;
+ private AsyncResult<byte[]> readCallback = nopAsyncByteArrayResult;
private AsyncCallback writeCallback = AsyncCallback.nopCb;
- private final StringBuilder readBuffer = new StringBuilder();
- private final StringBuilder writeBuffer = new StringBuilder();
+ private final DynamicByteBuffer readBuffer = DynamicByteBuffer.allocate(DEFAULT_INITIAL_READ_BYTEBUFFER_SIZE);
+ private final DynamicByteBuffer writeBuffer = DynamicByteBuffer.allocate(DEFAULT_INITIAL_WRITE_BYTEBUFFER_SIZE);
private boolean reachedEOF = false;
@@ -185,7 +189,7 @@ public class AsynchronousSocket implemen
@Override
public void handleRead(SelectionKey key) throws IOException {
logger.debug("handle read...");
- ByteBuffer buffer = ByteBuffer.allocate(DEFAULT_BYTEBUFFER_SIZE);
+ ByteBuffer buffer = ByteBuffer.allocate(DEFAULT_BYTEBUFFER_SIZE); // TODO RS 110723 reuse byte buffers
int read = 0;
try {
read = channel.read(buffer);
@@ -201,8 +205,9 @@ public class AsynchronousSocket implemen
ioLoop.updateHandler(channel, interestOps &= ~SelectionKey.OP_READ);
return;
}
- readBuffer.append(new String(buffer.array(), 0, buffer.position(), Charsets.ISO_8859_1));
- logger.debug("readBuffer size: {}", readBuffer.length());
+ buffer.flip();
+ readBuffer.put(buffer);
+ logger.debug("readBuffer size: {}", readBuffer.position());
checkReadState();
}
@@ -219,8 +224,8 @@ public class AsynchronousSocket implemen
* Reads from the underlaying SelectableChannel until delimiter is reached. When it its, the given
* AsyncResult will be invoked.
*/
- public void readUntil(String delimiter, AsyncResult<String> rcb) {
- logger.debug("readUntil delimiter: {}", delimiter);
+ public void readUntil(byte[] delimiter, AsyncResult<byte[]> rcb) {
+ logger.debug("readUntil delimiter: {}", new String(delimiter));
readDelimiter = delimiter;
readCallback = rcb;
checkReadState();
@@ -230,7 +235,7 @@ public class AsynchronousSocket implemen
* Reads from the underlaying SelectableChannel until n bytes are read. When it its, the given
* AsyncResult will be invoked.
*/
- public void readBytes(int n, AsyncResult<String> rcb) {
+ public void readBytes(int n, AsyncResult<byte[]> rcb) {
logger.debug("readBytes #bytes: {}", n);
readBytes = n;
readCallback = rcb;
@@ -247,31 +252,44 @@ public class AsynchronousSocket implemen
invokeReadFailureCallback(new EOFException("Reached end-of-stream"));
return;
}
- int index = readBuffer.indexOf(readDelimiter);
- if (index != -1 && !readDelimiter.isEmpty()) {
- String result = readBuffer.substring(0, index /*+ readDelimiter.length()*/);
- readBuffer.delete(0, index + readDelimiter.length());
- logger.debug("readBuffer size: {}", readBuffer.length());
- readDelimiter = "";
+ int index = KnuthMorrisPrattAlgorithm.indexOf(readBuffer.array(), 0, readBuffer.position(), readDelimiter);
+ if (index != -1 && readDelimiter.length > 0) {
+ byte[] result = getResult(index, readDelimiter.length);
+ readDelimiter = "".getBytes();
invokeReadSuccessfulCallback(result);
- } else if (readBuffer.length() >= readBytes) {
- String result = readBuffer.substring(0, readBytes);
- readBuffer.delete(0, readBytes);
- logger.debug("readBuffer size: {}", readBuffer.length());
+ } else if (readBuffer.position() >= readBytes) {
+ byte[] result = getResult(readBytes, 0);
readBytes = Integer.MAX_VALUE;
invokeReadSuccessfulCallback(result);
}
}
-
- private void invokeReadSuccessfulCallback(String result) {
- AsyncResult<String> cb = readCallback;
- readCallback = nopAsyncStringResult;
+
+ /**
+ * Returns the resulting byte[] data that was requested by the client through readUntil(..) or readBytes(..)
+ *
+ * @param size Number of bytes to fetch and remove from the read buffer.
+ * @param advance The number of bytes the read buffer's position should move forward after the data has been fetched.
+ * (To ignore the readDelimiter.)
+ */
+ private byte[] getResult(int size, int advance) {
+ readBuffer.flip();
+ byte[] result = new byte[size];
+ readBuffer.get(result, 0, size);
+ readBuffer.position(readBuffer.position() + advance); // ignore the delimiter (if it was a readUntil(..) call)
+ readBuffer.compact(); // "delete" the result data (data after result is left intact and will not be overwritten)
+ logger.debug("readBuffer size: {}", readBuffer.position());
+ return result;
+ }
+
+ private void invokeReadSuccessfulCallback(byte[] result) {
+ AsyncResult<byte[]> cb = readCallback;
+ readCallback = nopAsyncByteArrayResult;
cb.onSuccess(result);
}
private void invokeReadFailureCallback(Exception e) {
- AsyncResult<String> cb = readCallback;
- readCallback = nopAsyncStringResult;
+ AsyncResult<byte[]> cb = readCallback;
+ readCallback = nopAsyncByteArrayResult;
cb.onFailure(e);
}
@@ -303,10 +321,10 @@ public class AsynchronousSocket implemen
* Writes the given data to the underlaying SelectableChannel. When all data is successfully transmitted, the given
* AsyncCallback will be invoked
*/
- public void write(String data, AsyncCallback wcb) {
- logger.debug("write data: {}", data);
- writeBuffer.append(data);
- logger.debug("writeBuffer size: {}", writeBuffer.length());
+ public void write(byte[] data, AsyncCallback wcb) {
+ logger.debug("write data: {}", new String(data));
+ writeBuffer.put(data);
+ logger.debug("writeBuffer size: {}", writeBuffer.position());
writeCallback = wcb;
doWrite();
}
@@ -318,7 +336,9 @@ public class AsynchronousSocket implemen
int written = 0;
try {
if (channel.isConnected()) {
- written = channel.write(ByteBuffer.wrap(writeBuffer.toString().getBytes()));
+ writeBuffer.flip(); // prepare for write
+ written = channel.write(writeBuffer.getByteBuffer());
+ writeBuffer.compact(); // // make room for more data be "read" in
}
} catch (IOException e) {
logger.error("IOException during write: {}", e.getMessage());
@@ -326,10 +346,9 @@ public class AsynchronousSocket implemen
Closeables.closeQuietly(ioLoop, channel);
return;
}
- writeBuffer.delete(0, written);
logger.debug("wrote: {} bytes", written);
- logger.debug("writeBuffer size: {}", writeBuffer.length());
- if (writeBuffer.length() > 0) {
+ logger.debug("writeBuffer size: {}", writeBuffer.position());
+ if (writeBuffer.position() > 0) {
ioLoop.updateHandler(channel, interestOps |= SelectionKey.OP_WRITE);
} else {
ioLoop.updateHandler(channel, interestOps &= ~SelectionKey.OP_WRITE);
Modified: incubator/deft/sandbox/src/main/java/org/deftserver/io/buffer/DynamicByteBuffer.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/io/buffer/DynamicByteBuffer.java?rev=1151259&r1=1151258&r2=1151259&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/io/buffer/DynamicByteBuffer.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/io/buffer/DynamicByteBuffer.java Tue Jul 26 21:45:16 2011
@@ -51,6 +51,14 @@ public class DynamicByteBuffer {
ensureCapacity(src.length);
backend.put(src);
}
+
+ /**
+ * Append the bytes from the given src. Will reallocate if needed.
+ */
+ public void put(ByteBuffer src) {
+ ensureCapacity(src.limit());
+ backend.put(src);
+ }
/**
@@ -97,6 +105,17 @@ public class DynamicByteBuffer {
public ByteBuffer getByteBuffer() {
return backend;
}
+
+ /**
+ * See {@link ByteBuffer#get(byte[], int, int)}
+ */
+ public void get(byte[] dst, int offset, int length) {
+ backend.get(dst, offset, length);
+ }
+
+ public void position(int newPosition) {
+ backend.position(newPosition);
+ }
/**
* See {@link ByteBuffer#flip}
Added: incubator/deft/sandbox/src/main/java/org/deftserver/util/KnuthMorrisPrattAlgorithm.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/util/KnuthMorrisPrattAlgorithm.java?rev=1151259&view=auto
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/util/KnuthMorrisPrattAlgorithm.java (added)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/util/KnuthMorrisPrattAlgorithm.java Tue Jul 26 21:45:16 2011
@@ -0,0 +1,76 @@
+/*
+ * 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.util;
+
+/**
+ * The Knuth Morris Pratt string searching algorithm (or KMP algorithm) searches for occurrences of
+ * a "word" W within a main "text string" S by employing the observation that when a mismatch occurs,
+ * the word itself embodies sufficient information to determine where the next match could begin,
+ * thus bypassing re-examination of previously matched characters.
+ *
+ * The algorithm was conceived by Donald Knuth and Vaughan Pratt and independently by James H. Morris
+ * in 1977, but the three published it jointly.
+ *
+ */
+
+public class KnuthMorrisPrattAlgorithm {
+
+ /**
+ * Search for pattern in data, [start, end). Returns -1 if no match is found or if pattern is of length 0.
+ */
+ public static int indexOf(byte[] data, int start, int end, byte[] pattern) {
+ if (pattern.length == 0) {
+ return -1;
+ }
+ int[] failure = failure(pattern);
+
+ int j = 0;
+
+ for (int i = 0; i < end; i++) {
+ while (j > 0 && pattern[j] != data[i]) {
+ j = failure[j - 1];
+ }
+ if (pattern[j] == data[i]) {
+ j++;
+ }
+ if (j == pattern.length) {
+ return i - pattern.length + 1;
+ }
+ }
+ return -1;
+ }
+
+ private static int[] failure(byte[] pattern) {
+ int[] failure = new int[pattern.length];
+
+ int j = 0;
+ for (int i = 1; i < pattern.length; i++) {
+ while (j>0 && pattern[j] != pattern[i]) {
+ j = failure[j - 1];
+ }
+ if (pattern[j] == pattern[i]) {
+ j++;
+ }
+ failure[i] = j;
+ }
+
+ return failure;
+ }
+}
Modified: incubator/deft/sandbox/src/main/java/org/deftserver/web/HttpVerb.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/web/HttpVerb.java?rev=1151259&r1=1151258&r2=1151259&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/web/HttpVerb.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/web/HttpVerb.java Tue Jul 26 21:45:16 2011
@@ -19,14 +19,19 @@
*/
package org.deftserver.web;
+/**
+ * <code>Enumeration</code> of all available HTTP verbs, as defined by <a
+ * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html">Hypertext
+ * Transfer Protocol -- HTTP/1.1 (RFC 2616)</a>.
+ */
public enum HttpVerb {
-
- GET,
- HEAD,
- POST,
- PUT,
- DELETE,
- OPTIONS,
- TRACE,
- CONNECT
+
+ GET,
+ HEAD,
+ POST,
+ PUT,
+ DELETE,
+ OPTIONS,
+ TRACE,
+ CONNECT;
}
Added: incubator/deft/sandbox/src/main/java/org/deftserver/web/http/ContentType.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/web/http/ContentType.java?rev=1151259&view=auto
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/web/http/ContentType.java (added)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/web/http/ContentType.java Tue Jul 26 21:45:16 2011
@@ -0,0 +1,51 @@
+/*
+ * 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;
+
+/**
+ * A collection of known content types, for convenience.
+ */
+public class ContentType {
+
+ /** application/xml */
+ public static final String APPLICATION_XML = "application/xml";
+
+ /** application/json */
+ public static final String APPLICATION_JSON = "application/json";
+
+ /** application/x-www-form-urlencoded */
+ public static final String APPLICATION_FORM_URLENCODED = "application/x-www-form-urlencoded";
+
+ /** application/octet-stream */
+ public static final String APPLICATION_OCTET_STREAM = "application/octet-stream";
+
+ /** multipart/form-data */
+ public static final String MULTIPART_FORM_DATA = "multipart/form-data";
+
+ /** text/html */
+ public static final String TEXT_HTML = "text/html";
+
+ /** text/plain */
+ public static final String TEXT_PLAIN = "text/plain";
+
+ /** text/xml */
+ public static final String TEXT_XML = "text/xml";
+
+}
Modified: incubator/deft/sandbox/src/main/java/org/deftserver/web/http/client/AsynchronousHttpClient.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/web/http/client/AsynchronousHttpClient.java?rev=1151259&r1=1151258&r2=1151259&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/web/http/client/AsynchronousHttpClient.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/web/http/client/AsynchronousHttpClient.java Tue Jul 26 21:45:16 2011
@@ -34,251 +34,363 @@ import org.deftserver.web.HttpVerb;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Charsets;
+
/**
-* This class implements a simple HTTP 1.1 client on top of Deft's {@code AsynchronousSocket}.
-* It does not currently implement all applicable parts of the HTTP
-* specification.
-* <pre>
-* E.g the following is not supported.
-* - POST and PUT
-*
-* </pre>
-* This class has not been tested extensively in production and
-* should be considered experimental as of the release of
-* Deft 0.3.
-*
-* This http client is inspired by https://github.com/facebook/tornado/blob/master/tornado/simple_httpclient.py
-* and part of the documentation is simply copy pasted.
-*/
+ * This class implements a simple HTTP 1.1 client on top of Deft's {@code
+ * AsynchronousSocket}. It does not currently implement all applicable parts of
+ * the HTTP specification.
+ *
+ * <pre>
+ * E.g the following is not supported.
+ * - keep-alive
+ *
+ * </pre>
+ *
+ * This class has not been tested extensively in production and should be
+ * considered experimental as of the release of Deft 0.3. This http client is
+ * inspired by
+ * https://github.com/facebook/tornado/blob/master/tornado/simple_httpclient.py
+ * and part of the documentation is simply copy pasted.
+ */
public class AsynchronousHttpClient {
- private static final Logger logger = LoggerFactory.getLogger(AsynchronousHttpClient.class);
+ /** The <code>Logger</code>. */
+ private static final Logger logger = LoggerFactory.getLogger(AsynchronousHttpClient.class);
+
+ private static final long TIMEOUT = 15 * 1000; // 15s
+
+ private static final AsyncResult<Response> nopAsyncResult = NopAsyncResult.of(Response.class).nopAsyncResult;
+
+ private AsynchronousSocket socket;
+
+ private Request request;
+ private long requestStarted;
+ private Response response;
+ private AsyncResult<Response> responseCallback;
+
+ private Timeout timeout;
+
+ private final IOLoop ioLoop;
+
+ static final String HTTP_VERSION = "HTTP/1.1\r\n";
+ static final String USER_AGENT_HEADER = "User-Agent: Deft AsynchronousHttpClient/0.2-SNAPSHOT\r\n";
+ static final String NEWLINE = "\r\n";
+
+ /**
+ * Create an instance of this type.
+ */
+ public AsynchronousHttpClient() {
+ this(IOLoop.INSTANCE);
+ }
+
+ /**
+ * Create an instance of this type, utilising the given <code>IOLoop</code>.
+ *
+ * @param ioLoop
+ * the <code>IOLoop</code> to use.
+ */
+ public AsynchronousHttpClient(final IOLoop ioLoop) {
+ this.ioLoop = ioLoop;
+ }
+
+ /**
+ * Make an asynchronous call as per the given <code>Request</code>, invoking
+ * the passed callback upon completion.
+ *
+ * @param request
+ * the definition of the request to make.
+ * @param callback
+ * the callback to execute when the response is received.
+ */
+ public void fetch(final Request request, final AsyncResult<Response> callback) {
+ this.request = request;
+ doFetch(callback, System.currentTimeMillis());
+ }
+
+ /**
+ * Make an asynchronous HTTP GET request against the specified URL, and
+ * invoke the given callback when the response upon completion.
+ *
+ * @param url
+ * the URL from which to request, e.g.
+ * <em>http://incubator.apache.org/deft/</em>.
+ * @param callback
+ * the callback to execute when the response is received.
+ */
+ public void get(final String url, final AsyncResult<Response> callback) {
+ request = new Request(url, HttpVerb.GET);
+ doFetch(callback, System.currentTimeMillis());
+ }
+
+ /**
+ * Make an asynchronous HTTP POST request against the specified URL, and
+ * invoke the given callback when the response upon completion.
+ *
+ * @param url
+ * the URL from which to request, e.g.
+ * <em>http://incubator.apache.org/deft/</em>.
+ * @param body
+ * the message body to pass.
+ * @param callback
+ * the callback to execute when the response is received.
+ */
+ public void post(final String url, final String body, final AsyncResult<Response> callback) {
+ request = new Request(url, HttpVerb.POST);
+ request.setBody(body);
+ doFetch(callback, System.currentTimeMillis());
+ }
+
+ public void post(final String url, final byte[] body, final AsyncResult<Response> callback) {
+ request = new Request(url, HttpVerb.POST);
+ request.setBody(body);
+ doFetch(callback, System.currentTimeMillis());
+ }
+
+ /**
+ * Make an asynchronous HTTP PUT request against the specified URL, and
+ * invoke the given callback when the response upon completion.
+ *
+ * @param url
+ * the URL from which to request, e.g.
+ * <em>http://incubator.apache.org/deft/</em>.
+ * @param body
+ * the message body to pass.
+ * @param callback
+ * the callback to execute when the response is received.
+ */
+ public void put(final String url, final String body, final AsyncResult<Response> callback) {
+ request = new Request(url, HttpVerb.PUT);
+ request.setBody(body);
+ doFetch(callback, System.currentTimeMillis());
+ }
+
+ /**
+ * Perform the action of making a request against a known URL, before
+ * invoking the given callback type.
+ *
+ * @param callback
+ * the callback to execute when the response is received.
+ * @param requestStarted
+ * the current time in milliseconds at which the request was
+ * begun.
+ */
+ protected void doFetch(final AsyncResult<Response> callback, final long requestStarted) {
+
+ this.requestStarted = requestStarted;
+ try {
+ socket = new AsynchronousSocket(SocketChannel.open());
+ } catch (final IOException e) {
+ logger.error("Error opening SocketChannel: {}" + e.getMessage());
+ }
+
+ responseCallback = callback;
+ int port = request.getURL().getPort();
+ port = port == -1 ? 80 : port;
+
+ startTimeout();
+ socket.connect(
+ request.getURL().getHost(),
+ port,
+ new AsyncResult<Boolean>() {
+ public void onFailure(final Throwable t) { onConnectFailure(t); }
+ public void onSuccess(final Boolean result) { onConnect(); }
+ });
+ }
+
+ /**
+ * Close the underlaying {@code AsynchronousSocket}.
+ */
+ public void close() {
+ logger.debug("Closing http client connection...");
+ socket.close();
+ }
+
+ private void startTimeout() {
+ logger.debug("start timeout...");
+ timeout = new Timeout(
+ System.currentTimeMillis() + TIMEOUT,
+ new AsyncCallback() { public void onCallback() { onTimeout(); }}
+ );
+ ioLoop.addTimeout(timeout);
+ }
+
+ private void cancelTimeout() {
+ logger.debug("cancel timeout...");
+ timeout.cancel();
+ timeout = null;
+ }
+
+ private void onTimeout() {
+ logger.debug("Pending operation (connect, read or write) timed out...");
+ final AsyncResult<Response> cb = responseCallback;
+ responseCallback = nopAsyncResult;
+ cb.onFailure(new TimeoutException("Connection timed out"));
+ close();
+ }
+
+ private void onConnect() {
+ logger.debug("Connected...");
+ cancelTimeout();
+ startTimeout();
+ socket.write(
+ makeRequestLineAndHeaders().getBytes(),
+ new AsyncCallback() { public void onCallback() { onWriteComplete(); }
+ });
+ }
+
+ private void onConnectFailure(final Throwable t) {
+ logger.debug("Connect failed...");
+ cancelTimeout();
+ final AsyncResult<Response> cb = responseCallback;
+ responseCallback = nopAsyncResult;
+ cb.onFailure(t);
+ close();
+ }
+
+ /**
+ * Create the request lines and header, populating the body where not
+ * <code>null</code>. Ignoring origin of inputs, the output for a POST
+ * request might resemble:
+ * <p>
+ * <code>
+ * POST /path/to/target HTTP/1.1<br/>
+ * Host: hostname<br/>
+ * User-Agent: Deft AsynchronousHttpClient/0.x-SNAPSHOT<br/>
+ * Content-Type: application/x-www-form-urlencoded<br/>
+ * Content-Length: 20<br/>
+ * <br/>
+ * paramName=paramValue<br/>
+ * </code>
+ * </p>
+ *
+ * @see HttpVerb#hasRequestBody(HttpVerb)
+ */
+ String makeRequestLineAndHeaders() {
+
+ int length = request.getBody() == null ? 0 : request.getBody().length;
+ final StringBuilder builder = new StringBuilder(length + 1024);
+ builder.append(request.getVerb()).append(" ").append(request.getURL().getPath()).append(" ");
+ builder.append(HTTP_VERSION).append("Host: ").append(request.getURL().getHost()).append(NEWLINE);
+ builder.append(USER_AGENT_HEADER);
+
+ if (request.getBody() != null) {
+ builder.append("Content-Type: ").append(request.getContentType().toString()).append(NEWLINE);
+ builder.append("Content-Length: ").append(length);
+ builder.append(NEWLINE).append(NEWLINE);
+ builder.append(new String(request.getBody(), Charsets.ISO_8859_1));
+ }
+
+ builder.append(NEWLINE);
+ return builder.toString();
+ }
+
+ private void onWriteComplete() {
+ logger.debug("onWriteComplete...");
+ cancelTimeout();
+ startTimeout();
+ socket.readUntil(
+ "\r\n\r\n".getBytes(), /* header delimiter */
+ new NaiveAsyncResult() { public void onSuccess(final byte[] headers) { onHeaders(headers); }}
+ );
+ }
+
+ private void onHeaders(final byte[] rawResult) {
+ String result = new String(rawResult, Charsets.ISO_8859_1);
+ logger.debug("headers: {}", result);
+ cancelTimeout();
+ response = new Response(requestStarted);
+ final String[] headers = result.split("\r\n");
+ response.setStatuLine(headers[0]); // first entry contains status
+ // line
+ // (e.g. HTTP/1.1 200 OK)
+ for (int i = 1; i < headers.length; i++) {
+ final String[] header = headers[i].split(": ");
+ response.setHeader(header[0], header[1]);
+ }
+
+ final String contentLength = response.getHeader("Content-Length");
+ startTimeout();
+ if (contentLength != null) {
+ socket.readBytes(
+ Integer.parseInt(contentLength),
+ new NaiveAsyncResult() { public void onSuccess(byte[] body) { onBody(body);}
+ });
+ } else { // Transfer-Encoding: chunked
+ socket.readUntil(
+ NEWLINE.getBytes(), /* chunk delimiter */
+ new NaiveAsyncResult() { public void onSuccess(byte[] octet) { onChunkOctet(octet);
+ }
+ });
+ }
+ }
+
+ /**
+ * JM: TODO, especially noting the redirects we follow....
+ */
+ private void onBody(final byte[] rawBody) {
+ final String body = new String(rawBody, Charsets.ISO_8859_1);
+ logger.debug("body size: {}", body.length());
+ cancelTimeout();
+ response.setBody(body);
+ if ((response.getStatusLine().contains("301") || response.getStatusLine().contains("302"))
+ && request.isFollowingRedirects() && request.getMaxRedirects() > 0) {
+ final String newUrl = UrlUtil.urlJoin(request.getURL(), response.getHeader("Location"));
+ request = new Request(newUrl, request.getVerb(), true, request.getMaxRedirects() - 1);
+ logger.debug("Following redirect, new url: {}, redirects left: {}", newUrl, request.getMaxRedirects());
+ doFetch(responseCallback, requestStarted);
+ } else {
+ close();
+ invokeResponseCallback();
+ }
+ }
+
+ private void onChunk(final byte[] rawChunk) {
+ final String chunk = new String(rawChunk, Charsets.ISO_8859_1);
+ logger.debug("chunk size: {}", chunk.length());
+ cancelTimeout();
+ response.addChunk(chunk.substring(0, chunk.length() - NEWLINE.length()));
+ startTimeout();
+ socket.readUntil(
+ NEWLINE.getBytes(), /* chunk delimiter */
+ new NaiveAsyncResult() {public void onSuccess(final byte[] octet) { onChunkOctet(octet);
+ }
+ });
+ }
+
+ private void onChunkOctet(final byte[] rawOctet) {
+ final String octet = new String(rawOctet, Charsets.ISO_8859_1);
+ final int readBytes = Integer.parseInt(octet, 16);
+ logger.debug("chunk octet: {} (decimal: {})", octet, readBytes);
+ cancelTimeout();
+ startTimeout();
+ if (readBytes != 0) {
+ socket.readBytes(
+ readBytes + NEWLINE.length(), // chunk delimiter is \r\n
+ new NaiveAsyncResult() { public void onSuccess(final byte[] chunk) { onChunk(chunk); }}
+ );
+ } else {
+ onBody(response.getBody().getBytes(Charsets.ISO_8859_1));
+ }
+ }
+
+ private void invokeResponseCallback() {
+ final AsyncResult<Response> cb = responseCallback;
+ responseCallback = nopAsyncResult;
+ cb.onSuccess(response);
+ }
+
+ /**
+ * Naive because all it does when an exception is thrown is log the
+ * exception.
+ */
+ private abstract class NaiveAsyncResult implements AsyncResult<byte[]> {
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ logger.debug("onFailure: {}", caught);
+ }
- private static final long TIMEOUT = 15 * 1000; // 15s
-
- private static final AsyncResult<Response> nopAsyncResult = NopAsyncResult.of(Response.class).nopAsyncResult;
-
- private AsynchronousSocket socket;
-
- private Request request;
- private long requestStarted;
- private Response response;
- private AsyncResult<Response> responseCallback;
-
- private Timeout timeout;
-
- private final IOLoop ioLoop;
-
- private static final String HTTP_VERSION = "HTTP/1.1\r\n";
- private static final String USER_AGENT_HEADER = "User-Agent: Deft AsynchronousHttpClient/0.2-SNAPSHOT\r\n";
- private static final String NEWLINE = "\r\n";
-
- public AsynchronousHttpClient() {
- this(IOLoop.INSTANCE);
- }
-
- public AsynchronousHttpClient(IOLoop ioLoop) {
- this.ioLoop = ioLoop;
- }
-
- /**
- * Makes an asynchronous HTTP GET request against the specified url and invokes the given
- * callback when the response is fetched.
- *
- * @param url e.g "http://tt.se:80/start/"
- * @param cb callback that will be executed when the response is received.
- */
- public void fetch(String url, AsyncResult<Response> cb) {
- request = new Request(url, HttpVerb.GET);
- doFetch(cb, System.currentTimeMillis());
- }
-
- public void fetch(Request request, AsyncResult<Response> cb) {
- this.request = request;
- doFetch(cb, System.currentTimeMillis());
- }
-
- private void doFetch(AsyncResult<Response> cb, long requestStarted) {
- this.requestStarted = requestStarted;
- try {
- socket = new AsynchronousSocket(SocketChannel.open());
- } catch (IOException e) {
- logger.error("Error opening SocketChannel: {}" + e.getMessage());
- }
- responseCallback = cb;
- int port = request.getURL().getPort();
- port = port == -1 ? 80 : port;
- startTimeout();
- socket.connect(
- request.getURL().getHost(),
- port,
- new AsyncResult<Boolean>() {
- public void onFailure(Throwable t) { onConnectFailure(t); }
- public void onSuccess(Boolean result) { onConnect(); }
- }
- );
- }
-
- /**
- * Close the underlaying {@code AsynchronousSocket}.
- */
- public void close() {
- logger.debug("Closing http client connection...");
- socket.close();
- }
-
- private void startTimeout() {
- logger.debug("start timeout...");
- timeout = new Timeout(
- System.currentTimeMillis() + TIMEOUT,
- new AsyncCallback() { public void onCallback() { onTimeout(); } }
- );
- ioLoop.addTimeout(timeout);
- }
-
- private void cancelTimeout() {
- logger.debug("cancel timeout...");
- timeout.cancel();
- timeout = null;
- }
-
- private void onTimeout() {
- logger.debug("Pending operation (connect, read or write) timed out...");
- AsyncResult<Response> cb = responseCallback;
- responseCallback = nopAsyncResult;
- cb.onFailure(new TimeoutException("Connection timed out"));
- close();
- }
-
- private void onConnect() {
- logger.debug("Connected...");
- cancelTimeout();
- startTimeout();
- socket.write(
- makeRequestLineAndHeaders(),
- new AsyncCallback() { public void onCallback() { onWriteComplete(); }}
- );
- }
-
- private void onConnectFailure(Throwable t) {
- logger.debug("Connect failed...");
- cancelTimeout();
- AsyncResult<Response> cb = responseCallback;
- responseCallback = nopAsyncResult;
- cb.onFailure(t);
- close();
- }
-
- /**
- *
- * @return Eg.
- * GET /path/to/file/index.html HTTP/1.0
- * From: a@b.com
- * User-Agent: HTTPTool/1.0
- *
- */
- private String makeRequestLineAndHeaders() {
- return request.getVerb() + " " + request.getURL().getPath() + " " + HTTP_VERSION +
- "Host: " + request.getURL().getHost() + "\r\n" +
- USER_AGENT_HEADER +
- NEWLINE;
- }
-
- private void onWriteComplete() {
- logger.debug("onWriteComplete...");
- cancelTimeout();
- startTimeout();
- socket.readUntil(
- "\r\n\r\n", /* header delimiter */
- new NaiveAsyncResult() { public void onSuccess(String headers) { onHeaders(headers); }
- });
- }
-
- private void onHeaders(String result) {
- logger.debug("headers: {}", result);
- cancelTimeout();
- response = new Response(requestStarted);
- String[] headers = result.split("\r\n");
- response.setStatuLine(headers[0]); // first entry contains status line (e.g. HTTP/1.1 200 OK)
- for (int i = 1; i < headers.length; i++) {
- String[] header = headers[i].split(": ");
- response.setHeader(header[0], header[1]);
- }
-
- String contentLength = response.getHeader("Content-Length");
- startTimeout();
- if (contentLength != null) {
- socket.readBytes(
- Integer.parseInt(contentLength),
- new NaiveAsyncResult() { public void onSuccess(String body) { onBody(body); } }
- );
- } else { // Transfer-Encoding: chunked
- socket.readUntil(
- NEWLINE, /* chunk delimiter*/
- new NaiveAsyncResult() { public void onSuccess(String octet) { onChunkOctet(octet); } }
- );
- }
- }
-
- private void onBody(String body) {
- logger.debug("body size: {}", body.length());
- cancelTimeout();
- response.setBody(body);
- if ((response.getStatusLine().contains("301") || response.getStatusLine().contains("302")) &&
- request.isFollowingRedirects() &&
- request.getMaxRedirects() > 0) {
- String newUrl = UrlUtil.urlJoin(request.getURL(), response.getHeader("Location"));
- request = new Request(newUrl, HttpVerb.valueOf(request.getVerb()), true, request.getMaxRedirects() - 1);
- logger.debug("Following redirect, new url: {}, redirects left: {}", newUrl, request.getMaxRedirects());
- doFetch(responseCallback, requestStarted);
- } else {
- close();
- invokeResponseCallback();
- }
- }
-
- private void onChunk(String chunk) {
- logger.debug("chunk size: {}", chunk.length());
- cancelTimeout();
- response.addChunk(chunk.substring(0, chunk.length() - NEWLINE.length()));
- startTimeout();
- socket.readUntil(
- NEWLINE, /* chunk delimiter*/
- new NaiveAsyncResult() { public void onSuccess(String octet) { onChunkOctet(octet); } }
- );
- }
-
- private void onChunkOctet(String octet) {
- int readBytes = Integer.parseInt(octet, 16);
- logger.debug("chunk octet: {} (decimal: {})", octet, readBytes);
- cancelTimeout();
- startTimeout();
- if (readBytes != 0) {
- socket.readBytes(
- readBytes + NEWLINE.length(), // chunk delimiter is \r\n
- new NaiveAsyncResult() { public void onSuccess(String chunk) { onChunk(chunk); } }
- );
- } else {
- onBody(response.getBody());
- }
- }
-
- private void invokeResponseCallback() {
- AsyncResult<Response> cb = responseCallback;
- responseCallback = nopAsyncResult;
- cb.onSuccess(response);
- }
-
- /**
- * Naive because all it does when an exception is thrown is log the exception.
- */
- private abstract class NaiveAsyncResult implements AsyncResult<String> {
-
- @Override
- public void onFailure(Throwable caught) {
- logger.debug("onFailure: {}", caught);
- }
-
- }
+ }
}
Modified: incubator/deft/sandbox/src/main/java/org/deftserver/web/http/client/Request.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/main/java/org/deftserver/web/http/client/Request.java?rev=1151259&r1=1151258&r2=1151259&view=diff
==============================================================================
--- incubator/deft/sandbox/src/main/java/org/deftserver/web/http/client/Request.java (original)
+++ incubator/deft/sandbox/src/main/java/org/deftserver/web/http/client/Request.java Tue Jul 26 21:45:16 2011
@@ -23,60 +23,168 @@ import java.net.MalformedURLException;
import java.net.URL;
import org.deftserver.web.HttpVerb;
+import org.deftserver.web.http.ContentType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Request {
-
- private static final Logger logger = LoggerFactory.getLogger(Request.class);
- private final URL url;
- private final HttpVerb verb;
- private boolean followRedirects = true;
- private int maxRedirects = 7;
-
- /**
- * An HTTP {@code Request} that follows (at most 7) redirects (= HTTP status codes 301, 302)
- */
- public Request(String url, HttpVerb verb) {
- try {
- this.url = new URL(url);
- this.verb = verb;
- } catch (MalformedURLException e) {
- logger.error("Malformed URL: {}", e.getMessage());
- throw new RuntimeException(e);
- }
- }
-
- public Request(String url, HttpVerb verb, boolean followRedirects, int maxRedirects) {
- this(url, verb);
- this.followRedirects = followRedirects;
- this.maxRedirects = maxRedirects;
-
- }
-
- public URL getURL() {
- return url;
- }
-
- /**
- *
- * @return The verb (method) name, e.g. "GET" or "POST"
- */
- public String getVerb() {
- return verb.name();
- }
-
- /**
- *
- * @return The maximum number of redirects this {@code Request} follows.
- */
- public int getMaxRedirects() {
- return maxRedirects;
- }
-
- public boolean isFollowingRedirects() {
- return followRedirects;
- }
-
+ /** The <code>Logger</code>. */
+ private static final Logger logger = LoggerFactory.getLogger(Request.class);
+
+ /** The <code>URL</code> associated with the current <code>Request</code>. */
+ private final URL url;
+
+ /** The request type. */
+ private final HttpVerb verb;
+
+ /**
+ * Indicates whether 3xx Redirection codes should be followed; defaults to
+ * <code>true</code>.
+ *
+ * @see AsynchronousHttpClient#onBody
+ */
+ private boolean followRedirects = true;
+
+ /**
+ * The maximum number of redirects to follow, where enabled; defaults to 7.
+ *
+ * @see #followRedirects
+ */
+ private int maxRedirects = 7;
+
+ /** The body associated with the request. */
+ private byte[] body;
+
+ /** The type of content represented by this request. */
+ private String contentType = ContentType.APPLICATION_FORM_URLENCODED;
+
+ /**
+ * Create an instance of this type with the given <code>URL</code> and
+ * <code>HttpVerb</code>. Follows redirects and to a count as specified by
+ * default.
+ *
+ * @throws RuntimeException
+ * where a {@link MalformedURLException} is caught.
+ * @see #Request(String, HttpVerb, boolean, int)
+ */
+ public Request(final String url, final HttpVerb verb) {
+ try {
+ this.url = new URL(url);
+ this.verb = verb;
+ } catch (final MalformedURLException e) {
+ logger.error("Malformed URL: {}", e.getMessage());
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Create an instance of this type with the given <code>URL</code> and
+ * <code>HttpVerb</code>. Redirection behaviour and count are as specified.
+ *
+ * @param url
+ * the <code>URL</code> for the request.
+ * @param verb
+ * the type of request to be made.
+ * @param followRedirects
+ * <code>true</code> if redirects are to be followed;
+ * <code>false</code> otherwise.
+ * @param maxRedirects
+ * where redirects should be followed, the maximum number to
+ * follow.
+ * @see #Request(String, HttpVerb)
+ * @see AsynchronousHttpClient#onBody
+ */
+ public Request(final String url, final HttpVerb verb, final boolean followRedirects, final int maxRedirects) {
+ this(url, verb);
+ this.followRedirects = followRedirects;
+ this.maxRedirects = maxRedirects;
+ }
+
+ /**
+ * Retrieve the <code>URL</code> associated with this <code>Request</code>.
+ *
+ * @return the associated <code>URL</code>.
+ */
+ public URL getURL() {
+ return this.url;
+ }
+
+ /**
+ * Retrieve the current type of request.
+ *
+ * @return the associated <code>HttpVerb</code>.
+ */
+ public HttpVerb getVerb() {
+ return this.verb;
+ }
+
+ /**
+ * Indicates whether 3xx Redirection codes should be followed.
+ *
+ * @return <code>true</code> if redirects should be followed;
+ * <code>false</code> otherwise.
+ * @see #followRedirects
+ */
+ public boolean isFollowingRedirects() {
+ return this.followRedirects;
+ }
+
+ /**
+ * Retrieve the maximum number of redirects this <code>Request</code> will
+ * follow.
+ *
+ * @return an <code>int</code> representing the number of redirects.
+ */
+ public int getMaxRedirects() {
+ return this.maxRedirects;
+ }
+
+ /**
+ * Retrieve the body associated with this <code>Request</code>.
+ *
+ * @return the associated body; may be <code>null</code>.
+ */
+ public byte[] getBody() {
+ return this.body;
+ }
+
+ /**
+ * Set the body applicable to this <code>Request</code>.
+ *
+ * @param body
+ * the body to apply.
+ */
+ public void setBody(final byte[] body) {
+ this.body = body;
+ }
+
+ /**
+ * Set the body applicable to this <code>Request</code>.
+ *
+ * @param body
+ * the body to apply.
+ */
+ public void setBody(final String body) {
+ this.body = body.getBytes();
+ }
+
+ /**
+ * Retrieve the content type associated with this <code>Request</code>.
+ *
+ * @return the associated content type.
+ */
+ public String getContentType() {
+ return contentType;
+ }
+
+ /**
+ * Set the content type associated with this <code>Request</code>.
+ *
+ * @param contentType
+ * the content type to apply.
+ */
+ public void setContentType(String contentType) {
+ this.contentType = contentType;
+ }
}
Modified: incubator/deft/sandbox/src/test/java/org/deftserver/io/AsynchronousSocketTest.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/test/java/org/deftserver/io/AsynchronousSocketTest.java?rev=1151259&r1=1151258&r2=1151259&view=diff
==============================================================================
--- incubator/deft/sandbox/src/test/java/org/deftserver/io/AsynchronousSocketTest.java (original)
+++ incubator/deft/sandbox/src/test/java/org/deftserver/io/AsynchronousSocketTest.java Tue Jul 26 21:45:16 2011
@@ -126,16 +126,16 @@ public class AsynchronousSocketTest {
private void onConnect() {
latch.countDown();
AsyncCallback wcb = new AsyncCallback() { @Override public void onCallback() { onWriteComplete(); }};
- socket.write("roger|\r\n", wcb);
+ socket.write("roger|\r\n".getBytes(), wcb);
}
private void onWriteComplete() {
latch.countDown();
- AsyncResult<String> rcb = new AsyncResult<String>() {
+ AsyncResult<byte[]> rcb = new AsyncResult<byte[]>() {
@Override public void onFailure(Throwable caught) { assertTrue(false); }
- @Override public void onSuccess(String result) { onReadComplete(result); }
+ @Override public void onSuccess(byte[] result) { onReadComplete(new String(result)); }
};
- socket.readUntil("|", rcb);
+ socket.readUntil("|".getBytes(), rcb);
}
private void onReadComplete(String result) {
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=1151259&r1=1151258&r2=1151259&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 Tue Jul 26 21:45:16 2011
@@ -224,9 +224,9 @@ public class DeftSystemTest {
@Override
@Asynchronous
public void get(HttpRequest request, final org.deftserver.web.http.HttpResponse response) {
- client.get("deft", new AsyncResult<String>() {
+ client.get("deft", new AsyncResult<byte[]>() {
@Override public void onFailure(Throwable caught) { /* ignore */}
- @Override public void onSuccess(String result) { response.write(result).finish(); }
+ @Override public void onSuccess(byte[] result) { response.write(new String(result)).finish(); }
});
}
@@ -1109,7 +1109,7 @@ public class DeftSystemTest {
final AsyncCallback runByIOLoop = new AsyncCallback() {
public void onCallback() {
- client.fetch(unresolvableAddress, new AsyncResult<org.deftserver.web.http.client.Response>() {
+ client.get(unresolvableAddress, new AsyncResult<org.deftserver.web.http.client.Response>() {
public void onSuccess(org.deftserver.web.http.client.Response result) { client.close(); }
@@ -1134,7 +1134,7 @@ public class DeftSystemTest {
final AsyncCallback runByIOLoop = new AsyncCallback() {
public void onCallback() {
- client.fetch(unconnectableAddress, new AsyncResult<org.deftserver.web.http.client.Response>() {
+ client.get(unconnectableAddress, new AsyncResult<org.deftserver.web.http.client.Response>() {
public void onSuccess(org.deftserver.web.http.client.Response result) { client.close(); }
@@ -1170,7 +1170,7 @@ public class DeftSystemTest {
public void onFailure(Throwable ignore) { }
};
// make sure that the http.fetch(..) is invoked from the ioloop thread
- IOLoop.INSTANCE.addCallback(new AsyncCallback() { public void onCallback() { http.fetch(url, cb); }});
+ IOLoop.INSTANCE.addCallback(new AsyncCallback() { public void onCallback() { http.get(url, cb); }});
latch.await(15, TimeUnit.SECONDS);
assertEquals(0, latch.getCount());
assertEquals("hello test", result[0]);
@@ -1191,7 +1191,7 @@ public class DeftSystemTest {
public void onFailure(Throwable e) { if (e instanceof ConnectException) latch.countDown(); }
};
// make sure that the http.fetch(..) is invoked from the ioloop thread
- IOLoop.INSTANCE.addCallback(new AsyncCallback() { public void onCallback() { http.fetch(url, cb); }});
+ IOLoop.INSTANCE.addCallback(new AsyncCallback() { public void onCallback() { http.get(url, cb); }});
latch.await(5, TimeUnit.SECONDS);
assertEquals(0, latch.getCount());
}
@@ -1215,7 +1215,7 @@ public class DeftSystemTest {
};
// make sure that the http.fetch(..) is invoked from the ioloop thread
- IOLoop.INSTANCE.addCallback(new AsyncCallback() { public void onCallback() { http.fetch(url, cb); }});
+ IOLoop.INSTANCE.addCallback(new AsyncCallback() { public void onCallback() { http.get(url, cb); }});
latch.await(5, TimeUnit.SECONDS);
assertEquals(0, latch.getCount());
}
@@ -1240,7 +1240,7 @@ public class DeftSystemTest {
};
// make sure that the http.fetch(..) is invoked from the ioloop thread
- IOLoop.INSTANCE.addCallback(new AsyncCallback() { public void onCallback() { http.fetch(url, cb); }});
+ IOLoop.INSTANCE.addCallback(new AsyncCallback() { public void onCallback() { http.get(url, cb); }});
latch.await(5, TimeUnit.SECONDS);
assertEquals(0, latch.getCount());
}
Added: incubator/deft/sandbox/src/test/java/org/deftserver/web/HttpVerbTest.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/test/java/org/deftserver/web/HttpVerbTest.java?rev=1151259&view=auto
==============================================================================
--- incubator/deft/sandbox/src/test/java/org/deftserver/web/HttpVerbTest.java (added)
+++ incubator/deft/sandbox/src/test/java/org/deftserver/web/HttpVerbTest.java Tue Jul 26 21:45:16 2011
@@ -0,0 +1,36 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+/**
+ * Test cases for {@link HttpVerb}.
+ */
+public class HttpVerbTest {
+
+ @Test
+ public void testHttpVerb() {
+
+ assertEquals(8, HttpVerb.values().length);
+ }
+}
Added: incubator/deft/sandbox/src/test/java/org/deftserver/web/http/ContentTypeTest.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/test/java/org/deftserver/web/http/ContentTypeTest.java?rev=1151259&view=auto
==============================================================================
--- incubator/deft/sandbox/src/test/java/org/deftserver/web/http/ContentTypeTest.java (added)
+++ incubator/deft/sandbox/src/test/java/org/deftserver/web/http/ContentTypeTest.java Tue Jul 26 21:45:16 2011
@@ -0,0 +1,35 @@
+/*
+ * 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 static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+/**
+ * Test cases for {@link ContentType}.
+ */
+public class ContentTypeTest {
+
+ @Test
+ public void testname() throws Exception {
+ assertTrue("No functionality in class to test.", true);
+ }
+}
Added: incubator/deft/sandbox/src/test/java/org/deftserver/web/http/client/AsynchronousHttpClientTest.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/test/java/org/deftserver/web/http/client/AsynchronousHttpClientTest.java?rev=1151259&view=auto
==============================================================================
--- incubator/deft/sandbox/src/test/java/org/deftserver/web/http/client/AsynchronousHttpClientTest.java (added)
+++ incubator/deft/sandbox/src/test/java/org/deftserver/web/http/client/AsynchronousHttpClientTest.java Tue Jul 26 21:45:16 2011
@@ -0,0 +1,105 @@
+/*
+ * 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.client;
+
+import static org.junit.Assert.*;
+import static org.deftserver.web.http.client.AsynchronousHttpClient.HTTP_VERSION;
+import static org.deftserver.web.http.client.AsynchronousHttpClient.USER_AGENT_HEADER;
+import static org.deftserver.web.http.client.AsynchronousHttpClient.NEWLINE;
+
+import org.deftserver.web.AsyncResult;
+import org.deftserver.web.HttpVerb;
+import org.junit.Test;
+
+/**
+ * Test cases for {@link AsynchronousHttpClient}.
+ */
+public class AsynchronousHttpClientTest {
+
+ @Test
+ public void testMakeRequestLineAndHeaders() {
+
+ AsynchronousHttpClient client = new AsynchronousHttpClient() {
+ @Override
+ protected void doFetch(AsyncResult<Response> callback, long requestStarted) {
+ // Do nothing.
+ }
+ };
+
+ client.get("http://testurl.com/path/", null);
+
+ String expected = HttpVerb.GET + " /path/ " + HTTP_VERSION;
+ expected += "Host: testurl.com" + NEWLINE + USER_AGENT_HEADER;
+ expected += NEWLINE;
+
+ String actual = client.makeRequestLineAndHeaders();
+
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testMakeRequestLineAndHeadersWithBody() {
+
+ AsynchronousHttpClient client = new AsynchronousHttpClient() {
+ @Override
+ protected void doFetch(AsyncResult<Response> callback, long requestStarted) {
+ // Do nothing.
+ }
+ };
+
+ client.post("http://testurl.com/path/", "name=value", null);
+
+ String expected = HttpVerb.POST + " /path/ " + HTTP_VERSION;
+ expected += "Host: testurl.com" + NEWLINE + USER_AGENT_HEADER;
+ expected += "Content-Type: application/x-www-form-urlencoded" + NEWLINE;
+ expected += "Content-Length: 10";
+ expected += NEWLINE + NEWLINE;
+ expected += "name=value";
+ expected += NEWLINE;
+
+ String actual = client.makeRequestLineAndHeaders();
+
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testMakeRequestLineAndHeadersWithZeroLengthBody() {
+
+ AsynchronousHttpClient client = new AsynchronousHttpClient() {
+ @Override
+ protected void doFetch(AsyncResult<Response> callback, long requestStarted) {
+ // Do nothing.
+ }
+ };
+
+ client.post("http://testurl.com/path/", "", null);
+
+ String expected = HttpVerb.POST + " /path/ " + HTTP_VERSION;
+ expected += "Host: testurl.com" + NEWLINE + USER_AGENT_HEADER;
+ expected += "Content-Type: application/x-www-form-urlencoded" + NEWLINE;
+ expected += "Content-Length: 0";
+ expected += NEWLINE + NEWLINE;
+ expected += NEWLINE;
+
+ String actual = client.makeRequestLineAndHeaders();
+
+ assertEquals(expected, actual);
+ }
+}
Added: incubator/deft/sandbox/src/test/java/org/deftserver/web/http/client/RequestTest.java
URL: http://svn.apache.org/viewvc/incubator/deft/sandbox/src/test/java/org/deftserver/web/http/client/RequestTest.java?rev=1151259&view=auto
==============================================================================
--- incubator/deft/sandbox/src/test/java/org/deftserver/web/http/client/RequestTest.java (added)
+++ incubator/deft/sandbox/src/test/java/org/deftserver/web/http/client/RequestTest.java Tue Jul 26 21:45:16 2011
@@ -0,0 +1,80 @@
+/*
+ * 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.client;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+
+import org.deftserver.web.HttpVerb;
+import org.deftserver.web.http.ContentType;
+import org.junit.Test;
+
+import com.google.common.base.Charsets;
+
+/**
+ * Test cases for {@link Request}.
+ */
+public class RequestTest {
+
+ @Test
+ public void testRequest() {
+
+ final Request request = new Request("http://testurl.com:8080", HttpVerb.POST, false, 99);
+
+ assertEquals(request.getVerb(), HttpVerb.POST);
+
+ assertEquals("http", request.getURL().getProtocol());
+ assertEquals("testurl.com", request.getURL().getHost());
+ assertEquals(8080, request.getURL().getPort());
+
+ assertEquals(false, request.isFollowingRedirects());
+ assertEquals(99, request.getMaxRedirects());
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testRequestForMalformedUrl() {
+
+ new Request("malformed", HttpVerb.POST);
+ }
+
+ @Test
+ public void testSetGetBody() {
+
+ final Request request = new Request("http://unimportant-value.com", HttpVerb.POST);
+
+ request.setBody("testContent1");
+ assertTrue(Arrays.equals("testContent1".getBytes(), request.getBody()));
+
+ request.setBody("testContent2".getBytes());
+ assertEquals("testContent2", new String(request.getBody(), Charsets.ISO_8859_1));
+ }
+
+ @Test
+ public void testSetGetContentType() {
+
+ final Request request = new Request("http://unimportant-value.com", HttpVerb.POST);
+ assertEquals(ContentType.APPLICATION_FORM_URLENCODED, request.getContentType());
+
+ request.setContentType(ContentType.MULTIPART_FORM_DATA);
+ assertEquals(ContentType.MULTIPART_FORM_DATA, request.getContentType());
+ }
+}