diff options
author | Koushik Dutta <koushd@gmail.com> | 2015-02-15 21:26:02 -0800 |
---|---|---|
committer | Koushik Dutta <koushd@gmail.com> | 2015-02-15 21:26:02 -0800 |
commit | 2ec170541343eae8b59a7011b2c910454823e8c6 (patch) | |
tree | b4ec55f26fbb162f1786439bb2a2a7434a914304 | |
parent | 547a5edb1440cf1f7c02e5152af0c5a914c38cfc (diff) | |
download | AndroidAsync-2ec170541343eae8b59a7011b2c910454823e8c6.tar.gz AndroidAsync-2ec170541343eae8b59a7011b2c910454823e8c6.tar.bz2 AndroidAsync-2ec170541343eae8b59a7011b2c910454823e8c6.zip |
potential fix for issue 484
https://github.com/koush/ion/issues/484
3 files changed, 97 insertions, 143 deletions
diff --git a/AndroidAsync/src/com/koushikdutta/async/http/AsyncSocketMiddleware.java b/AndroidAsync/src/com/koushikdutta/async/http/AsyncSocketMiddleware.java index 7b642e6..4fc23b0 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/AsyncSocketMiddleware.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/AsyncSocketMiddleware.java @@ -198,7 +198,7 @@ public class AsyncSocketMiddleware extends SimpleMiddleware { @Override protected void error(Exception e) { super.error(e); - data.connectCallback.onConnectCompleted(e, null); + wrapCallback(data, uri, port, false, data.connectCallback).onConnectCompleted(e, null); } @Override @@ -210,7 +210,7 @@ public class AsyncSocketMiddleware extends SimpleMiddleware { if (lastException == null) lastException = new ConnectionFailedException("Unable to connect to remote address"); if (setComplete(lastException)) { - data.connectCallback.onConnectCompleted(lastException, null); + wrapCallback(data, uri, port, false, data.connectCallback).onConnectCompleted(lastException, null); } } }); diff --git a/AndroidAsync/src/com/koushikdutta/async/http/spdy/SpdyMiddleware.java b/AndroidAsync/src/com/koushikdutta/async/http/spdy/SpdyMiddleware.java index b5c838e..692c4b1 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/spdy/SpdyMiddleware.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/spdy/SpdyMiddleware.java @@ -5,6 +5,7 @@ import android.text.TextUtils; import com.koushikdutta.async.AsyncSSLSocket; import com.koushikdutta.async.AsyncSSLSocketWrapper; +import com.koushikdutta.async.AsyncSocket; import com.koushikdutta.async.ByteBufferList; import com.koushikdutta.async.DataEmitter; import com.koushikdutta.async.callback.ConnectCallback; @@ -124,6 +125,7 @@ public class SpdyMiddleware extends AsyncSSLSocketMiddleware { boolean spdyEnabled; private static class SpdyConnectionWaiter extends MultiFuture<AsyncSpdyConnection> { + SimpleCancellable originalCancellable = new SimpleCancellable(); } public boolean getSpdyEnabled() { @@ -167,67 +169,78 @@ public class SpdyMiddleware extends AsyncSSLSocketMiddleware { } private static final NoSpdyException NO_SPDY = new NoSpdyException(); - private void noSpdy(final GetSocketData data, String key) { + private void noSpdy(String key) { SpdyConnectionWaiter conn = connections.remove(key); if (conn != null) conn.setComplete(NO_SPDY); - data.request.logv("not using spdy"); + } + + private void invokeConnect(String key, final ConnectCallback callback, Exception e, AsyncSSLSocket socket) { + SpdyConnectionWaiter waiter = connections.get(key); + if (waiter.originalCancellable.setComplete()) + callback.onConnectCompleted(e, socket); } @Override protected AsyncSSLSocketWrapper.HandshakeCallback createHandshakeCallback(final GetSocketData data, final ConnectCallback callback) { + final String key = data.state.get("spdykey"); + if (key == null) + return super.createHandshakeCallback(data, callback); + return new AsyncSSLSocketWrapper.HandshakeCallback() { @Override public void onHandshakeCompleted(Exception e, AsyncSSLSocket socket) { - final String key = data.request.getUri().getHost(); data.request.logv("checking spdy handshake"); if (e != null || nativeGetAlpnNegotiatedProtocol == null) { - callback.onConnectCompleted(e, socket); - noSpdy(data, key); + invokeConnect(key, callback, e, socket); + noSpdy(key); return; } + String protoString; try { long ptr = (Long)sslNativePointer.get(socket.getSSLEngine()); byte[] proto = (byte[])nativeGetAlpnNegotiatedProtocol.invoke(null, ptr); if (proto == null) { - callback.onConnectCompleted(null, socket); - noSpdy(data, key); + invokeConnect(key, callback, null, socket); + noSpdy(key); return; } - String protoString = new String(proto); + protoString = new String(proto); Protocol p = Protocol.get(protoString); if (p == null) { - callback.onConnectCompleted(null, socket); - noSpdy(data, key); + invokeConnect(key, callback, null, socket); + noSpdy(key); return; } - final AsyncSpdyConnection connection = new AsyncSpdyConnection(socket, Protocol.get(protoString)) { - boolean hasReceivedSettings; - @Override - public void settings(boolean clearPrevious, Settings settings) { - super.settings(clearPrevious, settings); - if (!hasReceivedSettings) { - try { - sendConnectionPreface(); - } catch (IOException e1) { - e1.printStackTrace(); - } - hasReceivedSettings = true; + } + catch (Exception ex) { + throw new AssertionError(ex); + } + + final AsyncSpdyConnection connection = new AsyncSpdyConnection(socket, Protocol.get(protoString)) { + boolean hasReceivedSettings; + @Override + public void settings(boolean clearPrevious, Settings settings) { + super.settings(clearPrevious, settings); + if (!hasReceivedSettings) { + try { + sendConnectionPreface(); + } catch (IOException e1) { + e1.printStackTrace(); + } + hasReceivedSettings = true; + SpdyConnectionWaiter waiter = connections.get(key); + + if (waiter.originalCancellable.setComplete()) { data.request.logv("using new spdy connection for host: " + data.request.getUri().getHost()); newSocket(data, this, callback); - - SpdyConnectionWaiter waiter = connections.get(key); - waiter.setComplete(this); } + + waiter.setComplete(this); } - }; - } - catch (Exception ex) { - socket.close(); - callback.onConnectCompleted(ex, null); - noSpdy(data, key); - } + } + }; } }; } @@ -283,16 +296,39 @@ public class SpdyMiddleware extends AsyncSSLSocketMiddleware { } @Override - public Cancellable getSocket(final GetSocketData data) { - if (!spdyEnabled) - return super.getSocket(data); + protected ConnectCallback wrapCallback(final GetSocketData data, final Uri uri, final int port, final boolean proxied, ConnectCallback callback) { + final ConnectCallback superCallback = super.wrapCallback(data, uri, port, proxied, callback); + final String key = data.state.get("spdykey"); + if (key == null) + return superCallback; + + // new outgoing connection, try to make this a spdy connection + return new ConnectCallback() { + @Override + public void onConnectCompleted(Exception ex, AsyncSocket socket) { + // an exception here is an ssl or network exception... don't rule spdy out yet, but + // trigger the waiters + if (ex != null) { + final SpdyConnectionWaiter conn = connections.remove(key); + if (conn != null) + conn.setComplete(ex); + } + superCallback.onConnectCompleted(ex, socket); + } + }; + } + @Override + public Cancellable getSocket(final GetSocketData data) { final Uri uri = data.request.getUri(); final int port = getSchemePort(data.request.getUri()); if (port == -1) { return null; } + if (!spdyEnabled) + return super.getSocket(data); + // TODO: figure out why POST does not work if sending content-length header // see above regarding app engine comment as to why: drive requires content-length // but app engine sends a GO_AWAY if it sees a content-length... @@ -300,24 +336,30 @@ public class SpdyMiddleware extends AsyncSSLSocketMiddleware { return super.getSocket(data); // can we use an existing connection to satisfy this, or do we need a new one? - String key = uri.getHost(); + String key = uri.getHost() + port; SpdyConnectionWaiter conn = connections.get(key); - if (conn != null && conn.tryGet() != null && !conn.tryGet().socket.isOpen()) { - connections.remove(key); - conn = null; + if (conn != null) { + if (conn.tryGetException() instanceof NoSpdyException) + return super.getSocket(data); + + // dead connection check + if (conn.tryGet() != null && !conn.tryGet().socket.isOpen()) { + // old spdy connection is derped, kill it with fire. + connections.remove(key); + conn = null; + } } if (conn == null) { - Cancellable superSocket = super.getSocket(data);; - // see if we reuse a socket synchronously, otherwise a new connection is being created - if (!superSocket.isDone()) { - data.request.logv("waiting for spdy connection for host: " + data.request.getUri().getHost()); - connections.put(key, new SpdyConnectionWaiter()); - } - else { - data.request.logv("attempting spdy connection for host: " + data.request.getUri().getHost()); - } - return superSocket; + // no connection has ever been attempted (or previous one had a network death), so attempt one + data.state.put("spdykey", key); + // if we got something back synchronously, it's a keep alive socket + Cancellable ret = super.getSocket(data); + if (ret.isDone() || ret.isCancelled()) + return ret; + conn = new SpdyConnectionWaiter(); + connections.put(key, conn); + return conn.originalCancellable; } data.request.logv("waiting for potential spdy connection for host: " + data.request.getUri().getHost()); @@ -331,14 +373,13 @@ public class SpdyMiddleware extends AsyncSSLSocketMiddleware { return; } if (e != null) { - data.request.loge("spdy not available", e); - ret.setComplete(); - data.connectCallback.onConnectCompleted(e, null); + if (ret.setComplete()) + data.connectCallback.onConnectCompleted(e, null); return; } data.request.logv("using existing spdy connection for host: " + data.request.getUri().getHost()); - ret.setComplete(); - newSocket(data, conn, data.connectCallback); + if (ret.setComplete()) + newSocket(data, conn, data.connectCallback); } }); diff --git a/AndroidAsyncSample/AndroidAsyncSample.iml b/AndroidAsyncSample/AndroidAsyncSample.iml deleted file mode 100644 index 8a2f8e3..0000000 --- a/AndroidAsyncSample/AndroidAsyncSample.iml +++ /dev/null @@ -1,87 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="Gradle.AndroidAsync" external.system.module.version="unspecified" type="JAVA_MODULE" version="4"> - <component name="FacetManager"> - <facet type="android-gradle" name="Android-Gradle"> - <configuration> - <option name="GRADLE_PROJECT_PATH" value=":AndroidAsync:AndroidAsyncSample" /> - </configuration> - </facet> - <facet type="android" name="Android"> - <configuration> - <option name="SELECTED_BUILD_VARIANT" value="debug" /> - <option name="SELECTED_TEST_ARTIFACT" value="_android_test_" /> - <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" /> - <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" /> - <option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" /> - <option name="SOURCE_GEN_TASK_NAME" value="generateDebugSources" /> - <option name="TEST_SOURCE_GEN_TASK_NAME" value="generateDebugAndroidTestSources" /> - <option name="ALLOW_USER_CONFIGURATION" value="false" /> - <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" /> - </configuration> - </facet> - </component> - <component name="NewModuleRootManager" inherit-compiler-output="false"> - <output url="file://$MODULE_DIR$/build/intermediates/classes/debug" /> - <output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/debug" /> - <exclude-output /> - <content url="file://$MODULE_DIR$"> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/debug" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/androidTest/debug" type="java-test-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/res" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" /> - <excludeFolder url="file://$MODULE_DIR$/build/outputs" /> - </content> - <orderEntry type="jdk" jdkName="Android API 21 Platform" jdkType="Android SDK" /> - <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="module" module-name="AndroidAsync-AndroidAsync" exported="" /> - </component> -</module> - |