This is an automated email from the ASF dual-hosted git repository.
bneradt pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new 8af35fd319 Quiet ESI streaming gunzip zero-output logs (#13171)
8af35fd319 is described below
commit 8af35fd31944e9cd64763fa52d4e443c2f178f3a
Author: Brian Neradt <[email protected]>
AuthorDate: Mon Jun 1 14:59:44 2026 -0500
Quiet ESI streaming gunzip zero-output logs (#13171)
ESI streaming gunzip can receive valid gzip chunks that consume input
without producing output, such as an initial header-only chunk. Those
chunks currently emit a `buf below zero` error even though the stream
continues successfully, which creates noisy Traffic Server logs.
This treats zero-output progress as normal streaming inflate behavior
and reserves error logging for actual zlib failures. It also extends the
ESI gzip unit test support to capture error logs and covers the
header-only chunk case.
---
plugins/esi/lib/EsiGunzip.cc | 27 +++++++++++++++------------
plugins/esi/test/gzip_test.cc | 26 ++++++++++++++++++++++++++
plugins/esi/test/print_funcs.cc | 14 +++++++++++++-
3 files changed, 54 insertions(+), 13 deletions(-)
diff --git a/plugins/esi/lib/EsiGunzip.cc b/plugins/esi/lib/EsiGunzip.cc
index ec1341d818..19bfc6c205 100644
--- a/plugins/esi/lib/EsiGunzip.cc
+++ b/plugins/esi/lib/EsiGunzip.cc
@@ -72,29 +72,32 @@ EsiGunzip::stream_decode(const char *data, int data_len,
std::string &udata)
int32_t curr_buf_size;
do {
- _zstrm.next_out = reinterpret_cast<Bytef *>(raw_buf);
- _zstrm.avail_out = BUF_SIZE;
- inflate_result = inflate(&_zstrm, Z_SYNC_FLUSH);
- curr_buf_size = -1;
- if ((inflate_result == Z_OK) || (inflate_result == Z_BUF_ERROR)) {
- curr_buf_size = BUF_SIZE - _zstrm.avail_out;
- } else if (inflate_result == Z_STREAM_END) {
+ _zstrm.next_out = reinterpret_cast<Bytef *>(raw_buf);
+ _zstrm.avail_out = BUF_SIZE;
+ auto const avail_in_before = _zstrm.avail_in;
+ inflate_result = inflate(&_zstrm, Z_SYNC_FLUSH);
+ if ((inflate_result == Z_OK) || (inflate_result == Z_BUF_ERROR) ||
(inflate_result == Z_STREAM_END)) {
curr_buf_size = BUF_SIZE - _zstrm.avail_out;
+ } else {
+ TSError("[%s] Failure while inflating; error code %d", __FUNCTION__,
inflate_result);
+ _success = false;
+ return false;
}
if (curr_buf_size > BUF_SIZE) {
TSError("[%s] buf too large", __FUNCTION__);
break;
}
- if (curr_buf_size < 1) {
- TSError("[%s] buf below zero", __FUNCTION__);
+ if (curr_buf_size < 1 && avail_in_before == _zstrm.avail_in) {
break;
}
// push empty object onto list and add data to in-list object to
// avoid data copy for temporary
- buf_list.push_back(string());
- string &curr_buf = buf_list.back();
- curr_buf.assign(raw_buf, curr_buf_size);
+ if (curr_buf_size > 0) {
+ buf_list.push_back(string());
+ string &curr_buf = buf_list.back();
+ curr_buf.assign(raw_buf, curr_buf_size);
+ }
if (inflate_result == Z_STREAM_END) {
break;
diff --git a/plugins/esi/test/gzip_test.cc b/plugins/esi/test/gzip_test.cc
index a6bd50fa59..5d19366a7c 100644
--- a/plugins/esi/test/gzip_test.cc
+++ b/plugins/esi/test/gzip_test.cc
@@ -26,12 +26,16 @@
#include <catch2/catch_test_macros.hpp>
+#include "EsiGunzip.h"
#include "Utils.h"
#include "gzip.h"
using std::string;
using namespace EsiLib;
+extern void enableFakeErrorLog();
+extern string gFakeErrorLog;
+
TEST_CASE("test esi plugin - gzip")
{
SECTION("===================== Test 1")
@@ -102,4 +106,26 @@ TEST_CASE("test esi plugin - gzip")
// check output of gunzip
CHECK(gunzip(expected_cdata, 32, buf_list) == false);
}
+
+ SECTION("streaming gunzip does not log an error for chunks with no output")
+ {
+ const char expected_data[] = "Hello World!";
+
+ string cdata;
+ REQUIRE(gzip(expected_data, 12, cdata));
+
+ EsiGunzip gunzip;
+ string data;
+
+ enableFakeErrorLog();
+ REQUIRE(gunzip.stream_decode(cdata.data(), GZIP_HEADER_SIZE, data));
+ CHECK(data.empty());
+ CHECK(gFakeErrorLog.empty());
+
+ REQUIRE(gunzip.stream_decode(cdata.data() + GZIP_HEADER_SIZE, cdata.size()
- GZIP_HEADER_SIZE, data));
+ REQUIRE(gunzip.stream_finish());
+
+ CHECK(data == expected_data);
+ CHECK(gFakeErrorLog.empty());
+ }
}
diff --git a/plugins/esi/test/print_funcs.cc b/plugins/esi/test/print_funcs.cc
index 2596a46205..d73b973d65 100644
--- a/plugins/esi/test/print_funcs.cc
+++ b/plugins/esi/test/print_funcs.cc
@@ -34,9 +34,11 @@ static const int LINE_SIZE = 1024 * 1024;
namespace
{
bool fakeDebugLogEnabled;
-}
+bool fakeErrorLogEnabled;
+} // namespace
std::string gFakeDebugLog;
+std::string gFakeErrorLog;
void
enableFakeDebugLog()
@@ -45,6 +47,13 @@ enableFakeDebugLog()
gFakeDebugLog.assign("");
}
+void
+enableFakeErrorLog()
+{
+ fakeErrorLogEnabled = true;
+ gFakeErrorLog.assign("");
+}
+
void
DbgCtl::print(const char *tag, const char * /* file */, const char * /*
function */, int /* line */, const char *fmt, ...)
{
@@ -101,4 +110,7 @@ TSError(const char *fmt, ...)
vsnprintf(buf, LINE_SIZE, fmt, ap);
printf("Error: %s\n", buf);
va_end(ap);
+ if (fakeErrorLogEnabled) {
+ gFakeErrorLog.append(buf);
+ }
}