use of com.koushikdutta.async.future.SimpleCancellable in project AndroidAsync by koush.
the class ResponseCacheMiddleware method getSocket.
// step 1) see if we can serve request from the cache directly.
// also see if this can be turned into a conditional cache request.
@Override
public Cancellable getSocket(final GetSocketData data) {
RequestHeaders requestHeaders = new RequestHeaders(data.request.getUri(), RawHeaders.fromMultimap(data.request.getHeaders().getMultiMap()));
data.state.put("request-headers", requestHeaders);
if (cache == null || !caching || requestHeaders.isNoCache()) {
networkCount++;
return null;
}
String key = FileCache.toKeyString(data.request.getUri());
FileInputStream[] snapshot = null;
long contentLength;
Entry entry;
try {
snapshot = cache.get(key, ENTRY_COUNT);
if (snapshot == null) {
networkCount++;
return null;
}
contentLength = snapshot[ENTRY_BODY].available();
entry = new Entry(snapshot[ENTRY_METADATA]);
} catch (IOException e) {
// Give up because the cache cannot be read.
networkCount++;
StreamUtility.closeQuietly(snapshot);
return null;
}
// verify the entry matches
if (!entry.matches(data.request.getUri(), data.request.getMethod(), data.request.getHeaders().getMultiMap())) {
networkCount++;
StreamUtility.closeQuietly(snapshot);
return null;
}
EntryCacheResponse candidate = new EntryCacheResponse(entry, snapshot[ENTRY_BODY]);
Map<String, List<String>> responseHeadersMap;
FileInputStream cachedResponseBody;
try {
responseHeadersMap = candidate.getHeaders();
cachedResponseBody = candidate.getBody();
} catch (Exception e) {
networkCount++;
StreamUtility.closeQuietly(snapshot);
return null;
}
if (responseHeadersMap == null || cachedResponseBody == null) {
networkCount++;
StreamUtility.closeQuietly(snapshot);
return null;
}
RawHeaders rawResponseHeaders = RawHeaders.fromMultimap(responseHeadersMap);
ResponseHeaders cachedResponseHeaders = new ResponseHeaders(data.request.getUri(), rawResponseHeaders);
rawResponseHeaders.set("Content-Length", String.valueOf(contentLength));
rawResponseHeaders.removeAll("Content-Encoding");
rawResponseHeaders.removeAll("Transfer-Encoding");
cachedResponseHeaders.setLocalTimestamps(System.currentTimeMillis(), System.currentTimeMillis());
long now = System.currentTimeMillis();
ResponseSource responseSource = cachedResponseHeaders.chooseResponseSource(now, requestHeaders);
if (responseSource == ResponseSource.CACHE) {
data.request.logi("Response retrieved from cache");
final CachedSocket socket = entry.isHttps() ? new CachedSSLSocket(candidate, contentLength) : new CachedSocket(candidate, contentLength);
socket.pending.add(ByteBuffer.wrap(rawResponseHeaders.toHeaderString().getBytes()));
server.post(new Runnable() {
@Override
public void run() {
data.connectCallback.onConnectCompleted(null, socket);
socket.sendCachedDataOnNetworkThread();
}
});
cacheHitCount++;
data.state.put("socket-owner", this);
SimpleCancellable ret = new SimpleCancellable();
ret.setComplete();
return ret;
} else if (responseSource == ResponseSource.CONDITIONAL_CACHE) {
data.request.logi("Response may be served from conditional cache");
CacheData cacheData = new CacheData();
cacheData.snapshot = snapshot;
cacheData.contentLength = contentLength;
cacheData.cachedResponseHeaders = cachedResponseHeaders;
cacheData.candidate = candidate;
data.state.put("cache-data", cacheData);
return null;
} else {
data.request.logd("Response can not be served from cache");
// NETWORK or other
networkCount++;
StreamUtility.closeQuietly(snapshot);
return null;
}
}
use of com.koushikdutta.async.future.SimpleCancellable in project AndroidAsync by koush.
the class AsyncSocketMiddleware method getSocket.
@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;
}
data.state.put("socket-owner", this);
final String lookup = computeLookup(uri, port, data.request.getProxyHost(), data.request.getProxyPort());
ConnectionInfo info = getOrCreateConnectionInfo(lookup);
synchronized (AsyncSocketMiddleware.this) {
if (info.openCount >= maxConnectionCount) {
// wait for a connection queue to free up
SimpleCancellable queueCancel = new SimpleCancellable();
info.queue.add(data);
return queueCancel;
}
info.openCount++;
while (!info.sockets.isEmpty()) {
IdleSocketHolder idleSocketHolder = info.sockets.pop();
final AsyncSocket socket = idleSocketHolder.socket;
if (idleSocketHolder.idleTime + idleTimeoutMs < System.currentTimeMillis()) {
socket.setClosedCallback(null);
socket.close();
continue;
}
if (!socket.isOpen())
continue;
data.request.logd("Reusing keep-alive socket");
data.connectCallback.onConnectCompleted(null, socket);
// just a noop/dummy, as this can't actually be cancelled.
SimpleCancellable ret = new SimpleCancellable();
ret.setComplete();
return ret;
}
}
if (!connectAllAddresses || proxyHost != null || data.request.getProxyHost() != null) {
// just default to connecting to a single address
data.request.logd("Connecting socket");
String unresolvedHost;
int unresolvedPort;
boolean proxied = false;
if (data.request.getProxyHost() == null && proxyHost != null)
data.request.enableProxy(proxyHost, proxyPort);
if (data.request.getProxyHost() != null) {
unresolvedHost = data.request.getProxyHost();
unresolvedPort = data.request.getProxyPort();
proxied = true;
} else {
unresolvedHost = uri.getHost();
unresolvedPort = port;
}
if (proxied) {
data.request.logv("Using proxy: " + unresolvedHost + ":" + unresolvedPort);
}
return mClient.getServer().connectSocket(unresolvedHost, unresolvedPort, wrapCallback(data, uri, port, proxied, data.connectCallback));
}
// try to connect to everything...
data.request.logv("Resolving domain and connecting to all available addresses");
return mClient.getServer().getAllByName(uri.getHost()).then(new TransformFuture<AsyncSocket, InetAddress[]>() {
Exception lastException;
@Override
protected void error(Exception e) {
super.error(e);
wrapCallback(data, uri, port, false, data.connectCallback).onConnectCompleted(e, null);
}
@Override
protected void transform(final InetAddress[] result) throws Exception {
Continuation keepTrying = new Continuation(new CompletedCallback() {
@Override
public void onCompleted(Exception ex) {
// if it completed, that means that the connection failed
if (lastException == null)
lastException = new ConnectionFailedException("Unable to connect to remote address");
if (setComplete(lastException)) {
wrapCallback(data, uri, port, false, data.connectCallback).onConnectCompleted(lastException, null);
}
}
});
for (final InetAddress address : result) {
final String inetSockAddress = String.format(Locale.ENGLISH, "%s:%s", address, port);
keepTrying.add(new ContinuationCallback() {
@Override
public void onContinue(Continuation continuation, final CompletedCallback next) throws Exception {
data.request.logv("attempting connection to " + inetSockAddress);
mClient.getServer().connectSocket(new InetSocketAddress(address, port), wrapCallback(data, uri, port, false, new ConnectCallback() {
@Override
public void onConnectCompleted(Exception ex, AsyncSocket socket) {
if (isDone()) {
lastException = new Exception("internal error during connect to " + inetSockAddress);
next.onCompleted(null);
return;
}
// try the next address
if (ex != null) {
lastException = ex;
next.onCompleted(null);
return;
}
// if the socket is no longer needed, just hang onto it...
if (isDone() || isCancelled()) {
data.request.logd("Recycling extra socket leftover from cancelled operation");
idleSocket(socket);
recycleSocket(socket, data.request);
return;
}
if (setComplete(null, socket)) {
data.connectCallback.onConnectCompleted(null, socket);
}
}
}));
}
});
}
keepTrying.start();
}
});
}
use of com.koushikdutta.async.future.SimpleCancellable in project AndroidAsync by koush.
the class SpdyMiddleware method getSocket.
@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);
// but app engine sends a GO_AWAY if it sees a content-length...
if (!canSpdyRequest(data))
return super.getSocket(data);
// can we use an existing connection to satisfy this, or do we need a new one?
String key = uri.getHost() + port;
SpdyConnectionWaiter conn = connections.get(key);
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) {
// 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());
final SimpleCancellable ret = new SimpleCancellable();
conn.setCallback(new FutureCallback<AsyncSpdyConnection>() {
@Override
public void onCompleted(Exception e, AsyncSpdyConnection conn) {
if (e instanceof NoSpdyException) {
data.request.logv("spdy not available");
ret.setParent(SpdyMiddleware.super.getSocket(data));
return;
}
if (e != null) {
if (ret.setComplete())
data.connectCallback.onConnectCompleted(e, null);
return;
}
data.request.logv("using existing spdy connection for host: " + data.request.getUri().getHost());
if (ret.setComplete())
newSocket(data, conn, data.connectCallback);
}
});
return ret;
}
use of com.koushikdutta.async.future.SimpleCancellable in project AndroidAsync by koush.
the class AsyncSocketMiddleware method nextConnection.
private void nextConnection(AsyncHttpRequest request) {
Uri uri = request.getUri();
final int port = getSchemePort(uri);
String key = computeLookup(uri, port, request.getProxyHost(), request.getProxyPort());
synchronized (AsyncSocketMiddleware.this) {
ConnectionInfo info = connectionInfo.get(key);
if (info == null)
return;
--info.openCount;
while (info.openCount < maxConnectionCount && info.queue.size() > 0) {
GetSocketData gsd = info.queue.remove();
SimpleCancellable socketCancellable = (SimpleCancellable) gsd.socketCancellable;
if (socketCancellable.isCancelled())
continue;
Cancellable connect = getSocket(gsd);
socketCancellable.setParent(connect);
}
maybeCleanupConnectionInfo(key);
}
}
Aggregations