diff OrthancFramework/Sources/HttpServer/HttpContentNegociation.cpp @ 5338:78c59b02b121

accept parameters are now provided to HttpContentNegociation::IHandler::Handle()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 28 Jun 2023 08:29:43 +0200
parents 0ea402b4d901
children cb11e5ced4e3
line wrap: on
line diff
--- 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<float>(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<float>(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<Reference>& best,
+  void HttpContentNegociation::SelectBestMatch(std::unique_ptr<Reference>& target,
                                                const Handler& handler,
                                                const std::string& type,
                                                const std::string& subtype,
-                                               float quality)
+                                               const Dictionary& parameters)
   {
-    std::unique_ptr<Reference> match(new Reference(handler, type, subtype, quality));
+    std::unique_ptr<Reference> 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<Reference> 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;
     }
   }