diff options
author | Koushik Dutta <koushd@gmail.com> | 2014-07-28 20:07:37 -0700 |
---|---|---|
committer | Koushik Dutta <koushd@gmail.com> | 2014-07-28 20:07:37 -0700 |
commit | 1280d7b44e14dbac19f773136742224bcab91302 (patch) | |
tree | b0409cb1a6c26347891b141157c0b4ec4d9711cb /AndroidAsync | |
parent | 9ec1a8ded7e3b59da7c2b53ebe0e4cf4ece566e1 (diff) | |
download | AndroidAsync-1280d7b44e14dbac19f773136742224bcab91302.tar.gz AndroidAsync-1280d7b44e14dbac19f773136742224bcab91302.tar.bz2 AndroidAsync-1280d7b44e14dbac19f773136742224bcab91302.zip |
spdy posting works.
Diffstat (limited to 'AndroidAsync')
15 files changed, 236 insertions, 151 deletions
diff --git a/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpClient.java b/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpClient.java index ee4f805..49c7de5 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpClient.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpClient.java @@ -17,7 +17,6 @@ import com.koushikdutta.async.future.Cancellable; import com.koushikdutta.async.future.Future; import com.koushikdutta.async.future.FutureCallback; import com.koushikdutta.async.future.SimpleFuture; -import com.koushikdutta.async.http.AsyncHttpClientMiddleware.OnRequestCompleteData; import com.koushikdutta.async.http.callback.HttpConnectCallback; import com.koushikdutta.async.http.callback.RequestCallback; import com.koushikdutta.async.parser.AsyncParser; @@ -204,7 +203,7 @@ public class AsyncHttpClient { return; } final Uri uri = request.getUri(); - final OnRequestCompleteData data = new OnRequestCompleteData(); + final AsyncHttpClientMiddleware.OnResponseCompleteDataOnRequestSentData data = new AsyncHttpClientMiddleware.OnResponseCompleteDataOnRequestSentData(); request.executionTime = System.currentTimeMillis(); data.request = request; @@ -273,6 +272,12 @@ public class AsyncHttpClient { // set up the system default proxy and connect setupAndroidProxy(request); + // set the implicit content type + if (request.getBody() != null) { + if (request.getHeaders().get("Content-Type") == null) + request.getHeaders().set("Content-Type", request.getBody().getContentType()); + } + synchronized (mMiddleware) { for (AsyncHttpClientMiddleware middleware: mMiddleware) { Cancellable socketCancellable = middleware.getSocket(data); @@ -288,13 +293,18 @@ public class AsyncHttpClient { private void executeSocket(final AsyncHttpRequest request, final int redirectCount, final FutureAsyncHttpResponse cancel, final HttpConnectCallback callback, - final OnRequestCompleteData data) { + final AsyncHttpClientMiddleware.OnResponseCompleteDataOnRequestSentData 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) { + if (ex != null) { + reportConnectedCompleted(cancel, ex, null, request, callback); + return; + } + request.logv("request completed"); if (cancel.isCancelled()) return; @@ -303,6 +313,12 @@ public class AsyncHttpClient { mServer.removeAllCallbacks(cancel.scheduled); cancel.scheduled = mServer.postDelayed(cancel.timeoutRunnable, getTimeoutRemaining(request)); } + + synchronized (mMiddleware) { + for (AsyncHttpClientMiddleware middleware: mMiddleware) { + middleware.onRequestSent(data); + } + } } @Override @@ -402,7 +418,7 @@ public class AsyncHttpClient { data.exception = ex; synchronized (mMiddleware) { for (AsyncHttpClientMiddleware middleware: mMiddleware) { - middleware.onRequestComplete(data); + middleware.onResponseComplete(data); } } } diff --git a/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpClientMiddleware.java b/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpClientMiddleware.java index edce25d..48be209 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpClientMiddleware.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpClientMiddleware.java @@ -40,21 +40,24 @@ public interface AsyncHttpClientMiddleware { public String protocol; } - public static class ExchangeHeaderData extends GetSocketData { + public static class OnExchangeHeaderData extends GetSocketData { public AsyncSocket socket; public ResponseHead response; public CompletedCallback sendHeadersCallback; public CompletedCallback receiveHeadersCallback; } - public static class OnHeadersReceivedData extends ExchangeHeaderData { + public static class OnRequestSentData extends OnExchangeHeaderData { } - public static class OnBodyData extends OnHeadersReceivedData { + public static class OnHeadersReceivedDataOnRequestSentData extends OnRequestSentData { + } + + public static class OnBodyDataOnRequestSentData extends OnHeadersReceivedDataOnRequestSentData { public DataEmitter bodyEmitter; } - public static class OnRequestCompleteData extends OnBodyData { + public static class OnResponseCompleteDataOnRequestSentData extends OnBodyDataOnRequestSentData { public Exception exception; } @@ -77,23 +80,31 @@ public interface AsyncHttpClientMiddleware { * @param data * @return */ - public boolean exchangeHeaders(ExchangeHeaderData data); + public boolean exchangeHeaders(OnExchangeHeaderData data); + + /** + * Called once the headers and any optional request body has + * been sent + * @param data + */ + public void onRequestSent(OnRequestSentData data); /** * Called once the headers have been received via the socket * @param data */ - public void onHeadersReceived(OnHeadersReceivedData data); + public void onHeadersReceived(OnHeadersReceivedDataOnRequestSentData data); /** * Called before the body is decoded * @param data */ - public void onBodyDecoder(OnBodyData data); + public void onBodyDecoder(OnBodyDataOnRequestSentData data); /** - * Called once the request is complete + * Called once the request is complete and response has been received, + * or if an error occurred * @param data */ - public void onRequestComplete(OnRequestCompleteData data); + public void onResponseComplete(OnResponseCompleteDataOnRequestSentData data); } diff --git a/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpResponseImpl.java b/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpResponseImpl.java index 7938b93..cd6ffff 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpResponseImpl.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpResponseImpl.java @@ -193,8 +193,7 @@ abstract class AsyncHttpResponseImpl extends FilteredDataEmitter implements Asyn @Override public void end() { - if (mSink instanceof ChunkedOutputFilter) - mSink.end(); + throw new AssertionError("end called?"); } @Override diff --git a/AndroidAsync/src/com/koushikdutta/async/http/AsyncSocketMiddleware.java b/AndroidAsync/src/com/koushikdutta/async/http/AsyncSocketMiddleware.java index 5c11684..35ce3dd 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/AsyncSocketMiddleware.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/AsyncSocketMiddleware.java @@ -345,7 +345,7 @@ public class AsyncSocketMiddleware extends SimpleMiddleware { } @Override - public void onRequestComplete(final OnRequestCompleteData data) { + public void onResponseComplete(final OnResponseCompleteDataOnRequestSentData data) { if (!data.state.get(getClass().getCanonicalName() + ".owned", false)) { return; } diff --git a/AndroidAsync/src/com/koushikdutta/async/http/Headers.java b/AndroidAsync/src/com/koushikdutta/async/http/Headers.java index 8fb7025..1b4cdc2 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/Headers.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/Headers.java @@ -29,20 +29,20 @@ public class Headers { } public List<String> getAll(String header) { - return map.get(header); + return map.get(header.toLowerCase()); } public String get(String header) { - return map.getString(header); + return map.getString(header.toLowerCase()); } public Headers set(String header, String value) { - map.put(header, value); + map.put(header.toLowerCase(), value); return this; } public Headers add(String header, String value) { - map.add(header, value); + map.add(header.toLowerCase(), value); return this; } @@ -66,21 +66,26 @@ public class Headers { } public Headers addAll(Map<String, List<String>> m) { - map.putAll(m); + for (String key: m.keySet()) { + for (String value: m.get(key)) { + add(key, value); + } + } return this; } public Headers addAll(Headers headers) { + // safe to addall since this is another Headers object map.putAll(headers.map); return this; } public List<String> removeAll(String header) { - return map.remove(header); + return map.remove(header.toLowerCase()); } public String remove(String header) { - List<String> r = removeAll(header); + List<String> r = removeAll(header.toLowerCase()); if (r == null || r.size() == 0) return null; return r.get(0); diff --git a/AndroidAsync/src/com/koushikdutta/async/http/HttpTransportMiddleware.java b/AndroidAsync/src/com/koushikdutta/async/http/HttpTransportMiddleware.java index 837ca92..6659193 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/HttpTransportMiddleware.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/HttpTransportMiddleware.java @@ -14,7 +14,7 @@ import java.io.IOException; */ public class HttpTransportMiddleware extends SimpleMiddleware { @Override - public boolean exchangeHeaders(final ExchangeHeaderData data) { + public boolean exchangeHeaders(final OnExchangeHeaderData data) { Protocol p = Protocol.get(data.protocol); if (p != null && p != Protocol.HTTP_1_0 && p != Protocol.HTTP_1_1) return super.exchangeHeaders(data); @@ -23,8 +23,6 @@ public class HttpTransportMiddleware extends SimpleMiddleware { AsyncHttpRequestBody requestBody = data.request.getBody(); if (requestBody != null) { - if (request.getHeaders().get("Content-Type") == null) - request.getHeaders().set("Content-Type", requestBody.getContentType()); if (requestBody.length() >= 0) { request.getHeaders().set("Content-Length", String.valueOf(requestBody.length())); data.response.sink(data.socket); @@ -92,4 +90,14 @@ public class HttpTransportMiddleware extends SimpleMiddleware { liner.setLineCallback(headerCallback); return true; } + + @Override + public void onRequestSent(OnRequestSentData data) { + Protocol p = Protocol.get(data.protocol); + if (p != null && p != Protocol.HTTP_1_0 && p != Protocol.HTTP_1_1) + return; + + if (data.response.sink() instanceof ChunkedOutputFilter) + data.response.sink().end(); + } } diff --git a/AndroidAsync/src/com/koushikdutta/async/http/SimpleMiddleware.java b/AndroidAsync/src/com/koushikdutta/async/http/SimpleMiddleware.java index 242fdab..8b2998a 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/SimpleMiddleware.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/SimpleMiddleware.java @@ -13,19 +13,23 @@ public class SimpleMiddleware implements AsyncHttpClientMiddleware { } @Override - public void onHeadersReceived(OnHeadersReceivedData data) { + public boolean exchangeHeaders(OnExchangeHeaderData data) { + return false; } @Override - public void onBodyDecoder(OnBodyData data) { + public void onRequestSent(OnRequestSentData data) { } @Override - public void onRequestComplete(OnRequestCompleteData data) { + public void onHeadersReceived(OnHeadersReceivedDataOnRequestSentData data) { } @Override - public boolean exchangeHeaders(ExchangeHeaderData data) { - return false; + public void onBodyDecoder(OnBodyDataOnRequestSentData data) { + } + + @Override + public void onResponseComplete(OnResponseCompleteDataOnRequestSentData data) { } } diff --git a/AndroidAsync/src/com/koushikdutta/async/http/body/MultipartFormDataBody.java b/AndroidAsync/src/com/koushikdutta/async/http/body/MultipartFormDataBody.java index 309d9c3..4cf41f2 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/body/MultipartFormDataBody.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/body/MultipartFormDataBody.java @@ -126,11 +126,9 @@ public class MultipartFormDataBody extends BoundaryEmitter implements AsyncHttpR int written; @Override public void write(AsyncHttpRequest request, final DataSink sink, final CompletedCallback completed) { - if (mParts == null) { - sink.end(); + if (mParts == null) return; - } - + Continuation c = new Continuation(new CompletedCallback() { @Override public void onCompleted(Exception ex) { diff --git a/AndroidAsync/src/com/koushikdutta/async/http/body/UrlEncodedFormBody.java b/AndroidAsync/src/com/koushikdutta/async/http/body/UrlEncodedFormBody.java index b50b7c7..b52fc75 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/body/UrlEncodedFormBody.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/body/UrlEncodedFormBody.java @@ -45,6 +45,7 @@ public class UrlEncodedFormBody implements AsyncHttpRequestBody<Multimap> { mBodyBytes = b.toString().getBytes("ISO-8859-1"); } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); } } diff --git a/AndroidAsync/src/com/koushikdutta/async/http/cache/ResponseCacheMiddleware.java b/AndroidAsync/src/com/koushikdutta/async/http/cache/ResponseCacheMiddleware.java index 57e3895..40aa530 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/cache/ResponseCacheMiddleware.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/cache/ResponseCacheMiddleware.java @@ -213,7 +213,7 @@ public class ResponseCacheMiddleware extends SimpleMiddleware { // step 2) if this is a conditional cache request, serve it from the cache if necessary // otherwise, see if it is cacheable @Override - public void onBodyDecoder(OnBodyData data) { + public void onBodyDecoder(OnBodyDataOnRequestSentData data) { CachedSocket cached = com.koushikdutta.async.Util.getWrappedSocket(data.socket, CachedSocket.class); if (cached != null) { data.response.headers().set(SERVED_FROM, CACHE); @@ -292,7 +292,7 @@ public class ResponseCacheMiddleware extends SimpleMiddleware { // step 3: close up shop @Override - public void onRequestComplete(OnRequestCompleteData data) { + public void onResponseComplete(OnResponseCompleteDataOnRequestSentData data) { CacheData cacheData = data.state.get("cache-data"); if (cacheData != null && cacheData.snapshot != null) StreamUtility.closeQuietly(cacheData.snapshot); diff --git a/AndroidAsync/src/com/koushikdutta/async/http/filter/ChunkedOutputFilter.java b/AndroidAsync/src/com/koushikdutta/async/http/filter/ChunkedOutputFilter.java index f0f18d3..00d3f9e 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/filter/ChunkedOutputFilter.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/filter/ChunkedOutputFilter.java @@ -25,6 +25,7 @@ public class ChunkedOutputFilter extends FilteredDataSink { ByteBufferList fin = new ByteBufferList(); write(fin); setMaxBuffer(0); - super.end(); + // do NOT call through to super.end, as chunking is a framing protocol. + // we don't want to close the underlying transport. } } diff --git a/AndroidAsync/src/com/koushikdutta/async/http/server/BoundaryEmitter.java b/AndroidAsync/src/com/koushikdutta/async/http/server/BoundaryEmitter.java index 3eab5e2..e642ba4 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/server/BoundaryEmitter.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/server/BoundaryEmitter.java @@ -1,6 +1,5 @@ package com.koushikdutta.async.http.server; -import android.util.Log; import com.koushikdutta.async.ByteBufferList; import com.koushikdutta.async.DataEmitter; import com.koushikdutta.async.FilteredDataEmitter; diff --git a/AndroidAsync/src/com/koushikdutta/async/http/spdy/AsyncSpdyConnection.java b/AndroidAsync/src/com/koushikdutta/async/http/spdy/AsyncSpdyConnection.java index aca08f2..29b7fb8 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/spdy/AsyncSpdyConnection.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/spdy/AsyncSpdyConnection.java @@ -94,7 +94,7 @@ public class AsyncSpdyConnection implements FrameReader.Handler { } public class SpdySocket implements AsyncSocket { - long bytesLeftInWriteWindow; + long bytesLeftInWriteWindow = AsyncSpdyConnection.this.peerSettings.getInitialWindowSize(Settings.DEFAULT_INITIAL_WINDOW_SIZE); WritableCallback writable; final int id; CompletedCallback closedCallback; @@ -195,9 +195,29 @@ public class AsyncSpdyConnection implements FrameReader.Handler { return null; } + ByteBufferList writing = new ByteBufferList(); @Override public void write(ByteBufferList bb) { - System.out.println("writing!"); + int canWrite = (int)Math.min(bytesLeftInWriteWindow, AsyncSpdyConnection.this.bytesLeftInWriteWindow); + canWrite = Math.min(bb.remaining(), canWrite); + if (canWrite == 0) { + System.out.println("derp"); + return; + } + if (canWrite < bb.remaining()) { + if (writing.hasRemaining()) + throw new AssertionError("wtf"); + bb.get(writing, canWrite); + bb = writing; + } + + try { + writer.data(false, id, bb); + bytesLeftInWriteWindow -= canWrite; + } + catch (IOException e) { + throw new AssertionError(e); + } } @Override @@ -217,6 +237,12 @@ public class AsyncSpdyConnection implements FrameReader.Handler { @Override public void end() { + try { + writer.data(true, id, writing); + } + catch (IOException e) { + throw new AssertionError(e); + } } @Override diff --git a/AndroidAsync/src/com/koushikdutta/async/http/spdy/Spdy3.java b/AndroidAsync/src/com/koushikdutta/async/http/spdy/Spdy3.java index 35f4e6e..ddea2f2 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/spdy/Spdy3.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/spdy/Spdy3.java @@ -167,12 +167,12 @@ final class Spdy3 implements Variant { } }; + ByteBufferList partial = new ByteBufferList(); private final DataCallback onDataFrame = new DataCallback() { @Override public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) { int toRead = Math.min(bb.remaining(), length); if (toRead < bb.remaining()) { - ByteBufferList partial = new ByteBufferList(); bb.get(partial, toRead); bb = partial; } diff --git a/AndroidAsync/src/com/koushikdutta/async/http/spdy/SpdyMiddleware.java b/AndroidAsync/src/com/koushikdutta/async/http/spdy/SpdyMiddleware.java index 1ac43d5..b5a608c 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/spdy/SpdyMiddleware.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/spdy/SpdyMiddleware.java @@ -16,6 +16,7 @@ import com.koushikdutta.async.http.AsyncSSLSocketMiddleware; import com.koushikdutta.async.http.Headers; import com.koushikdutta.async.http.Multimap; import com.koushikdutta.async.http.Protocol; +import com.koushikdutta.async.http.body.AsyncHttpRequestBody; import com.koushikdutta.async.util.Charsets; import java.lang.reflect.Field; @@ -39,6 +40,94 @@ public class SpdyMiddleware extends AsyncSSLSocketMiddleware { }); } + private void configure(SSLEngine engine, String host, int port) { + if (!initialized) { + initialized = true; + try { + peerHost = engine.getClass().getSuperclass().getDeclaredField("peerHost"); + peerPort = engine.getClass().getSuperclass().getDeclaredField("peerPort"); + sslParameters = engine.getClass().getDeclaredField("sslParameters"); + npnProtocols = sslParameters.getType().getDeclaredField("npnProtocols"); + alpnProtocols = sslParameters.getType().getDeclaredField("alpnProtocols"); + useSni = sslParameters.getType().getDeclaredField("useSni"); + sslNativePointer = engine.getClass().getDeclaredField("sslNativePointer"); + String nativeCryptoName = sslParameters.getType().getPackage().getName() + ".NativeCrypto"; + nativeGetNpnNegotiatedProtocol = Class.forName(nativeCryptoName, true, sslParameters.getType().getClassLoader()) + .getDeclaredMethod("SSL_get_npn_negotiated_protocol", long.class); + nativeGetAlpnNegotiatedProtocol = Class.forName(nativeCryptoName, true, sslParameters.getType().getClassLoader()) + .getDeclaredMethod("SSL_get0_alpn_selected", long.class); + + peerHost.setAccessible(true); + peerPort.setAccessible(true); + sslParameters.setAccessible(true); + npnProtocols.setAccessible(true); + alpnProtocols.setAccessible(true); + useSni.setAccessible(true); + sslNativePointer.setAccessible(true); + nativeGetNpnNegotiatedProtocol.setAccessible(true); + nativeGetAlpnNegotiatedProtocol.setAccessible(true); + } + catch (Exception e) { + sslParameters = null; + npnProtocols = null; + alpnProtocols = null; + useSni = null; + sslNativePointer = null; + nativeGetNpnNegotiatedProtocol = null; + nativeGetAlpnNegotiatedProtocol = null; + } + } + + if (sslParameters != null) { + try { + byte[] protocols = concatLengthPrefixed( + Protocol.HTTP_1_1, + Protocol.SPDY_3 + ); + + peerHost.set(engine, host); + peerPort.set(engine, port); + Object sslp = sslParameters.get(engine); +// npnProtocols.set(sslp, protocols); + alpnProtocols.set(sslp, protocols); + useSni.set(sslp, true); + } + catch (Exception e ) { + e.printStackTrace(); + } + } + } + + @Override + protected SSLEngine createConfiguredSSLEngine(String host, int port) { + SSLContext sslContext = getSSLContext(); + SSLEngine sslEngine = sslContext.createSSLEngine(); + + for (AsyncSSLEngineConfigurator configurator : engineConfigurators) { + configurator.configureEngine(sslEngine, host, port); + } + + return sslEngine; + } + + boolean initialized; + Field peerHost; + Field peerPort; + Field sslParameters; + Field npnProtocols; + Field alpnProtocols; + Field sslNativePointer; + Field useSni; + Method nativeGetNpnNegotiatedProtocol; + Method nativeGetAlpnNegotiatedProtocol; + Hashtable<String, AsyncSpdyConnection> connections = new Hashtable<String, AsyncSpdyConnection>(); + + @Override + public void setSSLContext(SSLContext sslContext) { + super.setSSLContext(sslContext); + initialized = false; + } + static byte[] concatLengthPrefixed(Protocol... protocols) { ByteBuffer result = ByteBuffer.allocate(8192); for (Protocol protocol: protocols) { @@ -80,6 +169,7 @@ public class SpdyMiddleware extends AsyncSSLSocketMiddleware { callback.onConnectCompleted(null, socket); return; } + data.protocol = protoString; final AsyncSpdyConnection connection = new AsyncSpdyConnection(socket, Protocol.get(protoString)); connection.sendConnectionPreface(); @@ -120,15 +210,44 @@ public class SpdyMiddleware extends AsyncSSLSocketMiddleware { } } - AsyncSpdyConnection.SpdySocket spdy = connection.newStream(headers, false, true); + data.request.logv("\n" + data.request); + AsyncSpdyConnection.SpdySocket spdy = connection.newStream(headers, data.request.getBody() != null, true); callback.onConnectCompleted(null, spdy); } @Override - public boolean exchangeHeaders(final ExchangeHeaderData data) { + public Cancellable getSocket(GetSocketData data) { + final Uri uri = data.request.getUri(); + final int port = getSchemePort(data.request.getUri()); + if (port == -1) { + return null; + } + + // can we use an existing connection to satisfy this, or do we need a new one? + String host = uri.getHost(); + AsyncSpdyConnection conn = connections.get(host); + if (conn == null || !conn.socket.isOpen()) { + connections.remove(host); + return super.getSocket(data); + } + + newSocket(data, conn, data.connectCallback); + + SimpleCancellable ret = new SimpleCancellable(); + ret.setComplete(); + return ret; + } + + @Override + public boolean exchangeHeaders(final OnExchangeHeaderData data) { if (!(data.socket instanceof AsyncSpdyConnection.SpdySocket)) return false; + AsyncHttpRequestBody requestBody = data.request.getBody(); + if (requestBody != null) { + data.response.sink(data.socket); + } + // headers were already sent as part of the socket being opened. data.sendHeadersCallback.onCompleted(null); @@ -162,114 +281,12 @@ public class SpdyMiddleware extends AsyncSSLSocketMiddleware { return true; } - private void configure(SSLEngine engine, String host, int port) { - if (!initialized) { - initialized = true; - try { - peerHost = engine.getClass().getSuperclass().getDeclaredField("peerHost"); - peerPort = engine.getClass().getSuperclass().getDeclaredField("peerPort"); - sslParameters = engine.getClass().getDeclaredField("sslParameters"); - npnProtocols = sslParameters.getType().getDeclaredField("npnProtocols"); - alpnProtocols = sslParameters.getType().getDeclaredField("alpnProtocols"); - useSni = sslParameters.getType().getDeclaredField("useSni"); - sslNativePointer = engine.getClass().getDeclaredField("sslNativePointer"); - String nativeCryptoName = sslParameters.getType().getPackage().getName() + ".NativeCrypto"; - nativeGetNpnNegotiatedProtocol = Class.forName(nativeCryptoName, true, sslParameters.getType().getClassLoader()) - .getDeclaredMethod("SSL_get_npn_negotiated_protocol", long.class); - nativeGetAlpnNegotiatedProtocol = Class.forName(nativeCryptoName, true, sslParameters.getType().getClassLoader()) - .getDeclaredMethod("SSL_get0_alpn_selected", long.class); - - peerHost.setAccessible(true); - peerPort.setAccessible(true); - sslParameters.setAccessible(true); - npnProtocols.setAccessible(true); - alpnProtocols.setAccessible(true); - useSni.setAccessible(true); - sslNativePointer.setAccessible(true); - nativeGetNpnNegotiatedProtocol.setAccessible(true); - nativeGetAlpnNegotiatedProtocol.setAccessible(true); - } - catch (Exception e) { - sslParameters = null; - npnProtocols = null; - alpnProtocols = null; - useSni = null; - sslNativePointer = null; - nativeGetNpnNegotiatedProtocol = null; - nativeGetAlpnNegotiatedProtocol = null; - } - } - - if (sslParameters != null) { - try { - byte[] protocols = concatLengthPrefixed( - Protocol.HTTP_1_1, - Protocol.SPDY_3 - ); - - peerHost.set(engine, host); - peerPort.set(engine, port); - Object sslp = sslParameters.get(engine); -// npnProtocols.set(sslp, protocols); - alpnProtocols.set(sslp, protocols); - useSni.set(sslp, true); - } - catch (Exception e ) { - e.printStackTrace(); - } - } - } - @Override - protected SSLEngine createConfiguredSSLEngine(String host, int port) { - SSLContext sslContext = getSSLContext(); - SSLEngine sslEngine = sslContext.createSSLEngine(); - - for (AsyncSSLEngineConfigurator configurator : engineConfigurators) { - configurator.configureEngine(sslEngine, host, port); - } - - return sslEngine; - } - - boolean initialized; - Field peerHost; - Field peerPort; - Field sslParameters; - Field npnProtocols; - Field alpnProtocols; - Field sslNativePointer; - Field useSni; - Method nativeGetNpnNegotiatedProtocol; - Method nativeGetAlpnNegotiatedProtocol; - Hashtable<String, AsyncSpdyConnection> connections = new Hashtable<String, AsyncSpdyConnection>(); - - @Override - public void setSSLContext(SSLContext sslContext) { - super.setSSLContext(sslContext); - initialized = false; - } - - @Override - public Cancellable getSocket(GetSocketData data) { - final Uri uri = data.request.getUri(); - final int port = getSchemePort(data.request.getUri()); - if (port == -1) { - return null; - } - - // can we use an existing connection to satisfy this, or do we need a new one? - String host = uri.getHost(); - AsyncSpdyConnection conn = connections.get(host); - if (conn == null || !conn.socket.isOpen()) { - connections.remove(host); - return super.getSocket(data); - } - - newSocket(data, conn, data.connectCallback); + public void onRequestSent(OnRequestSentData data) { + if (!(data.socket instanceof AsyncSpdyConnection.SpdySocket)) + return; - SimpleCancellable ret = new SimpleCancellable(); - ret.setComplete(); - return ret; + if (data.request.getBody() != null) + data.response.sink().end(); } }
\ No newline at end of file |