changeset 1517:4f8c8ef114db

cont
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 11 Aug 2015 10:32:34 +0200
parents f09f5d3225a7
children eb46cc06389a
files CMakeLists.txt Core/HttpServer/EmbeddedResourceHttpHandler.cpp Core/HttpServer/FilesystemHttpHandler.cpp Core/HttpServer/HttpOutput.cpp Core/HttpServer/HttpOutput.h Core/HttpServer/IHttpHandler.cpp Core/HttpServer/IHttpHandler.h Core/HttpServer/MongooseServer.cpp Core/HttpServer/MongooseServer.h Core/RestApi/RestApi.cpp Core/RestApi/RestApiOutput.cpp OrthancServer/main.cpp Resources/Configuration.json
diffstat 13 files changed, 123 insertions(+), 146 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Tue Aug 11 08:53:47 2015 +0200
+++ b/CMakeLists.txt	Tue Aug 11 10:32:34 2015 +0200
@@ -104,7 +104,6 @@
   Core/HttpServer/FilesystemHttpHandler.cpp
   Core/HttpServer/HttpToolbox.cpp
   Core/HttpServer/HttpOutput.cpp
-  Core/HttpServer/IHttpHandler.cpp
   Core/HttpServer/StringHttpOutput.cpp
   Core/HttpServer/MongooseServer.cpp
   Core/HttpServer/HttpFileSender.cpp
--- a/Core/HttpServer/EmbeddedResourceHttpHandler.cpp	Tue Aug 11 08:53:47 2015 +0200
+++ b/Core/HttpServer/EmbeddedResourceHttpHandler.cpp	Tue Aug 11 10:32:34 2015 +0200
@@ -81,7 +81,7 @@
       size_t size = EmbeddedResources::GetDirectoryResourceSize(resourceId_, resourcePath.c_str());
 
       output.SetContentType(contentType.c_str());
-      output.SendBody(buffer, size, IHttpHandler::GetPreferredCompression(headers, size));
+      output.SendBody(buffer, size);
     }
     catch (OrthancException&)
     {
--- a/Core/HttpServer/FilesystemHttpHandler.cpp	Tue Aug 11 08:53:47 2015 +0200
+++ b/Core/HttpServer/FilesystemHttpHandler.cpp	Tue Aug 11 10:32:34 2015 +0200
@@ -56,8 +56,6 @@
   {
     namespace fs = boost::filesystem;
 
-    output.SetContentType("text/html");
-
     std::string s;
     s += "<html>";
     s += "  <body>";
@@ -105,7 +103,8 @@
     s += "  </body>";
     s += "</html>";
 
-    output.SendBody(s, IHttpHandler::GetPreferredCompression(headers, s.size()));
+    output.SetContentType("text/html");
+    output.SendBody(s);
   }
 
 
--- a/Core/HttpServer/HttpOutput.cpp	Tue Aug 11 08:53:47 2015 +0200
+++ b/Core/HttpServer/HttpOutput.cpp	Tue Aug 11 10:32:34 2015 +0200
@@ -215,6 +215,33 @@
   }
 
 
+  HttpCompression HttpOutput::GetPreferredCompression(size_t bodySize) const
+  {
+#if 0
+    // TODO Do not compress small files?
+    if (bodySize < 512)
+    {
+      return HttpCompression_None;
+    }
+#endif
+
+    // Prefer "gzip" over "deflate" if the choice is offered
+
+    if (isGzipAllowed_)
+    {
+      return HttpCompression_Gzip;
+    }
+    else if (isDeflateAllowed_)
+    {
+      return HttpCompression_Deflate;
+    }
+    else
+    {
+      return HttpCompression_None;
+    }
+  }
+
+
   void HttpOutput::SendMethodNotAllowed(const std::string& allowed)
   {
     stateMachine_.ClearHeaders();
@@ -259,8 +286,7 @@
   }
 
   void HttpOutput::SendBody(const void* buffer, 
-                            size_t length,
-                            HttpCompression compression)
+                            size_t length)
   {
     if (length == 0)
     {
@@ -268,6 +294,8 @@
     }
     else
     {
+      HttpCompression compression = GetPreferredCompression(length);
+
       switch (compression)
       {
         case HttpCompression_None:
@@ -318,10 +346,9 @@
     }
   }
 
-  void HttpOutput::SendBody(const std::string& str,
-                            HttpCompression compression)
+  void HttpOutput::SendBody(const std::string& str)
   {
-    SendBody(str.size() == 0 ? NULL : str.c_str(), str.size(), compression);
+    SendBody(str.size() == 0 ? NULL : str.c_str(), str.size());
   }
 
   void HttpOutput::SendBody()
--- a/Core/HttpServer/HttpOutput.h	Tue Aug 11 08:53:47 2015 +0200
+++ b/Core/HttpServer/HttpOutput.h	Tue Aug 11 10:32:34 2015 +0200
@@ -109,12 +109,38 @@
     };
 
     StateMachine stateMachine_;
+    bool         isDeflateAllowed_;
+    bool         isGzipAllowed_;
+
+    HttpCompression GetPreferredCompression(size_t bodySize) const;
 
   public:
     HttpOutput(IHttpOutputStream& stream,
                bool isKeepAlive) : 
-      stateMachine_(stream, isKeepAlive)
+      stateMachine_(stream, isKeepAlive),
+      isDeflateAllowed_(false),
+      isGzipAllowed_(false)
+    {
+    }
+
+    void SetDeflateAllowed(bool allowed)
     {
+      isDeflateAllowed_ = allowed;
+    }
+
+    bool IsDeflateAllowed() const
+    {
+      return isDeflateAllowed_;
+    }
+
+    void SetGzipAllowed(bool allowed)
+    {
+      isGzipAllowed_ = allowed;
+    }
+
+    bool IsGzipAllowed() const
+    {
+      return isGzipAllowed_;
     }
 
     void SendStatus(HttpStatus status);
@@ -147,11 +173,9 @@
     }
 
     void SendBody(const void* buffer, 
-                  size_t length,
-                  HttpCompression compression = HttpCompression_None);
+                  size_t length);
 
-    void SendBody(const std::string& str,
-                  HttpCompression compression = HttpCompression_None);
+    void SendBody(const std::string& str);
 
     void SendBody();
 
--- a/Core/HttpServer/IHttpHandler.cpp	Tue Aug 11 08:53:47 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,104 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "../PrecompiledHeaders.h"
-#include "IHttpHandler.h"
-
-#include "../Toolbox.h"
-
-namespace Orthanc
-{
-  void IHttpHandler::GetAcceptedCompressions(std::set<HttpCompression>& result,
-                                             const Arguments& headers)
-  {
-    // Look if the client wishes HTTP compression
-    // https://en.wikipedia.org/wiki/HTTP_compression
-    Arguments::const_iterator it = headers.find("accept-encoding");
-    if (it != headers.end())
-    {
-      std::vector<std::string> encodings;
-      Toolbox::TokenizeString(encodings, it->second, ',');
-      for (size_t i = 0; i < encodings.size(); i++)
-      {
-        std::string s = Toolbox::StripSpaces(encodings[i]);
-
-        if (s == "deflate")
-        {
-          result.insert(HttpCompression_Deflate);
-        }
-        else if (s == "gzip")
-        {
-          result.insert(HttpCompression_Gzip);
-        }
-      }
-    }
-  }
-
-
-  HttpCompression IHttpHandler::GetPreferredCompression(const Arguments& headers,
-                                                        size_t bodySize)
-  {
-#if 0
-    // TODO
-    if (bodySize < 1024)
-    {
-      // Do not compress small answers
-      return HttpCompression_None;
-    }
-#endif
-
-    HttpCompression result = HttpCompression_None;
-
-    Arguments::const_iterator it = headers.find("accept-encoding");
-    if (it != headers.end())
-    {
-      std::vector<std::string> encodings;
-      Toolbox::TokenizeString(encodings, it->second, ',');
-      for (size_t i = 0; i < encodings.size(); i++)
-      {
-        std::string s = Toolbox::StripSpaces(encodings[i]);
-
-        if (s == "deflate" &&
-            result != HttpCompression_Gzip)  // Always prefer "gzip" over "deflate"
-        {
-          result = HttpCompression_Deflate;
-        }
-        else if (s == "gzip")
-        {
-          result = HttpCompression_Gzip;
-        }
-      }
-    }
-
-    return result;
-  }
-}
--- a/Core/HttpServer/IHttpHandler.h	Tue Aug 11 08:53:47 2015 +0200
+++ b/Core/HttpServer/IHttpHandler.h	Tue Aug 11 10:32:34 2015 +0200
@@ -59,11 +59,5 @@
                         const GetArguments& getArguments,
                         const char* bodyData,
                         size_t bodySize) = 0;
-
-    static void GetAcceptedCompressions(std::set<HttpCompression>& result,
-                                        const Arguments& headers);
-
-    static HttpCompression GetPreferredCompression(const Arguments& headers,
-                                                   size_t bodySize);
   };
 }
--- a/Core/HttpServer/MongooseServer.cpp	Tue Aug 11 08:53:47 2015 +0200
+++ b/Core/HttpServer/MongooseServer.cpp	Tue Aug 11 10:32:34 2015 +0200
@@ -548,6 +548,34 @@
   }
 
 
+  static void ConfigureHttpCompression(HttpOutput& output,
+                                       const IHttpHandler::Arguments& headers)
+  {
+    // Look if the client wishes HTTP compression
+    // https://en.wikipedia.org/wiki/HTTP_compression
+    IHttpHandler::Arguments::const_iterator it = headers.find("accept-encoding");
+    if (it != headers.end())
+    {
+      std::vector<std::string> encodings;
+      Toolbox::TokenizeString(encodings, it->second, ',');
+
+      for (size_t i = 0; i < encodings.size(); i++)
+      {
+        std::string s = Toolbox::StripSpaces(encodings[i]);
+
+        if (s == "deflate")
+        {
+          output.SetDeflateAllowed(true);
+        }
+        else if (s == "gzip")
+        {
+          output.SetGzipAllowed(true);
+        }
+      }
+    }
+  }
+
+
   static void InternalCallback(struct mg_connection *connection,
                                const struct mg_request_info *request)
   {
@@ -574,6 +602,11 @@
       headers.insert(std::make_pair(name, request->http_headers[i].value));
     }
 
+    if (that->IsHttpCompressionEnabled())
+    {
+      ConfigureHttpCompression(output, headers);
+    }
+
 
     // Extract the GET arguments
     IHttpHandler::GetArguments argumentsGET;
@@ -799,6 +832,7 @@
     port_ = 8000;
     filter_ = NULL;
     keepAlive_ = false;
+    httpCompression_ = true;
 
 #if ORTHANC_SSL_ENABLED == 1
     // Check for the Heartbleed exploit
@@ -942,6 +976,12 @@
     remoteAllowed_ = allowed;
   }
 
+  void MongooseServer::SetHttpCompressionEnabled(bool enabled)
+  {
+    Stop();
+    httpCompression_ = enabled;
+  }
+
   void MongooseServer::SetIncomingHttpRequestFilter(IIncomingHttpRequestFilter& filter)
   {
     Stop();
--- a/Core/HttpServer/MongooseServer.h	Tue Aug 11 08:53:47 2015 +0200
+++ b/Core/HttpServer/MongooseServer.h	Tue Aug 11 10:32:34 2015 +0200
@@ -76,6 +76,7 @@
     uint16_t port_;
     IIncomingHttpRequestFilter* filter_;
     bool keepAlive_;
+    bool httpCompression_;
   
     bool IsRunning() const;
 
@@ -135,6 +136,13 @@
 
     void SetRemoteAccessAllowed(bool allowed);
 
+    bool IsHttpCompressionEnabled() const
+    {
+      return httpCompression_;;
+    }
+
+    void SetHttpCompressionEnabled(bool enabled);
+
     const IIncomingHttpRequestFilter* GetIncomingHttpRequestFilter() const
     {
       return filter_;
--- a/Core/RestApi/RestApi.cpp	Tue Aug 11 08:53:47 2015 +0200
+++ b/Core/RestApi/RestApi.cpp	Tue Aug 11 10:32:34 2015 +0200
@@ -194,20 +194,6 @@
     }
 #endif
 
-    std::set<HttpCompression> compressions;
-    GetAcceptedCompressions(compressions, headers);
-
-    if (compressions.find(HttpCompression_Deflate) != compressions.end())
-    {
-      wrappedOutput.AllowDeflateCompression(true);
-    }
-
-    if (compressions.find(HttpCompression_Gzip) != compressions.end())
-    {
-      wrappedOutput.AllowGzipCompression(true);
-    }
-
-
     Arguments compiled;
     HttpToolbox::CompileGetArguments(compiled, getArguments);
 
--- a/Core/RestApi/RestApiOutput.cpp	Tue Aug 11 08:53:47 2015 +0200
+++ b/Core/RestApi/RestApiOutput.cpp	Tue Aug 11 10:32:34 2015 +0200
@@ -117,13 +117,13 @@
   {
     CheckStatus();
 
-    std::string s;
-
     if (convertJsonToXml_)
     {
 #if ORTHANC_PUGIXML_ENABLED == 1
+      std::string s;
       Toolbox::JsonToXml(s, value);
       output_.SetContentType("application/xml");
+      output_.SendBody(s);
 #else
       LOG(ERROR) << "Orthanc was compiled without XML support";
       throw OrthancException(ErrorCode_InternalError);
@@ -133,11 +133,9 @@
     {
       Json::StyledWriter writer;
       output_.SetContentType("application/json");
-      s = writer.write(value);
+      output_.SendBody(writer.write(value));
     }
 
-    output_.SendBody(s, GetPreferredCompression(s.size()));
-
     alreadySent_ = true;
   }
 
@@ -154,7 +152,7 @@
   {
     CheckStatus();
     output_.SetContentType(contentType.c_str());
-    output_.SendBody(buffer, length, GetPreferredCompression(length));
+    output_.SendBody(buffer, length);
     alreadySent_ = true;
   }
 
--- a/OrthancServer/main.cpp	Tue Aug 11 08:53:47 2015 +0200
+++ b/OrthancServer/main.cpp	Tue Aug 11 10:32:34 2015 +0200
@@ -421,6 +421,7 @@
   httpServer.SetPortNumber(Configuration::GetGlobalIntegerParameter("HttpPort", 8042));
   httpServer.SetRemoteAccessAllowed(Configuration::GetGlobalBoolParameter("RemoteAccessAllowed", false));
   httpServer.SetKeepAliveEnabled(Configuration::GetGlobalBoolParameter("KeepAlive", false));
+  httpServer.SetHttpCompressionEnabled(Configuration::GetGlobalBoolParameter("HttpCompressionEnabled", true));
   httpServer.SetIncomingHttpRequestFilter(httpFilter);
 
   httpServer.SetAuthenticationEnabled(Configuration::GetGlobalBoolParameter("AuthenticationEnabled", false));
--- a/Resources/Configuration.json	Tue Aug 11 08:53:47 2015 +0200
+++ b/Resources/Configuration.json	Tue Aug 11 10:32:34 2015 +0200
@@ -232,5 +232,10 @@
   // When handling a C-Find SCP request, setting this flag to "false"
   // will enable case-insensitive match for PN value representation
   // (such as PatientName). By default, the search is case-insensitive.
-  "CaseSensitivePN" : false
+  "CaseSensitivePN" : false,
+
+  // Enable HTTP compression to improve network bandwidth utilization,
+  // at the expense of more computations on the server. Orthanc
+  // supports the "gzip" and "deflate" encodings.
+  "HttpCompressionEnabled" : true
 }