1 #include "http_request.hpp"
2 #include "multipart_parser.hpp"
3
4 #include <boost/beast/http/fields.hpp>
5
6 #include <iterator>
7 #include <string_view>
8 #include <system_error>
9 #include <vector>
10
11 #include <gtest/gtest.h> // IWYU pragma: keep
12
13 // IWYU pragma: no_include <gtest/gtest-message.h>
14 // IWYU pragma: no_include <gtest/gtest-test-part.h>
15 // IWYU pragma: no_include "gtest/gtest_pred_impl.h"
16 // IWYU pragma: no_include <boost/beast/http/impl/fields.hpp>
17 // IWYU pragma: no_include <boost/intrusive/detail/list_iterator.hpp>
18 // IWYU pragma: no_include <boost/intrusive/detail/tree_iterator.hpp>
19
20 namespace
21 {
22 using ::testing::Test;
23
24 class MultipartTest : public Test
25 {
26 public:
27 MultipartParser parser;
28 std::error_code ec;
29 };
30
TEST_F(MultipartTest,TestGoodMultipartParser)31 TEST_F(MultipartTest, TestGoodMultipartParser)
32 {
33 std::string_view body =
34 "-----------------------------d74496d66958873e\r\n"
35 "Content-Disposition: form-data; name=\"Test1\"\r\n\r\n"
36 "111111111111111111111111112222222222222222222222222222222\r\n"
37 "-----------------------------d74496d66958873e\r\n"
38 "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
39 "{\r\n-----------------------------d74496d66958873e123456\r\n"
40 "-----------------------------d74496d66958873e\r\n"
41 "Content-Disposition: form-data; name=\"Test3\"\r\n\r\n"
42 "{\r\n--------d74496d6695887}\r\n"
43 "-----------------------------d74496d66958873e--\r\n";
44
45 crow::Request reqIn(body, ec);
46
47 reqIn.addHeader("Content-Type",
48 "multipart/form-data; "
49 "boundary=---------------------------d74496d66958873e");
50
51 ParserError rc = parser.parse(reqIn);
52 ASSERT_EQ(rc, ParserError::PARSER_SUCCESS);
53
54 EXPECT_EQ(parser.boundary,
55 "\r\n-----------------------------d74496d66958873e");
56 EXPECT_EQ(parser.mime_fields.size(), 3);
57
58 EXPECT_EQ(parser.mime_fields[0].fields.at("Content-Disposition"),
59 "form-data; name=\"Test1\"");
60 EXPECT_EQ(parser.mime_fields[0].content,
61 "111111111111111111111111112222222222222222222222222222222");
62
63 EXPECT_EQ(parser.mime_fields[1].fields.at("Content-Disposition"),
64 "form-data; name=\"Test2\"");
65 EXPECT_EQ(parser.mime_fields[1].content,
66 "{\r\n-----------------------------d74496d66958873e123456");
67 EXPECT_EQ(parser.mime_fields[2].fields.at("Content-Disposition"),
68 "form-data; name=\"Test3\"");
69 EXPECT_EQ(parser.mime_fields[2].content, "{\r\n--------d74496d6695887}");
70 }
71
TEST_F(MultipartTest,TestBadMultipartParser1)72 TEST_F(MultipartTest, TestBadMultipartParser1)
73 {
74 std::string_view body =
75 "-----------------------------d74496d66958873e\r\n"
76 "Content-Disposition: form-data; name=\"Test1\"\r\n\r\n"
77 "1234567890\r\n"
78 "-----------------------------d74496d66958873e\r-\r\n";
79
80 crow::Request reqIn(body, ec);
81
82 reqIn.addHeader("Content-Type",
83 "multipart/form-data; "
84 "boundary=---------------------------d74496d66958873e");
85
86 ParserError rc = parser.parse(reqIn);
87
88 EXPECT_EQ(rc, ParserError::ERROR_UNEXPECTED_END_OF_INPUT);
89 }
90
TEST_F(MultipartTest,TestBadMultipartParser2)91 TEST_F(MultipartTest, TestBadMultipartParser2)
92 {
93 std::string_view body =
94 "-----------------------------d74496d66958873e\r\n"
95 "Content-Disposition: form-data; name=\"Test1\"\r\n\r\n"
96 "abcd\r\n"
97 "-----------------------------d74496d66958873e-\r\n";
98 crow::Request reqIn(body, ec);
99
100 reqIn.addHeader("Content-Type",
101 "multipart/form-data; "
102 "boundary=---------------------------d74496d66958873e");
103
104 ParserError rc = parser.parse(reqIn);
105
106 EXPECT_EQ(rc, ParserError::ERROR_UNEXPECTED_END_OF_INPUT);
107 }
108
TEST_F(MultipartTest,TestErrorBoundaryFormat)109 TEST_F(MultipartTest, TestErrorBoundaryFormat)
110 {
111 std::string_view body =
112 "-----------------------------d74496d66958873e\r\n"
113 "Content-Disposition: form-data; name=\"Test1\"\r\n\r\n"
114 "{\"Key1\": 11223333333333333333333333333333333333333333}\r\n"
115 "-----------------------------d74496d66958873e\r\n"
116 "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
117 "123456\r\n"
118 "-----------------------------d74496d66958873e--\r\n";
119
120 crow::Request reqIn(body, ec);
121
122 reqIn.addHeader("Content-Type",
123 "multipart/form-data; "
124 "boundary+=-----------------------------d74496d66958873e");
125
126 EXPECT_EQ(parser.parse(reqIn), ParserError::ERROR_BOUNDARY_FORMAT);
127 }
128
TEST_F(MultipartTest,TestErrorBoundaryCR)129 TEST_F(MultipartTest, TestErrorBoundaryCR)
130 {
131 std::string_view body =
132 "-----------------------------d74496d66958873e"
133 "Content-Disposition: form-data; name=\"Test1\"\r\n\r"
134 "{\"Key1\": 112233}\r\n"
135 "-----------------------------d74496d66958873e\r\n"
136 "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
137 "123456\r\n"
138 "-----------------------------d74496d66958873e--\r\n";
139 crow::Request reqIn(body, ec);
140
141 reqIn.addHeader("Content-Type",
142 "multipart/form-data; "
143 "boundary=---------------------------d74496d66958873e");
144
145 EXPECT_EQ(parser.parse(reqIn), ParserError::ERROR_BOUNDARY_CR);
146 }
147
TEST_F(MultipartTest,TestErrorBoundaryLF)148 TEST_F(MultipartTest, TestErrorBoundaryLF)
149 {
150 std::string_view body =
151 "-----------------------------d74496d66958873e\r"
152 "Content-Disposition: form-data; name=\"Test1\"\r\n\r\n"
153 "{\"Key1\": 112233}\r\n"
154 "-----------------------------d74496d66958873e\r\n"
155 "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
156 "123456\r\n"
157 "-----------------------------d74496d66958873e--\r\n";
158
159 crow::Request reqIn(body, ec);
160
161 reqIn.addHeader("Content-Type",
162 "multipart/form-data; "
163 "boundary=---------------------------d74496d66958873e");
164
165 EXPECT_EQ(parser.parse(reqIn), ParserError::ERROR_BOUNDARY_LF);
166 }
167
TEST_F(MultipartTest,TestErrorBoundaryData)168 TEST_F(MultipartTest, TestErrorBoundaryData)
169 {
170 std::string_view body =
171 "-----------------------------d74496d66958873e\r\n"
172 "Content-Disposition: form-data; name=\"Test1\"\r\n\r\n"
173 "{\"Key1\": 112233}\r\n"
174 "-----------------------------d74496d66958873e\r\n"
175 "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
176 "123456\r\n"
177 "-----------------------------d74496d66958873e--\r\n";
178
179 crow::Request reqIn(body, ec);
180
181 reqIn.addHeader("Content-Type",
182 "multipart/form-data; "
183 "boundary=---------------------------d7449sd6d66958873e");
184
185 EXPECT_EQ(parser.parse(reqIn), ParserError::ERROR_BOUNDARY_DATA);
186 }
187
TEST_F(MultipartTest,TestErrorEmptyHeader)188 TEST_F(MultipartTest, TestErrorEmptyHeader)
189 {
190 std::string_view body =
191 "-----------------------------d74496d66958873e\r\n"
192 ": form-data; name=\"Test1\"\r\n"
193 "{\"Key1\": 112233}\r\n"
194 "-----------------------------d74496d66958873e\r\n"
195 "Content-Disposition: form-data; name=\"Test2\"\r\n"
196 "123456\r\n"
197 "-----------------------------d74496d66958873e--\r\n";
198 crow::Request reqIn(body, ec);
199
200 reqIn.addHeader("Content-Type",
201 "multipart/form-data; "
202 "boundary=---------------------------d74496d66958873e");
203
204 EXPECT_EQ(parser.parse(reqIn), ParserError::ERROR_EMPTY_HEADER);
205 }
206
TEST_F(MultipartTest,TestErrorHeaderName)207 TEST_F(MultipartTest, TestErrorHeaderName)
208 {
209 std::string_view body =
210 "-----------------------------d74496d66958873e\r\n"
211 "Content-!!Disposition: form-data; name=\"Test1\"\r\n"
212 "{\"Key1\": 112233}\r\n"
213 "-----------------------------d74496d66958873e\r\n"
214 "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
215 "123456\r\n"
216 "-----------------------------d74496d66958873e--\r\n";
217 crow::Request reqIn(body, ec);
218
219 reqIn.addHeader("Content-Type",
220 "multipart/form-data; "
221 "boundary=---------------------------d74496d66958873e");
222
223 EXPECT_EQ(parser.parse(reqIn), ParserError::ERROR_HEADER_NAME);
224 }
225
TEST_F(MultipartTest,TestErrorHeaderValue)226 TEST_F(MultipartTest, TestErrorHeaderValue)
227 {
228 std::string_view body =
229 "-----------------------------d74496d66958873e\r\n"
230 "Content-Disposition: form-data; name=\"Test1\"\r"
231 "{\"Key1\": 112233}\r\n"
232 "-----------------------------d74496d66958873e\r\n"
233 "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
234 "123456\r\n"
235 "-----------------------------d74496d66958873e--\r\n";
236
237 crow::Request reqIn(body, ec);
238
239 reqIn.addHeader("Content-Type",
240 "multipart/form-data; "
241 "boundary=---------------------------d74496d66958873e");
242
243 EXPECT_EQ(parser.parse(reqIn), ParserError::ERROR_HEADER_VALUE);
244 }
245
TEST_F(MultipartTest,TestErrorHeaderEnding)246 TEST_F(MultipartTest, TestErrorHeaderEnding)
247 {
248 std::string_view body =
249 "-----------------------------d74496d66958873e\r\n"
250 "Content-Disposition: form-data; name=\"Test1\"\r\n\r"
251 "{\"Key1\": 112233}\r\n"
252 "-----------------------------d74496d66958873e\r\n"
253 "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
254 "123456\r\n"
255 "-----------------------------d74496d66958873e--\r\n";
256
257 crow::Request reqIn(body, ec);
258
259 reqIn.addHeader("Content-Type",
260 "multipart/form-data; "
261 "boundary=---------------------------d74496d66958873e");
262
263 EXPECT_EQ(parser.parse(reqIn), ParserError::ERROR_HEADER_ENDING);
264 }
265
TEST_F(MultipartTest,TestGoodMultipartParserMultipleHeaders)266 TEST_F(MultipartTest, TestGoodMultipartParserMultipleHeaders)
267 {
268 std::string_view body = "-----------------------------d74496d66958873e\r\n"
269 "Content-Disposition: form-data; name=\"Test1\"\r\n"
270 "Other-Header: value=\"v1\"\r\n"
271 "\r\n"
272 "Data1\r\n"
273 "-----------------------------d74496d66958873e--";
274
275 crow::Request reqIn(body, ec);
276
277 reqIn.addHeader("Content-Type",
278 "multipart/form-data; "
279 "boundary=---------------------------d74496d66958873e");
280
281 ParserError rc = parser.parse(reqIn);
282 ASSERT_EQ(rc, ParserError::PARSER_SUCCESS);
283
284 EXPECT_EQ(parser.boundary,
285 "\r\n-----------------------------d74496d66958873e");
286 ASSERT_EQ(parser.mime_fields.size(), 1);
287
288 EXPECT_EQ(parser.mime_fields[0].fields.at("Content-Disposition"),
289 "form-data; name=\"Test1\"");
290 EXPECT_EQ(parser.mime_fields[0].fields.at("Other-Header"), "value=\"v1\"");
291 EXPECT_EQ(parser.mime_fields[0].content, "Data1");
292 }
293
TEST_F(MultipartTest,TestErrorHeaderWithoutColon)294 TEST_F(MultipartTest, TestErrorHeaderWithoutColon)
295 {
296 std::string_view body = "----end\r\n"
297 "abc\r\n"
298 "\r\n"
299 "Data1\r\n"
300 "----end--\r\n";
301 crow::Request reqIn(body, ec);
302
303 reqIn.addHeader("Content-Type", "multipart/form-data; "
304 "boundary=--end");
305
306 EXPECT_EQ(parser.parse(reqIn), ParserError::ERROR_UNEXPECTED_END_OF_HEADER);
307 }
308
TEST_F(MultipartTest,TestUnknownHeaderIsCorrectlyParsed)309 TEST_F(MultipartTest, TestUnknownHeaderIsCorrectlyParsed)
310 {
311 std::string_view body =
312 "----end\r\n"
313 "t-DiPpcccc:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccgcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccaaaaaa\r\n"
314 "\r\n"
315 "Data1\r\n"
316 "----end--\r\n";
317
318 crow::Request reqIn(body, ec);
319
320 reqIn.addHeader("Content-Type", "multipart/form-data; "
321 "boundary=--end");
322 ParserError rc = parser.parse(reqIn);
323
324 ASSERT_EQ(rc, ParserError::PARSER_SUCCESS);
325
326 EXPECT_EQ(parser.boundary, "\r\n----end");
327 ASSERT_EQ(parser.mime_fields.size(), 1);
328
329 EXPECT_EQ(
330 parser.mime_fields[0].fields.at("t-DiPpcccc"),
331 "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccgcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccaaaaaa");
332 EXPECT_EQ(parser.mime_fields[0].content, "Data1");
333 }
334
TEST_F(MultipartTest,TestErrorMissingSeparatorBetweenMimeFieldsAndData)335 TEST_F(MultipartTest, TestErrorMissingSeparatorBetweenMimeFieldsAndData)
336 {
337 std::string_view body =
338 "-----------------------------d74496d66958873e\r\n"
339 "t-DiPpcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccgcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccaaaaaa\r\n"
340 "Data1"
341 "-----------------------------d74496d66958873e--";
342
343 crow::Request reqIn(body, ec);
344
345 reqIn.addHeader(
346 "Content-Type",
347 "multipart/form-data; boundary=---------------------------d74496d66958873e");
348
349 ParserError rc = parser.parse(reqIn);
350
351 EXPECT_EQ(rc, ParserError::ERROR_UNEXPECTED_END_OF_HEADER);
352 }
353
TEST_F(MultipartTest,TestDataWithoutMimeFields)354 TEST_F(MultipartTest, TestDataWithoutMimeFields)
355 {
356 std::string_view body = "-----------------------------d74496d66958873e\r\n"
357 "\r\n"
358 "Data1\r\n"
359 "-----------------------------d74496d66958873e--";
360
361 crow::Request reqIn(body, ec);
362
363 reqIn.addHeader(
364 "Content-Type",
365 "multipart/form-data; boundary=---------------------------d74496d66958873e");
366
367 ParserError rc = parser.parse(reqIn);
368
369 ASSERT_EQ(rc, ParserError::PARSER_SUCCESS);
370
371 EXPECT_EQ(parser.boundary,
372 "\r\n-----------------------------d74496d66958873e");
373 ASSERT_EQ(parser.mime_fields.size(), 1);
374
375 EXPECT_EQ(std::distance(parser.mime_fields[0].fields.begin(),
376 parser.mime_fields[0].fields.end()),
377 0);
378 EXPECT_EQ(parser.mime_fields[0].content, "Data1");
379 }
380
TEST_F(MultipartTest,TestErrorMissingFinalBoundry)381 TEST_F(MultipartTest, TestErrorMissingFinalBoundry)
382 {
383 std::string_view body =
384 "----XX\r\n"
385 "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
386 "t-DiPpccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccAAAAAAAAAAAAAAABCDz\r\n"
387 "\335\r\n\r\n";
388
389 crow::Request reqIn(body, ec);
390
391 reqIn.addHeader("Content-Type", "multipart/form-data; boundary=--XX");
392
393 ParserError rc = parser.parse(reqIn);
394
395 EXPECT_EQ(rc, ParserError::ERROR_UNEXPECTED_END_OF_INPUT);
396 }
397
TEST_F(MultipartTest,TestIgnoreDataAfterFinalBoundary)398 TEST_F(MultipartTest, TestIgnoreDataAfterFinalBoundary)
399 {
400 std::string_view body =
401 "----XX\r\n"
402 "Content-Disposition: form-data; name=\"Test1\"\r\n\r\n"
403 "Data1\r\n"
404 "----XX--\r\n"
405 "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
406 "Data2\r\n"
407 "----XX--\r\n";
408
409 crow::Request reqIn(body, ec);
410
411 reqIn.addHeader("Content-Type", "multipart/form-data; boundary=--XX");
412
413 ParserError rc = parser.parse(reqIn);
414
415 ASSERT_EQ(rc, ParserError::PARSER_SUCCESS);
416
417 EXPECT_EQ(parser.boundary, "\r\n----XX");
418 EXPECT_EQ(parser.mime_fields.size(), 1);
419
420 EXPECT_EQ(parser.mime_fields[0].fields.at("Content-Disposition"),
421 "form-data; name=\"Test1\"");
422 EXPECT_EQ(parser.mime_fields[0].content, "Data1");
423 }
424
TEST_F(MultipartTest,TestFinalBoundaryIsCorrectlyRecognized)425 TEST_F(MultipartTest, TestFinalBoundaryIsCorrectlyRecognized)
426 {
427 std::string_view body =
428 "----XX\r\n"
429 "Content-Disposition: form-data; name=\"Test1\"\r\n\r\n"
430 "Data1\r\n"
431 "----XX-abc-\r\n"
432 "StillData1\r\n"
433 "----XX--\r\n";
434
435 crow::Request reqIn(body, ec);
436
437 reqIn.addHeader("Content-Type", "multipart/form-data; boundary=--XX");
438
439 ParserError rc = parser.parse(reqIn);
440
441 ASSERT_EQ(rc, ParserError::PARSER_SUCCESS);
442
443 EXPECT_EQ(parser.boundary, "\r\n----XX");
444 EXPECT_EQ(parser.mime_fields.size(), 1);
445
446 EXPECT_EQ(parser.mime_fields[0].fields.at("Content-Disposition"),
447 "form-data; name=\"Test1\"");
448 EXPECT_EQ(parser.mime_fields[0].content, "Data1\r\n"
449 "----XX-abc-\r\n"
450 "StillData1");
451 }
452
453 } // namespace
454