| Index: components/cronet/android/java/src/org/chromium/net/UrlRequest.java
|
| diff --git a/components/cronet/android/java/src/org/chromium/net/UrlRequest.java b/components/cronet/android/java/src/org/chromium/net/UrlRequest.java
|
| index da00fdd9a61fb912d17a3ca4e849019bc9aab927..0b8e45a9caa0d07b2c8571fb614c26aa6f91ea03 100644
|
| --- a/components/cronet/android/java/src/org/chromium/net/UrlRequest.java
|
| +++ b/components/cronet/android/java/src/org/chromium/net/UrlRequest.java
|
| @@ -4,6 +4,8 @@
|
|
|
| package org.chromium.net;
|
|
|
| +import android.util.Log;
|
| +
|
| import org.apache.http.conn.ConnectTimeoutException;
|
| import org.chromium.base.CalledByNative;
|
| import org.chromium.base.JNINamespace;
|
| @@ -15,7 +17,9 @@ import java.net.UnknownHostException;
|
| import java.nio.ByteBuffer;
|
| import java.nio.channels.ReadableByteChannel;
|
| import java.nio.channels.WritableByteChannel;
|
| +import java.util.ArrayList;
|
| import java.util.HashMap;
|
| +import java.util.List;
|
| import java.util.Map;
|
| import java.util.Map.Entry;
|
| import java.util.concurrent.Semaphore;
|
| @@ -25,6 +29,9 @@ import java.util.concurrent.Semaphore;
|
| */
|
| @JNINamespace("cronet")
|
| public class UrlRequest {
|
| + private static final String TAG = "UrlRequest";
|
| + private static final boolean DBG = false;
|
| +
|
| private static final class ContextLock {
|
| }
|
|
|
| @@ -123,14 +130,19 @@ public class UrlRequest {
|
| */
|
| public void setUploadChannel(String contentType,
|
| ReadableByteChannel channel) {
|
| + throw new UnsupportedOperationException("Not implemented");
|
| + }
|
| +
|
| + public WritableByteChannel enableUpload(String contentType) {
|
| synchronized (mLock) {
|
| validateNotStarted();
|
| validatePostBodyNotSet();
|
| nativeBeginChunkedUpload(mUrlRequestPeer, contentType);
|
| - mPostBodyChannel = channel;
|
| + //mPostBodyChannel = new UploadChannel();
|
| mPostBodySet = true;
|
| }
|
| mAppendChunkSemaphore = new Semaphore(0);
|
| + return new UploadChannel();
|
| }
|
|
|
| public WritableByteChannel getSink() {
|
| @@ -167,9 +179,6 @@ public class UrlRequest {
|
| nativeStart(mUrlRequestPeer);
|
| }
|
|
|
| - if (mPostBodyChannel != null) {
|
| - uploadFromChannel(mPostBodyChannel);
|
| - }
|
| } finally {
|
| if (mPostBodyChannel != null) {
|
| try {
|
| @@ -181,56 +190,6 @@ public class UrlRequest {
|
| }
|
| }
|
|
|
| - /**
|
| - * Uploads data from a {@code ReadableByteChannel} using chunked transfer
|
| - * encoding. The native call to append a chunk is asynchronous so a
|
| - * semaphore is used to delay writing into the buffer again until chromium
|
| - * is finished with it.
|
| - *
|
| - * @param channel the channel to read data from.
|
| - */
|
| - private void uploadFromChannel(ReadableByteChannel channel) {
|
| - ByteBuffer buffer = ByteBuffer.allocateDirect(UPLOAD_BYTE_BUFFER_SIZE);
|
| -
|
| - // The chromium API requires us to specify in advance if a chunk is the
|
| - // last one. This extra ByteBuffer is needed to peek ahead and check for
|
| - // the end of the channel.
|
| - ByteBuffer checkForEnd = ByteBuffer.allocate(1);
|
| -
|
| - try {
|
| - boolean lastChunk;
|
| - do {
|
| - // First dump in the one byte we read to check for the end of
|
| - // the channel. (The first time through the loop the checkForEnd
|
| - // buffer will be empty).
|
| - checkForEnd.flip();
|
| - buffer.clear();
|
| - buffer.put(checkForEnd);
|
| - checkForEnd.clear();
|
| -
|
| - channel.read(buffer);
|
| - lastChunk = channel.read(checkForEnd) <= 0;
|
| - buffer.flip();
|
| - nativeAppendChunk(mUrlRequestPeer, buffer, buffer.limit(),
|
| - lastChunk);
|
| -
|
| - if (lastChunk) {
|
| - break;
|
| - }
|
| -
|
| - // Acquire permit before writing to the buffer again to ensure
|
| - // chromium is done with it.
|
| - mAppendChunkSemaphore.acquire();
|
| - } while (!lastChunk && !mFinished);
|
| - } catch (IOException e) {
|
| - mSinkException = e;
|
| - cancel();
|
| - } catch (InterruptedException e) {
|
| - mSinkException = new IOException(e);
|
| - cancel();
|
| - }
|
| - }
|
| -
|
| public void cancel() {
|
| synchronized (mLock) {
|
| if (mCanceled) {
|
| @@ -313,6 +272,23 @@ public class UrlRequest {
|
| return nativeGetHeader(mUrlRequestPeer, name);
|
| }
|
|
|
| + // All response headers.
|
| + public Map<String, List<String>> getAllHeaders() {
|
| + validateHeadersAvailable();
|
| + String[] headers = nativeGetAllHeaders(mUrlRequestPeer);
|
| + Map<String, List<String>> result = new HashMap<String, List<String>>();
|
| + for (int i = 0; i < headers.length / 2; ++i) {
|
| + String key = headers[2 * i];
|
| + String value = headers[2 * i + 1];
|
| + if (!result.containsKey(key)) {
|
| + result.put(key, new ArrayList<String>());
|
| + }
|
| + result.get(key).add(value);
|
| + }
|
| + return result;
|
| + }
|
| +
|
| +
|
| /**
|
| * A callback invoked when appending a chunk to the request has completed.
|
| */
|
| @@ -408,6 +384,39 @@ public class UrlRequest {
|
| }
|
| }
|
|
|
| + /**
|
| + * Invokes {@link #nativeAppendChunk(long, ByteBuffer, int, boolean)} and
|
| + * waits for it to notify completion.
|
| + * @param chunk The data. It's position must be zero.
|
| + * @param isLastChunk Whether chunk is the last one.
|
| + */
|
| + void appendChunkBlocking(ByteBuffer chunk, boolean isLastChunk)
|
| + throws IOException {
|
| + if (chunk.position() != 0) {
|
| + throw new IllegalArgumentException("The position must be zero.");
|
| + }
|
| + synchronized (mLock) {
|
| + if (mUrlRequestPeer == 0) {
|
| + throw new IOException("Native peer destroyed.");
|
| + }
|
| + nativeAppendChunk(mUrlRequestPeer, chunk, chunk.limit(), false);
|
| + // Wait for the data to be actually consumed.
|
| + try {
|
| + mAppendChunkSemaphore.acquire();
|
| + } catch (InterruptedException e) {
|
| + // We were interrupted before the data was uploaded. Recovering
|
| + // from this state is complicated so we cancel the upload
|
| + // operation and fail.
|
| + Thread.currentThread().interrupt();
|
| +
|
| + // TODO(miloslav): Not sure why do we set mSinkException here.
|
| + mSinkException = new IOException("Upload interrupted", e);
|
| + cancel();
|
| + throw mSinkException;
|
| + }
|
| + }
|
| + }
|
| +
|
| public String getUrl() {
|
| return mUrl;
|
| }
|
| @@ -444,4 +453,65 @@ public class UrlRequest {
|
| private native long nativeGetContentLength(long urlRequestPeer);
|
|
|
| private native String nativeGetHeader(long urlRequestPeer, String name);
|
| +
|
| + private native String[] nativeGetAllHeaders(long urlRequestPeer);
|
| +
|
| +
|
| + class UploadChannel implements WritableByteChannel {
|
| +
|
| + private boolean mOpen = true;
|
| + // Native wants a direct buffer.
|
| + private final ByteBuffer mBuffer =
|
| + ByteBuffer.allocateDirect(UPLOAD_BYTE_BUFFER_SIZE);
|
| +
|
| + @Override
|
| + public synchronized boolean isOpen() {
|
| + return mOpen;
|
| + }
|
| +
|
| + @Override
|
| + public synchronized void close() throws IOException {
|
| + if (DBG) Log.d(TAG, "UploadChannel.close() url=" + getUrl());
|
| + if (!mOpen) {
|
| + return;
|
| + }
|
| +
|
| + mOpen = false;
|
| + mBuffer.clear();
|
| +
|
| + // NOOP If the native peer has been destroyed.
|
| + try {
|
| + if (DBG) Log.d(TAG, "UploadChannel.close(): final chunk.");
|
| + appendChunkBlocking(mBuffer, true);
|
| + } catch (IOException e) {
|
| + Log.w(TAG, "Ignoring exception during closing.", e);
|
| + }
|
| +
|
| + if (DBG) Log.d(TAG, "UploadChannel.close() done.");
|
| + }
|
| +
|
| + @Override
|
| + public synchronized int write(ByteBuffer sourceBuffer)
|
| + throws IOException {
|
| + if (DBG) Log.d(TAG, "UploadChannel.write("
|
| + + sourceBuffer.remaining() + " bytes) url=" + getUrl());
|
| + int written = 0;
|
| + while (sourceBuffer.hasRemaining()) {
|
| + mBuffer.clear();
|
| + int oldLimit = sourceBuffer.limit();
|
| + if (sourceBuffer.remaining() > mBuffer.remaining()) {
|
| + sourceBuffer.limit(sourceBuffer.position()
|
| + + mBuffer.remaining());
|
| + }
|
| + mBuffer.put(sourceBuffer);
|
| + mBuffer.flip();
|
| + written += mBuffer.limit();
|
| + appendChunkBlocking(mBuffer, false);
|
| + sourceBuffer.limit(oldLimit);
|
| + }
|
| + if (DBG) Log.d(TAG, "UploadChannel.write() returning");
|
| + return written;
|
| + }
|
| +
|
| + }
|
| }
|
|
|