diff options
author | Koushik Dutta <koushd@gmail.com> | 2014-07-24 22:52:56 -0700 |
---|---|---|
committer | Koushik Dutta <koushd@gmail.com> | 2014-07-24 22:52:56 -0700 |
commit | 893442cba5cc5c20ea0be7953860513ab7e4e325 (patch) | |
tree | 6e67a98a1fd65f4bb5d2225b9e64d54e4a8e6272 /AndroidAsync | |
parent | 7ce289952764e8a8a7a85c4f43658e06b369ac98 (diff) | |
download | AndroidAsync-893442cba5cc5c20ea0be7953860513ab7e4e325.tar.gz AndroidAsync-893442cba5cc5c20ea0be7953860513ab7e4e325.tar.bz2 AndroidAsync-893442cba5cc5c20ea0be7953860513ab7e4e325.zip |
finished up transport refactor... time for some spdy.
Diffstat (limited to 'AndroidAsync')
6 files changed, 237 insertions, 257 deletions
diff --git a/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpClient.java b/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpClient.java index fb13811..ee4f805 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpClient.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpClient.java @@ -160,7 +160,7 @@ public class AsyncHttpClient { } if (complete) { callback.onConnectCompleted(ex, response); - assert ex != null || response.getSocket() == null || response.getDataCallback() != null || response.isPaused(); + assert ex != null || response.socket() == null || response.getDataCallback() != null || response.isPaused(); return; } @@ -266,174 +266,189 @@ public class AsyncHttpClient { return; } - // 4) wait for request to be sent fully - // and - // 6) wait for headers - final AsyncHttpResponseImpl ret = new AsyncHttpResponseImpl(request) { - @Override - protected void onRequestCompleted(Exception ex) { - request.logv("request completed"); - if (cancel.isCancelled()) - return; - // 5) after request is sent, set a header timeout - if (cancel.timeoutRunnable != null && mHeaders == null) { - mServer.removeAllCallbacks(cancel.scheduled); - cancel.scheduled = mServer.postDelayed(cancel.timeoutRunnable, getTimeoutRemaining(request)); - } - } + executeSocket(request, redirectCount, cancel, callback, data); + } + }; - @Override - public void setDataEmitter(DataEmitter emitter) { - data.bodyEmitter = emitter; - synchronized (mMiddleware) { - for (AsyncHttpClientMiddleware middleware: mMiddleware) { - middleware.onBodyDecoder(data); - } - } + // set up the system default proxy and connect + setupAndroidProxy(request); - super.setDataEmitter(data.bodyEmitter); - - Headers headers = mHeaders; - int responseCode = code(); - if ((responseCode == HttpURLConnection.HTTP_MOVED_PERM || responseCode == HttpURLConnection.HTTP_MOVED_TEMP || responseCode == 307) && request.getFollowRedirect()) { - String location = headers.get("Location"); - Uri redirect; - try { - redirect = Uri.parse(location); - if (redirect.getScheme() == null) { - redirect = Uri.parse(new URL(new URL(uri.toString()), location).toString()); - } - } - catch (Exception e) { - reportConnectedCompleted(cancel, e, this, request, callback); - return; - } - final String method = request.getMethod().equals(AsyncHttpHead.METHOD) ? AsyncHttpHead.METHOD : AsyncHttpGet.METHOD; - AsyncHttpRequest newReq = new AsyncHttpRequest(redirect, method); - newReq.executionTime = request.executionTime; - newReq.logLevel = request.logLevel; - newReq.LOGTAG = request.LOGTAG; - newReq.proxyHost = request.proxyHost; - newReq.proxyPort = request.proxyPort; - setupAndroidProxy(newReq); - copyHeader(request, newReq, "User-Agent"); - copyHeader(request, newReq, "Range"); - request.logi("Redirecting"); - newReq.logi("Redirected"); - execute(newReq, redirectCount + 1, cancel, callback); - - setDataCallback(new NullDataCallback()); - return; - } + synchronized (mMiddleware) { + for (AsyncHttpClientMiddleware middleware: mMiddleware) { + Cancellable socketCancellable = middleware.getSocket(data); + if (socketCancellable != null) { + data.socketCancellable = socketCancellable; + cancel.setParent(socketCancellable); + return; + } + } + } + reportConnectedCompleted(cancel, new IllegalArgumentException("invalid uri"), null, request, callback); + } - request.logv("Final (post cache response) headers:\n" + toString()); + private void executeSocket(final AsyncHttpRequest request, final int redirectCount, + final FutureAsyncHttpResponse cancel, final HttpConnectCallback callback, + final OnRequestCompleteData data) { + // 4) wait for request to be sent fully + // and + // 6) wait for headers + final AsyncHttpResponseImpl ret = new AsyncHttpResponseImpl(request) { + @Override + protected void onRequestCompleted(Exception ex) { + request.logv("request completed"); + if (cancel.isCancelled()) + return; + // 5) after request is sent, set a header timeout + if (cancel.timeoutRunnable != null && mHeaders == null) { + mServer.removeAllCallbacks(cancel.scheduled); + cancel.scheduled = mServer.postDelayed(cancel.timeoutRunnable, getTimeoutRemaining(request)); + } + } - // at this point the headers are done being modified - reportConnectedCompleted(cancel, null, this, request, callback); + @Override + public void setDataEmitter(DataEmitter emitter) { + data.bodyEmitter = emitter; + synchronized (mMiddleware) { + for (AsyncHttpClientMiddleware middleware: mMiddleware) { + middleware.onBodyDecoder(data); } + } - protected void onHeadersReceived() { - super.onHeadersReceived(); - if (cancel.isCancelled()) - return; + super.setDataEmitter(data.bodyEmitter); - // 7) on headers, cancel timeout - if (cancel.timeoutRunnable != null) - mServer.removeAllCallbacks(cancel.scheduled); + Headers headers = mHeaders; + int responseCode = code(); + if ((responseCode == HttpURLConnection.HTTP_MOVED_PERM || responseCode == HttpURLConnection.HTTP_MOVED_TEMP || responseCode == 307) && request.getFollowRedirect()) { + String location = headers.get("Location"); + Uri redirect; + try { + redirect = Uri.parse(location); + if (redirect.getScheme() == null) { + redirect = Uri.parse(new URL(new URL(request.getUri().toString()), location).toString()); + } + } + catch (Exception e) { + reportConnectedCompleted(cancel, e, this, request, callback); + return; + } + final String method = request.getMethod().equals(AsyncHttpHead.METHOD) ? AsyncHttpHead.METHOD : AsyncHttpGet.METHOD; + AsyncHttpRequest newReq = new AsyncHttpRequest(redirect, method); + newReq.executionTime = request.executionTime; + newReq.logLevel = request.logLevel; + newReq.LOGTAG = request.LOGTAG; + newReq.proxyHost = request.proxyHost; + newReq.proxyPort = request.proxyPort; + setupAndroidProxy(newReq); + copyHeader(request, newReq, "User-Agent"); + copyHeader(request, newReq, "Range"); + request.logi("Redirecting"); + newReq.logi("Redirected"); + execute(newReq, redirectCount + 1, cancel, callback); + + setDataCallback(new NullDataCallback()); + return; + } - // allow the middleware to massage the headers before the body is decoded - request.logv("Received headers:\n" + toString()); + request.logv("Final (post cache response) headers:\n" + toString()); - synchronized (mMiddleware) { - for (AsyncHttpClientMiddleware middleware: mMiddleware) { - middleware.onHeadersReceived(data); - } - } + // at this point the headers are done being modified + reportConnectedCompleted(cancel, null, this, request, callback); + } - // drop through, and setDataEmitter will be called for the body decoder. - // headers will be further massaged in there. - } + protected void onHeadersReceived() { + super.onHeadersReceived(); + if (cancel.isCancelled()) + return; - @Override - protected void report(Exception ex) { - if (ex != null) - request.loge("exception during response", ex); - if (cancel.isCancelled()) - return; - if (ex instanceof AsyncSSLException) { - request.loge("SSL Exception", ex); - AsyncSSLException ase = (AsyncSSLException)ex; - request.onHandshakeException(ase); - if (ase.getIgnore()) - return; - } - final AsyncSocket socket = getSocket(); - if (socket == null) - return; - super.report(ex); - if (!socket.isOpen() || ex != null) { - if (headers() == null && ex != null) - reportConnectedCompleted(cancel, ex, null, request, callback); - } + // 7) on headers, cancel timeout + if (cancel.timeoutRunnable != null) + mServer.removeAllCallbacks(cancel.scheduled); - data.exception = ex; - synchronized (mMiddleware) { - for (AsyncHttpClientMiddleware middleware: mMiddleware) { - middleware.onRequestComplete(data); - } - } - } + // allow the middleware to massage the headers before the body is decoded + request.logv("Received headers:\n" + toString()); - @Override - public AsyncSocket detachSocket() { - request.logd("Detaching socket"); - AsyncSocket socket = getSocket(); - if (socket == null) - return null; - socket.setWriteableCallback(null); - socket.setClosedCallback(null); - socket.setEndCallback(null); - socket.setDataCallback(null); - setSocket(null); - return socket; + synchronized (mMiddleware) { + for (AsyncHttpClientMiddleware middleware: mMiddleware) { + middleware.onHeadersReceived(data); } - }; + } - data.sendHeadersCallback = new CompletedCallback() { - @Override - public void onCompleted(Exception ex) { - if (ex != null) - ret.report(ex); - else - ret.onHeadersSent(); - } - }; - data.response = ret; - ret.setSocket(socket); + // drop through, and setDataEmitter will be called for the body decoder. + // headers will be further massaged in there. + } + + @Override + protected void report(Exception ex) { + if (ex != null) + request.loge("exception during response", ex); + if (cancel.isCancelled()) + return; + if (ex instanceof AsyncSSLException) { + request.loge("SSL Exception", ex); + AsyncSSLException ase = (AsyncSSLException)ex; + request.onHandshakeException(ase); + if (ase.getIgnore()) + return; + } + final AsyncSocket socket = socket(); + if (socket == null) + return; + super.report(ex); + if (!socket.isOpen() || ex != null) { + if (headers() == null && ex != null) + reportConnectedCompleted(cancel, ex, null, request, callback); + } + data.exception = ex; synchronized (mMiddleware) { for (AsyncHttpClientMiddleware middleware: mMiddleware) { - if (middleware.sendHeaders(data)) - break; + middleware.onRequestComplete(data); } } } + + @Override + public AsyncSocket detachSocket() { + request.logd("Detaching socket"); + AsyncSocket socket = socket(); + if (socket == null) + return null; + socket.setWriteableCallback(null); + socket.setClosedCallback(null); + socket.setEndCallback(null); + socket.setDataCallback(null); + setSocket(null); + return socket; + } }; - // set up the system default proxy and connect - setupAndroidProxy(request); + data.sendHeadersCallback = new CompletedCallback() { + @Override + public void onCompleted(Exception ex) { + if (ex != null) + ret.report(ex); + else + ret.onHeadersSent(); + } + }; + data.receiveHeadersCallback = new CompletedCallback() { + @Override + public void onCompleted(Exception ex) { + if (ex != null) + ret.report(ex); + else + ret.onHeadersReceived(); + } + }; + data.response = ret; + ret.setSocket(data.socket); synchronized (mMiddleware) { for (AsyncHttpClientMiddleware middleware: mMiddleware) { - Cancellable socketCancellable = middleware.getSocket(data); - if (socketCancellable != null) { - data.socketCancellable = socketCancellable; - cancel.setParent(socketCancellable); - return; - } + if (middleware.exchangeHeaders(data)) + break; } } - reportConnectedCompleted(cancel, new IllegalArgumentException("invalid uri"), null, request, callback); } public static abstract class RequestCallbackBase<T> implements RequestCallback<T> { diff --git a/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpClientMiddleware.java b/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpClientMiddleware.java index b198199..edce25d 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpClientMiddleware.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpClientMiddleware.java @@ -14,6 +14,7 @@ import com.koushikdutta.async.util.UntypedHashtable; */ public interface AsyncHttpClientMiddleware { public interface ResponseHead { + public AsyncSocket socket(); public String protocol(); public String message(); public int code(); @@ -24,6 +25,8 @@ public interface AsyncHttpClientMiddleware { public ResponseHead headers(Headers headers); public DataSink sink(); public ResponseHead sink(DataSink sink); + public DataEmitter emitter(); + public ResponseHead emitter(DataEmitter emitter); } public static class OnRequestData { @@ -37,14 +40,14 @@ public interface AsyncHttpClientMiddleware { public String protocol; } - public static class SendHeaderData extends GetSocketData { + public static class ExchangeHeaderData extends GetSocketData { public AsyncSocket socket; public ResponseHead response; public CompletedCallback sendHeadersCallback; + public CompletedCallback receiveHeadersCallback; } - public static class OnHeadersReceivedData extends SendHeaderData { -// public Headers headers; + public static class OnHeadersReceivedData extends ExchangeHeaderData { } public static class OnBodyData extends OnHeadersReceivedData { @@ -69,11 +72,12 @@ public interface AsyncHttpClientMiddleware { public Cancellable getSocket(GetSocketData data); /** - * Called before the headers are sent via the socket + * Called before when the headers are sent and received via the socket. + * Implementers return true to denote they will manage header exchange. * @param data * @return */ - public boolean sendHeaders(SendHeaderData data); + public boolean exchangeHeaders(ExchangeHeaderData data); /** * Called once the headers have been received via the socket diff --git a/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpResponseImpl.java b/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpResponseImpl.java index fa2bb11..7938b93 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpResponseImpl.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpResponseImpl.java @@ -19,9 +19,7 @@ import java.io.IOException; import java.nio.charset.Charset; abstract class AsyncHttpResponseImpl extends FilteredDataEmitter implements AsyncSocket, AsyncHttpResponse, AsyncHttpClientMiddleware.ResponseHead { - private AsyncHttpRequestBody mWriter; - - public AsyncSocket getSocket() { + public AsyncSocket socket() { return mSocket; } @@ -36,17 +34,12 @@ abstract class AsyncHttpResponseImpl extends FilteredDataEmitter implements Asyn return; mSocket.setEndCallback(mReporter); - - mWriter = mRequest.getBody(); - - LineEmitter liner = new LineEmitter(); - exchange.setDataCallback(liner); - liner.setLineCallback(mHeaderCallback); } protected void onHeadersSent() { - if (mWriter != null) { - mWriter.write(mRequest, AsyncHttpResponseImpl.this, new CompletedCallback() { + AsyncHttpRequestBody requestBody = mRequest.getBody(); + if (requestBody != null) { + requestBody.write(mRequest, AsyncHttpResponseImpl.this, new CompletedCallback() { @Override public void onCompleted(Exception ex) { onRequestCompleted(ex); @@ -75,48 +68,17 @@ abstract class AsyncHttpResponseImpl extends FilteredDataEmitter implements Asyn protected void onHeadersReceived() { } - StringCallback mHeaderCallback = new StringCallback() { - private Headers mRawHeaders = new Headers(); - private String statusLine; - @Override - public void onStringAvailable(String s) { - try { - if (statusLine == null) { - statusLine = s; - } - else if (!"\r".equals(s)) { - mRawHeaders.addLine(s); - } - else { - String[] parts = statusLine.split(" ", 3); - if (parts.length != 3) - throw new Exception(new IOException("Not HTTP")); - - protocol = parts[0]; - code = Integer.parseInt(parts[1]); - message = parts[2]; - mHeaders = mRawHeaders; - onHeadersReceived(); - // socket may get detached after headers (websocket) - if (mSocket == null) - return; - DataEmitter emitter; - // HEAD requests must not return any data. They still may - // return content length, etc, which will confuse the body decoder - if (AsyncHttpHead.METHOD.equalsIgnoreCase(mRequest.getMethod())) { - emitter = HttpUtil.EndEmitter.create(getServer(), null); - } - else { - emitter = HttpUtil.getBodyDecoder(mSocket, Protocol.get(protocol), mHeaders, false); - } - setDataEmitter(emitter); - } - } - catch (Exception ex) { - report(ex); - } - } - }; + + @Override + public DataEmitter emitter() { + return getDataEmitter(); + } + + @Override + public AsyncHttpClientMiddleware.ResponseHead emitter(DataEmitter emitter) { + setDataEmitter(emitter); + return this; + } @Override protected void report(Exception e) { diff --git a/AndroidAsync/src/com/koushikdutta/async/http/HttpTransportMiddleware.java b/AndroidAsync/src/com/koushikdutta/async/http/HttpTransportMiddleware.java index 1ca6b5a..837ca92 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/HttpTransportMiddleware.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/HttpTransportMiddleware.java @@ -1,20 +1,23 @@ package com.koushikdutta.async.http; +import com.koushikdutta.async.AsyncSocket; +import com.koushikdutta.async.DataEmitter; +import com.koushikdutta.async.LineEmitter; import com.koushikdutta.async.Util; -import com.koushikdutta.async.callback.CompletedCallback; import com.koushikdutta.async.http.body.AsyncHttpRequestBody; import com.koushikdutta.async.http.filter.ChunkedOutputFilter; +import java.io.IOException; + /** * Created by koush on 7/24/14. */ public class HttpTransportMiddleware extends SimpleMiddleware { - @Override - public boolean sendHeaders(final SendHeaderData data) { + public boolean exchangeHeaders(final ExchangeHeaderData data) { Protocol p = Protocol.get(data.protocol); if (p != null && p != Protocol.HTTP_1_0 && p != Protocol.HTTP_1_1) - return super.sendHeaders(data); + return super.exchangeHeaders(data); AsyncHttpRequest request = data.request; AsyncHttpRequestBody requestBody = data.request.getBody(); @@ -35,47 +38,58 @@ public class HttpTransportMiddleware extends SimpleMiddleware { String rs = request.getHeaders().toPrefixString(rl); request.logv("\n" + rs); - Util.writeAll(data.socket, rs.getBytes(), new CompletedCallback() { + Util.writeAll(data.socket, rs.getBytes(), data.sendHeadersCallback); + + LineEmitter.StringCallback headerCallback = new LineEmitter.StringCallback() { + Headers mRawHeaders = new Headers(); + String statusLine; + @Override - public void onCompleted(Exception ex) { - data.sendHeadersCallback.onCompleted(ex); + public void onStringAvailable(String s) { + try { + if (statusLine == null) { + statusLine = s; + } + else if (!"\r".equals(s)) { + mRawHeaders.addLine(s); + } + else { + String[] parts = statusLine.split(" ", 3); + if (parts.length != 3) + throw new Exception(new IOException("Not HTTP")); + + data.response.headers(mRawHeaders); + String protocol = parts[0]; + data.response.protocol(protocol); + data.response.code(Integer.parseInt(parts[1])); + data.response.message(parts[2]); + data.receiveHeadersCallback.onCompleted(null); + + // socket may get detached after headers (websocket) + AsyncSocket socket = data.response.socket(); + if (socket == null) + return; + DataEmitter emitter; + // HEAD requests must not return any data. They still may + // return content length, etc, which will confuse the body decoder + if (AsyncHttpHead.METHOD.equalsIgnoreCase(data.request.getMethod())) { + emitter = HttpUtil.EndEmitter.create(socket.getServer(), null); + } + else { + emitter = HttpUtil.getBodyDecoder(socket, Protocol.get(protocol), mRawHeaders, false); + } + data.response.emitter(emitter); + } + } + catch (Exception ex) { + data.receiveHeadersCallback.onCompleted(ex); + } } - }); + }; -// LineEmitter.StringCallback headerCallback = new LineEmitter.StringCallback() { -// Headers mRawHeaders = new Headers(); -// String statusLine; -// -// @Override -// public void onStringAvailable(String s) { -// try { -// if (statusLine == null) { -// statusLine = s; -// } -// else if (!"\r".equals(s)) { -// mRawHeaders.addLine(s); -// } -// else { -// String[] parts = statusLine.split(" ", 3); -// if (parts.length != 3) -// throw new Exception(new IOException("Not HTTP")); -// -// data.response.headers(mRawHeaders); -// data.response.protocol(parts[0]); -// data.response.code(Integer.parseInt(parts[1])); -// data.response.message(parts[2]); -// data.sendHeadersCallback.onCompleted(null); -// } -// } -// catch (Exception ex) { -// data.sendHeadersCallback.onCompleted(ex); -// } -// } -// }; -// -// LineEmitter liner = new LineEmitter(); -// data.socket.setDataCallback(liner); -// liner.setLineCallback(headerCallback); + LineEmitter liner = new LineEmitter(); + data.socket.setDataCallback(liner); + liner.setLineCallback(headerCallback); return true; } } diff --git a/AndroidAsync/src/com/koushikdutta/async/http/SimpleMiddleware.java b/AndroidAsync/src/com/koushikdutta/async/http/SimpleMiddleware.java index 7fe545d..242fdab 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/SimpleMiddleware.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/SimpleMiddleware.java @@ -25,7 +25,7 @@ public class SimpleMiddleware implements AsyncHttpClientMiddleware { } @Override - public boolean sendHeaders(SendHeaderData data) { + public boolean exchangeHeaders(ExchangeHeaderData data) { return false; } } diff --git a/AndroidAsync/src/com/koushikdutta/async/http/spdy/HttpTransport.java b/AndroidAsync/src/com/koushikdutta/async/http/spdy/HttpTransport.java deleted file mode 100644 index 0d017bb..0000000 --- a/AndroidAsync/src/com/koushikdutta/async/http/spdy/HttpTransport.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.koushikdutta.async.http.spdy; - -import com.koushikdutta.async.http.AsyncHttpClientMiddleware; -import com.koushikdutta.async.http.SimpleMiddleware; - -/** - * Created by koush on 7/19/14. - */ -public class HttpTransport extends SimpleMiddleware { - @Override - public boolean sendHeaders(SendHeaderData data) { - return super.sendHeaders(data); - } - -} |