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