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