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

Side by Side Diff: third_party/WebKit/Source/core/fetch/MultipartImageResourceParserTest.cpp

Issue 1693183002: Move multipart resource handling to core/fetch (1/2) (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@multipart-cleanup-preliminary
Patch Set: rebase 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
OLDNEW
(Empty)
1 // Copyright 2016 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 "core/fetch/MultipartImageResourceParser.h"
6
7 #include "platform/network/ResourceResponse.h"
8 #include "public/platform/Platform.h"
9 #include "public/platform/WebURL.h"
10 #include "public/platform/WebURLResponse.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12
13 #include <stddef.h>
14 #include <stdint.h>
15 #include <string.h>
16
17 namespace blink {
18
19 namespace {
20
21 String toString(const Vector<char>& data)
22 {
23 if (data.isEmpty())
24 return String("");
25 return String(data.data(), data.size());
26 }
27
28 class MockClient final : public NoBaseWillBeGarbageCollectedFinalized<MockClient >, public MultipartImageResourceParser::Client {
29 WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(MockClient);
30
31 public:
32 void onePartInMultipartReceived(const ResourceResponse& response) override
33 {
34 m_responses.append(response);
35 m_data.append(Vector<char>());
36 }
37 void multipartDataReceived(const char* bytes, size_t size) override
38 {
39 m_data.last().append(bytes, size);
40 }
41
42 Vector<ResourceResponse> m_responses;
43 Vector<Vector<char>> m_data;
44 };
45
46 TEST(MultipartResponseTest, PushOverLine)
47 {
48 struct {
49 const char* input;
50 const int position;
51 const int expected;
52 } lineTests[] = {
53 { "Line", 0, 0 },
54 { "Line", 2, 0 },
55 { "Line", 10, 0 },
56 { "\r\nLine", 0, 2 },
57 { "\nLine", 0, 1 },
58 { "\n\nLine", 0, 2 },
59 { "\rLine", 0, 1 },
60 { "Line\r\nLine", 4, 2 },
61 { "Line\nLine", 4, 1 },
62 { "Line\n\nLine", 4, 2 },
63 { "Line\rLine", 4, 1 },
64 { "Line\r\rLine", 4, 1 },
65 };
66 for (size_t i = 0; i < WTF_ARRAY_LENGTH(lineTests); ++i) {
67 Vector<char> input;
68 input.append(lineTests[i].input, strlen(lineTests[i].input));
69 EXPECT_EQ(lineTests[i].expected,
70 MultipartImageResourceParser::pushOverLineForTest(input, lineTests[i ].position));
71 }
72 }
73
74 TEST(MultipartResponseTest, ParseMultipartHeadersResult)
75 {
76 struct {
77 const char* data;
78 const bool result;
79 const size_t end;
80 } tests[] = {
81 { "This is junk", false, 0 },
82 { "Foo: bar\nBaz:\n\nAfter:\n", true, 15 },
83 { "Foo: bar\nBaz:\n", false, 0},
84 { "Foo: bar\r\nBaz:\r\n\r\nAfter:\r\n", true, 18 },
85 { "Foo: bar\r\nBaz:\r\n", false, 0 },
86 { "Foo: bar\nBaz:\r\n\r\nAfter:\n\n", true, 17 },
87 { "Foo: bar\r\nBaz:\n", false, 0 },
88 { "\r\n", true, 2 },
89 };
90 for (size_t i = 0; i < WTF_ARRAY_LENGTH(tests); ++i) {
91 WebURLResponse response;
92 response.initialize();
93 size_t end = 0;
94 bool result = Platform::current()->parseMultipartHeadersFromBody(tests[i ].data, strlen(tests[i].data), &response, &end);
95 EXPECT_EQ(tests[i].result, result);
96 EXPECT_EQ(tests[i].end, end);
97 }
98 }
99
100 TEST(MultipartResponseTest, ParseMultipartHeaders)
101 {
102 WebURLResponse webResponse;
103 webResponse.initialize();
104 webResponse.addHTTPHeaderField(WebString::fromLatin1("foo"), WebString::from Latin1("bar"));
105 webResponse.addHTTPHeaderField(WebString::fromLatin1("range"), WebString::fr omLatin1("piyo"));
106 webResponse.addHTTPHeaderField(WebString::fromLatin1("content-length"), WebS tring::fromLatin1("999"));
107
108 const char data[] = "content-type: image/png\ncontent-length: 10\n\n";
109 size_t end = 0;
110 bool result = Platform::current()->parseMultipartHeadersFromBody(data, strle n(data), &webResponse, &end);
111 const ResourceResponse& response = webResponse.toResourceResponse();
112
113 EXPECT_TRUE(result);
114 EXPECT_EQ(strlen(data), end);
115 EXPECT_EQ("image/png", response.httpHeaderField("content-type"));
116 EXPECT_EQ("10", response.httpHeaderField("content-length"));
117 EXPECT_EQ("bar", response.httpHeaderField("foo"));
118 EXPECT_EQ(AtomicString(), response.httpHeaderField("range"));
119 }
120
121 TEST(MultipartResponseTest, ParseMultipartHeadersContentCharset)
122 {
123 WebURLResponse webResponse;
124 webResponse.initialize();
125
126 const char data[] = "content-type: text/html; charset=utf-8\n\n";
127 size_t end = 0;
128 bool result = Platform::current()->parseMultipartHeadersFromBody(data, strle n(data), &webResponse, &end);
129 const ResourceResponse& response = webResponse.toResourceResponse();
130
131 EXPECT_TRUE(result);
132 EXPECT_EQ(strlen(data), end);
133 EXPECT_EQ("text/html; charset=utf-8", response.httpHeaderField("content-type "));
134 EXPECT_EQ("utf-8", response.textEncodingName());
135 }
136
137 TEST(MultipartResponseTest, FindBoundary)
138 {
139 struct {
140 const char* boundary;
141 const char* data;
142 const size_t position;
143 } boundaryTests[] = {
144 { "bound", "bound", 0 },
145 { "bound", "--bound", 0 },
146 { "bound", "junkbound", 4 },
147 { "bound", "junk--bound", 4 },
148 { "foo", "bound", kNotFound },
149 { "bound", "--boundbound", 0 },
150 };
151
152 for (size_t i = 0; i < WTF_ARRAY_LENGTH(boundaryTests); ++i) {
153 Vector<char> boundary, data;
154 boundary.append(boundaryTests[i].boundary, strlen(boundaryTests[i].bound ary));
155 data.append(boundaryTests[i].data, strlen(boundaryTests[i].data));
156 EXPECT_EQ(boundaryTests[i].position, MultipartImageResourceParser::findB oundaryForTest(data, &boundary));
157 }
158 }
159
160 TEST(MultipartResponseTest, NoStartBoundary)
161 {
162 ResourceResponse response;
163 response.setMimeType("multipart/x-mixed-replace");
164 response.setHTTPHeaderField("Foo", "Bar");
165 response.setHTTPHeaderField("Content-type", "text/plain");
166 MockClient* client = new MockClient;
167 Vector<char> boundary;
168 boundary.append("bound", 5);
169
170 MultipartImageResourceParser* parser = new MultipartImageResourceParser(resp onse, boundary, client);
171 const char data[] =
172 "Content-type: text/plain\n\n"
173 "This is a sample response\n"
174 "--bound--"
175 "ignore junk after end token --bound\n\nTest2\n";
176 parser->appendData(data, strlen(data));
177 ASSERT_EQ(1u, client->m_responses.size());
178 ASSERT_EQ(1u, client->m_data.size());
179 EXPECT_EQ("This is a sample response", toString(client->m_data[0]));
180
181 parser->finish();
182 ASSERT_EQ(1u, client->m_responses.size());
183 ASSERT_EQ(1u, client->m_data.size());
184 EXPECT_EQ("This is a sample response", toString(client->m_data[0]));
185 }
186
187 TEST(MultipartResponseTest, NoEndBoundary)
188 {
189 ResourceResponse response;
190 response.setMimeType("multipart/x-mixed-replace");
191 response.setHTTPHeaderField("Foo", "Bar");
192 response.setHTTPHeaderField("Content-type", "text/plain");
193 MockClient* client = new MockClient;
194 Vector<char> boundary;
195 boundary.append("bound", 5);
196
197 MultipartImageResourceParser* parser = new MultipartImageResourceParser(resp onse, boundary, client);
198 const char data[] =
199 "bound\nContent-type: text/plain\n\n"
200 "This is a sample response\n";
201 parser->appendData(data, strlen(data));
202 ASSERT_EQ(1u, client->m_responses.size());
203 ASSERT_EQ(1u, client->m_data.size());
204 EXPECT_EQ("This is a sample response\n", toString(client->m_data[0]));
205
206 parser->finish();
207 ASSERT_EQ(1u, client->m_responses.size());
208 ASSERT_EQ(1u, client->m_data.size());
209 EXPECT_EQ("This is a sample response\n", toString(client->m_data[0]));
210 }
211
212 TEST(MultipartResponseTest, NoStartAndEndBoundary)
213 {
214 ResourceResponse response;
215 response.setMimeType("multipart/x-mixed-replace");
216 response.setHTTPHeaderField("Foo", "Bar");
217 response.setHTTPHeaderField("Content-type", "text/plain");
218 MockClient* client = new MockClient;
219 Vector<char> boundary;
220 boundary.append("bound", 5);
221
222 MultipartImageResourceParser* parser = new MultipartImageResourceParser(resp onse, boundary, client);
223 const char data[] =
224 "Content-type: text/plain\n\n"
225 "This is a sample response\n";
226 parser->appendData(data, strlen(data));
227 ASSERT_EQ(1u, client->m_responses.size());
228 ASSERT_EQ(1u, client->m_data.size());
229 EXPECT_EQ("This is a sample response\n", toString(client->m_data[0]));
230
231 parser->finish();
232 ASSERT_EQ(1u, client->m_responses.size());
233 ASSERT_EQ(1u, client->m_data.size());
234 EXPECT_EQ("This is a sample response\n", toString(client->m_data[0]));
235 }
236
237 TEST(MultipartResponseTest, MalformedBoundary)
238 {
239 // Some servers send a boundary that is prefixed by "--". See bug 5786.
240 ResourceResponse response;
241 response.setMimeType("multipart/x-mixed-replace");
242 response.setHTTPHeaderField("Foo", "Bar");
243 response.setHTTPHeaderField("Content-type", "text/plain");
244 MockClient* client = new MockClient;
245 Vector<char> boundary;
246 boundary.append("--bound", 7);
247
248 MultipartImageResourceParser* parser = new MultipartImageResourceParser(resp onse, boundary, client);
249 const char data[] =
250 "--bound\n"
251 "Content-type: text/plain\n\n"
252 "This is a sample response\n"
253 "--bound--"
254 "ignore junk after end token --bound\n\nTest2\n";
255 parser->appendData(data, strlen(data));
256 ASSERT_EQ(1u, client->m_responses.size());
257 ASSERT_EQ(1u, client->m_data.size());
258 EXPECT_EQ("This is a sample response", toString(client->m_data[0]));
259
260 parser->finish();
261 ASSERT_EQ(1u, client->m_responses.size());
262 ASSERT_EQ(1u, client->m_data.size());
263 EXPECT_EQ("This is a sample response", toString(client->m_data[0]));
264 }
265
266 // Used in for tests that break the data in various places.
267 struct TestChunk {
268 const int startPosition; // offset in data
269 const int endPosition; // end offset in data
270 const size_t expectedResponses;
271 const char* expectedData;
272 };
273
274 void variousChunkSizesTest(const TestChunk chunks[], int chunksSize,
275 size_t responses, int receivedData,
276 const char* completedData)
277 {
278 const char data[] =
279 "--bound\n" // 0-7
280 "Content-type: image/png\n\n" // 8-32
281 "datadatadatadatadata" // 33-52
282 "--bound\n" // 53-60
283 "Content-type: image/jpg\n\n" // 61-85
284 "foofoofoofoofoo" // 86-100
285 "--bound--"; // 101-109
286
287 ResourceResponse response;
288 response.setMimeType("multipart/x-mixed-replace");
289 MockClient* client = new MockClient;
290 Vector<char> boundary;
291 boundary.append("bound", 5);
292
293 MultipartImageResourceParser* parser = new MultipartImageResourceParser(resp onse, boundary, client);
294
295 for (int i = 0; i < chunksSize; ++i) {
296 ASSERT_LT(chunks[i].startPosition, chunks[i].endPosition);
297 parser->appendData(data + chunks[i].startPosition, chunks[i].endPosition - chunks[i].startPosition);
298 EXPECT_EQ(chunks[i].expectedResponses, client->m_responses.size());
299 EXPECT_EQ(String(chunks[i].expectedData), client->m_data.size() > 0 ? to String(client->m_data.last()) : String(""));
300 }
301 // Check final state
302 parser->finish();
303 EXPECT_EQ(responses, client->m_responses.size());
304 EXPECT_EQ(completedData, toString(client->m_data.last()));
305 }
306
307 template <size_t N>
308 void variousChunkSizesTest(const TestChunk (&chunks)[N], size_t responses, int r eceivedData, const char* completedData)
309 {
310 variousChunkSizesTest(chunks, N, responses, receivedData, completedData);
311 }
312
313 TEST(MultipartResponseTest, BreakInBoundary)
314 {
315 // Break in the first boundary
316 const TestChunk bound1[] = {
317 { 0, 4, 0, "" },
318 { 4, 110, 2, "foofoofoofoofoo" },
319 };
320 variousChunkSizesTest(bound1, 2, 2, "foofoofoofoofoo");
321
322 // Break in first and second
323 const TestChunk bound2[] = {
324 { 0, 4, 0, "" },
325 { 4, 55, 1, "datadatadatadat" },
326 { 55, 65, 1, "datadatadatadatadata" },
327 { 65, 110, 2, "foofoofoofoofoo" },
328 };
329 variousChunkSizesTest(bound2, 2, 3, "foofoofoofoofoo");
330
331 // Break in second only
332 const TestChunk bound3[] = {
333 { 0, 55, 1, "datadatadatadat" },
334 { 55, 110, 2, "foofoofoofoofoo" },
335 };
336 variousChunkSizesTest(bound3, 2, 3, "foofoofoofoofoo");
337 }
338
339 TEST(MultipartResponseTest, BreakInHeaders)
340 {
341 // Break in first header
342 const TestChunk header1[] = {
343 { 0, 10, 0, "" },
344 { 10, 35, 1, "" },
345 { 35, 110, 2, "foofoofoofoofoo" },
346 };
347 variousChunkSizesTest(header1, 2, 2, "foofoofoofoofoo");
348
349 // Break in both headers
350 const TestChunk header2[] = {
351 { 0, 10, 0, "" },
352 { 10, 65, 1, "datadatadatadatadata" },
353 { 65, 110, 2, "foofoofoofoofoo" },
354 };
355 variousChunkSizesTest(header2, 2, 2, "foofoofoofoofoo");
356
357 // Break at end of a header
358 const TestChunk header3[] = {
359 { 0, 33, 1, "" },
360 { 33, 65, 1, "datadatadatadatadata" },
361 { 65, 110, 2, "foofoofoofoofoo" },
362 };
363 variousChunkSizesTest(header3, 2, 2, "foofoofoofoofoo");
364 }
365
366 TEST(MultipartResponseTest, BreakInData)
367 {
368 // All data as one chunk
369 const TestChunk data1[] = {
370 { 0, 110, 2, "foofoofoofoofoo" },
371 };
372 variousChunkSizesTest(data1, 2, 2, "foofoofoofoofoo");
373
374 // breaks in data segment
375 const TestChunk data2[] = {
376 { 0, 35, 1, "" },
377 { 35, 65, 1, "datadatadatadatadata" },
378 { 65, 90, 2, "" },
379 { 90, 110, 2, "foofoofoofoofoo" },
380 };
381 variousChunkSizesTest(data2, 2, 2, "foofoofoofoofoo");
382
383 // Incomplete send
384 const TestChunk data3[] = {
385 { 0, 35, 1, "" },
386 { 35, 90, 2, "" },
387 };
388 variousChunkSizesTest(data3, 2, 2, "foof");
389 }
390
391 TEST(MultipartResponseTest, SmallChunk)
392 {
393 ResourceResponse response;
394 response.setMimeType("multipart/x-mixed-replace");
395 response.setHTTPHeaderField("Content-type", "text/plain");
396 MockClient* client = new MockClient;
397 Vector<char> boundary;
398 boundary.append("bound", 5);
399
400 MultipartImageResourceParser* parser = new MultipartImageResourceParser(resp onse, boundary, client);
401
402 // Test chunks of size 1, 2, and 0.
403 const char data[] =
404 "--boundContent-type: text/plain\n\n"
405 "\n--boundContent-type: text/plain\n\n"
406 "\n\n--boundContent-type: text/plain\n\n"
407 "--boundContent-type: text/plain\n\n"
408 "end--bound--";
409 parser->appendData(data, strlen(data));
410 ASSERT_EQ(4u, client->m_responses.size());
411 ASSERT_EQ(4u, client->m_data.size());
412 EXPECT_EQ("", toString(client->m_data[0]));
413 EXPECT_EQ("\n", toString(client->m_data[1]));
414 EXPECT_EQ("", toString(client->m_data[2]));
415 EXPECT_EQ("end", toString(client->m_data[3]));
416
417 parser->finish();
418 ASSERT_EQ(4u, client->m_responses.size());
419 ASSERT_EQ(4u, client->m_data.size());
420 EXPECT_EQ("", toString(client->m_data[0]));
421 EXPECT_EQ("\n", toString(client->m_data[1]));
422 EXPECT_EQ("", toString(client->m_data[2]));
423 EXPECT_EQ("end", toString(client->m_data[3]));
424 }
425
426 TEST(MultipartResponseTest, MultipleBoundaries)
427 {
428 // Test multiple boundaries back to back
429 ResourceResponse response;
430 response.setMimeType("multipart/x-mixed-replace");
431 MockClient* client = new MockClient;
432 Vector<char> boundary;
433 boundary.append("bound", 5);
434
435 MultipartImageResourceParser* parser = new MultipartImageResourceParser(resp onse, boundary, client);
436
437 const char data[] = "--bound\r\n\r\n--bound\r\n\r\nfoofoo--bound--";
438 parser->appendData(data, strlen(data));
439 ASSERT_EQ(2u, client->m_responses.size());
440 ASSERT_EQ(2u, client->m_data.size());
441 EXPECT_EQ("", toString(client->m_data[0]));
442 EXPECT_EQ("foofoo", toString(client->m_data[1]));
443 }
444
445 TEST(MultipartResponseTest, MultipartPayloadSet)
446 {
447 ResourceResponse response;
448 response.setMimeType("multipart/x-mixed-replace");
449 MockClient* client = new MockClient;
450 Vector<char> boundary;
451 boundary.append("bound", 5);
452
453 MultipartImageResourceParser* parser = new MultipartImageResourceParser(resp onse, boundary, client);
454
455 const char data[] =
456 "--bound\n"
457 "Content-type: text/plain\n\n"
458 "response data\n"
459 "--bound\n";
460 parser->appendData(data, strlen(data));
461 ASSERT_EQ(1u, client->m_responses.size());
462 ASSERT_EQ(1u, client->m_data.size());
463 EXPECT_EQ("response data", toString(client->m_data[0]));
464 EXPECT_FALSE(client->m_responses[0].isMultipartPayload());
465
466 const char data2[] =
467 "Content-type: text/plain\n\n"
468 "response data2\n"
469 "--bound\n";
470 parser->appendData(data2, strlen(data2));
471 ASSERT_EQ(2u, client->m_responses.size());
472 ASSERT_EQ(2u, client->m_data.size());
473 EXPECT_EQ("response data2", toString(client->m_data[1]));
474 EXPECT_TRUE(client->m_responses[1].isMultipartPayload());
475 }
476
477 } // namespace
478
479 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698