Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.net.urlconnection; | 5 package org.chromium.net.urlconnection; |
| 6 | 6 |
| 7 import org.chromium.net.UploadDataProvider; | 7 import org.chromium.net.UploadDataProvider; |
| 8 import org.chromium.net.UploadDataSink; | 8 import org.chromium.net.UploadDataSink; |
| 9 | 9 |
| 10 import java.io.IOException; | 10 import java.io.IOException; |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 22 private final CronetHttpURLConnection mConnection; | 22 private final CronetHttpURLConnection mConnection; |
| 23 private final MessageLoop mMessageLoop; | 23 private final MessageLoop mMessageLoop; |
| 24 private final ByteBuffer mBuffer; | 24 private final ByteBuffer mBuffer; |
| 25 private final UploadDataProvider mUploadDataProvider = new UploadDataProvide rImpl(); | 25 private final UploadDataProvider mUploadDataProvider = new UploadDataProvide rImpl(); |
| 26 private long mBytesWritten; | 26 private long mBytesWritten; |
| 27 private boolean mLastChunk = false; | 27 private boolean mLastChunk = false; |
| 28 | 28 |
| 29 /** | 29 /** |
| 30 * Package protected constructor. | 30 * Package protected constructor. |
| 31 * @param connection The CronetHttpURLConnection object. | 31 * @param connection The CronetHttpURLConnection object. |
| 32 * @param contentLength The content length of the request body. Non-zero for | 32 * @param contentLength The content length of the request body. Non-zero for |
|
kapishnikov
2016/07/08 23:05:12
contentLength => chunkLength
xunjieli
2016/07/11 13:40:08
Done. Good catch!
| |
| 33 * non-chunked upload. | 33 * non-chunked upload. |
| 34 */ | 34 */ |
| 35 CronetChunkedOutputStream( | 35 CronetChunkedOutputStream( |
| 36 CronetHttpURLConnection connection, int chunkLength, MessageLoop mes sageLoop) { | 36 CronetHttpURLConnection connection, int chunkLength, MessageLoop mes sageLoop) { |
| 37 if (connection == null) { | 37 if (connection == null) { |
| 38 throw new NullPointerException(); | 38 throw new NullPointerException(); |
| 39 } | 39 } |
| 40 if (chunkLength <= 0) { | 40 if (chunkLength <= 0) { |
| 41 throw new IllegalArgumentException("chunkLength should be greater th an 0"); | 41 throw new IllegalArgumentException("chunkLength should be greater th an 0"); |
| 42 } | 42 } |
| 43 mBuffer = ByteBuffer.allocate(chunkLength); | 43 mBuffer = ByteBuffer.allocate(chunkLength); |
| 44 mConnection = connection; | 44 mConnection = connection; |
| 45 mMessageLoop = messageLoop; | 45 mMessageLoop = messageLoop; |
| 46 mBytesWritten = 0; | 46 mBytesWritten = 0; |
| 47 } | 47 } |
| 48 | 48 |
| 49 @Override | 49 @Override |
| 50 public void write(int oneByte) throws IOException { | 50 public void write(int oneByte) throws IOException { |
| 51 checkNotClosed(); | 51 ensureBufferHasRemaining(); |
| 52 while (mBuffer.position() == mBuffer.limit()) { | |
| 53 // Wait until buffer is consumed. | |
| 54 mMessageLoop.loop(); | |
| 55 } | |
| 56 mBuffer.put((byte) oneByte); | 52 mBuffer.put((byte) oneByte); |
| 57 mBytesWritten++; | 53 mBytesWritten++; |
| 58 } | 54 } |
| 59 | 55 |
| 60 @Override | 56 @Override |
| 61 public void write(byte[] buffer, int offset, int count) throws IOException { | 57 public void write(byte[] buffer, int offset, int count) throws IOException { |
| 62 checkNotClosed(); | 58 checkNotClosed(); |
| 63 if (buffer.length - offset < count || offset < 0 || count < 0) { | 59 if (buffer.length - offset < count || offset < 0 || count < 0) { |
| 64 throw new IndexOutOfBoundsException(); | 60 throw new IndexOutOfBoundsException(); |
| 65 } | 61 } |
| 66 if (count == 0) { | |
| 67 return; | |
| 68 } | |
| 69 int toSend = count; | 62 int toSend = count; |
| 70 // TODO(xunjieli): refactor commond code with CronetFixedModeOutputStrea m. | |
| 71 while (toSend > 0) { | 63 while (toSend > 0) { |
| 72 if (mBuffer.position() == mBuffer.limit()) { | 64 ensureBufferHasRemaining(); |
| 73 checkNotClosed(); | 65 int sent = Math.min(toSend, mBuffer.remaining()); |
|
kapishnikov
2016/07/08 23:05:12
If toSend == mBuffer.remaining(), is it going to w
xunjieli
2016/07/11 13:40:08
Done. That is a great suggestion. I agree that it
| |
| 74 // Wait until buffer is consumed. | |
| 75 mMessageLoop.loop(); | |
| 76 } | |
| 77 int sent = Math.min(toSend, mBuffer.limit() - mBuffer.position()); | |
| 78 mBuffer.put(buffer, offset + count - toSend, sent); | 66 mBuffer.put(buffer, offset + count - toSend, sent); |
| 79 toSend -= sent; | 67 toSend -= sent; |
| 80 } | 68 } |
| 81 mBytesWritten += count; | 69 mBytesWritten += count; |
| 82 } | 70 } |
| 83 | 71 |
| 84 @Override | 72 @Override |
| 85 public void close() throws IOException { | 73 public void close() throws IOException { |
| 86 super.close(); | 74 super.close(); |
| 87 // Last chunk is written. | 75 if (!mLastChunk) { |
|
kapishnikov
2016/07/08 23:05:12
Should we make it thread safe?
xunjieli
2016/07/11 13:40:08
HttpURLConnection is not thread-safe. The APIs are
| |
| 88 mLastChunk = true; | 76 // Consumer can only call close() when message loop is not running. |
| 77 // Set mLastChunk to be true and flip mBuffer to upload its contents . | |
| 78 mLastChunk = true; | |
| 79 mBuffer.flip(); | |
| 80 } | |
| 89 } | 81 } |
| 90 | 82 |
| 91 // Below are CronetOutputStream implementations: | 83 // Below are CronetOutputStream implementations: |
| 92 | 84 |
| 93 @Override | 85 @Override |
| 94 void setConnected() throws IOException { | 86 void setConnected() throws IOException { |
| 95 // Do nothing. | 87 // Do nothing. |
| 96 } | 88 } |
| 97 | 89 |
| 98 @Override | 90 @Override |
| 99 void checkReceivedEnoughContent() throws IOException { | 91 void checkReceivedEnoughContent() throws IOException { |
| 100 // Do nothing. | 92 // Do nothing. |
| 101 } | 93 } |
| 102 | 94 |
| 103 @Override | 95 @Override |
| 104 UploadDataProvider getUploadDataProvider() { | 96 UploadDataProvider getUploadDataProvider() { |
| 105 return mUploadDataProvider; | 97 return mUploadDataProvider; |
| 106 } | 98 } |
| 107 | 99 |
| 108 private class UploadDataProviderImpl extends UploadDataProvider { | 100 private class UploadDataProviderImpl extends UploadDataProvider { |
| 109 @Override | 101 @Override |
| 110 public long getLength() { | 102 public long getLength() { |
| 111 return -1; | 103 return -1; |
| 112 } | 104 } |
| 113 | 105 |
| 114 @Override | 106 @Override |
| 115 public void read(final UploadDataSink uploadDataSink, final ByteBuffer b yteBuffer) { | 107 public void read(final UploadDataSink uploadDataSink, final ByteBuffer b yteBuffer) { |
| 116 final int availableSpace = byteBuffer.remaining(); | 108 if (byteBuffer.remaining() >= mBuffer.remaining()) { |
|
xunjieli
2016/07/08 20:31:00
This is done to match the new code in CronetFixedM
| |
| 117 if (availableSpace < mBuffer.position()) { | |
| 118 // byteBuffer does not have enough capacity, so only put a porti on | |
| 119 // of mBuffer in it. | |
| 120 byteBuffer.put(mBuffer.array(), 0, availableSpace); | |
| 121 mBuffer.position(availableSpace); | |
| 122 // Move remaining buffer to the head of the buffer for use in th e | |
| 123 // next read call. | |
| 124 mBuffer.compact(); | |
| 125 uploadDataSink.onReadSucceeded(false); | |
| 126 } else { | |
| 127 // byteBuffer has enough capacity to hold the content of mBuffer . | |
| 128 mBuffer.flip(); | |
| 129 byteBuffer.put(mBuffer); | 109 byteBuffer.put(mBuffer); |
| 130 // Reuse this buffer. | |
| 131 mBuffer.clear(); | 110 mBuffer.clear(); |
| 132 uploadDataSink.onReadSucceeded(mLastChunk); | 111 uploadDataSink.onReadSucceeded(mLastChunk); |
| 133 if (!mLastChunk) { | 112 if (!mLastChunk) { |
| 134 // Quit message loop so embedder can write more data. | 113 // Quit message loop so embedder can write more data. |
| 135 mMessageLoop.quit(); | 114 mMessageLoop.quit(); |
| 136 } | 115 } |
| 116 } else { | |
| 117 int oldLimit = mBuffer.limit(); | |
| 118 mBuffer.limit(mBuffer.position() + byteBuffer.remaining()); | |
| 119 byteBuffer.put(mBuffer); | |
| 120 mBuffer.limit(oldLimit); | |
| 121 uploadDataSink.onReadSucceeded(false); | |
| 137 } | 122 } |
| 138 } | 123 } |
| 139 | 124 |
| 140 @Override | 125 @Override |
| 141 public void rewind(UploadDataSink uploadDataSink) { | 126 public void rewind(UploadDataSink uploadDataSink) { |
| 142 uploadDataSink.onRewindError( | 127 uploadDataSink.onRewindError( |
| 143 new HttpRetryException("Cannot retry streamed Http body", -1 )); | 128 new HttpRetryException("Cannot retry streamed Http body", -1 )); |
| 144 } | 129 } |
| 145 } | 130 } |
| 131 | |
| 132 /** | |
| 133 * If {@code mBuffer} is full, wait until it is consumed and there is | |
| 134 * space to write more data to it. | |
| 135 */ | |
| 136 private void ensureBufferHasRemaining() throws IOException { | |
| 137 if (!mBuffer.hasRemaining()) { | |
| 138 uploadBufferInternal(); | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 /** | |
| 143 * Helper function to upload {@code mBuffer} to the native stack. This | |
| 144 * function blocks until {@code mBuffer} is consumed and there is space to | |
| 145 * write more data. | |
| 146 */ | |
| 147 private void uploadBufferInternal() throws IOException { | |
| 148 checkNotClosed(); | |
| 149 mBuffer.flip(); | |
| 150 mMessageLoop.loop(); | |
| 151 } | |
| 146 } | 152 } |
| OLD | NEW |