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