diff options
14 files changed, 204 insertions, 124 deletions
diff --git a/AndroidAsync/src/com/koushikdutta/async/AsyncSocketImpl.java b/AndroidAsync/src/com/koushikdutta/async/AsyncSocketImpl.java index ee166dd..8dbc1a2 100644 --- a/AndroidAsync/src/com/koushikdutta/async/AsyncSocketImpl.java +++ b/AndroidAsync/src/com/koushikdutta/async/AsyncSocketImpl.java @@ -7,7 +7,6 @@ import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import junit.framework.Assert; -import android.util.Log; import com.koushikdutta.async.callback.ClosedCallback; import com.koushikdutta.async.callback.DataCallback; @@ -53,7 +52,9 @@ class AsyncSocketImpl implements AsyncSocket { handleRemaining(list.remaining()); } catch (IOException e) { - e.printStackTrace(); + close(); + report(e); + reportClose(); } } @@ -83,7 +84,9 @@ class AsyncSocketImpl implements AsyncSocket { handleRemaining(b.remaining()); } catch (IOException ex) { - ex.printStackTrace(); + close(); + report(ex); + reportClose(); } } @@ -217,6 +220,11 @@ class AsyncSocketImpl implements AsyncSocket { } @Override + public boolean isOpen() { + return mChannel.isConnected(); + } + + @Override public void pause() { mKey.interestOps(~SelectionKey.OP_READ & mKey.interestOps()); } diff --git a/AndroidAsync/src/com/koushikdutta/async/BufferedDataSink.java b/AndroidAsync/src/com/koushikdutta/async/BufferedDataSink.java index 5e1cf0c..8e7a2e1 100644 --- a/AndroidAsync/src/com/koushikdutta/async/BufferedDataSink.java +++ b/AndroidAsync/src/com/koushikdutta/async/BufferedDataSink.java @@ -25,60 +25,48 @@ public class BufferedDataSink implements DataSink { private void writePending() { // Log.i("NIO", "Writing to buffer..."); - mDataSink.write(mPendingWrites); - if (mPendingWrites.remaining() == 0) { - mPendingWrites = null; - onFlushed(); + if (mPendingWrites != null) { + mDataSink.write(mPendingWrites); + if (mPendingWrites.remaining() == 0) + mPendingWrites = null; } + if (mPendingWrites == null && mWritable != null) + mWritable.onWriteable(); } ByteBufferList mPendingWrites; @Override public void write(ByteBuffer bb) { - if (mPendingWrites == null) { - mDataSink.write(bb); - if (bb.remaining() > 0) { - mPendingWrites = new ByteBufferList(); - mPendingWrites.add(ByteBuffer.wrap(bb.array(), bb.arrayOffset() + bb.position(), bb.remaining())); - bb.position(0); - bb.limit(0); - } - } - else { - mPendingWrites.add(ByteBuffer.wrap(bb.array(), bb.arrayOffset() + bb.position(), bb.remaining())); - bb.position(0); - bb.limit(0); - writePending(); - } + ByteBufferList bbl = new ByteBufferList(); + bbl.add(bb); + write(bbl); } @Override public void write(ByteBufferList bb) { - if (mPendingWrites == null) { + if (mPendingWrites == null) mDataSink.write(bb); - if (bb.remaining() > 0) { - mPendingWrites = new ByteBufferList(); - mPendingWrites.add(bb); + + if (bb.remaining() > 0) { + int toRead = Math.min(bb.remaining(), mMaxBuffer); + if (toRead > 0) { + if (mPendingWrites == null) + mPendingWrites = new ByteBufferList(); + mPendingWrites.add(bb.get(toRead)); } - bb.clear(); - } - else { - mPendingWrites.add(bb); - bb.clear(); - writePending(); } } + WritableCallback mWritable; @Override public void setWriteableCallback(WritableCallback handler) { - Assert.fail("BufferingDataSink is always writeable."); + mWritable = handler; } @Override public WritableCallback getWriteableCallback() { - Assert.fail("BufferingDataSink is always writeable."); - return null; + return mWritable; } public int remaining() { @@ -86,7 +74,14 @@ public class BufferedDataSink implements DataSink { return 0; return mPendingWrites.remaining(); } - - public void onFlushed() { + + int mMaxBuffer = Integer.MAX_VALUE; + public int getMaxBuffer() { + return mMaxBuffer; + } + + public void setMaxBuffer(int maxBuffer) { + Assert.assertTrue(maxBuffer >= 0); + mMaxBuffer = maxBuffer; } } diff --git a/AndroidAsync/src/com/koushikdutta/async/CloseableData.java b/AndroidAsync/src/com/koushikdutta/async/CloseableData.java index 56563c5..f00bdf2 100644 --- a/AndroidAsync/src/com/koushikdutta/async/CloseableData.java +++ b/AndroidAsync/src/com/koushikdutta/async/CloseableData.java @@ -3,6 +3,7 @@ package com.koushikdutta.async; import com.koushikdutta.async.callback.ClosedCallback; public interface CloseableData { + public boolean isOpen(); public void close(); public void setClosedCallback(ClosedCallback handler); public ClosedCallback getCloseHandler(); diff --git a/AndroidAsync/src/com/koushikdutta/async/Util.java b/AndroidAsync/src/com/koushikdutta/async/Util.java index e4757d0..f463290 100644 --- a/AndroidAsync/src/com/koushikdutta/async/Util.java +++ b/AndroidAsync/src/com/koushikdutta/async/Util.java @@ -82,4 +82,22 @@ public class Util { cb.onWriteable(); } + + public static void writeAll(final DataSink sink, final ByteBufferList bb) { + sink.setWriteableCallback(new WritableCallback() { + @Override + public void onWriteable() { + if (bb.remaining() == 0) + return; + sink.write(bb); + } + }); + sink.write(bb); + } + public static void writeAll(DataSink sink, byte[] bytes) { + ByteBuffer bb = ByteBuffer.wrap(bytes); + ByteBufferList bbl = new ByteBufferList(); + bbl.add(bb); + writeAll(sink, bbl); + } } diff --git a/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpClient.java b/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpClient.java index 223299c..febfc5a 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpClient.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpClient.java @@ -101,6 +101,7 @@ public class AsyncHttpClient { String kas = headers.get("Connection"); if (kas != null && "keep-alive".toLowerCase().equals(kas.toLowerCase())) keepalive = true; + request.onConnect(this); callback.onConnectCompleted(null, this); } catch (Exception ex) { @@ -207,8 +208,8 @@ public class AsyncHttpClient { public Object convert(ByteBufferList bb) throws Exception; } - public static void download(String uri, final DownloadCallback callback) { - download(uri, callback, new ResultConvert() { + public static void get(String uri, final DownloadCallback callback) { + get(uri, callback, new ResultConvert() { @Override public Object convert(ByteBufferList b) { return b; @@ -216,8 +217,17 @@ public class AsyncHttpClient { }); } - public static void download(String uri, final StringCallback callback) { - download(uri, callback, new ResultConvert() { + public static void get(String uri, final StringCallback callback) { + try { + execute(new AsyncHttpGet(uri), callback); + } + catch (URISyntaxException e) { + callback.onCompleted(e, null, null); + } + } + + public static void execute(AsyncHttpRequest req, final StringCallback callback) { + execute(req, callback, new ResultConvert() { @Override public Object convert(ByteBufferList bb) { StringBuilder builder = new StringBuilder(); @@ -229,21 +239,17 @@ public class AsyncHttpClient { }); } - public static void download(String uri, final JSONObjectCallback callback) { - download(uri, callback, new ResultConvert() { - @Override - public Object convert(ByteBufferList bb) throws JSONException { - StringBuilder builder = new StringBuilder(); - for (ByteBuffer b: bb) { - builder.append(new String(b.array(), b.arrayOffset() + b.position(), b.remaining())); - } - return new JSONObject(builder.toString()); - } - }); + public static void get(String uri, final JSONObjectCallback callback) { + try { + execute(new AsyncHttpGet(uri), callback); + } + catch (URISyntaxException e) { + callback.onCompleted(e, null, null); + } } - public static void download(AsyncHttpRequest req, final JSONObjectCallback callback) { - download(req, callback, new ResultConvert() { + public static void execute(AsyncHttpRequest req, final JSONObjectCallback callback) { + execute(req, callback, new ResultConvert() { @Override public Object convert(ByteBufferList bb) throws JSONException { StringBuilder builder = new StringBuilder(); @@ -268,7 +274,16 @@ public class AsyncHttpClient { }); } - public static void download(String uri, final String filename, final FileCallback callback) { + public static void get(String uri, final String filename, final FileCallback callback) { + try { + execute(new AsyncHttpGet(uri), filename, callback); + } + catch (URISyntaxException e) { + callback.onCompleted(e, null, null); + } + } + + public static void execute(AsyncHttpRequest req, final String filename, final FileCallback callback) { final Handler handler = Looper.myLooper() == null ? null : new Handler(); final File file = new File(filename); final FileOutputStream fout; @@ -279,7 +294,7 @@ public class AsyncHttpClient { invoke(handler, callback, null, e, null); return; } - connect(uri, new HttpConnectCallback() { + connect(req, new HttpConnectCallback() { @Override public void onConnectCompleted(Exception ex, final AsyncHttpResponse response) { if (ex != null) { @@ -314,7 +329,7 @@ public class AsyncHttpClient { }); } - private static void download(AsyncHttpRequest req, final ResultPairCallback callback, final ResultConvert convert) { + private static void execute(AsyncHttpRequest req, final ResultPairCallback callback, final ResultConvert convert) { final Handler handler = Looper.myLooper() == null ? null : new Handler(); connect(req, new HttpConnectCallback() { ByteBufferList buffer = new ByteBufferList(); @@ -348,9 +363,9 @@ public class AsyncHttpClient { }); } - private static void download(String uri, final ResultPairCallback callback, final ResultConvert convert) { + private static void get(String uri, final ResultPairCallback callback, final ResultConvert convert) { try { - download(new AsyncHttpGet(new URI(uri)), callback, convert); + execute(new AsyncHttpGet(new URI(uri)), callback, convert); } catch (URISyntaxException e) { callback.onCompleted(e, null, null); diff --git a/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpPost.java b/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpPost.java new file mode 100644 index 0000000..21d743d --- /dev/null +++ b/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpPost.java @@ -0,0 +1,20 @@ +package com.koushikdutta.async.http; + +import java.net.URI; +import java.net.URISyntaxException; + +public class AsyncHttpPost extends AsyncHttpRequest { + public static final String METHOD = "POST"; + + public AsyncHttpPost(String uri) throws URISyntaxException { + super(new URI(uri), METHOD); + } + + public AsyncHttpPost(URI uri) { + super(uri, METHOD); + } + + @Override + protected void onConnect(AsyncHttpResponse response) { + } +} diff --git a/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpRequest.java b/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpRequest.java index 22a65a1..57d98fd 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpRequest.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpRequest.java @@ -61,4 +61,11 @@ public class AsyncHttpRequest { public void setFollowRedirect(boolean follow) { mFollowRedirect = follow; } + + void onConnectInternal(AsyncHttpResponse response) { + onConnect(response); + } + + protected void onConnect(AsyncHttpResponse response) { + } } diff --git a/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpRequestContentWriter.java b/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpRequestContentWriter.java new file mode 100644 index 0000000..1eb0f44 --- /dev/null +++ b/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpRequestContentWriter.java @@ -0,0 +1,7 @@ +package com.koushikdutta.async.http; + +import com.koushikdutta.async.DataSink; + +public abstract class AsyncHttpRequestContentWriter { + public abstract void write(DataSink sink); +} diff --git a/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpResponseImpl.java b/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpResponseImpl.java index 93e46ad..e6bc984 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpResponseImpl.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpResponseImpl.java @@ -11,13 +11,16 @@ import com.koushikdutta.async.DataEmitter; import com.koushikdutta.async.DataExchange; import com.koushikdutta.async.FilteredDataCallback; import com.koushikdutta.async.ExceptionCallback; +import com.koushikdutta.async.FilteredDataSink; import com.koushikdutta.async.LineEmitter; +import com.koushikdutta.async.Util; import com.koushikdutta.async.LineEmitter.StringCallback; import com.koushikdutta.async.callback.ClosedCallback; import com.koushikdutta.async.callback.CompletedCallback; import com.koushikdutta.async.callback.DataCallback; import com.koushikdutta.async.callback.WritableCallback; import com.koushikdutta.async.http.filter.ChunkedInputFilter; +import com.koushikdutta.async.http.filter.ChunkedOutputFilter; import com.koushikdutta.async.http.filter.GZIPInputFilter; import com.koushikdutta.async.http.filter.InflaterInputFilter; import com.koushikdutta.async.http.libcore.RawHeaders; @@ -33,9 +36,8 @@ public class AsyncHttpResponseImpl extends FilteredDataCallback implements Async mSocket = socket; mExchange = exchange; - mWriter = new BufferedDataSink(exchange); String rs = mRequest.getRequestString(); - mWriter.write(ByteBuffer.wrap(rs.getBytes())); + Util.writeAll(exchange, rs.getBytes()); LineEmitter liner = new LineEmitter(exchange); liner.setLineCallback(mHeaderCallback); @@ -146,7 +148,6 @@ public class AsyncHttpResponseImpl extends FilteredDataCallback implements Async onCompleted(e); } - private BufferedDataSink mWriter; private AsyncSocket mSocket; private AsyncHttpRequest mRequest; private DataExchange mExchange; @@ -201,27 +202,39 @@ public class AsyncHttpResponseImpl extends FilteredDataCallback implements Async return; mFirstWrite = false; Assert.assertNotNull(mRequest.getHeaders().getHeaders().get("Content-Type")); + Assert.assertTrue(mRequest.getHeaders().getHeaders().get("Transfer-Encoding") != null || mRequest.getHeaders().getContentLength() != -1); + } + + FilteredDataSink mChunker; + void initChunker() { + if (mChunker != null) + return; + mChunker = new ChunkedOutputFilter(mSocket); } @Override public void write(ByteBuffer bb) { assertContent(); + initChunker(); + mChunker.write(bb); } @Override public void write(ByteBufferList bb) { assertContent(); + initChunker(); + mChunker.write(bb); } @Override public void setWriteableCallback(WritableCallback handler) { - // TODO Auto-generated method stub - + initChunker(); + mChunker.setWriteableCallback(handler); } @Override public WritableCallback getWriteableCallback() { - // TODO Auto-generated method stub - return null; + initChunker(); + return mChunker.getWriteableCallback(); } } diff --git a/AndroidAsync/src/com/koushikdutta/async/http/UrlEncodedFormWriter.java b/AndroidAsync/src/com/koushikdutta/async/http/UrlEncodedFormWriter.java new file mode 100644 index 0000000..dc9fd2d --- /dev/null +++ b/AndroidAsync/src/com/koushikdutta/async/http/UrlEncodedFormWriter.java @@ -0,0 +1,29 @@ +package com.koushikdutta.async.http; + +import java.net.URLEncoder; +import java.util.List; + +import org.apache.http.NameValuePair; + +import com.koushikdutta.async.DataSink; + +public class UrlEncodedFormWriter extends AsyncHttpRequestContentWriter { + List<NameValuePair> mParameters; + public UrlEncodedFormWriter(List<NameValuePair> parameters) { + mParameters = parameters; + } + @Override + public void write(DataSink sink) { + boolean first = true; + StringBuilder b = new StringBuilder(); + for (NameValuePair pair: mParameters) { + if (!first) + b.append('&'); + first = false; + b.append(URLEncoder.encode(pair.getName())); + b.append('='); + b.append(URLEncoder.encode(pair.getValue())); + } + byte[] bytes = b.toString().getBytes(); + } +} diff --git a/AndroidAsync/src/com/koushikdutta/async/http/server/AsyncHttpServerResponseImpl.java b/AndroidAsync/src/com/koushikdutta/async/http/server/AsyncHttpServerResponseImpl.java index 5f5246f..f5b9394 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/server/AsyncHttpServerResponseImpl.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/server/AsyncHttpServerResponseImpl.java @@ -35,78 +35,40 @@ public class AsyncHttpServerResponseImpl implements AsyncHttpServerResponse { @Override public void write(ByteBuffer bb) { - if (!mHasWritten) { - Assert.assertTrue(mContentLength < 0); - Assert.assertNotNull(mRawHeaders.getStatusLine()); - mRawHeaders.set("Transfer-Encoding", "Chunked"); - writeHead(); - mHasWritten = true; - initChunker(); - } + if (!mHasWritten) + initFirstWrite(); mChunker.write(bb); } - /* - ByteBufferList mLastChunk = null; - @Override - public void write(ByteBufferList bb) { - if (mLastChunk != null) { - mSocket.write(mLastChunk); - if (mLastChunk.remaining() == 0) - mLastChunk = null; - else - return; - } - Assert.assertTrue(mContentLength < 0); - if (null == mRawHeaders.get("Transfer-Encoding")) { - Assert.assertNotNull(mRawHeaders.getStatusLine()); - mRawHeaders.set("Transfer-Encoding", "Chunked"); - writeHead(); - } - String chunkLen = Integer.toString(bb.remaining(), 16) + "\r\n"; - bb.add(0, ByteBuffer.wrap(chunkLen.getBytes())); - bb.add(ByteBuffer.wrap("\r\n".getBytes())); - mSocket.write(bb); - // this will only buffer entire chunks. - if (bb.remaining() > 0) { - mLastChunk = new ByteBufferList(); - ByteBuffer data = bb.read(bb.remaining()); - mLastChunk.add(data); - bb.clear(); - } - } - */ - boolean mHasWritten = false; FilteredDataSink mChunker; - void initChunker() { + void initFirstWrite() { + Assert.assertTrue(mContentLength < 0); + Assert.assertNotNull(mRawHeaders.getStatusLine()); + mRawHeaders.set("Transfer-Encoding", "Chunked"); + writeHead(); + mSink.setMaxBuffer(0); + mHasWritten = true; if (mChunker != null) return; - mChunker = new ChunkedOutputFilter(mSocket); + mChunker = new ChunkedOutputFilter(mSink); } @Override public void write(ByteBufferList bb) { - if (!mHasWritten) { - Assert.assertTrue(mContentLength < 0); - Assert.assertNotNull(mRawHeaders.getStatusLine()); - mRawHeaders.set("Transfer-Encoding", "Chunked"); - writeHead(); - mHasWritten = true; - initChunker(); - } + if (!mHasWritten) + initFirstWrite(); mChunker.write(bb); } @Override public void setWriteableCallback(WritableCallback handler) { -// mSocket.setWriteableCallback(handler); - initChunker(); + initFirstWrite(); mChunker.setWriteableCallback(handler); } @Override public WritableCallback getWriteableCallback() { -// return mSocket.getWriteableCallback(); + initFirstWrite(); return mChunker.getWriteableCallback(); } diff --git a/AndroidAsync/src/com/koushikdutta/async/http/server/WebSocketImpl.java b/AndroidAsync/src/com/koushikdutta/async/http/server/WebSocketImpl.java index aef1f5a..3c504f3 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/server/WebSocketImpl.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/server/WebSocketImpl.java @@ -137,4 +137,9 @@ public class WebSocketImpl implements WebSocket { public DataCallback getDataCallback() { return mDataCallback; } + + @Override + public boolean isOpen() { + return mSocket.isOpen(); + } } diff --git a/AndroidAsync/src/com/koushikdutta/test/TestActivity.java b/AndroidAsync/src/com/koushikdutta/test/TestActivity.java index 51e8c34..8bda571 100644 --- a/AndroidAsync/src/com/koushikdutta/test/TestActivity.java +++ b/AndroidAsync/src/com/koushikdutta/test/TestActivity.java @@ -277,7 +277,7 @@ public class TestActivity extends Activity { public void run() { try { for (int i = 0; i < 5; i++) { - AsyncHttpClient.download("http://builder.clockworkmod.com", new StringCallback() { + AsyncHttpClient.get("http://builder.clockworkmod.com", new StringCallback() { @Override public void onCompleted(Exception e, AsyncHttpResponse response, String result) { if (e != null) { diff --git a/AndroidAsyncSample/src/com/koushikdutta/async/sample/MainActivity.java b/AndroidAsyncSample/src/com/koushikdutta/async/sample/MainActivity.java index e102ddd..100fc6e 100644 --- a/AndroidAsyncSample/src/com/koushikdutta/async/sample/MainActivity.java +++ b/AndroidAsyncSample/src/com/koushikdutta/async/sample/MainActivity.java @@ -45,8 +45,8 @@ public class MainActivity extends Activity { return true; } - private void downloadFile(final ImageView iv, String url, final String filename) { - AsyncHttpClient.download(url, filename, new AsyncHttpClient.FileCallback() { + private void getFile(final ImageView iv, String url, final String filename) { + AsyncHttpClient.get(url, filename, new AsyncHttpClient.FileCallback() { @Override public void onCompleted(Exception e, AsyncHttpResponse response, File result) { if (e != null) { @@ -73,8 +73,8 @@ public class MainActivity extends Activity { tether.setImageBitmap(null); desksms.setImageBitmap(null); - downloadFile(rommanager, "https://raw.github.com/koush/AndroidAsync/master/rommanager.png", getFileStreamPath(randomFile()).getAbsolutePath()); - downloadFile(tether, "https://raw.github.com/koush/AndroidAsync/master/tether.png", getFileStreamPath(randomFile()).getAbsolutePath()); - downloadFile(desksms, "https://raw.github.com/koush/AndroidAsync/master/desksms.png", getFileStreamPath(randomFile()).getAbsolutePath()); + getFile(rommanager, "https://raw.github.com/koush/AndroidAsync/master/rommanager.png", getFileStreamPath(randomFile()).getAbsolutePath()); + getFile(tether, "https://raw.github.com/koush/AndroidAsync/master/tether.png", getFileStreamPath(randomFile()).getAbsolutePath()); + getFile(desksms, "https://raw.github.com/koush/AndroidAsync/master/desksms.png", getFileStreamPath(randomFile()).getAbsolutePath()); } } |