Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(222)

Side by Side Diff: components/cronet/android/java/src/org/chromium/net/urlconnection/CronetFixedModeOutputStream.java

Issue 2055083002: [Cronet] Fix CronetFixedModeOutputStream to not write more bytes than specified (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: address comments Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698