Commit b195f2b4 authored by mef@chromium.org's avatar mef@chromium.org

Upstream changes to:

- Add HttpUrlRequest callback for querying response headers.
- Always close ReadableByteChannel once an HttpUrlRequest is started.

BUG=354143

Review URL: https://codereview.chromium.org/268353013

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@271992 0039d316-1c4b-4281-b951-d872f2087c98
parent 4b5dda47
......@@ -221,6 +221,8 @@ class HttpUrlConnectionUrlRequest implements HttpUrlRequest {
return;
}
mListener.onResponseStarted(this);
mResponseStream = isError(mHttpStatusCode) ? mConnection
.getErrorStream()
: stream;
......@@ -250,6 +252,14 @@ class HttpUrlConnectionUrlRequest implements HttpUrlRequest {
} catch (IOException e) {
mException = e;
} finally {
if (mPostDataChannel != null) {
try {
mPostDataChannel.close();
} catch (IOException e) {
// Ignore
}
}
// Don't call onRequestComplete yet if we are reading the response
// on a separate thread
if (!readingResponse) {
......@@ -425,6 +435,15 @@ class HttpUrlConnectionUrlRequest implements HttpUrlRequest {
return mContentType;
}
@Override
public String getHeader(String name) {
if (mConnection == null) {
throw new IllegalStateException("Response headers not available");
}
return mConnection.getHeaderField(name);
}
private void validateNotStarted() {
if (mStarted) {
throw new IllegalStateException("Request already started");
......
......@@ -58,6 +58,9 @@ public interface HttpUrlRequest {
/**
* Sets a readable byte channel to upload as part of a POST request.
*
* <p>Once {@link #start()} is called, this channel is guaranteed to be
* closed, either when the upload completes, or when it is canceled.
*
* @param contentType MIME type of the post content or null if this is not a
* POST.
* @param channel The channel to read to read upload data from if this is a
......@@ -112,6 +115,13 @@ public interface HttpUrlRequest {
*/
int getHttpStatusCode();
/**
* Returns the response header value for the given name or {@code null} if
* not found.
*/
String getHeader(String name);
/**
* Returns the exception that occurred while executing the request of null
* if the request was successful.
......
......@@ -9,8 +9,10 @@ package org.chromium.net;
*/
public interface HttpUrlRequestListener {
/**
* A callback invoked when the first chunk of the response has arrived.
* The listener can only call request getContentType and getContentLength.
* A callback invoked when the first chunk of the response has arrived and
* response headers have been read. The listener can only call request
* getHeader, getContentType and getContentLength. This method will always
* be called before {@code onRequestComplete}.
*/
void onResponseStarted(HttpUrlRequest request);
......
......@@ -44,6 +44,7 @@ public class UrlRequest {
private volatile boolean mCanceled;
private volatile boolean mRecycled;
private volatile boolean mFinished;
private boolean mHeadersAvailable;
private String mContentType;
private long mContentLength;
private Semaphore mAppendChunkSemaphore;
......@@ -137,36 +138,46 @@ public class UrlRequest {
}
public void start() {
synchronized (mLock) {
if (mCanceled) {
return;
}
try {
synchronized (mLock) {
if (mCanceled) {
return;
}
validateNotStarted();
validateNotRecycled();
validateNotStarted();
validateNotRecycled();
mStarted = true;
mStarted = true;
if (mHeaders != null && !mHeaders.isEmpty()) {
for (Entry<String, String> entry : mHeaders.entrySet()) {
nativeAddHeader(mUrlRequestPeer, entry.getKey(),
entry.getValue());
if (mHeaders != null && !mHeaders.isEmpty()) {
for (Entry<String, String> entry : mHeaders.entrySet()) {
nativeAddHeader(mUrlRequestPeer, entry.getKey(),
entry.getValue());
}
}
}
if (mAdditionalHeaders != null && !mAdditionalHeaders.isEmpty()) {
for (Entry<String, String> entry :
mAdditionalHeaders.entrySet()) {
nativeAddHeader(mUrlRequestPeer, entry.getKey(),
entry.getValue());
if (mAdditionalHeaders != null && !mAdditionalHeaders.isEmpty()) {
for (Entry<String, String> entry :
mAdditionalHeaders.entrySet()) {
nativeAddHeader(mUrlRequestPeer, entry.getKey(),
entry.getValue());
}
}
}
nativeStart(mUrlRequestPeer);
}
nativeStart(mUrlRequestPeer);
}
if (mPostBodyChannel != null) {
uploadFromChannel(mPostBodyChannel);
if (mPostBodyChannel != null) {
uploadFromChannel(mPostBodyChannel);
}
} finally {
if (mPostBodyChannel != null) {
try {
mPostBodyChannel.close();
} catch (IOException e) {
// Ignore
}
}
}
}
......@@ -217,12 +228,6 @@ public class UrlRequest {
} catch (InterruptedException e) {
mSinkException = new IOException(e);
cancel();
} finally {
try {
mPostBodyChannel.close();
} catch (IOException ignore) {
;
}
}
}
......@@ -303,6 +308,11 @@ public class UrlRequest {
return mContentType;
}
public String getHeader(String name) {
validateHeadersAvailable();
return nativeGetHeader(mUrlRequestPeer, name);
}
/**
* A callback invoked when appending a chunk to the request has completed.
*/
......@@ -318,6 +328,7 @@ public class UrlRequest {
protected void onResponseStarted() {
mContentType = nativeGetContentType(mUrlRequestPeer);
mContentLength = nativeGetContentLength(mUrlRequestPeer);
mHeadersAvailable = true;
}
/**
......@@ -390,6 +401,13 @@ public class UrlRequest {
}
}
private void validateHeadersAvailable() {
if (!mHeadersAvailable) {
throw new IllegalStateException("Response headers not available");
}
}
public String getUrl() {
return mUrl;
}
......@@ -424,4 +442,6 @@ public class UrlRequest {
private native String nativeGetContentType(long urlRequestPeer);
private native long nativeGetContentLength(long urlRequestPeer);
private native String nativeGetHeader(long urlRequestPeer, String name);
}
......@@ -301,4 +301,20 @@ static jlong GetContentLength(JNIEnv* env,
return request->content_length();
}
static jstring GetHeader(
JNIEnv* env, jobject object, jlong urlRequestPeer, jstring name) {
URLRequestPeer* request = reinterpret_cast<URLRequestPeer*>(urlRequestPeer);
if (request == NULL) {
return NULL;
}
std::string name_string = base::android::ConvertJavaStringToUTF8(env, name);
std::string value = request->GetHeader(name_string);
if (!value.empty()) {
return ConvertUTF8ToJavaString(env, value.c_str()).Release();
} else {
return NULL;
}
}
} // namespace cronet
......@@ -70,6 +70,15 @@ void URLRequestPeer::AppendChunk(const char* bytes,
is_last_chunk));
}
std::string URLRequestPeer::GetHeader(const std::string &name) const {
std::string value;
if (url_request_ != NULL) {
url_request_->GetResponseHeaderByName(name, &value);
}
return value;
}
void URLRequestPeer::Start() {
context_->GetNetworkTaskRunner()->PostTask(
FROM_HERE,
......
......@@ -84,6 +84,9 @@ class URLRequestPeer : public net::URLRequest::Delegate {
// Returns the value of the content-type response header.
std::string content_type() const { return content_type_; }
// Returns the value of the specified response header.
std::string GetHeader(const std::string& name) const;
// Returns the overall number of bytes read.
size_t bytes_read() const { return bytes_read_; }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment