Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(817)

Side by Side Diff: content/child/multipart_response_delegate_unittest.cc

Issue 1710733002: Move multipart resource handling to core/fetch (2/2) (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@multipart-cleanup
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « content/child/multipart_response_delegate.cc ('k') | content/child/web_url_loader_impl.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 "content/child/multipart_response_delegate.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <vector>
11
12 #include "base/macros.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14 #include "third_party/WebKit/public/platform/WebString.h"
15 #include "third_party/WebKit/public/platform/WebURL.h"
16 #include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
17 #include "third_party/WebKit/public/platform/WebURLResponse.h"
18
19 using blink::WebString;
20 using blink::WebURL;
21 using blink::WebURLError;
22 using blink::WebURLLoader;
23 using blink::WebURLLoaderClient;
24 using blink::WebURLRequest;
25 using blink::WebURLResponse;
26 using std::string;
27
28 namespace content {
29
30 class MultipartResponseDelegateTester {
31 public:
32 MultipartResponseDelegateTester(MultipartResponseDelegate* delegate)
33 : delegate_(delegate) {
34 }
35
36 int PushOverLine(const std::string& data, size_t pos) {
37 return delegate_->PushOverLine(data, pos);
38 }
39
40 bool ParseHeaders() { return delegate_->ParseHeaders(); }
41 size_t FindBoundary() { return delegate_->FindBoundary(); }
42 std::string& boundary() { return delegate_->boundary_; }
43 std::string& data() { return delegate_->data_; }
44
45 private:
46 MultipartResponseDelegate* delegate_;
47 };
48
49 namespace {
50
51 class MultipartResponseTest : public testing::Test {
52 };
53
54 class MockWebURLLoaderClient : public WebURLLoaderClient {
55 public:
56 MockWebURLLoaderClient() { Reset(); }
57
58 void willFollowRedirect(WebURLLoader*,
59 WebURLRequest&,
60 const WebURLResponse&) override {}
61 void didSendData(WebURLLoader*,
62 unsigned long long,
63 unsigned long long) override {}
64
65 void didReceiveResponse(WebURLLoader* loader,
66 const WebURLResponse& response) override {
67 ++received_response_;
68 response_ = response;
69 data_.clear();
70 }
71 void didReceiveData(blink::WebURLLoader* loader,
72 const char* data,
73 int data_length,
74 int encoded_data_length) override {
75 ++received_data_;
76 data_.append(data, data_length);
77 total_encoded_data_length_ += encoded_data_length;
78 }
79 void didFinishLoading(WebURLLoader*,
80 double finishTime,
81 int64_t total_encoded_data_length) override {}
82 void didFail(WebURLLoader*, const WebURLError&) override {}
83
84 void Reset() {
85 received_response_ = received_data_ = total_encoded_data_length_ = 0;
86 data_.clear();
87 response_.reset();
88 }
89
90 string GetResponseHeader(const char* name) const {
91 return string(response_.httpHeaderField(WebString::fromUTF8(name)).utf8());
92 }
93
94 int received_response_, received_data_, total_encoded_data_length_;
95 string data_;
96 WebURLResponse response_;
97 };
98
99 // We can't put this in an anonymous function because it's a friend class for
100 // access to private members.
101 TEST(MultipartResponseTest, Functions) {
102 // PushOverLine tests
103
104 WebURLResponse response;
105 response.initialize();
106 response.setMIMEType("multipart/x-mixed-replace");
107 response.setHTTPHeaderField("Foo", "Bar");
108 response.setHTTPHeaderField("Content-type", "text/plain");
109 MockWebURLLoaderClient client;
110 MultipartResponseDelegate delegate(&client, NULL, response, "bound");
111 MultipartResponseDelegateTester delegate_tester(&delegate);
112
113 struct {
114 const char* input;
115 const int position;
116 const int expected;
117 } line_tests[] = {
118 { "Line", 0, 0 },
119 { "Line", 2, 0 },
120 { "Line", 10, 0 },
121 { "\r\nLine", 0, 2 },
122 { "\nLine", 0, 1 },
123 { "\n\nLine", 0, 2 },
124 { "\rLine", 0, 1 },
125 { "Line\r\nLine", 4, 2 },
126 { "Line\nLine", 4, 1 },
127 { "Line\n\nLine", 4, 2 },
128 { "Line\rLine", 4, 1 },
129 { "Line\r\rLine", 4, 1 },
130 };
131 for (size_t i = 0; i < arraysize(line_tests); ++i) {
132 EXPECT_EQ(line_tests[i].expected,
133 delegate_tester.PushOverLine(line_tests[i].input,
134 line_tests[i].position));
135 }
136
137 // ParseHeaders tests
138 struct {
139 const char* data;
140 const bool rv;
141 const int received_response_calls;
142 const char* newdata;
143 } header_tests[] = {
144 { "This is junk", false, 0, "This is junk" },
145 { "Foo: bar\nBaz:\n\nAfter:\n", true, 1, "After:\n" },
146 { "Foo: bar\nBaz:\n", false, 0, "Foo: bar\nBaz:\n" },
147 { "Foo: bar\r\nBaz:\r\n\r\nAfter:\r\n", true, 1, "After:\r\n" },
148 { "Foo: bar\r\nBaz:\r\n", false, 0, "Foo: bar\r\nBaz:\r\n" },
149 { "Foo: bar\nBaz:\r\n\r\nAfter:\n\n", true, 1, "After:\n\n" },
150 { "Foo: bar\r\nBaz:\n", false, 0, "Foo: bar\r\nBaz:\n" },
151 { "\r\n", true, 1, "" },
152 };
153 for (size_t i = 0; i < arraysize(header_tests); ++i) {
154 client.Reset();
155 delegate_tester.data().assign(header_tests[i].data);
156 EXPECT_EQ(header_tests[i].rv,
157 delegate_tester.ParseHeaders());
158 EXPECT_EQ(header_tests[i].received_response_calls,
159 client.received_response_);
160 EXPECT_EQ(string(header_tests[i].newdata),
161 delegate_tester.data());
162 }
163 // Test that the resource response is filled in correctly when parsing
164 // headers.
165 client.Reset();
166 string test_header("content-type: image/png\ncontent-length: 10\n\n");
167 delegate_tester.data().assign(test_header);
168 EXPECT_TRUE(delegate_tester.ParseHeaders());
169 EXPECT_TRUE(delegate_tester.data().length() == 0);
170 EXPECT_EQ(string("image/png"), client.GetResponseHeader("Content-Type"));
171 EXPECT_EQ(string("10"), client.GetResponseHeader("content-length"));
172 // This header is passed from the original request.
173 EXPECT_EQ(string("Bar"), client.GetResponseHeader("foo"));
174
175 // Make sure we parse the right mime-type if a charset is provided.
176 client.Reset();
177 string test_header2("content-type: text/html; charset=utf-8\n\n");
178 delegate_tester.data().assign(test_header2);
179 EXPECT_TRUE(delegate_tester.ParseHeaders());
180 EXPECT_TRUE(delegate_tester.data().length() == 0);
181 EXPECT_EQ(string("text/html; charset=utf-8"),
182 client.GetResponseHeader("Content-Type"));
183 EXPECT_EQ(string("utf-8"),
184 string(client.response_.textEncodingName().utf8()));
185
186 // FindBoundary tests
187 struct {
188 const char* boundary;
189 const char* data;
190 const size_t position;
191 } boundary_tests[] = {
192 { "bound", "bound", 0 },
193 { "bound", "--bound", 0 },
194 { "bound", "junkbound", 4 },
195 { "bound", "junk--bound", 4 },
196 { "foo", "bound", string::npos },
197 { "bound", "--boundbound", 0 },
198 };
199 for (size_t i = 0; i < arraysize(boundary_tests); ++i) {
200 delegate_tester.boundary().assign(boundary_tests[i].boundary);
201 delegate_tester.data().assign(boundary_tests[i].data);
202 EXPECT_EQ(boundary_tests[i].position,
203 delegate_tester.FindBoundary());
204 }
205 }
206
207 TEST(MultipartResponseTest, MissingBoundaries) {
208 WebURLResponse response;
209 response.initialize();
210 response.setMIMEType("multipart/x-mixed-replace");
211 response.setHTTPHeaderField("Foo", "Bar");
212 response.setHTTPHeaderField("Content-type", "text/plain");
213 MockWebURLLoaderClient client;
214 MultipartResponseDelegate delegate(&client, NULL, response, "bound");
215
216 // No start boundary
217 string no_start_boundary(
218 "Content-type: text/plain\n\n"
219 "This is a sample response\n"
220 "--bound--"
221 "ignore junk after end token --bound\n\nTest2\n");
222 delegate.OnReceivedData(no_start_boundary.c_str(),
223 static_cast<int>(no_start_boundary.length()),
224 static_cast<int>(no_start_boundary.length()));
225 EXPECT_EQ(1, client.received_response_);
226 EXPECT_EQ(1, client.received_data_);
227 EXPECT_EQ(string("This is a sample response"), client.data_);
228 EXPECT_EQ(static_cast<int>(no_start_boundary.length()),
229 client.total_encoded_data_length_);
230
231 delegate.OnCompletedRequest();
232 EXPECT_EQ(1, client.received_response_);
233 EXPECT_EQ(1, client.received_data_);
234
235 // No end boundary
236 client.Reset();
237 MultipartResponseDelegate delegate2(&client, NULL, response, "bound");
238 string no_end_boundary(
239 "bound\nContent-type: text/plain\n\n"
240 "This is a sample response\n");
241 delegate2.OnReceivedData(no_end_boundary.c_str(),
242 static_cast<int>(no_end_boundary.length()),
243 static_cast<int>(no_end_boundary.length()));
244 EXPECT_EQ(1, client.received_response_);
245 EXPECT_EQ(1, client.received_data_);
246 EXPECT_EQ("This is a sample response\n", client.data_);
247 EXPECT_EQ(static_cast<int>(no_end_boundary.length()),
248 client.total_encoded_data_length_);
249
250 delegate2.OnCompletedRequest();
251 EXPECT_EQ(1, client.received_response_);
252 EXPECT_EQ(1, client.received_data_);
253 EXPECT_EQ(string("This is a sample response\n"), client.data_);
254 EXPECT_EQ(static_cast<int>(no_end_boundary.length()),
255 client.total_encoded_data_length_);
256
257 // Neither boundary
258 client.Reset();
259 MultipartResponseDelegate delegate3(&client, NULL, response, "bound");
260 string no_boundaries(
261 "Content-type: text/plain\n\n"
262 "This is a sample response\n");
263 delegate3.OnReceivedData(no_boundaries.c_str(),
264 static_cast<int>(no_boundaries.length()),
265 static_cast<int>(no_boundaries.length()));
266 EXPECT_EQ(1, client.received_response_);
267 EXPECT_EQ(1, client.received_data_);
268 EXPECT_EQ("This is a sample response\n", client.data_);
269 EXPECT_EQ(static_cast<int>(no_boundaries.length()),
270 client.total_encoded_data_length_);
271
272 delegate3.OnCompletedRequest();
273 EXPECT_EQ(1, client.received_response_);
274 EXPECT_EQ(1, client.received_data_);
275 EXPECT_EQ(string("This is a sample response\n"), client.data_);
276 EXPECT_EQ(static_cast<int>(no_boundaries.length()),
277 client.total_encoded_data_length_);
278 }
279
280 TEST(MultipartResponseTest, MalformedBoundary) {
281 // Some servers send a boundary that is prefixed by "--". See bug 5786.
282
283 WebURLResponse response;
284 response.initialize();
285 response.setMIMEType("multipart/x-mixed-replace");
286 response.setHTTPHeaderField("Foo", "Bar");
287 response.setHTTPHeaderField("Content-type", "text/plain");
288 MockWebURLLoaderClient client;
289 MultipartResponseDelegate delegate(&client, NULL, response, "--bound");
290
291 string data(
292 "--bound\n"
293 "Content-type: text/plain\n\n"
294 "This is a sample response\n"
295 "--bound--"
296 "ignore junk after end token --bound\n\nTest2\n");
297 delegate.OnReceivedData(data.c_str(),
298 static_cast<int>(data.length()),
299 static_cast<int>(data.length()));
300 EXPECT_EQ(1, client.received_response_);
301 EXPECT_EQ(1, client.received_data_);
302 EXPECT_EQ(string("This is a sample response"), client.data_);
303 EXPECT_EQ(static_cast<int>(data.length()), client.total_encoded_data_length_);
304
305 delegate.OnCompletedRequest();
306 EXPECT_EQ(1, client.received_response_);
307 EXPECT_EQ(1, client.received_data_);
308 }
309
310
311 // Used in for tests that break the data in various places.
312 struct TestChunk {
313 const int start_pos; // offset in data
314 const int end_pos; // end offset in data
315 const int expected_responses;
316 const int expected_received_data;
317 const char* expected_data;
318 const int expected_encoded_data_length;
319 };
320
321 void VariousChunkSizesTest(const TestChunk chunks[], int chunks_size,
322 int responses, int received_data,
323 const char* completed_data,
324 int completed_encoded_data_length) {
325 const string data(
326 "--bound\n" // 0-7
327 "Content-type: image/png\n\n" // 8-32
328 "datadatadatadatadata" // 33-52
329 "--bound\n" // 53-60
330 "Content-type: image/jpg\n\n" // 61-85
331 "foofoofoofoofoo" // 86-100
332 "--bound--"); // 101-109
333
334 WebURLResponse response;
335 response.initialize();
336 response.setMIMEType("multipart/x-mixed-replace");
337 MockWebURLLoaderClient client;
338 MultipartResponseDelegate delegate(&client, NULL, response, "bound");
339
340 for (int i = 0; i < chunks_size; ++i) {
341 ASSERT_TRUE(chunks[i].start_pos < chunks[i].end_pos);
342 string chunk = data.substr(chunks[i].start_pos,
343 chunks[i].end_pos - chunks[i].start_pos);
344 delegate.OnReceivedData(
345 chunk.c_str(),
346 static_cast<int>(chunk.length()),
347 static_cast<int>(chunk.length()));
348 EXPECT_EQ(chunks[i].expected_responses, client.received_response_);
349 EXPECT_EQ(chunks[i].expected_received_data, client.received_data_);
350 EXPECT_EQ(string(chunks[i].expected_data), client.data_);
351 EXPECT_EQ(chunks[i].expected_encoded_data_length,
352 client.total_encoded_data_length_);
353 }
354 // Check final state
355 delegate.OnCompletedRequest();
356 EXPECT_EQ(responses, client.received_response_);
357 EXPECT_EQ(received_data, client.received_data_);
358 string completed_data_string(completed_data);
359 EXPECT_EQ(completed_data_string, client.data_);
360 EXPECT_EQ(completed_encoded_data_length, client.total_encoded_data_length_);
361 }
362
363 TEST(MultipartResponseTest, BreakInBoundary) {
364 // Break in the first boundary
365 const TestChunk bound1[] = {
366 { 0, 4, 0, 0, "", 0 },
367 { 4, 110, 2, 2, "foofoofoofoofoo", 110 },
368 };
369 VariousChunkSizesTest(bound1, arraysize(bound1),
370 2, 2, "foofoofoofoofoo", 110);
371
372 // Break in first and second
373 const TestChunk bound2[] = {
374 { 0, 4, 0, 0, "", 0 },
375 { 4, 55, 1, 1, "datadatadatadat", 55 },
376 { 55, 65, 1, 2, "datadatadatadatadata", 65 },
377 { 65, 110, 2, 3, "foofoofoofoofoo", 110 },
378 };
379 VariousChunkSizesTest(bound2, arraysize(bound2),
380 2, 3, "foofoofoofoofoo", 110);
381
382 // Break in second only
383 const TestChunk bound3[] = {
384 { 0, 55, 1, 1, "datadatadatadat", 55 },
385 { 55, 110, 2, 3, "foofoofoofoofoo", 110 },
386 };
387 VariousChunkSizesTest(bound3, arraysize(bound3),
388 2, 3, "foofoofoofoofoo", 110);
389 }
390
391 TEST(MultipartResponseTest, BreakInHeaders) {
392 // Break in first header
393 const TestChunk header1[] = {
394 { 0, 10, 0, 0, "", 0 },
395 { 10, 35, 1, 0, "", 0 },
396 { 35, 110, 2, 2, "foofoofoofoofoo", 110 },
397 };
398 VariousChunkSizesTest(header1, arraysize(header1),
399 2, 2, "foofoofoofoofoo", 110);
400
401 // Break in both headers
402 const TestChunk header2[] = {
403 { 0, 10, 0, 0, "", 0 },
404 { 10, 65, 1, 1, "datadatadatadatadata", 65 },
405 { 65, 110, 2, 2, "foofoofoofoofoo", 110 },
406 };
407 VariousChunkSizesTest(header2, arraysize(header2),
408 2, 2, "foofoofoofoofoo", 110);
409
410 // Break at end of a header
411 const TestChunk header3[] = {
412 { 0, 33, 1, 0, "", 0 },
413 { 33, 65, 1, 1, "datadatadatadatadata", 65 },
414 { 65, 110, 2, 2, "foofoofoofoofoo", 110 },
415 };
416 VariousChunkSizesTest(header3, arraysize(header3),
417 2, 2, "foofoofoofoofoo", 110);
418 }
419
420 TEST(MultipartResponseTest, BreakInData) {
421 // All data as one chunk
422 const TestChunk data1[] = {
423 { 0, 110, 2, 2, "foofoofoofoofoo", 110 },
424 };
425 VariousChunkSizesTest(data1, arraysize(data1),
426 2, 2, "foofoofoofoofoo", 110);
427
428 // breaks in data segment
429 const TestChunk data2[] = {
430 { 0, 35, 1, 0, "", 0 },
431 { 35, 65, 1, 1, "datadatadatadatadata", 65 },
432 { 65, 90, 2, 1, "", 65 },
433 { 90, 110, 2, 2, "foofoofoofoofoo", 110 },
434 };
435 VariousChunkSizesTest(data2, arraysize(data2),
436 2, 2, "foofoofoofoofoo", 110);
437
438 // Incomplete send
439 const TestChunk data3[] = {
440 { 0, 35, 1, 0, "", 0 },
441 { 35, 90, 2, 1, "", 90 },
442 };
443 VariousChunkSizesTest(data3, arraysize(data3),
444 2, 2, "foof", 90);
445 }
446
447 TEST(MultipartResponseTest, SmallChunk) {
448 WebURLResponse response;
449 response.initialize();
450 response.setMIMEType("multipart/x-mixed-replace");
451 response.setHTTPHeaderField("Content-type", "text/plain");
452 MockWebURLLoaderClient client;
453 MultipartResponseDelegate delegate(&client, NULL, response, "bound");
454
455 // Test chunks of size 1, 2, and 0.
456 string data(
457 "--boundContent-type: text/plain\n\n"
458 "\n--boundContent-type: text/plain\n\n"
459 "\n\n--boundContent-type: text/plain\n\n"
460 "--boundContent-type: text/plain\n\n"
461 "end--bound--");
462 delegate.OnReceivedData(data.c_str(),
463 static_cast<int>(data.length()),
464 static_cast<int>(data.length()));
465 EXPECT_EQ(4, client.received_response_);
466 EXPECT_EQ(2, client.received_data_);
467 EXPECT_EQ(string("end"), client.data_);
468 EXPECT_EQ(static_cast<int>(data.length()), client.total_encoded_data_length_);
469
470 delegate.OnCompletedRequest();
471 EXPECT_EQ(4, client.received_response_);
472 EXPECT_EQ(2, client.received_data_);
473 }
474
475 TEST(MultipartResponseTest, MultipleBoundaries) {
476 // Test multiple boundaries back to back
477 WebURLResponse response;
478 response.initialize();
479 response.setMIMEType("multipart/x-mixed-replace");
480 MockWebURLLoaderClient client;
481 MultipartResponseDelegate delegate(&client, NULL, response, "bound");
482
483 string data("--bound\r\n\r\n--bound\r\n\r\nfoofoo--bound--");
484 delegate.OnReceivedData(data.c_str(),
485 static_cast<int>(data.length()),
486 static_cast<int>(data.length()));
487 EXPECT_EQ(2, client.received_response_);
488 EXPECT_EQ(1, client.received_data_);
489 EXPECT_EQ(string("foofoo"), client.data_);
490 EXPECT_EQ(static_cast<int>(data.length()), client.total_encoded_data_length_);
491 }
492
493 TEST(MultipartResponseTest, MultipartPayloadSet) {
494 WebURLResponse response;
495 response.initialize();
496 response.setMIMEType("multipart/x-mixed-replace");
497 MockWebURLLoaderClient client;
498 MultipartResponseDelegate delegate(&client, NULL, response, "bound");
499
500 string data(
501 "--bound\n"
502 "Content-type: text/plain\n\n"
503 "response data\n"
504 "--bound\n");
505 delegate.OnReceivedData(data.c_str(),
506 static_cast<int>(data.length()),
507 static_cast<int>(data.length()));
508 EXPECT_EQ(1, client.received_response_);
509 EXPECT_EQ(string("response data"), client.data_);
510 EXPECT_EQ(static_cast<int>(data.length()), client.total_encoded_data_length_);
511 EXPECT_FALSE(client.response_.isMultipartPayload());
512
513 string data2(
514 "Content-type: text/plain\n\n"
515 "response data2\n"
516 "--bound\n");
517 delegate.OnReceivedData(data2.c_str(),
518 static_cast<int>(data2.length()),
519 static_cast<int>(data2.length()));
520 EXPECT_EQ(2, client.received_response_);
521 EXPECT_EQ(string("response data2"), client.data_);
522 EXPECT_EQ(static_cast<int>(data.length()) + static_cast<int>(data2.length()),
523 client.total_encoded_data_length_);
524 EXPECT_TRUE(client.response_.isMultipartPayload());
525 }
526
527 } // namespace
528
529 } // namespace content
OLDNEW
« no previous file with comments | « content/child/multipart_response_delegate.cc ('k') | content/child/web_url_loader_impl.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698