# HG changeset patch # User Sebastien Jodogne # Date 1435682493 -7200 # Node ID 02f5a3f5c0a085ea941efe770a501c5db203f99c # Parent 0a3e3be590946b34430eb2f669bb7226bce0e1d6 access to the REST API from Lua diff -r 0a3e3be59094 -r 02f5a3f5c0a0 CMakeLists.txt --- 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 diff -r 0a3e3be59094 -r 02f5a3f5c0a0 Core/HttpServer/HttpHandler.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 #include +#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; + } + } + } diff -r 0a3e3be59094 -r 02f5a3f5c0a0 Core/HttpServer/HttpHandler.h --- 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); }; } diff -r 0a3e3be59094 -r 02f5a3f5c0a0 Core/HttpServer/StringHttpOutput.cpp --- /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 . + **/ + + +#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(buffer), length); + } + } +} diff -r 0a3e3be59094 -r 02f5a3f5c0a0 Core/HttpServer/StringHttpOutput.h --- /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 . + **/ + + +#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); + } + }; +} diff -r 0a3e3be59094 -r 02f5a3f5c0a0 Core/Lua/LuaContext.cpp --- 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(reinterpret_cast(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(reinterpret_cast(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; + } } diff -r 0a3e3be59094 -r 02f5a3f5c0a0 Core/Lua/LuaContext.h --- 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); }; } diff -r 0a3e3be59094 -r 02f5a3f5c0a0 OrthancServer/LuaScripting.cpp --- 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(reinterpret_cast(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, diff -r 0a3e3be59094 -r 02f5a3f5c0a0 OrthancServer/LuaScripting.h --- 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, diff -r 0a3e3be59094 -r 02f5a3f5c0a0 OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp --- 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 diff -r 0a3e3be59094 -r 02f5a3f5c0a0 OrthancServer/OrthancRestApi/OrthancRestApi.cpp --- 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 @@ -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(); + } } diff -r 0a3e3be59094 -r 02f5a3f5c0a0 OrthancServer/OrthancRestApi/OrthancRestApi.h --- 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 namespace Orthanc { + class ServerContext; + class ServerIndex; + class OrthancRestApi : public RestApi { public: @@ -76,15 +78,9 @@ return dynamic_cast(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, diff -r 0a3e3be59094 -r 02f5a3f5c0a0 OrthancServer/OrthancRestApi/OrthancRestArchive.cpp --- 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 #include diff -r 0a3e3be59094 -r 02f5a3f5c0a0 OrthancServer/OrthancRestApi/OrthancRestChanges.cpp --- 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 namespace Orthanc diff -r 0a3e3be59094 -r 02f5a3f5c0a0 OrthancServer/OrthancRestApi/OrthancRestResources.cpp --- 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 diff -r 0a3e3be59094 -r 02f5a3f5c0a0 OrthancServer/OrthancRestApi/OrthancRestSystem.cpp --- 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 diff -r 0a3e3be59094 -r 02f5a3f5c0a0 OrthancServer/main.cpp --- 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"; diff -r 0a3e3be59094 -r 02f5a3f5c0a0 Plugins/Engine/OrthancPlugins.cpp --- 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(buffer), length); - } - } - }; - } - - struct OrthancPlugins::PImpl { @@ -649,6 +616,8 @@ const _OrthancPluginRestApiGet& p = *reinterpret_cast(parameters); + // TODO : Use "HttpHandler::SimpleGet()" + HttpHandler::Arguments headers; // No HTTP header std::string body; // No body for a GET request