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