| 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 |