| 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.base.VisibleForTesting; | 7 import org.chromium.base.VisibleForTesting; |
| 8 import org.chromium.net.UploadDataProvider; | 8 import org.chromium.net.UploadDataProvider; |
| 9 import org.chromium.net.UploadDataSink; | 9 import org.chromium.net.UploadDataSink; |
| 10 | 10 |
| (...skipping 12 matching lines...) Expand all Loading... |
| 23 final class CronetFixedModeOutputStream extends CronetOutputStream { | 23 final class CronetFixedModeOutputStream extends CronetOutputStream { |
| 24 // CronetFixedModeOutputStream buffers up to this value and wait for UploadD
ataStream | 24 // CronetFixedModeOutputStream buffers up to this value and wait for UploadD
ataStream |
| 25 // to consume the data. This field is non-final, so it can be changed for te
sts. | 25 // to consume the data. This field is non-final, so it can be changed for te
sts. |
| 26 // Using 16384 bytes is because the internal read buffer is 14520 for QUIC, | 26 // Using 16384 bytes is because the internal read buffer is 14520 for QUIC, |
| 27 // 16384 for SPDY, and 16384 for normal HTTP/1.1 stream. | 27 // 16384 for SPDY, and 16384 for normal HTTP/1.1 stream. |
| 28 @VisibleForTesting | 28 @VisibleForTesting |
| 29 private static int sDefaultBufferLength = 16384; | 29 private static int sDefaultBufferLength = 16384; |
| 30 private final CronetHttpURLConnection mConnection; | 30 private final CronetHttpURLConnection mConnection; |
| 31 private final MessageLoop mMessageLoop; | 31 private final MessageLoop mMessageLoop; |
| 32 private final long mContentLength; | 32 private final long mContentLength; |
| 33 // Internal buffer for holding bytes from the client until the bytes are |
| 34 // copied to the UploadDataSink in UploadDataProvider.read(). |
| 35 // CronetFixedModeOutputStream allows client to provide up to |
| 36 // sDefaultBufferLength bytes, and wait for UploadDataProvider.read() to be |
| 37 // called after which point mBuffer is cleared so client can fill in again. |
| 38 // While the client is filling the buffer (via {@code write()}), the buffer'
s |
| 39 // position points to the next byte to be provided by the client, and limit |
| 40 // points to the end of the buffer. The buffer is flipped before it is |
| 41 // passed to the UploadDataProvider for consuming. Once it is flipped, |
| 42 // buffer position points to the next byte to be copied to the |
| 43 // UploadDataSink, and limit points to the end of data available to be |
| 44 // copied to UploadDataSink. When the UploadDataProvider has provided all |
| 45 // remaining bytes from the buffer to UploadDataSink, it clears the buffer |
| 46 // so client can fill it again. |
| 33 private final ByteBuffer mBuffer; | 47 private final ByteBuffer mBuffer; |
| 34 private final UploadDataProvider mUploadDataProvider = new UploadDataProvide
rImpl(); | 48 private final UploadDataProvider mUploadDataProvider = new UploadDataProvide
rImpl(); |
| 35 private long mBytesWritten; | 49 private long mBytesWritten; |
| 36 | 50 |
| 37 /** | 51 /** |
| 38 * Package protected constructor. | 52 * Package protected constructor. |
| 39 * @param connection The CronetHttpURLConnection object. | 53 * @param connection The CronetHttpURLConnection object. |
| 40 * @param contentLength The content length of the request body. Non-zero for | 54 * @param contentLength The content length of the request body. Non-zero for |
| 41 * non-chunked upload. | 55 * non-chunked upload. |
| 42 */ | 56 */ |
| (...skipping 10 matching lines...) Expand all Loading... |
| 53 int bufferSize = (int) Math.min(mContentLength, sDefaultBufferLength); | 67 int bufferSize = (int) Math.min(mContentLength, sDefaultBufferLength); |
| 54 mBuffer = ByteBuffer.allocate(bufferSize); | 68 mBuffer = ByteBuffer.allocate(bufferSize); |
| 55 mConnection = connection; | 69 mConnection = connection; |
| 56 mMessageLoop = messageLoop; | 70 mMessageLoop = messageLoop; |
| 57 mBytesWritten = 0; | 71 mBytesWritten = 0; |
| 58 } | 72 } |
| 59 | 73 |
| 60 @Override | 74 @Override |
| 61 public void write(int oneByte) throws IOException { | 75 public void write(int oneByte) throws IOException { |
| 62 checkNotExceedContentLength(1); | 76 checkNotExceedContentLength(1); |
| 63 while (mBuffer.position() == mBuffer.limit()) { | 77 ensureBufferHasRemaining(); |
| 64 // Wait until buffer is consumed. | |
| 65 mMessageLoop.loop(); | |
| 66 } | |
| 67 mBuffer.put((byte) oneByte); | 78 mBuffer.put((byte) oneByte); |
| 68 mBytesWritten++; | 79 mBytesWritten++; |
| 69 if (mBytesWritten == mContentLength) { | 80 uploadIfComplete(); |
| 70 // Entire post data has been received. Now wait for network stack to | |
| 71 // read it. | |
| 72 mMessageLoop.loop(); | |
| 73 } | |
| 74 } | 81 } |
| 75 | 82 |
| 76 @Override | 83 @Override |
| 77 public void write(byte[] buffer, int offset, int count) throws IOException { | 84 public void write(byte[] buffer, int offset, int count) throws IOException { |
| 78 if (buffer.length - offset < count || offset < 0 || count < 0) { | 85 if (buffer.length - offset < count || offset < 0 || count < 0) { |
| 79 throw new IndexOutOfBoundsException(); | 86 throw new IndexOutOfBoundsException(); |
| 80 } | 87 } |
| 81 checkNotExceedContentLength(count); | 88 checkNotExceedContentLength(count); |
| 82 if (count == 0) { | |
| 83 return; | |
| 84 } | |
| 85 int toSend = count; | 89 int toSend = count; |
| 86 while (toSend > 0) { | 90 while (toSend > 0) { |
| 87 if (mBuffer.position() == mBuffer.limit()) { | 91 ensureBufferHasRemaining(); |
| 88 // Wait until buffer is consumed. | 92 int sent = Math.min(toSend, mBuffer.remaining()); |
| 89 mMessageLoop.loop(); | |
| 90 } | |
| 91 int sent = Math.min(toSend, mBuffer.limit() - mBuffer.position()); | |
| 92 mBuffer.put(buffer, offset + count - toSend, sent); | 93 mBuffer.put(buffer, offset + count - toSend, sent); |
| 93 toSend -= sent; | 94 toSend -= sent; |
| 94 } | 95 } |
| 95 mBytesWritten += count; | 96 mBytesWritten += count; |
| 97 uploadIfComplete(); |
| 98 } |
| 99 |
| 100 /** |
| 101 * If {@code mBuffer} is full, wait until it is consumed and there is |
| 102 * space to write more data to it. |
| 103 */ |
| 104 private void ensureBufferHasRemaining() throws IOException { |
| 105 if (!mBuffer.hasRemaining()) { |
| 106 uploadBufferInternal(); |
| 107 } |
| 108 } |
| 109 |
| 110 /** |
| 111 * Waits for the native stack to upload {@code mBuffer}'s contents because |
| 112 * the client has provided all bytes to be uploaded and there is no need to |
| 113 * wait for or expect the client to provide more bytes. |
| 114 */ |
| 115 private void uploadIfComplete() throws IOException { |
| 96 if (mBytesWritten == mContentLength) { | 116 if (mBytesWritten == mContentLength) { |
| 97 // Entire post data has been received. Now wait for network stack to | 117 // Entire post data has been received. Now wait for network stack to |
| 98 // read it. | 118 // read it. |
| 99 mMessageLoop.loop(); | 119 uploadBufferInternal(); |
| 100 } | 120 } |
| 101 } | 121 } |
| 102 | 122 |
| 103 /** | 123 /** |
| 124 * Helper function to upload {@code mBuffer} to the native stack. This |
| 125 * function blocks until {@code mBuffer} is consumed and there is space to |
| 126 * write more data. |
| 127 */ |
| 128 private void uploadBufferInternal() throws IOException { |
| 129 mBuffer.flip(); |
| 130 mMessageLoop.loop(); |
| 131 } |
| 132 |
| 133 /** |
| 104 * Throws {@link java.net.ProtocolException} if adding {@code numBytes} will | 134 * Throws {@link java.net.ProtocolException} if adding {@code numBytes} will |
| 105 * exceed content length. | 135 * exceed content length. |
| 106 */ | 136 */ |
| 107 private void checkNotExceedContentLength(int numBytes) throws ProtocolExcept
ion { | 137 private void checkNotExceedContentLength(int numBytes) throws ProtocolExcept
ion { |
| 108 if (mBytesWritten + numBytes > mContentLength) { | 138 if (mBytesWritten + numBytes > mContentLength) { |
| 109 throw new ProtocolException("expected " | 139 throw new ProtocolException("expected " |
| 110 + (mContentLength - mBytesWritten) + " bytes but received " | 140 + (mContentLength - mBytesWritten) + " bytes but received " |
| 111 + numBytes); | 141 + numBytes); |
| 112 } | 142 } |
| 113 } | 143 } |
| (...skipping 20 matching lines...) Expand all Loading... |
| 134 } | 164 } |
| 135 | 165 |
| 136 private class UploadDataProviderImpl extends UploadDataProvider { | 166 private class UploadDataProviderImpl extends UploadDataProvider { |
| 137 @Override | 167 @Override |
| 138 public long getLength() { | 168 public long getLength() { |
| 139 return mContentLength; | 169 return mContentLength; |
| 140 } | 170 } |
| 141 | 171 |
| 142 @Override | 172 @Override |
| 143 public void read(final UploadDataSink uploadDataSink, final ByteBuffer b
yteBuffer) { | 173 public void read(final UploadDataSink uploadDataSink, final ByteBuffer b
yteBuffer) { |
| 144 final int availableSpace = byteBuffer.remaining(); | 174 if (byteBuffer.remaining() >= mBuffer.remaining()) { |
| 145 if (availableSpace < mBuffer.position()) { | |
| 146 // byteBuffer does not have enough capacity, so only put a porti
on | |
| 147 // of mBuffer in it. | |
| 148 byteBuffer.put(mBuffer.array(), 0, availableSpace); | |
| 149 mBuffer.position(availableSpace); | |
| 150 // Move remaining buffer to the head of the buffer for use in th
e | |
| 151 // next read call. | |
| 152 mBuffer.compact(); | |
| 153 } else { | |
| 154 // byteBuffer has enough capacity to hold the content of mBuffer
. | |
| 155 mBuffer.flip(); | |
| 156 byteBuffer.put(mBuffer); | 175 byteBuffer.put(mBuffer); |
| 157 // Reuse this buffer. | 176 // Reuse this buffer. |
| 158 mBuffer.clear(); | 177 mBuffer.clear(); |
| 178 uploadDataSink.onReadSucceeded(false); |
| 159 // Quit message loop so embedder can write more data. | 179 // Quit message loop so embedder can write more data. |
| 160 mMessageLoop.quit(); | 180 mMessageLoop.quit(); |
| 181 } else { |
| 182 int oldLimit = mBuffer.limit(); |
| 183 mBuffer.limit(mBuffer.position() + byteBuffer.remaining()); |
| 184 byteBuffer.put(mBuffer); |
| 185 mBuffer.limit(oldLimit); |
| 186 uploadDataSink.onReadSucceeded(false); |
| 161 } | 187 } |
| 162 uploadDataSink.onReadSucceeded(false); | |
| 163 } | 188 } |
| 164 | 189 |
| 165 @Override | 190 @Override |
| 166 public void rewind(UploadDataSink uploadDataSink) { | 191 public void rewind(UploadDataSink uploadDataSink) { |
| 167 uploadDataSink.onRewindError( | 192 uploadDataSink.onRewindError( |
| 168 new HttpRetryException("Cannot retry streamed Http body", -1
)); | 193 new HttpRetryException("Cannot retry streamed Http body", -1
)); |
| 169 } | 194 } |
| 170 } | 195 } |
| 171 | 196 |
| 172 /** | 197 /** |
| 173 * Sets the default buffer length for use in tests. | 198 * Sets the default buffer length for use in tests. |
| 174 */ | 199 */ |
| 175 @VisibleForTesting | 200 @VisibleForTesting |
| 176 static void setDefaultBufferLengthForTesting(int length) { | 201 static void setDefaultBufferLengthForTesting(int length) { |
| 177 sDefaultBufferLength = length; | 202 sDefaultBufferLength = length; |
| 178 } | 203 } |
| 179 } | 204 } |
| OLD | NEW |