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