| 1 | // |
| 2 | // HTMLFormTest.cpp |
| 3 | // |
| 4 | // Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH. |
| 5 | // and Contributors. |
| 6 | // |
| 7 | // SPDX-License-Identifier: BSL-1.0 |
| 8 | // |
| 9 | |
| 10 | |
| 11 | #include "HTMLFormTest.h" |
| 12 | #include "Poco/CppUnit/TestCaller.h" |
| 13 | #include "Poco/CppUnit/TestSuite.h" |
| 14 | #include "Poco/Net/HTMLForm.h" |
| 15 | #include "Poco/Net/PartSource.h" |
| 16 | #include "Poco/Net/StringPartSource.h" |
| 17 | #include "Poco/Net/PartHandler.h" |
| 18 | #include "Poco/Net/HTTPRequest.h" |
| 19 | #include "Poco/Net/NetException.h" |
| 20 | #include <sstream> |
| 21 | |
| 22 | |
| 23 | using Poco::Net::HTMLForm; |
| 24 | using Poco::Net::PartSource; |
| 25 | using Poco::Net::StringPartSource; |
| 26 | using Poco::Net::PartHandler; |
| 27 | using Poco::Net::HTTPRequest; |
| 28 | using Poco::Net::HTTPMessage; |
| 29 | using Poco::Net::MessageHeader; |
| 30 | |
| 31 | |
| 32 | namespace |
| 33 | { |
| 34 | class StringPartHandler: public PartHandler |
| 35 | { |
| 36 | public: |
| 37 | StringPartHandler() |
| 38 | { |
| 39 | } |
| 40 | |
| 41 | void handlePart(const MessageHeader& , std::istream& stream) |
| 42 | { |
| 43 | _disp = header["Content-Disposition" ]; |
| 44 | _type = header["Content-Type" ]; |
| 45 | int ch = stream.get(); |
| 46 | while (ch > 0) |
| 47 | { |
| 48 | _data += (char) ch; |
| 49 | ch = stream.get(); |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | const std::string& data() const |
| 54 | { |
| 55 | return _data; |
| 56 | } |
| 57 | |
| 58 | const std::string& disp() const |
| 59 | { |
| 60 | return _disp; |
| 61 | } |
| 62 | |
| 63 | const std::string& type() const |
| 64 | { |
| 65 | return _type; |
| 66 | } |
| 67 | |
| 68 | private: |
| 69 | std::string _data; |
| 70 | std::string _disp; |
| 71 | std::string _type; |
| 72 | }; |
| 73 | } |
| 74 | |
| 75 | |
| 76 | HTMLFormTest::HTMLFormTest(const std::string& name): CppUnit::TestCase(name) |
| 77 | { |
| 78 | } |
| 79 | |
| 80 | |
| 81 | HTMLFormTest::~HTMLFormTest() |
| 82 | { |
| 83 | } |
| 84 | |
| 85 | |
| 86 | void HTMLFormTest::testWriteUrl() |
| 87 | { |
| 88 | HTMLForm form; |
| 89 | form.set("field1" , "value1" ); |
| 90 | form.set("field2" , "value 2" ); |
| 91 | form.set("field3" , "value=3" ); |
| 92 | form.set("field4" , "value&4" ); |
| 93 | form.set("field5" , "value+5" ); |
| 94 | |
| 95 | std::ostringstream ostr; |
| 96 | form.write(ostr); |
| 97 | std::string s = ostr.str(); |
| 98 | assertTrue (s == "field1=value1&field2=value%202&field3=value%3D3&field4=value%264&field5=value%2B5" ); |
| 99 | } |
| 100 | |
| 101 | |
| 102 | void HTMLFormTest::testWriteMultipart() |
| 103 | { |
| 104 | HTMLForm form(HTMLForm::ENCODING_MULTIPART); |
| 105 | form.set("field1" , "value1" ); |
| 106 | form.set("field2" , "value 2" ); |
| 107 | form.set("field3" , "value=3" ); |
| 108 | form.set("field4" , "value&4" ); |
| 109 | |
| 110 | form.addPart("attachment1" , new StringPartSource("This is an attachment" )); |
| 111 | StringPartSource* pSPS = new StringPartSource("This is another attachment" , "text/plain" , "att2.txt" ); |
| 112 | pSPS->headers().set("Content-ID" , "1234abcd" ); |
| 113 | form.addPart("attachment2" , pSPS); |
| 114 | |
| 115 | std::ostringstream ostr; |
| 116 | form.write(ostr, "MIME_boundary_0123456789" ); |
| 117 | std::string s = ostr.str(); |
| 118 | assertTrue (s == |
| 119 | "--MIME_boundary_0123456789\r\n" |
| 120 | "Content-Disposition: form-data; name=\"field1\"\r\n" |
| 121 | "\r\n" |
| 122 | "value1\r\n" |
| 123 | "--MIME_boundary_0123456789\r\n" |
| 124 | "Content-Disposition: form-data; name=\"field2\"\r\n" |
| 125 | "\r\n" |
| 126 | "value 2\r\n" |
| 127 | "--MIME_boundary_0123456789\r\n" |
| 128 | "Content-Disposition: form-data; name=\"field3\"\r\n" |
| 129 | "\r\n" |
| 130 | "value=3\r\n" |
| 131 | "--MIME_boundary_0123456789\r\n" |
| 132 | "Content-Disposition: form-data; name=\"field4\"\r\n" |
| 133 | "\r\n" |
| 134 | "value&4\r\n" |
| 135 | "--MIME_boundary_0123456789\r\n" |
| 136 | "Content-Disposition: form-data; name=\"attachment1\"\r\n" |
| 137 | "Content-Type: text/plain\r\n" |
| 138 | "\r\n" |
| 139 | "This is an attachment\r\n" |
| 140 | "--MIME_boundary_0123456789\r\n" |
| 141 | "Content-ID: 1234abcd\r\n" |
| 142 | "Content-Disposition: form-data; name=\"attachment2\"; filename=\"att2.txt\"\r\n" |
| 143 | "Content-Type: text/plain\r\n" |
| 144 | "\r\n" |
| 145 | "This is another attachment\r\n" |
| 146 | "--MIME_boundary_0123456789--\r\n" |
| 147 | ); |
| 148 | assertTrue (s.length() == form.calculateContentLength()); |
| 149 | |
| 150 | const HTMLForm::PartVec& parts = form.getPartList(); |
| 151 | assertTrue (parts.size() == 2); |
| 152 | assertTrue (parts[0].name() == "attachment1" ); |
| 153 | assertTrue (parts[1].name() == "attachment2" ); |
| 154 | assertTrue (parts[1].filename() == "att2.txt" ); |
| 155 | assertTrue (parts[1].headers()["Content-ID" ] == "1234abcd" ); |
| 156 | } |
| 157 | |
| 158 | |
| 159 | void HTMLFormTest::testReadUrlGET() |
| 160 | { |
| 161 | HTTPRequest req("GET" , "/form.cgi?field1=value1&field2=value%202&field3=value%3D3&field4=value%264" ); |
| 162 | HTMLForm form(req); |
| 163 | assertTrue (form.size() == 4); |
| 164 | assertTrue (form["field1" ] == "value1" ); |
| 165 | assertTrue (form["field2" ] == "value 2" ); |
| 166 | assertTrue (form["field3" ] == "value=3" ); |
| 167 | assertTrue (form["field4" ] == "value&4" ); |
| 168 | } |
| 169 | |
| 170 | |
| 171 | void HTMLFormTest::testReadUrlGETMultiple() |
| 172 | { |
| 173 | HTTPRequest req("GET" , "/form.cgi?field1=value1&field1=value%202&field1=value%3D3&field1=value%264" ); |
| 174 | HTMLForm form(req); |
| 175 | assertTrue (form.size() == 4); |
| 176 | |
| 177 | HTMLForm::ConstIterator it = form.find("field1" ); |
| 178 | assertTrue (it != form.end()); |
| 179 | assertTrue (it->first == "field1" && it->second == "value1" ); |
| 180 | ++it; |
| 181 | assertTrue (it != form.end()); |
| 182 | assertTrue (it->first == "field1" && it->second == "value 2" ); |
| 183 | ++it; |
| 184 | assertTrue (it != form.end()); |
| 185 | assertTrue (it->first == "field1" && it->second == "value=3" ); |
| 186 | ++it; |
| 187 | assertTrue (it != form.end()); |
| 188 | assertTrue (it->first == "field1" && it->second == "value&4" ); |
| 189 | ++it; |
| 190 | assertTrue (it == form.end()); |
| 191 | } |
| 192 | |
| 193 | |
| 194 | void HTMLFormTest::testReadUrlPOST() |
| 195 | { |
| 196 | HTTPRequest req("POST" , "/form.cgi?field0=value0" ); |
| 197 | std::istringstream istr("field1=value1&field2=value%202&field3=value%3D3&field4=value%264" ); |
| 198 | HTMLForm form(req, istr); |
| 199 | assertTrue (form.size() == 5); |
| 200 | assertTrue (form["field0" ] == "value0" ); |
| 201 | assertTrue (form["field1" ] == "value1" ); |
| 202 | assertTrue (form["field2" ] == "value 2" ); |
| 203 | assertTrue (form["field3" ] == "value=3" ); |
| 204 | assertTrue (form["field4" ] == "value&4" ); |
| 205 | } |
| 206 | |
| 207 | |
| 208 | void HTMLFormTest::testReadUrlPUT() |
| 209 | { |
| 210 | HTTPRequest req("PUT" , "/form.cgi?field0=value0" ); |
| 211 | std::istringstream istr("field1=value1&field2=value%202&field3=value%3D3&field4=value%264" ); |
| 212 | HTMLForm form(req, istr); |
| 213 | assertTrue (form.size() == 5); |
| 214 | assertTrue (form["field0" ] == "value0" ); |
| 215 | assertTrue (form["field1" ] == "value1" ); |
| 216 | assertTrue (form["field2" ] == "value 2" ); |
| 217 | assertTrue (form["field3" ] == "value=3" ); |
| 218 | assertTrue (form["field4" ] == "value&4" ); |
| 219 | } |
| 220 | |
| 221 | |
| 222 | void HTMLFormTest::testReadUrlBOM() |
| 223 | { |
| 224 | HTTPRequest req("PUT" , "/form.cgi?field0=value0" ); |
| 225 | std::istringstream istr("\357\273\277field1=value1&field2=value%202&field3=value%3D3&field4=value%264" ); |
| 226 | HTMLForm form(req, istr); |
| 227 | assertTrue (form.size() == 5); |
| 228 | assertTrue (form["field0" ] == "value0" ); |
| 229 | assertTrue (form["field1" ] == "value1" ); |
| 230 | assertTrue (form["field2" ] == "value 2" ); |
| 231 | assertTrue (form["field3" ] == "value=3" ); |
| 232 | assertTrue (form["field4" ] == "value&4" ); |
| 233 | } |
| 234 | |
| 235 | |
| 236 | void HTMLFormTest::testReadMultipart() |
| 237 | { |
| 238 | std::istringstream istr( |
| 239 | "\r\n" |
| 240 | "--MIME_boundary_0123456789\r\n" |
| 241 | "Content-Disposition: form-data; name=\"field1\"\r\n" |
| 242 | "\r\n" |
| 243 | "value1\r\n" |
| 244 | "--MIME_boundary_0123456789\r\n" |
| 245 | "Content-Disposition: form-data; name=\"field2\"\r\n" |
| 246 | "\r\n" |
| 247 | "value 2\r\n" |
| 248 | "--MIME_boundary_0123456789\r\n" |
| 249 | "Content-Disposition: form-data; name=\"field3\"\r\n" |
| 250 | "\r\n" |
| 251 | "value=3\r\n" |
| 252 | "--MIME_boundary_0123456789\r\n" |
| 253 | "Content-Disposition: form-data; name=\"field4\"\r\n" |
| 254 | "\r\n" |
| 255 | "value&4\r\n" |
| 256 | "--MIME_boundary_0123456789\r\n" |
| 257 | "Content-Disposition: file; name=\"attachment1\"; filename=\"att1.txt\"\r\n" |
| 258 | "Content-Type: text/plain\r\n" |
| 259 | "\r\n" |
| 260 | "This is an attachment\r\n" |
| 261 | "--MIME_boundary_0123456789--\r\n" |
| 262 | ); |
| 263 | HTTPRequest req("POST" , "/form.cgi" ); |
| 264 | req.setContentType(HTMLForm::ENCODING_MULTIPART + "; boundary=\"MIME_boundary_0123456789\"" ); |
| 265 | StringPartHandler sah; |
| 266 | HTMLForm form(req, istr, sah); |
| 267 | assertTrue (form.size() == 4); |
| 268 | assertTrue (form["field1" ] == "value1" ); |
| 269 | assertTrue (form["field2" ] == "value 2" ); |
| 270 | assertTrue (form["field3" ] == "value=3" ); |
| 271 | assertTrue (form["field4" ] == "value&4" ); |
| 272 | |
| 273 | assertTrue (sah.type() == "text/plain" ); |
| 274 | assertTrue (sah.disp() == "file; name=\"attachment1\"; filename=\"att1.txt\"" ); |
| 275 | assertTrue (sah.data() == "This is an attachment" ); |
| 276 | } |
| 277 | |
| 278 | |
| 279 | void HTMLFormTest::testSubmit1() |
| 280 | { |
| 281 | HTMLForm form; |
| 282 | form.set("field1" , "value1" ); |
| 283 | form.set("field2" , "value 2" ); |
| 284 | form.set("field3" , "value=3" ); |
| 285 | form.set("field4" , "value&4" ); |
| 286 | |
| 287 | HTTPRequest req("GET" , "/form.cgi" ); |
| 288 | form.prepareSubmit(req); |
| 289 | assertTrue (req.getURI() == "/form.cgi?field1=value1&field2=value%202&field3=value%3D3&field4=value%264" ); |
| 290 | } |
| 291 | |
| 292 | |
| 293 | void HTMLFormTest::testSubmit2() |
| 294 | { |
| 295 | HTMLForm form; |
| 296 | form.set("field1" , "value1" ); |
| 297 | form.set("field2" , "value 2" ); |
| 298 | form.set("field3" , "value=3" ); |
| 299 | form.set("field4" , "value&4" ); |
| 300 | |
| 301 | HTTPRequest req("POST" , "/form.cgi" ); |
| 302 | form.prepareSubmit(req); |
| 303 | assertTrue (req.getContentType() == HTMLForm::ENCODING_URL); |
| 304 | assertTrue (req.getContentLength() == 64); |
| 305 | } |
| 306 | |
| 307 | |
| 308 | void HTMLFormTest::testSubmit3() |
| 309 | { |
| 310 | HTMLForm form(HTMLForm::ENCODING_MULTIPART); |
| 311 | form.set("field1" , "value1" ); |
| 312 | form.set("field2" , "value 2" ); |
| 313 | form.set("field3" , "value=3" ); |
| 314 | form.set("field4" , "value&4" ); |
| 315 | |
| 316 | HTTPRequest req("POST" , "/form.cgi" , HTTPMessage::HTTP_1_1); |
| 317 | form.prepareSubmit(req); |
| 318 | std::string expCT(HTMLForm::ENCODING_MULTIPART); |
| 319 | expCT.append("; boundary=\"" ); |
| 320 | expCT.append(form.boundary()); |
| 321 | expCT.append("\"" ); |
| 322 | assertTrue (req.getContentType() == expCT); |
| 323 | assertTrue (req.getChunkedTransferEncoding()); |
| 324 | } |
| 325 | |
| 326 | |
| 327 | void HTMLFormTest::testSubmit4() |
| 328 | { |
| 329 | HTMLForm form; |
| 330 | form.add("field1" , "value1" ); |
| 331 | form.add("field1" , "value 2" ); |
| 332 | form.add("field1" , "value=3" ); |
| 333 | form.add("field1" , "value&4" ); |
| 334 | |
| 335 | HTTPRequest req("GET" , "/form.cgi" ); |
| 336 | form.prepareSubmit(req); |
| 337 | |
| 338 | assertTrue (req.getURI() == "/form.cgi?field1=value1&field1=value%202&field1=value%3D3&field1=value%264" ); |
| 339 | } |
| 340 | |
| 341 | |
| 342 | void HTMLFormTest::testSubmit5() |
| 343 | { |
| 344 | HTMLForm form(HTMLForm::ENCODING_MULTIPART); |
| 345 | form.set("field1" , "value1" ); |
| 346 | form.set("field2" , "value 2" ); |
| 347 | form.set("field3" , "value=3" ); |
| 348 | form.set("field4" , "value&4" ); |
| 349 | |
| 350 | HTTPRequest req("POST" , "/form.cgi" , HTTPMessage::HTTP_1_1); |
| 351 | form.prepareSubmit(req, HTMLForm::OPT_USE_CONTENT_LENGTH); |
| 352 | std::string expCT(HTMLForm::ENCODING_MULTIPART); |
| 353 | expCT.append("; boundary=\"" ); |
| 354 | expCT.append(form.boundary()); |
| 355 | expCT.append("\"" ); |
| 356 | assertTrue (req.getContentType() == expCT); |
| 357 | assertTrue (req.getContentLength() == 403); |
| 358 | } |
| 359 | |
| 360 | |
| 361 | void HTMLFormTest::testFieldLimitUrl() |
| 362 | { |
| 363 | HTTPRequest req("GET" , "/form.cgi?field1=value1&field2=value%202&field3=value%3D3&field4=value%264" ); |
| 364 | HTMLForm form; |
| 365 | form.setFieldLimit(3); |
| 366 | try |
| 367 | { |
| 368 | form.load(req); |
| 369 | fail("field limit violated - must throw" ); |
| 370 | } |
| 371 | catch (Poco::Net::HTMLFormException&) |
| 372 | { |
| 373 | } |
| 374 | } |
| 375 | |
| 376 | |
| 377 | void HTMLFormTest::testFieldLimitMultipart() |
| 378 | { |
| 379 | std::istringstream istr( |
| 380 | "\r\n" |
| 381 | "--MIME_boundary_0123456789\r\n" |
| 382 | "Content-Disposition: form-data; name=\"field1\"\r\n" |
| 383 | "\r\n" |
| 384 | "value1\r\n" |
| 385 | "--MIME_boundary_0123456789\r\n" |
| 386 | "Content-Disposition: form-data; name=\"field2\"\r\n" |
| 387 | "\r\n" |
| 388 | "value 2\r\n" |
| 389 | "--MIME_boundary_0123456789\r\n" |
| 390 | "Content-Disposition: form-data; name=\"field3\"\r\n" |
| 391 | "\r\n" |
| 392 | "value=3\r\n" |
| 393 | "--MIME_boundary_0123456789\r\n" |
| 394 | "Content-Disposition: form-data; name=\"field4\"\r\n" |
| 395 | "\r\n" |
| 396 | "value&4\r\n" |
| 397 | "--MIME_boundary_0123456789\r\n" |
| 398 | "Content-Disposition: file; name=\"attachment1\"; filename=\"att1.txt\"\r\n" |
| 399 | "Content-Type: text/plain\r\n" |
| 400 | "\r\n" |
| 401 | "This is an attachment\r\n" |
| 402 | "--MIME_boundary_0123456789--\r\n" |
| 403 | ); |
| 404 | HTTPRequest req("POST" , "/form.cgi" ); |
| 405 | req.setContentType(HTMLForm::ENCODING_MULTIPART + "; boundary=\"MIME_boundary_0123456789\"" ); |
| 406 | StringPartHandler sah; |
| 407 | HTMLForm form; |
| 408 | form.setFieldLimit(3); |
| 409 | try |
| 410 | { |
| 411 | form.load(req, istr, sah); |
| 412 | fail("field limit violated - must throw" ); |
| 413 | } |
| 414 | catch (Poco::Net::HTMLFormException&) |
| 415 | { |
| 416 | } |
| 417 | } |
| 418 | |
| 419 | |
| 420 | void HTMLFormTest::setUp() |
| 421 | { |
| 422 | } |
| 423 | |
| 424 | |
| 425 | void HTMLFormTest::tearDown() |
| 426 | { |
| 427 | } |
| 428 | |
| 429 | |
| 430 | CppUnit::Test* HTMLFormTest::suite() |
| 431 | { |
| 432 | CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("HTMLFormTest" ); |
| 433 | |
| 434 | CppUnit_addTest(pSuite, HTMLFormTest, testWriteUrl); |
| 435 | CppUnit_addTest(pSuite, HTMLFormTest, testWriteMultipart); |
| 436 | CppUnit_addTest(pSuite, HTMLFormTest, testReadUrlGET); |
| 437 | CppUnit_addTest(pSuite, HTMLFormTest, testReadUrlGETMultiple); |
| 438 | CppUnit_addTest(pSuite, HTMLFormTest, testReadUrlPOST); |
| 439 | CppUnit_addTest(pSuite, HTMLFormTest, testReadUrlPUT); |
| 440 | CppUnit_addTest(pSuite, HTMLFormTest, testReadUrlBOM); |
| 441 | CppUnit_addTest(pSuite, HTMLFormTest, testReadMultipart); |
| 442 | CppUnit_addTest(pSuite, HTMLFormTest, testSubmit1); |
| 443 | CppUnit_addTest(pSuite, HTMLFormTest, testSubmit2); |
| 444 | CppUnit_addTest(pSuite, HTMLFormTest, testSubmit3); |
| 445 | CppUnit_addTest(pSuite, HTMLFormTest, testSubmit4); |
| 446 | CppUnit_addTest(pSuite, HTMLFormTest, testSubmit5); |
| 447 | CppUnit_addTest(pSuite, HTMLFormTest, testFieldLimitUrl); |
| 448 | CppUnit_addTest(pSuite, HTMLFormTest, testFieldLimitMultipart); |
| 449 | |
| 450 | return pSuite; |
| 451 | } |
| 452 | |