Requestpublic class Request extends Object Represents an HTTP request for a given host.
{@hide} |
Fields Summary |
---|
EventHandler | mEventHandlerThe eventhandler to call as the request progresses | private Connection | mConnection | BasicHttpRequest | mHttpRequestThe Apache http request | String | mPathThe path component of this request | HttpHost | mHostHost serving this request | HttpHost | mProxyHostSet if I'm using a proxy server | volatile boolean | mCancelledTrue if request has been cancelled | int | mFailCount | private int | mReceivedBytes | private InputStream | mBodyProvider | private int | mBodyLength | private static final String | HOST_HEADER | private static final String | ACCEPT_ENCODING_HEADER | private static final String | CONTENT_LENGTH_HEADER | private final Object | mClientResource | private boolean | mLoadingPausedTrue if loading should be paused | private static RequestContent | requestContentProcessorProcessor used to set content-length and transfer-encoding
headers. |
Constructors Summary |
---|
Request(String method, HttpHost host, HttpHost proxyHost, String path, InputStream bodyProvider, int bodyLength, EventHandler eventHandler, Map headers)Instantiates a new Request.
mEventHandler = eventHandler;
mHost = host;
mProxyHost = proxyHost;
mPath = path;
mBodyProvider = bodyProvider;
mBodyLength = bodyLength;
if (bodyProvider == null && !"POST".equalsIgnoreCase(method)) {
mHttpRequest = new BasicHttpRequest(method, getUri());
} else {
mHttpRequest = new BasicHttpEntityEnclosingRequest(
method, getUri());
// it is ok to have null entity for BasicHttpEntityEnclosingRequest.
// By using BasicHttpEntityEnclosingRequest, it will set up the
// correct content-length, content-type and content-encoding.
if (bodyProvider != null) {
setBodyProvider(bodyProvider, bodyLength);
}
}
addHeader(HOST_HEADER, getHostPort());
/* FIXME: if webcore will make the root document a
high-priority request, we can ask for gzip encoding only on
high priority reqs (saving the trouble for images, etc) */
addHeader(ACCEPT_ENCODING_HEADER, "gzip");
addHeaders(headers);
|
Methods Summary |
---|
void | addHeader(java.lang.String name, java.lang.String value)Add header represented by given pair to request. Header will
be formatted in request as "name: value\r\n".
if (name == null) {
String damage = "Null http header name";
HttpLog.e(damage);
throw new NullPointerException(damage);
}
if (value == null || value.length() == 0) {
String damage = "Null or empty value for header \"" + name + "\"";
HttpLog.e(damage);
throw new RuntimeException(damage);
}
mHttpRequest.addHeader(name, value);
| void | addHeaders(java.util.Map headers)Add all headers in given map to this request. This is a helper
method: it calls addHeader for each pair in the map.
if (headers == null) {
return;
}
Entry<String, String> entry;
Iterator<Entry<String, String>> i = headers.entrySet().iterator();
while (i.hasNext()) {
entry = i.next();
addHeader(entry.getKey(), entry.getValue());
}
| private static boolean | canResponseHaveBody(org.apache.http.HttpRequest request, int status)Decide whether a response comes with an entity.
The implementation in this class is based on RFC 2616.
Unknown methods and response codes are supposed to
indicate responses with an entity.
Derived executors can override this method to handle
methods and response codes not specified in RFC 2616.
if ("HEAD".equalsIgnoreCase(request.getRequestLine().getMethod())) {
return false;
}
return status >= HttpStatus.SC_OK
&& status != HttpStatus.SC_NO_CONTENT
&& status != HttpStatus.SC_NOT_MODIFIED;
| synchronized void | cancel()Data will not be sent to or received from server after cancel()
call. Does not close connection--use close() below for that.
Called by RequestHandle from non-network thread
if (HttpLog.LOGV) {
HttpLog.v("Request.cancel(): " + getUri());
}
// Ensure that the network thread is not blocked by a hanging request from WebCore to
// pause the load.
mLoadingPaused = false;
notify();
mCancelled = true;
if (mConnection != null) {
mConnection.cancel();
}
| void | complete()
synchronized (mClientResource) {
mClientResource.notifyAll();
}
| void | error(int errorId, int resourceId)Helper: calls error() on eventhandler with appropriate message
This should not be called before the mConnection is set.
mEventHandler.error(
errorId,
mConnection.mContext.getText(
resourceId).toString());
| EventHandler | getEventHandler()
return mEventHandler;
| java.lang.String | getHostPort()
String myScheme = mHost.getSchemeName();
int myPort = mHost.getPort();
// Only send port when we must... many servers can't deal with it
if (myPort != 80 && myScheme.equals("http") ||
myPort != 443 && myScheme.equals("https")) {
return mHost.toHostString();
} else {
return mHost.getHostName();
}
| java.lang.String | getUri()
if (mProxyHost == null ||
mHost.getSchemeName().equals("https")) {
return mPath;
}
return mHost.getSchemeName() + "://" + getHostPort() + mPath;
| public void | handleSslErrorResponse(boolean proceed)Handles SSL error(s) on the way down from the user (the user
has already provided their feedback).
HttpsConnection connection = (HttpsConnection)(mConnection);
if (connection != null) {
connection.restartConnection(proceed);
}
| void | readResponse(AndroidHttpClientConnection httpClientConnection)Receive a single http response.
if (mCancelled) return; // don't send cancelled requests
StatusLine statusLine = null;
boolean hasBody = false;
httpClientConnection.flush();
int statusCode = 0;
Headers header = new Headers();
do {
statusLine = httpClientConnection.parseResponseHeader(header);
statusCode = statusLine.getStatusCode();
} while (statusCode < HttpStatus.SC_OK);
if (HttpLog.LOGV) HttpLog.v(
"Request.readResponseStatus() " +
statusLine.toString().length() + " " + statusLine);
ProtocolVersion v = statusLine.getProtocolVersion();
mEventHandler.status(v.getMajor(), v.getMinor(),
statusCode, statusLine.getReasonPhrase());
mEventHandler.headers(header);
HttpEntity entity = null;
hasBody = canResponseHaveBody(mHttpRequest, statusCode);
if (hasBody)
entity = httpClientConnection.receiveResponseEntity(header);
// restrict the range request to the servers claiming that they are
// accepting ranges in bytes
boolean supportPartialContent = "bytes".equalsIgnoreCase(header
.getAcceptRanges());
if (entity != null) {
InputStream is = entity.getContent();
// process gzip content encoding
Header contentEncoding = entity.getContentEncoding();
InputStream nis = null;
byte[] buf = null;
int count = 0;
try {
if (contentEncoding != null &&
contentEncoding.getValue().equals("gzip")) {
nis = new GZIPInputStream(is);
} else {
nis = is;
}
/* accumulate enough data to make it worth pushing it
* up the stack */
buf = mConnection.getBuf();
int len = 0;
int lowWater = buf.length / 2;
while (len != -1) {
synchronized(this) {
while (mLoadingPaused) {
// Put this (network loading) thread to sleep if WebCore
// has asked us to. This can happen with plugins for
// example, if we are streaming data but the plugin has
// filled its internal buffers.
try {
wait();
} catch (InterruptedException e) {
HttpLog.e("Interrupted exception whilst "
+ "network thread paused at WebCore's request."
+ " " + e.getMessage());
}
}
}
len = nis.read(buf, count, buf.length - count);
if (len != -1) {
count += len;
if (supportPartialContent) mReceivedBytes += len;
}
if (len == -1 || count >= lowWater) {
if (HttpLog.LOGV) HttpLog.v("Request.readResponse() " + count);
mEventHandler.data(buf, count);
count = 0;
}
}
} catch (EOFException e) {
/* InflaterInputStream throws an EOFException when the
server truncates gzipped content. Handle this case
as we do truncated non-gzipped content: no error */
if (count > 0) {
// if there is uncommited content, we should commit them
mEventHandler.data(buf, count);
}
if (HttpLog.LOGV) HttpLog.v( "readResponse() handling " + e);
} catch(IOException e) {
// don't throw if we have a non-OK status code
if (statusCode == HttpStatus.SC_OK
|| statusCode == HttpStatus.SC_PARTIAL_CONTENT) {
if (supportPartialContent && count > 0) {
// if there is uncommited content, we should commit them
// as we will continue the request
mEventHandler.data(buf, count);
}
throw e;
}
} finally {
if (nis != null) {
nis.close();
}
}
}
mConnection.setCanPersist(entity, statusLine.getProtocolVersion(),
header.getConnectionType());
mEventHandler.endData();
complete();
if (HttpLog.LOGV) HttpLog.v("Request.readResponse(): done " +
mHost.getSchemeName() + "://" + getHostPort() + mPath);
| void | reset()If this request has been sent once and failed, it must be reset
before it can be sent again.
/* clear content-length header */
mHttpRequest.removeHeaders(CONTENT_LENGTH_HEADER);
if (mBodyProvider != null) {
try {
mBodyProvider.reset();
} catch (IOException ex) {
if (HttpLog.LOGV) HttpLog.v(
"failed to reset body provider " +
getUri());
}
setBodyProvider(mBodyProvider, mBodyLength);
}
if (mReceivedBytes > 0) {
// reset the fail count as we continue the request
mFailCount = 0;
// set the "Range" header to indicate that the retry will continue
// instead of restarting the request
HttpLog.v("*** Request.reset() to range:" + mReceivedBytes);
mHttpRequest.setHeader("Range", "bytes=" + mReceivedBytes + "-");
}
| void | sendRequest(AndroidHttpClientConnection httpClientConnection)Send the request line and headers
if (mCancelled) return; // don't send cancelled requests
if (HttpLog.LOGV) {
HttpLog.v("Request.sendRequest() " + mHost.getSchemeName() + "://" + getHostPort());
// HttpLog.v(mHttpRequest.getRequestLine().toString());
if (false) {
Iterator i = mHttpRequest.headerIterator();
while (i.hasNext()) {
Header header = (Header)i.next();
HttpLog.v(header.getName() + ": " + header.getValue());
}
}
}
requestContentProcessor.process(mHttpRequest,
mConnection.getHttpContext());
httpClientConnection.sendRequestHeader(mHttpRequest);
if (mHttpRequest instanceof HttpEntityEnclosingRequest) {
httpClientConnection.sendRequestEntity(
(HttpEntityEnclosingRequest) mHttpRequest);
}
if (HttpLog.LOGV) {
HttpLog.v("Request.requestSent() " + mHost.getSchemeName() + "://" + getHostPort() + mPath);
}
| private void | setBodyProvider(java.io.InputStream bodyProvider, int bodyLength)Supply an InputStream that provides the body of a request. It's
not great that the caller must also provide the length of the data
returned by that InputStream, but the client needs to know up
front, and I'm not sure how to get this out of the InputStream
itself without a costly readthrough. I'm not sure skip() would
do what we want. If you know a better way, please let me know.
if (!bodyProvider.markSupported()) {
throw new IllegalArgumentException(
"bodyProvider must support mark()");
}
// Mark beginning of stream
bodyProvider.mark(Integer.MAX_VALUE);
((BasicHttpEntityEnclosingRequest)mHttpRequest).setEntity(
new InputStreamEntity(bodyProvider, bodyLength));
| void | setConnection(Connection connection)
mConnection = connection;
| synchronized void | setLoadingPaused(boolean pause)
mLoadingPaused = pause;
// Wake up the paused thread if we're unpausing the load.
if (!mLoadingPaused) {
notify();
}
| public java.lang.String | toString()for debugging
return mPath;
| void | waitUntilComplete()Pause thread request completes. Used for synchronous requests,
and testing
synchronized (mClientResource) {
try {
if (HttpLog.LOGV) HttpLog.v("Request.waitUntilComplete()");
mClientResource.wait();
if (HttpLog.LOGV) HttpLog.v("Request.waitUntilComplete() done waiting");
} catch (InterruptedException e) {
}
}
|
|