changeset 1437:02f5a3f5c0a0

access to the REST API from Lua
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 30 Jun 2015 18:41:33 +0200
parents 0a3e3be59094
children af112b7d9cba
files CMakeLists.txt Core/HttpServer/HttpHandler.cpp Core/HttpServer/HttpHandler.h Core/HttpServer/StringHttpOutput.cpp Core/HttpServer/StringHttpOutput.h Core/Lua/LuaContext.cpp Core/Lua/LuaContext.h OrthancServer/LuaScripting.cpp OrthancServer/LuaScripting.h OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp OrthancServer/OrthancRestApi/OrthancRestApi.cpp OrthancServer/OrthancRestApi/OrthancRestApi.h OrthancServer/OrthancRestApi/OrthancRestArchive.cpp OrthancServer/OrthancRestApi/OrthancRestChanges.cpp OrthancServer/OrthancRestApi/OrthancRestResources.cpp OrthancServer/OrthancRestApi/OrthancRestSystem.cpp OrthancServer/main.cpp Plugins/Engine/OrthancPlugins.cpp
diffstat 18 files changed, 313 insertions(+), 55 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Tue Jun 30 17:19:26 2015 +0200
+++ b/CMakeLists.txt	Tue Jun 30 18:41:33 2015 +0200
@@ -100,6 +100,7 @@
   Core/HttpServer/FilesystemHttpHandler.cpp
   Core/HttpServer/HttpHandler.cpp
   Core/HttpServer/HttpOutput.cpp
+  Core/HttpServer/StringHttpOutput.cpp
   Core/HttpServer/MongooseServer.cpp
   Core/HttpServer/HttpFileSender.cpp
   Core/HttpServer/FilesystemHttpSender.cpp
--- a/Core/HttpServer/HttpHandler.cpp	Tue Jun 30 17:19:26 2015 +0200
+++ b/Core/HttpServer/HttpHandler.cpp	Tue Jun 30 18:41:33 2015 +0200
@@ -36,6 +36,9 @@
 #include <string.h>
 #include <iostream>
 
+#include "HttpOutput.h"
+#include "StringHttpOutput.h"
+
 
 namespace Orthanc
 {
@@ -188,4 +191,30 @@
       compiled[source[i].first] = source[i].second;
     }
   }
+
+
+  bool HttpHandler::SimpleGet(std::string& output,
+                              const std::string& uri)
+  {
+    Arguments headers;  // No HTTP header
+    std::string body;  // No body for a GET request
+
+    UriComponents curi;
+    GetArguments getArguments;
+    ParseGetQuery(curi, getArguments, uri.c_str());
+
+    StringHttpOutput stream;
+    HttpOutput http(stream, false /* no keep alive */);
+
+    if (Handle(http, HttpMethod_Get, curi, headers, getArguments, body))
+    {
+      stream.GetOutput(output);
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
 }
--- a/Core/HttpServer/HttpHandler.h	Tue Jun 30 17:19:26 2015 +0200
+++ b/Core/HttpServer/HttpHandler.h	Tue Jun 30 18:41:33 2015 +0200
@@ -78,5 +78,8 @@
 
     static void CompileGetArguments(Arguments& compiled,
                                     const GetArguments& source);
+
+    bool SimpleGet(std::string& output,
+                   const std::string& uri);
   };
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/HttpServer/StringHttpOutput.cpp	Tue Jun 30 18:41:33 2015 +0200
@@ -0,0 +1,55 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../PrecompiledHeaders.h"
+#include "StringHttpOutput.h"
+
+#include "../OrthancException.h"
+
+namespace Orthanc
+{
+  void StringHttpOutput::OnHttpStatusReceived(HttpStatus status)
+  {
+    if (status != HttpStatus_200_Ok)
+    {
+      throw OrthancException(ErrorCode_BadRequest);
+    }
+  }
+
+  void StringHttpOutput::Send(bool isHeader, const void* buffer, size_t length)
+  {
+    if (!isHeader)
+    {
+      buffer_.AddChunk(reinterpret_cast<const char*>(buffer), length);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/HttpServer/StringHttpOutput.h	Tue Jun 30 18:41:33 2015 +0200
@@ -0,0 +1,56 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "IHttpOutputStream.h"
+
+#include "../ChunkedBuffer.h"
+
+namespace Orthanc
+{
+  class StringHttpOutput : public IHttpOutputStream
+  {
+  private:
+    ChunkedBuffer buffer_;
+
+  public:
+    virtual void OnHttpStatusReceived(HttpStatus status);
+
+    virtual void Send(bool isHeader, const void* buffer, size_t length);
+
+    void GetOutput(std::string& output)
+    {
+      buffer_.Flatten(output);
+    }
+  };
+}
--- a/Core/Lua/LuaContext.cpp	Tue Jun 30 17:19:26 2015 +0200
+++ b/Core/Lua/LuaContext.cpp	Tue Jun 30 18:41:33 2015 +0200
@@ -46,14 +46,10 @@
 {
   LuaContext& LuaContext::GetLuaContext(lua_State *state)
   {
-    // Get the pointer to the "LuaContext" underlying object
-    lua_getglobal(state, "_LuaContext");
-    assert(lua_type(state, -1) == LUA_TLIGHTUSERDATA);
-    LuaContext* that = const_cast<LuaContext*>(reinterpret_cast<const LuaContext*>(lua_topointer(state, -1)));
-    assert(that != NULL);
-    lua_pop(state, 1);
+    const void* value = GetGlobalVariable(state, "_LuaContext");
+    assert(value != NULL);
 
-    return *that;
+    return *const_cast<LuaContext*>(reinterpret_cast<const LuaContext*>(value));
   }
 
   int LuaContext::PrintToLog(lua_State *state)
@@ -94,6 +90,35 @@
   }
 
 
+  int LuaContext::ParseJsonString(lua_State *state)
+  {
+    LuaContext& that = GetLuaContext(state);
+
+    int nArgs = lua_gettop(state);
+    if (nArgs != 1 ||
+        !lua_isstring(state, 1))    // Password
+    {
+      lua_pushnil(state);
+      return 1;
+    }
+
+    const char* str = lua_tostring(state, 1);
+
+    Json::Value value;
+    Json::Reader reader;
+    if (reader.parse(str, str + strlen(str), value))
+    {
+      that.PushJson(value);
+    }
+    else
+    {
+      lua_pushnil(state);
+    }
+
+    return 1;
+  }
+
+
   int LuaContext::SetHttpCredentials(lua_State *state)
   {
     LuaContext& that = GetLuaContext(state);
@@ -333,14 +358,14 @@
 
     luaL_openlibs(lua_);
     lua_register(lua_, "print", PrintToLog);
+    lua_register(lua_, "ParseJson", ParseJsonString);
     lua_register(lua_, "HttpGet", CallHttpGet);
     lua_register(lua_, "HttpPost", CallHttpPost);
     lua_register(lua_, "HttpPut", CallHttpPut);
     lua_register(lua_, "HttpDelete", CallHttpDelete);
     lua_register(lua_, "SetHttpCredentials", SetHttpCredentials);
-    
-    lua_pushlightuserdata(lua_, this);
-    lua_setglobal(lua_, "_LuaContext");
+
+    SetGlobalVariable("_LuaContext", this);
   }
 
 
@@ -403,4 +428,29 @@
     }
   }
 
+
+  void LuaContext::RegisterFunction(const char* name,
+                                    lua_CFunction func)
+  {
+    lua_register(lua_, name, func);
+  }
+
+
+  void LuaContext::SetGlobalVariable(const char* name,
+                                     void* value)
+  {
+    lua_pushlightuserdata(lua_, value);
+    lua_setglobal(lua_, name);
+  }
+
+  
+  const void* LuaContext::GetGlobalVariable(lua_State* state,
+                                            const char* name)
+  {
+    lua_getglobal(state, name);
+    assert(lua_type(state, -1) == LUA_TLIGHTUSERDATA);
+    const void* value = lua_topointer(state, -1);
+    lua_pop(state, 1);
+    return value;
+  }
 }
--- a/Core/Lua/LuaContext.h	Tue Jun 30 17:19:26 2015 +0200
+++ b/Core/Lua/LuaContext.h	Tue Jun 30 18:41:33 2015 +0200
@@ -57,6 +57,7 @@
     static LuaContext& GetLuaContext(lua_State *state);
 
     static int PrintToLog(lua_State *state);
+    static int ParseJsonString(lua_State *state);
 
     static int SetHttpCredentials(lua_State *state);
 
@@ -107,5 +108,14 @@
     {
       httpClient_.SetProxy(proxy);
     }
+
+    void RegisterFunction(const char* name,
+                          lua_CFunction func);
+
+    void SetGlobalVariable(const char* name,
+                           void* value);
+
+    static const void* GetGlobalVariable(lua_State* state,
+                                         const char* name);
   };
 }
--- a/OrthancServer/LuaScripting.cpp	Tue Jun 30 17:19:26 2015 +0200
+++ b/OrthancServer/LuaScripting.cpp	Tue Jun 30 18:41:33 2015 +0200
@@ -36,6 +36,7 @@
 #include "ServerContext.h"
 #include "OrthancInitialization.h"
 #include "../Core/Lua/LuaFunctionCall.h"
+#include "../Core/HttpServer/StringHttpOutput.h"
 
 #include "Scheduler/DeleteInstanceCommand.h"
 #include "Scheduler/StoreScuCommand.h"
@@ -50,6 +51,49 @@
 
 namespace Orthanc
 {
+  OrthancRestApi* LuaScripting::GetRestApi(lua_State *state)
+  {
+    const void* value = LuaContext::GetGlobalVariable(state, "_RestApi");
+    return const_cast<OrthancRestApi*>(reinterpret_cast<const OrthancRestApi*>(value));
+  }
+
+
+  int LuaScripting::RestApiGet(lua_State *state)
+  {
+    OrthancRestApi* restApi = GetRestApi(state);
+    if (restApi == NULL)
+    {
+      LOG(ERROR) << "Lua: The REST API is unavailable";
+      lua_pushnil(state);
+      return 1;
+    }
+
+    // Check the types of the arguments
+    int nArgs = lua_gettop(state);
+    if (nArgs != 1 || !lua_isstring(state, 1))  // URI
+    {
+      LOG(ERROR) << "Lua: Bad parameters to RestApiGet()";
+      lua_pushnil(state);
+      return 1;
+    }
+
+    const char* uri = lua_tostring(state, 1);
+    
+    std::string str;
+    if (restApi->SimpleGet(str, uri))
+    {
+      lua_pushstring(state, str.c_str());
+    }
+    else
+    {
+      LOG(ERROR) << "Lua: Error in RestApiGet() for URI " << uri;
+      lua_pushnil(state);
+    }
+
+    return 1;
+  }
+
+
   IServerCommand* LuaScripting::ParseOperation(const std::string& operation,
                                                const Json::Value& parameters)
   {
@@ -200,13 +244,27 @@
   }
 
 
-  LuaScripting::LuaScripting(ServerContext& context) : context_(context)
+  LuaScripting::LuaScripting(ServerContext& context) : context_(context), restApi_(NULL)
   {
+    lua_.RegisterFunction("RestApiGet", RestApiGet);
+
     lua_.Execute(Orthanc::EmbeddedResources::LUA_TOOLBOX);
     lua_.SetHttpProxy(Configuration::GetGlobalStringParameter("HttpProxy", ""));
   }
 
 
+  void LuaScripting::SetOrthancRestApi(OrthancRestApi& restApi)
+  {
+    lua_.SetGlobalVariable("_RestApi", &restApi);
+  }
+
+
+  void LuaScripting::ResetOrthancRestApi()
+  {
+    lua_.SetGlobalVariable("_RestApi", NULL);
+  }
+
+
   void LuaScripting::ApplyOnStoredInstance(const std::string& instanceId,
                                            const Json::Value& simplifiedTags,
                                            const Json::Value& metadata,
--- a/OrthancServer/LuaScripting.h	Tue Jun 30 17:19:26 2015 +0200
+++ b/OrthancServer/LuaScripting.h	Tue Jun 30 18:41:33 2015 +0200
@@ -35,6 +35,7 @@
 #include "IServerListener.h"
 #include "../Core/Lua/LuaContext.h"
 #include "Scheduler/IServerCommand.h"
+#include "OrthancRestApi/OrthancRestApi.h"
 
 namespace Orthanc
 {
@@ -43,6 +44,10 @@
   class LuaScripting : public IServerListener
   {
   private:
+    static OrthancRestApi* GetRestApi(lua_State *state);
+
+    static int RestApiGet(lua_State *state);
+
     void ApplyOnStoredInstance(const std::string& instanceId,
                                const Json::Value& simplifiedDicom,
                                const Json::Value& metadata,
@@ -61,6 +66,7 @@
     boost::mutex    mutex_;
     LuaContext      lua_;
     ServerContext&  context_;
+    OrthancRestApi* restApi_;
 
   public:
     class Locker : public boost::noncopyable
@@ -86,6 +92,10 @@
     };
 
     LuaScripting(ServerContext& context);
+    
+    void SetOrthancRestApi(OrthancRestApi& restApi);
+
+    void ResetOrthancRestApi();
 
     virtual void SignalStoredInstance(const std::string& publicId,
                                       DicomInstanceToStore& instance,
--- a/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Tue Jun 30 17:19:26 2015 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Tue Jun 30 18:41:33 2015 +0200
@@ -35,6 +35,7 @@
 
 #include "../FromDcmtkBridge.h"
 #include "../../Core/Uuid.h"
+#include "../ServerContext.h"
 
 #include <glog/logging.h>
 
--- a/OrthancServer/OrthancRestApi/OrthancRestApi.cpp	Tue Jun 30 17:19:26 2015 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestApi.cpp	Tue Jun 30 18:41:33 2015 +0200
@@ -34,6 +34,7 @@
 #include "OrthancRestApi.h"
 
 #include "../DicomModification.h"
+#include "../ServerContext.h"
 
 #include <glog/logging.h>
 
@@ -112,4 +113,16 @@
     Register("/tools/reset", ResetOrthanc);
     Register("/instances/{id}/frames/{frame}", RestApi::AutoListChildren);
   }
+
+
+  ServerContext& OrthancRestApi::GetContext(RestApiCall& call)
+  {
+    return GetApi(call).context_;
+  }
+
+
+  ServerIndex& OrthancRestApi::GetIndex(RestApiCall& call)
+  {
+    return GetContext(call).GetIndex();
+  }
 }
--- a/OrthancServer/OrthancRestApi/OrthancRestApi.h	Tue Jun 30 17:19:26 2015 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestApi.h	Tue Jun 30 18:41:33 2015 +0200
@@ -34,12 +34,14 @@
 
 #include "../../Core/RestApi/RestApi.h"
 #include "../DicomModification.h"
-#include "../ServerContext.h"
 
 #include <set>
 
 namespace Orthanc
 {
+  class ServerContext;
+  class ServerIndex;
+
   class OrthancRestApi : public RestApi
   {
   public:
@@ -76,15 +78,9 @@
       return dynamic_cast<OrthancRestApi&>(call.GetContext());
     }
 
-    static ServerContext& GetContext(RestApiCall& call)
-    {
-      return GetApi(call).context_;
-    }
+    static ServerContext& GetContext(RestApiCall& call);
 
-    static ServerIndex& GetIndex(RestApiCall& call)
-    {
-      return GetContext(call).GetIndex();
-    }
+    static ServerIndex& GetIndex(RestApiCall& call);
 
     void AnswerStoredInstance(RestApiPostCall& call,
                               const std::string& publicId,
--- a/OrthancServer/OrthancRestApi/OrthancRestArchive.cpp	Tue Jun 30 17:19:26 2015 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestArchive.cpp	Tue Jun 30 18:41:33 2015 +0200
@@ -37,6 +37,7 @@
 #include "../../Core/Compression/HierarchicalZipWriter.h"
 #include "../../Core/HttpServer/FilesystemHttpSender.h"
 #include "../../Core/Uuid.h"
+#include "../ServerContext.h"
 
 #include <glog/logging.h>
 #include <stdio.h>
--- a/OrthancServer/OrthancRestApi/OrthancRestChanges.cpp	Tue Jun 30 17:19:26 2015 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestChanges.cpp	Tue Jun 30 18:41:33 2015 +0200
@@ -33,6 +33,8 @@
 #include "../PrecompiledHeadersServer.h"
 #include "OrthancRestApi.h"
 
+#include "../ServerContext.h"
+
 #include <glog/logging.h>
 
 namespace Orthanc
--- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Tue Jun 30 17:19:26 2015 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Tue Jun 30 18:41:33 2015 +0200
@@ -37,6 +37,7 @@
 #include "../FromDcmtkBridge.h"
 #include "../ResourceFinder.h"
 #include "../DicomFindQuery.h"
+#include "../ServerContext.h"
 
 #include <glog/logging.h>
 
--- a/OrthancServer/OrthancRestApi/OrthancRestSystem.cpp	Tue Jun 30 17:19:26 2015 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestSystem.cpp	Tue Jun 30 18:41:33 2015 +0200
@@ -37,6 +37,7 @@
 #include "../FromDcmtkBridge.h"
 #include "../../Plugins/Engine/PluginsManager.h"
 #include "../../Plugins/Engine/OrthancPlugins.h"
+#include "../ServerContext.h"
 
 #include <glog/logging.h>
 
--- a/OrthancServer/main.cpp	Tue Jun 30 17:19:26 2015 +0200
+++ b/OrthancServer/main.cpp	Tue Jun 30 18:41:33 2015 +0200
@@ -503,7 +503,7 @@
     }
     
     context->SetStorageArea(*storage);
-
+    context->GetLua().SetOrthancRestApi(restApi);
 
     // GO !!! Start the requested servers
     if (Configuration::GetGlobalBoolParameter("HttpServerEnabled", true))
@@ -549,6 +549,8 @@
     LOG(WARNING) << "    Plugins have stopped";
 #endif
 
+    context->GetLua().ResetOrthancRestApi();
+
     dicomServer.Stop();
     LOG(WARNING) << "    DICOM server has stopped";
 
--- a/Plugins/Engine/OrthancPlugins.cpp	Tue Jun 30 17:19:26 2015 +0200
+++ b/Plugins/Engine/OrthancPlugins.cpp	Tue Jun 30 18:41:33 2015 +0200
@@ -33,7 +33,7 @@
 #include "OrthancPlugins.h"
 
 #include "../../Core/ChunkedBuffer.h"
-#include "../../Core/HttpServer/HttpOutput.h"
+#include "../../Core/HttpServer/StringHttpOutput.h"
 #include "../../Core/ImageFormats/PngWriter.h"
 #include "../../Core/OrthancException.h"
 #include "../../Core/Toolbox.h"
@@ -109,39 +109,6 @@
   }
 
 
-  namespace
-  {
-    // Anonymous namespace to avoid clashes between compilation modules
-    class StringHttpOutput : public IHttpOutputStream
-    {
-    private:
-      ChunkedBuffer buffer_;
-
-    public:
-      void GetOutput(std::string& output)
-      {
-        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)
-        {
-          buffer_.AddChunk(reinterpret_cast<const char*>(buffer), length);
-        }
-      }
-    };
-  }
-
-
 
   struct OrthancPlugins::PImpl
   {
@@ -649,6 +616,8 @@
     const _OrthancPluginRestApiGet& p = 
       *reinterpret_cast<const _OrthancPluginRestApiGet*>(parameters);
         
+    // TODO : Use "HttpHandler::SimpleGet()"
+
     HttpHandler::Arguments headers;  // No HTTP header
     std::string body;  // No body for a GET request