You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by bc...@apache.org on 2020/10/06 00:44:59 UTC

[trafficserver] 01/03: Updates to webp_transform to convert webp to jpeg

This is an automated email from the ASF dual-hosted git repository.

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

commit 9a0f785db5a5ffb370141762b474fe5dbb19a2f5
Author: Bryan Call <bc...@apache.org>
AuthorDate: Mon Oct 5 12:33:28 2020 -0700

    Updates to webp_transform to convert webp to jpeg
---
 .../experimental/webp_transform/ImageTransform.cc  | 102 +++++++++++++++++----
 1 file changed, 86 insertions(+), 16 deletions(-)

diff --git a/plugins/experimental/webp_transform/ImageTransform.cc b/plugins/experimental/webp_transform/ImageTransform.cc
index 88d19cd..96ecc73 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,21 @@ using namespace atscppapi;
 namespace
 {
 GlobalPlugin *plugin;
-}
+
+enum class TransformImageType { webp, jpeg };
+
+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, TransformImageType image_type)
+    : TransformationPlugin(transaction, TransformationPlugin::RESPONSE_TRANSFORMATION), _image_type(image_type)
   {
     TransformationPlugin::registerHook(HOOK_READ_RESPONSE_HEADERS);
   }
@@ -48,8 +57,12 @@ 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.
+    if (_image_type == TransformImageType::webp) {
+      transaction.getServerResponse().getHeaders()["Content-Type"] = "image/webp";
+    } else {
+      transaction.getServerResponse().getHeaders()["Content-Type"] = "image/jpeg";
+    }
+    transaction.getServerResponse().getHeaders()["Vary"] = "Accpet"; // to have a separate cache entry
 
     TS_DEBUG(TAG, "url %s", transaction.getServerRequest().getUrl().getUrlString().c_str());
     transaction.resume();
@@ -64,13 +77,21 @@ 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");
+    if (_image_type == TransformImageType::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 wepb to jpeg");
+      image.magick("JPEG");
+    }
     image.write(&output_blob);
     produce(std::string_view(reinterpret_cast<const char *>(output_blob.data()), output_blob.length()));
 
@@ -81,6 +102,7 @@ public:
 
 private:
   std::stringstream _img;
+  TransformImageType _image_type;
 };
 
 class GlobalHookPlugin : public GlobalPlugin
@@ -90,16 +112,40 @@ 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 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");
 
-    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 jpeg or png to webp
+    bool transaction_convert_to_wepb = false;
+    if (config_convert_to_webp == true) {
+      transaction_convert_to_wepb = ctype.find("image/jpeg") != std::string::npos || ctype.find("image/png") != std::string::npos;
+    }
 
-    if (webp_supported && image_format) {
-      TS_DEBUG(TAG, "Content type is either jpeg or png. Converting to webp");
-      transaction.addPlugin(new ImageTransform(transaction));
+    // 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_wepb == false) {
+      transaction_convert_to_jpeg = ctype.find("image/webp") != std::string::npos;
+    }
+
+    TSDebug(TAG, "User-Agent: %s transaction_convert_to_wepb: %d transaction_convert_to_jpeg: %d", ctype.c_str(),
+            transaction_convert_to_wepb, transaction_convert_to_jpeg);
+
+    // If we might need to convert check to see if what the browser supports
+    if (transaction_convert_to_wepb == 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_wepb == true) {
+        TSDebug(TAG, "Content type is either jpeg or png. Converting to webp");
+        transaction.addPlugin(new ImageTransform(transaction, TransformImageType::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, TransformImageType::jpeg));
+      } else {
+        TSDebug(TAG, "Nothing to convert");
+      }
     }
 
     transaction.resume();
@@ -107,11 +153,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 wepb");
+      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();
 }