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