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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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