Search in sources :

Example 1 with AsyncSocket

use of com.koushikdutta.async.AsyncSocket 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 2 with AsyncSocket

use of com.koushikdutta.async.AsyncSocket in project AndroidAsync by koush.

the class AsyncHttpClient method executeSocket.

private void executeSocket(final AsyncHttpRequest request, final int redirectCount, final FutureAsyncHttpResponse cancel, final HttpConnectCallback callback, 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;
            // 5) after request is sent, set a header timeout
            if (cancel.timeoutRunnable != null && mHeaders == null) {
                mServer.removeAllCallbacks(cancel.scheduled);
                cancel.scheduled = mServer.postDelayed(cancel.timeoutRunnable, getTimeoutRemaining(request));
            }
            for (AsyncHttpClientMiddleware middleware : mMiddleware) {
                middleware.onRequestSent(data);
            }
        }

        @Override
        public void setDataEmitter(DataEmitter emitter) {
            data.bodyEmitter = emitter;
            for (AsyncHttpClientMiddleware middleware : mMiddleware) {
                middleware.onBodyDecoder(data);
            }
            super.setDataEmitter(data.bodyEmitter);
            Headers headers = mHeaders;
            int responseCode = code();
            if ((responseCode == HttpURLConnection.HTTP_MOVED_PERM || responseCode == HttpURLConnection.HTTP_MOVED_TEMP || responseCode == 307) && request.getFollowRedirect()) {
                String location = headers.get("Location");
                Uri redirect;
                try {
                    redirect = Uri.parse(location);
                    if (redirect.getScheme() == null) {
                        redirect = Uri.parse(new URL(new URL(request.getUri().toString()), location).toString());
                    }
                } catch (Exception e) {
                    reportConnectedCompleted(cancel, e, this, request, callback);
                    return;
                }
                final String method = request.getMethod().equals(AsyncHttpHead.METHOD) ? AsyncHttpHead.METHOD : AsyncHttpGet.METHOD;
                AsyncHttpRequest newReq = new AsyncHttpRequest(redirect, method);
                newReq.executionTime = request.executionTime;
                newReq.logLevel = request.logLevel;
                newReq.LOGTAG = request.LOGTAG;
                newReq.proxyHost = request.proxyHost;
                newReq.proxyPort = request.proxyPort;
                setupAndroidProxy(newReq);
                copyHeader(request, newReq, "User-Agent");
                copyHeader(request, newReq, "Range");
                request.logi("Redirecting");
                newReq.logi("Redirected");
                execute(newReq, redirectCount + 1, cancel, callback);
                setDataCallback(new NullDataCallback());
                return;
            }
            request.logv("Final (post cache response) headers:\n" + toString());
            // at this point the headers are done being modified
            reportConnectedCompleted(cancel, null, this, request, callback);
        }

        protected void onHeadersReceived() {
            super.onHeadersReceived();
            if (cancel.isCancelled())
                return;
            // 7) on headers, cancel timeout
            if (cancel.timeoutRunnable != null)
                mServer.removeAllCallbacks(cancel.scheduled);
            // allow the middleware to massage the headers before the body is decoded
            request.logv("Received headers:\n" + toString());
            for (AsyncHttpClientMiddleware middleware : mMiddleware) {
                middleware.onHeadersReceived(data);
            }
        // drop through, and setDataEmitter will be called for the body decoder.
        // headers will be further massaged in there.
        }

        @Override
        protected void report(Exception ex) {
            if (ex != null)
                request.loge("exception during response", ex);
            if (cancel.isCancelled())
                return;
            if (ex instanceof AsyncSSLException) {
                request.loge("SSL Exception", ex);
                AsyncSSLException ase = (AsyncSSLException) ex;
                request.onHandshakeException(ase);
                if (ase.getIgnore())
                    return;
            }
            final AsyncSocket socket = socket();
            if (socket == null)
                return;
            super.report(ex);
            if (!socket.isOpen() || ex != null) {
                if (headers() == null && ex != null)
                    reportConnectedCompleted(cancel, ex, null, request, callback);
            }
            data.exception = ex;
            for (AsyncHttpClientMiddleware middleware : mMiddleware) {
                middleware.onResponseComplete(data);
            }
        }

        @Override
        public AsyncSocket detachSocket() {
            request.logd("Detaching socket");
            AsyncSocket socket = socket();
            if (socket == null)
                return null;
            socket.setWriteableCallback(null);
            socket.setClosedCallback(null);
            socket.setEndCallback(null);
            socket.setDataCallback(null);
            setSocket(null);
            return socket;
        }
    };
    data.sendHeadersCallback = new CompletedCallback() {

        @Override
        public void onCompleted(Exception ex) {
            if (ex != null)
                ret.report(ex);
            else
                ret.onHeadersSent();
        }
    };
    data.receiveHeadersCallback = new CompletedCallback() {

        @Override
        public void onCompleted(Exception ex) {
            if (ex != null)
                ret.report(ex);
            else
                ret.onHeadersReceived();
        }
    };
    data.response = ret;
    ret.setSocket(data.socket);
    for (AsyncHttpClientMiddleware middleware : mMiddleware) {
        if (middleware.exchangeHeaders(data))
            break;
    }
}
Also used : CompletedCallback(com.koushikdutta.async.callback.CompletedCallback) Uri(android.net.Uri) TimeoutException(java.util.concurrent.TimeoutException) AsyncSSLException(com.koushikdutta.async.AsyncSSLException) IOException(java.io.IOException) FileNotFoundException(java.io.FileNotFoundException) SuppressLint(android.annotation.SuppressLint) URL(java.net.URL) AsyncSocket(com.koushikdutta.async.AsyncSocket) DataEmitter(com.koushikdutta.async.DataEmitter) AsyncSSLException(com.koushikdutta.async.AsyncSSLException)

Example 3 with AsyncSocket

use of com.koushikdutta.async.AsyncSocket in project AndroidAsync by koush.

the class AsyncHttpClient method executeAffinity.

private void executeAffinity(final AsyncHttpRequest request, final int redirectCount, final FutureAsyncHttpResponse cancel, final HttpConnectCallback callback) {
    assert mServer.isAffinityThread();
    if (redirectCount > 15) {
        reportConnectedCompleted(cancel, new RedirectLimitExceededException("too many redirects"), null, request, callback);
        return;
    }
    final Uri uri = request.getUri();
    final AsyncHttpClientMiddleware.OnResponseCompleteDataOnRequestSentData data = new AsyncHttpClientMiddleware.OnResponseCompleteDataOnRequestSentData();
    request.executionTime = System.currentTimeMillis();
    data.request = request;
    request.logd("Executing request.");
    for (AsyncHttpClientMiddleware middleware : mMiddleware) {
        middleware.onRequest(data);
    }
    if (request.getTimeout() > 0) {
        // set connect timeout
        cancel.timeoutRunnable = new Runnable() {

            @Override
            public void run() {
                // we've timed out, kill the connections
                if (data.socketCancellable != null) {
                    data.socketCancellable.cancel();
                    if (data.socket != null)
                        data.socket.close();
                }
                reportConnectedCompleted(cancel, new TimeoutException(), null, request, callback);
            }
        };
        cancel.scheduled = mServer.postDelayed(cancel.timeoutRunnable, getTimeoutRemaining(request));
    }
    // 2) wait for a connect
    data.connectCallback = new ConnectCallback() {

        boolean reported;

        @Override
        public void onConnectCompleted(Exception ex, AsyncSocket socket) {
            if (reported) {
                if (socket != null) {
                    socket.setDataCallback(new DataCallback.NullDataCallback());
                    socket.setEndCallback(new CompletedCallback.NullCompletedCallback());
                    socket.close();
                    throw new AssertionError("double connect callback");
                }
            }
            reported = true;
            request.logv("socket connected");
            if (cancel.isCancelled()) {
                if (socket != null)
                    socket.close();
                return;
            }
            // 3) on connect, cancel timeout
            if (cancel.timeoutRunnable != null)
                mServer.removeAllCallbacks(cancel.scheduled);
            if (ex != null) {
                reportConnectedCompleted(cancel, ex, null, request, callback);
                return;
            }
            data.socket = socket;
            cancel.socket = socket;
            executeSocket(request, redirectCount, cancel, callback, data);
        }
    };
    // 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());
    }
    final Exception unsupportedURI;
    for (AsyncHttpClientMiddleware middleware : mMiddleware) {
        Cancellable socketCancellable = middleware.getSocket(data);
        if (socketCancellable != null) {
            data.socketCancellable = socketCancellable;
            cancel.setParent(socketCancellable);
            return;
        }
    }
    unsupportedURI = new IllegalArgumentException("invalid uri=" + request.getUri() + " middlewares=" + mMiddleware);
    reportConnectedCompleted(cancel, unsupportedURI, null, request, callback);
}
Also used : Cancellable(com.koushikdutta.async.future.Cancellable) Uri(android.net.Uri) TimeoutException(java.util.concurrent.TimeoutException) AsyncSSLException(com.koushikdutta.async.AsyncSSLException) IOException(java.io.IOException) FileNotFoundException(java.io.FileNotFoundException) AsyncSocket(com.koushikdutta.async.AsyncSocket) HttpConnectCallback(com.koushikdutta.async.http.callback.HttpConnectCallback) ConnectCallback(com.koushikdutta.async.callback.ConnectCallback) TimeoutException(java.util.concurrent.TimeoutException)

Example 4 with AsyncSocket

use of com.koushikdutta.async.AsyncSocket in project AndroidAsync by koush.

the class AsyncSSLSocketMiddleware method wrapCallback.

@Override
protected ConnectCallback wrapCallback(final GetSocketData data, final Uri uri, final int port, final boolean proxied, final ConnectCallback callback) {
    return new ConnectCallback() {

        @Override
        public void onConnectCompleted(Exception ex, final AsyncSocket socket) {
            if (ex != null) {
                callback.onConnectCompleted(ex, socket);
                return;
            }
            if (!proxied) {
                tryHandshake(socket, data, uri, port, callback);
                return;
            }
            // this SSL connection is proxied, must issue a CONNECT request to the proxy server
            // http://stackoverflow.com/a/6594880/704837
            // some proxies also require 'Host' header, it should be safe to provide it every time
            String connect = String.format(Locale.ENGLISH, "CONNECT %s:%s HTTP/1.1\r\nHost: %s\r\n\r\n", uri.getHost(), port, uri.getHost());
            data.request.logv("Proxying: " + connect);
            Util.writeAll(socket, connect.getBytes(), new CompletedCallback() {

                @Override
                public void onCompleted(Exception ex) {
                    if (ex != null) {
                        callback.onConnectCompleted(ex, socket);
                        return;
                    }
                    LineEmitter liner = new LineEmitter();
                    liner.setLineCallback(new LineEmitter.StringCallback() {

                        String statusLine;

                        @Override
                        public void onStringAvailable(String s) {
                            data.request.logv(s);
                            if (statusLine == null) {
                                statusLine = s.trim();
                                if (!statusLine.matches("HTTP/1.\\d 2\\d\\d .*")) {
                                    // connect response is allowed to have any 2xx status code
                                    socket.setDataCallback(null);
                                    socket.setEndCallback(null);
                                    callback.onConnectCompleted(new IOException("non 2xx status line: " + statusLine), socket);
                                }
                            } else if (TextUtils.isEmpty(s.trim())) {
                                // skip all headers, complete handshake once empty line is received
                                socket.setDataCallback(null);
                                socket.setEndCallback(null);
                                tryHandshake(socket, data, uri, port, callback);
                            }
                        }
                    });
                    socket.setDataCallback(liner);
                    socket.setEndCallback(new CompletedCallback() {

                        @Override
                        public void onCompleted(Exception ex) {
                            if (!socket.isOpen() && ex == null)
                                ex = new IOException("socket closed before proxy connect response");
                            callback.onConnectCompleted(ex, socket);
                        }
                    });
                }
            });
        }
    };
}
Also used : AsyncSocket(com.koushikdutta.async.AsyncSocket) CompletedCallback(com.koushikdutta.async.callback.CompletedCallback) LineEmitter(com.koushikdutta.async.LineEmitter) IOException(java.io.IOException) ConnectCallback(com.koushikdutta.async.callback.ConnectCallback) IOException(java.io.IOException)

Example 5 with AsyncSocket

use of com.koushikdutta.async.AsyncSocket in project AndroidAsync by koush.

the class AsyncSocketMiddleware method maybeCleanupConnectionInfo.

private void maybeCleanupConnectionInfo(String lookup) {
    ConnectionInfo info = connectionInfo.get(lookup);
    if (info == null)
        return;
    while (!info.sockets.isEmpty()) {
        IdleSocketHolder idleSocketHolder = info.sockets.peekLast();
        AsyncSocket socket = idleSocketHolder.socket;
        if (idleSocketHolder.idleTime + idleTimeoutMs > System.currentTimeMillis())
            break;
        info.sockets.pop();
        // remove the callback, prevent reentrancy.
        socket.setClosedCallback(null);
        socket.close();
    }
    if (info.openCount == 0 && info.queue.isEmpty() && info.sockets.isEmpty())
        connectionInfo.remove(lookup);
}
Also used : AsyncSocket(com.koushikdutta.async.AsyncSocket)

Aggregations

AsyncSocket (com.koushikdutta.async.AsyncSocket)9 CompletedCallback (com.koushikdutta.async.callback.CompletedCallback)5 ConnectCallback (com.koushikdutta.async.callback.ConnectCallback)5 IOException (java.io.IOException)5 Uri (android.net.Uri)3 AsyncSSLException (com.koushikdutta.async.AsyncSSLException)2 DataEmitter (com.koushikdutta.async.DataEmitter)2 LineEmitter (com.koushikdutta.async.LineEmitter)2 FileNotFoundException (java.io.FileNotFoundException)2 TimeoutException (java.util.concurrent.TimeoutException)2 SuppressLint (android.annotation.SuppressLint)1 AsyncServer (com.koushikdutta.async.AsyncServer)1 AsyncServerSocket (com.koushikdutta.async.AsyncServerSocket)1 BufferedDataSink (com.koushikdutta.async.BufferedDataSink)1 DataSink (com.koushikdutta.async.DataSink)1 ContinuationCallback (com.koushikdutta.async.callback.ContinuationCallback)1 ListenCallback (com.koushikdutta.async.callback.ListenCallback)1 Cancellable (com.koushikdutta.async.future.Cancellable)1 Continuation (com.koushikdutta.async.future.Continuation)1 SimpleCancellable (com.koushikdutta.async.future.SimpleCancellable)1