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