aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AndroidAsync/src/com/koushikdutta/async/http/AsyncSocketMiddleware.java4
-rw-r--r--AndroidAsync/src/com/koushikdutta/async/http/spdy/SpdyMiddleware.java149
-rw-r--r--AndroidAsyncSample/AndroidAsyncSample.iml87
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>
-