1 // Copyright 2014 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/base/chunked_upload_data_stream.h"
6
7 #include <memory>
8 #include <string>
9
10 #include "net/base/io_buffer.h"
11 #include "net/base/net_errors.h"
12 #include "net/base/test_completion_callback.h"
13 #include "net/base/upload_data_stream.h"
14 #include "net/log/net_log_with_source.h"
15 #include "net/test/gtest_util.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18
19 using net::test::IsError;
20 using net::test::IsOk;
21
22 namespace net {
23
24 namespace {
25
26 constexpr char kTestData[] = "0123456789";
27 constexpr size_t kTestDataSize = std::size(kTestData) - 1;
28 constexpr size_t kTestBufferSize = 1 << 14; // 16KB.
29
30 } // namespace
31
32 // Reads data once from the upload data stream, and returns the data as string.
33 // Expects the read to succeed synchronously.
ReadSync(UploadDataStream * stream,int buffer_size)34 std::string ReadSync(UploadDataStream* stream, int buffer_size) {
35 auto buf = base::MakeRefCounted<IOBufferWithSize>(buffer_size);
36 int result = stream->Read(buf.get(),
37 buffer_size,
38 TestCompletionCallback().callback());
39 EXPECT_GE(result, 0);
40 return std::string(buf->data(), result);
41 }
42
43 // Check the case data is added after the first read attempt.
TEST(ChunkedUploadDataStreamTest,AppendOnce)44 TEST(ChunkedUploadDataStreamTest, AppendOnce) {
45 ChunkedUploadDataStream stream(0);
46
47 ASSERT_THAT(
48 stream.Init(TestCompletionCallback().callback(), NetLogWithSource()),
49 IsOk());
50 EXPECT_FALSE(stream.IsInMemory());
51 EXPECT_EQ(0u, stream.size()); // Content-Length is 0 for chunked data.
52 EXPECT_EQ(0u, stream.position());
53 EXPECT_FALSE(stream.IsEOF());
54
55 TestCompletionCallback callback;
56 auto buf = base::MakeRefCounted<IOBufferWithSize>(kTestBufferSize);
57 int result = stream.Read(buf.get(), kTestBufferSize, callback.callback());
58 ASSERT_THAT(result, IsError(ERR_IO_PENDING));
59
60 stream.AppendData(kTestData, kTestDataSize, true);
61 int read = callback.WaitForResult();
62 ASSERT_GE(read, 0);
63 EXPECT_EQ(kTestData, std::string(buf->data(), read));
64 EXPECT_EQ(0u, stream.size()); // Content-Length is 0 for chunked data.
65 EXPECT_EQ(kTestDataSize, stream.position());
66 EXPECT_TRUE(stream.IsEOF());
67 }
68
TEST(ChunkedUploadDataStreamTest,AppendOnceBeforeRead)69 TEST(ChunkedUploadDataStreamTest, AppendOnceBeforeRead) {
70 ChunkedUploadDataStream stream(0);
71
72 ASSERT_THAT(
73 stream.Init(TestCompletionCallback().callback(), NetLogWithSource()),
74 IsOk());
75 EXPECT_FALSE(stream.IsInMemory());
76 EXPECT_EQ(0u, stream.size()); // Content-Length is 0 for chunked data.
77 EXPECT_EQ(0u, stream.position());
78 EXPECT_FALSE(stream.IsEOF());
79
80 stream.AppendData(kTestData, kTestDataSize, true);
81 EXPECT_EQ(0u, stream.size()); // Content-Length is 0 for chunked data.
82 EXPECT_EQ(0u, stream.position());
83 EXPECT_FALSE(stream.IsEOF());
84
85 std::string data = ReadSync(&stream, kTestBufferSize);
86 EXPECT_EQ(kTestData, data);
87 EXPECT_EQ(0u, stream.size()); // Content-Length is 0 for chunked data.
88 EXPECT_EQ(kTestDataSize, stream.position());
89 EXPECT_TRUE(stream.IsEOF());
90 }
91
TEST(ChunkedUploadDataStreamTest,AppendOnceBeforeInit)92 TEST(ChunkedUploadDataStreamTest, AppendOnceBeforeInit) {
93 ChunkedUploadDataStream stream(0);
94
95 stream.AppendData(kTestData, kTestDataSize, true);
96 ASSERT_THAT(
97 stream.Init(TestCompletionCallback().callback(), NetLogWithSource()),
98 IsOk());
99 EXPECT_FALSE(stream.IsInMemory());
100 EXPECT_EQ(0u, stream.size()); // Content-Length is 0 for chunked data.
101 EXPECT_EQ(0u, stream.position());
102 EXPECT_FALSE(stream.IsEOF());
103
104 std::string data = ReadSync(&stream, kTestBufferSize);
105 EXPECT_EQ(kTestData, data);
106 EXPECT_EQ(0u, stream.size()); // Content-Length is 0 for chunked data.
107 EXPECT_EQ(kTestDataSize, stream.position());
108 EXPECT_TRUE(stream.IsEOF());
109 }
110
TEST(ChunkedUploadDataStreamTest,MultipleAppends)111 TEST(ChunkedUploadDataStreamTest, MultipleAppends) {
112 ChunkedUploadDataStream stream(0);
113
114 ASSERT_THAT(
115 stream.Init(TestCompletionCallback().callback(), NetLogWithSource()),
116 IsOk());
117 EXPECT_FALSE(stream.IsInMemory());
118 EXPECT_EQ(0u, stream.size());
119 EXPECT_EQ(0u, stream.position());
120 EXPECT_FALSE(stream.IsEOF());
121
122 TestCompletionCallback callback;
123 auto buf = base::MakeRefCounted<IOBufferWithSize>(kTestBufferSize);
124 for (size_t i = 0; i < kTestDataSize; ++i) {
125 EXPECT_EQ(0u, stream.size()); // Content-Length is 0 for chunked data.
126 EXPECT_EQ(i, stream.position());
127 ASSERT_FALSE(stream.IsEOF());
128 int bytes_read = stream.Read(buf.get(),
129 kTestBufferSize,
130 callback.callback());
131 ASSERT_THAT(bytes_read, IsError(ERR_IO_PENDING));
132 stream.AppendData(&kTestData[i], 1, i == kTestDataSize - 1);
133 ASSERT_EQ(1, callback.WaitForResult());
134 EXPECT_EQ(kTestData[i], buf->data()[0]);
135 }
136
137 EXPECT_EQ(0u, stream.size()); // Content-Length is 0 for chunked data.
138 EXPECT_EQ(kTestDataSize, stream.position());
139 ASSERT_TRUE(stream.IsEOF());
140 }
141
TEST(ChunkedUploadDataStreamTest,MultipleAppendsBetweenReads)142 TEST(ChunkedUploadDataStreamTest, MultipleAppendsBetweenReads) {
143 ChunkedUploadDataStream stream(0);
144
145 ASSERT_THAT(
146 stream.Init(TestCompletionCallback().callback(), NetLogWithSource()),
147 IsOk());
148 EXPECT_FALSE(stream.IsInMemory());
149 EXPECT_EQ(0u, stream.size()); // Content-Length is 0 for chunked data.
150 EXPECT_EQ(0u, stream.position());
151 EXPECT_FALSE(stream.IsEOF());
152
153 auto buf = base::MakeRefCounted<IOBufferWithSize>(kTestBufferSize);
154 for (size_t i = 0; i < kTestDataSize; ++i) {
155 EXPECT_EQ(i, stream.position());
156 ASSERT_FALSE(stream.IsEOF());
157 stream.AppendData(&kTestData[i], 1, i == kTestDataSize - 1);
158 int bytes_read = stream.Read(buf.get(),
159 kTestBufferSize,
160 TestCompletionCallback().callback());
161 ASSERT_EQ(1, bytes_read);
162 EXPECT_EQ(kTestData[i], buf->data()[0]);
163 }
164
165 EXPECT_EQ(kTestDataSize, stream.position());
166 ASSERT_TRUE(stream.IsEOF());
167 }
168
169 // Checks that multiple reads can be merged.
TEST(ChunkedUploadDataStreamTest,MultipleAppendsBeforeInit)170 TEST(ChunkedUploadDataStreamTest, MultipleAppendsBeforeInit) {
171 ChunkedUploadDataStream stream(0);
172 stream.AppendData(kTestData, 1, false);
173 stream.AppendData(kTestData + 1, 1, false);
174 stream.AppendData(kTestData + 2, kTestDataSize - 2, true);
175
176 ASSERT_THAT(
177 stream.Init(TestCompletionCallback().callback(), NetLogWithSource()),
178 IsOk());
179 EXPECT_FALSE(stream.IsInMemory());
180 EXPECT_EQ(0u, stream.size()); // Content-Length is 0 for chunked data.
181 EXPECT_EQ(0u, stream.position());
182 EXPECT_FALSE(stream.IsEOF());
183
184 std::string data = ReadSync(&stream, kTestBufferSize);
185 EXPECT_EQ(kTestData, data);
186 EXPECT_EQ(kTestDataSize, stream.position());
187 ASSERT_TRUE(stream.IsEOF());
188 }
189
TEST(ChunkedUploadDataStreamTest,MultipleReads)190 TEST(ChunkedUploadDataStreamTest, MultipleReads) {
191 // Use a read size different from the write size to test bounds checking.
192 const size_t kReadSize = kTestDataSize + 3;
193
194 ChunkedUploadDataStream stream(0);
195 stream.AppendData(kTestData, kTestDataSize, false);
196 stream.AppendData(kTestData, kTestDataSize, false);
197 stream.AppendData(kTestData, kTestDataSize, false);
198 stream.AppendData(kTestData, kTestDataSize, true);
199
200 ASSERT_THAT(
201 stream.Init(TestCompletionCallback().callback(), NetLogWithSource()),
202 IsOk());
203 EXPECT_FALSE(stream.IsInMemory());
204 EXPECT_EQ(0u, stream.size()); // Content-Length is 0 for chunked data.
205 EXPECT_EQ(0u, stream.position());
206 EXPECT_FALSE(stream.IsEOF());
207
208 std::string data = ReadSync(&stream, kReadSize);
209 EXPECT_EQ("0123456789012", data);
210 EXPECT_EQ(kReadSize, stream.position());
211 EXPECT_FALSE(stream.IsEOF());
212
213 data = ReadSync(&stream, kReadSize);
214 EXPECT_EQ("3456789012345", data);
215 EXPECT_EQ(2 * kReadSize, stream.position());
216 EXPECT_FALSE(stream.IsEOF());
217
218 data = ReadSync(&stream, kReadSize);
219 EXPECT_EQ("6789012345678", data);
220 EXPECT_EQ(3 * kReadSize, stream.position());
221 EXPECT_FALSE(stream.IsEOF());
222
223 data = ReadSync(&stream, kReadSize);
224 EXPECT_EQ("9", data);
225 EXPECT_EQ(4 * kTestDataSize, stream.position());
226 EXPECT_TRUE(stream.IsEOF());
227 }
228
TEST(ChunkedUploadDataStreamTest,EmptyUpload)229 TEST(ChunkedUploadDataStreamTest, EmptyUpload) {
230 ChunkedUploadDataStream stream(0);
231
232 ASSERT_THAT(
233 stream.Init(TestCompletionCallback().callback(), NetLogWithSource()),
234 IsOk());
235 EXPECT_FALSE(stream.IsInMemory());
236 EXPECT_EQ(0u, stream.size()); // Content-Length is 0 for chunked data.
237 EXPECT_EQ(0u, stream.position());
238 EXPECT_FALSE(stream.IsEOF());
239
240 TestCompletionCallback callback;
241 auto buf = base::MakeRefCounted<IOBufferWithSize>(kTestBufferSize);
242 int result = stream.Read(buf.get(), kTestBufferSize, callback.callback());
243 ASSERT_THAT(result, IsError(ERR_IO_PENDING));
244
245 stream.AppendData(nullptr, 0, true);
246 int read = callback.WaitForResult();
247 EXPECT_EQ(0, read);
248 EXPECT_EQ(0u, stream.position());
249 EXPECT_TRUE(stream.IsEOF());
250 }
251
TEST(ChunkedUploadDataStreamTest,EmptyUploadEndedBeforeInit)252 TEST(ChunkedUploadDataStreamTest, EmptyUploadEndedBeforeInit) {
253 ChunkedUploadDataStream stream(0);
254 stream.AppendData(nullptr, 0, true);
255
256 ASSERT_THAT(
257 stream.Init(TestCompletionCallback().callback(), NetLogWithSource()),
258 IsOk());
259 EXPECT_FALSE(stream.IsInMemory());
260 EXPECT_EQ(0u, stream.size()); // Content-Length is 0 for chunked data.
261 EXPECT_EQ(0u, stream.position());
262 EXPECT_FALSE(stream.IsEOF());
263
264 std::string data = ReadSync(&stream, kTestBufferSize);
265 ASSERT_EQ("", data);
266 EXPECT_EQ(0u, stream.position());
267 EXPECT_TRUE(stream.IsEOF());
268 }
269
TEST(ChunkedUploadDataStreamTest,RewindAfterComplete)270 TEST(ChunkedUploadDataStreamTest, RewindAfterComplete) {
271 ChunkedUploadDataStream stream(0);
272 stream.AppendData(kTestData, 1, false);
273 stream.AppendData(kTestData + 1, kTestDataSize - 1, true);
274
275 ASSERT_THAT(
276 stream.Init(TestCompletionCallback().callback(), NetLogWithSource()),
277 IsOk());
278 EXPECT_FALSE(stream.IsInMemory());
279 EXPECT_EQ(0u, stream.size()); // Content-Length is 0 for chunked data.
280 EXPECT_EQ(0u, stream.position());
281 EXPECT_FALSE(stream.IsEOF());
282
283 std::string data = ReadSync(&stream, kTestBufferSize);
284 EXPECT_EQ(kTestData, data);
285 EXPECT_EQ(kTestDataSize, stream.position());
286 ASSERT_TRUE(stream.IsEOF());
287
288 // Rewind stream and repeat.
289 ASSERT_THAT(
290 stream.Init(TestCompletionCallback().callback(), NetLogWithSource()),
291 IsOk());
292 EXPECT_FALSE(stream.IsInMemory());
293 EXPECT_EQ(0u, stream.size()); // Content-Length is 0 for chunked data.
294 EXPECT_EQ(0u, stream.position());
295 EXPECT_FALSE(stream.IsEOF());
296
297 data = ReadSync(&stream, kTestBufferSize);
298 EXPECT_EQ(kTestData, data);
299 EXPECT_EQ(kTestDataSize, stream.position());
300 ASSERT_TRUE(stream.IsEOF());
301 }
302
TEST(ChunkedUploadDataStreamTest,RewindWhileReading)303 TEST(ChunkedUploadDataStreamTest, RewindWhileReading) {
304 ChunkedUploadDataStream stream(0);
305
306 ASSERT_THAT(
307 stream.Init(TestCompletionCallback().callback(), NetLogWithSource()),
308 IsOk());
309 EXPECT_FALSE(stream.IsInMemory());
310 EXPECT_EQ(0u, stream.size()); // Content-Length is 0 for chunked data.
311 EXPECT_EQ(0u, stream.position());
312 EXPECT_FALSE(stream.IsEOF());
313
314 TestCompletionCallback callback;
315 auto buf = base::MakeRefCounted<IOBufferWithSize>(kTestBufferSize);
316 int result = stream.Read(buf.get(), kTestBufferSize, callback.callback());
317 ASSERT_THAT(result, IsError(ERR_IO_PENDING));
318
319 ASSERT_THAT(
320 stream.Init(TestCompletionCallback().callback(), NetLogWithSource()),
321 IsOk());
322 EXPECT_FALSE(stream.IsInMemory());
323 EXPECT_EQ(0u, stream.size()); // Content-Length is 0 for chunked data.
324 EXPECT_EQ(0u, stream.position());
325 EXPECT_FALSE(stream.IsEOF());
326
327 // Adding data now should not result in calling the original read callback,
328 // since the stream was re-initialized for reuse, which cancels all pending
329 // reads.
330 stream.AppendData(kTestData, kTestDataSize, true);
331 EXPECT_FALSE(callback.have_result());
332
333 std::string data = ReadSync(&stream, kTestBufferSize);
334 EXPECT_EQ(kTestData, data);
335 EXPECT_EQ(kTestDataSize, stream.position());
336 ASSERT_TRUE(stream.IsEOF());
337 EXPECT_FALSE(callback.have_result());
338 }
339
340 // Check the behavior of ChunkedUploadDataStream::Writer.
TEST(ChunkedUploadDataStreamTest,ChunkedUploadDataStreamWriter)341 TEST(ChunkedUploadDataStreamTest, ChunkedUploadDataStreamWriter) {
342 auto stream = std::make_unique<ChunkedUploadDataStream>(0);
343 std::unique_ptr<ChunkedUploadDataStream::Writer> writer(
344 stream->CreateWriter());
345
346 // Write before Init.
347 ASSERT_TRUE(writer->AppendData(kTestData, 1, false));
348 ASSERT_THAT(
349 stream->Init(TestCompletionCallback().callback(), NetLogWithSource()),
350 IsOk());
351
352 // Write after Init.
353 ASSERT_TRUE(writer->AppendData(kTestData + 1, kTestDataSize - 1, false));
354
355 TestCompletionCallback callback;
356 std::string data = ReadSync(stream.get(), kTestBufferSize);
357 EXPECT_EQ(kTestData, data);
358
359 // Writing data should gracefully fail if the stream is deleted while still
360 // appending data to it.
361 stream.reset();
362 EXPECT_FALSE(writer->AppendData(kTestData, kTestDataSize, true));
363 }
364
365 } // namespace net
366