Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 package org.chromium.net.urlconnection; | |
| 6 | |
| 7 import org.chromium.net.UploadDataProvider; | |
| 8 import org.chromium.net.UploadDataSink; | |
| 9 | |
| 10 import java.io.ByteArrayOutputStream; | |
| 11 import java.io.IOException; | |
| 12 import java.io.OutputStream; | |
| 13 import java.net.ProtocolException; | |
| 14 import java.nio.ByteBuffer; | |
| 15 | |
| 16 /** | |
| 17 * An implementation of {@link java.io.OutputStream} that buffers entire request | |
| 18 * body in memory. This is used when neither | |
| 19 * {@link CronetHttpURLConnection#setFixedLengthStreamingMode} | |
| 20 * or {@link CronetHttpURLConnection#setChunkedStreamingMode} is set. | |
| 21 */ | |
| 22 final class CronetBufferedOutputStream extends OutputStream | |
| 23 implements UploadDataProvider { | |
| 24 // If content length is not passed in the constructor, this is -1. | |
| 25 private final int mInitialContentLength; | |
| 26 private final CronetHttpURLConnection mConnection; | |
| 27 // Internal buffer that is used to buffer the request body. | |
| 28 private final ByteArrayOutputStream mBuffer; | |
| 29 // Number of bytes consumed by the native UploadDataStream. | |
| 30 private int mBytesConsumed; | |
| 31 | |
| 32 /** | |
| 33 * Package protected constructor. | |
| 34 * @param connection The CronetHttpURLConnection object. | |
| 35 * @param contentLength The content length of the request body. It must not | |
| 36 * be smaller than 0 or bigger than {@link Integer.MAX_VALUE}. | |
| 37 */ | |
| 38 CronetBufferedOutputStream(final CronetHttpURLConnection connection, | |
| 39 final long contentLength) { | |
| 40 if (connection == null) { | |
| 41 throw new NullPointerException(); | |
| 42 } | |
| 43 | |
| 44 if (contentLength > Integer.MAX_VALUE) { | |
| 45 throw new IllegalStateException("Use setFixedLengthStreamingMode()" | |
| 46 + " or setChunkedStreamingMode() for requests larger than 2GB.") ; | |
| 47 } | |
| 48 if (contentLength < 0) { | |
| 49 throw new IllegalArgumentException("Content length < 0."); | |
| 50 } | |
| 51 mConnection = connection; | |
| 52 mInitialContentLength = (int) contentLength; | |
| 53 mBuffer = new ByteArrayOutputStream(mInitialContentLength); | |
| 54 mBytesConsumed = 0; | |
| 55 } | |
| 56 | |
| 57 /** | |
| 58 * Package protected constructor used when content length is not known. | |
| 59 * @param connection The CronetHttpURLConnection object. | |
| 60 */ | |
| 61 CronetBufferedOutputStream(final CronetHttpURLConnection connection) { | |
| 62 if (connection == null) { | |
| 63 throw new NullPointerException(); | |
| 64 } | |
| 65 | |
| 66 mConnection = connection; | |
| 67 mInitialContentLength = -1; | |
| 68 // Bufferring without knowing content-length. | |
| 69 mBuffer = new ByteArrayOutputStream(); | |
| 70 mBytesConsumed = 0; | |
| 71 } | |
| 72 | |
| 73 @Override | |
| 74 public void write(int oneByte) throws IOException { | |
| 75 checkNotExceedContentLength(1); | |
| 76 mBuffer.write((byte) oneByte); | |
| 77 if (mBuffer.size() == mInitialContentLength) { | |
| 78 // Entire post data has been received. Now start the request. | |
| 79 mConnection.connect(); | |
| 80 } | |
| 81 } | |
| 82 | |
| 83 @Override | |
| 84 public void write(byte[] buffer, int offset, int count) throws IOException { | |
| 85 checkNotExceedContentLength(count); | |
| 86 mBuffer.write(buffer, offset, count); | |
| 87 if (mBuffer.size() == mInitialContentLength) { | |
| 88 // Entire post data has been received. Now start the request. | |
| 89 mConnection.connect(); | |
| 90 } | |
| 91 } | |
| 92 | |
| 93 /** | |
| 94 * Throws {@link java.net.ProtocolException} if adding {@code numBytes} will | |
| 95 * exceed content length. | |
| 96 */ | |
| 97 private void checkNotExceedContentLength(int numBytes) throws ProtocolExcept ion { | |
| 98 if (mInitialContentLength != -1 | |
| 99 && mBuffer.size() + numBytes > mInitialContentLength) { | |
| 100 throw new ProtocolException("exceeded content-length limit of " | |
| 101 + mInitialContentLength + " bytes"); | |
| 102 } | |
| 103 } | |
| 104 | |
| 105 // Below are UploadDataProvider implementations. Only intended to be used | |
| 106 // within Cronet. | |
| 107 | |
| 108 @Override | |
| 109 public long getLength() { | |
| 110 // This method is supposed to be called just before starting the request . | |
| 111 // If content length is not initially passed in, the number of bytes | |
| 112 // written will be used as the content length. | |
| 113 if (mInitialContentLength == -1) { | |
| 114 return mBuffer.size(); | |
|
mmenke
2015/03/13 14:51:11
Wait...we're requiring the entire body be passed i
xunjieli
2015/03/13 19:26:40
Yes, we require the entire body to be passed to us
mmenke
2015/03/25 18:34:57
So when uploading data, calling connect() breaks t
| |
| 115 } | |
| 116 return mInitialContentLength; | |
| 117 } | |
| 118 | |
| 119 @Override | |
| 120 public void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer) { | |
| 121 int toConsume = Math.min(byteBuffer.remaining(), | |
| 122 mBuffer.size() - mBytesConsumed); | |
| 123 if (toConsume > 0) { | |
| 124 byteBuffer.put(mBuffer.toByteArray(), mBytesConsumed, toConsume); | |
| 125 } | |
| 126 mBytesConsumed += toConsume; | |
| 127 uploadDataSink.onReadSucceeded(false); | |
| 128 } | |
| 129 | |
| 130 @Override | |
| 131 public void rewind(UploadDataSink uploadDataSink) { | |
| 132 mBytesConsumed = 0; | |
| 133 uploadDataSink.onRewindSucceeded(); | |
| 134 } | |
| 135 } | |
| OLD | NEW |