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 <[email protected]>
AuthorDate: Thu Jul 15 09:19:47 2021 -0700
Updates to webp_transform to convert webp to jpeg (#8005)
Co-authored-by: Siva Renganathan <[email protected]>
(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",
"[email protected]")) {
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)