Skip to content

Commit 74cd8b5

Browse files
committed
Add Zstandard (zstd) compression support to the compress plugin
- Updated CMakeLists.txt and Dockerfiles to include zstd dependencies. - Implemented zstd compression in compress.cc and added related functions. - Enhanced configuration to support zstd as a compression algorithm. - Updated tests to verify zstd functionality.
1 parent 3116856 commit 74cd8b5

File tree

12 files changed

+196
-11
lines changed

12 files changed

+196
-11
lines changed

CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,11 @@ set(TS_USE_MALLOC_ALLOCATOR ${ENABLE_MALLOC_ALLOCATOR})
338338
set(TS_USE_ALLOCATOR_METRICS ${ENABLE_ALLOCATOR_METRICS})
339339
find_package(ZLIB REQUIRED)
340340

341+
find_package(ZSTD)
342+
if(ZSTD_FOUND)
343+
set(HAVE_ZSTD_H TRUE)
344+
endif()
345+
341346
# ncurses is used in traffic_top
342347
find_package(Curses)
343348
set(HAVE_CURSES_H ${CURSES_HAVE_CURSES_H})

ci/docker/deb/Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ RUN apt-get update; apt-get -y dist-upgrade; \
5555
apt-get -y install libssl-dev libexpat1-dev libpcre3-dev libcap-dev \
5656
libhwloc-dev libunwind8 libunwind-dev zlib1g-dev \
5757
tcl-dev tcl8.6-dev libjemalloc-dev libluajit-5.1-dev liblzma-dev \
58-
libhiredis-dev libbrotli-dev libncurses-dev libgeoip-dev libmagick++-dev; \
58+
libhiredis-dev libbrotli-dev libncurses-dev libgeoip-dev libmagick++-dev \
59+
libzstd-dev; \
5960
# Optional: This is for the OpenSSH server, and Jenkins account + access (comment out if not needed)
6061
apt-get -y install openssh-server openjdk-8-jre && mkdir /run/sshd; \
6162
groupadd -g 665 jenkins && \

ci/docker/yum/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ RUN yum -y update; \
5252
# Devel packages that ATS needs
5353
yum -y install openssl-devel expat-devel pcre-devel libcap-devel hwloc-devel libunwind-devel \
5454
xz-devel libcurl-devel ncurses-devel jemalloc-devel GeoIP-devel luajit-devel brotli-devel \
55-
ImageMagick-devel ImageMagick-c++-devel hiredis-devel zlib-devel \
55+
ImageMagick-devel ImageMagick-c++-devel hiredis-devel zlib-devel zstd-devel \
5656
perl-ExtUtils-MakeMaker perl-Digest-SHA perl-URI; \
5757
# This is for autest stuff
5858
yum -y install python3 httpd-tools procps-ng nmap-ncat pipenv \

cmake/Findzstd.cmake

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#######################
2+
#
3+
# Licensed to the Apache Software Foundation (ASF) under one or more contributor license
4+
# agreements. See the NOTICE file distributed with this work for additional information regarding
5+
# copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0
6+
# (the "License"); you may not use this file except in compliance with the License. You may obtain
7+
# a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software distributed under the License
12+
# is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
14+
# or implied. See the License for the specific language governing permissions and limitations under
15+
# the License.
16+
#
17+
#######################
18+
19+
# Findzstd.cmake
20+
#
21+
# This will define the following variables
22+
#
23+
# ZSTD_FOUND
24+
# ZSTD_LIBRARY
25+
# ZSTD_INCLUDE_DIRS
26+
#
27+
# and the following imported target
28+
#
29+
# zstd::zstd
30+
#
31+
32+
find_path(ZSTD_INCLUDE_DIR NAMES zstd.h)
33+
34+
find_library(ZSTD_LIBRARY_DEBUG NAMES zstdd zstd_staticd)
35+
find_library(ZSTD_LIBRARY_RELEASE NAMES zstd zstd_static)
36+
37+
mark_as_advanced(ZSTD_LIBRARY ZSTD_INCLUDE_DIR)
38+
39+
include(SelectLibraryConfigurations)
40+
SELECT_LIBRARY_CONFIGURATIONS(ZSTD)
41+
42+
include(FindPackageHandleStandardArgs)
43+
find_package_handle_standard_args(ZSTD REQUIRED_VARS ZSTD_LIBRARY ZSTD_INCLUDE_DIR)
44+
45+
if(ZSTD_FOUND)
46+
set(ZSTD_INCLUDE_DIRS "${ZSTD_INCLUDE_DIR}")
47+
endif()
48+
49+
if(ZSTD_FOUND AND NOT TARGET zstd::zstd)
50+
add_library(zstd::zstd INTERFACE IMPORTED)
51+
target_include_directories(zstd::zstd INTERFACE ${ZSTD_INCLUDE_DIRS})
52+
target_link_libraries(zstd::zstd INTERFACE "${ZSTD_LIBRARY}")
53+
endif()

contrib/docker/ubuntu/noble/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ RUN apt update \
4848
libpcre3-dev \
4949
hwloc \
5050
libbrotli-dev \
51+
libzstd-dev \
5152
luajit \
5253
libcap-dev \
5354
libmagick++-dev \

plugins/compress/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,10 @@ target_link_libraries(compress PRIVATE libswoc::libswoc)
2020
if(HAVE_BROTLI_ENCODE_H)
2121
target_link_libraries(compress PRIVATE brotli::brotlienc)
2222
endif()
23+
24+
if(HAVE_ZSTD_H)
25+
target_link_libraries(compress PRIVATE zstd)
26+
endif()
27+
2328
verify_global_plugin(compress)
2429
verify_remap_plugin(compress)

plugins/compress/compress.cc

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/** @file
22
3-
Transforms content using gzip, deflate or brotli
3+
Transforms content using gzip, deflate, brotli or zstd
44
55
@section license License
66
@@ -23,6 +23,9 @@
2323

2424
#include <cstring>
2525
#include <zlib.h>
26+
#if HAVE_ZSTD_H
27+
#include <zstd.h>
28+
#endif
2629

2730
#include "ts/apidefs.h"
2831
#include "tscore/ink_config.h"
@@ -71,6 +74,10 @@ const int BROTLI_COMPRESSION_LEVEL = 6;
7174
const int BROTLI_LGW = 16;
7275
#endif
7376

77+
#if HAVE_ZSTD_H
78+
const int ZSTD_COMPRESSION_LEVEL = 6;
79+
#endif
80+
7481
static const char *global_hidden_header_name = nullptr;
7582

7683
static TSMutex compress_config_mutex = nullptr;
@@ -191,6 +198,16 @@ data_alloc(int compression_type, int compression_algorithms)
191198
data->bstrm.avail_out = 0;
192199
data->bstrm.total_out = 0;
193200
}
201+
#endif
202+
#if HAVE_ZSTD_H
203+
data->zstd_ctx = nullptr;
204+
if (compression_type & COMPRESSION_TYPE_ZSTD) {
205+
debug("zstd compression. Create Zstd Compression Context.");
206+
data->zstd_ctx = ZSTD_createCCtx();
207+
if (!data->zstd_ctx) {
208+
fatal("Zstd Compression Context Creation Failed");
209+
}
210+
}
194211
#endif
195212
return data;
196213
}
@@ -212,6 +229,11 @@ data_destroy(Data *data)
212229
#if HAVE_BROTLI_ENCODE_H
213230
BrotliEncoderDestroyInstance(data->bstrm.br);
214231
#endif
232+
#if HAVE_ZSTD_H
233+
if (data->zstd_ctx) {
234+
ZSTD_freeCCtx(data->zstd_ctx);
235+
}
236+
#endif
215237

216238
TSfree(data);
217239
}
@@ -233,6 +255,9 @@ content_encoding_header(TSMBuffer bufp, TSMLoc hdr_loc, const int compression_ty
233255
} else if (compression_type & COMPRESSION_TYPE_DEFLATE && (algorithm & ALGORITHM_DEFLATE)) {
234256
value = TS_HTTP_VALUE_DEFLATE;
235257
value_len = TS_HTTP_LEN_DEFLATE;
258+
} else if (compression_type & COMPRESSION_TYPE_ZSTD && (algorithm & ALGORITHM_ZSTD)) {
259+
value = "zstd";
260+
value_len = strlen("zstd");
236261
}
237262

238263
if (value_len == 0) {
@@ -357,6 +382,16 @@ compress_transform_init(TSCont contp, Data *data)
357382
data->downstream_vio = TSVConnWrite(downstream_conn, contp, data->downstream_reader, INT64_MAX);
358383
}
359384

385+
#if HAVE_ZSTD_H
386+
if (data->compression_type & COMPRESSION_TYPE_ZSTD) {
387+
zstd_compress_init(data);
388+
if (!data->zstd_cctx) {
389+
TSError("Failed to create Zstandard compression context");
390+
return;
391+
}
392+
}
393+
#endif
394+
360395
TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
361396
}
362397

@@ -462,6 +497,40 @@ brotli_transform_one(Data *data, const char *upstream_buffer, int64_t upstream_l
462497
}
463498
#endif
464499

500+
#if HAVE_ZSTD_H
501+
static void
502+
zstd_compress_init(Data *data)
503+
{
504+
data->zstd_cctx = ZSTD_createCCtx();
505+
if (!data->zstd_cctx) {
506+
error("Failed to initialize Zstd compression context");
507+
}
508+
}
509+
510+
static void
511+
zstd_compress_finish(Data *data)
512+
{
513+
if (data->zstd_cctx) {
514+
ZSTD_freeCCtx(data->zstd_cctx);
515+
data->zstd_cctx = nullptr;
516+
}
517+
}
518+
519+
static void
520+
zstd_compress_one(Data *data, const char *upstream_buffer, int64_t upstream_length)
521+
{
522+
char output_buffer[ZSTD_CStreamOutSize()];
523+
size_t compressed_size = ZSTD_compressCCtx(data->zstd_cctx, output_buffer, sizeof(output_buffer), upstream_buffer,
524+
upstream_length, ZSTD_COMPRESSION_LEVEL);
525+
if (ZSTD_isError(compressed_size)) {
526+
error("Zstd compression failed: %s", ZSTD_getErrorName(compressed_size));
527+
return;
528+
}
529+
TSIOBufferWrite(data->downstream_buffer, output_buffer, compressed_size);
530+
data->downstream_length += compressed_size;
531+
}
532+
#endif
533+
465534
static void
466535
compress_transform_one(Data *data, TSIOBufferReader upstream_reader, int amount)
467536
{
@@ -488,6 +557,11 @@ compress_transform_one(Data *data, TSIOBufferReader upstream_reader, int amount)
488557
if (data->compression_type & COMPRESSION_TYPE_BROTLI && (data->compression_algorithms & ALGORITHM_BROTLI)) {
489558
brotli_transform_one(data, upstream_buffer, upstream_length);
490559
} else
560+
#endif
561+
#if HAVE_ZSTD_H
562+
if (data->compression_type & COMPRESSION_TYPE_ZSTD && (data->compression_algorithms & ALGORITHM_ZSTD)) {
563+
zstd_compress_one(data, upstream_buffer, upstream_length);
564+
} else
491565
#endif
492566
if ((data->compression_type & (COMPRESSION_TYPE_GZIP | COMPRESSION_TYPE_DEFLATE)) &&
493567
(data->compression_algorithms & (ALGORITHM_GZIP | ALGORITHM_DEFLATE))) {
@@ -576,6 +650,12 @@ compress_transform_finish(Data *data)
576650
brotli_transform_finish(data);
577651
debug("compress_transform_finish: brotli compression finish");
578652
} else
653+
#endif
654+
#if HAVE_ZSTD_H
655+
if (data->compression_type & COMPRESSION_TYPE_ZSTD && data->compression_algorithms & ALGORITHM_ZSTD) {
656+
zstd_compress_finish(data);
657+
debug("compress_transform_finish: zstd compression finish");
658+
} else
579659
#endif
580660
if ((data->compression_type & (COMPRESSION_TYPE_GZIP | COMPRESSION_TYPE_DEFLATE)) &&
581661
(data->compression_algorithms & (ALGORITHM_GZIP | ALGORITHM_DEFLATE))) {
@@ -771,6 +851,11 @@ transformable(TSHttpTxn txnp, bool server, HostConfiguration *host_configuration
771851
compression_acceptable = 1;
772852
}
773853
*compress_type |= COMPRESSION_TYPE_GZIP;
854+
} else if (strncasecmp(value, "zstd", sizeof("zstd") - 1) == 0) {
855+
if (*algorithms & ALGORITHM_ZSTD) {
856+
compression_acceptable = 1;
857+
}
858+
*compress_type |= COMPRESSION_TYPE_ZSTD;
774859
}
775860
}
776861

plugins/compress/configuration.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,12 @@ HostConfiguration::add_compression_algorithms(string &line)
229229
string token = extractFirstToken(line, isCommaOrSpace);
230230
if (token.empty()) {
231231
break;
232+
} else if (token == "zstd") {
233+
#ifdef HAVE_ZSTD_H
234+
compression_algorithms_ |= ALGORITHM_ZSTD;
235+
#else
236+
error("supported-algorithms: zstd support not compiled in.");
237+
#endif
232238
} else if (token == "br") {
233239
#ifdef HAVE_BROTLI_ENCODE_H
234240
compression_algorithms_ |= ALGORITHM_BROTLI;
@@ -334,6 +340,10 @@ Configuration::Parse(const char *path)
334340
continue;
335341
}
336342

343+
if (strstr(line.c_str(), "zstd")) {
344+
current_host_configuration->add_compression_algorithms(line);
345+
}
346+
337347
for (;;) {
338348
string token = extractFirstToken(line, isspace);
339349

plugins/compress/configuration.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ enum CompressionAlgorithm {
3838
ALGORITHM_DEFAULT = 0,
3939
ALGORITHM_DEFLATE = 1,
4040
ALGORITHM_GZIP = 2,
41-
ALGORITHM_BROTLI = 4 // For bit manipulations
41+
ALGORITHM_BROTLI = 4, // For bit manipulations
42+
ALGORITHM_ZSTD = 8
4243
};
4344

4445
enum class RangeRequestCtrl : int {

plugins/compress/misc.cc

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ normalize_accept_encoding(TSHttpTxn /* txnp ATS_UNUSED */, TSMBuffer reqp, TSMLo
8484
bool deflate = false;
8585
bool gzip = false;
8686
bool br = false;
87+
bool zstd = false;
8788
// remove the accept encoding field(s),
8889
// while finding out if gzip or deflate is supported.
8990
while (field) {
@@ -100,6 +101,8 @@ normalize_accept_encoding(TSHttpTxn /* txnp ATS_UNUSED */, TSMBuffer reqp, TSMLo
100101
br = true;
101102
} else if (strcasecmp("deflate", next) == 0) {
102103
deflate = true;
104+
} else if (strcasecmp("zstd", next) == 0) {
105+
zstd = true;
103106
}
104107
}
105108
}
@@ -111,9 +114,13 @@ normalize_accept_encoding(TSHttpTxn /* txnp ATS_UNUSED */, TSMBuffer reqp, TSMLo
111114
}
112115

113116
// append a new accept-encoding field in the header
114-
if (deflate || gzip || br) {
117+
if (deflate || gzip || br || zstd) {
115118
TSMimeHdrFieldCreate(reqp, hdr_loc, &field);
116119
TSMimeHdrFieldNameSet(reqp, hdr_loc, field, TS_MIME_FIELD_ACCEPT_ENCODING, TS_MIME_LEN_ACCEPT_ENCODING);
120+
if (zstd) {
121+
TSMimeHdrFieldValueStringInsert(reqp, hdr_loc, field, -1, "zstd", strlen("zstd"));
122+
info("normalized accept encoding to zstd");
123+
}
117124
if (br) {
118125
TSMimeHdrFieldValueStringInsert(reqp, hdr_loc, field, -1, "br", strlen("br"));
119126
info("normalized accept encoding to br");

0 commit comments

Comments
 (0)