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