package com.koushikdutta.async.http; import java.net.URI; import java.util.HashSet; import java.util.Hashtable; import android.util.Log; import com.koushikdutta.async.AsyncSocket; import com.koushikdutta.async.callback.CompletedCallback; import com.koushikdutta.async.callback.ConnectCallback; import com.koushikdutta.async.future.Cancellable; import com.koushikdutta.async.future.SimpleCancelable; public class AsyncSocketMiddleware extends SimpleMiddleware { String scheme; int port; public AsyncSocketMiddleware(AsyncHttpClient client, String scheme, int port) { mClient = client; this.scheme = scheme; this.port = port; } public int getSchemePort(URI uri) { if (!uri.getScheme().equals(scheme)) return -1; if (uri.getPort() == -1) { return port; } else { return uri.getPort(); } } public AsyncSocketMiddleware(AsyncHttpClient client) { this(client, "http", 80); } AsyncHttpClient mClient; private Hashtable<String, HashSet<AsyncSocket>> mSockets = new Hashtable<String, HashSet<AsyncSocket>>(); protected ConnectCallback wrapCallback(ConnectCallback callback, URI uri, int port) { return callback; } @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; } final String lookup = uri.getScheme() + "//" + uri.getHost() + ":" + port; data.state.putBoolean(getClass().getCanonicalName() + ".owned", true); HashSet<AsyncSocket> sockets = mSockets.get(lookup); if (sockets != null) { synchronized (sockets) { for (final AsyncSocket socket: sockets) { if (socket.isOpen()) { sockets.remove(socket); socket.setClosedCallback(null); mClient.getServer().post(new Runnable() { @Override public void run() { // Log.i("AsyncHttpSocket", "Reusing keep-alive socket"); data.connectCallback.onConnectCompleted(null, socket); } }); // just a noop/dummy, as this can't actually be cancelled. return new SimpleCancelable(); } } } } return mClient.getServer().connectSocket(uri.getHost(), port, wrapCallback(data.connectCallback, uri, port)); } @Override public void onRequestComplete(final OnRequestCompleteData data) { if (!data.state.getBoolean(getClass().getCanonicalName() + ".owned", false)) { Log.i("AsyncHttpSocket", getClass().getCanonicalName() + " Not keeping non-owned socket: " + data.state.getString("socket.owner")); return; } if (data.exception != null || !data.socket.isOpen()) { data.socket.close(); return; } String kas = data.headers.getConnection(); if (kas == null || !"keep-alive".toLowerCase().equals(kas.toLowerCase())) { data.socket.close(); return; } // Log.i("AsynchttpSocket", "Recycling keep-alive socket"); final URI uri = data.request.getUri(); final int port = getSchemePort(data.request.getUri()); final String lookup = uri.getScheme() + "//" + uri.getHost() + ":" + port; HashSet<AsyncSocket> sockets = mSockets.get(lookup); if (sockets == null) { sockets = new HashSet<AsyncSocket>(); mSockets.put(lookup, sockets); } final HashSet<AsyncSocket> ss = sockets; synchronized (sockets) { sockets.add(data.socket); data.socket.setClosedCallback(new CompletedCallback() { @Override public void onCompleted(Exception ex) { synchronized (ss) { ss.remove(data.socket); } data.socket.setClosedCallback(null); } }); } } }