# HG changeset patch # User Sebastien Jodogne # Date 1687933783 -7200 # Node ID 78c59b02b12105d06246f0919fcf2cedad4bf36b # Parent b376abae664aa6e50a8cd199aa6e014f0e0c871b accept parameters are now provided to HttpContentNegociation::IHandler::Handle() diff -r b376abae664a -r 78c59b02b121 OrthancFramework/Sources/HttpServer/HttpContentNegociation.cpp --- a/OrthancFramework/Sources/HttpServer/HttpContentNegociation.cpp Tue Jun 27 17:55:09 2023 +0200 +++ b/OrthancFramework/Sources/HttpServer/HttpContentNegociation.cpp Wed Jun 28 08:29:43 2023 +0200 @@ -49,28 +49,70 @@ { return true; } - - if (subtype == "*" && type == type_) + else if (subtype == "*" && type == type_) { return true; } - - return type == type_ && subtype == subtype_; + else + { + return type == type_ && subtype == subtype_; + } } - struct HttpContentNegociation::Reference : public boost::noncopyable + class HttpContentNegociation::Reference : public boost::noncopyable { + private: const Handler& handler_; uint8_t level_; float quality_; + std::string application_; + Dictionary parameters_; + static float GetQuality(const Dictionary& parameters) + { + Dictionary::const_iterator found = parameters.find("q"); + + if (found != parameters.end()) + { + float quality; + bool ok = false; + + try + { + quality = boost::lexical_cast(found->second); + ok = (quality >= 0.0f && quality <= 1.0f); + } + catch (boost::bad_lexical_cast&) + { + } + + if (ok) + { + return quality; + } + else + { + throw OrthancException( + ErrorCode_BadRequest, + "Quality parameter out of range in a HTTP request (must be between 0 and 1): " + found->second); + } + } + else + { + return 1.0f; // Default quality + } + } + + public: Reference(const Handler& handler, const std::string& type, const std::string& subtype, - float quality) : + const Dictionary& parameters) : handler_(handler), - quality_(quality) + quality_(GetQuality(parameters)), + application_(type + "/" + subtype), + parameters_(parameters) { if (type == "*" && subtype == "*") { @@ -85,6 +127,11 @@ level_ = 2; } } + + void Call() const + { + handler_.Call(parameters_); + } bool operator< (const Reference& other) const { @@ -92,13 +139,14 @@ { return true; } - - if (level_ > other.level_) + else if (level_ > other.level_) { return false; } - - return quality_ < other.quality_; + else + { + return quality_ < other.quality_; + } } }; @@ -123,58 +171,21 @@ } - float HttpContentNegociation::GetQuality(const Tokens& parameters) - { - for (size_t i = 1; i < parameters.size(); i++) - { - std::string key, value; - if (SplitPair(key, value, parameters[i], '=') && - key == "q") - { - float quality; - bool ok = false; - - try - { - quality = boost::lexical_cast(value); - ok = (quality >= 0.0f && quality <= 1.0f); - } - catch (boost::bad_lexical_cast&) - { - } - - if (ok) - { - return quality; - } - else - { - throw OrthancException( - ErrorCode_BadRequest, - "Quality parameter out of range in a HTTP request (must be between 0 and 1): " + value); - } - } - } - - return 1.0f; // Default quality - } - - - void HttpContentNegociation::SelectBestMatch(std::unique_ptr& best, + void HttpContentNegociation::SelectBestMatch(std::unique_ptr& target, const Handler& handler, const std::string& type, const std::string& subtype, - float quality) + const Dictionary& parameters) { - std::unique_ptr match(new Reference(handler, type, subtype, quality)); + std::unique_ptr match(new Reference(handler, type, subtype, parameters)); - if (best.get() == NULL || - *best < *match) + if (target.get() == NULL || + *target < *match) { #if __cplusplus < 201103L - best.reset(match.release()); + target.reset(match.release()); #else - best = std::move(match); + target = std::move(match); #endif } } @@ -198,9 +209,9 @@ } - bool HttpContentNegociation::Apply(const HttpHeaders& headers) + bool HttpContentNegociation::Apply(const Dictionary& headers) { - HttpHeaders::const_iterator accept = headers.find("accept"); + Dictionary::const_iterator accept = headers.find("accept"); if (accept != headers.end()) { return Apply(accept->second); @@ -222,26 +233,39 @@ Toolbox::TokenizeString(mediaRanges, accept, ','); std::unique_ptr bestMatch; + Dictionary bestParameters; for (Tokens::const_iterator it = mediaRanges.begin(); it != mediaRanges.end(); ++it) { - Tokens parameters; - Toolbox::TokenizeString(parameters, *it, ';'); + Tokens tokens; + Toolbox::TokenizeString(tokens, *it, ';'); - if (parameters.size() > 0) + if (tokens.size() > 0) { - float quality = GetQuality(parameters); + Dictionary parameters; + for (size_t i = 1; i < tokens.size(); i++) + { + std::string key, value; + + if (!SplitPair(key, value, tokens[i], '=')) + { + key = Toolbox::StripSpaces(tokens[i]); + value = ""; + } + parameters[key] = value; + } + std::string type, subtype; - if (SplitPair(type, subtype, parameters[0], '/')) + if (SplitPair(type, subtype, tokens[0], '/')) { for (Handlers::const_iterator it2 = handlers_.begin(); it2 != handlers_.end(); ++it2) { if (it2->IsMatch(type, subtype)) { - SelectBestMatch(bestMatch, *it2, type, subtype, quality); + SelectBestMatch(bestMatch, *it2, type, subtype, parameters); } } } @@ -254,7 +278,7 @@ } else { - bestMatch->handler_.Call(); + bestMatch->Call(); return true; } } diff -r b376abae664a -r 78c59b02b121 OrthancFramework/Sources/HttpServer/HttpContentNegociation.h --- a/OrthancFramework/Sources/HttpServer/HttpContentNegociation.h Tue Jun 27 17:55:09 2023 +0200 +++ b/OrthancFramework/Sources/HttpServer/HttpContentNegociation.h Wed Jun 28 08:29:43 2023 +0200 @@ -39,7 +39,7 @@ class ORTHANC_PUBLIC HttpContentNegociation : public boost::noncopyable { public: - typedef std::map HttpHeaders; + typedef std::map Dictionary; class IHandler : public boost::noncopyable { @@ -49,7 +49,8 @@ } virtual void Handle(const std::string& type, - const std::string& subtype) = 0; + const std::string& subtype, + const Dictionary& parameters) = 0; }; private: @@ -66,9 +67,9 @@ bool IsMatch(const std::string& type, const std::string& subtype) const; - void Call() const + void Call(const Dictionary& parameters) const { - handler_.Handle(type_, subtype_); + handler_.Handle(type_, subtype_, parameters); } }; @@ -86,19 +87,17 @@ const std::string& source, char separator); - static float GetQuality(const Tokens& parameters); - - static void SelectBestMatch(std::unique_ptr& best, + static void SelectBestMatch(std::unique_ptr& target, const Handler& handler, const std::string& type, const std::string& subtype, - float quality); + const Dictionary& parameters); public: void Register(const std::string& mime, IHandler& handler); - bool Apply(const HttpHeaders& headers); + bool Apply(const Dictionary& headers); bool Apply(const std::string& accept); }; diff -r b376abae664a -r 78c59b02b121 OrthancFramework/UnitTestsSources/RestApiTests.cpp --- a/OrthancFramework/UnitTestsSources/RestApiTests.cpp Tue Jun 27 17:55:09 2023 +0200 +++ b/OrthancFramework/UnitTestsSources/RestApiTests.cpp Wed Jun 28 08:29:43 2023 +0200 @@ -384,6 +384,7 @@ private: std::string type_; std::string subtype_; + HttpContentNegociation::Dictionary parameters_; public: AcceptHandler() @@ -393,7 +394,8 @@ void Reset() { - Handle("nope", "nope"); + HttpContentNegociation::Dictionary parameters; + Handle("nope", "nope", parameters); } const std::string& GetType() const @@ -406,11 +408,18 @@ return subtype_; } + HttpContentNegociation::Dictionary& GetParameters() + { + return parameters_; + } + virtual void Handle(const std::string& type, - const std::string& subtype) ORTHANC_OVERRIDE + const std::string& subtype, + const HttpContentNegociation::Dictionary& parameters) ORTHANC_OVERRIDE { type_ = type; subtype_ = subtype; + parameters_ = parameters; } }; } @@ -430,22 +439,29 @@ ASSERT_TRUE(d.Apply("audio/*; q=0.2, audio/basic")); ASSERT_EQ("audio", h.GetType()); ASSERT_EQ("basic", h.GetSubType()); + ASSERT_EQ(0u, h.GetParameters().size()); - ASSERT_TRUE(d.Apply("audio/*; q=0.2, audio/nope")); + ASSERT_TRUE(d.Apply("audio/*; q=0.2 ; type = test ; hello , audio/nope")); ASSERT_EQ("audio", h.GetType()); ASSERT_EQ("mp3", h.GetSubType()); + ASSERT_EQ(3u, h.GetParameters().size()); + ASSERT_EQ("0.2", h.GetParameters() ["q"]); + ASSERT_EQ("test", h.GetParameters() ["type"]); + ASSERT_EQ("", h.GetParameters() ["hello"]); ASSERT_FALSE(d.Apply("application/*; q=0.2, application/pdf")); - ASSERT_TRUE(d.Apply("*/*; application/*; q=0.2, application/pdf")); + ASSERT_TRUE(d.Apply("*/*; hello=world, application/*; q=0.2, application/pdf")); ASSERT_EQ("audio", h.GetType()); + ASSERT_EQ(1u, h.GetParameters().size()); + ASSERT_EQ("world", h.GetParameters() ["hello"]); } // "This would be interpreted as "text/html and text/x-c are the // preferred media types, but if they do not exist, then send the // text/x-dvi entity, and if that does not exist, send the // text/plain entity."" - const std::string T1 = "text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c"; + const std::string T1 = "text/plain; q=0.5, text/html ; hello = world , text/x-dvi; q=0.8, text/x-c"; { HttpContentNegociation d; @@ -455,6 +471,8 @@ ASSERT_TRUE(d.Apply(T1)); ASSERT_EQ("text", h.GetType()); ASSERT_EQ("html", h.GetSubType()); + ASSERT_EQ(1u, h.GetParameters().size()); + ASSERT_EQ("world", h.GetParameters() ["hello"]); } { @@ -465,6 +483,7 @@ ASSERT_TRUE(d.Apply(T1)); ASSERT_EQ("text", h.GetType()); ASSERT_EQ("x-c", h.GetSubType()); + ASSERT_EQ(0u, h.GetParameters().size()); } { @@ -476,6 +495,15 @@ ASSERT_TRUE(d.Apply(T1)); ASSERT_EQ("text", h.GetType()); ASSERT_TRUE(h.GetSubType() == "x-c" || h.GetSubType() == "html"); + if (h.GetSubType() == "x-c") + { + ASSERT_EQ(0u, h.GetParameters().size()); + } + else + { + ASSERT_EQ(1u, h.GetParameters().size()); + ASSERT_EQ("world", h.GetParameters() ["hello"]); + } } { @@ -485,6 +513,8 @@ ASSERT_TRUE(d.Apply(T1)); ASSERT_EQ("text", h.GetType()); ASSERT_EQ("x-dvi", h.GetSubType()); + ASSERT_EQ(1u, h.GetParameters().size()); + ASSERT_EQ("0.8", h.GetParameters() ["q"]); } { @@ -493,6 +523,8 @@ ASSERT_TRUE(d.Apply(T1)); ASSERT_EQ("text", h.GetType()); ASSERT_EQ("plain", h.GetSubType()); + ASSERT_EQ(1u, h.GetParameters().size()); + ASSERT_EQ("0.5", h.GetParameters() ["q"]); } } diff -r b376abae664a -r 78c59b02b121 OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Tue Jun 27 17:55:09 2023 +0200 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Wed Jun 28 08:29:43 2023 +0200 @@ -639,7 +639,8 @@ } virtual void Handle(const std::string& type, - const std::string& subtype) ORTHANC_OVERRIDE + const std::string& subtype, + const HttpContentNegociation::Dictionary& parameters) ORTHANC_OVERRIDE { assert(type == "image"); assert(subtype == "png"); @@ -658,7 +659,8 @@ } virtual void Handle(const std::string& type, - const std::string& subtype) ORTHANC_OVERRIDE + const std::string& subtype, + const HttpContentNegociation::Dictionary& parameters) ORTHANC_OVERRIDE { assert(type == "image"); assert(subtype == "x-portable-arbitrarymap"); @@ -698,7 +700,8 @@ } virtual void Handle(const std::string& type, - const std::string& subtype) ORTHANC_OVERRIDE + const std::string& subtype, + const HttpContentNegociation::Dictionary& parameters) ORTHANC_OVERRIDE { assert(type == "image"); assert(subtype == "jpeg");