trafficserver-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bc...@apache.org
Subject [trafficserver] branch 9.1.x updated: Updates to webp_transform to convert webp to jpeg (#8005)
Date Mon, 19 Jul 2021 19:05:03 GMT
This is an automated email from the ASF dual-hosted git repository.

bcall pushed a commit to branch 9.1.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/9.1.x by this push:
     new c98b986  Updates to webp_transform to convert webp to jpeg (#8005)
c98b986 is described below

commit c98b9869b737b62084557f9bf6b37eeaf741d8c1
Author: Bryan Call <bcall@apache.org>
AuthorDate: Thu Jul 15 09:19:47 2021 -0700

    Updates to webp_transform to convert webp to jpeg (#8005)
    
    Co-authored-by: Siva Renganathan <siva.rg@protonmail.com>
    (cherry picked from commit b2004604e00bc7216856ea5e79082b4e53118a76)
---
 doc/admin-guide/plugins/webp_transform.en.rst      |   7 +-
 .../experimental/webp_transform/ImageTransform.cc  | 148 ++++++++++++++++++---
 plugins/experimental/webp_transform/Makefile.inc   |  12 +-
 3 files changed, 138 insertions(+), 29 deletions(-)

diff --git a/doc/admin-guide/plugins/webp_transform.en.rst b/doc/admin-guide/plugins/webp_transform.en.rst
index 1fa2262..f729a59 100644
--- a/doc/admin-guide/plugins/webp_transform.en.rst
+++ b/doc/admin-guide/plugins/webp_transform.en.rst
@@ -22,16 +22,17 @@
 Webp Transform Plugin
 *********************
 
-This plugin converts jpeg and png images and transforms them into webp format.
+This plugin converts jpeg and png images and transforms them into webp format for browsers
that support webp.
+Also, the plugin converts webp images and transforms them to jpeg for browsers that don't
support webp
 All response with content-type 'image/jpeg' or 'image/png' will go through the transform.
-Content-type is changed to 'image/webp' on successful transformation.
+Content-type is changed to 'image/webp' or 'image/jpeg' on successful transformation.
 
 Installation
 ============
 
 Add the following line to :file:`plugin.config`::
 
-    webp_transform.so
+    webp_transform.so [convert_to_jpeg,convert_to_webp]
 
 
 Note
diff --git a/plugins/experimental/webp_transform/ImageTransform.cc b/plugins/experimental/webp_transform/ImageTransform.cc
index 88d19cd..0ade23e 100644
--- a/plugins/experimental/webp_transform/ImageTransform.cc
+++ b/plugins/experimental/webp_transform/ImageTransform.cc
@@ -23,10 +23,10 @@
 #include "tscpp/api/GlobalPlugin.h"
 #include "tscpp/api/TransformationPlugin.h"
 #include "tscpp/api/Logger.h"
+#include "tscpp/api/Stat.h"
 
 #include <Magick++.h>
 
-using std::string;
 using namespace Magick;
 using namespace atscppapi;
 
@@ -35,12 +35,23 @@ using namespace atscppapi;
 namespace
 {
 GlobalPlugin *plugin;
-}
+
+enum class ImageEncoding { webp, jpeg, png, unknown };
+
+bool config_convert_to_webp = false;
+bool config_convert_to_jpeg = false;
+
+Stat stat_convert_to_webp;
+Stat stat_convert_to_jpeg;
+} // namespace
 
 class ImageTransform : public TransformationPlugin
 {
 public:
-  ImageTransform(Transaction &transaction) : TransformationPlugin(transaction, TransformationPlugin::RESPONSE_TRANSFORMATION)
+  ImageTransform(Transaction &transaction, ImageEncoding input_image_type, ImageEncoding
transform_image_type)
+    : TransformationPlugin(transaction, TransformationPlugin::RESPONSE_TRANSFORMATION),
+      _input_image_type(input_image_type),
+      _transform_image_type(transform_image_type)
   {
     TransformationPlugin::registerHook(HOOK_READ_RESPONSE_HEADERS);
   }
@@ -48,8 +59,22 @@ public:
   void
   handleReadResponseHeaders(Transaction &transaction) override
   {
-    transaction.getServerResponse().getHeaders()["Content-Type"] = "image/webp";
-    transaction.getServerResponse().getHeaders()["Vary"]         = "Content-Type"; // to
have a separate cache entry.
+    switch (_transform_image_type) {
+    case ImageEncoding::webp:
+      transaction.getServerResponse().getHeaders()["Content-Type"] = "image/webp";
+      break;
+    case ImageEncoding::jpeg:
+      transaction.getServerResponse().getHeaders()["Content-Type"] = "image/jpeg";
+      break;
+    case ImageEncoding::png:
+      transaction.getServerResponse().getHeaders()["Content-Type"] = "image/png";
+      break;
+    case ImageEncoding::unknown:
+      // do nothing
+      break;
+    }
+
+    transaction.getServerResponse().getHeaders()["Vary"] = "Accept"; // to have a separate
cache entry
 
     TS_DEBUG(TAG, "url %s", transaction.getServerRequest().getUrl().getUrlString().c_str());
     transaction.resume();
@@ -64,15 +89,35 @@ public:
   void
   handleInputComplete() override
   {
-    string input_data = _img.str();
+    std::string input_data = _img.str();
     Blob input_blob(input_data.data(), input_data.length());
     Image image;
-    image.read(input_blob);
 
-    Blob output_blob;
-    image.magick("WEBP");
-    image.write(&output_blob);
-    produce(std::string_view(reinterpret_cast<const char *>(output_blob.data()), output_blob.length()));
+    try {
+      image.read(input_blob);
+
+      Blob output_blob;
+      if (_transform_image_type == ImageEncoding::webp) {
+        stat_convert_to_webp.increment(1);
+        TSDebug(TAG, "Transforming jpeg or png to webp");
+        image.magick("WEBP");
+      } else {
+        stat_convert_to_jpeg.increment(1);
+        TSDebug(TAG, "Transforming webp to jpeg");
+        image.magick("JPEG");
+      }
+      image.write(&output_blob);
+      produce(std::string_view(reinterpret_cast<const char *>(output_blob.data()),
output_blob.length()));
+    } catch (Magick::Warning &warning) {
+      TSError("ImageMagick++ warning: %s", warning.what());
+      produce(std::string_view(reinterpret_cast<const char *>(input_blob.data()), input_blob.length()));
+      _transform_image_type = _input_image_type; // Revert to original encoding on error
+    } catch (Magick::Error &error) {
+      TSError("ImageMagick++ error: %s _image_type: %d input_data.length(): %zd", error.what(),
(int)_transform_image_type,
+              input_data.length());
+      produce(std::string_view(reinterpret_cast<const char *>(input_blob.data()), input_blob.length()));
+      _transform_image_type = _input_image_type; // Revert to original encoding on error
+    }
 
     setOutputComplete();
   }
@@ -81,6 +126,8 @@ public:
 
 private:
   std::stringstream _img;
+  ImageEncoding _input_image_type;
+  ImageEncoding _transform_image_type;
 };
 
 class GlobalHookPlugin : public GlobalPlugin
@@ -90,16 +137,53 @@ public:
   void
   handleReadResponseHeaders(Transaction &transaction) override
   {
-    string ctype      = transaction.getServerResponse().getHeaders().values("Content-Type");
-    string user_agent = transaction.getServerRequest().getHeaders().values("User-Agent");
-    string accept     = transaction.getServerRequest().getHeaders().values("Accept");
+    // This variable stores the incoming image type
+    ImageEncoding input_image_type = ImageEncoding::unknown;
+
+    // This method tries to optimize the amount of string searching at the expense of double
checking some of the booleans
+
+    std::string ctype = transaction.getServerResponse().getHeaders().values("Content-Type");
+
+    // Test to if in this transaction we might want to convert jpeg or png to webp
+    bool transaction_convert_to_webp = false;
+    if (config_convert_to_webp == true) {
+      if (ctype.find("image/jpeg") != std::string::npos) {
+        input_image_type            = ImageEncoding::jpeg;
+        transaction_convert_to_webp = true;
+      }
+      if (ctype.find("image/png") != std::string::npos) {
+        input_image_type            = ImageEncoding::png;
+        transaction_convert_to_webp = true;
+      }
+    }
 
-    bool webp_supported = accept.find("image/webp") != string::npos || user_agent.find("Chrome")
!= string::npos;
-    bool image_format   = ctype.find("jpeg") != string::npos || ctype.find("png") != string::npos;
+    // Test to if in this transaction we might want to convert webp to jpeg
+    bool transaction_convert_to_jpeg = false;
+    if (config_convert_to_jpeg == true && transaction_convert_to_webp == false) {
+      transaction_convert_to_jpeg = ctype.find("image/webp") != std::string::npos;
+      if (transaction_convert_to_jpeg) {
+        input_image_type = ImageEncoding::webp;
+      }
+    }
 
-    if (webp_supported && image_format) {
-      TS_DEBUG(TAG, "Content type is either jpeg or png. Converting to webp");
-      transaction.addPlugin(new ImageTransform(transaction));
+    TSDebug(TAG, "User-Agent: %s transaction_convert_to_webp: %d transaction_convert_to_jpeg:
%d", ctype.c_str(),
+            transaction_convert_to_webp, transaction_convert_to_jpeg);
+
+    // If we might need to convert check to see if what the browser supports
+    if (transaction_convert_to_webp == true || transaction_convert_to_jpeg == true) {
+      std::string accept  = transaction.getServerRequest().getHeaders().values("Accept");
+      bool webp_supported = accept.find("image/webp") != std::string::npos;
+      TSDebug(TAG, "Accept: %s webp_suppported: %d", accept.c_str(), webp_supported);
+
+      if (webp_supported == true && transaction_convert_to_webp == true) {
+        TSDebug(TAG, "Content type is either jpeg or png. Converting to webp");
+        transaction.addPlugin(new ImageTransform(transaction, input_image_type, ImageEncoding::webp));
+      } else if (webp_supported == false && transaction_convert_to_jpeg == true)
{
+        TSDebug(TAG, "Content type is webp. Converting to jpeg");
+        transaction.addPlugin(new ImageTransform(transaction, input_image_type, ImageEncoding::jpeg));
+      } else {
+        TSDebug(TAG, "Nothing to convert");
+      }
     }
 
     transaction.resume();
@@ -107,11 +191,35 @@ public:
 };
 
 void
-TSPluginInit(int argc ATSCPPAPI_UNUSED, const char *argv[] ATSCPPAPI_UNUSED)
+TSPluginInit(int argc, const char *argv[])
 {
   if (!RegisterGlobalPlugin("CPP_Webp_Transform", "apache", "dev@trafficserver.apache.org"))
{
     return;
   }
+
+  if (argc >= 2) {
+    std::string option(argv[1]);
+    if (option.find("convert_to_webp") != std::string::npos) {
+      TSDebug(TAG, "Configured to convert to webp");
+      config_convert_to_webp = true;
+    }
+    if (option.find("convert_to_jpeg") != std::string::npos) {
+      TSDebug(TAG, "Configured to convert to jpeg");
+      config_convert_to_jpeg = true;
+    }
+    if (config_convert_to_webp == false && config_convert_to_jpeg == false) {
+      TSDebug(TAG, "Unknown option: %s", option.c_str());
+      TSError("Unknown option: %s", option.c_str());
+    }
+  } else {
+    TSDebug(TAG, "Default configuration is to convert both webp and jpeg");
+    config_convert_to_webp = true;
+    config_convert_to_jpeg = true;
+  }
+
+  stat_convert_to_webp.init("plugin." TAG ".convert_to_webp", Stat::SYNC_SUM, false);
+  stat_convert_to_jpeg.init("plugin." TAG ".convert_to_jpeg", Stat::SYNC_SUM, false);
+
   InitializeMagick("");
   plugin = new GlobalHookPlugin();
 }
diff --git a/plugins/experimental/webp_transform/Makefile.inc b/plugins/experimental/webp_transform/Makefile.inc
index fd24dab..a7423bc 100644
--- a/plugins/experimental/webp_transform/Makefile.inc
+++ b/plugins/experimental/webp_transform/Makefile.inc
@@ -15,18 +15,18 @@
 #  See the License for the specific language governing permissions and
 #  limitations under the License.
 
-experimental_webp_transform_WebpTransform_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBMAGICKCPP_CFLAGS)
-experimental_webp_transform_WebpTransform_la_CXXFLAGS = $(AM_CXXFLAGS)
+experimental_webp_transform_webp_transform_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBMAGICKCPP_CFLAGS)
+experimental_webp_transform_webp_transform_la_CXXFLAGS = $(AM_CXXFLAGS)
 
-pkglib_LTLIBRARIES += experimental/webp_transform/WebpTransform.la
+pkglib_LTLIBRARIES += experimental/webp_transform/webp_transform.la
 
-experimental_webp_transform_WebpTransform_la_SOURCES = \
+experimental_webp_transform_webp_transform_la_SOURCES = \
   experimental/webp_transform/ImageTransform.cc
 
-experimental_webp_transform_WebpTransform_la_LDFLAGS = \
+experimental_webp_transform_webp_transform_la_LDFLAGS = \
   $(AM_LDFLAGS) \
   -L$(top_builddir)/src/tscpp/api
 
-experimental_webp_transform_WebpTransform_la_LIBADD = \
+experimental_webp_transform_webp_transform_la_LIBADD = \
   -ltscppapi \
   $(LIBMAGICKCPP_LIBS)

Mime
View raw message