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.UploadDataSink; | 9 import org.chromium.net.UploadDataSink; |
9 | 10 |
10 import java.io.IOException; | 11 import java.io.IOException; |
11 import java.net.HttpRetryException; | 12 import java.net.HttpRetryException; |
12 import java.net.ProtocolException; | 13 import java.net.ProtocolException; |
13 import java.nio.ByteBuffer; | 14 import java.nio.ByteBuffer; |
14 | 15 |
15 /** | 16 /** |
16 * An implementation of {@link java.io.OutputStream} to send data to a server, | 17 * An implementation of {@link java.io.OutputStream} to send data to a server, |
17 * when {@link CronetHttpURLConnection#setFixedLengthStreamingMode} is used. | 18 * when {@link CronetHttpURLConnection#setFixedLengthStreamingMode} is used. |
18 * This implementation does not buffer the entire request body in memory. | 19 * This implementation does not buffer the entire request body in memory. |
19 * It does not support rewind. Note that {@link #write} should only be called | 20 * It does not support rewind. Note that {@link #write} should only be called |
20 * from the thread on which the {@link #mConnection} is created. | 21 * from the thread on which the {@link #mConnection} is created. |
21 */ | 22 */ |
22 final class CronetFixedModeOutputStream extends CronetOutputStream { | 23 final class CronetFixedModeOutputStream extends CronetOutputStream { |
23 // CronetFixedModeOutputStream buffers up to this value and wait for UploadD
ataStream | 24 // CronetFixedModeOutputStream buffers up to this value and wait for UploadD
ataStream |
24 // 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. |
25 // Using 2048 bytes is because the internal read buffer is 14520 for QUIC, | 26 // Using 2048 bytes is because the internal read buffer is 14520 for QUIC, |
26 // 2852 for SPDY, and 16384 for normal stream. If a large value is used | 27 // 2852 for SPDY, and 16384 for normal stream. If a large value is used |
27 // here, the buffer might not fit the internal buffer and compacting the buf
fer | 28 // here, the buffer might not fit the internal buffer and compacting the buf
fer |
28 // will be costly, see #read method below. | 29 // will be costly, see #read method below. |
29 @VisibleForTesting | 30 @VisibleForTesting |
30 private static int sDefaultBufferLength = 2048; | 31 private static int sDefaultBufferLength = 2048; |
31 private final CronetHttpURLConnection mConnection; | 32 private final CronetHttpURLConnection mConnection; |
32 private final MessageLoop mMessageLoop; | 33 private final MessageLoop mMessageLoop; |
33 private final long mContentLength; | 34 private final long mContentLength; |
34 private final ByteBuffer mBuffer; | 35 private final ByteBuffer mBuffer; |
| 36 private final UploadDataProvider mUploadDataProvider = new UploadDataProvide
rImpl(); |
35 private long mBytesWritten; | 37 private long mBytesWritten; |
36 | 38 |
37 /** | 39 /** |
38 * Package protected constructor. | 40 * Package protected constructor. |
39 * @param connection The CronetHttpURLConnection object. | 41 * @param connection The CronetHttpURLConnection object. |
40 * @param contentLength The content length of the request body. Non-zero for | 42 * @param contentLength The content length of the request body. Non-zero for |
41 * non-chunked upload. | 43 * non-chunked upload. |
42 */ | 44 */ |
43 CronetFixedModeOutputStream(CronetHttpURLConnection connection, | 45 CronetFixedModeOutputStream(CronetHttpURLConnection connection, |
44 long contentLength, MessageLoop messageLoop) { | 46 long contentLength, MessageLoop messageLoop) { |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
122 } | 124 } |
123 | 125 |
124 @Override | 126 @Override |
125 void checkReceivedEnoughContent() throws IOException { | 127 void checkReceivedEnoughContent() throws IOException { |
126 if (mBytesWritten < mContentLength) { | 128 if (mBytesWritten < mContentLength) { |
127 throw new ProtocolException("Content received is less than Content-L
ength."); | 129 throw new ProtocolException("Content received is less than Content-L
ength."); |
128 } | 130 } |
129 } | 131 } |
130 | 132 |
131 @Override | 133 @Override |
132 public long getLength() { | 134 UploadDataProvider getUploadDataProvider() { |
133 return mContentLength; | 135 return mUploadDataProvider; |
134 } | 136 } |
135 | 137 |
136 @Override | 138 private class UploadDataProviderImpl extends UploadDataProvider { |
137 public void read(final UploadDataSink uploadDataSink, final ByteBuffer byteB
uffer) { | 139 @Override |
138 int availableSpace = byteBuffer.capacity() - byteBuffer.position(); | 140 public long getLength() { |
139 if (availableSpace < mBuffer.position()) { | 141 return mContentLength; |
140 // byteBuffer does not have enough capacity, so only put a portion | |
141 // of mBuffer in it. | |
142 byteBuffer.put(mBuffer.array(), 0, availableSpace); | |
143 mBuffer.position(availableSpace); | |
144 // Move remaining buffer to the head of the buffer for use in the | |
145 // next read call. | |
146 mBuffer.compact(); | |
147 } else { | |
148 // byteBuffer has enough capacity to hold the content of mBuffer. | |
149 mBuffer.flip(); | |
150 byteBuffer.put(mBuffer); | |
151 // Reuse this buffer. | |
152 mBuffer.clear(); | |
153 // Quit message loop so embedder can write more data. | |
154 mMessageLoop.quit(); | |
155 } | 142 } |
156 uploadDataSink.onReadSucceeded(false); | |
157 } | |
158 | 143 |
159 @Override | 144 @Override |
160 public void rewind(UploadDataSink uploadDataSink) { | 145 public void read(final UploadDataSink uploadDataSink, final ByteBuffer b
yteBuffer) { |
161 uploadDataSink.onRewindError(new HttpRetryException( | 146 int availableSpace = byteBuffer.capacity() - byteBuffer.position(); |
162 "Cannot retry streamed Http body", -1)); | 147 if (availableSpace < mBuffer.position()) { |
| 148 // byteBuffer does not have enough capacity, so only put a porti
on |
| 149 // of mBuffer in it. |
| 150 byteBuffer.put(mBuffer.array(), 0, availableSpace); |
| 151 mBuffer.position(availableSpace); |
| 152 // Move remaining buffer to the head of the buffer for use in th
e |
| 153 // next read call. |
| 154 mBuffer.compact(); |
| 155 } else { |
| 156 // byteBuffer has enough capacity to hold the content of mBuffer
. |
| 157 mBuffer.flip(); |
| 158 byteBuffer.put(mBuffer); |
| 159 // Reuse this buffer. |
| 160 mBuffer.clear(); |
| 161 // Quit message loop so embedder can write more data. |
| 162 mMessageLoop.quit(); |
| 163 } |
| 164 uploadDataSink.onReadSucceeded(false); |
| 165 } |
| 166 |
| 167 @Override |
| 168 public void rewind(UploadDataSink uploadDataSink) { |
| 169 uploadDataSink.onRewindError( |
| 170 new HttpRetryException("Cannot retry streamed Http body", -1
)); |
| 171 } |
163 } | 172 } |
164 | 173 |
165 /** | 174 /** |
166 * Sets the default buffer length for use in tests. | 175 * Sets the default buffer length for use in tests. |
167 */ | 176 */ |
168 @VisibleForTesting | 177 @VisibleForTesting |
169 static void setDefaultBufferLengthForTesting(int length) { | 178 static void setDefaultBufferLengthForTesting(int length) { |
170 sDefaultBufferLength = length; | 179 sDefaultBufferLength = length; |
171 } | 180 } |
172 } | 181 } |
OLD | NEW |