changeset 5407:3206537cbb56

HttpCompression: now disabled by default + only compress known compressible content types
author Alain Mazy <am@osimis.io>
date Sat, 04 Nov 2023 14:46:38 +0100
parents 62bb63346185
children 095c59b52f04
files NEWS OrthancFramework/Sources/Enumerations.cpp OrthancFramework/Sources/Enumerations.h OrthancFramework/Sources/HttpServer/FilesystemHttpHandler.cpp OrthancFramework/Sources/HttpServer/HttpFileSender.cpp OrthancFramework/Sources/HttpServer/HttpOutput.cpp OrthancFramework/Sources/HttpServer/HttpOutput.h OrthancFramework/Sources/SystemToolbox.cpp OrthancFramework/Sources/SystemToolbox.h OrthancServer/Resources/Configuration.json OrthancServer/Sources/main.cpp
diffstat 11 files changed, 119 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Tue Oct 17 15:06:11 2023 +0200
+++ b/NEWS	Sat Nov 04 14:46:38 2023 +0100
@@ -8,6 +8,16 @@
   - Update to rebuild the cache of the DicomWeb plugin when updating to DicomWeb 1.15.
   - New trigger configuration: "DicomWebCacheChange"
   - Fixed reading the triggers configuration.
+* HTTP Compression:
+  - The default value of the "HttpCompressionEnabled" is now false by default.  This reduces
+    the Orthanc overall CPU usage and latency.  This is suitable for setups with large  
+    bandwidth network like LAN.
+  - When "HttpCompressionEnabled" is true, only the content that is clearly identified as
+    compressible is compressed (JSON, XML, HTML, text, ...).  DICOM files are never
+    compressed over HTTP.  In prior versions, all content types were compressed.
+    This notably greatly improve loading time of large DICOM 
+    files through WADO-RS e.g in StoneViewer when working on large bandwidth networks.
+  - When "HttpCompressionEnabled" is true, content < 2KB are never compressed.
 
 REST API
 --------
--- a/OrthancFramework/Sources/Enumerations.cpp	Tue Oct 17 15:06:11 2023 +0200
+++ b/OrthancFramework/Sources/Enumerations.cpp	Sat Nov 04 14:46:38 2023 +0100
@@ -35,26 +35,6 @@
 
 namespace Orthanc
 {
-  static const char* const MIME_CSS = "text/css";
-  static const char* const MIME_DICOM = "application/dicom";
-  static const char* const MIME_GIF = "image/gif";
-  static const char* const MIME_GZIP = "application/gzip";
-  static const char* const MIME_HTML = "text/html";
-  static const char* const MIME_JAVASCRIPT = "application/javascript";
-  static const char* const MIME_JPEG2000 = "image/jp2";
-  static const char* const MIME_NACL = "application/x-nacl";
-  static const char* const MIME_PLAIN_TEXT = "text/plain";
-  static const char* const MIME_PNACL = "application/x-pnacl";
-  static const char* const MIME_SVG = "image/svg+xml";
-  static const char* const MIME_WEB_ASSEMBLY = "application/wasm";
-  static const char* const MIME_WOFF = "application/x-font-woff";
-  static const char* const MIME_WOFF2 = "font/woff2";
-  static const char* const MIME_XML_2 = "text/xml";
-  static const char* const MIME_ZIP = "application/zip";
-  static const char* const MIME_DICOM_WEB_JSON = "application/dicom+json";
-  static const char* const MIME_DICOM_WEB_XML = "application/dicom+xml";
-  static const char* const MIME_ICO = "image/x-icon";
-
   // This function is autogenerated by the script
   // "Resources/CodeGeneration/GenerateErrorCodes.py"
   const char* EnumerationToString(ErrorCode error)
--- a/OrthancFramework/Sources/Enumerations.h	Tue Oct 17 15:06:11 2023 +0200
+++ b/OrthancFramework/Sources/Enumerations.h	Sat Nov 04 14:46:38 2023 +0100
@@ -47,6 +47,27 @@
   static const char* const MIME_MTL = "model/mtl";
   static const char* const MIME_STL = "model/stl";
 
+  static const char* const MIME_CSS = "text/css";
+  static const char* const MIME_DICOM = "application/dicom";
+  static const char* const MIME_GIF = "image/gif";
+  static const char* const MIME_GZIP = "application/gzip";
+  static const char* const MIME_HTML = "text/html";
+  static const char* const MIME_JAVASCRIPT = "application/javascript";
+  static const char* const MIME_JPEG2000 = "image/jp2";
+  static const char* const MIME_NACL = "application/x-nacl";
+  static const char* const MIME_PLAIN_TEXT = "text/plain";
+  static const char* const MIME_PNACL = "application/x-pnacl";
+  static const char* const MIME_SVG = "image/svg+xml";
+  static const char* const MIME_WEB_ASSEMBLY = "application/wasm";
+  static const char* const MIME_WOFF = "application/x-font-woff";
+  static const char* const MIME_WOFF2 = "font/woff2";
+  static const char* const MIME_XML_2 = "text/xml";
+  static const char* const MIME_ZIP = "application/zip";
+  static const char* const MIME_DICOM_WEB_JSON = "application/dicom+json";
+  static const char* const MIME_DICOM_WEB_XML = "application/dicom+xml";
+  static const char* const MIME_ICO = "image/x-icon";
+
+
   /**
    * "No Internet Media Type (aka MIME type, content type) for PBM has
    * been registered with IANA, but the unofficial value
--- a/OrthancFramework/Sources/HttpServer/FilesystemHttpHandler.cpp	Tue Oct 17 15:06:11 2023 +0200
+++ b/OrthancFramework/Sources/HttpServer/FilesystemHttpHandler.cpp	Sat Nov 04 14:46:38 2023 +0100
@@ -153,7 +153,7 @@
     {
       FilesystemHttpSender sender(p);
       sender.SetContentType(SystemToolbox::AutodetectMimeType(p.string()));
-      output.Answer(sender);   // TODO COMPRESSION
+      output.Answer(sender);
     }
     else if (listDirectoryContent_ &&
              fs::exists(p) && 
--- a/OrthancFramework/Sources/HttpServer/HttpFileSender.cpp	Tue Oct 17 15:06:11 2023 +0200
+++ b/OrthancFramework/Sources/HttpServer/HttpFileSender.cpp	Sat Nov 04 14:46:38 2023 +0100
@@ -53,7 +53,8 @@
 
     if (contentType_.empty())
     {
-      contentType_ = SystemToolbox::AutodetectMimeType(filename);
+      MimeType mimeType = SystemToolbox::AutodetectMimeType(filename);
+      contentType_ = EnumerationToString(mimeType);
     }
   }
 
--- a/OrthancFramework/Sources/HttpServer/HttpOutput.cpp	Tue Oct 17 15:06:11 2023 +0200
+++ b/OrthancFramework/Sources/HttpServer/HttpOutput.cpp	Sat Nov 04 14:46:38 2023 +0100
@@ -30,6 +30,7 @@
 #include "../Logging.h"
 #include "../OrthancException.h"
 #include "../Toolbox.h"
+#include "../SystemToolbox.h"
 
 #include <iostream>
 #include <vector>
@@ -51,6 +52,7 @@
                                          unsigned int keepAliveTimeout) : 
     stream_(stream),
     state_(State_WritingHeader),
+    isContentCompressible_(false),
     status_(HttpStatus_200_Ok),
     hasContentLength_(false),
     contentLength_(0),
@@ -102,6 +104,17 @@
     AddHeader("Content-Type", contentType);
   }
 
+  void HttpOutput::StateMachine::SetContentCompressible(bool isContentCompressible)
+  {
+    isContentCompressible_ = isContentCompressible;
+  }
+
+  bool HttpOutput::StateMachine::IsContentCompressible() const
+  {
+    // We assume that all files that compress correctly (mainly JSON, XML) are clearly identified.
+    return isContentCompressible_;
+  }
+
   void HttpOutput::StateMachine::SetContentFilename(const char* filename)
   {
     // TODO Escape double quotes
@@ -275,13 +288,11 @@
 
   HttpCompression HttpOutput::GetPreferredCompression(size_t bodySize) const
   {
-#if 0
-    // TODO Do not compress small files?
-    if (bodySize < 512)
+    // Do not compress small files since there is no real size benefit.
+    if (bodySize < 2048)
     {
       return HttpCompression_None;
     }
-#endif
 
     // Prefer "gzip" over "deflate" if the choice is offered
 
@@ -368,11 +379,13 @@
   void HttpOutput::SetContentType(MimeType contentType)
   {
     stateMachine_.SetContentType(EnumerationToString(contentType));
+    stateMachine_.SetContentCompressible(SystemToolbox::IsContentCompressible(contentType));
   }
 
   void HttpOutput::SetContentType(const std::string &contentType)
   {
     stateMachine_.SetContentType(contentType.c_str());
+    stateMachine_.SetContentCompressible(SystemToolbox::IsContentCompressible(contentType));
   }
 
   void HttpOutput::SetContentFilename(const char *filename)
@@ -442,7 +455,7 @@
 
     HttpCompression compression = GetPreferredCompression(length);
 
-    if (compression == HttpCompression_None)
+    if (compression == HttpCompression_None || !IsContentCompressible())
     {
       stateMachine_.SetContentLength(length);
       stateMachine_.SendBody(buffer, length);
--- a/OrthancFramework/Sources/HttpServer/HttpOutput.h	Tue Oct 17 15:06:11 2023 +0200
+++ b/OrthancFramework/Sources/HttpServer/HttpOutput.h	Sat Nov 04 14:46:38 2023 +0100
@@ -56,6 +56,7 @@
       IHttpOutputStream& stream_;
       State state_;
 
+      bool isContentCompressible_;
       HttpStatus status_;
       bool hasContentLength_;
       uint64_t contentLength_;
@@ -82,6 +83,8 @@
 
       void SetContentType(const char* contentType);
 
+      void SetContentCompressible(bool isCompressible);
+
       void SetContentFilename(const char* filename);
 
       void SetCookie(const std::string& cookie,
@@ -110,6 +113,8 @@
         return state_;
       }
 
+      bool IsContentCompressible() const;
+
       void CheckHeadersCompatibilityWithMultipart() const;
 
       void StartStream(const std::string& contentType);
@@ -139,6 +144,11 @@
 
     bool IsGzipAllowed() const;
 
+    bool IsContentCompressible() const
+    {
+      return stateMachine_.IsContentCompressible();
+    }
+
     void SendStatus(HttpStatus status,
 		    const char* message,
 		    size_t messageSize);
--- a/OrthancFramework/Sources/SystemToolbox.cpp	Tue Oct 17 15:06:11 2023 +0200
+++ b/OrthancFramework/Sources/SystemToolbox.cpp	Sat Nov 04 14:46:38 2023 +0100
@@ -725,6 +725,52 @@
     }
   }
 
+  bool SystemToolbox::IsContentCompressible(MimeType mime)
+  {
+    switch (mime)
+    {
+      case MimeType_Css:
+      case MimeType_Html:
+      case MimeType_JavaScript:
+      case MimeType_Json:
+      case MimeType_Pam:
+      case MimeType_Pdf:
+      case MimeType_PlainText:
+      case MimeType_WebAssembly:
+      case MimeType_Xml:
+      case MimeType_PrometheusText:
+      case MimeType_DicomWebJson:
+      case MimeType_DicomWebXml:
+        return true;
+      default: // for all other (JPEG, DICOM, binary, ...)
+        return false;
+    }
+  }
+  
+  bool SystemToolbox::IsContentCompressible(const std::string& contentType)
+  {
+    if (contentType.empty())
+    {
+      return false;
+    }
+
+    if (contentType.find(MIME_JSON) != std::string::npos ||
+        contentType.find(MIME_XML) != std::string::npos ||
+        contentType.find(MIME_DICOM_WEB_JSON) != std::string::npos ||
+        contentType.find(MIME_DICOM_WEB_XML) != std::string::npos ||
+        contentType.find(MIME_PDF) != std::string::npos ||
+        contentType.find(MIME_CSS) != std::string::npos ||
+        contentType.find(MIME_HTML) != std::string::npos ||
+        contentType.find(MIME_JAVASCRIPT) != std::string::npos ||
+        contentType.find(MIME_PLAIN_TEXT) != std::string::npos ||
+        contentType.find(MIME_WEB_ASSEMBLY) != std::string::npos ||
+        contentType.find(MIME_XML_2) != std::string::npos)
+    {
+      return true;
+    }
+
+    return false;
+  }
 
   MimeType SystemToolbox::AutodetectMimeType(const std::string& path)
   {
--- a/OrthancFramework/Sources/SystemToolbox.h	Tue Oct 17 15:06:11 2023 +0200
+++ b/OrthancFramework/Sources/SystemToolbox.h	Sat Nov 04 14:46:38 2023 +0100
@@ -108,6 +108,10 @@
 
     static unsigned int GetHardwareConcurrency();
 
+    static bool IsContentCompressible(MimeType mime);
+
+    static bool IsContentCompressible(const std::string& contentType);
+
     static MimeType AutodetectMimeType(const std::string& path);
 
     static void GetEnvironmentVariables(std::map<std::string, std::string>& env);
--- a/OrthancServer/Resources/Configuration.json	Tue Oct 17 15:06:11 2023 +0200
+++ b/OrthancServer/Resources/Configuration.json	Sat Nov 04 14:46:38 2023 +0100
@@ -125,7 +125,12 @@
   // Enable HTTP compression to improve network bandwidth utilization,
   // at the expense of more computations on the server. Orthanc
   // supports the "gzip" and "deflate" HTTP encodings.
-  "HttpCompressionEnabled" : true,
+  // When working on a LAN or on localhost, you should typically set
+  // this configuration to false while when working on low-bandwidth,
+  // you should set it to true.
+  // Note in versions up to 1.12.1, the default value was "true" and is
+  // "false" since 1.12.2.
+  "HttpCompressionEnabled" : false,
 
   // Enable the publication of the content of the Orthanc server as a
   // WebDAV share (new in Orthanc 1.8.0). On the localhost, the WebDAV
--- a/OrthancServer/Sources/main.cpp	Tue Oct 17 15:06:11 2023 +0200
+++ b/OrthancServer/Sources/main.cpp	Sat Nov 04 14:46:38 2023 +0100
@@ -1031,7 +1031,7 @@
       httpServer.SetRemoteAccessAllowed(lock.GetConfiguration().GetBooleanParameter("RemoteAccessAllowed", false));
       httpServer.SetKeepAliveEnabled(lock.GetConfiguration().GetBooleanParameter("KeepAlive", defaultKeepAlive));
       httpServer.SetKeepAliveTimeout(lock.GetConfiguration().GetUnsignedIntegerParameter("KeepAliveTimeout", 1));
-      httpServer.SetHttpCompressionEnabled(lock.GetConfiguration().GetBooleanParameter("HttpCompressionEnabled", true));
+      httpServer.SetHttpCompressionEnabled(lock.GetConfiguration().GetBooleanParameter("HttpCompressionEnabled", false));
       httpServer.SetTcpNoDelay(lock.GetConfiguration().GetBooleanParameter("TcpNoDelay", true));
       httpServer.SetRequestTimeout(lock.GetConfiguration().GetUnsignedIntegerParameter("HttpRequestTimeout", 30));