comparison UnitTestsSources/RestApiTests.cpp @ 1782:9f2df9bb2cdf

fix
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 18 Nov 2015 09:05:43 +0100
parents 5ad4e4d92ecb
children dbb07eb1a2f3
comparison
equal deleted inserted replaced
1781:5ad4e4d92ecb 1782:9f2df9bb2cdf
341 341
342 342
343 343
344 namespace Orthanc 344 namespace Orthanc
345 { 345 {
346 class AcceptMediaDispatcher : public boost::noncopyable 346 class HttpContentNegociation : public boost::noncopyable
347 { 347 {
348 public: 348 public:
349 typedef std::map<std::string, std::string> HttpHeaders; 349 typedef std::map<std::string, std::string> HttpHeaders;
350 350
351 class IHandler : public boost::noncopyable 351 class IHandler : public boost::noncopyable
354 virtual ~IHandler() 354 virtual ~IHandler()
355 { 355 {
356 } 356 }
357 357
358 virtual void Handle(const std::string& type, 358 virtual void Handle(const std::string& type,
359 const std::string& subtype, 359 const std::string& subtype) = 0;
360 float quality /* between 0 and 1 */) = 0;
361 }; 360 };
362 361
363 private: 362 private:
364 struct Handler 363 struct Handler
365 { 364 {
389 return true; 388 return true;
390 } 389 }
391 390
392 return type == type_ && subtype == subtype_; 391 return type == type_ && subtype == subtype_;
393 } 392 }
393
394 void Call() const
395 {
396 handler_.Handle(type_, subtype_);
397 }
394 }; 398 };
395 399
396 400
397 struct Reference : public boost::noncopyable 401 struct Reference : public boost::noncopyable
398 { 402 {
399 const Handler& handler_; 403 const Handler& handler_;
400 uint8_t level_; 404 uint8_t level_;
401 float quality_; 405 float quality_;
402 size_t specificity_; // Number of arguments
403 406
404 Reference(const Handler& handler, 407 Reference(const Handler& handler,
405 const std::string& type, 408 const std::string& type,
406 const std::string& subtype, 409 const std::string& subtype,
407 float quality, 410 float quality) :
408 size_t specificity) :
409 handler_(handler), 411 handler_(handler),
410 quality_(quality), 412 quality_(quality)
411 specificity_(specificity)
412 { 413 {
413 if (type == "*" && subtype == "*") 414 if (type == "*" && subtype == "*")
414 { 415 {
415 level_ = 0; 416 level_ = 0;
416 } 417 }
434 if (level_ > other.level_) 435 if (level_ > other.level_)
435 { 436 {
436 return false; 437 return false;
437 } 438 }
438 439
439 return specificity_ < other.specificity_; 440 return quality_ < other.quality_;
440 }
441
442 void Call() const
443 {
444 handler_.handler_.Handle(handler_.type_, handler_.subtype_, quality_);
445 } 441 }
446 }; 442 };
447 443
448 444
449 typedef std::vector<std::string> Tokens; 445 typedef std::vector<std::string> Tokens;
470 return true; 466 return true;
471 } 467 }
472 } 468 }
473 469
474 470
475 static void GetQualityAndSpecificity(float& quality /* out */, 471 static float GetQuality(const Tokens& parameters)
476 size_t& specificity /* out */, 472 {
477 const Tokens& parameters)
478 {
479 assert(!parameters.empty());
480
481 quality = 1.0f;
482 specificity = parameters.size() - 1;
483
484 for (size_t i = 1; i < parameters.size(); i++) 473 for (size_t i = 1; i < parameters.size(); i++)
485 { 474 {
486 std::string key, value; 475 std::string key, value;
487 if (SplitPair(key, value, parameters[i], '=') && 476 if (SplitPair(key, value, parameters[i], '=') &&
488 key == "q") 477 key == "q")
489 { 478 {
479 float quality;
490 bool ok = false; 480 bool ok = false;
491 481
492 try 482 try
493 { 483 {
494 quality = boost::lexical_cast<float>(value); 484 quality = boost::lexical_cast<float>(value);
498 { 488 {
499 } 489 }
500 490
501 if (ok) 491 if (ok)
502 { 492 {
503 assert(parameters.size() >= 2); 493 return quality;
504 specificity = parameters.size() - 2;
505 return;
506 } 494 }
507 else 495 else
508 { 496 {
509 LOG(ERROR) << "Quality parameter out of range in a HTTP request (must be between 0 and 1): " << value; 497 LOG(ERROR) << "Quality parameter out of range in a HTTP request (must be between 0 and 1): " << value;
510 throw OrthancException(ErrorCode_ParameterOutOfRange); 498 throw OrthancException(ErrorCode_BadRequest);
511 } 499 }
512 } 500 }
513 } 501 }
502
503 return 1.0f; // Default quality
514 } 504 }
515 505
516 506
517 static void SelectBestMatch(std::auto_ptr<Reference>& best, 507 static void SelectBestMatch(std::auto_ptr<Reference>& best,
518 const Handler& handler, 508 const Handler& handler,
519 const std::string& type, 509 const std::string& type,
520 const std::string& subtype, 510 const std::string& subtype,
521 float quality, 511 float quality)
522 size_t specificity) 512 {
523 { 513 std::auto_ptr<Reference> match(new Reference(handler, type, subtype, quality));
524 std::auto_ptr<Reference> match(new Reference(handler, type, subtype, quality, specificity));
525 514
526 if (best.get() == NULL || 515 if (best.get() == NULL ||
527 *best < *match) 516 *best < *match)
528 { 517 {
529 best = match; 518 best = match;
565 554
566 555
567 bool Apply(const std::string& accept) 556 bool Apply(const std::string& accept)
568 { 557 {
569 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1 558 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
559 // https://en.wikipedia.org/wiki/Content_negotiation
560 // http://www.newmediacampaigns.com/blog/browser-rest-http-accept-headers
570 561
571 Tokens mediaRanges; 562 Tokens mediaRanges;
572 Toolbox::TokenizeString(mediaRanges, accept, ','); 563 Toolbox::TokenizeString(mediaRanges, accept, ',');
573 564
574 std::auto_ptr<Reference> bestMatch; 565 std::auto_ptr<Reference> bestMatch;
575 566
576 for (Tokens::const_reverse_iterator it = mediaRanges.rbegin(); 567 for (Tokens::const_iterator it = mediaRanges.begin();
577 it != mediaRanges.rend(); ++it) 568 it != mediaRanges.end(); ++it)
578 { 569 {
579 Tokens parameters; 570 Tokens parameters;
580 Toolbox::TokenizeString(parameters, *it, ';'); 571 Toolbox::TokenizeString(parameters, *it, ';');
581 572
582 if (parameters.size() > 0) 573 if (parameters.size() > 0)
583 { 574 {
584 float quality; 575 float quality = GetQuality(parameters);
585 size_t specificity;
586 GetQualityAndSpecificity(quality, specificity, parameters);
587 576
588 std::string type, subtype; 577 std::string type, subtype;
589 if (SplitPair(type, subtype, parameters[0], '/')) 578 if (SplitPair(type, subtype, parameters[0], '/'))
590 { 579 {
591 for (Handlers::const_iterator it2 = handlers_.begin(); 580 for (Handlers::const_iterator it2 = handlers_.begin();
592 it2 != handlers_.end(); ++it2) 581 it2 != handlers_.end(); ++it2)
593 { 582 {
594 if (it2->IsMatch(type, subtype)) 583 if (it2->IsMatch(type, subtype))
595 { 584 {
596 SelectBestMatch(bestMatch, *it2, type, subtype, quality, specificity); 585 SelectBestMatch(bestMatch, *it2, type, subtype, quality);
597 } 586 }
598 } 587 }
599 } 588 }
600 } 589 }
601 } 590 }
604 { 593 {
605 return false; 594 return false;
606 } 595 }
607 else 596 else
608 { 597 {
609 bestMatch->Call(); 598 bestMatch->handler_.Call();
610 return true; 599 return true;
611 } 600 }
612 } 601 }
613 }; 602 };
614 } 603 }
615 604
616 605
617 606
618 namespace 607 namespace
619 { 608 {
620 class AcceptHandler : public Orthanc::AcceptMediaDispatcher::IHandler 609 class AcceptHandler : public Orthanc::HttpContentNegociation::IHandler
621 { 610 {
622 private: 611 private:
623 std::string type_; 612 std::string type_;
624 std::string subtype_; 613 std::string subtype_;
625 float quality_;
626 614
627 public: 615 public:
628 AcceptHandler() 616 AcceptHandler()
629 { 617 {
630 Reset(); 618 Reset();
631 } 619 }
632 620
633 void Reset() 621 void Reset()
634 { 622 {
635 Handle("nope", "nope", 0.0f); 623 Handle("nope", "nope");
636 } 624 }
637 625
638 const std::string& GetType() const 626 const std::string& GetType() const
639 { 627 {
640 return type_; 628 return type_;
643 const std::string& GetSubType() const 631 const std::string& GetSubType() const
644 { 632 {
645 return subtype_; 633 return subtype_;
646 } 634 }
647 635
648 float GetQuality() const
649 {
650 return quality_;
651 }
652
653 virtual void Handle(const std::string& type, 636 virtual void Handle(const std::string& type,
654 const std::string& subtype, 637 const std::string& subtype)
655 float quality /* between 0 and 1 */)
656 { 638 {
657 type_ = type; 639 type_ = type;
658 subtype_ = subtype; 640 subtype_ = subtype;
659 quality_ = quality;
660 } 641 }
661 }; 642 };
662 } 643 }
663 644
664 645
665 TEST(RestApi, AcceptMediaDispatcher) 646 TEST(RestApi, HttpContentNegociation)
666 { 647 {
667 // Reference: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1 648 // Reference: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
668 649
669 AcceptHandler h; 650 AcceptHandler h;
670 651
671 { 652 {
672 Orthanc::AcceptMediaDispatcher d; 653 Orthanc::HttpContentNegociation d;
673 d.Register("audio/mp3", h); 654 d.Register("audio/mp3", h);
674 d.Register("audio/basic", h); 655 d.Register("audio/basic", h);
675 656
676 ASSERT_TRUE(d.Apply("audio/*; q=0.2, audio/basic")); 657 ASSERT_TRUE(d.Apply("audio/*; q=0.2, audio/basic"));
677 ASSERT_EQ("audio", h.GetType()); 658 ASSERT_EQ("audio", h.GetType());
678 ASSERT_EQ("basic", h.GetSubType()); 659 ASSERT_EQ("basic", h.GetSubType());
679 ASSERT_FLOAT_EQ(1.0f, h.GetQuality());
680 660
681 ASSERT_TRUE(d.Apply("audio/*; q=0.2, audio/nope")); 661 ASSERT_TRUE(d.Apply("audio/*; q=0.2, audio/nope"));
682 ASSERT_EQ("audio", h.GetType()); 662 ASSERT_EQ("audio", h.GetType());
683 ASSERT_EQ("mp3", h.GetSubType()); 663 ASSERT_EQ("mp3", h.GetSubType());
684 ASSERT_FLOAT_EQ(0.2f, h.GetQuality());
685 664
686 ASSERT_FALSE(d.Apply("application/*; q=0.2, application/pdf")); 665 ASSERT_FALSE(d.Apply("application/*; q=0.2, application/pdf"));
687 666
688 ASSERT_TRUE(d.Apply("*/*; application/*; q=0.2, application/pdf")); 667 ASSERT_TRUE(d.Apply("*/*; application/*; q=0.2, application/pdf"));
689 ASSERT_EQ("audio", h.GetType()); 668 ASSERT_EQ("audio", h.GetType());
694 // text/x-dvi entity, and if that does not exist, send the 673 // text/x-dvi entity, and if that does not exist, send the
695 // text/plain entity."" 674 // text/plain entity.""
696 const std::string T1 = "text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c"; 675 const std::string T1 = "text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c";
697 676
698 { 677 {
699 Orthanc::AcceptMediaDispatcher d; 678 Orthanc::HttpContentNegociation d;
700 d.Register("text/plain", h); 679 d.Register("text/plain", h);
701 //d.Register("text/x-dvi", h);
702 d.Register("text/html", h); 680 d.Register("text/html", h);
681 d.Register("text/x-dvi", h);
703 ASSERT_TRUE(d.Apply(T1)); 682 ASSERT_TRUE(d.Apply(T1));
704 ASSERT_EQ("text", h.GetType()); 683 ASSERT_EQ("text", h.GetType());
705 ASSERT_EQ("html", h.GetSubType()); 684 ASSERT_EQ("html", h.GetSubType());
706 ASSERT_EQ(1.0f, h.GetQuality());
707 } 685 }
708 686
709 { 687 {
710 Orthanc::AcceptMediaDispatcher d; 688 Orthanc::HttpContentNegociation d;
711 d.Register("text/plain", h); 689 d.Register("text/plain", h);
712 //d.Register("text/x-dvi", h); 690 d.Register("text/x-dvi", h);
713 d.Register("text/x-c", h); 691 d.Register("text/x-c", h);
714 ASSERT_TRUE(d.Apply(T1)); 692 ASSERT_TRUE(d.Apply(T1));
715 ASSERT_EQ("text", h.GetType()); 693 ASSERT_EQ("text", h.GetType());
716 ASSERT_EQ("x-c", h.GetSubType()); 694 ASSERT_EQ("x-c", h.GetSubType());
717 ASSERT_EQ(1.0f, h.GetQuality());
718 } 695 }
719 696
720 { 697 {
721 Orthanc::AcceptMediaDispatcher d; 698 Orthanc::HttpContentNegociation d;
699 d.Register("text/plain", h);
700 d.Register("text/x-dvi", h);
701 d.Register("text/x-c", h);
702 d.Register("text/html", h);
703 ASSERT_TRUE(d.Apply(T1));
704 ASSERT_EQ("text", h.GetType());
705 ASSERT_TRUE(h.GetSubType() == "x-c" || h.GetSubType() == "html");
706 }
707
708 {
709 Orthanc::HttpContentNegociation d;
722 d.Register("text/plain", h); 710 d.Register("text/plain", h);
723 d.Register("text/x-dvi", h); 711 d.Register("text/x-dvi", h);
724 ASSERT_TRUE(d.Apply(T1)); 712 ASSERT_TRUE(d.Apply(T1));
725 ASSERT_EQ("text", h.GetType()); 713 ASSERT_EQ("text", h.GetType());
726 ASSERT_EQ("x-dvi", h.GetSubType()); 714 ASSERT_EQ("x-dvi", h.GetSubType());
727 ASSERT_EQ(0.8f, h.GetQuality());
728 } 715 }
729 716
730 { 717 {
731 Orthanc::AcceptMediaDispatcher d; 718 Orthanc::HttpContentNegociation d;
732 d.Register("text/plain", h); 719 d.Register("text/plain", h);
733 ASSERT_TRUE(d.Apply(T1)); 720 ASSERT_TRUE(d.Apply(T1));
734 ASSERT_EQ("text", h.GetType()); 721 ASSERT_EQ("text", h.GetType());
735 ASSERT_EQ("plain", h.GetSubType()); 722 ASSERT_EQ("plain", h.GetSubType());
736 ASSERT_EQ(0.5f, h.GetQuality()); 723 }
737 } 724 }
738 }