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. | |
mmenke
2015/03/25 18:34:57
nit: or -> nor
xunjieli
2015/03/27 17:46:44
Done.
| |
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. | |
mmenke
2015/03/25 18:34:57
nit: Buffering. Last letter often isn't doubled
xunjieli
2015/03/27 17:46:44
Done. :)
| |
69 mBuffer = new ByteArrayOutputStream(); | |
70 mBytesConsumed = 0; | |
71 } | |
72 | |
73 @Override | |
74 public void write(int oneByte) throws IOException { | |
75 checkNotExceedContentLength(1); | |
mmenke
2015/03/25 18:34:57
Should we also check that the connection hasn't st
xunjieli
2015/03/27 17:46:44
Strangely the default implementation allows writin
| |
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 // TODO(xunjieli): implement close(). | |
94 | |
95 /** | |
96 * Throws {@link java.net.ProtocolException} if adding {@code numBytes} will | |
97 * exceed content length. | |
98 */ | |
99 private void checkNotExceedContentLength(int numBytes) throws ProtocolExcept ion { | |
100 if (mInitialContentLength != -1 | |
101 && mBuffer.size() + numBytes > mInitialContentLength) { | |
102 throw new ProtocolException("exceeded content-length limit of " | |
103 + mInitialContentLength + " bytes"); | |
104 } | |
105 } | |
106 | |
107 // Below are UploadDataProvider implementations. Only intended to be used | |
108 // within Cronet. | |
109 | |
110 @Override | |
111 public long getLength() { | |
112 // This method is supposed to be called just before starting the request . | |
113 // If content length is not initially passed in, the number of bytes | |
114 // written will be used as the content length. | |
115 if (mInitialContentLength == -1) { | |
116 return mBuffer.size(); | |
117 } | |
118 return mInitialContentLength; | |
119 } | |
120 | |
121 @Override | |
122 public void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer) { | |
123 int toConsume = Math.min(byteBuffer.remaining(), | |
124 mBuffer.size() - mBytesConsumed); | |
125 if (toConsume > 0) { | |
126 byteBuffer.put(mBuffer.toByteArray(), mBytesConsumed, toConsume); | |
mmenke
2015/03/25 18:34:57
The docs for toByteArray says it's a copy of the b
xunjieli
2015/03/27 17:46:44
Hmm.. Not sure if I understand, mBuffer is an Byte
mmenke
2015/03/27 17:54:10
Ahh, right. This is still a problem, since ByteAr
| |
127 } | |
128 mBytesConsumed += toConsume; | |
129 uploadDataSink.onReadSucceeded(false); | |
130 } | |
131 | |
132 @Override | |
133 public void rewind(UploadDataSink uploadDataSink) { | |
134 mBytesConsumed = 0; | |
135 uploadDataSink.onRewindSucceeded(); | |
136 } | |
137 } | |
OLD | NEW |