| 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.UploadDataSink; | 
|  | 8 | 
|  | 9 import java.io.IOException; | 
|  | 10 import java.net.ProtocolException; | 
|  | 11 import java.nio.ByteBuffer; | 
|  | 12 | 
|  | 13 /** | 
|  | 14  * An implementation of {@link java.io.OutputStream} that buffers entire request | 
|  | 15  * body in memory. This is used when neither | 
|  | 16  * {@link CronetHttpURLConnection#setFixedLengthStreamingMode} | 
|  | 17  * nor {@link CronetHttpURLConnection#setChunkedStreamingMode} is set. | 
|  | 18  */ | 
|  | 19 final class CronetBufferedOutputStream extends CronetOutputStream { | 
|  | 20     // QUIC uses a read buffer of 14520 bytes, SPDY uses 2852 bytes, and normal | 
|  | 21     // stream uses 16384 bytes. Therefore, use 16384 for now to avoid growing | 
|  | 22     // the buffer too many times. | 
|  | 23     private static final int INITIAL_BUFFER_SIZE = 16384; | 
|  | 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 ByteBuffer mBuffer; | 
|  | 29     private boolean mConnected = false; | 
|  | 30 | 
|  | 31     /** | 
|  | 32      * Package protected constructor. | 
|  | 33      * @param connection The CronetHttpURLConnection object. | 
|  | 34      * @param contentLength The content length of the request body. It must not | 
|  | 35      *            be smaller than 0 or bigger than {@link Integer.MAX_VALUE}. | 
|  | 36      */ | 
|  | 37     CronetBufferedOutputStream(final CronetHttpURLConnection connection, | 
|  | 38             final long contentLength) { | 
|  | 39         if (connection == null) { | 
|  | 40             throw new NullPointerException("Argument connection cannot be null."
     ); | 
|  | 41         } | 
|  | 42 | 
|  | 43         if (contentLength > Integer.MAX_VALUE) { | 
|  | 44             throw new IllegalArgumentException("Use setFixedLengthStreamingMode(
     )" | 
|  | 45                 + " or setChunkedStreamingMode() for requests larger than 2GB.")
     ; | 
|  | 46         } | 
|  | 47         if (contentLength < 0) { | 
|  | 48             throw new IllegalArgumentException("Content length < 0."); | 
|  | 49         } | 
|  | 50         mConnection = connection; | 
|  | 51         mInitialContentLength = (int) contentLength; | 
|  | 52         mBuffer = ByteBuffer.allocate(mInitialContentLength); | 
|  | 53     } | 
|  | 54 | 
|  | 55     /** | 
|  | 56      * Package protected constructor used when content length is not known. | 
|  | 57      * @param connection The CronetHttpURLConnection object. | 
|  | 58      */ | 
|  | 59     CronetBufferedOutputStream(final CronetHttpURLConnection connection) { | 
|  | 60         if (connection == null) { | 
|  | 61             throw new NullPointerException(); | 
|  | 62         } | 
|  | 63 | 
|  | 64         mConnection = connection; | 
|  | 65         mInitialContentLength = -1; | 
|  | 66         // Buffering without knowing content-length. | 
|  | 67         mBuffer = ByteBuffer.allocate(INITIAL_BUFFER_SIZE); | 
|  | 68     } | 
|  | 69 | 
|  | 70     @Override | 
|  | 71     public void write(int oneByte) throws IOException { | 
|  | 72         ensureCanWrite(1); | 
|  | 73         mBuffer.put((byte) oneByte); | 
|  | 74     } | 
|  | 75 | 
|  | 76     @Override | 
|  | 77     public void write(byte[] buffer, int offset, int count) throws IOException { | 
|  | 78         ensureCanWrite(count); | 
|  | 79         mBuffer.put(buffer, offset, count); | 
|  | 80     } | 
|  | 81 | 
|  | 82     // TODO(xunjieli): implement close(). | 
|  | 83 | 
|  | 84     /** | 
|  | 85      * Ensures that {@code count} bytes can be written to the internal buffer. | 
|  | 86      */ | 
|  | 87     private void ensureCanWrite(int count) throws IOException { | 
|  | 88         if (mInitialContentLength != -1 | 
|  | 89                 && mBuffer.position() + count > mInitialContentLength) { | 
|  | 90             // Error message is to match that of the default implementation. | 
|  | 91             throw new ProtocolException("exceeded content-length limit of " | 
|  | 92                     + mInitialContentLength + " bytes"); | 
|  | 93         } | 
|  | 94         if (mConnected) { | 
|  | 95             throw new IllegalStateException("Cannot write after being connected.
     "); | 
|  | 96         } | 
|  | 97         if (mInitialContentLength != -1) { | 
|  | 98             // If mInitialContentLength is known, the buffer should not grow. | 
|  | 99             return; | 
|  | 100         } | 
|  | 101         if (mBuffer.limit() - mBuffer.position() > count) { | 
|  | 102             // If there is enough capacity, the buffer should not grow. | 
|  | 103             return; | 
|  | 104         } | 
|  | 105         int afterSize = Math.max(mBuffer.capacity() * 2, mBuffer.capacity() + co
     unt); | 
|  | 106         ByteBuffer newByteBuffer = ByteBuffer.allocate(afterSize); | 
|  | 107         mBuffer.flip(); | 
|  | 108         newByteBuffer.put(mBuffer); | 
|  | 109         mBuffer = newByteBuffer; | 
|  | 110     } | 
|  | 111 | 
|  | 112     // Below are CronetOutputStream implementations: | 
|  | 113 | 
|  | 114     /** | 
|  | 115      * Sets {@link #mConnected} to {@code true}. | 
|  | 116      */ | 
|  | 117     @Override | 
|  | 118     void setConnected() throws IOException { | 
|  | 119         mConnected = true; | 
|  | 120         if (mBuffer.position() < mInitialContentLength) { | 
|  | 121             throw new ProtocolException("Content received is less than Content-L
     ength"); | 
|  | 122         } | 
|  | 123         // Flip the buffer to prepare it for UploadDataProvider read calls. | 
|  | 124         mBuffer.flip(); | 
|  | 125     } | 
|  | 126 | 
|  | 127     @Override | 
|  | 128     void checkReceivedEnoughContent() throws IOException { | 
|  | 129         // Already checked in setConnected. Skip the check here, since mBuffer | 
|  | 130         // might be flipped. | 
|  | 131     } | 
|  | 132 | 
|  | 133     @Override | 
|  | 134     public long getLength() { | 
|  | 135         // This method is supposed to be called just before starting the request
     . | 
|  | 136         // If content length is not initially passed in, the number of bytes | 
|  | 137         // written will be used as the content length. | 
|  | 138         // TODO(xunjieli): Think of a less fragile way, since getLength() can be | 
|  | 139         // potentially called in other places in the future. | 
|  | 140         if (mInitialContentLength == -1) { | 
|  | 141             return mBuffer.position(); | 
|  | 142         } | 
|  | 143         return mInitialContentLength; | 
|  | 144     } | 
|  | 145 | 
|  | 146     @Override | 
|  | 147     public void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer) { | 
|  | 148         int availableSpace = byteBuffer.capacity() - byteBuffer.position(); | 
|  | 149         if (availableSpace  < mBuffer.limit() - mBuffer.position()) { | 
|  | 150             byteBuffer.put(mBuffer.array(), mBuffer.position(), availableSpace); | 
|  | 151             mBuffer.position(mBuffer.position() + availableSpace); | 
|  | 152         } else { | 
|  | 153             byteBuffer.put(mBuffer); | 
|  | 154         } | 
|  | 155         uploadDataSink.onReadSucceeded(false); | 
|  | 156     } | 
|  | 157 | 
|  | 158     @Override | 
|  | 159     public void rewind(UploadDataSink uploadDataSink) { | 
|  | 160         mBuffer.position(0); | 
|  | 161         uploadDataSink.onRewindSucceeded(); | 
|  | 162     } | 
|  | 163 } | 
| OLD | NEW | 
|---|