FileDocCategorySizeDatePackage
Connection.javaAPI DocAndroid 5.1 API18019Thu Mar 12 22:22:10 GMT 2015android.net.http

Connection

public abstract class Connection extends Object
{@hide}

Fields Summary
static final int
SOCKET_TIMEOUT
Allow a TCP connection 60 idle seconds before erroring out
private static final int
SEND
private static final int
READ
private static final int
DRAIN
private static final int
DONE
private static final String[]
states
android.content.Context
mContext
protected AndroidHttpClientConnection
mHttpClientConnection
The low level connection
protected SslCertificate
mCertificate
The server SSL certificate associated with this connection (null if the connection is not secure) It would be nice to store the whole certificate chain, but we want to keep things as light-weight as possible
HttpHost
mHost
The host this connection is connected to. If using proxy, this is set to the proxy address
private boolean
mCanPersist
true if the connection can be reused for sending more requests
private HttpContext
mHttpContext
context required by ConnectionReuseStrategy.
private static int
STATE_NORMAL
set when cancelled
private static int
STATE_CANCEL_REQUESTED
private int
mActive
private static final int
RETRY_REQUEST_LIMIT
The number of times to try to re-connect (if connect fails).
private static final int
MIN_PIPE
private static final int
MAX_PIPE
private static final String
HTTP_CONNECTION
Doesn't seem to exist anymore in the new HTTP client, so copied here.
RequestFeeder
mRequestFeeder
private byte[]
mBuf
Buffer for feeding response blocks to webkit. One block per connection reduces memory churn.
Constructors Summary
protected Connection(android.content.Context context, HttpHost host, RequestFeeder requestFeeder)


        
                           
        mContext = context;
        mHost = host;
        mRequestFeeder = requestFeeder;

        mCanPersist = false;
        mHttpContext = new BasicHttpContext(null);
    
Methods Summary
voidcancel()
Close current network connection Note: this runs in non-network thread

        mActive = STATE_CANCEL_REQUESTED;
        closeConnection();
        if (HttpLog.LOGV) HttpLog.v(
            "Connection.cancel(): connection closed " + mHost);
    
private booleanclearPipe(java.util.LinkedList pipe)
After a send/receive failure, any pipelined requests must be cleared back to the mRequest queue

return
true if mRequests is empty after pipe cleared

        boolean empty = true;
        if (HttpLog.LOGV) HttpLog.v(
                "Connection.clearPipe(): clearing pipe " + pipe.size());
        synchronized (mRequestFeeder) {
            Request tReq;
            while (!pipe.isEmpty()) {
                tReq = (Request)pipe.removeLast();
                if (HttpLog.LOGV) HttpLog.v(
                        "clearPipe() adding back " + mHost + " " + tReq);
                mRequestFeeder.requeueRequest(tReq);
                empty = false;
            }
            if (empty) empty = !mRequestFeeder.haveRequest(mHost);
        }
        return empty;
    
abstract voidcloseConnection()

byte[]getBuf()

        if (mBuf == null) mBuf = new byte[8192];
        return mBuf;
    
booleangetCanPersist()

        return mCanPersist;
    
SslCertificategetCertificate()

return
The server SSL certificate associated with this connection (null if the connection is not secure)

        return mCertificate;
    
static android.net.http.ConnectiongetConnection(android.content.Context context, org.apache.http.HttpHost host, org.apache.http.HttpHost proxy, RequestFeeder requestFeeder)
connection factory: returns an HTTP or HTTPS connection as necessary


        if (host.getSchemeName().equals("http")) {
            return new HttpConnection(context, host, requestFeeder);
        }

        // Otherwise, default to https
        return new HttpsConnection(context, host, proxy, requestFeeder);
    
org.apache.http.HttpHostgetHost()

        return mHost;
    
org.apache.http.protocol.HttpContextgetHttpContext()

        return mHttpContext;
    
abstract java.lang.StringgetScheme()
typically http or https... set by subclass

private booleanhttpFailure(Request req, int errorId, java.lang.Exception e)
Helper. Calls the mEventHandler's error() method only if request failed permanently. Increments mFailcount on failure. Increments failcount only if the network is believed to be connected

return
true if request can be retried (less than RETRY_REQUEST_LIMIT failures have occurred).

        boolean ret = true;

        // e.printStackTrace();
        if (HttpLog.LOGV) HttpLog.v(
                "httpFailure() ******* " + e + " count " + req.mFailCount +
                " " + mHost + " " + req.getUri());

        if (++req.mFailCount >= RETRY_REQUEST_LIMIT) {
            ret = false;
            String error;
            if (errorId < 0) {
                error = ErrorStrings.getString(errorId, mContext);
            } else {
                Throwable cause = e.getCause();
                error = cause != null ? cause.toString() : e.getMessage();
            }
            req.mEventHandler.error(errorId, error);
            req.complete();
        }

        closeConnection();
        mHttpContext.removeAttribute(HTTP_CONNECTION);

        return ret;
    
private booleankeepAlive(org.apache.http.HttpEntity entity, org.apache.http.ProtocolVersion ver, int connType, org.apache.http.protocol.HttpContext context)
Use same logic as ConnectionReuseStrategy

see
ConnectionReuseStrategy

        org.apache.http.HttpConnection conn = (org.apache.http.HttpConnection)
            context.getAttribute(ExecutionContext.HTTP_CONNECTION);

        if (conn != null && !conn.isOpen())
            return false;
        // do NOT check for stale connection, that is an expensive operation

        if (entity != null) {
            if (entity.getContentLength() < 0) {
                if (!entity.isChunked() || ver.lessEquals(HttpVersion.HTTP_1_0)) {
                    // if the content length is not known and is not chunk
                    // encoded, the connection cannot be reused
                    return false;
                }
            }
        }
        // Check for 'Connection' directive
        if (connType == Headers.CONN_CLOSE) {
            return false;
        } else if (connType == Headers.CONN_KEEP_ALIVE) {
            return true;
        }
        // Resorting to protocol version default close connection policy
        return !ver.lessEquals(HttpVersion.HTTP_1_0);
    
abstract AndroidHttpClientConnectionopenConnection(Request req)

private booleanopenHttpConnection(Request req)

return
true on success


        long now = SystemClock.uptimeMillis();
        int error = EventHandler.OK;
        Exception exception = null;

        try {
            // reset the certificate to null before opening a connection
            mCertificate = null;
            mHttpClientConnection = openConnection(req);
            if (mHttpClientConnection != null) {
                mHttpClientConnection.setSocketTimeout(SOCKET_TIMEOUT);
                mHttpContext.setAttribute(HTTP_CONNECTION,
                                          mHttpClientConnection);
            } else {
                // we tried to do SSL tunneling, failed,
                // and need to drop the request;
                // we have already informed the handler
                req.mFailCount = RETRY_REQUEST_LIMIT;
                return false;
            }
        } catch (UnknownHostException e) {
            if (HttpLog.LOGV) HttpLog.v("Failed to open connection");
            error = EventHandler.ERROR_LOOKUP;
            exception = e;
        } catch (IllegalArgumentException e) {
            if (HttpLog.LOGV) HttpLog.v("Illegal argument exception");
            error = EventHandler.ERROR_CONNECT;
            req.mFailCount = RETRY_REQUEST_LIMIT;
            exception = e;
        } catch (SSLConnectionClosedByUserException e) {
            // hack: if we have an SSL connection failure,
            // we don't want to reconnect
            req.mFailCount = RETRY_REQUEST_LIMIT;
            // no error message
            return false;
        } catch (SSLHandshakeException e) {
            // hack: if we have an SSL connection failure,
            // we don't want to reconnect
            req.mFailCount = RETRY_REQUEST_LIMIT;
            if (HttpLog.LOGV) HttpLog.v(
                    "SSL exception performing handshake");
            error = EventHandler.ERROR_FAILED_SSL_HANDSHAKE;
            exception = e;
        } catch (IOException e) {
            error = EventHandler.ERROR_CONNECT;
            exception = e;
        }

        if (HttpLog.LOGV) {
            long now2 = SystemClock.uptimeMillis();
            HttpLog.v("Connection.openHttpConnection() " +
                      (now2 - now) + " " + mHost);
        }

        if (error == EventHandler.OK) {
            return true;
        } else {
            if (req.mFailCount < RETRY_REQUEST_LIMIT) {
                // requeue
                mRequestFeeder.requeueRequest(req);
                req.mFailCount++;
            } else {
                httpFailure(req, error, exception);
            }
            return error == EventHandler.OK;
        }
    
voidprocessRequests(Request firstRequest)
Process requests in queue pipelines requests

        Request req = null;
        boolean empty;
        int error = EventHandler.OK;
        Exception exception = null;

        LinkedList<Request> pipe = new LinkedList<Request>();

        int minPipe = MIN_PIPE, maxPipe = MAX_PIPE;
        int state = SEND;

        while (state != DONE) {
            if (HttpLog.LOGV) HttpLog.v(
                    states[state] + " pipe " + pipe.size());

            /* If a request was cancelled, give other cancel requests
               some time to go through so we don't uselessly restart
               connections */
            if (mActive == STATE_CANCEL_REQUESTED) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException x) { /* ignore */ }
                mActive = STATE_NORMAL;
            }

            switch (state) {
                case SEND: {
                    if (pipe.size() == maxPipe) {
                        state = READ;
                        break;
                    }
                    /* get a request */
                    if (firstRequest == null) {
                        req = mRequestFeeder.getRequest(mHost);
                    } else {
                        req = firstRequest;
                        firstRequest = null;
                    }
                    if (req == null) {
                        state = DRAIN;
                        break;
                    }
                    req.setConnection(this);

                    /* Don't work on cancelled requests. */
                    if (req.mCancelled) {
                        if (HttpLog.LOGV) HttpLog.v(
                                "processRequests(): skipping cancelled request "
                                + req);
                        req.complete();
                        break;
                    }

                    if (mHttpClientConnection == null ||
                        !mHttpClientConnection.isOpen()) {
                        /* If this call fails, the address is bad or
                           the net is down.  Punt for now.

                           FIXME: blow out entire queue here on
                           connection failure if net up? */

                        if (!openHttpConnection(req)) {
                            state = DONE;
                            break;
                        }
                    }

                    /* we have a connection, let the event handler
                     * know of any associated certificate,
                     * potentially none.
                     */
                    req.mEventHandler.certificate(mCertificate);

                    try {
                        /* FIXME: don't increment failure count if old
                           connection?  There should not be a penalty for
                           attempting to reuse an old connection */
                        req.sendRequest(mHttpClientConnection);
                    } catch (HttpException e) {
                        exception = e;
                        error = EventHandler.ERROR;
                    } catch (IOException e) {
                        exception = e;
                        error = EventHandler.ERROR_IO;
                    } catch (IllegalStateException e) {
                        exception = e;
                        error = EventHandler.ERROR_IO;
                    }
                    if (exception != null) {
                        if (httpFailure(req, error, exception) &&
                            !req.mCancelled) {
                            /* retry request if not permanent failure
                               or cancelled */
                            pipe.addLast(req);
                        }
                        exception = null;
                        state = clearPipe(pipe) ? DONE : SEND;
                        minPipe = maxPipe = 1;
                        break;
                    }

                    pipe.addLast(req);
                    if (!mCanPersist) state = READ;
                    break;

                }
                case DRAIN:
                case READ: {
                    empty = !mRequestFeeder.haveRequest(mHost);
                    int pipeSize = pipe.size();
                    if (state != DRAIN && pipeSize < minPipe &&
                        !empty && mCanPersist) {
                        state = SEND;
                        break;
                    } else if (pipeSize == 0) {
                        /* Done if no other work to do */
                        state = empty ? DONE : SEND;
                        break;
                    }

                    req = (Request)pipe.removeFirst();
                    if (HttpLog.LOGV) HttpLog.v(
                            "processRequests() reading " + req);

                    try {
                        req.readResponse(mHttpClientConnection);
                    } catch (ParseException e) {
                        exception = e;
                        error = EventHandler.ERROR_IO;
                    } catch (IOException e) {
                        exception = e;
                        error = EventHandler.ERROR_IO;
                    } catch (IllegalStateException e) {
                        exception = e;
                        error = EventHandler.ERROR_IO;
                    }
                    if (exception != null) {
                        if (httpFailure(req, error, exception) &&
                            !req.mCancelled) {
                            /* retry request if not permanent failure
                               or cancelled */
                            req.reset();
                            pipe.addFirst(req);
                        }
                        exception = null;
                        mCanPersist = false;
                    }
                    if (!mCanPersist) {
                        if (HttpLog.LOGV) HttpLog.v(
                                "processRequests(): no persist, closing " +
                                mHost);

                        closeConnection();

                        mHttpContext.removeAttribute(HTTP_CONNECTION);
                        clearPipe(pipe);
                        minPipe = maxPipe = 1;
                        state = SEND;
                    }
                    break;
                }
            }
        }
    
voidsetCanPersist(org.apache.http.HttpEntity entity, org.apache.http.ProtocolVersion ver, int connType)

        mCanPersist = keepAlive(entity, ver, connType, mHttpContext);
    
voidsetCanPersist(boolean canPersist)

        mCanPersist = canPersist;
    
public synchronized java.lang.StringtoString()
Prints request queue to log, for debugging. returns request count

        return mHost.toString();