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.net.UploadDataProvider; | 7 import org.chromium.net.UploadDataProvider; |
8 import org.chromium.net.UploadDataSink; | 8 import org.chromium.net.UploadDataSink; |
9 | 9 |
10 import java.io.IOException; | 10 import java.io.IOException; |
(...skipping 11 matching lines...) Expand all Loading... | |
22 private final CronetHttpURLConnection mConnection; | 22 private final CronetHttpURLConnection mConnection; |
23 private final MessageLoop mMessageLoop; | 23 private final MessageLoop mMessageLoop; |
24 private final ByteBuffer mBuffer; | 24 private final ByteBuffer mBuffer; |
25 private final UploadDataProvider mUploadDataProvider = new UploadDataProvide rImpl(); | 25 private final UploadDataProvider mUploadDataProvider = new UploadDataProvide rImpl(); |
26 private long mBytesWritten; | 26 private long mBytesWritten; |
27 private boolean mLastChunk = false; | 27 private boolean mLastChunk = false; |
28 | 28 |
29 /** | 29 /** |
30 * Package protected constructor. | 30 * Package protected constructor. |
31 * @param connection The CronetHttpURLConnection object. | 31 * @param connection The CronetHttpURLConnection object. |
32 * @param contentLength The content length of the request body. Non-zero for | 32 * @param contentLength The content length of the request body. Non-zero for |
kapishnikov
2016/07/08 23:05:12
contentLength => chunkLength
xunjieli
2016/07/11 13:40:08
Done. Good catch!
| |
33 * non-chunked upload. | 33 * non-chunked upload. |
34 */ | 34 */ |
35 CronetChunkedOutputStream( | 35 CronetChunkedOutputStream( |
36 CronetHttpURLConnection connection, int chunkLength, MessageLoop mes sageLoop) { | 36 CronetHttpURLConnection connection, int chunkLength, MessageLoop mes sageLoop) { |
37 if (connection == null) { | 37 if (connection == null) { |
38 throw new NullPointerException(); | 38 throw new NullPointerException(); |
39 } | 39 } |
40 if (chunkLength <= 0) { | 40 if (chunkLength <= 0) { |
41 throw new IllegalArgumentException("chunkLength should be greater th an 0"); | 41 throw new IllegalArgumentException("chunkLength should be greater th an 0"); |
42 } | 42 } |
43 mBuffer = ByteBuffer.allocate(chunkLength); | 43 mBuffer = ByteBuffer.allocate(chunkLength); |
44 mConnection = connection; | 44 mConnection = connection; |
45 mMessageLoop = messageLoop; | 45 mMessageLoop = messageLoop; |
46 mBytesWritten = 0; | 46 mBytesWritten = 0; |
47 } | 47 } |
48 | 48 |
49 @Override | 49 @Override |
50 public void write(int oneByte) throws IOException { | 50 public void write(int oneByte) throws IOException { |
51 checkNotClosed(); | 51 ensureBufferHasRemaining(); |
52 while (mBuffer.position() == mBuffer.limit()) { | |
53 // Wait until buffer is consumed. | |
54 mMessageLoop.loop(); | |
55 } | |
56 mBuffer.put((byte) oneByte); | 52 mBuffer.put((byte) oneByte); |
57 mBytesWritten++; | 53 mBytesWritten++; |
58 } | 54 } |
59 | 55 |
60 @Override | 56 @Override |
61 public void write(byte[] buffer, int offset, int count) throws IOException { | 57 public void write(byte[] buffer, int offset, int count) throws IOException { |
62 checkNotClosed(); | 58 checkNotClosed(); |
63 if (buffer.length - offset < count || offset < 0 || count < 0) { | 59 if (buffer.length - offset < count || offset < 0 || count < 0) { |
64 throw new IndexOutOfBoundsException(); | 60 throw new IndexOutOfBoundsException(); |
65 } | 61 } |
66 if (count == 0) { | |
67 return; | |
68 } | |
69 int toSend = count; | 62 int toSend = count; |
70 // TODO(xunjieli): refactor commond code with CronetFixedModeOutputStrea m. | |
71 while (toSend > 0) { | 63 while (toSend > 0) { |
72 if (mBuffer.position() == mBuffer.limit()) { | 64 ensureBufferHasRemaining(); |
73 checkNotClosed(); | 65 int sent = Math.min(toSend, mBuffer.remaining()); |
kapishnikov
2016/07/08 23:05:12
If toSend == mBuffer.remaining(), is it going to w
xunjieli
2016/07/11 13:40:08
Done. That is a great suggestion. I agree that it
| |
74 // Wait until buffer is consumed. | |
75 mMessageLoop.loop(); | |
76 } | |
77 int sent = Math.min(toSend, mBuffer.limit() - mBuffer.position()); | |
78 mBuffer.put(buffer, offset + count - toSend, sent); | 66 mBuffer.put(buffer, offset + count - toSend, sent); |
79 toSend -= sent; | 67 toSend -= sent; |
80 } | 68 } |
81 mBytesWritten += count; | 69 mBytesWritten += count; |
82 } | 70 } |
83 | 71 |
84 @Override | 72 @Override |
85 public void close() throws IOException { | 73 public void close() throws IOException { |
86 super.close(); | 74 super.close(); |
87 // Last chunk is written. | 75 if (!mLastChunk) { |
kapishnikov
2016/07/08 23:05:12
Should we make it thread safe?
xunjieli
2016/07/11 13:40:08
HttpURLConnection is not thread-safe. The APIs are
| |
88 mLastChunk = true; | 76 // Consumer can only call close() when message loop is not running. |
77 // Set mLastChunk to be true and flip mBuffer to upload its contents . | |
78 mLastChunk = true; | |
79 mBuffer.flip(); | |
80 } | |
89 } | 81 } |
90 | 82 |
91 // Below are CronetOutputStream implementations: | 83 // Below are CronetOutputStream implementations: |
92 | 84 |
93 @Override | 85 @Override |
94 void setConnected() throws IOException { | 86 void setConnected() throws IOException { |
95 // Do nothing. | 87 // Do nothing. |
96 } | 88 } |
97 | 89 |
98 @Override | 90 @Override |
99 void checkReceivedEnoughContent() throws IOException { | 91 void checkReceivedEnoughContent() throws IOException { |
100 // Do nothing. | 92 // Do nothing. |
101 } | 93 } |
102 | 94 |
103 @Override | 95 @Override |
104 UploadDataProvider getUploadDataProvider() { | 96 UploadDataProvider getUploadDataProvider() { |
105 return mUploadDataProvider; | 97 return mUploadDataProvider; |
106 } | 98 } |
107 | 99 |
108 private class UploadDataProviderImpl extends UploadDataProvider { | 100 private class UploadDataProviderImpl extends UploadDataProvider { |
109 @Override | 101 @Override |
110 public long getLength() { | 102 public long getLength() { |
111 return -1; | 103 return -1; |
112 } | 104 } |
113 | 105 |
114 @Override | 106 @Override |
115 public void read(final UploadDataSink uploadDataSink, final ByteBuffer b yteBuffer) { | 107 public void read(final UploadDataSink uploadDataSink, final ByteBuffer b yteBuffer) { |
116 final int availableSpace = byteBuffer.remaining(); | 108 if (byteBuffer.remaining() >= mBuffer.remaining()) { |
xunjieli
2016/07/08 20:31:00
This is done to match the new code in CronetFixedM
| |
117 if (availableSpace < mBuffer.position()) { | |
118 // byteBuffer does not have enough capacity, so only put a porti on | |
119 // of mBuffer in it. | |
120 byteBuffer.put(mBuffer.array(), 0, availableSpace); | |
121 mBuffer.position(availableSpace); | |
122 // Move remaining buffer to the head of the buffer for use in th e | |
123 // next read call. | |
124 mBuffer.compact(); | |
125 uploadDataSink.onReadSucceeded(false); | |
126 } else { | |
127 // byteBuffer has enough capacity to hold the content of mBuffer . | |
128 mBuffer.flip(); | |
129 byteBuffer.put(mBuffer); | 109 byteBuffer.put(mBuffer); |
130 // Reuse this buffer. | |
131 mBuffer.clear(); | 110 mBuffer.clear(); |
132 uploadDataSink.onReadSucceeded(mLastChunk); | 111 uploadDataSink.onReadSucceeded(mLastChunk); |
133 if (!mLastChunk) { | 112 if (!mLastChunk) { |
134 // Quit message loop so embedder can write more data. | 113 // Quit message loop so embedder can write more data. |
135 mMessageLoop.quit(); | 114 mMessageLoop.quit(); |
136 } | 115 } |
116 } else { | |
117 int oldLimit = mBuffer.limit(); | |
118 mBuffer.limit(mBuffer.position() + byteBuffer.remaining()); | |
119 byteBuffer.put(mBuffer); | |
120 mBuffer.limit(oldLimit); | |
121 uploadDataSink.onReadSucceeded(false); | |
137 } | 122 } |
138 } | 123 } |
139 | 124 |
140 @Override | 125 @Override |
141 public void rewind(UploadDataSink uploadDataSink) { | 126 public void rewind(UploadDataSink uploadDataSink) { |
142 uploadDataSink.onRewindError( | 127 uploadDataSink.onRewindError( |
143 new HttpRetryException("Cannot retry streamed Http body", -1 )); | 128 new HttpRetryException("Cannot retry streamed Http body", -1 )); |
144 } | 129 } |
145 } | 130 } |
131 | |
132 /** | |
133 * If {@code mBuffer} is full, wait until it is consumed and there is | |
134 * space to write more data to it. | |
135 */ | |
136 private void ensureBufferHasRemaining() throws IOException { | |
137 if (!mBuffer.hasRemaining()) { | |
138 uploadBufferInternal(); | |
139 } | |
140 } | |
141 | |
142 /** | |
143 * Helper function to upload {@code mBuffer} to the native stack. This | |
144 * function blocks until {@code mBuffer} is consumed and there is space to | |
145 * write more data. | |
146 */ | |
147 private void uploadBufferInternal() throws IOException { | |
148 checkNotClosed(); | |
149 mBuffer.flip(); | |
150 mMessageLoop.loop(); | |
151 } | |
146 } | 152 } |
OLD | NEW |