Search in sources :

Example 1 with SimpleCancellable

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;
    }
}
Also used : IOException(java.io.IOException) FileInputStream(java.io.FileInputStream) IOException(java.io.IOException) CertificateException(java.security.cert.CertificateException) CertificateEncodingException(java.security.cert.CertificateEncodingException) SimpleCancellable(com.koushikdutta.async.future.SimpleCancellable) ByteBufferList(com.koushikdutta.async.ByteBufferList) List(java.util.List)

Example 2 with SimpleCancellable

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();
        }
    });
}
Also used : Continuation(com.koushikdutta.async.future.Continuation) CompletedCallback(com.koushikdutta.async.callback.CompletedCallback) InetSocketAddress(java.net.InetSocketAddress) Uri(android.net.Uri) AsyncSocket(com.koushikdutta.async.AsyncSocket) ContinuationCallback(com.koushikdutta.async.callback.ContinuationCallback) SimpleCancellable(com.koushikdutta.async.future.SimpleCancellable) ConnectCallback(com.koushikdutta.async.callback.ConnectCallback) InetAddress(java.net.InetAddress)

Example 3 with SimpleCancellable

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;
}
Also used : SimpleCancellable(com.koushikdutta.async.future.SimpleCancellable) Cancellable(com.koushikdutta.async.future.Cancellable) SimpleCancellable(com.koushikdutta.async.future.SimpleCancellable) Uri(android.net.Uri) IOException(java.io.IOException)

Example 4 with SimpleCancellable

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);
    }
}
Also used : Cancellable(com.koushikdutta.async.future.Cancellable) SimpleCancellable(com.koushikdutta.async.future.SimpleCancellable) SimpleCancellable(com.koushikdutta.async.future.SimpleCancellable) Uri(android.net.Uri)

Aggregations

SimpleCancellable (com.koushikdutta.async.future.SimpleCancellable)4 Uri (android.net.Uri)3 Cancellable (com.koushikdutta.async.future.Cancellable)2 IOException (java.io.IOException)2 AsyncSocket (com.koushikdutta.async.AsyncSocket)1 ByteBufferList (com.koushikdutta.async.ByteBufferList)1 CompletedCallback (com.koushikdutta.async.callback.CompletedCallback)1 ConnectCallback (com.koushikdutta.async.callback.ConnectCallback)1 ContinuationCallback (com.koushikdutta.async.callback.ContinuationCallback)1 Continuation (com.koushikdutta.async.future.Continuation)1 FileInputStream (java.io.FileInputStream)1 InetAddress (java.net.InetAddress)1 InetSocketAddress (java.net.InetSocketAddress)1 CertificateEncodingException (java.security.cert.CertificateEncodingException)1 CertificateException (java.security.cert.CertificateException)1 List (java.util.List)1