Mercurial > hg > orthanc
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, ¶ms); + } + + #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;