changeset 901:7d88f3f4a3b3 plugins

refactoring IsServedUri, answer PNG images, regular expression groups
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 18 Jun 2014 15:22:13 +0200
parents 1b92ce45cc8d
children 5c255e465b33
files Core/HttpServer/EmbeddedResourceHttpHandler.cpp Core/HttpServer/EmbeddedResourceHttpHandler.h Core/HttpServer/FilesystemHttpHandler.cpp Core/HttpServer/FilesystemHttpHandler.h Core/HttpServer/HttpHandler.h Core/HttpServer/MongooseServer.cpp Core/HttpServer/MongooseServer.h Core/RestApi/RestApi.cpp Core/RestApi/RestApi.h Plugins/Engine/PluginsHttpHandler.cpp Plugins/Engine/PluginsHttpHandler.h Plugins/Engine/PluginsManager.cpp Plugins/OrthancCPlugin/OrthancCPlugin.h Resources/OrthancPlugin.doxygen Resources/Samples/Plugins/Basic/Plugin.c
diffstat 15 files changed, 265 insertions(+), 137 deletions(-) [+]
line wrap: on
line diff
--- a/Core/HttpServer/EmbeddedResourceHttpHandler.cpp	Wed Jun 18 14:02:02 2014 +0200
+++ b/Core/HttpServer/EmbeddedResourceHttpHandler.cpp	Wed Jun 18 15:22:13 2014 +0200
@@ -51,12 +51,6 @@
   }
 
 
-  bool EmbeddedResourceHttpHandler::IsServedUri(const UriComponents& uri)
-  {
-    return Toolbox::IsChildUri(baseUri_, uri);
-  }
-
-
   bool EmbeddedResourceHttpHandler::Handle(
     HttpOutput& output,
     HttpMethod method,
@@ -65,6 +59,12 @@
     const Arguments& arguments,
     const std::string&)
   {
+    if (!Toolbox::IsChildUri(baseUri_, uri))
+    {
+      // This URI is not served by this handler
+      return false;
+    }
+
     if (method != HttpMethod_Get)
     {
       output.SendMethodNotAllowedError("GET");
--- a/Core/HttpServer/EmbeddedResourceHttpHandler.h	Wed Jun 18 14:02:02 2014 +0200
+++ b/Core/HttpServer/EmbeddedResourceHttpHandler.h	Wed Jun 18 15:22:13 2014 +0200
@@ -50,8 +50,6 @@
       const std::string& baseUri,
       EmbeddedResources::DirectoryResourceId resourceId);
 
-    virtual bool IsServedUri(const UriComponents& uri);
-
     virtual bool Handle(
       HttpOutput& output,
       HttpMethod method,
--- a/Core/HttpServer/FilesystemHttpHandler.cpp	Wed Jun 18 14:02:02 2014 +0200
+++ b/Core/HttpServer/FilesystemHttpHandler.cpp	Wed Jun 18 15:22:13 2014 +0200
@@ -120,12 +120,6 @@
   }
 
 
-  bool FilesystemHttpHandler::IsServedUri(const UriComponents& uri)
-  {
-    return Toolbox::IsChildUri(pimpl_->baseUri_, uri);
-  }
-
-
   bool FilesystemHttpHandler::Handle(
     HttpOutput& output,
     HttpMethod method,
@@ -134,6 +128,12 @@
     const Arguments& arguments,
     const std::string&)
   {
+    if (!Toolbox::IsChildUri(pimpl_->baseUri_, uri))
+    {
+      // This URI is not served by this handler
+      return false;
+    }
+
     if (method != HttpMethod_Get)
     {
       output.SendMethodNotAllowedError("GET");
--- a/Core/HttpServer/FilesystemHttpHandler.h	Wed Jun 18 14:02:02 2014 +0200
+++ b/Core/HttpServer/FilesystemHttpHandler.h	Wed Jun 18 15:22:13 2014 +0200
@@ -52,8 +52,6 @@
     FilesystemHttpHandler(const std::string& baseUri,
                           const std::string& root);
 
-    virtual bool IsServedUri(const UriComponents& uri);
-
     virtual bool Handle(
       HttpOutput& output,
       HttpMethod method,
--- a/Core/HttpServer/HttpHandler.h	Wed Jun 18 14:02:02 2014 +0200
+++ b/Core/HttpServer/HttpHandler.h	Wed Jun 18 15:22:13 2014 +0200
@@ -50,8 +50,6 @@
     {
     }
 
-    virtual bool IsServedUri(const UriComponents& uri) = 0;
-
     virtual bool Handle(HttpOutput& output,
                         HttpMethod method,
                         const UriComponents& uri,
--- a/Core/HttpServer/MongooseServer.cpp	Wed Jun 18 14:02:02 2014 +0200
+++ b/Core/HttpServer/MongooseServer.cpp	Wed Jun 18 15:22:13 2014 +0200
@@ -255,22 +255,6 @@
 
 
 
-  void MongooseServer::FindHandlers(std::list<HttpHandler*>& result,
-                                    const UriComponents& forUri) const
-  {
-    for (Handlers::const_iterator it = 
-           handlers_.begin(); it != handlers_.end(); ++it) 
-    {
-      if ((*it)->IsServedUri(forUri))
-      {
-        result.push_back(*it);
-      }
-    }
-  }
-
-
-
-
   static PostDataStatus ReadBody(std::string& postData,
                                  struct mg_connection *connection,
                                  const HttpHandler::Arguments& headers)
@@ -702,20 +686,15 @@
       }
 
 
-      // Locate the candidate handlers for this URI
+      // Loop over the candidate handlers for this URI
       LOG(INFO) << EnumerationToString(method) << " " << Toolbox::FlattenUri(uri);
-      std::list<HttpHandler*> handlers;
-      that->FindHandlers(handlers, uri);
-
       bool found = false;
       bool isError = false;
       HttpStatus errorStatus;
       std::string errorDescription;
 
-
-      // Loop over the candidate handlers for this URI
-      for (std::list<HttpHandler*>::const_iterator
-             it = handlers.begin(); it != handlers.end() && !found; it++)
+      for (MongooseServer::Handlers::const_iterator it = 
+             that->GetHandlers().begin(); it != that->GetHandlers().end(); ++it) 
       {
         try
         {
--- a/Core/HttpServer/MongooseServer.h	Wed Jun 18 14:02:02 2014 +0200
+++ b/Core/HttpServer/MongooseServer.h	Wed Jun 18 15:22:13 2014 +0200
@@ -59,12 +59,14 @@
 
   class MongooseServer
   {
+  public:
+    typedef std::list<HttpHandler*> Handlers;
+
   private:
     // http://stackoverflow.com/questions/311166/stdauto-ptr-or-boostshared-ptr-for-pimpl-idiom
     struct PImpl;
     boost::shared_ptr<PImpl> pimpl_;
 
-    typedef std::list<HttpHandler*> Handlers;
     Handlers handlers_;
 
     typedef std::set<std::string> RegisteredUsers;
@@ -139,11 +141,13 @@
 
     void ClearHandlers();
 
-    void FindHandlers(std::list<HttpHandler*>& result,
-                      const UriComponents& forUri) const;
-
     ChunkStore& GetChunkStore();
 
     bool IsValidBasicHttpAuthentication(const std::string& basic) const;
+
+    const Handlers& GetHandlers() const
+    {
+      return handlers_;
+    }
   };
 }
--- a/Core/RestApi/RestApi.cpp	Wed Jun 18 14:02:02 2014 +0200
+++ b/Core/RestApi/RestApi.cpp	Wed Jun 18 15:22:13 2014 +0200
@@ -172,13 +172,6 @@
     } 
   }
 
-  bool RestApi::IsServedUri(const UriComponents& uri)
-  {
-    return (IsGetAccepted(uri) ||
-            IsPutAccepted(uri) ||
-            IsPostAccepted(uri) ||
-            IsDeleteAccepted(uri));
-  }
 
   bool RestApi::Handle(HttpOutput& output,
                        HttpMethod method,
@@ -187,6 +180,16 @@
                        const Arguments& getArguments,
                        const std::string& postData)
   {
+    if (!IsGetAccepted(uri) &&
+        !IsPutAccepted(uri) &&
+        !IsPostAccepted(uri) &&
+        !IsDeleteAccepted(uri))
+    {
+      // This URI is not served by this handler
+      return false;
+    }
+
+
     bool ok = false;
     RestApiOutput restOutput(output);
     RestApiPath::Components components;
--- a/Core/RestApi/RestApi.h	Wed Jun 18 14:02:02 2014 +0200
+++ b/Core/RestApi/RestApi.h	Wed Jun 18 15:22:13 2014 +0200
@@ -271,8 +271,6 @@
 
     ~RestApi();
 
-    virtual bool IsServedUri(const UriComponents& uri);
-
     virtual bool Handle(HttpOutput& output,
                         HttpMethod method,
                         const UriComponents& uri,
--- a/Plugins/Engine/PluginsHttpHandler.cpp	Wed Jun 18 14:02:02 2014 +0200
+++ b/Plugins/Engine/PluginsHttpHandler.cpp	Wed Jun 18 15:22:13 2014 +0200
@@ -35,6 +35,7 @@
 #include "../../Core/OrthancException.h"
 #include "../../Core/Toolbox.h"
 #include "../../Core/HttpServer/HttpOutput.h"
+#include "../../Core/ImageFormats/PngWriter.h"
 
 #include <boost/regex.hpp> 
 #include <glog/logging.h>
@@ -47,11 +48,6 @@
     typedef std::list<Callback>  Callbacks;
 
     Callbacks callbacks_;
-    OrthancPluginRestCallback currentCallback_;
-
-    PImpl() : currentCallback_(NULL)
-    {
-    }
   };
 
 
@@ -71,24 +67,6 @@
   }
 
 
-  bool PluginsHttpHandler::IsServedUri(const UriComponents& uri)
-  {
-    pimpl_->currentCallback_ = NULL;    
-    std::string tmp = Toolbox::FlattenUri(uri);
-
-    for (PImpl::Callbacks::const_iterator it = pimpl_->callbacks_.begin(); 
-         it != pimpl_->callbacks_.end(); it++)
-    {
-      if (boost::regex_match(tmp, *(it->first)))
-      {
-        pimpl_->currentCallback_ = it->second;
-        return true;
-      }
-    }
-
-    return false;
-  }
-
   bool PluginsHttpHandler::Handle(HttpOutput& output,
                                   HttpMethod method,
                                   const UriComponents& uri,
@@ -97,6 +75,39 @@
                                   const std::string& postData)
   {
     std::string flatUri = Toolbox::FlattenUri(uri);
+    OrthancPluginRestCallback callback = NULL;
+
+    std::vector<std::string> groups;
+    std::vector<const char*> cgroups;
+
+    bool found = false;
+    for (PImpl::Callbacks::const_iterator it = pimpl_->callbacks_.begin(); 
+         it != pimpl_->callbacks_.end() && !found; it++)
+    {
+      boost::cmatch what;
+      if (boost::regex_match(flatUri.c_str(), what, *(it->first)))
+      {
+        callback = it->second;
+
+        if (what.size() > 1)
+        {
+          groups.resize(what.size() - 1);
+          cgroups.resize(what.size() - 1);
+          for (size_t i = 1; i < what.size(); i++)
+          {
+            groups[i - 1] = what[i];
+            cgroups[i - 1] = groups[i - 1].c_str();
+          }
+        }
+
+        found = true;
+      }
+    }
+
+    if (!found)
+    {
+      return false;
+    }
 
     LOG(INFO) << "Delegating HTTP request to plugin for URI: " << flatUri;
 
@@ -140,6 +151,8 @@
     }
 
 
+    request.groupValues = (cgroups.size() ? &cgroups[0] : NULL);
+    request.groupCount = cgroups.size();
     request.getCount = getArguments.size();
     request.body = (postData.size() ? &postData[0] : NULL);
     request.bodySize = postData.size();
@@ -150,10 +163,10 @@
       request.getValues = &getValues[0];
     }
 
-    assert(pimpl_->currentCallback_ != NULL);
-    int32_t error = (*pimpl_->currentCallback_) (reinterpret_cast<OrthancPluginRestOutput*>(&output), 
-                                                 flatUri.c_str(), 
-                                                 &request);
+    assert(callback != NULL);
+    int32_t error = callback(reinterpret_cast<OrthancPluginRestOutput*>(&output), 
+                             flatUri.c_str(), 
+                             &request);
 
     if (error < 0)
     {
@@ -175,36 +188,6 @@
   bool PluginsHttpHandler::InvokeService(OrthancPluginService service,
                                          const void* parameters)
   {
-
-
-    /*void PluginsManager::RegisterRestCallback(const OrthancPluginContext* context,
-      const char* pathRegularExpression, 
-      OrthancPluginRestCallback callback)
-      {
-      LOG(INFO) << "Plugin has registered a REST callback on: " << pathRegularExpression;
-      PluginsManager* manager = reinterpret_cast<PluginsManager*>(context->pluginsManager);
-      manager->restCallbacks_.push_back(std::make_pair(pathRegularExpression, callback));
-      }*/
-
-
-    /*static void AnswerBuffer(OrthancPluginRestOutput* output,
-      const char* answer,
-      uint32_t answerSize,
-      const char* mimeType)
-      {
-      HttpOutput* translatedOutput = reinterpret_cast<HttpOutput*>(output);
-      translatedOutput->AnswerBufferWithContentType(answer, answerSize, mimeType);
-      }*/
-
-
-
-    /*for (PluginsManager::RestCallbacks::const_iterator
-           it = manager.GetRestCallbacks().begin(); it != manager.GetRestCallbacks().end(); ++it)
-    {
-      pimpl_->callbacks_.push_back(std::make_pair(new boost::regex(it->first), it->second));
-      }*/
-
-
     switch (service)
     {
       case OrthancPluginService_RegisterRestCallback:
@@ -229,6 +212,52 @@
         return true;
       }
 
+      case OrthancPluginService_CompressAndAnswerPngImage:
+      {
+        const _OrthancPluginCompressAndAnswerPngImageParams& p = 
+          *reinterpret_cast<const _OrthancPluginCompressAndAnswerPngImageParams*>(parameters);
+
+        HttpOutput* translatedOutput = reinterpret_cast<HttpOutput*>(p.output);
+
+        PixelFormat format;
+        switch (p.format)
+        {
+          case OrthancPluginPixelFormat_Grayscale8:  
+            format = PixelFormat_Grayscale8;
+            break;
+
+          case OrthancPluginPixelFormat_Grayscale16:  
+            format = PixelFormat_Grayscale16;
+            break;
+
+          case OrthancPluginPixelFormat_SignedGrayscale16:  
+            format = PixelFormat_SignedGrayscale16;
+            break;
+
+          case OrthancPluginPixelFormat_RGB24:  
+            format = PixelFormat_RGB24;
+            break;
+
+          case OrthancPluginPixelFormat_RGBA32:  
+            format = PixelFormat_RGBA32;
+            break;
+
+          default:
+            throw OrthancException(ErrorCode_ParameterOutOfRange);
+        }
+
+        ImageAccessor accessor;
+        accessor.AssignReadOnly(format, p.width, p.height, p.pitch, p.buffer);
+
+        PngWriter writer;
+        std::string png;
+        writer.WriteToMemory(png, accessor);
+
+        translatedOutput->AnswerBufferWithContentType(png, "image/png");
+
+        return true;
+      }
+
       default:
         return false;
     }
--- a/Plugins/Engine/PluginsHttpHandler.h	Wed Jun 18 14:02:02 2014 +0200
+++ b/Plugins/Engine/PluginsHttpHandler.h	Wed Jun 18 15:22:13 2014 +0200
@@ -52,8 +52,6 @@
 
     virtual ~PluginsHttpHandler();
 
-    virtual bool IsServedUri(const UriComponents& uri);
-
     virtual bool Handle(HttpOutput& output,
                         HttpMethod method,
                         const UriComponents& uri,
--- a/Plugins/Engine/PluginsManager.cpp	Wed Jun 18 14:02:02 2014 +0200
+++ b/Plugins/Engine/PluginsManager.cpp	Wed Jun 18 15:22:13 2014 +0200
@@ -155,6 +155,7 @@
     }
 
     PluginsManager* that = reinterpret_cast<PluginsManager*>(context->pluginsManager);
+    bool error = false;
 
     for (std::list<IPluginServiceProvider*>::iterator
            it = that->serviceProviders_.begin(); 
@@ -170,10 +171,19 @@
       catch (OrthancException&)
       {
         // This service provider has failed, go to the next
+        error = true;
       }
     }
 
-    LOG(ERROR) << "Plugin invoking unknown service " << service;
+    if (error)
+    {
+      LOG(ERROR) << "Exception when dealing with service " << service;
+    }
+    else
+    {
+      LOG(ERROR) << "Plugin invoking unknown service " << service;
+    }
+
     return -1;
   }
 
--- a/Plugins/OrthancCPlugin/OrthancCPlugin.h	Wed Jun 18 14:02:02 2014 +0200
+++ b/Plugins/OrthancCPlugin/OrthancCPlugin.h	Wed Jun 18 15:22:13 2014 +0200
@@ -123,6 +123,10 @@
   {
     OrthancPluginHttpMethod method;
 
+    /* Groups of the regular expression */
+    const char* const*      groupValues;
+    uint32_t                groupCount;
+
     /* For GET requests */
     const char* const*      getKeys;
     const char* const*      getValues;
@@ -144,11 +148,58 @@
     OrthancPluginService_RegisterRestCallback = 1000,
 
     /* Sending answers to REST calls */
-    OrthancPluginService_AnswerBuffer = 2000
+    OrthancPluginService_AnswerBuffer = 2000,
+    OrthancPluginService_CompressAndAnswerPngImage = 2001
   } OrthancPluginService;
 
 
 
+  /**
+   * The memory layout of the pixels of an image.
+   **/
+  typedef enum
+  {
+    /**
+     * @brief Graylevel 8bpp image.
+     *
+     * The image is graylevel. Each pixel is unsigned and stored in
+     * one byte.
+     **/
+    OrthancPluginPixelFormat_Grayscale8 = 1,
+
+    /**
+     * @brief Graylevel, unsigned 16bpp image.
+     *
+     * The image is graylevel. Each pixel is unsigned and stored in
+     * two bytes.
+     **/
+    OrthancPluginPixelFormat_Grayscale16 = 2,
+
+    /**
+     * @brief Graylevel, signed 16bpp image.
+     *
+     * The image is graylevel. Each pixel is signed and stored in two
+     * bytes.
+     **/
+    OrthancPluginPixelFormat_SignedGrayscale16 = 3,
+
+    /**
+     * @brief Color image in RGB24 format.
+     *
+     * This format describes a color image. The pixels are stored in 3
+     * consecutive bytes. The memory layout is RGB.
+     **/
+    OrthancPluginPixelFormat_RGB24 = 4,
+
+    /**
+     * @brief Color image in RGBA32 format.
+     *
+     * This format describes a color image. The pixels are stored in 4
+     * consecutive bytes. The memory layout is RGBA.
+     **/
+    OrthancPluginPixelFormat_RGBA32 = 5
+  } OrthancPluginPixelFormat;
+
 
   typedef struct _OrthancPluginRestOutput_t OrthancPluginRestOutput;
 
@@ -168,22 +219,6 @@
   } OrthancPluginContext;
 
 
-  typedef struct
-  {
-    const char* pathRegularExpression;
-    OrthancPluginRestCallback callback;
-  } _OrthancPluginRestCallbackParams;
-
-
-  typedef struct
-  {
-    OrthancPluginRestOutput* output;
-    const char*              answer;
-    uint32_t                 answerSize;
-    const char*              mimeType;
-  } _OrthancPluginAnswerBufferParams;
-
-
   ORTHANC_PLUGIN_INLINE void OrthancPluginLogError(
     OrthancPluginContext* context,
     const char* str)
@@ -208,6 +243,13 @@
   }
 
 
+  typedef struct
+  {
+    const char* pathRegularExpression;
+    OrthancPluginRestCallback callback;
+  } _OrthancPluginRestCallbackParams;
+
+
   ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterRestCallback(
     OrthancPluginContext*     context,
     const char*               pathRegularExpression,
@@ -220,6 +262,14 @@
   }
 
 
+  typedef struct
+  {
+    OrthancPluginRestOutput* output;
+    const char*              answer;
+    uint32_t                 answerSize;
+    const char*              mimeType;
+  } _OrthancPluginAnswerBufferParams;
+
   ORTHANC_PLUGIN_INLINE void OrthancPluginAnswerBuffer(
     OrthancPluginContext*    context,
     OrthancPluginRestOutput* output,
@@ -236,6 +286,36 @@
   }
 
 
+  typedef struct
+  {
+    OrthancPluginRestOutput*  output;
+    OrthancPluginPixelFormat  format;
+    uint32_t                  width;
+    uint32_t                  height;
+    uint32_t                  pitch;
+    const void*               buffer;
+  } _OrthancPluginCompressAndAnswerPngImageParams;
+
+  ORTHANC_PLUGIN_INLINE void OrthancPluginCompressAndAnswerPngImage(
+    OrthancPluginContext*     context,
+    OrthancPluginRestOutput*  output,
+    OrthancPluginPixelFormat  format,
+    uint32_t                  width,
+    uint32_t                  height,
+    uint32_t                  pitch,
+    const void*               buffer)
+  {
+    _OrthancPluginCompressAndAnswerPngImageParams params;
+    params.output = output;
+    params.format = format;
+    params.width = width;
+    params.height = height;
+    params.pitch = pitch;
+    params.buffer = buffer;
+    context->InvokeService(context, OrthancPluginService_CompressAndAnswerPngImage, &params);
+  }
+
+
 
   /**
      Each plugin must define 4 functions, whose signature are:
--- a/Resources/OrthancPlugin.doxygen	Wed Jun 18 14:02:02 2014 +0200
+++ b/Resources/OrthancPlugin.doxygen	Wed Jun 18 15:22:13 2014 +0200
@@ -545,7 +545,7 @@
 # documentation can be controlled using \showinitializer or \hideinitializer
 # command in the documentation regardless of this setting.
 
-MAX_INITIALIZER_LINES  = 30
+MAX_INITIALIZER_LINES  = 0
 
 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated
 # at the bottom of the documentation of classes and structs. If set to YES the
--- a/Resources/Samples/Plugins/Basic/Plugin.c	Wed Jun 18 14:02:02 2014 +0200
+++ b/Resources/Samples/Plugins/Basic/Plugin.c	Wed Jun 18 15:22:13 2014 +0200
@@ -33,9 +33,9 @@
 static OrthancPluginContext* context = NULL;
 
 
-ORTHANC_PLUGINS_API int32_t Callback(OrthancPluginRestOutput* output,
-                                     const char* url,
-                                     const OrthancPluginHttpRequest* request)
+ORTHANC_PLUGINS_API int32_t Callback1(OrthancPluginRestOutput* output,
+                                      const char* url,
+                                      const OrthancPluginHttpRequest* request)
 {
   char buffer[1024];
   uint32_t i;
@@ -51,10 +51,42 @@
     OrthancPluginLogInfo(context, buffer);    
   }
 
+  printf("** %d\n", request->groupCount);
+  for (i = 0; i < request->groupCount; i++)
+  {
+    printf("** [%s]\n",  request->groupValues[i]);
+  }
+
   return 1;
 }
 
 
+ORTHANC_PLUGINS_API int32_t Callback2(OrthancPluginRestOutput* output,
+                                      const char* url,
+                                      const OrthancPluginHttpRequest* request)
+{
+  /**
+   * Answer with a sample 16bpp image.
+   **/
+
+  uint16_t buffer[256 * 256];
+  uint32_t x, y, value;
+
+  value = 0;
+  for (y = 0; y < 256; y++)
+  {
+    for (x = 0; x < 256; x++, value++)
+    {
+      buffer[value] = value;
+    }
+  }
+
+  OrthancPluginCompressAndAnswerPngImage(context, output, OrthancPluginPixelFormat_Grayscale16,
+                                         256, 256, sizeof(uint16_t) * 256, buffer);
+  return 0;
+}
+
+
 ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* c)
 {
   char info[1024];
@@ -65,7 +97,8 @@
   sprintf(info, "The version of Orthanc is '%s'", context->orthancVersion);
   OrthancPluginLogInfo(context, info);
 
-  OrthancPluginRegisterRestCallback(context, "/plu.*/hello", Callback);
+  OrthancPluginRegisterRestCallback(context, "/(plu.*)/hello", Callback1);
+  OrthancPluginRegisterRestCallback(context, "/plu.*/image", Callback2);
 
   return 0;
 }