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 = 269 "-----------------------------d74496d66958873e\r\n" 270 "Content-Disposition: form-data; name=\"Test1\"\r\n" 271 "Other-Header: value=\"v1\"\r\n" 272 "\r\n" 273 "Data1\r\n" 274 "-----------------------------d74496d66958873e--"; 275 276 crow::Request reqIn(body, ec); 277 278 reqIn.addHeader("Content-Type", 279 "multipart/form-data; " 280 "boundary=---------------------------d74496d66958873e"); 281 282 ParserError rc = parser.parse(reqIn); 283 ASSERT_EQ(rc, ParserError::PARSER_SUCCESS); 284 285 EXPECT_EQ(parser.boundary, 286 "\r\n-----------------------------d74496d66958873e"); 287 ASSERT_EQ(parser.mime_fields.size(), 1); 288 289 EXPECT_EQ(parser.mime_fields[0].fields.at("Content-Disposition"), 290 "form-data; name=\"Test1\""); 291 EXPECT_EQ(parser.mime_fields[0].fields.at("Other-Header"), "value=\"v1\""); 292 EXPECT_EQ(parser.mime_fields[0].content, "Data1"); 293 } 294 295 TEST_F(MultipartTest, TestErrorHeaderWithoutColon) 296 { 297 std::string_view body = 298 "----end\r\n" 299 "abc\r\n" 300 "\r\n" 301 "Data1\r\n" 302 "----end--\r\n"; 303 crow::Request reqIn(body, ec); 304 305 reqIn.addHeader("Content-Type", "multipart/form-data; " 306 "boundary=--end"); 307 308 EXPECT_EQ(parser.parse(reqIn), ParserError::ERROR_UNEXPECTED_END_OF_HEADER); 309 } 310 311 TEST_F(MultipartTest, TestUnknownHeaderIsCorrectlyParsed) 312 { 313 std::string_view body = 314 "----end\r\n" 315 "t-DiPpcccc:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccgcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccaaaaaa\r\n" 316 "\r\n" 317 "Data1\r\n" 318 "----end--\r\n"; 319 320 crow::Request reqIn(body, ec); 321 322 reqIn.addHeader("Content-Type", "multipart/form-data; " 323 "boundary=--end"); 324 ParserError rc = parser.parse(reqIn); 325 326 ASSERT_EQ(rc, ParserError::PARSER_SUCCESS); 327 328 EXPECT_EQ(parser.boundary, "\r\n----end"); 329 ASSERT_EQ(parser.mime_fields.size(), 1); 330 331 EXPECT_EQ( 332 parser.mime_fields[0].fields.at("t-DiPpcccc"), 333 "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccgcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccaaaaaa"); 334 EXPECT_EQ(parser.mime_fields[0].content, "Data1"); 335 } 336 337 TEST_F(MultipartTest, TestErrorMissingSeparatorBetweenMimeFieldsAndData) 338 { 339 std::string_view body = 340 "-----------------------------d74496d66958873e\r\n" 341 "t-DiPpcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccgcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccaaaaaa\r\n" 342 "Data1" 343 "-----------------------------d74496d66958873e--"; 344 345 crow::Request reqIn(body, ec); 346 347 reqIn.addHeader( 348 "Content-Type", 349 "multipart/form-data; boundary=---------------------------d74496d66958873e"); 350 351 ParserError rc = parser.parse(reqIn); 352 353 EXPECT_EQ(rc, ParserError::ERROR_UNEXPECTED_END_OF_HEADER); 354 } 355 356 TEST_F(MultipartTest, TestDataWithoutMimeFields) 357 { 358 std::string_view body = 359 "-----------------------------d74496d66958873e\r\n" 360 "\r\n" 361 "Data1\r\n" 362 "-----------------------------d74496d66958873e--"; 363 364 crow::Request reqIn(body, ec); 365 366 reqIn.addHeader( 367 "Content-Type", 368 "multipart/form-data; boundary=---------------------------d74496d66958873e"); 369 370 ParserError rc = parser.parse(reqIn); 371 372 ASSERT_EQ(rc, ParserError::PARSER_SUCCESS); 373 374 EXPECT_EQ(parser.boundary, 375 "\r\n-----------------------------d74496d66958873e"); 376 ASSERT_EQ(parser.mime_fields.size(), 1); 377 378 EXPECT_EQ(std::distance(parser.mime_fields[0].fields.begin(), 379 parser.mime_fields[0].fields.end()), 380 0); 381 EXPECT_EQ(parser.mime_fields[0].content, "Data1"); 382 } 383 384 TEST_F(MultipartTest, TestErrorMissingFinalBoundry) 385 { 386 std::string_view body = 387 "----XX\r\n" 388 "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n" 389 "t-DiPpccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccAAAAAAAAAAAAAAABCDz\r\n" 390 "\335\r\n\r\n"; 391 392 crow::Request reqIn(body, ec); 393 394 reqIn.addHeader("Content-Type", "multipart/form-data; boundary=--XX"); 395 396 ParserError rc = parser.parse(reqIn); 397 398 EXPECT_EQ(rc, ParserError::ERROR_UNEXPECTED_END_OF_INPUT); 399 } 400 401 TEST_F(MultipartTest, TestIgnoreDataAfterFinalBoundary) 402 { 403 std::string_view body = 404 "----XX\r\n" 405 "Content-Disposition: form-data; name=\"Test1\"\r\n\r\n" 406 "Data1\r\n" 407 "----XX--\r\n" 408 "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n" 409 "Data2\r\n" 410 "----XX--\r\n"; 411 412 crow::Request reqIn(body, ec); 413 414 reqIn.addHeader("Content-Type", "multipart/form-data; boundary=--XX"); 415 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"); 426 } 427 428 TEST_F(MultipartTest, TestFinalBoundaryIsCorrectlyRecognized) 429 { 430 std::string_view body = 431 "----XX\r\n" 432 "Content-Disposition: form-data; name=\"Test1\"\r\n\r\n" 433 "Data1\r\n" 434 "----XX-abc-\r\n" 435 "StillData1\r\n" 436 "----XX--\r\n"; 437 438 crow::Request reqIn(body, ec); 439 440 reqIn.addHeader("Content-Type", "multipart/form-data; boundary=--XX"); 441 442 ParserError rc = parser.parse(reqIn); 443 444 ASSERT_EQ(rc, ParserError::PARSER_SUCCESS); 445 446 EXPECT_EQ(parser.boundary, "\r\n----XX"); 447 EXPECT_EQ(parser.mime_fields.size(), 1); 448 449 EXPECT_EQ(parser.mime_fields[0].fields.at("Content-Disposition"), 450 "form-data; name=\"Test1\""); 451 EXPECT_EQ(parser.mime_fields[0].content, 452 "Data1\r\n" 453 "----XX-abc-\r\n" 454 "StillData1"); 455 } 456 457 } // namespace 458