Mercurial > hg > orthanc
comparison UnitTestsSources/RestApiTests.cpp @ 3398:4acd1431e603
new classes: StringMatcher and MultipartStreamReader
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 07 Jun 2019 13:36:43 +0200 |
parents | 4e43e67f8ecf |
children | 4e8205871967 |
comparison
equal
deleted
inserted
replaced
3397:9019279dbfd7 | 3398:4acd1431e603 |
---|---|
45 #include "../Core/RestApi/RestApi.h" | 45 #include "../Core/RestApi/RestApi.h" |
46 #include "../Core/OrthancException.h" | 46 #include "../Core/OrthancException.h" |
47 #include "../Core/Compression/ZlibCompressor.h" | 47 #include "../Core/Compression/ZlibCompressor.h" |
48 #include "../Core/RestApi/RestApiHierarchy.h" | 48 #include "../Core/RestApi/RestApiHierarchy.h" |
49 #include "../Core/HttpServer/HttpContentNegociation.h" | 49 #include "../Core/HttpServer/HttpContentNegociation.h" |
50 #include "../Core/HttpServer/MultipartStreamReader.h" | |
51 | |
50 | 52 |
51 using namespace Orthanc; | 53 using namespace Orthanc; |
52 | 54 |
53 #if !defined(UNIT_TESTS_WITH_HTTP_CONNEXIONS) | 55 #if !defined(UNIT_TESTS_WITH_HTTP_CONNEXIONS) |
54 #error "Please set UNIT_TESTS_WITH_HTTP_CONNEXIONS" | 56 #error "Please set UNIT_TESTS_WITH_HTTP_CONNEXIONS" |
351 | 353 |
352 | 354 |
353 | 355 |
354 namespace | 356 namespace |
355 { | 357 { |
356 class AcceptHandler : public Orthanc::HttpContentNegociation::IHandler | 358 class AcceptHandler : public HttpContentNegociation::IHandler |
357 { | 359 { |
358 private: | 360 private: |
359 std::string type_; | 361 std::string type_; |
360 std::string subtype_; | 362 std::string subtype_; |
361 | 363 |
395 // Reference: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1 | 397 // Reference: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1 |
396 | 398 |
397 AcceptHandler h; | 399 AcceptHandler h; |
398 | 400 |
399 { | 401 { |
400 Orthanc::HttpContentNegociation d; | 402 HttpContentNegociation d; |
401 d.Register("audio/mp3", h); | 403 d.Register("audio/mp3", h); |
402 d.Register("audio/basic", h); | 404 d.Register("audio/basic", h); |
403 | 405 |
404 ASSERT_TRUE(d.Apply("audio/*; q=0.2, audio/basic")); | 406 ASSERT_TRUE(d.Apply("audio/*; q=0.2, audio/basic")); |
405 ASSERT_EQ("audio", h.GetType()); | 407 ASSERT_EQ("audio", h.GetType()); |
420 // text/x-dvi entity, and if that does not exist, send the | 422 // text/x-dvi entity, and if that does not exist, send the |
421 // text/plain entity."" | 423 // text/plain entity."" |
422 const std::string T1 = "text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c"; | 424 const std::string T1 = "text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c"; |
423 | 425 |
424 { | 426 { |
425 Orthanc::HttpContentNegociation d; | 427 HttpContentNegociation d; |
426 d.Register("text/plain", h); | 428 d.Register("text/plain", h); |
427 d.Register("text/html", h); | 429 d.Register("text/html", h); |
428 d.Register("text/x-dvi", h); | 430 d.Register("text/x-dvi", h); |
429 ASSERT_TRUE(d.Apply(T1)); | 431 ASSERT_TRUE(d.Apply(T1)); |
430 ASSERT_EQ("text", h.GetType()); | 432 ASSERT_EQ("text", h.GetType()); |
431 ASSERT_EQ("html", h.GetSubType()); | 433 ASSERT_EQ("html", h.GetSubType()); |
432 } | 434 } |
433 | 435 |
434 { | 436 { |
435 Orthanc::HttpContentNegociation d; | 437 HttpContentNegociation d; |
436 d.Register("text/plain", h); | 438 d.Register("text/plain", h); |
437 d.Register("text/x-dvi", h); | 439 d.Register("text/x-dvi", h); |
438 d.Register("text/x-c", h); | 440 d.Register("text/x-c", h); |
439 ASSERT_TRUE(d.Apply(T1)); | 441 ASSERT_TRUE(d.Apply(T1)); |
440 ASSERT_EQ("text", h.GetType()); | 442 ASSERT_EQ("text", h.GetType()); |
441 ASSERT_EQ("x-c", h.GetSubType()); | 443 ASSERT_EQ("x-c", h.GetSubType()); |
442 } | 444 } |
443 | 445 |
444 { | 446 { |
445 Orthanc::HttpContentNegociation d; | 447 HttpContentNegociation d; |
446 d.Register("text/plain", h); | 448 d.Register("text/plain", h); |
447 d.Register("text/x-dvi", h); | 449 d.Register("text/x-dvi", h); |
448 d.Register("text/x-c", h); | 450 d.Register("text/x-c", h); |
449 d.Register("text/html", h); | 451 d.Register("text/html", h); |
450 ASSERT_TRUE(d.Apply(T1)); | 452 ASSERT_TRUE(d.Apply(T1)); |
451 ASSERT_EQ("text", h.GetType()); | 453 ASSERT_EQ("text", h.GetType()); |
452 ASSERT_TRUE(h.GetSubType() == "x-c" || h.GetSubType() == "html"); | 454 ASSERT_TRUE(h.GetSubType() == "x-c" || h.GetSubType() == "html"); |
453 } | 455 } |
454 | 456 |
455 { | 457 { |
456 Orthanc::HttpContentNegociation d; | 458 HttpContentNegociation d; |
457 d.Register("text/plain", h); | 459 d.Register("text/plain", h); |
458 d.Register("text/x-dvi", h); | 460 d.Register("text/x-dvi", h); |
459 ASSERT_TRUE(d.Apply(T1)); | 461 ASSERT_TRUE(d.Apply(T1)); |
460 ASSERT_EQ("text", h.GetType()); | 462 ASSERT_EQ("text", h.GetType()); |
461 ASSERT_EQ("x-dvi", h.GetSubType()); | 463 ASSERT_EQ("x-dvi", h.GetSubType()); |
462 } | 464 } |
463 | 465 |
464 { | 466 { |
465 Orthanc::HttpContentNegociation d; | 467 HttpContentNegociation d; |
466 d.Register("text/plain", h); | 468 d.Register("text/plain", h); |
467 ASSERT_TRUE(d.Apply(T1)); | 469 ASSERT_TRUE(d.Apply(T1)); |
468 ASSERT_EQ("text", h.GetType()); | 470 ASSERT_EQ("text", h.GetType()); |
469 ASSERT_EQ("plain", h.GetSubType()); | 471 ASSERT_EQ("plain", h.GetSubType()); |
470 } | 472 } |
641 ASSERT_TRUE(p.LookupUserProperty(s, "a")); ASSERT_TRUE(s == "b"); | 643 ASSERT_TRUE(p.LookupUserProperty(s, "a")); ASSERT_TRUE(s == "b"); |
642 ASSERT_TRUE(p.LookupUserProperty(s, "Hello")); ASSERT_TRUE(s == "world"); | 644 ASSERT_TRUE(p.LookupUserProperty(s, "Hello")); ASSERT_TRUE(s == "world"); |
643 ASSERT_FALSE(p.LookupUserProperty(s, "hello")); | 645 ASSERT_FALSE(p.LookupUserProperty(s, "hello")); |
644 } | 646 } |
645 } | 647 } |
648 | |
649 | |
650 TEST(StringMatcher, Basic) | |
651 { | |
652 StringMatcher matcher("---"); | |
653 | |
654 ASSERT_THROW(matcher.GetMatchBegin(), OrthancException); | |
655 | |
656 { | |
657 const std::string s = ""; | |
658 ASSERT_FALSE(matcher.Apply(s)); | |
659 } | |
660 | |
661 { | |
662 const std::string s = "abc----def"; | |
663 ASSERT_TRUE(matcher.Apply(s)); | |
664 ASSERT_EQ(3, std::distance(s.begin(), matcher.GetMatchBegin())); | |
665 ASSERT_EQ("---", std::string(matcher.GetMatchBegin(), matcher.GetMatchEnd())); | |
666 } | |
667 | |
668 { | |
669 const std::string s = "abc---"; | |
670 ASSERT_TRUE(matcher.Apply(s)); | |
671 ASSERT_EQ(3, std::distance(s.begin(), matcher.GetMatchBegin())); | |
672 ASSERT_EQ(s.end(), matcher.GetMatchEnd()); | |
673 ASSERT_EQ("---", std::string(matcher.GetMatchBegin(), matcher.GetMatchEnd())); | |
674 ASSERT_EQ("", std::string(matcher.GetMatchEnd(), s.end())); | |
675 } | |
676 | |
677 { | |
678 const std::string s = "abc--def"; | |
679 ASSERT_FALSE(matcher.Apply(s)); | |
680 ASSERT_THROW(matcher.GetMatchBegin(), OrthancException); | |
681 ASSERT_THROW(matcher.GetMatchEnd(), OrthancException); | |
682 } | |
683 | |
684 { | |
685 std::string s(10u, '\0'); // String with null values | |
686 ASSERT_EQ(10u, s.size()); | |
687 ASSERT_EQ(10u, s.size()); | |
688 ASSERT_FALSE(matcher.Apply(s)); | |
689 | |
690 s[9] = '-'; | |
691 ASSERT_FALSE(matcher.Apply(s)); | |
692 | |
693 s[8] = '-'; | |
694 ASSERT_FALSE(matcher.Apply(s)); | |
695 | |
696 s[7] = '-'; | |
697 ASSERT_TRUE(matcher.Apply(s)); | |
698 ASSERT_EQ(s.c_str() + 7, matcher.GetPointerBegin()); | |
699 ASSERT_EQ(s.c_str() + 10, matcher.GetPointerEnd()); | |
700 ASSERT_EQ(s.end() - 3, matcher.GetMatchBegin()); | |
701 ASSERT_EQ(s.end(), matcher.GetMatchEnd()); | |
702 } | |
703 } | |
704 | |
705 | |
706 | |
707 class MultipartTester : public MultipartStreamReader::IHandler | |
708 { | |
709 private: | |
710 struct Part | |
711 { | |
712 MultipartStreamReader::HttpHeaders headers_; | |
713 std::string data_; | |
714 | |
715 Part(const MultipartStreamReader::HttpHeaders& headers, | |
716 const void* part, | |
717 size_t size) : | |
718 headers_(headers), | |
719 data_(reinterpret_cast<const char*>(part), size) | |
720 { | |
721 } | |
722 }; | |
723 | |
724 std::vector<Part> parts_; | |
725 | |
726 public: | |
727 virtual void Apply(const MultipartStreamReader::HttpHeaders& headers, | |
728 const void* part, | |
729 size_t size) | |
730 { | |
731 parts_.push_back(Part(headers, part, size)); | |
732 } | |
733 | |
734 unsigned int GetCount() const | |
735 { | |
736 return parts_.size(); | |
737 } | |
738 | |
739 MultipartStreamReader::HttpHeaders& GetHeaders(size_t i) | |
740 { | |
741 return parts_[i].headers_; | |
742 } | |
743 | |
744 const std::string& GetData(size_t i) const | |
745 { | |
746 return parts_[i].data_; | |
747 } | |
748 }; | |
749 | |
750 | |
751 TEST(MultipartStreamReader, ParseHeaders) | |
752 { | |
753 std::string ct, b, st; | |
754 | |
755 { | |
756 MultipartStreamReader::HttpHeaders h; | |
757 h["hello"] = "world"; | |
758 h["Content-Type"] = "world"; // Should be in lower-case | |
759 h["CONTENT-type"] = "world"; // Should be in lower-case | |
760 ASSERT_FALSE(MultipartStreamReader::GetMainContentType(ct, h)); | |
761 ASSERT_FALSE(MultipartStreamReader::ParseMultipartHeaders(ct, st, b, h)); | |
762 } | |
763 | |
764 { | |
765 MultipartStreamReader::HttpHeaders h; | |
766 h["content-type"] = "world"; | |
767 ASSERT_TRUE(MultipartStreamReader::GetMainContentType(ct, h)); | |
768 ASSERT_EQ(ct, "world"); | |
769 ASSERT_FALSE(MultipartStreamReader::ParseMultipartHeaders(ct, st, b, h)); | |
770 } | |
771 | |
772 { | |
773 MultipartStreamReader::HttpHeaders h; | |
774 h["content-type"] = "multipart/related; dummy=value; boundary=1234; hello=world"; | |
775 ASSERT_TRUE(MultipartStreamReader::GetMainContentType(ct, h)); | |
776 ASSERT_EQ(ct, h["content-type"]); | |
777 ASSERT_TRUE(MultipartStreamReader::ParseMultipartHeaders(ct, st, b, h)); | |
778 ASSERT_EQ(ct, "multipart/related"); | |
779 ASSERT_EQ(b, "1234"); | |
780 ASSERT_TRUE(st.empty()); | |
781 } | |
782 | |
783 { | |
784 MultipartStreamReader::HttpHeaders h; | |
785 h["content-type"] = "multipart/related; boundary="; | |
786 ASSERT_TRUE(MultipartStreamReader::GetMainContentType(ct, h)); | |
787 ASSERT_EQ(ct, h["content-type"]); | |
788 ASSERT_FALSE(MultipartStreamReader::ParseMultipartHeaders(ct, st, b, h)); // Empty boundary | |
789 } | |
790 | |
791 { | |
792 MultipartStreamReader::HttpHeaders h; | |
793 h["content-type"] = "Multipart/Related; TYPE=Application/Dicom; Boundary=heLLO"; | |
794 ASSERT_TRUE(MultipartStreamReader::ParseMultipartHeaders(ct, st, b, h)); | |
795 ASSERT_EQ(ct, "multipart/related"); | |
796 ASSERT_EQ(b, "heLLO"); | |
797 ASSERT_EQ(st, "application/dicom"); | |
798 } | |
799 | |
800 { | |
801 MultipartStreamReader::HttpHeaders h; | |
802 h["content-type"] = "Multipart/Related; type=\"application/DICOM\"; Boundary=a"; | |
803 ASSERT_TRUE(MultipartStreamReader::ParseMultipartHeaders(ct, st, b, h)); | |
804 ASSERT_EQ(ct, "multipart/related"); | |
805 ASSERT_EQ(b, "a"); | |
806 ASSERT_EQ(st, "application/dicom"); | |
807 } | |
808 } | |
809 | |
810 | |
811 TEST(MultipartStreamReader, BytePerByte) | |
812 { | |
813 std::string stream = "GARBAGE"; | |
814 | |
815 std::string boundary = "123456789123456789"; | |
816 | |
817 { | |
818 for (size_t i = 0; i < 10; i++) | |
819 { | |
820 std::string f = "hello " + boost::lexical_cast<std::string>(i); | |
821 | |
822 stream += "\r\n--" + boundary + "\r\n"; | |
823 if (i % 2 == 0) | |
824 stream += "Content-Length: " + boost::lexical_cast<std::string>(f.size()) + "\r\n"; | |
825 stream += "Content-Type: toto " + boost::lexical_cast<std::string>(i) + "\r\n\r\n"; | |
826 stream += f; | |
827 } | |
828 | |
829 stream += "\r\n--" + boundary + "--"; | |
830 stream += "GARBAGE"; | |
831 } | |
832 | |
833 for (unsigned int k = 0; k < 2; k++) | |
834 { | |
835 MultipartTester decoded; | |
836 | |
837 MultipartStreamReader reader(boundary); | |
838 reader.SetBlockSize(1); | |
839 reader.SetHandler(decoded); | |
840 | |
841 if (k == 0) | |
842 { | |
843 for (size_t i = 0; i < stream.size(); i++) | |
844 { | |
845 reader.AddChunk(&stream[i], 1); | |
846 } | |
847 } | |
848 else | |
849 { | |
850 reader.AddChunk(stream); | |
851 } | |
852 | |
853 reader.CloseStream(); | |
854 | |
855 ASSERT_EQ(10u, decoded.GetCount()); | |
856 | |
857 for (size_t i = 0; i < 10; i++) | |
858 { | |
859 ASSERT_EQ("hello " + boost::lexical_cast<std::string>(i), decoded.GetData(i)); | |
860 ASSERT_EQ("toto " + boost::lexical_cast<std::string>(i), decoded.GetHeaders(i)["content-type"]); | |
861 | |
862 if (i % 2 == 0) | |
863 { | |
864 ASSERT_EQ(2u, decoded.GetHeaders(i).size()); | |
865 ASSERT_TRUE(decoded.GetHeaders(i).find("content-length") != decoded.GetHeaders(i).end()); | |
866 } | |
867 } | |
868 } | |
869 } |