# HG changeset patch # User Sebastien Jodogne # Date 1559747868 -7200 # Node ID a48d652f15001c775c36bf21b47c2b3a6b684de8 # Parent af9432e46c07a2a2237b898dfee2673c3cbcb7ad new function OrthancPluginHttpClientChunkedBody(), new class OrthancPlugins::HttpClient diff -r af9432e46c07 -r a48d652f1500 Core/HttpClient.cpp --- a/Core/HttpClient.cpp Wed Jun 05 14:40:14 2019 +0200 +++ b/Core/HttpClient.cpp Wed Jun 05 17:17:48 2019 +0200 @@ -201,16 +201,16 @@ }; - class HttpClient::CurlBodyStream : public boost::noncopyable + class HttpClient::CurlChunkedBody : public boost::noncopyable { private: - HttpClient::IBodyStream* stream_; - std::string buffer_; + HttpClient::IChunkedBody* body_; + std::string buffer_; size_t CallbackInternal(char* curlBuffer, size_t curlBufferSize) { - if (stream_ == NULL) + if (body_ == NULL) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } @@ -220,11 +220,11 @@ throw OrthancException(ErrorCode_InternalError); } - // Read chunks from the stream so as to fill the target buffer + // Read chunks from the body stream so as to fill the target buffer std::string chunk; while (buffer_.size() < curlBufferSize && - stream_->ReadNextChunk(chunk)) + body_->ReadNextChunk(chunk)) { buffer_ += chunk; } @@ -243,26 +243,26 @@ } public: - CurlBodyStream() : - stream_(NULL) + CurlChunkedBody() : + body_(NULL) { } - void SetStream(HttpClient::IBodyStream& stream) + void SetBody(HttpClient::IChunkedBody& body) { - stream_ = &stream; + body_ = &body; buffer_.clear(); } void Clear() { - stream_ = NULL; + body_ = NULL; buffer_.clear(); } bool IsValid() const { - return stream_ != NULL; + return body_ != NULL; } static size_t Callback(char *buffer, @@ -272,15 +272,15 @@ { try { - HttpClient::CurlBodyStream* stream = reinterpret_cast(userdata); + HttpClient::CurlChunkedBody* body = reinterpret_cast(userdata); - if (stream == NULL) + if (body == NULL) { throw OrthancException(ErrorCode_NullPointer); } else { - return stream->CallbackInternal(buffer, size * nitems); + return body->CallbackInternal(buffer, size * nitems); } } catch (OrthancException& e) @@ -404,7 +404,7 @@ CurlHeaders defaultPostHeaders_; CurlHeaders defaultChunkedHeaders_; CurlHeaders userHeaders_; - CurlBodyStream bodyStream_; + CurlChunkedBody chunkedBody_; }; @@ -604,19 +604,21 @@ void HttpClient::SetBody(const std::string& data) { body_ = data; - pimpl_->bodyStream_.Clear(); + pimpl_->chunkedBody_.Clear(); } - void HttpClient::SetBodyStream(IBodyStream& stream) + void HttpClient::SetBody(IChunkedBody& body) { - pimpl_->bodyStream_.SetStream(stream); + body_.clear(); + pimpl_->chunkedBody_.SetBody(body); } - void HttpClient::ClearBodyStream() + void HttpClient::ClearBody() { - pimpl_->bodyStream_.Clear(); + body_.clear(); + pimpl_->chunkedBody_.Clear(); } @@ -825,10 +827,10 @@ LOG(INFO) << "For performance, the HTTP header \"Expect\" should be set to empty string in POST/PUT requests"; } - if (pimpl_->bodyStream_.IsValid()) + if (pimpl_->chunkedBody_.IsValid()) { - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_READFUNCTION, CurlBodyStream::Callback)); - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_READDATA, &pimpl_->bodyStream_)); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_READFUNCTION, CurlChunkedBody::Callback)); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_READDATA, &pimpl_->chunkedBody_)); CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POST, 1L)); CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, -1L)); diff -r af9432e46c07 -r a48d652f1500 Core/HttpClient.h --- a/Core/HttpClient.h Wed Jun 05 14:40:14 2019 +0200 +++ b/Core/HttpClient.h Wed Jun 05 17:17:48 2019 +0200 @@ -57,10 +57,10 @@ public: typedef std::map HttpHeaders; - class IBodyStream : public boost::noncopyable + class IChunkedBody : public boost::noncopyable { public: - virtual ~IBodyStream() + virtual ~IChunkedBody() { } @@ -69,7 +69,7 @@ private: class CurlHeaders; - class CurlBodyStream; + class CurlChunkedBody; class GlobalParameters; struct PImpl; @@ -158,9 +158,9 @@ return body_; } - void SetBodyStream(IBodyStream& stream); + void SetBody(IChunkedBody& body); - void ClearBodyStream(); + void ClearBody(); void SetVerbose(bool isVerbose); diff -r af9432e46c07 -r a48d652f1500 NEWS --- a/NEWS Wed Jun 05 14:40:14 2019 +0200 +++ b/NEWS Wed Jun 05 17:17:48 2019 +0200 @@ -9,6 +9,12 @@ to bypass the automated correction of outgoing C-FIND queries * Reporting of "ParentResources" in "DicomModalityStore" and "DicomModalityStore" jobs +Plugins +------- + +* New functions in the SDK: + - OrthancPluginHttpClientChunkedBody(): POST/PUT query with a chunked body + Maintenance ----------- diff -r af9432e46c07 -r a48d652f1500 Plugins/Engine/OrthancPlugins.cpp --- a/Plugins/Engine/OrthancPlugins.cpp Wed Jun 05 14:40:14 2019 +0200 +++ b/Plugins/Engine/OrthancPlugins.cpp Wed Jun 05 17:17:48 2019 +0200 @@ -973,7 +973,55 @@ return new Driver(driver, size, params_.applyMove, params_.freeMove); } }; - + + + + class OrthancPlugins::ChunkedBody : public HttpClient::IChunkedBody + { + private: + const _OrthancPluginHttpClientChunkedBody& params_; + PluginsErrorDictionary& errorDictionary_; + + public: + ChunkedBody(const _OrthancPluginHttpClientChunkedBody& params, + PluginsErrorDictionary& errorDictionary) : + params_(params), + errorDictionary_(errorDictionary) + { + } + + virtual bool ReadNextChunk(std::string& chunk) + { + if (params_.bodyDone(params_.body)) + { + return false; + } + else + { + size_t size = params_.bodyChunkSize(params_.body); + + chunk.resize(size); + + if (size != 0) + { + const void* data = params_.bodyChunkData(params_.body); + memcpy(&chunk[0], data, size); + } + + OrthancPluginErrorCode error = params_.bodyNext(params_.body); + + if (error != OrthancPluginErrorCode_Success) + { + errorDictionary_.LogError(error, true); + throw OrthancException(static_cast(error)); + } + else + { + return true; + } + } + } + }; OrthancPlugins::OrthancPlugins() @@ -2037,20 +2085,55 @@ } - void OrthancPlugins::CallHttpClient(const void* parameters) + static void RunHttpClient(HttpClient& client, + const _OrthancPluginCallHttpClient2& parameters) { - const _OrthancPluginCallHttpClient& p = *reinterpret_cast(parameters); - - HttpClient client; - client.SetUrl(p.url); - - if (p.username != NULL && - p.password != NULL) + client.SetUrl(parameters.url); + client.SetConvertHeadersToLowerCase(false); + + if (parameters.timeout != 0) + { + client.SetTimeout(parameters.timeout); + } + + if (parameters.username != NULL && + parameters.password != NULL) + { + client.SetCredentials(parameters.username, parameters.password); + } + + if (parameters.certificateFile != NULL) { - client.SetCredentials(p.username, p.password); + std::string certificate(parameters.certificateFile); + std::string key, password; + + if (parameters.certificateKeyFile) + { + key.assign(parameters.certificateKeyFile); + } + + if (parameters.certificateKeyPassword) + { + password.assign(parameters.certificateKeyPassword); + } + + client.SetClientCertificate(certificate, key, password); } - switch (p.method) + client.SetPkcs11Enabled(parameters.pkcs11 ? true : false); + + for (uint32_t i = 0; i < parameters.headersCount; i++) + { + if (parameters.headersKeys[i] == NULL || + parameters.headersValues[i] == NULL) + { + throw OrthancException(ErrorCode_NullPointer); + } + + client.AddHeader(parameters.headersKeys[i], parameters.headersValues[i]); + } + + switch (parameters.method) { case OrthancPluginHttpMethod_Get: client.SetMethod(HttpMethod_Get); @@ -2058,96 +2141,10 @@ case OrthancPluginHttpMethod_Post: client.SetMethod(HttpMethod_Post); - client.GetBody().assign(p.body, p.bodySize); break; case OrthancPluginHttpMethod_Put: client.SetMethod(HttpMethod_Put); - client.GetBody().assign(p.body, p.bodySize); - break; - - case OrthancPluginHttpMethod_Delete: - client.SetMethod(HttpMethod_Delete); - break; - - default: - throw OrthancException(ErrorCode_ParameterOutOfRange); - } - - std::string s; - client.ApplyAndThrowException(s); - - if (p.method != OrthancPluginHttpMethod_Delete) - { - CopyToMemoryBuffer(*p.target, s); - } - } - - - void OrthancPlugins::CallHttpClient2(const void* parameters) - { - const _OrthancPluginCallHttpClient2& p = *reinterpret_cast(parameters); - - HttpClient client; - client.SetUrl(p.url); - client.SetConvertHeadersToLowerCase(false); - - if (p.timeout != 0) - { - client.SetTimeout(p.timeout); - } - - if (p.username != NULL && - p.password != NULL) - { - client.SetCredentials(p.username, p.password); - } - - if (p.certificateFile != NULL) - { - std::string certificate(p.certificateFile); - std::string key, password; - - if (p.certificateKeyFile) - { - key.assign(p.certificateKeyFile); - } - - if (p.certificateKeyPassword) - { - password.assign(p.certificateKeyPassword); - } - - client.SetClientCertificate(certificate, key, password); - } - - client.SetPkcs11Enabled(p.pkcs11 ? true : false); - - for (uint32_t i = 0; i < p.headersCount; i++) - { - if (p.headersKeys[i] == NULL || - p.headersValues[i] == NULL) - { - throw OrthancException(ErrorCode_NullPointer); - } - - client.AddHeader(p.headersKeys[i], p.headersValues[i]); - } - - switch (p.method) - { - case OrthancPluginHttpMethod_Get: - client.SetMethod(HttpMethod_Get); - break; - - case OrthancPluginHttpMethod_Post: - client.SetMethod(HttpMethod_Post); - client.GetBody().assign(p.body, p.bodySize); - break; - - case OrthancPluginHttpMethod_Put: - client.SetMethod(HttpMethod_Put); - client.GetBody().assign(p.body, p.bodySize); break; case OrthancPluginHttpMethod_Delete: @@ -2164,7 +2161,7 @@ bool success = client.Apply(body, headers); // The HTTP request has succeeded - *p.httpStatus = static_cast(client.GetLastStatus()); + *parameters.httpStatus = static_cast(client.GetLastStatus()); if (!success) { @@ -2172,7 +2169,7 @@ } // Copy the HTTP headers of the answer, if the plugin requested them - if (p.answerHeaders != NULL) + if (parameters.answerHeaders != NULL) { Json::Value json = Json::objectValue; @@ -2183,17 +2180,107 @@ } std::string s = json.toStyledString(); - CopyToMemoryBuffer(*p.answerHeaders, s); + CopyToMemoryBuffer(*parameters.answerHeaders, s); } // Copy the body of the answer if it makes sense - if (p.method != OrthancPluginHttpMethod_Delete) + if (parameters.method != OrthancPluginHttpMethod_Delete) { - CopyToMemoryBuffer(*p.answerBody, body); + CopyToMemoryBuffer(*parameters.answerBody, body); } } + void OrthancPlugins::CallHttpClient(const void* parameters) + { + const _OrthancPluginCallHttpClient& p = *reinterpret_cast(parameters); + + _OrthancPluginCallHttpClient2 converted; + memset(&converted, 0, sizeof(converted)); + + uint16_t httpStatus; + + converted.answerBody = p.target; + converted.answerHeaders = NULL; + converted.httpStatus = &httpStatus; + converted.method = p.method; + converted.url = p.url; + converted.headersCount = 0; + converted.headersKeys = NULL; + converted.headersValues = NULL; + converted.body = p.body; + converted.bodySize = p.bodySize; + converted.username = p.username; + converted.password = p.password; + converted.timeout = 0; // Use default timeout + converted.certificateFile = NULL; + converted.certificateKeyFile = NULL; + converted.certificateKeyPassword = NULL; + converted.pkcs11 = false; + + HttpClient client; + RunHttpClient(client, converted); + } + + + void OrthancPlugins::CallHttpClient2(const void* parameters) + { + const _OrthancPluginCallHttpClient2& p = *reinterpret_cast(parameters); + + HttpClient client; + + if (p.method == OrthancPluginHttpMethod_Post || + p.method == OrthancPluginHttpMethod_Put) + { + client.GetBody().assign(p.body, p.bodySize); + } + + RunHttpClient(client, p); + } + + + void OrthancPlugins::HttpClientChunkedBody(const void* parameters) + { + const _OrthancPluginHttpClientChunkedBody& p = + *reinterpret_cast(parameters); + + if (p.method != OrthancPluginHttpMethod_Post && + p.method != OrthancPluginHttpMethod_Put) + { + throw OrthancException(ErrorCode_ParameterOutOfRange, + "This plugin service is only allowed for PUT and POST HTTP requests"); + } + + ChunkedBody body(p, pimpl_->dictionary_); + + HttpClient client; + client.SetBody(body); + + _OrthancPluginCallHttpClient2 converted; + memset(&converted, 0, sizeof(converted)); + + converted.answerBody = p.answerBody; + converted.answerHeaders = p.answerHeaders; + converted.httpStatus = p.httpStatus; + converted.method = p.method; + converted.url = p.url; + converted.headersCount = p.headersCount; + converted.headersKeys = p.headersKeys; + converted.headersValues = p.headersValues; + converted.body = NULL; + converted.bodySize = 0; + converted.username = p.username; + converted.password = p.password; + converted.timeout = p.timeout; + converted.certificateFile = p.certificateFile; + converted.certificateKeyFile = p.certificateKeyFile; + converted.certificateKeyPassword = p.certificateKeyPassword; + converted.pkcs11 = p.pkcs11; + + RunHttpClient(client, converted); + } + + void OrthancPlugins::CallPeerApi(const void* parameters) { const _OrthancPluginCallPeerApi& p = *reinterpret_cast(parameters); @@ -2872,6 +2959,10 @@ CallHttpClient2(parameters); return true; + case _OrthancPluginService_HttpClientChunkedBody: + HttpClientChunkedBody(parameters); + return true; + case _OrthancPluginService_ConvertPixelFormat: ConvertPixelFormat(parameters); return true; diff -r af9432e46c07 -r a48d652f1500 Plugins/Engine/OrthancPlugins.h --- a/Plugins/Engine/OrthancPlugins.h Wed Jun 05 14:40:14 2019 +0200 +++ b/Plugins/Engine/OrthancPlugins.h Wed Jun 05 17:17:48 2019 +0200 @@ -89,7 +89,8 @@ class WorklistHandler; class FindHandler; class MoveHandler; - + class ChunkedBody; + void RegisterRestCallback(const void* parameters, bool lock); @@ -164,6 +165,8 @@ void CallHttpClient2(const void* parameters); + void HttpClientChunkedBody(const void* parameters); + void CallPeerApi(const void* parameters); void GetFontInfo(const void* parameters); diff -r af9432e46c07 -r a48d652f1500 Plugins/Include/orthanc/OrthancCPlugin.h --- a/Plugins/Include/orthanc/OrthancCPlugin.h Wed Jun 05 14:40:14 2019 +0200 +++ b/Plugins/Include/orthanc/OrthancCPlugin.h Wed Jun 05 17:17:48 2019 +0200 @@ -120,7 +120,7 @@ #define ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER 1 #define ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER 5 -#define ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER 4 +#define ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER 7 #if !defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) @@ -429,6 +429,7 @@ _OrthancPluginService_SetMetricsValue = 31, _OrthancPluginService_EncodeDicomWebJson = 32, _OrthancPluginService_EncodeDicomWebXml = 33, + _OrthancPluginService_HttpClientChunkedBody = 34, /* New in Orthanc 1.5.7 */ /* Registration of callbacks */ _OrthancPluginService_RegisterRestCallback = 1000, @@ -6794,6 +6795,110 @@ } + + + + + + + + + + + + + + + + typedef uint8_t (*OrthancPluginChunkedBodyIsDone) (void* body); + + typedef OrthancPluginErrorCode (*OrthancPluginChunkedBodyNext) (void* body); + + typedef const void* (*OrthancPluginChunkedBodyGetChunkData) (void* body); + + typedef uint32_t (*OrthancPluginChunkedBodyGetChunkSize) (void* body); + + + + typedef struct + { + OrthancPluginMemoryBuffer* answerBody; + OrthancPluginMemoryBuffer* answerHeaders; + uint16_t* httpStatus; + OrthancPluginHttpMethod method; + const char* url; + uint32_t headersCount; + const char* const* headersKeys; + const char* const* headersValues; + const char* username; + const char* password; + uint32_t timeout; + const char* certificateFile; + const char* certificateKeyFile; + const char* certificateKeyPassword; + uint8_t pkcs11; + void* body; + OrthancPluginChunkedBodyIsDone bodyDone; + OrthancPluginChunkedBodyGetChunkData bodyChunkData; + OrthancPluginChunkedBodyGetChunkSize bodyChunkSize; + OrthancPluginChunkedBodyNext bodyNext; + } _OrthancPluginHttpClientChunkedBody; + + ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginHttpClientChunkedBody( + OrthancPluginContext* context, + OrthancPluginMemoryBuffer* answerBody, + OrthancPluginMemoryBuffer* answerHeaders, + uint16_t* httpStatus, + OrthancPluginHttpMethod method, + const char* url, + uint32_t headersCount, + const char* const* headersKeys, + const char* const* headersValues, + const char* username, + const char* password, + uint32_t timeout, + const char* certificateFile, + const char* certificateKeyFile, + const char* certificateKeyPassword, + uint8_t pkcs11, + void* body, + OrthancPluginChunkedBodyIsDone bodyDone, + OrthancPluginChunkedBodyGetChunkData bodyChunkData, + OrthancPluginChunkedBodyGetChunkSize bodyChunkSize, + OrthancPluginChunkedBodyNext bodyNext) + { + _OrthancPluginHttpClientChunkedBody params; + memset(¶ms, 0, sizeof(params)); + + /* In common with OrthancPluginHttpClient() */ + params.answerBody = answerBody; + params.answerHeaders = answerHeaders; + params.httpStatus = httpStatus; + params.method = method; + params.url = url; + params.headersCount = headersCount; + params.headersKeys = headersKeys; + params.headersValues = headersValues; + params.username = username; + params.password = password; + params.timeout = timeout; + params.certificateFile = certificateFile; + params.certificateKeyFile = certificateKeyFile; + params.certificateKeyPassword = certificateKeyPassword; + params.pkcs11 = pkcs11; + + /* For body stream */ + params.body = body; + params.bodyDone = bodyDone; + params.bodyChunkData = bodyChunkData; + params.bodyChunkSize = bodyChunkSize; + params.bodyNext = bodyNext; + + return context->InvokeService(context, _OrthancPluginService_HttpClientChunkedBody, ¶ms); + } + + + #ifdef __cplusplus } #endif diff -r af9432e46c07 -r a48d652f1500 Plugins/Samples/Common/OrthancPluginCppWrapper.cpp --- a/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp Wed Jun 05 14:40:14 2019 +0200 +++ b/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp Wed Jun 05 17:17:48 2019 +0200 @@ -440,9 +440,9 @@ const std::string& password) { OrthancPluginErrorCode error = OrthancPluginHttpDelete - (GetGlobalContext(), url.c_str(), - username.empty() ? NULL : username.c_str(), - password.empty() ? NULL : password.c_str()); + (GetGlobalContext(), url.c_str(), + username.empty() ? NULL : username.c_str(), + password.empty() ? NULL : password.c_str()); if (error == OrthancPluginErrorCode_Success) { @@ -591,19 +591,19 @@ switch (configuration_[key].type()) { - case Json::intValue: - target = configuration_[key].asInt(); - return true; + case Json::intValue: + target = configuration_[key].asInt(); + return true; - case Json::uintValue: - target = configuration_[key].asUInt(); - return true; + case Json::uintValue: + target = configuration_[key].asUInt(); + return true; - default: - LogError("The configuration option \"" + GetPath(key) + - "\" is not an integer as expected"); + default: + LogError("The configuration option \"" + GetPath(key) + + "\" is not an integer as expected"); - ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat); + ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat); } } @@ -667,23 +667,23 @@ switch (configuration_[key].type()) { - case Json::realValue: - target = configuration_[key].asFloat(); - return true; + case Json::realValue: + target = configuration_[key].asFloat(); + return true; - case Json::intValue: - target = static_cast(configuration_[key].asInt()); - return true; + case Json::intValue: + target = static_cast(configuration_[key].asInt()); + return true; - case Json::uintValue: - target = static_cast(configuration_[key].asUInt()); - return true; + case Json::uintValue: + target = static_cast(configuration_[key].asUInt()); + return true; - default: - LogError("The configuration option \"" + GetPath(key) + - "\" is not an integer as expected"); + default: + LogError("The configuration option \"" + GetPath(key) + + "\" is not an integer as expected"); - ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat); + ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat); } } @@ -703,41 +703,41 @@ switch (configuration_[key].type()) { - case Json::arrayValue: - { - bool ok = true; - - for (Json::Value::ArrayIndex i = 0; ok && i < configuration_[key].size(); i++) + case Json::arrayValue: { - if (configuration_[key][i].type() == Json::stringValue) + bool ok = true; + + for (Json::Value::ArrayIndex i = 0; ok && i < configuration_[key].size(); i++) { - target.push_back(configuration_[key][i].asString()); + if (configuration_[key][i].type() == Json::stringValue) + { + target.push_back(configuration_[key][i].asString()); + } + else + { + ok = false; + } } - else + + if (ok) { - ok = false; + return true; } + + break; } - if (ok) - { - return true; - } - - break; - } + case Json::stringValue: + if (allowSingleString) + { + target.push_back(configuration_[key].asString()); + return true; + } - case Json::stringValue: - if (allowSingleString) - { - target.push_back(configuration_[key].asString()); - return true; - } + break; - break; - - default: - break; + default: + break; } LogError("The configuration option \"" + GetPath(key) + @@ -758,7 +758,7 @@ target.clear(); for (std::list::const_iterator - it = lst.begin(); it != lst.end(); ++it) + it = lst.begin(); it != lst.end(); ++it) { target.insert(*it); } @@ -941,7 +941,7 @@ void* buffer) { image_ = OrthancPluginCreateImageAccessor - (GetGlobalContext(), format, width, height, pitch, buffer); + (GetGlobalContext(), format, width, height, pitch, buffer); if (image_ == NULL) { @@ -1143,7 +1143,7 @@ void AnswerJson(const Json::Value& value, OrthancPluginRestOutput* output - ) + ) { Json::StyledWriter writer; std::string bodyString = writer.write(value); @@ -1154,7 +1154,7 @@ void AnswerString(const std::string& answer, const char* mimeType, OrthancPluginRestOutput* output - ) + ) { OrthancPluginAnswerBuffer(GetGlobalContext(), output, answer.c_str(), answer.size(), mimeType); } @@ -1354,15 +1354,15 @@ // Parse the version of the Orthanc core int aa, bb, cc; if ( - #ifdef _MSC_VER - sscanf_s - #else - sscanf - #endif - (GetGlobalContext()->orthancVersion, "%4d.%4d.%4d", &aa, &bb, &cc) != 3 || - aa < 0 || - bb < 0 || - cc < 0) +#ifdef _MSC_VER + sscanf_s +#else + sscanf +#endif + (GetGlobalContext()->orthancVersion, "%4d.%4d.%4d", &aa, &bb, &cc) != 3 || + aa < 0 || + bb < 0 || + cc < 0) { return false; } @@ -1590,9 +1590,9 @@ OrthancPlugins::MemoryBuffer answer; uint16_t status; OrthancPluginErrorCode code = OrthancPluginCallPeerApi - (GetGlobalContext(), *answer, NULL, &status, peers_, - static_cast(index), OrthancPluginHttpMethod_Get, uri.c_str(), - 0, NULL, NULL, NULL, 0, timeout_); + (GetGlobalContext(), *answer, NULL, &status, peers_, + static_cast(index), OrthancPluginHttpMethod_Get, uri.c_str(), + 0, NULL, NULL, NULL, 0, timeout_); if (code == OrthancPluginErrorCode_Success) { @@ -1714,9 +1714,9 @@ OrthancPlugins::MemoryBuffer answer; uint16_t status; OrthancPluginErrorCode code = OrthancPluginCallPeerApi - (GetGlobalContext(), *answer, NULL, &status, peers_, - static_cast(index), OrthancPluginHttpMethod_Post, uri.c_str(), - 0, NULL, NULL, body.empty() ? NULL : body.c_str(), body.size(), timeout_); + (GetGlobalContext(), *answer, NULL, &status, peers_, + static_cast(index), OrthancPluginHttpMethod_Post, uri.c_str(), + 0, NULL, NULL, body.empty() ? NULL : body.c_str(), body.size(), timeout_); if (code == OrthancPluginErrorCode_Success) { @@ -1742,9 +1742,9 @@ OrthancPlugins::MemoryBuffer answer; uint16_t status; OrthancPluginErrorCode code = OrthancPluginCallPeerApi - (GetGlobalContext(), *answer, NULL, &status, peers_, - static_cast(index), OrthancPluginHttpMethod_Put, uri.c_str(), - 0, NULL, NULL, body.empty() ? NULL : body.c_str(), body.size(), timeout_); + (GetGlobalContext(), *answer, NULL, &status, peers_, + static_cast(index), OrthancPluginHttpMethod_Put, uri.c_str(), + 0, NULL, NULL, body.empty() ? NULL : body.c_str(), body.size(), timeout_); if (code == OrthancPluginErrorCode_Success) { @@ -2000,9 +2000,9 @@ } OrthancPluginJob* orthanc = OrthancPluginCreateJob( - GetGlobalContext(), job, CallbackFinalize, job->jobType_.c_str(), - CallbackGetProgress, CallbackGetContent, CallbackGetSerialized, - CallbackStep, CallbackStop, CallbackReset); + GetGlobalContext(), job, CallbackFinalize, job->jobType_.c_str(), + CallbackGetProgress, CallbackGetContent, CallbackGetSerialized, + CallbackStep, CallbackStop, CallbackReset); if (orthanc == NULL) { @@ -2055,4 +2055,220 @@ OrthancPluginMetricsType_Timer); } #endif + + + +#if HAS_ORTHANC_PLUGIN_HTTP_CHUNKED_BODY == 1 + class HttpClient::ChunkedBody : public boost::noncopyable + { + private: + static ChunkedBody& GetObject(void* body) + { + assert(body != NULL); + return *reinterpret_cast(body); + } + + IChunkedBody& body_; + bool done_; + std::string chunk_; + + public: + ChunkedBody(IChunkedBody& body) : + body_(body), + done_(false) + { + } + + static uint8_t IsDone(void* body) + { + return GetObject(body).done_; + } + + static const void* GetChunkData(void* body) + { + return GetObject(body).chunk_.c_str(); + } + + static uint32_t GetChunkSize(void* body) + { + return static_cast(GetObject(body).chunk_.size()); + } + + static OrthancPluginErrorCode Next(void* body) + { + ChunkedBody& that = GetObject(body); + + if (that.done_) + { + return OrthancPluginErrorCode_BadSequenceOfCalls; + } + else + { + try + { + that.done_ = !that.body_.ReadNextChunk(that.chunk_); + return OrthancPluginErrorCode_Success; + } + catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e) + { + return static_cast(e.GetErrorCode()); + } + catch (...) + { + return OrthancPluginErrorCode_InternalError; + } + } + } + }; +#endif + + HttpClient::HttpClient() : + httpStatus_(0), + method_(OrthancPluginHttpMethod_Get), + timeout_(0), + pkcs11_(false), + chunkedBody_(NULL) + { + } + + + void HttpClient::SetCredentials(const std::string& username, + const std::string& password) + { + username_ = username; + password_ = password; + } + + + void HttpClient::ClearCredentials() + { + username_.empty(); + password_.empty(); + } + + + void HttpClient::SetCertificate(const std::string& certificateFile, + const std::string& keyFile, + const std::string& keyPassword) + { + certificateFile_ = certificateFile; + certificateKeyFile_ = keyFile; + certificateKeyPassword_ = keyPassword; + } + + + void HttpClient::ClearCertificate() + { + certificateFile_.clear(); + certificateKeyFile_.clear(); + certificateKeyPassword_.clear(); + } + + + void HttpClient::ClearBody() + { + body_.clear(); + chunkedBody_ = NULL; + } + + + void HttpClient::SwapBody(std::string& body) + { + body_.swap(body); + chunkedBody_ = NULL; + } + + + void HttpClient::SetBody(const std::string& body) + { + body_ = body; + chunkedBody_ = NULL; + } + + +#if HAS_ORTHANC_PLUGIN_HTTP_CHUNKED_BODY == 1 + void HttpClient::SetBody(IChunkedBody& body) + { + body_.clear(); + chunkedBody_ = &body; + } +#endif + + + void HttpClient::Execute() + { + std::vector headersKeys; + std::vector headersValues; + + headersKeys.reserve(headers_.size()); + headersValues.reserve(headers_.size()); + + for (HttpHeaders::const_iterator it = headers_.begin(); + it != headers_.end(); ++it) + { + headersKeys.push_back(it->first.c_str()); + headersValues.push_back(it->second.c_str()); + } + + OrthancPluginErrorCode error; + + if (chunkedBody_ == NULL) + { + error = OrthancPluginHttpClient( + GetGlobalContext(), + *answerBody_, + *answerHeaders_, + &httpStatus_, + method_, + url_.c_str(), + headersKeys.size(), + headersKeys.empty() ? NULL : &headersKeys[0], + headersValues.empty() ? NULL : &headersValues[0], + body_.empty() ? NULL : body_.c_str(), + body_.size(), + username_.empty() ? NULL : username_.c_str(), + password_.empty() ? NULL : password_.c_str(), + timeout_, + certificateFile_.empty() ? NULL : certificateFile_.c_str(), + certificateFile_.empty() ? NULL : certificateKeyFile_.c_str(), + certificateFile_.empty() ? NULL : certificateKeyPassword_.c_str(), + pkcs11_ ? 1 : 0); + } + else + { +#if HAS_ORTHANC_PLUGIN_HTTP_CHUNKED_BODY != 1 + error = OrthancPluginErrorCode_InternalError; +#else + ChunkedBody wrapper(*chunkedBody_); + + error = OrthancPluginHttpClientChunkedBody( + GetGlobalContext(), + *answerBody_, + *answerHeaders_, + &httpStatus_, + method_, + url_.c_str(), + headersKeys.size(), + headersKeys.empty() ? NULL : &headersKeys[0], + headersValues.empty() ? NULL : &headersValues[0], + username_.empty() ? NULL : username_.c_str(), + password_.empty() ? NULL : password_.c_str(), + timeout_, + certificateFile_.empty() ? NULL : certificateFile_.c_str(), + certificateFile_.empty() ? NULL : certificateKeyFile_.c_str(), + certificateFile_.empty() ? NULL : certificateKeyPassword_.c_str(), + pkcs11_ ? 1 : 0, + &wrapper, + ChunkedBody::IsDone, + ChunkedBody::GetChunkData, + ChunkedBody::GetChunkSize, + ChunkedBody::Next); +#endif + } + + if (error != OrthancPluginErrorCode_Success) + { + ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(error); + } + } } diff -r af9432e46c07 -r a48d652f1500 Plugins/Samples/Common/OrthancPluginCppWrapper.h --- a/Plugins/Samples/Common/OrthancPluginCppWrapper.h Wed Jun 05 14:40:14 2019 +0200 +++ b/Plugins/Samples/Common/OrthancPluginCppWrapper.h Wed Jun 05 17:17:48 2019 +0200 @@ -85,6 +85,12 @@ # define HAS_ORTHANC_PLUGIN_METRICS 0 #endif +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 7) +# define HAS_ORTHANC_PLUGIN_HTTP_CHUNKED_BODY 1 +#else +# define HAS_ORTHANC_PLUGIN_HTTP_CHUNKED_BODY 0 +#endif + namespace OrthancPlugins @@ -771,4 +777,120 @@ ~MetricsTimer(); }; #endif + + + class HttpClient : public boost::noncopyable + { + public: +#if HAS_ORTHANC_PLUGIN_HTTP_CHUNKED_BODY == 1 + class IChunkedBody : public boost::noncopyable + { + public: + virtual ~IChunkedBody() + { + } + + virtual bool ReadNextChunk(std::string& chunk) = 0; + }; +#endif + + + private: + typedef std::map HttpHeaders; + + MemoryBuffer answerBody_; + MemoryBuffer answerHeaders_; + uint16_t httpStatus_; + OrthancPluginHttpMethod method_; + std::string url_; + HttpHeaders headers_; + std::string username_; + std::string password_; + uint32_t timeout_; + std::string certificateFile_; + std::string certificateKeyFile_; + std::string certificateKeyPassword_; + bool pkcs11_; + std::string body_; + +#if HAS_ORTHANC_PLUGIN_HTTP_CHUNKED_BODY == 1 + class ChunkedBody; + IChunkedBody* chunkedBody_; +#else + // Dummy variable for backward compatibility + void* chunkedBody_; +#endif + + public: + HttpClient(); + + const MemoryBuffer& GetAnswerBody() const + { + return answerBody_; + } + + const MemoryBuffer& GetAnswerHeaders() const + { + return answerHeaders_; + } + + uint16_t GetHttpStatus() const + { + return httpStatus_; + } + + void SetMethod(OrthancPluginHttpMethod method) + { + method_ = method; + } + + const std::string& GetUrl() const + { + return url_; + } + + void SetUrl(const std::string& url) + { + url_ = url; + } + + void AddHeader(const std::string& key, + const std::string& value) + { + headers_[key] = value; + } + + void SetCredentials(const std::string& username, + const std::string& password); + + void ClearCredentials(); + + void SetTimeout(unsigned int timeout) // 0 for default timeout + { + timeout_ = timeout; + } + + void SetCertificate(const std::string& certificateFile, + const std::string& keyFile, + const std::string& keyPassword); + + void ClearCertificate(); + + void SetPkcs11(bool pkcs11) + { + pkcs11_ = pkcs11; + } + + void ClearBody(); + + void SwapBody(std::string& body); + + void SetBody(const std::string& body); + +#if HAS_ORTHANC_PLUGIN_HTTP_CHUNKED_BODY == 1 + void SetBody(IChunkedBody& body); +#endif + + void Execute(); + }; }