Fields Summary |
---|
static final int | SOCKET_TIMEOUTAllow 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 | mHttpClientConnectionThe low level connection |
protected SslCertificate | mCertificateThe 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 | mHostThe host this connection is connected to. If using proxy,
this is set to the proxy address |
private boolean | mCanPersisttrue if the connection can be reused for sending more requests |
private HttpContext | mHttpContextcontext required by ConnectionReuseStrategy. |
private static int | STATE_NORMALset when cancelled |
private static int | STATE_CANCEL_REQUESTED |
private int | mActive |
private static final int | RETRY_REQUEST_LIMITThe 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_CONNECTIONDoesn't seem to exist anymore in the new HTTP client, so copied here. |
RequestQueue.ConnectionManager | mConnectionManager |
RequestFeeder | mRequestFeeder |
private byte[] | mBufBuffer for feeding response blocks to webkit. One block per
connection reduces memory churn. |
Methods Summary |
---|
void | cancel()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 boolean | clearPipe(java.util.LinkedList pipe)After a send/receive failure, any pipelined requests must be
cleared back to the mRequest queue
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 void | closeConnection()
|
byte[] | getBuf()
if (mBuf == null) mBuf = new byte[8192];
return mBuf;
|
boolean | getCanPersist()
return mCanPersist;
|
SslCertificate | getCertificate()
return mCertificate;
|
static android.net.http.Connection | getConnection(android.content.Context context, org.apache.http.HttpHost host, RequestQueue.ConnectionManager connectionManager, RequestFeeder requestFeeder)connection factory: returns an HTTP or HTTPS connection as
necessary
if (host.getSchemeName().equals("http")) {
return new HttpConnection(context, host, connectionManager,
requestFeeder);
}
// Otherwise, default to https
return new HttpsConnection(context, host, connectionManager,
requestFeeder);
|
org.apache.http.HttpHost | getHost()
return mHost;
|
org.apache.http.protocol.HttpContext | getHttpContext()
return mHttpContext;
|
abstract java.lang.String | getScheme()typically http or https... set by subclass
|
private boolean | httpFailure(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
boolean ret = true;
boolean networkConnected = mConnectionManager.isNetworkConnected();
// e.printStackTrace();
if (HttpLog.LOGV) HttpLog.v(
"httpFailure() ******* " + e + " count " + req.mFailCount +
" networkConnected " + networkConnected + " " + mHost + " " + req.getUri());
if (networkConnected && ++req.mFailCount >= RETRY_REQUEST_LIMIT) {
ret = false;
String error;
if (errorId < 0) {
error = mContext.getText(
EventHandler.errorStringResources[-errorId]).toString();
} 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 boolean | keepAlive(org.apache.http.HttpEntity entity, org.apache.http.ProtocolVersion ver, int connType, org.apache.http.protocol.HttpContext context)Use same logic as 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 AndroidHttpClientConnection | openConnection(Request req)
|
private boolean | openHttpConnection(Request req)
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 (mConnectionManager.isNetworkConnected() == false ||
req.mFailCount < RETRY_REQUEST_LIMIT) {
// requeue
mRequestFeeder.requeueRequest(req);
req.mFailCount++;
} else {
httpFailure(req, error, exception);
}
return error == EventHandler.OK;
}
|
void | processRequests(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;
}
}
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) ||
!mConnectionManager.isNetworkConnected()) ?
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;
/* If network active continue to service this queue */
state = mConnectionManager.isNetworkConnected() ?
SEND : DONE;
}
break;
}
}
}
|
void | setCanPersist(org.apache.http.HttpEntity entity, org.apache.http.ProtocolVersion ver, int connType)
mCanPersist = keepAlive(entity, ver, connType, mHttpContext);
|
void | setCanPersist(boolean canPersist)
mCanPersist = canPersist;
|
public synchronized java.lang.String | toString()Prints request queue to log, for debugging.
returns request count
return mHost.toString();
|