changeset 912:dcb2469f00f4 plugins

PluginsHttpHandler::RestApiGet
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 20 Jun 2014 14:55:24 +0200
parents 306afd58a0b3
children 3e43de893d88
files Core/HttpServer/HttpHandler.cpp Core/HttpServer/HttpHandler.h Core/HttpServer/HttpOutput.h Core/HttpServer/MongooseServer.cpp OrthancServer/main.cpp Plugins/Engine/PluginsHttpHandler.cpp Plugins/Engine/PluginsHttpHandler.h Plugins/OrthancCPlugin/OrthancCPlugin.h Plugins/Samples/Basic/Plugin.c Resources/Configuration.json UnitTestsSources/UnitTestsMain.cpp
diffstat 11 files changed, 286 insertions(+), 114 deletions(-) [+]
line wrap: on
line diff
--- a/Core/HttpServer/HttpHandler.cpp	Fri Jun 20 13:45:22 2014 +0200
+++ b/Core/HttpServer/HttpHandler.cpp	Fri Jun 20 14:55:24 2014 +0200
@@ -34,6 +34,8 @@
 #include "HttpHandler.h"
 
 #include <string.h>
+#include <iostream>
+
 
 namespace Orthanc
 {
@@ -62,7 +64,7 @@
   }
 
 
-  void HttpHandler::ParseGetQuery(HttpHandler::Arguments& result, const char* query)
+  void HttpHandler::ParseGetArguments(HttpHandler::Arguments& result, const char* query)
   {
     const char* pos = query;
 
@@ -84,6 +86,24 @@
   }
 
 
+  void  HttpHandler::ParseGetQuery(UriComponents& uri,
+                                   HttpHandler::Arguments& getArguments, 
+                                   const char* query)
+  {
+    const char *questionMark = ::strchr(query, '?');
+    if (questionMark == NULL)
+    {
+      // No question mark in the string
+      Toolbox::SplitUriComponents(uri, query);
+      getArguments.clear();
+    }
+    else
+    {
+      Toolbox::SplitUriComponents(uri, std::string(query, questionMark));
+      HttpHandler::ParseGetArguments(getArguments, questionMark + 1);
+    }    
+  }
+
 
   std::string HttpHandler::GetArgument(const Arguments& getArguments,
                                        const std::string& name,
--- a/Core/HttpServer/HttpHandler.h	Fri Jun 20 13:45:22 2014 +0200
+++ b/Core/HttpServer/HttpHandler.h	Fri Jun 20 14:55:24 2014 +0200
@@ -57,7 +57,11 @@
                         const Arguments& getArguments,
                         const std::string& postData) = 0;
 
-    static void ParseGetQuery(HttpHandler::Arguments& result, 
+    static void ParseGetArguments(HttpHandler::Arguments& result, 
+                                  const char* query);
+
+    static void ParseGetQuery(UriComponents& uri,
+                              HttpHandler::Arguments& getArguments, 
                               const char* query);
 
     static std::string GetArgument(const Arguments& getArguments,
--- a/Core/HttpServer/HttpOutput.h	Fri Jun 20 13:45:22 2014 +0200
+++ b/Core/HttpServer/HttpOutput.h	Fri Jun 20 14:55:24 2014 +0200
@@ -41,7 +41,7 @@
 
 namespace Orthanc
 {
-  class HttpOutput
+  class HttpOutput : public boost::noncopyable
   {
   private:
     typedef std::list< std::pair<std::string, std::string> >  Header;
--- a/Core/HttpServer/MongooseServer.cpp	Fri Jun 20 13:45:22 2014 +0200
+++ b/Core/HttpServer/MongooseServer.cpp	Fri Jun 20 14:55:24 2014 +0200
@@ -581,7 +581,7 @@
       HttpHandler::Arguments argumentsGET;
       if (!strcmp(request->request_method, "GET"))
       {
-        HttpHandler::ParseGetQuery(argumentsGET, request->query_string);
+        HttpHandler::ParseGetArguments(argumentsGET, request->query_string);
       }
 
 
--- a/OrthancServer/main.cpp	Fri Jun 20 13:45:22 2014 +0200
+++ b/OrthancServer/main.cpp	Fri Jun 20 14:55:24 2014 +0200
@@ -374,12 +374,6 @@
 
     LoadLuaScripts(context);
 
-    PluginsHttpHandler httpPlugins(context);
-
-    PluginsManager pluginsManager;
-    pluginsManager.RegisterServiceProvider(httpPlugins);
-    LoadPlugins(pluginsManager);
-
     try
     {
       context.GetIndex().SetMaximumPatientCount(Configuration::GetGlobalIntegerParameter("MaximumPatientCount", 0));
@@ -443,6 +437,13 @@
       FilesystemHttpHandler staticResources("/app", ORTHANC_PATH "/OrthancExplorer");
 #endif
 
+      PluginsHttpHandler httpPlugins(context);
+      httpPlugins.SetOrthancRestApi(restApi);
+
+      PluginsManager pluginsManager;
+      pluginsManager.RegisterServiceProvider(httpPlugins);
+      LoadPlugins(pluginsManager);
+
       httpServer.RegisterHandler(httpPlugins);
       httpServer.RegisterHandler(staticResources);
       httpServer.RegisterHandler(restApi);
--- a/Plugins/Engine/PluginsHttpHandler.cpp	Fri Jun 20 13:45:22 2014 +0200
+++ b/Plugins/Engine/PluginsHttpHandler.cpp	Fri Jun 20 14:55:24 2014 +0200
@@ -32,6 +32,7 @@
 
 #include "PluginsHttpHandler.h"
 
+#include "../../Core/ChunkedBuffer.h"
 #include "../../Core/OrthancException.h"
 #include "../../Core/Toolbox.h"
 #include "../../Core/HttpServer/HttpOutput.h"
@@ -45,30 +46,30 @@
   namespace
   {
     // Anonymous namespace to avoid clashes between compilation modules
-    class StringHttpOutput : public HttpOutput
+    class StringHttpOutput : public IHttpOutputStream
     {
     private:
-      std::string target_;
+      ChunkedBuffer buffer_;
 
     public:
-      const std::string& GetOutput() const
+      void GetOutput(std::string& output)
       {
-        return target_;
+        buffer_.Flatten(output);
+      }
+
+      virtual void OnHttpStatusReceived(HttpStatus status)
+      {
+        if (status != HttpStatus_200_Ok)
+        {
+          throw OrthancException(ErrorCode_BadRequest);
+        }
       }
 
       virtual void Send(bool isHeader, const void* buffer, size_t length)
       {
-        if (isHeader)
+        if (!isHeader)
         {
-          return;
-        }
-
-        size_t pos = target_.size();
-        target_.resize(pos + length);
-
-        if (length > 0)
-        {
-          memcpy(&target_[pos], buffer, length);
+          buffer_.AddChunk(reinterpret_cast<const char*>(buffer), length);
         }
       }
     };
@@ -225,6 +226,154 @@
   }
 
 
+  static void CopyToMemoryBuffer(OrthancPluginMemoryBuffer& target,
+                                 const void* data,
+                                 size_t size)
+  {
+    target.size = size;
+
+    if (size == 0)
+    {
+      target.data = NULL;
+    }
+    else
+    {
+      target.data = malloc(size);
+      if (target.data != NULL)
+      {
+        memcpy(target.data, data, size);
+      }
+      else
+      {
+        throw OrthancException(ErrorCode_NotEnoughMemory);
+      }
+    }
+  }
+
+
+  static void CopyToMemoryBuffer(OrthancPluginMemoryBuffer& target,
+                                 const std::string& str)
+  {
+    if (str.size() == 0)
+    {
+      target.size = 0;
+      target.data = NULL;
+    }
+    else
+    {
+      CopyToMemoryBuffer(target, str.c_str(), str.size());
+    }
+  }
+
+
+  void PluginsHttpHandler::RegisterRestCallback(const void* parameters)
+  {
+    const _OrthancPluginRestCallback& p = 
+      *reinterpret_cast<const _OrthancPluginRestCallback*>(parameters);
+
+    LOG(INFO) << "Plugin has registered a REST callback on: " << p.pathRegularExpression;
+    pimpl_->callbacks_.push_back(std::make_pair(new boost::regex(p.pathRegularExpression), p.callback));
+  }
+
+
+
+  void PluginsHttpHandler::AnswerBuffer(const void* parameters)
+  {
+    const _OrthancPluginAnswerBuffer& p = 
+      *reinterpret_cast<const _OrthancPluginAnswerBuffer*>(parameters);
+
+    HttpOutput* translatedOutput = reinterpret_cast<HttpOutput*>(p.output);
+    translatedOutput->AnswerBufferWithContentType(p.answer, p.answerSize, p.mimeType);
+  }
+
+
+  void PluginsHttpHandler::CompressAndAnswerPngImage(const void* parameters)
+  {
+    const _OrthancPluginCompressAndAnswerPngImage& p = 
+      *reinterpret_cast<const _OrthancPluginCompressAndAnswerPngImage*>(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");
+  }
+
+
+  void PluginsHttpHandler::GetDicomForInstance(const void* parameters)
+  {
+    const _OrthancPluginGetDicomForInstance& p = 
+      *reinterpret_cast<const _OrthancPluginGetDicomForInstance*>(parameters);
+
+    std::string dicom;
+    pimpl_->context_.ReadFile(dicom, p.instanceId, FileContentType_Dicom);
+    CopyToMemoryBuffer(*p.target, dicom);
+  }
+
+
+  void PluginsHttpHandler::RestApiGet(const void* parameters)
+  {
+    const _OrthancPluginRestApiGet& p = 
+      *reinterpret_cast<const _OrthancPluginRestApiGet*>(parameters);
+        
+    HttpHandler::Arguments headers;  // No HTTP header
+    std::string body;  // No body for a GET request
+
+    UriComponents uri;
+    HttpHandler::Arguments getArguments;
+    HttpHandler::ParseGetQuery(uri, getArguments, p.uri);
+
+    StringHttpOutput stream;
+    HttpOutput http(stream);
+
+    LOG(INFO) << "Plugin making REST call on URI " << p.uri;
+
+    if (pimpl_->restApi_ != NULL &&
+        pimpl_->restApi_->Handle(http, HttpMethod_Get, uri, headers, getArguments, body))
+    {
+      std::string result;
+      stream.GetOutput(result);
+      CopyToMemoryBuffer(*p.target, result);
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_BadRequest);
+    }
+  }
+
+
   bool PluginsHttpHandler::InvokeService(_OrthancPluginService service,
                                          const void* parameters)
   {
@@ -232,99 +381,31 @@
     {
       case _OrthancPluginService_RegisterRestCallback:
       {
-        const _OrthancPluginRestCallback& p = 
-          *reinterpret_cast<const _OrthancPluginRestCallback*>(parameters);
-
-        LOG(INFO) << "Plugin has registered a REST callback on: " << p.pathRegularExpression;
-        pimpl_->callbacks_.push_back(std::make_pair(new boost::regex(p.pathRegularExpression), p.callback));
-
+        RegisterRestCallback(parameters);
         return true;
       }
 
       case _OrthancPluginService_AnswerBuffer:
       {
-        const _OrthancPluginAnswerBuffer& p = 
-          *reinterpret_cast<const _OrthancPluginAnswerBuffer*>(parameters);
-
-        HttpOutput* translatedOutput = reinterpret_cast<HttpOutput*>(p.output);
-        translatedOutput->AnswerBufferWithContentType(p.answer, p.answerSize, p.mimeType);
-
+        AnswerBuffer(parameters);
         return true;
       }
 
       case _OrthancPluginService_CompressAndAnswerPngImage:
       {
-        const _OrthancPluginCompressAndAnswerPngImage& p = 
-          *reinterpret_cast<const _OrthancPluginCompressAndAnswerPngImage*>(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");
-
+        CompressAndAnswerPngImage(parameters);
         return true;
       }
 
       case _OrthancPluginService_GetDicomForInstance:
       {
-        const _OrthancPluginGetDicomForInstance& p = 
-          *reinterpret_cast<const _OrthancPluginGetDicomForInstance*>(parameters);
-
-        std::string dicom;
-        pimpl_->context_.ReadFile(dicom, p.instanceId, FileContentType_Dicom);
-
-        p.target->size = dicom.size();
+        GetDicomForInstance(parameters);
+        return true;
+      }
 
-        if (dicom.size() == 0)
-        {
-          p.target->data = NULL;
-        }
-        else
-        {
-          p.target->data = malloc(dicom.size());
-          if (p.target->data != NULL)
-          {
-            memcpy(p.target->data, &dicom[0], dicom.size());
-          }
-          else
-          {
-            return false;
-          }
-        }
-
+      case _OrthancPluginService_RestApiGet:
+      {
+        RestApiGet(parameters);
         return true;
       }
 
--- a/Plugins/Engine/PluginsHttpHandler.h	Fri Jun 20 13:45:22 2014 +0200
+++ b/Plugins/Engine/PluginsHttpHandler.h	Fri Jun 20 14:55:24 2014 +0200
@@ -36,6 +36,7 @@
 #include "../../Core/HttpServer/HttpHandler.h"
 #include "../../OrthancServer/ServerContext.h"
 #include "../../OrthancServer/OrthancRestApi/OrthancRestApi.h"
+#include "../OrthancCPlugin/OrthancCPlugin.h"
 
 #include <list>
 #include <boost/shared_ptr.hpp>
@@ -49,6 +50,16 @@
 
     boost::shared_ptr<PImpl> pimpl_;
 
+    void RegisterRestCallback(const void* parameters);
+
+    void AnswerBuffer(const void* parameters);
+
+    void CompressAndAnswerPngImage(const void* parameters);
+
+    void GetDicomForInstance(const void* parameters);
+
+    void RestApiGet(const void* parameters);
+
   public:
     PluginsHttpHandler(ServerContext& context);
 
--- a/Plugins/OrthancCPlugin/OrthancCPlugin.h	Fri Jun 20 13:45:22 2014 +0200
+++ b/Plugins/OrthancCPlugin/OrthancCPlugin.h	Fri Jun 20 14:55:24 2014 +0200
@@ -214,8 +214,12 @@
     _OrthancPluginService_AnswerBuffer = 2000,
     _OrthancPluginService_CompressAndAnswerPngImage = 2001,
 
-    /* Access to the Orthanc database */
-    _OrthancPluginService_GetDicomForInstance = 3000
+    /* Access to the Orthanc database and API */
+    _OrthancPluginService_GetDicomForInstance = 3000,
+    _OrthancPluginService_RestApiGet = 3001,
+    _OrthancPluginService_RestApiPost = 3002,
+    _OrthancPluginService_RestApiDelete = 3003,
+    _OrthancPluginService_RestApiPut = 3004
   } _OrthancPluginService;
 
 
@@ -544,6 +548,25 @@
   }
 
 
+
+  typedef struct
+  {
+    OrthancPluginMemoryBuffer*  target;
+    const char*                 uri;
+  } _OrthancPluginRestApiGet;
+
+  ORTHANC_PLUGIN_INLINE int  OrthancPluginRestApiGet(
+    OrthancPluginContext*       context,
+    OrthancPluginMemoryBuffer*  target,
+    const char*                 uri)
+  {
+    _OrthancPluginRestApiGet params;
+    params.target = target;
+    params.uri = uri;
+    return context->InvokeService(context, _OrthancPluginService_RestApiGet, &params);
+  }
+
+
 #ifdef  __cplusplus
 }
 #endif
--- a/Plugins/Samples/Basic/Plugin.c	Fri Jun 20 13:45:22 2014 +0200
+++ b/Plugins/Samples/Basic/Plugin.c	Fri Jun 20 14:55:24 2014 +0200
@@ -129,6 +129,7 @@
 
 ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* c)
 {
+  OrthancPluginMemoryBuffer tmp;
   char info[1024];
 
   context = c;
@@ -143,6 +144,11 @@
 
   OrthancPluginRegisterRestCallback(context, "/instances/([^/]+)/preview", Callback4);
 
+  
+  printf(">> %d\n", OrthancPluginRestApiGet(context, &tmp, "/instances"));
+  printf(">> [%s]\n", (const char*) tmp.data);
+  OrthancPluginFreeMemoryBuffer(context, &tmp);
+
   return 0;
 }
 
--- a/Resources/Configuration.json	Fri Jun 20 13:45:22 2014 +0200
+++ b/Resources/Configuration.json	Fri Jun 20 14:55:24 2014 +0200
@@ -34,10 +34,9 @@
   ],
 
   // List of paths to the plugins that are to be loaded into this
-  // instance of Orthanc
+  // instance of Orthanc (e.g. "/libPluginTest.so" for Linux, or
+  // "./PluginTest.dll" for Windows).
   "Plugins" : [
-    // "./libPluginTest.so" (for Linux) 
-    // "./PluginTest.dll" (for Windows)
   ],
 
 
--- a/UnitTestsSources/UnitTestsMain.cpp	Fri Jun 20 13:45:22 2014 +0200
+++ b/UnitTestsSources/UnitTestsMain.cpp	Fri Jun 20 14:55:24 2014 +0200
@@ -174,42 +174,69 @@
 }
 
 
-TEST(ParseGetQuery, Basic)
+TEST(ParseGetArguments, Basic)
 {
   HttpHandler::Arguments a;
-  HttpHandler::ParseGetQuery(a, "aaa=baaa&bb=a&aa=c");
+  HttpHandler::ParseGetArguments(a, "aaa=baaa&bb=a&aa=c");
   ASSERT_EQ(3u, a.size());
   ASSERT_EQ(a["aaa"], "baaa");
   ASSERT_EQ(a["bb"], "a");
   ASSERT_EQ(a["aa"], "c");
 }
 
-TEST(ParseGetQuery, BasicEmpty)
+TEST(ParseGetArguments, BasicEmpty)
 {
   HttpHandler::Arguments a;
-  HttpHandler::ParseGetQuery(a, "aaa&bb=aa&aa");
+  HttpHandler::ParseGetArguments(a, "aaa&bb=aa&aa");
   ASSERT_EQ(3u, a.size());
   ASSERT_EQ(a["aaa"], "");
   ASSERT_EQ(a["bb"], "aa");
   ASSERT_EQ(a["aa"], "");
 }
 
-TEST(ParseGetQuery, Single)
+TEST(ParseGetArguments, Single)
 {
   HttpHandler::Arguments a;
-  HttpHandler::ParseGetQuery(a, "aaa=baaa");
+  HttpHandler::ParseGetArguments(a, "aaa=baaa");
   ASSERT_EQ(1u, a.size());
   ASSERT_EQ(a["aaa"], "baaa");
 }
 
-TEST(ParseGetQuery, SingleEmpty)
+TEST(ParseGetArguments, SingleEmpty)
 {
   HttpHandler::Arguments a;
-  HttpHandler::ParseGetQuery(a, "aaa");
+  HttpHandler::ParseGetArguments(a, "aaa");
   ASSERT_EQ(1u, a.size());
   ASSERT_EQ(a["aaa"], "");
 }
 
+TEST(ParseGetQuery, Test1)
+{
+  UriComponents uri;
+  HttpHandler::Arguments a;
+  HttpHandler::ParseGetQuery(uri, a, "/instances/test/world?aaa=baaa&bb=a&aa=c");
+  ASSERT_EQ(3u, uri.size());
+  ASSERT_EQ("instances", uri[0]);
+  ASSERT_EQ("test", uri[1]);
+  ASSERT_EQ("world", uri[2]);
+  ASSERT_EQ(3u, a.size());
+  ASSERT_EQ(a["aaa"], "baaa");
+  ASSERT_EQ(a["bb"], "a");
+  ASSERT_EQ(a["aa"], "c");
+}
+
+TEST(ParseGetQuery, Test2)
+{
+  UriComponents uri;
+  HttpHandler::Arguments a;
+  HttpHandler::ParseGetQuery(uri, a, "/instances/test/world");
+  ASSERT_EQ(3u, uri.size());
+  ASSERT_EQ("instances", uri[0]);
+  ASSERT_EQ("test", uri[1]);
+  ASSERT_EQ("world", uri[2]);
+  ASSERT_EQ(0u, a.size());
+}
+
 TEST(Uri, SplitUriComponents)
 {
   UriComponents c;