changeset 3442:dd1e68f2d0c0

Fix issue #106 (Unable to export preview as jpeg from Lua script)
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 24 Jun 2019 16:06:47 +0200
parents 6cc72ebfd6ef
children 0371b65d5f76
files Core/HttpServer/HttpToolbox.cpp Core/HttpServer/HttpToolbox.h Core/Lua/LuaContext.cpp Core/Lua/LuaContext.h Core/Lua/LuaFunctionCall.cpp NEWS OrthancServer/LuaScripting.cpp Plugins/Engine/OrthancPlugins.cpp
diffstat 8 files changed, 126 insertions(+), 89 deletions(-) [+]
line wrap: on
line diff
--- a/Core/HttpServer/HttpToolbox.cpp	Mon Jun 24 12:37:52 2019 +0200
+++ b/Core/HttpServer/HttpToolbox.cpp	Mon Jun 24 16:06:47 2019 +0200
@@ -225,25 +225,15 @@
   }
 
 
-  bool HttpToolbox::SimpleGet(std::string& result,
-                              IHttpHandler& handler,
-                              RequestOrigin origin,
-                              const std::string& uri)
-  {
-    IHttpHandler::Arguments headers;  // No HTTP header
-    return SimpleGet(result, handler, origin, uri, headers);
-  }
-
-
   static bool SimplePostOrPut(std::string& result,
                               IHttpHandler& handler,
                               RequestOrigin origin,
                               HttpMethod method,
                               const std::string& uri,
                               const void* bodyData,
-                              size_t bodySize)
+                              size_t bodySize,
+                              const IHttpHandler::Arguments& httpHeaders)
   {
-    IHttpHandler::Arguments headers;  // No HTTP header
     IHttpHandler::GetArguments getArguments;  // No GET argument for POST/PUT
 
     UriComponents curi;
@@ -253,7 +243,7 @@
     HttpOutput http(stream, false /* no keep alive */);
 
     if (handler.Handle(http, origin, LOCALHOST, "", method, curi, 
-                       headers, getArguments, bodyData, bodySize))
+                       httpHeaders, getArguments, bodyData, bodySize))
     {
       stream.GetOutput(result);
       return true;
@@ -270,9 +260,10 @@
                                RequestOrigin origin,
                                const std::string& uri,
                                const void* bodyData,
-                               size_t bodySize)
+                               size_t bodySize,
+                               const IHttpHandler::Arguments& httpHeaders)
   {
-    return SimplePostOrPut(result, handler, origin, HttpMethod_Post, uri, bodyData, bodySize);
+    return SimplePostOrPut(result, handler, origin, HttpMethod_Post, uri, bodyData, bodySize, httpHeaders);
   }
 
 
@@ -281,26 +272,27 @@
                               RequestOrigin origin,
                               const std::string& uri,
                               const void* bodyData,
-                              size_t bodySize)
+                              size_t bodySize,
+                              const IHttpHandler::Arguments& httpHeaders)
   {
-    return SimplePostOrPut(result, handler, origin, HttpMethod_Put, uri, bodyData, bodySize);
+    return SimplePostOrPut(result, handler, origin, HttpMethod_Put, uri, bodyData, bodySize, httpHeaders);
   }
 
 
   bool HttpToolbox::SimpleDelete(IHttpHandler& handler,
                                  RequestOrigin origin,
-                                 const std::string& uri)
+                                 const std::string& uri,
+                                 const IHttpHandler::Arguments& httpHeaders)
   {
     UriComponents curi;
     Toolbox::SplitUriComponents(curi, uri);
 
-    IHttpHandler::Arguments headers;  // No HTTP header
     IHttpHandler::GetArguments getArguments;  // No GET argument for DELETE
 
     StringHttpOutput stream;
     HttpOutput http(stream, false /* no keep alive */);
 
     return handler.Handle(http, origin, LOCALHOST, "", HttpMethod_Delete, curi, 
-                          headers, getArguments, NULL /* no body for DELETE */, 0);
+                          httpHeaders, getArguments, NULL /* no body for DELETE */, 0);
   }
 }
--- a/Core/HttpServer/HttpToolbox.h	Mon Jun 24 12:37:52 2019 +0200
+++ b/Core/HttpServer/HttpToolbox.h	Mon Jun 24 16:06:47 2019 +0200
@@ -64,11 +64,6 @@
     static bool SimpleGet(std::string& result,
                           IHttpHandler& handler,
                           RequestOrigin origin,
-                          const std::string& uri);
-
-    static bool SimpleGet(std::string& result,
-                          IHttpHandler& handler,
-                          RequestOrigin origin,
                           const std::string& uri,
                           const IHttpHandler::Arguments& httpHeaders);
 
@@ -77,17 +72,20 @@
                            RequestOrigin origin,
                            const std::string& uri,
                            const void* bodyData,
-                           size_t bodySize);
+                           size_t bodySize,
+                          const IHttpHandler::Arguments& httpHeaders);
 
     static bool SimplePut(std::string& result,
                           IHttpHandler& handler,
                           RequestOrigin origin,
                           const std::string& uri,
                           const void* bodyData,
-                          size_t bodySize);
+                          size_t bodySize,
+                          const IHttpHandler::Arguments& httpHeaders);
 
     static bool SimpleDelete(IHttpHandler& handler,
                              RequestOrigin origin,
-                             const std::string& uri);
+                             const std::string& uri,
+                             const IHttpHandler::Arguments& httpHeaders);
   };
 }
--- a/Core/Lua/LuaContext.cpp	Mon Jun 24 12:37:52 2019 +0200
+++ b/Core/Lua/LuaContext.cpp	Mon Jun 24 16:06:47 2019 +0200
@@ -36,6 +36,7 @@
 
 #include "../Logging.h"
 #include "../OrthancException.h"
+#include "../Toolbox.h"
 
 #include <set>
 #include <cassert>
@@ -156,7 +157,7 @@
     }
 
     Json::Value json;
-    that.GetJson(json, 1, keepStrings);
+    that.GetJson(json, state, 1, keepStrings);
 
     Json::FastWriter writer;
     std::string s = writer.write(json);
@@ -208,27 +209,21 @@
 
     return true;
   }
-
-  void LuaContext::SetHttpHeaders(lua_State *state, int top)
-  {
-    this->httpClient_.ClearHeaders(); // always reset headers in case they have been set in a previous request
-
-    if (lua_gettop(state) >= top)
-    {
-      Json::Value headers;
-      this->GetJson(headers, top, true);
+  
 
-      Json::Value::Members members = headers.getMemberNames();
+  void LuaContext::SetHttpHeaders(int top)
+  {
+    std::map<std::string, std::string> headers;
+    GetDictionaryArgument(headers, lua_, top, false /* keep key case as provided by Lua script */);
+      
+    httpClient_.ClearHeaders(); // always reset headers in case they have been set in a previous request
 
-      for (Json::Value::Members::const_iterator 
-           it = members.begin(); it != members.end(); ++it)
-      {
-        this->httpClient_.AddHeader(*it, headers[*it].asString());
-      }
+    for (std::map<std::string, std::string>::const_iterator
+           it = headers.begin(); it != headers.end(); ++it)
+    {
+      httpClient_.AddHeader(it->first, it->second);
     }
-
-  }
-  
+  }  
 
 
   int LuaContext::CallHttpGet(lua_State *state)
@@ -237,8 +232,8 @@
 
     // Check the types of the arguments
     int nArgs = lua_gettop(state);
-    if ((nArgs < 1 || nArgs > 2) ||         // check args count
-       !lua_isstring(state, 1))             // URL is a string
+    if (nArgs < 1 || nArgs > 2 ||         // check args count
+       !lua_isstring(state, 1))           // URL is a string
     {
       LOG(ERROR) << "Lua: Bad parameters to HttpGet()";
       lua_pushnil(state);
@@ -250,7 +245,7 @@
     that.httpClient_.SetMethod(HttpMethod_Get);
     that.httpClient_.SetUrl(url);
     that.httpClient_.GetBody().clear();
-    that.SetHttpHeaders(state, 2);
+    that.SetHttpHeaders(2);
 
     // Do the HTTP GET request
     if (!that.AnswerHttpQuery(state))
@@ -283,7 +278,7 @@
     const char* url = lua_tostring(state, 1);
     that.httpClient_.SetMethod(method);
     that.httpClient_.SetUrl(url);
-    that.SetHttpHeaders(state, 3);
+    that.SetHttpHeaders(3);
 
     if (nArgs >= 2 && !lua_isnil(state, 2))
     {
@@ -345,7 +340,7 @@
     that.httpClient_.SetMethod(HttpMethod_Delete);
     that.httpClient_.SetUrl(url);
     that.httpClient_.GetBody().clear();
-    that.SetHttpHeaders(state, 2);
+    that.SetHttpHeaders(2);
 
     // Do the HTTP DELETE request
     std::string s;
@@ -434,10 +429,11 @@
 
 
   void LuaContext::GetJson(Json::Value& result,
+                           lua_State* state,
                            int top,
                            bool keepStrings)
   {
-    if (lua_istable(lua_, top))
+    if (lua_istable(state, top))
     {
       Json::Value tmp = Json::objectValue;
       bool isArray = true;
@@ -448,19 +444,19 @@
       // Push another reference to the table on top of the stack (so we know
       // where it is, and this function can work for negative, positive and
       // pseudo indices
-      lua_pushvalue(lua_, top);
+      lua_pushvalue(state, top);
       // stack now contains: -1 => table
-      lua_pushnil(lua_);
+      lua_pushnil(state);
       // stack now contains: -1 => nil; -2 => table
-      while (lua_next(lua_, -2))
+      while (lua_next(state, -2))
       {
         // stack now contains: -1 => value; -2 => key; -3 => table
         // copy the key so that lua_tostring does not modify the original
-        lua_pushvalue(lua_, -2);
+        lua_pushvalue(state, -2);
         // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table
-        std::string key(lua_tostring(lua_, -1));
+        std::string key(lua_tostring(state, -1));
         Json::Value v;
-        GetJson(v, -2, keepStrings);
+        GetJson(v, state, -2, keepStrings);
 
         tmp[key] = v;
 
@@ -479,13 +475,13 @@
         }
         
         // pop value + copy of key, leaving original key
-        lua_pop(lua_, 2);
+        lua_pop(state, 2);
         // stack now contains: -1 => key; -2 => table
       }
       // stack now contains: -1 => table (when lua_next returns 0 it pops the key
       // but does not push anything.)
       // Pop table
-      lua_pop(lua_, 1);
+      lua_pop(state, 1);
 
       // Stack is now the same as it was on entry to this function
 
@@ -502,20 +498,20 @@
         result = tmp;
       }
     }
-    else if (lua_isnil(lua_, top))
+    else if (lua_isnil(state, top))
     {
       result = Json::nullValue;
     }
     else if (!keepStrings &&
-             lua_isboolean(lua_, top))
+             lua_isboolean(state, top))
     {
-      result = lua_toboolean(lua_, top) ? true : false;
+      result = lua_toboolean(state, top) ? true : false;
     }
     else if (!keepStrings &&
-             lua_isnumber(lua_, top))
+             lua_isnumber(state, top))
     {
       // Convert to "int" if truncation does not loose precision
-      double value = static_cast<double>(lua_tonumber(lua_, top));
+      double value = static_cast<double>(lua_tonumber(state, top));
       int truncated = static_cast<int>(value);
 
       if (std::abs(value - static_cast<double>(truncated)) <= 
@@ -528,15 +524,15 @@
         result = value;
       }
     }
-    else if (lua_isstring(lua_, top))
+    else if (lua_isstring(state, top))
     {
       // Caution: The "lua_isstring()" case must be the last, since
       // Lua can convert most types to strings by default.
-      result = std::string(lua_tostring(lua_, top));
+      result = std::string(lua_tostring(state, top));
     }
-    else if (lua_isboolean(lua_, top))
+    else if (lua_isboolean(state, top))
     {
-      result = lua_toboolean(lua_, top) ? true : false;
+      result = lua_toboolean(state, top) ? true : false;
     }
     else
     {
@@ -653,4 +649,33 @@
     lua_pop(state, 1);
     return value;
   }
+
+
+  void LuaContext::GetDictionaryArgument(std::map<std::string, std::string>& target,
+                                         lua_State* state,
+                                         int top,
+                                         bool keyToLowerCase)
+  {
+    target.clear();
+
+    if (lua_gettop(state) >= top)
+    {
+      Json::Value headers;
+      GetJson(headers, state, top, true);
+
+      Json::Value::Members members = headers.getMemberNames();
+
+      for (size_t i = 0; i < members.size(); i++)
+      {
+        std::string key = members[i];
+
+        if (keyToLowerCase)
+        {
+          Toolbox::ToLowerCase(key);
+        }
+        
+        target[key] = headers[members[i]].asString();
+      }
+    }
+  }
 }
--- a/Core/Lua/LuaContext.h	Mon Jun 24 12:37:52 2019 +0200
+++ b/Core/Lua/LuaContext.h	Mon Jun 24 16:06:47 2019 +0200
@@ -87,11 +87,12 @@
     void ExecuteInternal(std::string* output,
                          const std::string& command);
 
-    void GetJson(Json::Value& result,
-                 int top,
-                 bool keepStrings);
+    static void GetJson(Json::Value& result,
+                        lua_State* state,
+                        int top,
+                        bool keepStrings);
 
-    void SetHttpHeaders(lua_State* state, int top);
+    void SetHttpHeaders(int top);
     
   public:
     LuaContext();
@@ -136,5 +137,10 @@
                                          const char* name);
 
     void PushJson(const Json::Value& value);
+
+    static void GetDictionaryArgument(std::map<std::string, std::string>& target,
+                                      lua_State* state,
+                                      int top,
+                                      bool keyToLowerCase);
   };
 }
--- a/Core/Lua/LuaFunctionCall.cpp	Mon Jun 24 12:37:52 2019 +0200
+++ b/Core/Lua/LuaFunctionCall.cpp	Mon Jun 24 16:06:47 2019 +0200
@@ -134,7 +134,7 @@
                                       bool keepStrings)
   {
     ExecuteInternal(1);
-    context_.GetJson(result, lua_gettop(context_.lua_), keepStrings);
+    context_.GetJson(result, context_.lua_, lua_gettop(context_.lua_), keepStrings);
   }
 
 
--- a/NEWS	Mon Jun 24 12:37:52 2019 +0200
+++ b/NEWS	Mon Jun 24 16:06:47 2019 +0200
@@ -27,6 +27,7 @@
 * Allow the serialization of signed 16bpp images in PAM format
 * HTTP header "Accept-Encoding" is honored for streams without built-in support for compression
 * The default HTTP timeout is now 60 seconds (instead of 10 seconds in previous versions)
+* Fix issue #106 (Unable to export preview as jpeg from Lua script)
 * Fix issue #136 (C-FIND request fails when found DICOM file does not have certain tags)
 * Fix issue #137 (C-STORE fails for unknown SOP Class although server is configured to accept any)
 * Fix issue #138 (POST to modalities/{name} accepts invalid characters)
--- a/OrthancServer/LuaScripting.cpp	Mon Jun 24 12:37:52 2019 +0200
+++ b/OrthancServer/LuaScripting.cpp	Mon Jun 24 16:06:47 2019 +0200
@@ -279,9 +279,9 @@
 
     // Check the types of the arguments
     int nArgs = lua_gettop(state);
-    if ((nArgs != 1 && nArgs != 2) || 
+    if (nArgs < 1 || nArgs > 3 || 
         !lua_isstring(state, 1) ||                 // URI
-        (nArgs == 2 && !lua_isboolean(state, 2)))  // Restrict to built-in API?
+        (nArgs >= 2 && !lua_isboolean(state, 2)))  // Restrict to built-in API?
     {
       LOG(ERROR) << "Lua: Bad parameters to RestApiGet()";
       lua_pushnil(state);
@@ -291,11 +291,14 @@
     const char* uri = lua_tostring(state, 1);
     bool builtin = (nArgs == 2 ? lua_toboolean(state, 2) != 0 : false);
 
+    std::map<std::string, std::string> headers;
+    LuaContext::GetDictionaryArgument(headers, state, 3, true /* HTTP header key to lower case */);
+
     try
     {
       std::string result;
       if (HttpToolbox::SimpleGet(result, serverContext->GetHttpHandler().RestrictToOrthancRestApi(builtin), 
-                                 RequestOrigin_Lua, uri))
+                                 RequestOrigin_Lua, uri, headers))
       {
         lua_pushlstring(state, result.c_str(), result.size());
         return 1;
@@ -325,10 +328,10 @@
 
     // Check the types of the arguments
     int nArgs = lua_gettop(state);
-    if ((nArgs != 2 && nArgs != 3) || 
+    if (nArgs < 2 || nArgs > 4 || 
         !lua_isstring(state, 1) ||                 // URI
         !lua_isstring(state, 2) ||                 // Body
-        (nArgs == 3 && !lua_isboolean(state, 3)))  // Restrict to built-in API?
+        (nArgs >= 3 && !lua_isboolean(state, 3)))  // Restrict to built-in API?
     {
       LOG(ERROR) << "Lua: Bad parameters to " << (isPost ? "RestApiPost()" : "RestApiPut()");
       lua_pushnil(state);
@@ -340,14 +343,17 @@
     const char* bodyData = lua_tolstring(state, 2, &bodySize);
     bool builtin = (nArgs == 3 ? lua_toboolean(state, 3) != 0 : false);
 
+    std::map<std::string, std::string> headers;
+    LuaContext::GetDictionaryArgument(headers, state, 4, true /* HTTP header key to lower case */);
+        
     try
     {
       std::string result;
       if (isPost ?
           HttpToolbox::SimplePost(result, serverContext->GetHttpHandler().RestrictToOrthancRestApi(builtin), 
-                                  RequestOrigin_Lua, uri, bodyData, bodySize) :
+                                  RequestOrigin_Lua, uri, bodyData, bodySize, headers) :
           HttpToolbox::SimplePut(result, serverContext->GetHttpHandler().RestrictToOrthancRestApi(builtin), 
-                                 RequestOrigin_Lua, uri, bodyData, bodySize))
+                                 RequestOrigin_Lua, uri, bodyData, bodySize, headers))
       {
         lua_pushlstring(state, result.c_str(), result.size());
         return 1;
@@ -391,9 +397,9 @@
 
     // Check the types of the arguments
     int nArgs = lua_gettop(state);
-    if ((nArgs != 1 && nArgs != 2) || 
+    if (nArgs < 1 || nArgs > 3 ||
         !lua_isstring(state, 1) ||                 // URI
-        (nArgs == 2 && !lua_isboolean(state, 2)))  // Restrict to built-in API?
+        (nArgs >= 2 && !lua_isboolean(state, 2)))  // Restrict to built-in API?
     {
       LOG(ERROR) << "Lua: Bad parameters to RestApiDelete()";
       lua_pushnil(state);
@@ -403,10 +409,13 @@
     const char* uri = lua_tostring(state, 1);
     bool builtin = (nArgs == 2 ? lua_toboolean(state, 2) != 0 : false);
 
+    std::map<std::string, std::string> headers;
+    LuaContext::GetDictionaryArgument(headers, state, 3, true /* HTTP header key to lower case */);
+    
     try
     {
       if (HttpToolbox::SimpleDelete(serverContext->GetHttpHandler().RestrictToOrthancRestApi(builtin), 
-                                    RequestOrigin_Lua, uri))
+                                    RequestOrigin_Lua, uri, headers))
       {
         lua_pushboolean(state, 1);
         return 1;
--- a/Plugins/Engine/OrthancPlugins.cpp	Mon Jun 24 12:37:52 2019 +0200
+++ b/Plugins/Engine/OrthancPlugins.cpp	Mon Jun 24 16:06:47 2019 +0200
@@ -1948,8 +1948,10 @@
       handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
     }
 
+    std::map<std::string, std::string> httpHeaders;
+
     std::string result;
-    if (HttpToolbox::SimpleGet(result, *handler, RequestOrigin_Plugins, p.uri))
+    if (HttpToolbox::SimpleGet(result, *handler, RequestOrigin_Plugins, p.uri, httpHeaders))
     {
       CopyToMemoryBuffer(*p.target, result);
     }
@@ -2013,10 +2015,12 @@
       handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
     }
       
+    std::map<std::string, std::string> httpHeaders;
+
     std::string result;
     if (isPost ? 
-        HttpToolbox::SimplePost(result, *handler, RequestOrigin_Plugins, p.uri, p.body, p.bodySize) :
-        HttpToolbox::SimplePut (result, *handler, RequestOrigin_Plugins, p.uri, p.body, p.bodySize))
+        HttpToolbox::SimplePost(result, *handler, RequestOrigin_Plugins, p.uri, p.body, p.bodySize, httpHeaders) :
+        HttpToolbox::SimplePut (result, *handler, RequestOrigin_Plugins, p.uri, p.body, p.bodySize, httpHeaders))
     {
       CopyToMemoryBuffer(*p.target, result);
     }
@@ -2041,7 +2045,9 @@
       handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
     }
       
-    if (!HttpToolbox::SimpleDelete(*handler, RequestOrigin_Plugins, uri))
+    std::map<std::string, std::string> httpHeaders;
+
+    if (!HttpToolbox::SimpleDelete(*handler, RequestOrigin_Plugins, uri, httpHeaders))
     {
       throw OrthancException(ErrorCode_UnknownResource);
     }