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

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

Issue 966743003: [Cronet] Implement getOutputStream in CronetHttpURLConnection (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@chunked_support
Patch Set: Test write after connect for fixed length streaming mode Created 5 years, 8 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
(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.IOException;
11 import java.io.OutputStream;
12 import java.net.ProtocolException;
13 import java.nio.ByteBuffer;
14
15 /**
16 * An implementation of {@link java.io.OutputStream} that buffers entire request
17 * body in memory. This is used when neither
18 * {@link CronetHttpURLConnection#setFixedLengthStreamingMode}
19 * nor {@link CronetHttpURLConnection#setChunkedStreamingMode} is set.
20 */
21 final class CronetBufferedOutputStream extends OutputStream
22 implements UploadDataProvider {
23 // If content length is not passed in the constructor, this is -1.
24 private final int mInitialContentLength;
25 private final CronetHttpURLConnection mConnection;
26 // Internal buffer that is used to buffer the request body.
27 private ByteBuffer mBuffer;
28 private boolean mConnected = false;
29 private boolean mFirstReadInitiated = 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();
mef 2015/04/06 16:14:54 add error message?
xunjieli 2015/04/06 18:09:29 Done.
41 }
42
43 if (contentLength > Integer.MAX_VALUE) {
44 throw new IllegalStateException("Use setFixedLengthStreamingMode()"
mef 2015/04/06 16:14:54 I would throw IllegalArgumentException here for co
xunjieli 2015/04/06 18:09:29 Done.
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(2048);
mef 2015/04/06 16:14:54 Define 2048 as constant? While there, should it be
xunjieli 2015/04/06 18:09:29 Hmm.. 2048 isn't used anywhere else in this file,
mef 2015/04/06 18:37:18 16k sgtm. My concern is the need for reallocs/copi
xunjieli 2015/04/06 21:03:45 Done. I don't know which one is better. I guess we
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 /**
83 * Sets {@link #mConnected} to {@code true}.
84 */
85 void setConnected() throws IOException {
86 mConnected = true;
87 if (mBuffer.position() < mInitialContentLength) {
88 throw new ProtocolException("Content received is less than Content-L ength");
89 }
90 // Flip the buffer to prepare it for UploadDataProvider read calls.
91 mBuffer.flip();
92 }
93
94 // TODO(xunjieli): implement close().
95
96 /**
97 * Ensures that {@code count} bytes can be written to the internal buffer.
98 */
99 private void ensureCanWrite(int count) throws IOException {
100 if (mInitialContentLength != -1
101 && mBuffer.position() + count > mInitialContentLength) {
102 // Error message is to match that of the default implementation.
103 throw new ProtocolException("exceeded content-length limit of "
104 + mInitialContentLength + " bytes");
105 }
106 if (mConnected) {
107 throw new IllegalStateException("Cannot write after being connected. ");
108 }
109 if (mInitialContentLength != -1 || mBuffer.limit() - mBuffer.position() > count) {
mef 2015/04/06 16:14:54 Can we split it into 2 separate checks? Also mayb
xunjieli 2015/04/06 18:09:29 Done.
110 // If mInitialContentLength is known or there is enough capacity,
111 // the buffer should not grow.
112 return;
113 }
114 int afterSize = Math.max(mBuffer.capacity() * 2, mBuffer.capacity() + co unt);
115 ByteBuffer newByteBuffer = ByteBuffer.allocate(afterSize);
116 mBuffer.flip();
117 newByteBuffer.put(mBuffer);
118 mBuffer = newByteBuffer;
119 }
120
121 // Below are UploadDataProvider implementations. Only intended to be used
122 // within Cronet.
123
124 @Override
125 public long getLength() {
126 // This method is supposed to be called just before starting the request .
127 // If content length is not initially passed in, the number of bytes
128 // written will be used as the content length.
129 if (mInitialContentLength == -1) {
130 return mBuffer.position();
mef 2015/04/06 16:14:54 hrm, this will be 0 if rewind() is called.
xunjieli 2015/04/06 18:09:29 getLength() is called in CronetUploadDataStream's
mef 2015/04/06 18:37:18 My concern here is fragility, few years from now s
xunjieli 2015/04/06 21:03:45 If we have a buffer of 2048 bytes, and we write 18
131 }
132 return mInitialContentLength;
133 }
134
135 @Override
136 public void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer) {
137 int oldPos = byteBuffer.position();
138 int availableSpace = byteBuffer.capacity() - byteBuffer.position();
mef 2015/04/06 16:14:54 Maybe rename availableSpace => readSize?
xunjieli 2015/04/06 18:09:29 It's actually not the read size, since the else cl
mef 2015/04/06 18:37:18 ok
139 if (availableSpace < mBuffer.limit() - mBuffer.position()) {
140 byteBuffer.put(mBuffer.array(), mBuffer.position(), availableSpace);
141 mBuffer.position(mBuffer.position() + availableSpace);
142 } else {
143 byteBuffer.put(mBuffer);
144 }
145 uploadDataSink.onReadSucceeded(false);
146 }
147
148 @Override
149 public void rewind(UploadDataSink uploadDataSink) {
150 mBuffer.position(0);
151 uploadDataSink.onRewindSucceeded();
152 }
153 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698