# HG changeset patch # User Sebastien Jodogne # Date 1405605340 -7200 # Node ID 8d1845feb27732d5a2f84643099ee137b924962a # Parent 2c49b7dffcec384f905899806274e36c6d552001 set cookies, not allowed methods, unauthorized in plugins diff -r 2c49b7dffcec -r 8d1845feb277 Core/HttpServer/EmbeddedResourceHttpHandler.cpp --- a/Core/HttpServer/EmbeddedResourceHttpHandler.cpp Thu Jul 17 14:14:15 2014 +0200 +++ b/Core/HttpServer/EmbeddedResourceHttpHandler.cpp Thu Jul 17 15:55:40 2014 +0200 @@ -67,7 +67,7 @@ if (method != HttpMethod_Get) { - output.SendMethodNotAllowedError("GET"); + output.SendMethodNotAllowed("GET"); return true; } diff -r 2c49b7dffcec -r 8d1845feb277 Core/HttpServer/FilesystemHttpHandler.cpp --- a/Core/HttpServer/FilesystemHttpHandler.cpp Thu Jul 17 14:14:15 2014 +0200 +++ b/Core/HttpServer/FilesystemHttpHandler.cpp Thu Jul 17 15:55:40 2014 +0200 @@ -136,7 +136,7 @@ if (method != HttpMethod_Get) { - output.SendMethodNotAllowedError("GET"); + output.SendMethodNotAllowed("GET"); return true; } diff -r 2c49b7dffcec -r 8d1845feb277 Core/HttpServer/HttpOutput.cpp --- a/Core/HttpServer/HttpOutput.cpp Thu Jul 17 14:14:15 2014 +0200 +++ b/Core/HttpServer/HttpOutput.cpp Thu Jul 17 15:55:40 2014 +0200 @@ -36,6 +36,7 @@ #include #include #include +#include #include #include "../OrthancException.h" #include "../Toolbox.h" @@ -46,7 +47,8 @@ { if (state_ != State_WaitingHttpStatus) { - throw OrthancException(ErrorCode_BadSequenceOfCalls); + LOG(ERROR) << "Sending twice an HTTP status"; + return; } stream_.OnHttpStatusReceived(status); @@ -153,11 +155,17 @@ s += it->first + ": " + it->second + "\r\n"; } + for (HttpHandler::Arguments::const_iterator + it = cookies_.begin(); it != cookies_.end(); ++it) + { + s += "Set-Cookie: " + it->first + "=" + it->second + "\r\n"; + } + stateMachine_.SendHeaderString(s); } - void HttpOutput::SendMethodNotAllowedError(const std::string& allowed) + void HttpOutput::SendMethodNotAllowed(const std::string& allowed) { stateMachine_.SendHttpStatus(HttpStatus_405_MethodNotAllowed); stateMachine_.SendHeaderString("Allow: " + allowed + "\r\n"); @@ -181,29 +189,8 @@ void HttpOutput::AnswerBufferWithContentType(const std::string& buffer, const std::string& contentType) { - SendOkHeader(contentType.c_str(), true, buffer.size(), NULL); - SendBodyString(buffer); - } - - - void HttpOutput::PrepareCookies(Header& header, - const HttpHandler::Arguments& cookies) - { - for (HttpHandler::Arguments::const_iterator it = cookies.begin(); - it != cookies.end(); ++it) - { - header.push_back(std::make_pair("Set-Cookie", it->first + "=" + it->second)); - } - } - - - void HttpOutput::AnswerBufferWithContentType(const std::string& buffer, - const std::string& contentType, - const HttpHandler::Arguments& cookies) - { Header header; PrepareOkHeader(header, contentType.c_str(), true, buffer.size(), NULL); - PrepareCookies(header, cookies); SendOkHeader(header); SendBodyString(buffer); } @@ -213,19 +200,8 @@ size_t size, const std::string& contentType) { - SendOkHeader(contentType.c_str(), true, size, NULL); - SendBodyData(buffer, size); - } - - - void HttpOutput::AnswerBufferWithContentType(const void* buffer, - size_t size, - const std::string& contentType, - const HttpHandler::Arguments& cookies) - { Header header; PrepareOkHeader(header, contentType.c_str(), true, size, NULL); - PrepareCookies(header, cookies); SendOkHeader(header); SendBodyData(buffer, size); } diff -r 2c49b7dffcec -r 8d1845feb277 Core/HttpServer/HttpOutput.h --- a/Core/HttpServer/HttpOutput.h Thu Jul 17 14:14:15 2014 +0200 +++ b/Core/HttpServer/HttpOutput.h Thu Jul 17 15:55:40 2014 +0200 @@ -85,10 +85,8 @@ void SendOkHeader(const Header& header); - void PrepareCookies(Header& header, - const HttpHandler::Arguments& cookies); - StateMachine stateMachine_; + HttpHandler::Arguments cookies_; public: HttpOutput(IHttpOutputStream& stream) : stateMachine_(stream) @@ -110,7 +108,7 @@ stateMachine_.SendBodyString(str); } - void SendMethodNotAllowedError(const std::string& allowed); + void SendMethodNotAllowed(const std::string& allowed); void SendHeader(HttpStatus status); @@ -118,22 +116,19 @@ void SendUnauthorized(const std::string& realm); + void SetCookie(const std::string& cookie, + const std::string& value) + { + cookies_[cookie] = value; + } + // Higher-level constructs to send entire buffers ---------------------------- void AnswerBufferWithContentType(const std::string& buffer, const std::string& contentType); - void AnswerBufferWithContentType(const std::string& buffer, - const std::string& contentType, - const HttpHandler::Arguments& cookies); - void AnswerBufferWithContentType(const void* buffer, size_t size, const std::string& contentType); - - void AnswerBufferWithContentType(const void* buffer, - size_t size, - const std::string& contentType, - const HttpHandler::Arguments& cookies); }; } diff -r 2c49b7dffcec -r 8d1845feb277 Core/HttpServer/MongooseServer.cpp --- a/Core/HttpServer/MongooseServer.cpp Thu Jul 17 14:14:15 2014 +0200 +++ b/Core/HttpServer/MongooseServer.cpp Thu Jul 17 15:55:40 2014 +0200 @@ -714,7 +714,13 @@ if (!found) { - output.SendHeader(HttpStatus_404_NotFound); + try + { + output.SendHeader(HttpStatus_404_NotFound); + } + catch (OrthancException&) + { + } } // Mark as processed diff -r 2c49b7dffcec -r 8d1845feb277 Core/RestApi/RestApi.cpp --- a/Core/RestApi/RestApi.cpp Thu Jul 17 14:14:15 2014 +0200 +++ b/Core/RestApi/RestApi.cpp Thu Jul 17 15:55:40 2014 +0200 @@ -192,7 +192,7 @@ LOG(INFO) << "REST method " << EnumerationToString(method) << " not allowed on: " << Toolbox::FlattenUri(uri); - output.SendMethodNotAllowedError(MethodsToString(methods)); + output.SendMethodNotAllowed(MethodsToString(methods)); return true; } diff -r 2c49b7dffcec -r 8d1845feb277 Core/RestApi/RestApiOutput.cpp --- a/Core/RestApi/RestApiOutput.cpp Thu Jul 17 14:14:15 2014 +0200 +++ b/Core/RestApi/RestApiOutput.cpp Thu Jul 17 15:55:40 2014 +0200 @@ -73,7 +73,7 @@ CheckStatus(); Json::StyledWriter writer; std::string s = writer.write(value); - output_.AnswerBufferWithContentType(s, "application/json", cookies_); + output_.AnswerBufferWithContentType(s, "application/json"); alreadySent_ = true; } @@ -81,7 +81,7 @@ const std::string& contentType) { CheckStatus(); - output_.AnswerBufferWithContentType(buffer, contentType, cookies_); + output_.AnswerBufferWithContentType(buffer, contentType); alreadySent_ = true; } @@ -90,7 +90,7 @@ const std::string& contentType) { CheckStatus(); - output_.AnswerBufferWithContentType(buffer, length, contentType, cookies_); + output_.AnswerBufferWithContentType(buffer, length, contentType); alreadySent_ = true; } @@ -135,7 +135,7 @@ v += ";max-age=" + boost::lexical_cast(maxAge); } - cookies_[name] = v; + output_.SetCookie(name, v); } void RestApiOutput::ResetCookie(const std::string& name) diff -r 2c49b7dffcec -r 8d1845feb277 Core/RestApi/RestApiOutput.h --- a/Core/RestApi/RestApiOutput.h Thu Jul 17 14:14:15 2014 +0200 +++ b/Core/RestApi/RestApiOutput.h Thu Jul 17 15:55:40 2014 +0200 @@ -44,7 +44,6 @@ private: HttpOutput& output_; bool alreadySent_; - HttpHandler::Arguments cookies_; void CheckStatus(); diff -r 2c49b7dffcec -r 8d1845feb277 NEWS --- a/NEWS Thu Jul 17 14:14:15 2014 +0200 +++ b/NEWS Thu Jul 17 15:55:40 2014 +0200 @@ -2,6 +2,7 @@ =============================== * Lookup for DICOM UIDs in the plugin SDK +* Plugins have access to the HTTP headers and can answer with HTTP status codes Version 0.8.0 (2014/07/10) diff -r 2c49b7dffcec -r 8d1845feb277 Plugins/Engine/PluginsHttpHandler.cpp --- a/Plugins/Engine/PluginsHttpHandler.cpp Thu Jul 17 14:14:15 2014 +0200 +++ b/Plugins/Engine/PluginsHttpHandler.cpp Thu Jul 17 15:55:40 2014 +0200 @@ -232,14 +232,14 @@ if (error < 0) { - LOG(ERROR) << "Plugin failed with error code " << error; + LOG(ERROR) << "Plugin callback failed with error code " << error; return false; } else { if (error > 0) { - LOG(WARNING) << "Plugin finished with warning code " << error; + LOG(WARNING) << "Plugin callback finished with warning code " << error; } return true; @@ -310,11 +310,51 @@ void PluginsHttpHandler::Redirect(const void* parameters) { - const _OrthancPluginRedirect& p = - *reinterpret_cast(parameters); + const _OrthancPluginOutputPlusArgument& p = + *reinterpret_cast(parameters); + + HttpOutput* translatedOutput = reinterpret_cast(p.output); + translatedOutput->Redirect(p.argument); + } + + + void PluginsHttpHandler::SendHttpStatusCode(const void* parameters) + { + const _OrthancPluginSendHttpStatusCode& p = + *reinterpret_cast(parameters); HttpOutput* translatedOutput = reinterpret_cast(p.output); - translatedOutput->Redirect(p.redirection); + translatedOutput->SendHeader(static_cast(p.status)); + } + + + void PluginsHttpHandler::SendUnauthorized(const void* parameters) + { + const _OrthancPluginOutputPlusArgument& p = + *reinterpret_cast(parameters); + + HttpOutput* translatedOutput = reinterpret_cast(p.output); + translatedOutput->SendUnauthorized(p.argument); + } + + + void PluginsHttpHandler::SendMethodNotAllowed(const void* parameters) + { + const _OrthancPluginOutputPlusArgument& p = + *reinterpret_cast(parameters); + + HttpOutput* translatedOutput = reinterpret_cast(p.output); + translatedOutput->SendMethodNotAllowed(p.argument); + } + + + void PluginsHttpHandler::SetCookie(const void* parameters) + { + const _OrthancPluginSetCookie& p = + *reinterpret_cast(parameters); + + HttpOutput* translatedOutput = reinterpret_cast(p.output); + translatedOutput->SetCookie(p.cookie, p.value); } @@ -568,6 +608,22 @@ Redirect(parameters); return true; + case _OrthancPluginService_SendUnauthorized: + SendUnauthorized(parameters); + return true; + + case _OrthancPluginService_SendMethodNotAllowed: + SendMethodNotAllowed(parameters); + return true; + + case _OrthancPluginService_SendHttpStatusCode: + SendHttpStatusCode(parameters); + return true; + + case _OrthancPluginService_SetCookie: + SetCookie(parameters); + return true; + case _OrthancPluginService_LookupPatient: LookupResource(ResourceType_Patient, parameters); return true; diff -r 2c49b7dffcec -r 8d1845feb277 Plugins/Engine/PluginsHttpHandler.h --- a/Plugins/Engine/PluginsHttpHandler.h Thu Jul 17 14:14:15 2014 +0200 +++ b/Plugins/Engine/PluginsHttpHandler.h Thu Jul 17 15:55:40 2014 +0200 @@ -71,6 +71,14 @@ void LookupResource(ResourceType level, const void* parameters); + void SendHttpStatusCode(const void* parameters); + + void SendUnauthorized(const void* parameters); + + void SendMethodNotAllowed(const void* parameters); + + void SetCookie(const void* parameters); + public: PluginsHttpHandler(ServerContext& context); diff -r 2c49b7dffcec -r 8d1845feb277 Plugins/OrthancCPlugin/OrthancCPlugin.h --- a/Plugins/OrthancCPlugin/OrthancCPlugin.h Thu Jul 17 14:14:15 2014 +0200 +++ b/Plugins/OrthancCPlugin/OrthancCPlugin.h Thu Jul 17 15:55:40 2014 +0200 @@ -243,6 +243,10 @@ _OrthancPluginService_AnswerBuffer = 2000, _OrthancPluginService_CompressAndAnswerPngImage = 2001, _OrthancPluginService_Redirect = 2002, + _OrthancPluginService_SendHttpStatusCode = 2003, + _OrthancPluginService_SendUnauthorized = 2004, + _OrthancPluginService_SendMethodNotAllowed = 2005, + _OrthancPluginService_SetCookie = 2006, /* Access to the Orthanc database and API */ _OrthancPluginService_GetDicomForInstance = 3000, @@ -773,14 +777,15 @@ } + typedef struct { OrthancPluginRestOutput* output; - const char* redirection; - } _OrthancPluginRedirect; + const char* argument; + } _OrthancPluginOutputPlusArgument; /** - * @brief Redirect a GET request. + * @brief Redirect a REST request. * * This function answers to a REST request by redirecting the user * to another URI using HTTP status 301. @@ -794,9 +799,9 @@ OrthancPluginRestOutput* output, const char* redirection) { - _OrthancPluginRedirect params; + _OrthancPluginOutputPlusArgument params; params.output = output; - params.redirection = redirection; + params.argument = redirection; context->InvokeService(context, _OrthancPluginService_Redirect, ¶ms); } @@ -932,6 +937,114 @@ } + + typedef struct + { + OrthancPluginRestOutput* output; + uint16_t status; + } _OrthancPluginSendHttpStatusCode; + + /** + * @brief Send a HTTP status code. + * + * This function answers to a REST request by sending a HTTP status + * code (such as "400 - Bad Request"). Note that: + * - Successful requests (status 200) must use ::OrthancPluginAnswerBuffer(). + * - Redirections (status 301) must use ::OrthancPluginRedirect(). + * - Unauthorized access (status 401) must use ::OrthancPluginSendUnauthorized(). + * - Methods not allowed (status 405) must use ::OrthancPluginSendMethodNotAllowed(). + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param output The HTTP connection to the client application. + * @param status The HTTP status code to be sent. + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginSendHttpStatusCode( + OrthancPluginContext* context, + OrthancPluginRestOutput* output, + uint16_t status) + { + _OrthancPluginSendHttpStatusCode params; + params.output = output; + params.status = status; + context->InvokeService(context, _OrthancPluginService_SendHttpStatusCode, ¶ms); + } + + + /** + * @brief Signal that a REST request is not authorized. + * + * This function answers to a REST request by signaling that it is + * not authorized. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param output The HTTP connection to the client application. + * @param realm The realm for the authorization process. + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginSendUnauthorized( + OrthancPluginContext* context, + OrthancPluginRestOutput* output, + const char* realm) + { + _OrthancPluginOutputPlusArgument params; + params.output = output; + params.argument = realm; + context->InvokeService(context, _OrthancPluginService_SendUnauthorized, ¶ms); + } + + + /** + * @brief Signal that this URI does not support this HTTP method. + * + * This function answers to a REST request by signaling that the + * queried URI does not support this method. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param output The HTTP connection to the client application. + * @param allowedMethods The allowed methods for this URI (e.g. "GET,POST" after a PUT or a POST request). + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginSendMethodNotAllowed( + OrthancPluginContext* context, + OrthancPluginRestOutput* output, + const char* allowedMethods) + { + _OrthancPluginOutputPlusArgument params; + params.output = output; + params.argument = allowedMethods; + context->InvokeService(context, _OrthancPluginService_SendMethodNotAllowed, ¶ms); + } + + + typedef struct + { + OrthancPluginRestOutput* output; + const char* cookie; + const char* value; + } _OrthancPluginSetCookie; + + /** + * @brief Set a cookie. + * + * This function sets a cookie in the HTTP client. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param output The HTTP connection to the client application. + * @param cookie The cookie to be set. + * @param value The value of the cookie. + **/ + ORTHANC_PLUGIN_INLINE void OrthancPluginSetCookie( + OrthancPluginContext* context, + OrthancPluginRestOutput* output, + const char* cookie, + const char* value) + { + _OrthancPluginSetCookie params; + params.output = output; + params.cookie = cookie; + params.value = value; + context->InvokeService(context, _OrthancPluginService_SetCookie, ¶ms); + } + + #ifdef __cplusplus } #endif diff -r 2c49b7dffcec -r 8d1845feb277 Plugins/Samples/Basic/Plugin.c --- a/Plugins/Samples/Basic/Plugin.c Thu Jul 17 14:14:15 2014 +0200 +++ b/Plugins/Samples/Basic/Plugin.c Thu Jul 17 15:55:40 2014 +0200 @@ -43,6 +43,7 @@ sprintf(buffer, "Callback on URL [%s] with body [%s]\n", url, request->body); OrthancPluginLogWarning(context, buffer); + OrthancPluginSetCookie(context, output, "hello", "world"); OrthancPluginAnswerBuffer(context, output, buffer, strlen(buffer), "text/plain"); OrthancPluginLogWarning(context, ""); @@ -69,7 +70,7 @@ OrthancPluginLogWarning(context, buffer); } - OrthancPluginLogWarning(context, ""); + OrthancPluginLogWarning(context, ""); return 1; } @@ -84,6 +85,12 @@ uint16_t buffer[256 * 256]; uint32_t x, y, value; + if (request->method != OrthancPluginHttpMethod_Get) + { + OrthancPluginSendMethodNotAllowed(context, output, "GET"); + return -1; + } + value = 0; for (y = 0; y < 256; y++) {