changeset 1042:8d1845feb277

set cookies, not allowed methods, unauthorized in plugins
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 17 Jul 2014 15:55:40 +0200
parents 2c49b7dffcec
children be849646b2bf
files Core/HttpServer/EmbeddedResourceHttpHandler.cpp Core/HttpServer/FilesystemHttpHandler.cpp Core/HttpServer/HttpOutput.cpp Core/HttpServer/HttpOutput.h Core/HttpServer/MongooseServer.cpp Core/RestApi/RestApi.cpp Core/RestApi/RestApiOutput.cpp Core/RestApi/RestApiOutput.h NEWS Plugins/Engine/PluginsHttpHandler.cpp Plugins/Engine/PluginsHttpHandler.h Plugins/OrthancCPlugin/OrthancCPlugin.h Plugins/Samples/Basic/Plugin.c
diffstat 13 files changed, 228 insertions(+), 67 deletions(-) [+]
line wrap: on
line diff
--- 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;
     }
 
--- 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;
     }
 
--- 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 <iostream>
 #include <vector>
 #include <stdio.h>
+#include <glog/logging.h>
 #include <boost/lexical_cast.hpp>
 #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);
   }
--- 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);
   };
 }
--- 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
--- 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;
     }
--- 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<std::string>(maxAge);
     }
 
-    cookies_[name] = v;
+    output_.SetCookie(name, v);
   }
 
   void RestApiOutput::ResetCookie(const std::string& name)
--- 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();
 
--- 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)
--- 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<const _OrthancPluginRedirect*>(parameters);
+    const _OrthancPluginOutputPlusArgument& p = 
+      *reinterpret_cast<const _OrthancPluginOutputPlusArgument*>(parameters);
+
+    HttpOutput* translatedOutput = reinterpret_cast<HttpOutput*>(p.output);
+    translatedOutput->Redirect(p.argument);
+  }
+
+
+  void PluginsHttpHandler::SendHttpStatusCode(const void* parameters)
+  {
+    const _OrthancPluginSendHttpStatusCode& p = 
+      *reinterpret_cast<const _OrthancPluginSendHttpStatusCode*>(parameters);
 
     HttpOutput* translatedOutput = reinterpret_cast<HttpOutput*>(p.output);
-    translatedOutput->Redirect(p.redirection);
+    translatedOutput->SendHeader(static_cast<HttpStatus>(p.status));
+  }
+
+
+  void PluginsHttpHandler::SendUnauthorized(const void* parameters)
+  {
+    const _OrthancPluginOutputPlusArgument& p = 
+      *reinterpret_cast<const _OrthancPluginOutputPlusArgument*>(parameters);
+
+    HttpOutput* translatedOutput = reinterpret_cast<HttpOutput*>(p.output);
+    translatedOutput->SendUnauthorized(p.argument);
+  }
+
+
+  void PluginsHttpHandler::SendMethodNotAllowed(const void* parameters)
+  {
+    const _OrthancPluginOutputPlusArgument& p = 
+      *reinterpret_cast<const _OrthancPluginOutputPlusArgument*>(parameters);
+
+    HttpOutput* translatedOutput = reinterpret_cast<HttpOutput*>(p.output);
+    translatedOutput->SendMethodNotAllowed(p.argument);
+  }
+
+
+  void PluginsHttpHandler::SetCookie(const void* parameters)
+  {
+    const _OrthancPluginSetCookie& p = 
+      *reinterpret_cast<const _OrthancPluginSetCookie*>(parameters);
+
+    HttpOutput* translatedOutput = reinterpret_cast<HttpOutput*>(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;
--- 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);
 
--- 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, &params);
   }
 
@@ -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, &params);
+  }
+
+
+  /**
+   * @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, &params);
+  }
+
+
+  /**
+   * @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, &params);
+  }
+
+
+  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, &params);
+  }
+
+
 #ifdef  __cplusplus
 }
 #endif
--- 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++)
   {