Mercurial > hg > orthanc
changeset 1987:ce90d109bb64
new plugin functions: OrthancPluginHttpClient and OrthancPluginGenerateUuid
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 26 Apr 2016 17:40:55 +0200 |
parents | 99b249867052 |
children | e29aea2349b9 |
files | Core/HttpClient.cpp Core/HttpClient.h Core/Lua/LuaContext.h NEWS OrthancServer/LuaScripting.cpp OrthancServer/OrthancInitialization.cpp OrthancServer/Scheduler/StorePeerCommand.cpp OrthancServer/main.cpp Plugins/Engine/OrthancPlugins.cpp Plugins/Engine/OrthancPlugins.h Plugins/Include/orthanc/OrthancCPlugin.h |
diffstat | 11 files changed, 322 insertions(+), 68 deletions(-) [+] |
line wrap: on
line diff
--- a/Core/HttpClient.cpp Mon Apr 25 17:23:07 2016 +0200 +++ b/Core/HttpClient.cpp Tue Apr 26 17:40:55 2016 +0200 @@ -42,10 +42,6 @@ #include <boost/algorithm/string/predicate.hpp> -static std::string globalCACertificates_; -static bool globalVerifyPeers_ = true; -static long globalTimeout_ = 0; - extern "C" { static CURLcode GetHttpStatus(CURLcode code, CURL* curl, long* status) @@ -77,6 +73,79 @@ namespace Orthanc { + class HttpClient::GlobalParameters + { + private: + boost::mutex mutex_; + bool httpsVerifyPeers_; + std::string httpsCACertificates_; + std::string proxy_; + long timeout_; + + GlobalParameters() : + httpsVerifyPeers_(true), + timeout_(0) + { + } + + public: + // Singleton pattern + static GlobalParameters& GetInstance() + { + static GlobalParameters parameters; + return parameters; + } + + void ConfigureSsl(bool httpsVerifyPeers, + const std::string& httpsCACertificates) + { + boost::mutex::scoped_lock lock(mutex_); + httpsVerifyPeers_ = httpsVerifyPeers; + httpsCACertificates_ = httpsCACertificates; + } + + void GetSslConfiguration(bool& httpsVerifyPeers, + std::string& httpsCACertificates) + { + boost::mutex::scoped_lock lock(mutex_); + httpsVerifyPeers = httpsVerifyPeers_; + httpsCACertificates = httpsCACertificates_; + } + + void SetDefaultProxy(const std::string& proxy) + { + LOG(INFO) << "Setting the default proxy for HTTP client connections: " << proxy; + + { + boost::mutex::scoped_lock lock(mutex_); + proxy_ = proxy; + } + } + + void GetDefaultProxy(std::string& target) + { + boost::mutex::scoped_lock lock(mutex_); + target = proxy_; + } + + void SetDefaultTimeout(long seconds) + { + LOG(INFO) << "Setting the default timeout for HTTP client connections: " << seconds << " seconds"; + + { + boost::mutex::scoped_lock lock(mutex_); + timeout_ = seconds; + } + } + + long GetDefaultTimeout() + { + boost::mutex::scoped_lock lock(mutex_); + return timeout_; + } + }; + + struct HttpClient::PImpl { CURL* curl_; @@ -93,6 +162,7 @@ throw OrthancException(ErrorCode_BadRequest); case HttpStatus_401_Unauthorized: + case HttpStatus_403_Forbidden: throw OrthancException(ErrorCode_Unauthorized); case HttpStatus_404_NotFound: @@ -163,8 +233,9 @@ method_ = HttpMethod_Get; lastStatus_ = HttpStatus_200_Ok; isVerbose_ = false; - timeout_ = globalTimeout_; - verifyPeers_ = globalVerifyPeers_; + timeout_ = GlobalParameters::GetInstance().GetDefaultTimeout(); + GlobalParameters::GetInstance().GetDefaultProxy(proxy_); + GlobalParameters::GetInstance().GetSslConfiguration(verifyPeers_, caCertificates_); } @@ -174,22 +245,6 @@ } - HttpClient::HttpClient(const HttpClient& other) : pimpl_(new PImpl) - { - Setup(); - - if (other.IsVerbose()) - { - SetVerbose(true); - } - - if (other.credentials_.size() != 0) - { - credentials_ = other.credentials_; - } - } - - HttpClient::~HttpClient() { curl_easy_cleanup(pimpl_->curl_); @@ -248,9 +303,9 @@ // Setup HTTPS-related options #if ORTHANC_SSL_ENABLED == 1 - if (IsHttpsVerifyPeers()) + if (verifyPeers_) { - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CAINFO, GetHttpsCACertificates().c_str())); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CAINFO, caCertificates_.c_str())); CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYHOST, 2)); // libcurl default is strict verifyhost CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYPEER, 1)); } @@ -375,7 +430,15 @@ lastStatus_ = static_cast<HttpStatus>(status); } - return (status >= 200 && status < 300); + bool success = (status >= 200 && status < 300); + + if (!success) + { + LOG(INFO) << "Error in HTTP request, received HTTP status " << status + << " (" << EnumerationToString(lastStatus_) << ")"; + } + + return success; } @@ -400,59 +463,54 @@ credentials_ = std::string(username) + ":" + std::string(password); } - - const std::string& HttpClient::GetHttpsCACertificates() const + + void HttpClient::ConfigureSsl(bool httpsVerifyPeers, + const std::string& httpsVerifyCertificates) { - if (caCertificates_.empty()) - { - return globalCACertificates_; - } - else - { - return caCertificates_; - } - } - - - void HttpClient::GlobalInitialize(bool httpsVerifyPeers, - const std::string& httpsVerifyCertificates) - { - globalVerifyPeers_ = httpsVerifyPeers; - globalCACertificates_ = httpsVerifyCertificates; - #if ORTHANC_SSL_ENABLED == 1 if (httpsVerifyPeers) { - if (globalCACertificates_.empty()) + if (httpsVerifyCertificates.empty()) { LOG(WARNING) << "No certificates are provided to validate peers, " << "set \"HttpsCACertificates\" if you need to do HTTPS requests"; } else { - LOG(WARNING) << "HTTPS will use the CA certificates from this file: " << globalCACertificates_; + LOG(WARNING) << "HTTPS will use the CA certificates from this file: " << httpsVerifyCertificates; } } else { - LOG(WARNING) << "The verification of the peers in HTTPS requests is disabled!"; + LOG(WARNING) << "The verification of the peers in HTTPS requests is disabled"; } #endif + GlobalParameters::GetInstance().ConfigureSsl(httpsVerifyPeers, httpsVerifyCertificates); + } + + + void HttpClient::GlobalInitialize() + { CheckCode(curl_global_init(CURL_GLOBAL_DEFAULT)); } - + void HttpClient::GlobalFinalize() { curl_global_cleanup(); } + - + void HttpClient::SetDefaultProxy(const std::string& proxy) + { + GlobalParameters::GetInstance().SetDefaultProxy(proxy); + } + + void HttpClient::SetDefaultTimeout(long timeout) { - LOG(INFO) << "Setting the default timeout for HTTP client connections: " << timeout << " seconds"; - globalTimeout_ = timeout; + GlobalParameters::GetInstance().SetDefaultTimeout(timeout); }
--- a/Core/HttpClient.h Mon Apr 25 17:23:07 2016 +0200 +++ b/Core/HttpClient.h Tue Apr 26 17:40:55 2016 +0200 @@ -43,6 +43,8 @@ class HttpClient { private: + class GlobalParameters; + struct PImpl; boost::shared_ptr<PImpl> pimpl_; @@ -59,11 +61,10 @@ void Setup(); - void operator= (const HttpClient&); // Forbidden + void operator= (const HttpClient&); // Assignment forbidden + HttpClient(const HttpClient& base); // Copy forbidden public: - HttpClient(const HttpClient& base); - HttpClient(); ~HttpClient(); @@ -162,13 +163,20 @@ caCertificates_ = certificates; } - const std::string& GetHttpsCACertificates() const; + const std::string& GetHttpsCACertificates() const + { + return caCertificates_; + } - static void GlobalInitialize(bool httpsVerifyPeers, - const std::string& httpsCACertificates); + static void GlobalInitialize(); static void GlobalFinalize(); + static void ConfigureSsl(bool httpsVerifyPeers, + const std::string& httpsCACertificates); + + static void SetDefaultProxy(const std::string& proxy); + static void SetDefaultTimeout(long timeout); void ApplyAndThrowException(std::string& answer);
--- a/Core/Lua/LuaContext.h Mon Apr 25 17:23:07 2016 +0200 +++ b/Core/Lua/LuaContext.h Tue Apr 26 17:40:55 2016 +0200 @@ -104,11 +104,6 @@ httpClient_.SetCredentials(username, password); } - void SetHttpProxy(const std::string& proxy) - { - httpClient_.SetProxy(proxy); - } - void RegisterFunction(const char* name, lua_CFunction func);
--- a/NEWS Mon Apr 25 17:23:07 2016 +0200 +++ b/NEWS Tue Apr 26 17:40:55 2016 +0200 @@ -22,6 +22,8 @@ ------- * New callback to filter incoming HTTP requests: OrthancPluginRegisterIncomingHttpRequestFilter() +* New function: "OrthancPluginHttpClient()" to do HTTP requests with full control +* New function: "OrthancPluginGenerateUuid()" to generate a UUID Lua ---
--- a/OrthancServer/LuaScripting.cpp Mon Apr 25 17:23:07 2016 +0200 +++ b/OrthancServer/LuaScripting.cpp Tue Apr 26 17:40:55 2016 +0200 @@ -391,7 +391,6 @@ lua_.RegisterFunction("GetOrthancConfiguration", GetOrthancConfiguration); lua_.Execute(Orthanc::EmbeddedResources::LUA_TOOLBOX); - lua_.SetHttpProxy(Configuration::GetGlobalStringParameter("HttpProxy", "")); }
--- a/OrthancServer/OrthancInitialization.cpp Mon Apr 25 17:23:07 2016 +0200 +++ b/OrthancServer/OrthancInitialization.cpp Tue Apr 26 17:40:55 2016 +0200 @@ -411,8 +411,7 @@ ReadGlobalConfiguration(configurationFile); ValidateGlobalConfiguration(); - HttpClient::GlobalInitialize(GetGlobalBoolParameterInternal("HttpsVerifyPeers", true), - GetGlobalStringParameterInternal("HttpsCACertificates", "")); + HttpClient::GlobalInitialize(); RegisterUserMetadata(); RegisterUserContentType();
--- a/OrthancServer/Scheduler/StorePeerCommand.cpp Mon Apr 25 17:23:07 2016 +0200 +++ b/OrthancServer/Scheduler/StorePeerCommand.cpp Tue Apr 26 17:40:55 2016 +0200 @@ -52,7 +52,6 @@ { // Configure the HTTP client HttpClient client; - client.SetProxy(Configuration::GetGlobalStringParameter("HttpProxy", "")); if (peer_.GetUsername().size() != 0 && peer_.GetPassword().size() != 0) {
--- a/OrthancServer/main.cpp Mon Apr 25 17:23:07 2016 +0200 +++ b/OrthancServer/main.cpp Tue Apr 26 17:40:55 2016 +0200 @@ -892,7 +892,10 @@ { ServerContext context(database, storageArea); + HttpClient::ConfigureSsl(Configuration::GetGlobalBoolParameter("HttpsVerifyPeers", true), + Configuration::GetGlobalStringParameter("HttpsCACertificates", "")); HttpClient::SetDefaultTimeout(Configuration::GetGlobalIntegerParameter("HttpTimeout", 0)); + HttpClient::SetDefaultProxy(Configuration::GetGlobalStringParameter("HttpProxy", "")); context.SetCompressionEnabled(Configuration::GetGlobalBoolParameter("StorageCompression", false)); context.SetStoreMD5ForAttachments(Configuration::GetGlobalBoolParameter("StoreMD5ForAttachments", true));
--- a/Plugins/Engine/OrthancPlugins.cpp Mon Apr 25 17:23:07 2016 +0200 +++ b/Plugins/Engine/OrthancPlugins.cpp Tue Apr 26 17:40:55 2016 +0200 @@ -1415,6 +1415,76 @@ } + void OrthancPlugins::CallHttpClient2(const void* parameters) + { + const _OrthancPluginCallHttpClient2& p = *reinterpret_cast<const _OrthancPluginCallHttpClient2*>(parameters); + + HttpClient client; + client.SetUrl(p.url); + + if (p.timeout != 0) + { + client.SetTimeout(p.timeout); + } + + if (p.username != NULL && + p.password != NULL) + { + client.SetCredentials(p.username, p.password); + } + + for (uint32_t i = 0; i < p.headersCount; i++) + { + if (p.headersKeys[i] == NULL || + p.headersValues[i] == NULL) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + 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: + client.SetMethod(HttpMethod_Delete); + break; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + std::string s; + + if (!client.Apply(s)) + { + *p.httpStatus = 0; + throw OrthancException(ErrorCode_NetworkProtocol); + } + + *p.httpStatus = static_cast<uint16_t>(client.GetLastStatus()); + + if (p.method != OrthancPluginHttpMethod_Delete) + { + CopyToMemoryBuffer(*p.target, s); + } + } + + void OrthancPlugins::ConvertPixelFormat(const void* parameters) { const _OrthancPluginConvertPixelFormat& p = *reinterpret_cast<const _OrthancPluginConvertPixelFormat*>(parameters); @@ -2115,6 +2185,10 @@ CallHttpClient(parameters); return true; + case _OrthancPluginService_CallHttpClient2: + CallHttpClient2(parameters); + return true; + case _OrthancPluginService_ConvertPixelFormat: ConvertPixelFormat(parameters); return true; @@ -2255,6 +2329,13 @@ ApplyLookupDictionary(parameters); return true; + case _OrthancPluginService_GenerateUuid: + { + *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result = + CopyString(Toolbox::GenerateUuid()); + return true; + } + default: { // This service is unknown to the Orthanc plugin engine
--- a/Plugins/Engine/OrthancPlugins.h Mon Apr 25 17:23:07 2016 +0200 +++ b/Plugins/Engine/OrthancPlugins.h Tue Apr 26 17:40:55 2016 +0200 @@ -139,6 +139,8 @@ void CallHttpClient(const void* parameters); + void CallHttpClient2(const void* parameters); + void GetFontInfo(const void* parameters); void DrawText(const void* parameters);
--- a/Plugins/Include/orthanc/OrthancCPlugin.h Mon Apr 25 17:23:07 2016 +0200 +++ b/Plugins/Include/orthanc/OrthancCPlugin.h Tue Apr 26 17:40:55 2016 +0200 @@ -403,6 +403,8 @@ _OrthancPluginService_ComputeMd5 = 24, _OrthancPluginService_ComputeSha1 = 25, _OrthancPluginService_LookupDictionary = 26, + _OrthancPluginService_CallHttpClient2 = 27, + _OrthancPluginService_GenerateUuid = 28, /* Registration of callbacks */ _OrthancPluginService_RegisterRestCallback = 1000, @@ -4148,8 +4150,8 @@ * @param target The target memory buffer. It must be freed with OrthancPluginFreeMemoryBuffer(). * @param uri The URI in the built-in Orthanc API. * @param headersCount The number of HTTP headers. - * @param headersKeys Array containing the keys of the HTTP headers. - * @param headersValues Array containing the values of the HTTP headers. + * @param headersKeys Array containing the keys of the HTTP headers (can be <tt>NULL</tt> if no header). + * @param headersValues Array containing the values of the HTTP headers (can be <tt>NULL</tt> if no header). * @param afterPlugins If 0, the built-in API of Orthanc is used. * If 1, the API is tainted by the plugins. * @return 0 if success, or the error code if failure. @@ -4789,6 +4791,112 @@ } + + typedef struct + { + OrthancPluginMemoryBuffer* target; + uint16_t* httpStatus; + OrthancPluginHttpMethod method; + const char* url; + uint32_t headersCount; + const char* const* headersKeys; + const char* const* headersValues; + const char* body; + uint32_t bodySize; + const char* username; + const char* password; + uint32_t timeout; + } _OrthancPluginCallHttpClient2; + + + + /** + * @brief Issue a HTTP call with full flexibility. + * + * Make a HTTP call to the given URL. The result to the query is + * stored into a newly allocated memory buffer. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param target The target memory buffer. It must be freed with OrthancPluginFreeMemoryBuffer() (out argument). + * @param httpStatus The HTTP status after the execution of the request (out argument). + * @param method HTTP method to be used. + * @param url The URL of interest. + * @param headersCount The number of HTTP headers. + * @param headersKeys Array containing the keys of the HTTP headers (can be <tt>NULL</tt> if no header). + * @param headersValues Array containing the values of the HTTP headers (can be <tt>NULL</tt> if no header). + * @param username The username (can be <tt>NULL</tt> if no password protection). + * @param password The password (can be <tt>NULL</tt> if no password protection). + * @param body The body of the POST request. + * @param bodySize The size of the body. + * @param timeout Timeout in seconds (0 for default timeout). + * @return 0 if success, or the error code if failure. + **/ + ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginHttpClient( + OrthancPluginContext* context, + OrthancPluginMemoryBuffer* target, + uint16_t* httpStatus, + OrthancPluginHttpMethod method, + const char* url, + uint32_t headersCount, + const char* const* headersKeys, + const char* const* headersValues, + const char* body, + uint32_t bodySize, + const char* username, + const char* password, + uint32_t timeout) + { + _OrthancPluginCallHttpClient2 params; + memset(¶ms, 0, sizeof(params)); + + params.target = target; + params.httpStatus = httpStatus; + params.method = method; + params.url = url; + params.headersCount = headersCount; + params.headersKeys = headersKeys; + params.headersValues = headersValues; + params.body = body; + params.bodySize = bodySize; + params.username = username; + params.password = password; + params.timeout = timeout; + + return context->InvokeService(context, _OrthancPluginService_CallHttpClient2, ¶ms); + } + + + /** + * @brief Generate an UUID. + * + * Generate a random GUID/UUID (globally unique identifier). + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @return NULL in the case of an error, or a newly allocated string + * containing the UUID. This string must be freed by OrthancPluginFreeString(). + * @ingroup Toolbox + **/ + ORTHANC_PLUGIN_INLINE char* OrthancPluginGenerateUuid( + OrthancPluginContext* context) + { + char* result; + + _OrthancPluginRetrieveDynamicString params; + params.result = &result; + params.argument = NULL; + + if (context->InvokeService(context, _OrthancPluginService_GenerateUuid, ¶ms) != OrthancPluginErrorCode_Success) + { + /* Error */ + return NULL; + } + else + { + return result; + } + } + + #ifdef __cplusplus } #endif