# HG changeset patch # User Sebastien Jodogne # Date 1441121846 -7200 # Node ID 31f4adefb88fb0c457512e06e7aa5818c5c97fef # Parent fd0464ce1962d96a76fec62c2cf364e5e5247608 issuing HTTP requests from the plugin SDK diff -r fd0464ce1962 -r 31f4adefb88f Core/Enumerations.cpp --- a/Core/Enumerations.cpp Tue Sep 01 16:41:16 2015 +0200 +++ b/Core/Enumerations.cpp Tue Sep 01 17:37:26 2015 +0200 @@ -136,6 +136,9 @@ case ErrorCode_BadJson: return "Cannot parse a JSON document"; + case ErrorCode_Unauthorized: + return "Bad credentials were provided to an HTTP request"; + case ErrorCode_SQLiteNotOpened: return "SQLite: The database is not opened"; @@ -1124,6 +1127,9 @@ case ErrorCode_BadJson: return HttpStatus_400_BadRequest; + case ErrorCode_Unauthorized: + return HttpStatus_401_Unauthorized; + default: return HttpStatus_500_InternalServerError; } diff -r fd0464ce1962 -r 31f4adefb88f Core/Enumerations.h --- a/Core/Enumerations.h Tue Sep 01 16:41:16 2015 +0200 +++ b/Core/Enumerations.h Tue Sep 01 17:37:26 2015 +0200 @@ -75,6 +75,7 @@ ErrorCode_UnknownPluginService = 26 /*!< Plugin invoking an unknown service */, ErrorCode_UnknownDicomTag = 27 /*!< Unknown DICOM tag */, ErrorCode_BadJson = 28 /*!< Cannot parse a JSON document */, + ErrorCode_Unauthorized = 29 /*!< Bad credentials were provided to an HTTP request */, ErrorCode_SQLiteNotOpened = 1000 /*!< SQLite: The database is not opened */, ErrorCode_SQLiteAlreadyOpened = 1001 /*!< SQLite: Connection is already open */, ErrorCode_SQLiteCannotOpen = 1002 /*!< SQLite: Unable to open the database */, diff -r fd0464ce1962 -r 31f4adefb88f Core/HttpClient.cpp --- a/Core/HttpClient.cpp Tue Sep 01 16:41:16 2015 +0200 +++ b/Core/HttpClient.cpp Tue Sep 01 17:37:26 2015 +0200 @@ -84,6 +84,26 @@ }; + static void ThrowException(HttpStatus status) + { + switch (status) + { + case HttpStatus_400_BadRequest: + throw OrthancException(ErrorCode_BadRequest); + + case HttpStatus_401_Unauthorized: + throw OrthancException(ErrorCode_Unauthorized); + + case HttpStatus_404_NotFound: + throw OrthancException(ErrorCode_InexistentItem); + + default: + throw OrthancException(ErrorCode_NetworkProtocol); + } + } + + + static CURLcode CheckCode(CURLcode code) { if (code != CURLE_OK) @@ -275,10 +295,10 @@ if (method_ == HttpMethod_Post || method_ == HttpMethod_Put) { - if (postData_.size() > 0) + if (body_.size() > 0) { - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, postData_.c_str())); - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, postData_.size())); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, body_.c_str())); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, body_.size())); } else { @@ -392,4 +412,21 @@ LOG(INFO) << "Setting the default timeout for HTTP client connections: " << timeout << " seconds"; globalTimeout_ = timeout; } + + + void HttpClient::ApplyAndThrowException(std::string& answer) + { + if (!Apply(answer)) + { + ThrowException(GetLastStatus()); + } + } + + void HttpClient::ApplyAndThrowException(Json::Value& answer) + { + if (!Apply(answer)) + { + ThrowException(GetLastStatus()); + } + } } diff -r fd0464ce1962 -r 31f4adefb88f Core/HttpClient.h --- a/Core/HttpClient.h Tue Sep 01 16:41:16 2015 +0200 +++ b/Core/HttpClient.h Tue Sep 01 17:37:26 2015 +0200 @@ -50,7 +50,7 @@ std::string credentials_; HttpMethod method_; HttpStatus lastStatus_; - std::string postData_; + std::string body_; // This only makes sense for POST and PUT requests bool isVerbose_; long timeout_; std::string proxy_; @@ -103,19 +103,19 @@ return timeout_; } - void SetPostData(const std::string& data) + void SetBody(const std::string& data) { - postData_ = data; + body_ = data; } - std::string& AccessPostData() + std::string& GetBody() { - return postData_; + return body_; } - const std::string& AccessPostData() const + const std::string& GetBody() const { - return postData_; + return body_; } void SetVerbose(bool isVerbose); @@ -165,5 +165,9 @@ static void GlobalFinalize(); static void SetDefaultTimeout(long timeout); + + void ApplyAndThrowException(std::string& answer); + + void ApplyAndThrowException(Json::Value& answer); }; } diff -r fd0464ce1962 -r 31f4adefb88f Core/Lua/LuaContext.cpp --- a/Core/Lua/LuaContext.cpp Tue Sep 01 16:41:16 2015 +0200 +++ b/Core/Lua/LuaContext.cpp Tue Sep 01 17:37:26 2015 +0200 @@ -241,11 +241,11 @@ if (nArgs >= 2) { - that.httpClient_.SetPostData(lua_tostring(state, 2)); + that.httpClient_.SetBody(lua_tostring(state, 2)); } else { - that.httpClient_.AccessPostData().clear(); + that.httpClient_.GetBody().clear(); } // Do the HTTP POST/PUT request diff -r fd0464ce1962 -r 31f4adefb88f OrthancServer/Scheduler/StorePeerCommand.cpp --- a/OrthancServer/Scheduler/StorePeerCommand.cpp Tue Sep 01 16:41:16 2015 +0200 +++ b/OrthancServer/Scheduler/StorePeerCommand.cpp Tue Sep 01 17:37:26 2015 +0200 @@ -70,7 +70,7 @@ try { - context_.ReadFile(client.AccessPostData(), *it, FileContentType_Dicom); + context_.ReadFile(client.GetBody(), *it, FileContentType_Dicom); std::string answer; if (!client.Apply(answer)) diff -r fd0464ce1962 -r 31f4adefb88f Plugins/Engine/OrthancPlugins.cpp --- a/Plugins/Engine/OrthancPlugins.cpp Tue Sep 01 16:41:16 2015 +0200 +++ b/Plugins/Engine/OrthancPlugins.cpp Tue Sep 01 17:37:26 2015 +0200 @@ -1121,6 +1121,53 @@ } + void OrthancPlugins::CallHttpClient(const void* parameters) + { + const _OrthancPluginCallHttpClient& p = *reinterpret_cast(parameters); + + HttpClient client; + client.SetUrl(p.url); + + if (p.username != NULL && + p.password != NULL) + { + client.SetCredentials(p.username, p.password); + } + + 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; + client.ApplyAndThrowException(s); + + if (p.method != OrthancPluginHttpMethod_Delete) + { + CopyToMemoryBuffer(*p.target, s); + } + } + + bool OrthancPlugins::InvokeService(_OrthancPluginService service, const void* parameters) { @@ -1479,6 +1526,10 @@ CompressImage(parameters); return true; + case _OrthancPluginService_CallHttpClient: + CallHttpClient(parameters); + return true; + default: { // This service is unknown to the Orthanc plugin engine diff -r fd0464ce1962 -r 31f4adefb88f Plugins/Engine/OrthancPlugins.h --- a/Plugins/Engine/OrthancPlugins.h Tue Sep 01 16:41:16 2015 +0200 +++ b/Plugins/Engine/OrthancPlugins.h Tue Sep 01 17:37:26 2015 +0200 @@ -104,6 +104,8 @@ void CompressImage(const void* parameters); + void CallHttpClient(const void* parameters); + public: OrthancPlugins(); diff -r fd0464ce1962 -r 31f4adefb88f Plugins/Include/orthanc/OrthancCPlugin.h --- a/Plugins/Include/orthanc/OrthancCPlugin.h Tue Sep 01 16:41:16 2015 +0200 +++ b/Plugins/Include/orthanc/OrthancCPlugin.h Tue Sep 01 17:37:26 2015 +0200 @@ -208,6 +208,7 @@ OrthancPluginErrorCode_UnknownPluginService = 26 /*!< Plugin invoking an unknown service */, OrthancPluginErrorCode_UnknownDicomTag = 27 /*!< Unknown DICOM tag */, OrthancPluginErrorCode_BadJson = 28 /*!< Cannot parse a JSON document */, + OrthancPluginErrorCode_Unauthorized = 29 /*!< Bad credentials were provided to an HTTP request */, OrthancPluginErrorCode_SQLiteNotOpened = 1000 /*!< SQLite: The database is not opened */, OrthancPluginErrorCode_SQLiteAlreadyOpened = 1001 /*!< SQLite: Connection is already open */, OrthancPluginErrorCode_SQLiteCannotOpen = 1002 /*!< SQLite: Unable to open the database */, @@ -375,6 +376,7 @@ _OrthancPluginService_ReadFile = 15, _OrthancPluginService_WriteFile = 16, _OrthancPluginService_GetErrorDescription = 17, + _OrthancPluginService_CallHttpClient = 18, /* Registration of callbacks */ _OrthancPluginService_RegisterRestCallback = 1000, @@ -1154,6 +1156,7 @@ * @param target The target memory buffer. * @param uri The URI in the built-in Orthanc API. * @return 0 if success, or the error code if failure. + * @see OrthancPluginRestApiGetAfterPlugins * @ingroup Orthanc **/ ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginRestApiGet( @@ -1182,6 +1185,7 @@ * @param target The target memory buffer. * @param uri The URI in the built-in Orthanc API. * @return 0 if success, or the error code if failure. + * @see OrthancPluginRestApiGet * @ingroup Orthanc **/ ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginRestApiGetAfterPlugins( @@ -1217,6 +1221,7 @@ * @param body The body of the POST request. * @param bodySize The size of the body. * @return 0 if success, or the error code if failure. + * @see OrthancPluginRestApiPostAfterPlugins * @ingroup Orthanc **/ ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginRestApiPost( @@ -1250,6 +1255,7 @@ * @param body The body of the POST request. * @param bodySize The size of the body. * @return 0 if success, or the error code if failure. + * @see OrthancPluginRestApiPost * @ingroup Orthanc **/ ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginRestApiPostAfterPlugins( @@ -1277,6 +1283,7 @@ * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). * @param uri The URI to delete in the built-in Orthanc API. * @return 0 if success, or the error code if failure. + * @see OrthancPluginRestApiDeleteAfterPlugins * @ingroup Orthanc **/ ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginRestApiDelete( @@ -1298,6 +1305,7 @@ * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). * @param uri The URI to delete in the built-in Orthanc API. * @return 0 if success, or the error code if failure. + * @see OrthancPluginRestApiDelete * @ingroup Orthanc **/ ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginRestApiDeleteAfterPlugins( @@ -1321,6 +1329,7 @@ * @param body The body of the PUT request. * @param bodySize The size of the body. * @return 0 if success, or the error code if failure. + * @see OrthancPluginRestApiPutAfterPlugins * @ingroup Orthanc **/ ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginRestApiPut( @@ -1355,6 +1364,7 @@ * @param body The body of the PUT request. * @param bodySize The size of the body. * @return 0 if success, or the error code if failure. + * @see OrthancPluginRestApiPut * @ingroup Orthanc **/ ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginRestApiPutAfterPlugins( @@ -3100,6 +3110,168 @@ + + typedef struct + { + OrthancPluginMemoryBuffer* target; + OrthancPluginHttpMethod method; + const char* url; + const char* username; + const char* password; + const char* body; + uint32_t bodySize; + } _OrthancPluginCallHttpClient; + + + /** + * @brief Issue a HTTP GET call. + * + * Make a HTTP GET call to the given URL. The result to the query is + * stored into a newly allocated memory buffer. Favor + * OrthancPluginRestApiGet() if calling the built-in REST API of the + * Orthanc instance that hosts this plugin. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param target The target memory buffer. + * @param url The URL of interest. + * @param username The username (can be NULL if no password protection). + * @param password The password (can be NULL if no password protection). + * @return 0 if success, or the error code if failure. + **/ + ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginHttpGet( + OrthancPluginContext* context, + OrthancPluginMemoryBuffer* target, + const char* url, + const char* username, + const char* password) + { + _OrthancPluginCallHttpClient params; + memset(¶ms, 0, sizeof(params)); + + params.target = target; + params.method = OrthancPluginHttpMethod_Get; + params.url = url; + params.username = username; + params.password = password; + + return context->InvokeService(context, _OrthancPluginService_CallHttpClient, ¶ms); + } + + + /** + * @brief Issue a HTTP POST call. + * + * Make a HTTP POST call to the given URL. The result to the query + * is stored into a newly allocated memory buffer. Favor + * OrthancPluginRestApiPost() if calling the built-in REST API of + * the Orthanc instance that hosts this plugin. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param target The target memory buffer. + * @param url The URL of interest. + * @param body The content of the body of the request. + * @param bodySize The size of the body of the request. + * @param username The username (can be NULL if no password protection). + * @param password The password (can be NULL if no password protection). + * @return 0 if success, or the error code if failure. + **/ + ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginHttpPost( + OrthancPluginContext* context, + OrthancPluginMemoryBuffer* target, + const char* url, + const char* body, + uint32_t bodySize, + const char* username, + const char* password) + { + _OrthancPluginCallHttpClient params; + memset(¶ms, 0, sizeof(params)); + + params.target = target; + params.method = OrthancPluginHttpMethod_Post; + params.url = url; + params.body = body; + params.bodySize = bodySize; + params.username = username; + params.password = password; + + return context->InvokeService(context, _OrthancPluginService_CallHttpClient, ¶ms); + } + + + /** + * @brief Issue a HTTP PUT call. + * + * Make a HTTP PUT call to the given URL. The result to the query is + * stored into a newly allocated memory buffer. Favor + * OrthancPluginRestApiPut() if calling the built-in REST API of the + * Orthanc instance that hosts this plugin. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param target The target memory buffer. + * @param url The URL of interest. + * @param body The content of the body of the request. + * @param bodySize The size of the body of the request. + * @param username The username (can be NULL if no password protection). + * @param password The password (can be NULL if no password protection). + * @return 0 if success, or the error code if failure. + **/ + ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginHttpPut( + OrthancPluginContext* context, + OrthancPluginMemoryBuffer* target, + const char* url, + const char* body, + uint32_t bodySize, + const char* username, + const char* password) + { + _OrthancPluginCallHttpClient params; + memset(¶ms, 0, sizeof(params)); + + params.target = target; + params.method = OrthancPluginHttpMethod_Put; + params.url = url; + params.body = body; + params.bodySize = bodySize; + params.username = username; + params.password = password; + + return context->InvokeService(context, _OrthancPluginService_CallHttpClient, ¶ms); + } + + + /** + * @brief Issue a HTTP DELETE call. + * + * Make a HTTP DELETE call to the given URL. Favor + * OrthancPluginRestApiDelete() if calling the built-in REST API of + * the Orthanc instance that hosts this plugin. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param url The URL of interest. + * @param username The username (can be NULL if no password protection). + * @param password The password (can be NULL if no password protection). + * @return 0 if success, or the error code if failure. + **/ + ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginHttpDelete( + OrthancPluginContext* context, + const char* url, + const char* username, + const char* password) + { + _OrthancPluginCallHttpClient params; + memset(¶ms, 0, sizeof(params)); + + params.method = OrthancPluginHttpMethod_Delete; + params.url = url; + params.username = username; + params.password = password; + + return context->InvokeService(context, _OrthancPluginService_CallHttpClient, ¶ms); + } + + + #ifdef __cplusplus } #endif diff -r fd0464ce1962 -r 31f4adefb88f Resources/ErrorCodes.json --- a/Resources/ErrorCodes.json Tue Sep 01 16:41:16 2015 +0200 +++ b/Resources/ErrorCodes.json Tue Sep 01 17:37:26 2015 +0200 @@ -163,6 +163,12 @@ "HttpStatus": 400, "Name": "BadJson", "Description": "Cannot parse a JSON document" + }, + { + "Code": 29, + "HttpStatus": 401, + "Name": "Unauthorized", + "Description": "Bad credentials were provided to an HTTP request" },