Mercurial > hg > orthanc
changeset 3414:b9cba6a91780
simplification
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 11 Jun 2019 19:44:10 +0200 |
parents | f09bfdea3fc3 |
children | 2a821deece64 |
files | Core/HttpServer/EmbeddedResourceHttpHandler.h Core/HttpServer/FilesystemHttpHandler.h Core/HttpServer/HttpServer.cpp Core/HttpServer/IHttpHandler.h Core/RestApi/RestApi.h OrthancServer/OrthancHttpHandler.cpp OrthancServer/OrthancHttpHandler.h Plugins/Engine/OrthancPlugins.cpp Plugins/Engine/OrthancPlugins.h Plugins/Include/orthanc/OrthancCPlugin.h Plugins/Samples/Common/OrthancPluginCppWrapper.cpp Plugins/Samples/Common/OrthancPluginCppWrapper.h |
diffstat | 12 files changed, 660 insertions(+), 727 deletions(-) [+] |
line wrap: on
line diff
--- a/Core/HttpServer/EmbeddedResourceHttpHandler.h Tue Jun 11 14:02:57 2019 +0200 +++ b/Core/HttpServer/EmbeddedResourceHttpHandler.h Tue Jun 11 19:44:10 2019 +0200 @@ -51,26 +51,26 @@ const std::string& baseUri, EmbeddedResources::DirectoryResourceId resourceId); - virtual IStream* CreateStreamHandler(RequestOrigin origin, - const char* remoteIp, - const char* username, - HttpMethod method, - const UriComponents& uri, - const Arguments& headers) + virtual bool CreateChunkedRequestReader(std::auto_ptr<IChunkedRequestReader>& target, + RequestOrigin origin, + const char* remoteIp, + const char* username, + HttpMethod method, + const UriComponents& uri, + const Arguments& headers) { - return NULL; + return false; } - virtual bool Handle( - HttpOutput& output, - RequestOrigin origin, - const char* remoteIp, - const char* username, - HttpMethod method, - const UriComponents& uri, - const Arguments& headers, - const GetArguments& arguments, - const void* /*bodyData*/, - size_t /*bodySize*/); + virtual bool Handle(HttpOutput& output, + RequestOrigin origin, + const char* remoteIp, + const char* username, + HttpMethod method, + const UriComponents& uri, + const Arguments& headers, + const GetArguments& arguments, + const void* /*bodyData*/, + size_t /*bodySize*/); }; }
--- a/Core/HttpServer/FilesystemHttpHandler.h Tue Jun 11 14:02:57 2019 +0200 +++ b/Core/HttpServer/FilesystemHttpHandler.h Tue Jun 11 19:44:10 2019 +0200 @@ -53,14 +53,15 @@ FilesystemHttpHandler(const std::string& baseUri, const std::string& root); - virtual IStream* CreateStreamHandler(RequestOrigin origin, - const char* remoteIp, - const char* username, - HttpMethod method, - const UriComponents& uri, - const Arguments& headers) + virtual bool CreateChunkedRequestReader(std::auto_ptr<IChunkedRequestReader>& target, + RequestOrigin origin, + const char* remoteIp, + const char* username, + HttpMethod method, + const UriComponents& uri, + const Arguments& headers) { - return NULL; + return false; } virtual bool Handle(
--- a/Core/HttpServer/HttpServer.cpp Tue Jun 11 14:02:57 2019 +0200 +++ b/Core/HttpServer/HttpServer.cpp Tue Jun 11 19:44:10 2019 +0200 @@ -384,7 +384,7 @@ } - static PostDataStatus ReadBodyToStream(IHttpHandler::IStream& stream, + static PostDataStatus ReadBodyToStream(IHttpHandler::IChunkedRequestReader& stream, struct mg_connection *connection, const IHttpHandler::Arguments& headers) { @@ -847,16 +847,21 @@ if (!isMultipartForm) { - std::auto_ptr<IHttpHandler::IStream> stream; + std::auto_ptr<IHttpHandler::IChunkedRequestReader> stream; if (server.HasHandler()) { - stream.reset(server.GetHandler().CreateStreamHandler - (RequestOrigin_RestApi, remoteIp, username.c_str(), method, uri, headers)); + found = server.GetHandler().CreateChunkedRequestReader + (stream, RequestOrigin_RestApi, remoteIp, username.c_str(), method, uri, headers); } + + if (found) + { + if (stream.get() == NULL) + { + throw OrthancException(ErrorCode_InternalError); + } - if (stream.get() != NULL) - { status = ReadBodyToStream(*stream, connection, headers); if (status == PostDataStatus_Success)
--- a/Core/HttpServer/IHttpHandler.h Tue Jun 11 14:02:57 2019 +0200 +++ b/Core/HttpServer/IHttpHandler.h Tue Jun 11 19:44:10 2019 +0200 @@ -40,6 +40,7 @@ #include <set> #include <vector> #include <string> +#include <memory> namespace Orthanc { @@ -50,10 +51,10 @@ typedef std::vector< std::pair<std::string, std::string> > GetArguments; - class IStream : public boost::noncopyable + class IChunkedRequestReader : public boost::noncopyable { public: - virtual ~IStream() + virtual ~IChunkedRequestReader() { } @@ -68,14 +69,17 @@ { } - // This function is called for each incomding POST/PUT request - // (new in Orthanc 1.5.7). It allows to deal with chunked transfers. - virtual IStream* CreateStreamHandler(RequestOrigin origin, - const char* remoteIp, - const char* username, - HttpMethod method, - const UriComponents& uri, - const Arguments& headers) = 0; + /** + * This function allows to deal with chunked transfers (new in + * Orthanc 1.5.7). It is only called if "method" is POST or PUT. + **/ + virtual bool CreateChunkedRequestReader(std::auto_ptr<IChunkedRequestReader>& target, + RequestOrigin origin, + const char* remoteIp, + const char* username, + HttpMethod method, + const UriComponents& uri, + const Arguments& headers) = 0; virtual bool Handle(HttpOutput& output, RequestOrigin origin,
--- a/Core/RestApi/RestApi.h Tue Jun 11 14:02:57 2019 +0200 +++ b/Core/RestApi/RestApi.h Tue Jun 11 19:44:10 2019 +0200 @@ -47,14 +47,15 @@ public: static void AutoListChildren(RestApiGetCall& call); - virtual IStream* CreateStreamHandler(RequestOrigin origin, - const char* remoteIp, - const char* username, - HttpMethod method, - const UriComponents& uri, - const Arguments& headers) + virtual bool CreateChunkedRequestReader(std::auto_ptr<IChunkedRequestReader>& target, + RequestOrigin origin, + const char* remoteIp, + const char* username, + HttpMethod method, + const UriComponents& uri, + const Arguments& headers) { - return NULL; + return false; } virtual bool Handle(HttpOutput& output,
--- a/OrthancServer/OrthancHttpHandler.cpp Tue Jun 11 14:02:57 2019 +0200 +++ b/OrthancServer/OrthancHttpHandler.cpp Tue Jun 11 19:44:10 2019 +0200 @@ -39,25 +39,36 @@ namespace Orthanc { - IHttpHandler::IStream* OrthancHttpHandler::CreateStreamHandler(RequestOrigin origin, - const char* remoteIp, - const char* username, - HttpMethod method, - const UriComponents& uri, - const Arguments& headers) + bool OrthancHttpHandler::CreateChunkedRequestReader( + std::auto_ptr<IHttpHandler::IChunkedRequestReader>& target, + RequestOrigin origin, + const char* remoteIp, + const char* username, + HttpMethod method, + const UriComponents& uri, + const Arguments& headers) { + if (method != HttpMethod_Post && + method != HttpMethod_Put) + { + throw OrthancException(ErrorCode_InternalError); + } + for (Handlers::const_iterator it = handlers_.begin(); it != handlers_.end(); ++it) { - IHttpHandler::IStream* stream = (*it)->CreateStreamHandler( - origin, remoteIp, username, method, uri, headers); + if ((*it)->CreateChunkedRequestReader + (target, origin, remoteIp, username, method, uri, headers)) + { + if (target.get() == NULL) + { + throw OrthancException(ErrorCode_InternalError); + } - if (stream != NULL) - { - return stream; + return true; } } - return NULL; + return false; }
--- a/OrthancServer/OrthancHttpHandler.h Tue Jun 11 14:02:57 2019 +0200 +++ b/OrthancServer/OrthancHttpHandler.h Tue Jun 11 19:44:10 2019 +0200 @@ -50,12 +50,13 @@ { } - virtual IStream* CreateStreamHandler(RequestOrigin origin, - const char* remoteIp, - const char* username, - HttpMethod method, - const UriComponents& uri, - const Arguments& headers); + virtual bool CreateChunkedRequestReader(std::auto_ptr<IChunkedRequestReader>& target, + RequestOrigin origin, + const char* remoteIp, + const char* username, + HttpMethod method, + const UriComponents& uri, + const Arguments& headers); virtual bool Handle(HttpOutput& output, RequestOrigin origin,
--- a/Plugins/Engine/OrthancPlugins.cpp Tue Jun 11 14:02:57 2019 +0200 +++ b/Plugins/Engine/OrthancPlugins.cpp Tue Jun 11 19:44:10 2019 +0200 @@ -51,7 +51,6 @@ #include "../../Core/DicomParsing/Internals/DicomImageDecoder.h" #include "../../Core/DicomParsing/ToDcmtkBridge.h" #include "../../Core/HttpServer/HttpToolbox.h" -#include "../../Core/HttpServer/MultipartStreamReader.h" #include "../../Core/Images/Image.h" #include "../../Core/Images/ImageProcessing.h" #include "../../Core/Images/JpegReader.h" @@ -76,12 +75,19 @@ #include <dcmtk/dcmdata/dcdict.h> #include <dcmtk/dcmdata/dcdicent.h> +#define ERROR_MESSAGE_64BIT "A 64bit version of the Orthanc API is necessary" + namespace Orthanc { static void CopyToMemoryBuffer(OrthancPluginMemoryBuffer& target, const void* data, size_t size) { + if (static_cast<uint32_t>(size) != size) + { + throw OrthancException(ErrorCode_NotEnoughMemory, ERROR_MESSAGE_64BIT); + } + target.size = size; if (size == 0) @@ -120,6 +126,11 @@ static char* CopyString(const std::string& str) { + if (static_cast<uint32_t>(str.size()) != str.size()) + { + throw OrthancException(ErrorCode_NotEnoughMemory, ERROR_MESSAGE_64BIT); + } + char *result = reinterpret_cast<char*>(malloc(str.size() + 1)); if (result == NULL) { @@ -539,16 +550,16 @@ }; - class MultipartRestCallback : public boost::noncopyable + class ChunkedRestCallback : public boost::noncopyable { private: - _OrthancPluginMultipartRestCallback parameters_; + _OrthancPluginChunkedRestCallback parameters_; boost::regex regex_; public: - MultipartRestCallback(_OrthancPluginMultipartRestCallback parameters) : - parameters_(parameters), - regex_(parameters.pathRegularExpression) + ChunkedRestCallback(_OrthancPluginChunkedRestCallback parameters) : + parameters_(parameters), + regex_(parameters.pathRegularExpression) { } @@ -557,7 +568,7 @@ return regex_; } - const _OrthancPluginMultipartRestCallback& GetParameters() const + const _OrthancPluginChunkedRestCallback& GetParameters() const { return parameters_; } @@ -598,7 +609,7 @@ typedef std::pair<std::string, _OrthancPluginProperty> Property; typedef std::list<RestCallback*> RestCallbacks; - typedef std::list<MultipartRestCallback*> MultipartRestCallbacks; + typedef std::list<ChunkedRestCallback*> ChunkedRestCallbacks; typedef std::list<OrthancPluginOnStoredInstanceCallback> OnStoredCallbacks; typedef std::list<OrthancPluginOnChangeCallback> OnChangeCallbacks; typedef std::list<OrthancPluginIncomingHttpRequestFilter> IncomingHttpRequestFilters; @@ -611,7 +622,7 @@ PluginsManager manager_; RestCallbacks restCallbacks_; - MultipartRestCallbacks multipartRestCallbacks_; + ChunkedRestCallbacks chunkedRestCallbacks_; OnStoredCallbacks onStoredCallbacks_; OnChangeCallbacks onChangeCallbacks_; OrthancPluginFindCallback findCallback_; @@ -1030,15 +1041,15 @@ - class OrthancPlugins::ChunkedHttpRequest : public HttpClient::IRequestBody + class OrthancPlugins::HttpClientChunkedRequest : public HttpClient::IRequestBody { private: const _OrthancPluginChunkedHttpClient& params_; PluginsErrorDictionary& errorDictionary_; public: - ChunkedHttpRequest(const _OrthancPluginChunkedHttpClient& params, - PluginsErrorDictionary& errorDictionary) : + HttpClientChunkedRequest(const _OrthancPluginChunkedHttpClient& params, + PluginsErrorDictionary& errorDictionary) : params_(params), errorDictionary_(errorDictionary) { @@ -1078,15 +1089,15 @@ }; - class OrthancPlugins::ChunkedHttpAnswer : public HttpClient::IAnswer + class OrthancPlugins::HttpClientChunkedAnswer : public HttpClient::IAnswer { private: const _OrthancPluginChunkedHttpClient& params_; PluginsErrorDictionary& errorDictionary_; public: - ChunkedHttpAnswer(const _OrthancPluginChunkedHttpClient& params, - PluginsErrorDictionary& errorDictionary) : + HttpClientChunkedAnswer(const _OrthancPluginChunkedHttpClient& params, + PluginsErrorDictionary& errorDictionary) : params_(params), errorDictionary_(errorDictionary) { @@ -1180,8 +1191,8 @@ delete *it; } - for (PImpl::MultipartRestCallbacks::iterator it = pimpl_->multipartRestCallbacks_.begin(); - it != pimpl_->multipartRestCallbacks_.end(); ++it) + for (PImpl::ChunkedRestCallbacks::iterator it = pimpl_->chunkedRestCallbacks_.begin(); + it != pimpl_->chunkedRestCallbacks_.end(); ++it) { delete *it; } @@ -1232,7 +1243,7 @@ public: RestCallbackMatcher(const UriComponents& uri) : - flatUri_(Toolbox::FlattenUri(uri)) + flatUri_(Toolbox::FlattenUri(uri)) { } @@ -1280,6 +1291,133 @@ return flatUri_; } }; + + + // WARNING - The lifetime of this internal object must be smaller + // than "matcher", "headers" and "getArguments" objects + class HttpRequestConverter + { + private: + std::vector<const char*> getKeys_; + std::vector<const char*> getValues_; + std::vector<const char*> headersKeys_; + std::vector<const char*> headersValues_; + OrthancPluginHttpRequest converted_; + + public: + HttpRequestConverter(const RestCallbackMatcher& matcher, + HttpMethod method, + const IHttpHandler::Arguments& headers) + { + memset(&converted_, 0, sizeof(OrthancPluginHttpRequest)); + + ArgumentsToPlugin(headersKeys_, headersValues_, headers); + assert(headersKeys_.size() == headersValues_.size()); + + switch (method) + { + case HttpMethod_Get: + converted_.method = OrthancPluginHttpMethod_Get; + break; + + case HttpMethod_Post: + converted_.method = OrthancPluginHttpMethod_Post; + break; + + case HttpMethod_Delete: + converted_.method = OrthancPluginHttpMethod_Delete; + break; + + case HttpMethod_Put: + converted_.method = OrthancPluginHttpMethod_Put; + break; + + default: + throw OrthancException(ErrorCode_InternalError); + } + + converted_.groups = matcher.GetGroups(); + converted_.groupsCount = matcher.GetGroupsCount(); + converted_.getCount = 0; + converted_.getKeys = NULL; + converted_.getValues = NULL; + converted_.body = NULL; + converted_.bodySize = 0; + converted_.headersCount = headers.size(); + + if (headers.size() > 0) + { + converted_.headersKeys = &headersKeys_[0]; + converted_.headersValues = &headersValues_[0]; + } + } + + void SetGetArguments(const IHttpHandler::GetArguments& getArguments) + { + ArgumentsToPlugin(getKeys_, getValues_, getArguments); + assert(getKeys_.size() == getValues_.size()); + + converted_.getCount = getArguments.size(); + + if (getArguments.size() > 0) + { + converted_.getKeys = &getKeys_[0]; + converted_.getValues = &getValues_[0]; + } + } + + OrthancPluginHttpRequest& GetRequest() + { + return converted_; + } + }; + } + + + bool OrthancPlugins::HandleChunkedGetDelete(HttpOutput& output, + HttpMethod method, + const UriComponents& uri, + const Arguments& headers, + const GetArguments& getArguments) + { + if (method == HttpMethod_Get || + method == HttpMethod_Delete) + { + RestCallbackMatcher matcher(uri); + + PImpl::ChunkedRestCallback* callback = NULL; + + // Loop over the callbacks registered by the plugins + for (PImpl::ChunkedRestCallbacks::const_iterator it = pimpl_->chunkedRestCallbacks_.begin(); + it != pimpl_->chunkedRestCallbacks_.end(); ++it) + { + if (matcher.IsMatch((*it)->GetRegularExpression())) + { + callback = *it; + break; + } + } + + if (callback != NULL) + { + LOG(INFO) << "Delegating HTTP request to plugin for URI: " << matcher.GetFlatUri(); + + HttpRequestConverter converter(matcher, method, headers); + converter.SetGetArguments(getArguments); + + PImpl::PluginHttpOutput pluginOutput(output); + + assert(callback != NULL); + OrthancPluginErrorCode error = callback->GetParameters().handler + (reinterpret_cast<OrthancPluginRestOutput*>(&pluginOutput), + NULL /* no reader */, matcher.GetFlatUri().c_str(), &converter.GetRequest()); + + pluginOutput.Close(error, GetErrorDictionary()); + return true; + } + } + + return false; } @@ -1311,69 +1449,22 @@ if (callback == NULL) { - // Callback not found - return false; + // Callback not found, try to find a chunked callback + return HandleChunkedGetDelete(output, method, uri, headers, getArguments); } LOG(INFO) << "Delegating HTTP request to plugin for URI: " << matcher.GetFlatUri(); - std::vector<const char*> getKeys, getValues, headersKeys, headersValues; - - OrthancPluginHttpRequest request; - memset(&request, 0, sizeof(OrthancPluginHttpRequest)); - - ArgumentsToPlugin(headersKeys, headersValues, headers); - assert(headersKeys.size() == headersValues.size()); - - switch (method) - { - case HttpMethod_Get: - request.method = OrthancPluginHttpMethod_Get; - ArgumentsToPlugin(getKeys, getValues, getArguments); - assert(getKeys.size() == getValues.size()); - break; - - case HttpMethod_Post: - request.method = OrthancPluginHttpMethod_Post; - break; - - case HttpMethod_Delete: - request.method = OrthancPluginHttpMethod_Delete; - break; - - case HttpMethod_Put: - request.method = OrthancPluginHttpMethod_Put; - break; - - default: - throw OrthancException(ErrorCode_InternalError); - } - - request.groups = matcher.GetGroups(); - request.groupsCount = matcher.GetGroupsCount(); - request.getCount = getArguments.size(); - request.body = bodyData; - request.bodySize = bodySize; - request.headersCount = headers.size(); - - if (getArguments.size() > 0) - { - request.getKeys = &getKeys[0]; - request.getValues = &getValues[0]; - } - - if (headers.size() > 0) - { - request.headersKeys = &headersKeys[0]; - request.headersValues = &headersValues[0]; - } + HttpRequestConverter converter(matcher, method, headers); + converter.SetGetArguments(getArguments); + converter.GetRequest().body = bodyData; + converter.GetRequest().bodySize = bodySize; + + PImpl::PluginHttpOutput pluginOutput(output); assert(callback != NULL); - - PImpl::PluginHttpOutput pluginOutput(output); - OrthancPluginErrorCode error = callback->Invoke - (pimpl_->restCallbackMutex_, pluginOutput, matcher.GetFlatUri(), request); + (pimpl_->restCallbackMutex_, pluginOutput, matcher.GetFlatUri(), converter.GetRequest()); pluginOutput.Close(error, GetErrorDictionary()); return true; @@ -1450,15 +1541,15 @@ } - void OrthancPlugins::RegisterMultipartRestCallback(const void* parameters) + void OrthancPlugins::RegisterChunkedRestCallback(const void* parameters) { - const _OrthancPluginMultipartRestCallback& p = - *reinterpret_cast<const _OrthancPluginMultipartRestCallback*>(parameters); - - LOG(INFO) << "Plugin has registered a REST callback for multipart streams on: " + const _OrthancPluginChunkedRestCallback& p = + *reinterpret_cast<const _OrthancPluginChunkedRestCallback*>(parameters); + + LOG(INFO) << "Plugin has registered a REST callback for chunked streams on: " << p.pathRegularExpression; - pimpl_->multipartRestCallbacks_.push_back(new PImpl::MultipartRestCallback(p)); + pimpl_->chunkedRestCallbacks_.push_back(new PImpl::ChunkedRestCallback(p)); } @@ -2432,10 +2523,10 @@ SetupHttpClient(client, converted); } - ChunkedHttpRequest body(p, pimpl_->dictionary_); + HttpClientChunkedRequest body(p, pimpl_->dictionary_); client.SetBody(body); - ChunkedHttpAnswer answer(p, pimpl_->dictionary_); + HttpClientChunkedAnswer answer(p, pimpl_->dictionary_); bool success = client.Apply(answer); @@ -3546,8 +3637,8 @@ RegisterRestCallback(parameters, false); return true; - case _OrthancPluginService_RegisterMultipartRestCallback: - RegisterMultipartRestCallback(parameters); + case _OrthancPluginService_RegisterChunkedRestCallback: + RegisterChunkedRestCallback(parameters); return true; case _OrthancPluginService_RegisterOnStoredInstanceCallback: @@ -4110,170 +4201,115 @@ } - class OrthancPlugins::MultipartStream : - public IHttpHandler::IStream, - private MultipartStreamReader::IHandler + class OrthancPlugins::HttpServerChunkedReader : public IHttpHandler::IChunkedRequestReader { private: - OrthancPluginMultipartRestHandler* handler_; - _OrthancPluginMultipartRestCallback parameters_; - MultipartStreamReader reader_; - PluginsErrorDictionary& errorDictionary_; - - virtual void HandlePart(const MultipartStreamReader::HttpHeaders& headers, - const void* part, - size_t size) - { - assert(handler_ != NULL); - - std::string contentType; - MultipartStreamReader::GetMainContentType(contentType, headers); - Orthanc::Toolbox::ToLowerCase(contentType); - - std::vector<const char*> headersKeys, headersValues; - ArgumentsToPlugin(headersKeys, headersValues, headers); - assert(headersKeys.size() == headersValues.size()); - - OrthancPluginErrorCode error = parameters_.addPart( - handler_, contentType.c_str(), headersKeys.size(), - headersKeys.empty() ? NULL : &headersKeys[0], - headersValues.empty() ? NULL : &headersValues[0], - part, size); - - if (error != OrthancPluginErrorCode_Success) - { - errorDictionary_.LogError(error, true); - throw OrthancException(static_cast<ErrorCode>(error)); - } - } + OrthancPluginServerChunkedRequestReader* reader_; + _OrthancPluginChunkedRestCallback parameters_; + PluginsErrorDictionary& errorDictionary_; public: - MultipartStream(OrthancPluginMultipartRestHandler* handler, - const _OrthancPluginMultipartRestCallback& parameters, - const std::string& boundary, - PluginsErrorDictionary& errorDictionary) : - handler_(handler), + HttpServerChunkedReader(OrthancPluginServerChunkedRequestReader* reader, + const _OrthancPluginChunkedRestCallback& parameters, + PluginsErrorDictionary& errorDictionary) : + reader_(reader), parameters_(parameters), - reader_(boundary), errorDictionary_(errorDictionary) { - if (handler_ == NULL) - { - throw OrthancException(ErrorCode_Plugin, "The plugin has not created a multipart stream handler"); - } - - reader_.SetHandler(*this); + assert(reader_ != NULL); } - virtual ~MultipartStream() + virtual ~HttpServerChunkedReader() { - if (handler_ != NULL) - { - parameters_.finalize(handler_); - } + assert(reader_ != NULL); + parameters_.finalize(reader_); } virtual void AddBodyChunk(const void* data, size_t size) { - reader_.AddChunk(data, size); - } + if (static_cast<uint32_t>(size) != size) + { + throw OrthancException(ErrorCode_NotEnoughMemory, ERROR_MESSAGE_64BIT); + } + + assert(reader_ != NULL); + parameters_.addChunk(reader_, data, size); + } virtual void Execute(HttpOutput& output) { - assert(handler_ != NULL); - - reader_.CloseStream(); + assert(reader_ != NULL); PImpl::PluginHttpOutput pluginOutput(output); OrthancPluginErrorCode error = parameters_.execute( - handler_, reinterpret_cast<OrthancPluginRestOutput*>(&pluginOutput)); + reader_, reinterpret_cast<OrthancPluginRestOutput*>(&pluginOutput)); pluginOutput.Close(error, errorDictionary_); } }; - IHttpHandler::IStream* OrthancPlugins::CreateStreamHandler(RequestOrigin origin, - const char* remoteIp, - const char* username, - HttpMethod method, - const UriComponents& uri, - const Arguments& headers) + bool OrthancPlugins::CreateChunkedRequestReader(std::auto_ptr<IChunkedRequestReader>& target, + RequestOrigin origin, + const char* remoteIp, + const char* username, + HttpMethod method, + const UriComponents& uri, + const Arguments& headers) { + if (method != HttpMethod_Post && + method != HttpMethod_Put) + { + throw OrthancException(ErrorCode_InternalError); + } + RestCallbackMatcher matcher(uri); + PImpl::ChunkedRestCallback* callback = NULL; + // Loop over the callbacks registered by the plugins - for (PImpl::MultipartRestCallbacks::const_iterator it = pimpl_->multipartRestCallbacks_.begin(); - it != pimpl_->multipartRestCallbacks_.end(); ++it) + for (PImpl::ChunkedRestCallbacks::const_iterator it = pimpl_->chunkedRestCallbacks_.begin(); + it != pimpl_->chunkedRestCallbacks_.end(); ++it) { if (matcher.IsMatch((*it)->GetRegularExpression())) { - LOG(INFO) << "Delegating HTTP multipart request to plugin for URI: " << matcher.GetFlatUri(); - - std::vector<const char*> headersKeys, headersValues; - ArgumentsToPlugin(headersKeys, headersValues, headers); - assert(headersKeys.size() == headersValues.size()); - - OrthancPluginHttpMethod convertedMethod; - switch (method) - { - case HttpMethod_Post: - convertedMethod = OrthancPluginHttpMethod_Post; - break; - - case HttpMethod_Put: - convertedMethod = OrthancPluginHttpMethod_Put; - break; - - default: - throw OrthancException(ErrorCode_ParameterOutOfRange); - } - - std::string mainContentType; - if (!MultipartStreamReader::GetMainContentType(mainContentType, headers)) - { - LOG(INFO) << "Missing Content-Type HTTP header, prevents streaming the body"; - continue; - } - - std::string contentType, subType, boundary; - if (!MultipartStreamReader::ParseMultipartContentType - (contentType, subType, boundary, mainContentType)) - { - LOG(INFO) << "Invalid Content-Type HTTP header, " - << "prevents streaming the body: \"" << mainContentType << "\""; - continue; - } - - OrthancPluginErrorCode errorCode = OrthancPluginErrorCode_Plugin; - - OrthancPluginMultipartRestHandler* handler = (*it)->GetParameters().createHandler( - (*it)->GetParameters().factory, &errorCode, - convertedMethod, matcher.GetFlatUri().c_str(), contentType.c_str(), subType.c_str(), - matcher.GetGroupsCount(), matcher.GetGroups(), headers.size(), - headers.empty() ? NULL : &headersKeys[0], - headers.empty() ? NULL : &headersValues[0]); - - if (handler == NULL) - { - if (errorCode == OrthancPluginErrorCode_Success) - { - // Ignore: The factory cannot create a handler for this request - } - else - { - throw OrthancException(static_cast<ErrorCode>(errorCode)); - } - } - else - { - return new MultipartStream(handler, (*it)->GetParameters(), boundary, GetErrorDictionary()); - } + callback = *it; + break; } } - return NULL; + if (callback == NULL) + { + // Callback not found + return false; + } + + LOG(INFO) << "Delegating chunked HTTP request to plugin for URI: " << matcher.GetFlatUri(); + + HttpRequestConverter converter(matcher, method, headers); + converter.GetRequest().body = NULL; + converter.GetRequest().bodySize = 0; + + OrthancPluginServerChunkedRequestReader* reader = NULL; + + OrthancPluginErrorCode errorCode = callback->GetParameters().handler( + NULL /* no HTTP output */, &reader, matcher.GetFlatUri().c_str(), &converter.GetRequest()); + + if (reader == NULL) + { + // The plugin has not created a reader for chunked body + return false; + } + else if (errorCode != OrthancPluginErrorCode_Success) + { + throw OrthancException(static_cast<ErrorCode>(errorCode)); + } + else + { + target.reset(new HttpServerChunkedReader(reader, callback->GetParameters(), GetErrorDictionary())); + return true; + } } }
--- a/Plugins/Engine/OrthancPlugins.h Tue Jun 11 14:02:57 2019 +0200 +++ b/Plugins/Engine/OrthancPlugins.h Tue Jun 11 19:44:10 2019 +0200 @@ -89,14 +89,20 @@ class WorklistHandler; class FindHandler; class MoveHandler; - class ChunkedHttpRequest; - class ChunkedHttpAnswer; - class MultipartStream; + class HttpClientChunkedRequest; + class HttpClientChunkedAnswer; + class HttpServerChunkedReader; void RegisterRestCallback(const void* parameters, bool lock); - void RegisterMultipartRestCallback(const void* parameters); + void RegisterChunkedRestCallback(const void* parameters); + + bool HandleChunkedGetDelete(HttpOutput& output, + HttpMethod method, + const UriComponents& uri, + const Arguments& headers, + const GetArguments& getArguments); void RegisterOnStoredInstanceCallback(const void* parameters); @@ -326,12 +332,13 @@ void RefreshMetrics(); // New in Orthanc 1.5.7 - virtual IStream* CreateStreamHandler(RequestOrigin origin, - const char* remoteIp, - const char* username, - HttpMethod method, - const UriComponents& uri, - const Arguments& headers); + virtual bool CreateChunkedRequestReader(std::auto_ptr<IChunkedRequestReader>& target, + RequestOrigin origin, + const char* remoteIp, + const char* username, + HttpMethod method, + const UriComponents& uri, + const Arguments& headers); }; }
--- a/Plugins/Include/orthanc/OrthancCPlugin.h Tue Jun 11 14:02:57 2019 +0200 +++ b/Plugins/Include/orthanc/OrthancCPlugin.h Tue Jun 11 19:44:10 2019 +0200 @@ -445,7 +445,7 @@ _OrthancPluginService_RegisterMoveCallback = 1009, _OrthancPluginService_RegisterIncomingHttpRequestFilter2 = 1010, _OrthancPluginService_RegisterRefreshMetricsCallback = 1011, - _OrthancPluginService_RegisterMultipartRestCallback = 1012, /* New in Orthanc 1.5.7 */ + _OrthancPluginService_RegisterChunkedRestCallback = 1012, /* New in Orthanc 1.5.7 */ /* Sending answers to REST calls */ _OrthancPluginService_AnswerBuffer = 2000, @@ -6810,72 +6810,72 @@ - typedef OrthancPluginErrorCode (*OrthancPluginChunkedHttpAnswerAddHeader) (void* answer, + typedef OrthancPluginErrorCode (*OrthancPluginChunkedClientAnswerAddHeader) (void* answer, const char* key, const char* value); - typedef OrthancPluginErrorCode (*OrthancPluginChunkedHttpAnswerAddChunk) (void* answer, + typedef OrthancPluginErrorCode (*OrthancPluginChunkedClientAnswerAddChunk) (void* answer, const void* data, uint32_t size); - typedef uint8_t (*OrthancPluginChunkedHttpRequestIsDone) (void* request); - - typedef OrthancPluginErrorCode (*OrthancPluginChunkedHttpRequestNext) (void* request); - - typedef const void* (*OrthancPluginChunkedHttpRequestGetChunkData) (void* request); - - typedef uint32_t (*OrthancPluginChunkedHttpRequestGetChunkSize) (void* request); + typedef uint8_t (*OrthancPluginChunkedClientRequestIsDone) (void* request); + + typedef OrthancPluginErrorCode (*OrthancPluginChunkedClientRequestNext) (void* request); + + typedef const void* (*OrthancPluginChunkedClientRequestGetChunkData) (void* request); + + typedef uint32_t (*OrthancPluginChunkedClientRequestGetChunkSize) (void* request); typedef struct { - void* answer; - OrthancPluginChunkedHttpAnswerAddChunk answerAddChunk; - OrthancPluginChunkedHttpAnswerAddHeader answerAddHeader; - uint16_t* httpStatus; - OrthancPluginHttpMethod method; - const char* url; - uint32_t headersCount; - const char* const* headersKeys; - const char* const* headersValues; - void* request; - OrthancPluginChunkedHttpRequestIsDone requestIsDone; - OrthancPluginChunkedHttpRequestGetChunkData requestChunkData; - OrthancPluginChunkedHttpRequestGetChunkSize requestChunkSize; - OrthancPluginChunkedHttpRequestNext requestNext; - const char* username; - const char* password; - uint32_t timeout; - const char* certificateFile; - const char* certificateKeyFile; - const char* certificateKeyPassword; - uint8_t pkcs11; + void* answer; + OrthancPluginChunkedClientAnswerAddChunk answerAddChunk; + OrthancPluginChunkedClientAnswerAddHeader answerAddHeader; + uint16_t* httpStatus; + OrthancPluginHttpMethod method; + const char* url; + uint32_t headersCount; + const char* const* headersKeys; + const char* const* headersValues; + void* request; + OrthancPluginChunkedClientRequestIsDone requestIsDone; + OrthancPluginChunkedClientRequestGetChunkData requestChunkData; + OrthancPluginChunkedClientRequestGetChunkSize requestChunkSize; + OrthancPluginChunkedClientRequestNext requestNext; + const char* username; + const char* password; + uint32_t timeout; + const char* certificateFile; + const char* certificateKeyFile; + const char* certificateKeyPassword; + uint8_t pkcs11; } _OrthancPluginChunkedHttpClient; ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginChunkedHttpClient( - OrthancPluginContext* context, - void* answer, - OrthancPluginChunkedHttpAnswerAddChunk answerAddChunk, - OrthancPluginChunkedHttpAnswerAddHeader answerAddHeader, - uint16_t* httpStatus, - OrthancPluginHttpMethod method, - const char* url, - uint32_t headersCount, - const char* const* headersKeys, - const char* const* headersValues, - void* request, - OrthancPluginChunkedHttpRequestIsDone requestIsDone, - OrthancPluginChunkedHttpRequestGetChunkData requestChunkData, - OrthancPluginChunkedHttpRequestGetChunkSize requestChunkSize, - OrthancPluginChunkedHttpRequestNext requestNext, - const char* username, - const char* password, - uint32_t timeout, - const char* certificateFile, - const char* certificateKeyFile, - const char* certificateKeyPassword, - uint8_t pkcs11) + OrthancPluginContext* context, + void* answer, + OrthancPluginChunkedClientAnswerAddChunk answerAddChunk, + OrthancPluginChunkedClientAnswerAddHeader answerAddHeader, + uint16_t* httpStatus, + OrthancPluginHttpMethod method, + const char* url, + uint32_t headersCount, + const char* const* headersKeys, + const char* const* headersValues, + void* request, + OrthancPluginChunkedClientRequestIsDone requestIsDone, + OrthancPluginChunkedClientRequestGetChunkData requestChunkData, + OrthancPluginChunkedClientRequestGetChunkSize requestChunkSize, + OrthancPluginChunkedClientRequestNext requestNext, + const char* username, + const char* password, + uint32_t timeout, + const char* certificateFile, + const char* certificateKeyFile, + const char* certificateKeyPassword, + uint8_t pkcs11) { _OrthancPluginChunkedHttpClient params; memset(¶ms, 0, sizeof(params)); @@ -6910,67 +6910,51 @@ - typedef struct _OrthancPluginMultipartRestHandler_t OrthancPluginMultipartRestHandler; - - typedef struct _OrthancPluginMultipartRestFactory_t OrthancPluginMultipartRestFactory; - - typedef OrthancPluginMultipartRestHandler* (*OrthancPluginMultipartRestCreateHandler) ( - OrthancPluginMultipartRestFactory* factory, - OrthancPluginErrorCode* errorCode, /* out: to report an exception on handler creation */ - OrthancPluginHttpMethod method, - const char* url, - const char* contentType, - const char* subType, - uint32_t groupsCount, - const char* const* groups, - uint32_t headersCount, - const char* const* headersKeys, - const char* const* headersValues); - - typedef OrthancPluginErrorCode (*OrthancPluginMultipartHandlerAddPart) ( - OrthancPluginMultipartRestHandler* handler, - const char* contentType, - uint32_t headersCount, - const char* const* headersKeys, - const char* const* headersValues, - const void* data, - uint32_t size); + typedef struct _OrthancPluginServerChunkedRequestReader_t OrthancPluginServerChunkedRequestReader; + + typedef OrthancPluginErrorCode (*OrthancPluginServerChunkedRequestHandler) ( + OrthancPluginRestOutput* output, /* out, for GET/DELETE only, NULL if POST/PUT */ + OrthancPluginServerChunkedRequestReader** reader, /* out, for POST/PUT only, NULL if GET/DELETE */ + const char* url, + const OrthancPluginHttpRequest* request); /* body and bodySize are not used */ + + typedef OrthancPluginErrorCode (*OrthancPluginServerChunkedRequestReaderAddChunk) ( + OrthancPluginServerChunkedRequestReader* reader, + const void* data, + uint32_t size); - typedef OrthancPluginErrorCode (*OrthancPluginMultipartHandlerExecute) ( - OrthancPluginMultipartRestHandler* handler, - OrthancPluginRestOutput* output); - - typedef void (*OrthancPluginMultipartHandlerFinalize) ( - OrthancPluginMultipartRestHandler* handler); + typedef OrthancPluginErrorCode (*OrthancPluginServerChunkedRequestReaderExecute) ( + OrthancPluginServerChunkedRequestReader* reader, + OrthancPluginRestOutput* output); + + typedef void (*OrthancPluginServerChunkedRequestReaderFinalize) ( + OrthancPluginServerChunkedRequestReader* reader); typedef struct { - const char* pathRegularExpression; - OrthancPluginMultipartRestFactory* factory; - OrthancPluginMultipartRestCreateHandler createHandler; - OrthancPluginMultipartHandlerAddPart addPart; - OrthancPluginMultipartHandlerExecute execute; - OrthancPluginMultipartHandlerFinalize finalize; - } _OrthancPluginMultipartRestCallback; - - ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterMultipartRestCallback( - OrthancPluginContext* context, - const char* pathRegularExpression, - OrthancPluginMultipartRestFactory* factory, - OrthancPluginMultipartRestCreateHandler createHandler, - OrthancPluginMultipartHandlerAddPart addPart, - OrthancPluginMultipartHandlerExecute execute, - OrthancPluginMultipartHandlerFinalize finalize) - { - _OrthancPluginMultipartRestCallback params; + const char* pathRegularExpression; + OrthancPluginServerChunkedRequestHandler handler; + OrthancPluginServerChunkedRequestReaderAddChunk addChunk; + OrthancPluginServerChunkedRequestReaderExecute execute; + OrthancPluginServerChunkedRequestReaderFinalize finalize; + } _OrthancPluginChunkedRestCallback; + + ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterChunkedRestCallback( + OrthancPluginContext* context, + const char* pathRegularExpression, + OrthancPluginServerChunkedRequestHandler handler, + OrthancPluginServerChunkedRequestReaderAddChunk addChunk, + OrthancPluginServerChunkedRequestReaderExecute execute, + OrthancPluginServerChunkedRequestReaderFinalize finalize) + { + _OrthancPluginChunkedRestCallback params; params.pathRegularExpression = pathRegularExpression; - params.factory = factory; - params.createHandler = createHandler; - params.addPart = addPart; + params.handler = handler; + params.addChunk = addChunk; params.execute = execute; params.finalize = finalize; - context->InvokeService(context, _OrthancPluginService_RegisterMultipartRestCallback, ¶ms); + context->InvokeService(context, _OrthancPluginService_RegisterChunkedRestCallback, ¶ms); } #ifdef __cplusplus
--- a/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp Tue Jun 11 14:02:57 2019 +0200 +++ b/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp Tue Jun 11 19:44:10 2019 +0200 @@ -33,17 +33,6 @@ #include "OrthancPluginCppWrapper.h" -#if HAS_ORTHANC_PLUGIN_HTTP_MULTIPART_SERVER == 0 -# if ORTHANC_FRAMEWORK_VERSION_IS_ABOVE(1, 5, 7) -# define HAS_ORTHANC_FRAMEWORK_MULTIPART_READER 1 -# include "../../../Core/HttpServer/MultipartStreamReader.h" -# include <boost/thread/shared_mutex.hpp> -# else -# define HAS_ORTHANC_FRAMEWORK_MULTIPART_READER 0 -# endif -#endif - - #include <boost/algorithm/string/predicate.hpp> #include <json/reader.h> #include <json/writer.h> @@ -2615,283 +2604,4 @@ } #endif /* HAS_ORTHANC_PLUGIN_HTTP_CLIENT == 1 */ - - - - - - /****************************************************************** - ** MULTIPART REST SERVER - ******************************************************************/ - -#if HAS_ORTHANC_PLUGIN_HTTP_MULTIPART_SERVER == 1 - static OrthancPluginMultipartRestHandler* MultipartRestFactory( - OrthancPluginMultipartRestFactory* factory, - OrthancPluginErrorCode* errorCode, - OrthancPluginHttpMethod method, - const char* url, - const char* contentType, - const char* subType, - uint32_t groupsCount, - const char* const* groups, - uint32_t headersCount, - const char* const* headersKeys, - const char* const* headersValues) - { - *errorCode = OrthancPluginErrorCode_Success; - - try - { - assert(factory != NULL); - MultipartRestCallback& that = *reinterpret_cast<MultipartRestCallback*>(factory); - - std::vector<std::string> g; - g.resize(groupsCount); - - for (uint32_t i = 0; i < groupsCount; i++) - { - g[i] = groups[i]; - } - - std::map<std::string, std::string> headers; - for (uint32_t i = 0; i < headersCount; i++) - { - headers[headersKeys[i]] = headersValues[i]; - } - - return reinterpret_cast<OrthancPluginMultipartRestHandler*>( - that.CreateHandler(method, url, contentType, - subType == NULL ? "" : subType, g, headers)); - } - catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) - { - LogError("Exception while creating a multipart handler"); - *errorCode = static_cast<OrthancPluginErrorCode>(e.GetErrorCode()); - return NULL; - } - catch (...) - { - LogError("Native exception while creating a multipart handler"); - *errorCode = OrthancPluginErrorCode_Plugin; - return NULL; - } - } - - static OrthancPluginErrorCode MultipartRestAddPart( - OrthancPluginMultipartRestHandler* handler, - const char* contentType, - uint32_t headersCount, - const char* const* headersKeys, - const char* const* headersValues, - const void* data, - uint32_t size) - { - try - { - assert(handler != NULL); - std::map<std::string, std::string> headers; - for (uint32_t i = 0; i < headersCount; i++) - { - headers[headersKeys[i]] = headersValues[i]; - } - - return reinterpret_cast<MultipartRestCallback::IHandler*>(handler)-> - AddPart(contentType, headers, data, size); - } - catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) - { - LogError("Exception while adding a part to a multipart handler"); - return static_cast<OrthancPluginErrorCode>(e.GetErrorCode()); - } - catch (...) - { - LogError("Native exception while adding a part to a multipart handler"); - return OrthancPluginErrorCode_Plugin; - } - } - - static OrthancPluginErrorCode MultipartRestExecute( - OrthancPluginMultipartRestHandler* handler, - OrthancPluginRestOutput* output) - { - try - { - assert(handler != NULL); - return reinterpret_cast<MultipartRestCallback::IHandler*>(handler)->Execute(output); - } - catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) - { - LogError("Exception while executing a multipart handler"); - return static_cast<OrthancPluginErrorCode>(e.GetErrorCode()); - } - catch (...) - { - LogError("Native exception while executing a multipart handler"); - return OrthancPluginErrorCode_Plugin; - } - } - - static void MultipartRestFinalize(OrthancPluginMultipartRestHandler* handler) - { - try - { - assert(handler != NULL); - delete reinterpret_cast<MultipartRestCallback::IHandler*>(handler); - } - catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) - { - LogError("Exception while finalizing a multipart handler"); - } - catch (...) - { - LogError("Native exception while finalizing a multipart handler"); - } - } - - void MultipartRestCallback::Register(const std::string& regularExpression) - { - OrthancPluginRegisterMultipartRestCallback( - GetGlobalContext(), regularExpression.c_str(), - reinterpret_cast<OrthancPluginMultipartRestFactory*>(this), - MultipartRestFactory, MultipartRestAddPart, MultipartRestExecute, MultipartRestFinalize); - } - -#elif HAS_ORTHANC_FRAMEWORK_MULTIPART_READER == 1 - - namespace - { - class CompatibilityPartHandler : public Orthanc::MultipartStreamReader::IHandler - { - private: - MultipartRestCallback::IHandler& handler_; - - public: - CompatibilityPartHandler(MultipartRestCallback::IHandler& handler) : - handler_(handler) - { - } - - virtual void HandlePart(const Orthanc::MultipartStreamReader::HttpHeaders& headers, - const void* part, - size_t size) - { - std::string contentType; - if (!Orthanc::MultipartStreamReader::GetMainContentType(contentType, headers)) - { - contentType.clear(); - } - - OrthancPluginErrorCode code = handler_.AddPart(contentType, headers, part, size); - - if (code != OrthancPluginErrorCode_Success) - { - ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code); - } - } - }; - } - - static boost::shared_mutex compatibilityMultipartMutex_; - static std::set<MultipartRestCallback*> compatibilityMultipartCallbacks_; - - - namespace - { - void CompatibilityMultipartRestCallback(OrthancPluginRestOutput* output, - const char* url, - const OrthancPluginHttpRequest* request) - { - std::map<std::string, std::string> headers; - for (uint32_t i = 0; i < request->headersCount; i++) - { - headers[request->headersKeys[i]] = request->headersValues[i]; - } - - std::string mainContentType; - if (!Orthanc::MultipartStreamReader::GetMainContentType(mainContentType, headers)) - { - LogError("Missing Content-Type HTTP header, prevents streaming the body"); - ORTHANC_PLUGINS_THROW_EXCEPTION(UnsupportedMediaType); - } - - std::string contentType, subType, boundary; - if (!Orthanc::MultipartStreamReader::ParseMultipartContentType - (contentType, subType, boundary, mainContentType)) - { - LogError("Invalid Content-Type HTTP header, prevents streaming the body: \"" + - mainContentType + "\""); - ORTHANC_PLUGINS_THROW_EXCEPTION(UnsupportedMediaType); - } - - std::vector<std::string> groups; - groups.reserve(request->groupsCount); - for (uint32_t i = 0; i < request->groupsCount; i++) - { - groups[i] = request->groups[i]; - } - - std::auto_ptr<MultipartRestCallback::IHandler> handler; - - { - boost::shared_lock<boost::shared_mutex> lock(compatibilityMultipartMutex_); - - for (std::set<MultipartRestCallback*>::const_iterator - it = compatibilityMultipartCallbacks_.begin(); - it != compatibilityMultipartCallbacks_.end(); ++it) - { - assert(*it != NULL); - handler.reset((*it)->CreateHandler(request->method, url, contentType, subType, groups, headers)); - if (handler.get() != NULL) - { - break; - } - } - } - - if (handler.get() != NULL) - { - { - CompatibilityPartHandler wrapper(*handler); - Orthanc::MultipartStreamReader reader(boundary); - reader.SetHandler(wrapper); - reader.AddChunk(request->body, request->bodySize); - reader.CloseStream(); - } - - handler->Execute(output); - } - } - } - - void MultipartRestCallback::Register(const std::string& regularExpression) - { - LogWarning("Performance warning: The plugin was compiled against a pre-1.5.7 version " - "of the Orthanc SDK. Multipart transfers will be entirely stored in RAM."); - - { - boost::unique_lock<boost::shared_mutex> lock(compatibilityMultipartMutex_); - compatibilityMultipartCallbacks_.insert(this); - } - - OrthancPluginRegisterRestCallback(GetGlobalContext(), regularExpression.c_str(), - Internals::Protect<CompatibilityMultipartRestCallback>); - } - -#else - - void MultipartRestCallback::Register(const std::string& regularExpression) - { - /** - * Version >= 1.5.7 of the Orthanc framework must be - * available. Make sure that macros "HAS_ORTHANC_FRAMEWORK=1", - * "ORTHANC_FRAMEWORK_VERSION_MAJOR", - * "ORTHANC_FRAMEWORK_VERSION_MINOR" and - * "ORTHANC_FRAMEWORK_VERSION_REVISION" are properly set. - **/ - LogError("The compatibility wrappers for multipart transfers is not available, stopping now."); - ORTHANC_PLUGINS_THROW_EXCEPTION(NotImplemented); - } - -#endif - }
--- a/Plugins/Samples/Common/OrthancPluginCppWrapper.h Tue Jun 11 14:02:57 2019 +0200 +++ b/Plugins/Samples/Common/OrthancPluginCppWrapper.h Tue Jun 11 19:44:10 2019 +0200 @@ -57,21 +57,6 @@ #endif -#if !defined(ORTHANC_FRAMEWORK_VERSION_IS_ABOVE) -#define ORTHANC_FRAMEWORK_VERSION_IS_ABOVE(major, minor, revision) \ - (defined(HAS_ORTHANC_FRAMEWORK) && \ - defined(ORTHANC_FRAMEWORK_VERSION_MAJOR) && \ - defined(ORTHANC_FRAMEWORK_VERSION_MINOR) && \ - defined(ORTHANC_FRAMEWORK_VERSION_REVISION) && \ - HAS_ORTHANC_FRAMEWORK == 1 && \ - ORTHANC_FRAMEWORK_VERSION_MAJOR > major || \ - (ORTHANC_FRAMEWORK_VERSION_MAJOR == major && \ - (ORTHANC_FRAMEWORK_VERSION_MINOR > minor || \ - (ORTHANC_FRAMEWORK_VERSION_MINOR == minor && \ - ORTHANC_FRAMEWORK_VERSION_REVISION >= revision)))) -#endif - - #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 2, 0) // The "OrthancPluginFindMatcher()" primitive was introduced in Orthanc 1.2.0 # define HAS_ORTHANC_PLUGIN_FIND_MATCHER 1 @@ -113,9 +98,9 @@ #endif #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 7) -# define HAS_ORTHANC_PLUGIN_HTTP_MULTIPART_SERVER 1 +# define HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER 1 #else -# define HAS_ORTHANC_PLUGIN_HTTP_MULTIPART_SERVER 0 +# define HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER 0 #endif @@ -126,7 +111,6 @@ const char* url, const OrthancPluginHttpRequest* request); - void SetGlobalContext(OrthancPluginContext* context); bool HasGlobalContext(); @@ -936,36 +920,225 @@ - class MultipartRestCallback : public boost::noncopyable + class IChunkedRequestReader : public boost::noncopyable { public: - class IHandler : public boost::noncopyable - { - public: - virtual ~IHandler() - { - } - - virtual OrthancPluginErrorCode AddPart(const std::string& contentType, - const std::map<std::string, std::string>& headers, - const void* data, - size_t size) = 0; - - virtual OrthancPluginErrorCode Execute(OrthancPluginRestOutput* output) = 0; - }; - - - virtual ~MultipartRestCallback() + virtual ~IChunkedRequestReader() { } - virtual IHandler* CreateHandler(OrthancPluginHttpMethod method, - const std::string& url, - const std::string& contentType, - const std::string& subType, // Possibly empty - const std::vector<std::string>& groups, - const std::map<std::string, std::string>& headers) = 0; + virtual void AddChunk(const void* data, + size_t size) = 0; + + virtual void Execute(OrthancPluginRestOutput* output) = 0; + }; + + + typedef IChunkedRequestReader* (*ChunkedRestCallback) (OrthancPluginRestOutput* output, + const char* url, + const OrthancPluginHttpRequest* request); + + + namespace Internals + { +#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER == 1 + template <ChunkedRestCallback Callback> + OrthancPluginErrorCode Protect(OrthancPluginRestOutput* output, + OrthancPluginServerChunkedRequestReader** reader, + const char* url, + const OrthancPluginHttpRequest* request) + { + try + { + switch (request->method) + { + case OrthancPluginHttpMethod_Get: + case OrthancPluginHttpMethod_Delete: + if (output == NULL || + reader != NULL || + Callback(output, url, request) != NULL) + { + return OrthancPluginErrorCode_InternalError; + } + + return OrthancPluginErrorCode_Success; + + case OrthancPluginHttpMethod_Post: + case OrthancPluginHttpMethod_Put: + if (output != NULL || + reader == NULL) + { + return OrthancPluginErrorCode_InternalError; + } + else + { + *reader = reinterpret_cast<OrthancPluginServerChunkedRequestReader*>(Callback(NULL, url, request)); + if (*reader == NULL) + { + return OrthancPluginErrorCode_Plugin; + } + } + + return OrthancPluginErrorCode_Success; + + default: + return OrthancPluginErrorCode_InternalError; + } + } + catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) + { +#if HAS_ORTHANC_EXCEPTION == 1 && HAS_ORTHANC_PLUGIN_EXCEPTION_DETAILS == 1 + if (HasGlobalContext() && + e.HasDetails()) + { + // The "false" instructs Orthanc not to log the detailed + // error message. This is to avoid duplicating the details, + // because "OrthancException" already does it on construction. + OrthancPluginSetHttpErrorDetails + (GetGlobalContext(), output, e.GetDetails(), false); + } +#endif + + return static_cast<OrthancPluginErrorCode>(e.GetErrorCode()); + } + catch (boost::bad_lexical_cast&) + { + return OrthancPluginErrorCode_BadFileFormat; + } + catch (...) + { + return OrthancPluginErrorCode_Plugin; + } + } + + static OrthancPluginErrorCode ChunkedRequestReaderAddChunk( + OrthancPluginServerChunkedRequestReader* reader, + const void* data, + uint32_t size) + { + try + { + if (reader == NULL) + { + return OrthancPluginErrorCode_InternalError; + } - void Register(const std::string& regularExpression); - }; + reinterpret_cast<IChunkedRequestReader*>(reader)->AddChunk(data, size); + return OrthancPluginErrorCode_Success; + } + catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) + { + return static_cast<OrthancPluginErrorCode>(e.GetErrorCode()); + } + catch (boost::bad_lexical_cast&) + { + return OrthancPluginErrorCode_BadFileFormat; + } + catch (...) + { + return OrthancPluginErrorCode_Plugin; + } + } + + static OrthancPluginErrorCode ChunkedRequestReaderExecute( + OrthancPluginServerChunkedRequestReader* reader, + OrthancPluginRestOutput* output) + { + try + { + if (reader == NULL) + { + return OrthancPluginErrorCode_InternalError; + } + + reinterpret_cast<IChunkedRequestReader*>(reader)->Execute(output); + return OrthancPluginErrorCode_Success; + } + catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) + { + return static_cast<OrthancPluginErrorCode>(e.GetErrorCode()); + } + catch (boost::bad_lexical_cast&) + { + return OrthancPluginErrorCode_BadFileFormat; + } + catch (...) + { + return OrthancPluginErrorCode_Plugin; + } + } + + static void ChunkedRequestReaderFinalize( + OrthancPluginServerChunkedRequestReader* reader) + { + if (reader != NULL) + { + delete reinterpret_cast<IChunkedRequestReader*>(reader); + } + } + +#else + + template <ChunkedRestCallback Callback> + void ChunkedRestCompatibility(OrthancPluginRestOutput* output, + const char* url, + const OrthancPluginHttpRequest* request) + { + switch (request->method) + { + case OrthancPluginHttpMethod_Get: + case OrthancPluginHttpMethod_Delete: + if (Callback(output, url, request) != NULL) + { + ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError); + } + else + { + return; + } + + case OrthancPluginHttpMethod_Post: + case OrthancPluginHttpMethod_Put: + { + std::auto_ptr<IChunkedRequestReader> reader(Callback(NULL, url, request)); + if (reader.get() == NULL) + { + ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError); + } + else + { + if (request->bodySize != 0) + { + reader->AddChunk(request->body, request->bodySize); + } + reader->Execute(output); + } + return; + } + + default: + ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError); + } + } +#endif + } + + + template <ChunkedRestCallback Callback> + void RegisterChunkedRestCallback(const std::string& uri) + { +#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER == 1 + OrthancPluginRegisterChunkedRestCallback( + GetGlobalContext(), uri.c_str(), Internals::Protect<Callback>, + Internals::ChunkedRequestReaderAddChunk, + Internals::ChunkedRequestReaderExecute, + Internals::ChunkedRequestReaderFinalize); +#else + LogWarning("Performance warning: The plugin was compiled against a pre-1.5.7 version " + "of the Orthanc SDK. Multipart transfers will be entirely stored in RAM."); + OrthancPluginRegisterRestCallback( + GetGlobalContext(), uri.c_str(), + Internals::Protect< Internals::ChunkedRestCompatibility<Callback> >); +#endif + } }