# HG changeset patch # User Sebastien Jodogne # Date 1435765326 -7200 # Node ID 5ba7471780ae63b9130e93b12413dfdb8bccc1fd # Parent 8dc80ba768aa96ff6b0acbaf9a1b2ea1f75844a9 refactoring: HttpToolbox, DumpJson in Lua diff -r 8dc80ba768aa -r 5ba7471780ae Core/HttpServer/HttpToolbox.cpp --- a/Core/HttpServer/HttpToolbox.cpp Wed Jul 01 13:16:12 2015 +0200 +++ b/Core/HttpServer/HttpToolbox.cpp Wed Jul 01 17:42:06 2015 +0200 @@ -194,7 +194,7 @@ } - bool HttpToolbox::SimpleGet(std::string& output, + bool HttpToolbox::SimpleGet(std::string& result, IHttpHandler& handler, const std::string& uri) { @@ -210,7 +210,35 @@ if (handler.Handle(http, HttpMethod_Get, curi, headers, getArguments, NULL /* no body for GET */, 0)) { - stream.GetOutput(output); + stream.GetOutput(result); + return true; + } + else + { + return false; + } + } + + + static bool SimplePostOrPut(std::string& result, + IHttpHandler& handler, + HttpMethod method, + const std::string& uri, + const char* bodyData, + size_t bodySize) + { + IHttpHandler::Arguments headers; // No HTTP header + IHttpHandler::GetArguments getArguments; // No GET argument for POST/PUT + + UriComponents curi; + Toolbox::SplitUriComponents(curi, uri); + + StringHttpOutput stream; + HttpOutput http(stream, false /* no keep alive */); + + if (handler.Handle(http, method, curi, headers, getArguments, bodyData, bodySize)) + { + stream.GetOutput(result); return true; } else @@ -219,4 +247,40 @@ } } + + bool HttpToolbox::SimplePost(std::string& result, + IHttpHandler& handler, + const std::string& uri, + const char* bodyData, + size_t bodySize) + { + return SimplePostOrPut(result, handler, HttpMethod_Post, uri, bodyData, bodySize); + } + + + bool HttpToolbox::SimplePut(std::string& result, + IHttpHandler& handler, + const std::string& uri, + const char* bodyData, + size_t bodySize) + { + return SimplePostOrPut(result, handler, HttpMethod_Put, uri, bodyData, bodySize); + } + + + bool HttpToolbox::SimpleDelete(IHttpHandler& handler, + const std::string& uri) + { + 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, HttpMethod_Delete, curi, headers, getArguments, + NULL /* no body for DELETE */, 0); + } } diff -r 8dc80ba768aa -r 5ba7471780ae Core/HttpServer/HttpToolbox.h --- a/Core/HttpServer/HttpToolbox.h Wed Jul 01 13:16:12 2015 +0200 +++ b/Core/HttpServer/HttpToolbox.h Wed Jul 01 17:42:06 2015 +0200 @@ -60,8 +60,23 @@ static void CompileGetArguments(IHttpHandler::Arguments& compiled, const IHttpHandler::GetArguments& source); - static bool SimpleGet(std::string& output, + static bool SimpleGet(std::string& result, IHttpHandler& handler, const std::string& uri); + + static bool SimplePost(std::string& result, + IHttpHandler& handler, + const std::string& uri, + const char* bodyData, + size_t bodySize); + + static bool SimplePut(std::string& result, + IHttpHandler& handler, + const std::string& uri, + const char* bodyData, + size_t bodySize); + + static bool SimpleDelete(IHttpHandler& handler, + const std::string& uri); }; } diff -r 8dc80ba768aa -r 5ba7471780ae Core/Lua/LuaContext.cpp --- a/Core/Lua/LuaContext.cpp Wed Jul 01 13:16:12 2015 +0200 +++ b/Core/Lua/LuaContext.cpp Wed Jul 01 17:42:06 2015 +0200 @@ -33,8 +33,10 @@ #include "../PrecompiledHeaders.h" #include "LuaContext.h" +#include #include #include +#include extern "C" { @@ -90,7 +92,7 @@ } - int LuaContext::ParseJsonString(lua_State *state) + int LuaContext::ParseJson(lua_State *state) { LuaContext& that = GetLuaContext(state); @@ -119,6 +121,32 @@ } + int LuaContext::DumpJson(lua_State *state) + { + int nArgs = lua_gettop(state); + if (nArgs != 1) + { + lua_pushnil(state); + return 1; + } + + Json::Value json; + if (GetJson(json, state, 1)) + { + Json::FastWriter writer; + std::string s = writer.write(json); + lua_pushstring(state, s.c_str()); + } + else + { + LOG(ERROR) << "Lua: Unable to convert a JSON variable to a string"; + lua_pushnil(state); + } + + return 1; + } + + int LuaContext::SetHttpCredentials(lua_State *state) { LuaContext& that = GetLuaContext(state); @@ -348,6 +376,135 @@ } + static bool CompactObjectToArray(Json::Value& result, + const Json::Value& source) + { + Json::Value::Members members = source.getMemberNames(); + + std::set keys; + for (Json::Value::ArrayIndex i = 0; i < members.size(); i++) + { + try + { + size_t key = boost::lexical_cast(members[i]); + keys.insert(key); + } + catch (boost::bad_lexical_cast&) + { + return false; + } + } + + if (keys.size() != members.size()) + { + return false; + } + + for (size_t i = 1; i <= members.size(); i++) + { + if (keys.find(i) == keys.end()) + { + return false; + } + } + + result = Json::arrayValue; + result.resize(members.size()); + for (size_t i = 0; i < members.size(); i++) + { + Json::Value::ArrayIndex key = boost::lexical_cast(members[i]); + assert(key > 0); + result[key - 1] = source[members[i]]; + } + + return true; + } + + + bool LuaContext::GetJson(Json::Value& result, + lua_State *state, + int i) + { + // Caution: The order of the calls below is important, otherwise + // Lua considers everything as a string. + + if (lua_isnil(state, i)) + { + result = Json::nullValue; + return true; + } + + if (lua_isboolean(state, i)) + { + result = lua_toboolean(state, i) ? true : false; + return true; + } + + if (lua_isnumber(state, i)) + { + result = lua_tonumber(state, i); + return true; + } + + if (lua_isstring(state, i)) + { + result = lua_tostring(state, i); + return true; + } + + if (lua_istable(state, i)) + { + result = Json::objectValue; + + // http://stackoverflow.com/a/6142700/881731 + // 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(state, i); + // stack now contains: -1 => table + lua_pushnil(state); + // stack now contains: -1 => nil; -2 => table + 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(state, -2); + // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table + const char *key = lua_tostring(state, -1); + + Json::Value item; + if (!GetJson(item, state, -2)) + { + lua_pop(state, 3); // TODO IS THIS CORRECT? + return false; + } + + result[key] = item; + + // pop value + copy of key, leaving original key + 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(state, 1); + + // Stack is now the same as it was on entry to this function + + Json::Value array; + if (CompactObjectToArray(array, result)) + { + result = array; + } + + return true; + } + + return false; + } + + LuaContext::LuaContext() { lua_ = luaL_newstate(); @@ -358,7 +515,8 @@ luaL_openlibs(lua_); lua_register(lua_, "print", PrintToLog); - lua_register(lua_, "ParseJson", ParseJsonString); + lua_register(lua_, "ParseJson", ParseJson); + lua_register(lua_, "DumpJson", DumpJson); lua_register(lua_, "HttpGet", CallHttpGet); lua_register(lua_, "HttpPost", CallHttpPost); lua_register(lua_, "HttpPut", CallHttpPut); diff -r 8dc80ba768aa -r 5ba7471780ae Core/Lua/LuaContext.h --- a/Core/Lua/LuaContext.h Wed Jul 01 13:16:12 2015 +0200 +++ b/Core/Lua/LuaContext.h Wed Jul 01 17:42:06 2015 +0200 @@ -57,7 +57,8 @@ static LuaContext& GetLuaContext(lua_State *state); static int PrintToLog(lua_State *state); - static int ParseJsonString(lua_State *state); + static int ParseJson(lua_State *state); + static int DumpJson(lua_State *state); static int SetHttpCredentials(lua_State *state); @@ -74,6 +75,10 @@ const std::string& command); void PushJson(const Json::Value& value); + + static bool GetJson(Json::Value& result, + lua_State *state, + int index); public: LuaContext(); diff -r 8dc80ba768aa -r 5ba7471780ae NEWS --- a/NEWS Wed Jul 01 13:16:12 2015 +0200 +++ b/NEWS Wed Jul 01 17:42:06 2015 +0200 @@ -14,6 +14,7 @@ Fixes ----- +* Many code refactorings * Fix compatibility issues for C-Find SCU to Siemens Syngo.Via modalities SCP * Fix issue 35 (Characters in PatientID string are not protected for C-Find) * Fix issue 37 (Hyphens trigger range query even if datatype does not support ranges) diff -r 8dc80ba768aa -r 5ba7471780ae OrthancServer/LuaScripting.cpp --- a/OrthancServer/LuaScripting.cpp Wed Jul 01 13:16:12 2015 +0200 +++ b/OrthancServer/LuaScripting.cpp Wed Jul 01 17:42:06 2015 +0200 @@ -58,6 +58,7 @@ } + // Syntax in Lua: RestApiGet(uri, builtin) int LuaScripting::RestApiGet(lua_State *state) { ServerContext* serverContext = GetServerContext(state); @@ -89,7 +90,107 @@ } else { - LOG(ERROR) << "Lua: Error in RestApiGet() for URI " << uri; + LOG(ERROR) << "Lua: Error in RestApiGet() for URI: " << uri; + lua_pushnil(state); + } + + return 1; + } + + + int LuaScripting::RestApiPostOrPut(lua_State *state, + bool isPost) + { + ServerContext* serverContext = GetServerContext(state); + if (serverContext == NULL) + { + LOG(ERROR) << "Lua: The Orthanc API is unavailable"; + lua_pushnil(state); + return 1; + } + + // Check the types of the arguments + int nArgs = lua_gettop(state); + if ((nArgs != 2 && nArgs != 3) || + !lua_isstring(state, 1) || // URI + !lua_isstring(state, 2) || // Body + (nArgs == 3 && !lua_isboolean(state, 3))) // Restrict to built-in API? + { + LOG(ERROR) << "Lua: Bad parameters to " << (isPost ? "RestApiPost()" : "RestApiPut()"); + lua_pushnil(state); + return 1; + } + + const char* uri = lua_tostring(state, 1); + size_t bodySize = 0; + const char* bodyData = lua_tolstring(state, 2, &bodySize); + bool builtin = (nArgs == 3 ? lua_toboolean(state, 3) : false); + + std::string result; + if (isPost ? + HttpToolbox::SimplePost(result, serverContext->GetHttpHandler().RestrictToOrthancRestApi(builtin), + uri, bodyData, bodySize) : + HttpToolbox::SimplePut(result, serverContext->GetHttpHandler().RestrictToOrthancRestApi(builtin), + uri, bodyData, bodySize)) + { + lua_pushstring(state, result.c_str()); + } + else + { + LOG(ERROR) << "Lua: Error in " << (isPost ? "RestApiPost()" : "RestApiPut()") << " for URI: " << uri; + lua_pushnil(state); + } + + return 1; + } + + + // Syntax in Lua: RestApiPost(uri, body, builtin) + int LuaScripting::RestApiPost(lua_State *state) + { + return RestApiPostOrPut(state, true); + } + + + // Syntax in Lua: RestApiPut(uri, body, builtin) + int LuaScripting::RestApiPut(lua_State *state) + { + return RestApiPostOrPut(state, false); + } + + + // Syntax in Lua: RestApiDelete(uri, builtin) + int LuaScripting::RestApiDelete(lua_State *state) + { + ServerContext* serverContext = GetServerContext(state); + if (serverContext == NULL) + { + LOG(ERROR) << "Lua: The Orthanc API is unavailable"; + lua_pushnil(state); + return 1; + } + + // Check the types of the arguments + int nArgs = lua_gettop(state); + if ((nArgs != 1 && nArgs != 2) || + !lua_isstring(state, 1) || // URI + (nArgs == 2 && !lua_isboolean(state, 2))) // Restrict to built-in API? + { + LOG(ERROR) << "Lua: Bad parameters to RestApiGet()"; + lua_pushnil(state); + return 1; + } + + const char* uri = lua_tostring(state, 1); + bool builtin = (nArgs == 2 ? lua_toboolean(state, 2) : false); + + if (HttpToolbox::SimpleDelete(serverContext->GetHttpHandler().RestrictToOrthancRestApi(builtin), uri)) + { + lua_pushboolean(state, 1); + } + else + { + LOG(ERROR) << "Lua: Error in RestApiGet() for URI: " << uri; lua_pushnil(state); } @@ -251,6 +352,9 @@ { lua_.SetGlobalVariable("_ServerContext", &context); lua_.RegisterFunction("RestApiGet", RestApiGet); + lua_.RegisterFunction("RestApiPost", RestApiPost); + lua_.RegisterFunction("RestApiPut", RestApiPut); + lua_.RegisterFunction("RestApiDelete", RestApiDelete); lua_.Execute(Orthanc::EmbeddedResources::LUA_TOOLBOX); lua_.SetHttpProxy(Configuration::GetGlobalStringParameter("HttpProxy", "")); @@ -329,8 +433,9 @@ } - Json::Value tags; - if (context_.GetIndex().LookupResource(tags, change.GetPublicId(), change.GetResourceType())) + Json::Value tags, metadata; + if (context_.GetIndex().LookupResource(tags, change.GetPublicId(), change.GetResourceType()) && + context_.GetIndex().GetMetadata(metadata, change.GetPublicId())) { boost::mutex::scoped_lock lock(mutex_); @@ -341,6 +446,7 @@ LuaFunctionCall call(lua_, name); call.PushString(change.GetPublicId()); call.PushJson(tags["MainDicomTags"]); + call.PushJson(metadata); call.Execute(); SubmitJob(std::string("Lua script: ") + name); diff -r 8dc80ba768aa -r 5ba7471780ae OrthancServer/LuaScripting.h --- a/OrthancServer/LuaScripting.h Wed Jul 01 13:16:12 2015 +0200 +++ b/OrthancServer/LuaScripting.h Wed Jul 01 17:42:06 2015 +0200 @@ -45,7 +45,12 @@ private: static ServerContext* GetServerContext(lua_State *state); + static int RestApiPostOrPut(lua_State *state, + bool isPost); static int RestApiGet(lua_State *state); + static int RestApiPost(lua_State *state); + static int RestApiPut(lua_State *state); + static int RestApiDelete(lua_State *state); void ApplyOnStoredInstance(const std::string& instanceId, const Json::Value& simplifiedDicom, diff -r 8dc80ba768aa -r 5ba7471780ae OrthancServer/ServerContext.cpp --- a/OrthancServer/ServerContext.cpp Wed Jul 01 13:16:12 2015 +0200 +++ b/OrthancServer/ServerContext.cpp Wed Jul 01 17:42:06 2015 +0200 @@ -71,7 +71,7 @@ { while (!that->done_) { - std::auto_ptr obj(that->pendingChanges_.Dequeue(500)); + std::auto_ptr obj(that->pendingChanges_.Dequeue(100)); if (obj.get() != NULL) { @@ -119,11 +119,20 @@ ServerContext::~ServerContext() { - done_ = true; + Stop(); + } + - if (changeThread_.joinable()) + void ServerContext::Stop() + { + if (!done_) { - changeThread_.join(); + done_ = true; + + if (changeThread_.joinable()) + { + changeThread_.join(); + } } } diff -r 8dc80ba768aa -r 5ba7471780ae OrthancServer/ServerContext.h --- a/OrthancServer/ServerContext.h Wed Jul 01 13:16:12 2015 +0200 +++ b/OrthancServer/ServerContext.h Wed Jul 01 17:42:06 2015 +0200 @@ -237,6 +237,8 @@ return httpHandler_; } + void Stop(); + /** * Management of the plugins diff -r 8dc80ba768aa -r 5ba7471780ae OrthancServer/main.cpp --- a/OrthancServer/main.cpp Wed Jul 01 13:16:12 2015 +0200 +++ b/OrthancServer/main.cpp Wed Jul 01 17:42:06 2015 +0200 @@ -545,6 +545,8 @@ // We're done LOG(WARNING) << "Orthanc is stopping"; + context->Stop(); + #if ENABLE_PLUGINS == 1 context->ResetPlugins(); plugins.ResetOrthancRestApi(); diff -r 8dc80ba768aa -r 5ba7471780ae Plugins/Engine/OrthancPlugins.cpp --- a/Plugins/Engine/OrthancPlugins.cpp Wed Jul 01 13:16:12 2015 +0200 +++ b/Plugins/Engine/OrthancPlugins.cpp Wed Jul 01 17:42:06 2015 +0200 @@ -642,38 +642,17 @@ const _OrthancPluginRestApiPostPut& p = *reinterpret_cast(parameters); - // TODO : Use "HttpToolbox::SimplePost()" - - IHttpHandler::Arguments headers; // No HTTP header - IHttpHandler::GetArguments getArguments; // No GET argument for POST/PUT + LOG(INFO) << "Plugin making REST " << EnumerationToString(isPost ? HttpMethod_Post : HttpMethod_Put) + << " call on URI " << p.uri << (afterPlugins ? " (after plugins)" : " (built-in API)"); - UriComponents uri; - Toolbox::SplitUriComponents(uri, p.uri); - - StringHttpOutput stream; - HttpOutput http(stream, false /* no keep alive */); - - HttpMethod method = (isPost ? HttpMethod_Post : HttpMethod_Put); - LOG(INFO) << "Plugin making REST " << EnumerationToString(method) << " call on URI " << p.uri - << (afterPlugins ? " (after plugins)" : " (built-in API)"); + CheckContextAvailable(); + IHttpHandler& handler = pimpl_->context_->GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins); - bool ok = false; std::string result; - - if (afterPlugins) + if (isPost ? + HttpToolbox::SimplePost(result, handler, p.uri, p.body, p.bodySize) : + HttpToolbox::SimplePut (result, handler, p.uri, p.body, p.bodySize)) { - ok = Handle(http, method, uri, headers, getArguments, p.body, p.bodySize); - } - - if (!ok) - { - ok = (pimpl_->restApi_ != NULL && - pimpl_->restApi_->Handle(http, method, uri, headers, getArguments, p.body, p.bodySize)); - } - - if (ok) - { - stream.GetOutput(result); CopyToMemoryBuffer(*p.target, result); } else @@ -686,38 +665,14 @@ void OrthancPlugins::RestApiDelete(const void* parameters, bool afterPlugins) { - // The "parameters" point to the URI - UriComponents uri; - Toolbox::SplitUriComponents(uri, reinterpret_cast(parameters)); - - // TODO : Use "HttpToolbox::SimpleDelete()" - - IHttpHandler::Arguments headers; // No HTTP header - IHttpHandler::GetArguments getArguments; // No GET argument for POST/PUT - - StringHttpOutput stream; - HttpOutput http(stream, false /* no keep alive */); - - LOG(INFO) << "Plugin making REST DELETE call on URI " - << reinterpret_cast(parameters) + const char* uri = reinterpret_cast(parameters); + LOG(INFO) << "Plugin making REST DELETE call on URI " << uri << (afterPlugins ? " (after plugins)" : " (built-in API)"); - bool ok = false; - - if (afterPlugins) - { - ok = Handle(http, HttpMethod_Delete, uri, headers, getArguments, - NULL /* no body for DELETE */, 0); - } + CheckContextAvailable(); + IHttpHandler& handler = pimpl_->context_->GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins); - if (!ok) - { - ok = (pimpl_->restApi_ != NULL && - pimpl_->restApi_->Handle(http, HttpMethod_Delete, uri, headers, getArguments, - NULL /* no body for DELETE */, 0)); - } - - if (!ok) + if (!HttpToolbox::SimpleDelete(handler, uri)) { throw OrthancException(ErrorCode_BadRequest); }