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