1
0
mirror of http://git.haproxy.org/git/haproxy.git/ synced 2025-04-11 03:31:36 +00:00

MEDIUM: compression: Make it so we can compress requests as well.

Add code so that compression can be used for requests as well.
New compression keywords are introduced :
"direction" that specifies what we want to compress. Valid values are
"request", "response", or "both".
"type-req" and "type-res" define content-type to be compressed for
requests and responses, respectively. "type" is kept as an alias for
"type-res" for backward compatibilty.
"algo-req" specifies the compression algorithm to be used for requests.
Only one algorithm can be provided.
"algo-res" provides the list of algorithm that can be used to compress
responses. "algo" is kept as an alias for "algo-res" for backward
compatibility.
This commit is contained in:
Olivier Houchard 2023-04-06 00:33:48 +02:00 committed by Olivier Houchard
parent dea25f51b6
commit ead43fe4f2
3 changed files with 187 additions and 8 deletions

View File

@ -5014,13 +5014,25 @@ clitcpka-intvl <timeout>
compression algo <algorithm> ...
compression algo-req <algorithm>
compression algo-res <algorithm>
compression type <mime type> ...
Enable HTTP compression.
May be used in sections : defaults | frontend | listen | backend
yes | yes | yes | yes
Arguments :
algo is followed by the list of supported compression algorithms.
type is followed by the list of MIME types that will be compressed.
algo is followed by the list of supported compression algorithms for
responses (legacy keyword)
algo-req is followed by compression algorithm for request (only one is
provided).
algo-res is followed by the list of supported compression algorithms for
responses.
type is followed by the list of MIME types that will be compressed for
responses (legacy keyword).
type-req is followed by the list of MIME types that will be compressed for
requests.
type-res is followed by the list of MIME types that will be compressed for
responses.
The currently supported algorithms are :
identity this is mostly for debugging, and it was useful for developing
@ -5077,7 +5089,7 @@ compression type <mime type> ...
compression algo gzip
compression type text/html text/plain
See also : "compression offload"
See also : "compression offload", "compression direction"
compression offload
Makes HAProxy work as a compression offloader only.
@ -5099,7 +5111,15 @@ compression offload
If this setting is used in a defaults section, a warning is emitted and the
option is ignored.
See also : "compression type", "compression algo"
See also : "compression type", "compression algo", "compression direction"
compression direction <direction>
Makes haproxy able to compress both requests and responses.
Valid values are "request", to compress only requests, "response", to
compress only responses, or "both", when you want to compress both.
The default value is "response".
See also : "compression type", "compression algo", "compression offload"
cookie <name> [ rewrite | insert | prefix ] [ indirect ] [ nocache ]
[ postonly ] [ preserve ] [ httponly ] [ secure ]

View File

@ -42,6 +42,9 @@
/* Compression flags */
#define COMP_FL_OFFLOAD 0x00000001 /* Compression offload */
#define COMP_FL_DIR_REQ 0x00000002 /* Compress requests */
#define COMP_FL_DIR_RES 0x00000004 /* Compress responses */
struct comp {
struct comp_algo *algos_res; /* Algos available for response */
struct comp_algo *algo_req; /* Algo to use for request */

View File

@ -130,17 +130,109 @@ comp_strm_deinit(struct stream *s, struct filter *filter)
filter->ctx = NULL;
}
static void
comp_prepare_compress_request(struct comp_state *st, struct stream *s, struct http_msg *msg)
{
struct htx *htx = htxbuf(&msg->chn->buf);
struct http_txn *txn = s->txn;
struct http_hdr_ctx ctx;
struct comp_type *comp_type;
ctx.blk = NULL;
/* Already compressed, don't bother */
if (http_find_header(htx, ist("Content-Encoding"), &ctx, 1))
return;
/* HTTP < 1.1 should not be compressed */
if (!(msg->flags & HTTP_MSGF_VER_11) || !(txn->req.flags & HTTP_MSGF_VER_11))
return;
comp_type = NULL;
/*
* We don't want to compress content-types not listed in the "compression type" directive if any. If no content-type was found but configuration
* requires one, we don't compress either. Backend has the priority.
*/
ctx.blk = NULL;
if (http_find_header(htx, ist("Content-Type"), &ctx, 1)) {
if ((s->be->comp && (comp_type = s->be->comp->types_req)) ||
(strm_fe(s)->comp && (comp_type = strm_fe(s)->comp->types_req))) {
for (; comp_type; comp_type = comp_type->next) {
if (ctx.value.len >= comp_type->name_len &&
strncasecmp(ctx.value.ptr, comp_type->name, comp_type->name_len) == 0)
/* this Content-Type should be compressed */
break;
}
/* this Content-Type should not be compressed */
if (comp_type == NULL)
goto fail;
}
}
else { /* no content-type header */
if ((s->be->comp && s->be->comp->types_req) ||
(strm_fe(s)->comp && strm_fe(s)->comp->types_req))
goto fail; /* a content-type was required */
}
/* limit compression rate */
if (global.comp_rate_lim > 0)
if (read_freq_ctr(&global.comp_bps_in) > global.comp_rate_lim)
goto fail;
/* limit cpu usage */
if (th_ctx->idle_pct < compress_min_idle)
goto fail;
if (txn->meth == HTTP_METH_HEAD)
return;
if (s->be->comp->algo_req != NULL)
st->comp_algo[COMP_DIR_REQ] = s->be->comp->algo_req;
else if (strm_fe(s)->comp->algo_req != NULL)
st->comp_algo[COMP_DIR_REQ] = strm_fe(s)->comp->algo_req;
/* limit compression rate */
if (global.comp_rate_lim > 0)
if (read_freq_ctr(&global.comp_bps_in) > global.comp_rate_lim)
goto fail;
/* limit cpu usage */
if (th_ctx->idle_pct < compress_min_idle)
goto fail;
/* initialize compression */
if (st->comp_algo[COMP_DIR_REQ]->init(&st->comp_ctx[COMP_DIR_REQ], global.tune.comp_maxlevel) < 0)
goto fail;
return;
fail:
st->comp_algo[COMP_DIR_REQ] = NULL;
}
static int
comp_http_headers(struct stream *s, struct filter *filter, struct http_msg *msg)
{
struct comp_state *st = filter->ctx;
int comp_flags = 0;
if (!strm_fe(s)->comp && !s->be->comp)
goto end;
if (strm_fe(s)->comp)
comp_flags |= strm_fe(s)->comp->flags;
if (s->be->comp)
comp_flags |= s->be->comp->flags;
if (!(msg->chn->flags & CF_ISRESP))
select_compression_request_header(st, s, msg);
else {
if (!(msg->chn->flags & CF_ISRESP)) {
if (comp_flags & COMP_FL_DIR_REQ) {
comp_prepare_compress_request(st, s, msg);
if (st->comp_algo[COMP_DIR_REQ]) {
if (!set_compression_header(st, s, msg))
goto end;
register_data_filter(s, msg->chn, filter);
st->flags |= COMP_STATE_PROCESSING;
}
}
if (comp_flags & COMP_FL_DIR_RES)
select_compression_request_header(st, s, msg);
} else if (comp_flags & COMP_FL_DIR_RES) {
/* Response headers have already been checked in
* comp_http_post_analyze callback. */
if (st->comp_algo[COMP_DIR_RES]) {
@ -669,6 +761,8 @@ parse_compression_options(char **args, int section, struct proxy *proxy,
if (proxy->comp == NULL) {
comp = calloc(1, sizeof(*comp));
/* Always default to compress responses */
comp->flags = COMP_FL_DIR_RES;
proxy->comp = comp;
}
else
@ -709,6 +803,30 @@ parse_compression_options(char **args, int section, struct proxy *proxy,
continue;
}
}
else if (strcmp(args[1], "algo-req") == 0) {
struct comp_ctx *ctx;
int retval = comp_append_algo(&comp->algo_req, args[2]);
if (retval) {
if (retval < 0)
memprintf(err, "'%s' : '%s' is not a supported algorithm.",
args[0], args[2]);
else
memprintf(err, "'%s' : out of memory while parsing algo '%s'.",
args[0], args[2]);
ret = -1;
goto end;
}
if (proxy->comp->algo_req->init(&ctx, 9) == 0)
proxy->comp->algo_req->end(&ctx);
else {
memprintf(err, "'%s' : Can't init '%s' algorithm.",
args[0], args[2]);
ret = -1;
goto end;
}
}
else if (strcmp(args[1], "offload") == 0) {
if (proxy->cap & PR_CAP_DEF) {
memprintf(err, "'%s' : '%s' ignored in 'defaults' section.",
@ -735,8 +853,46 @@ parse_compression_options(char **args, int section, struct proxy *proxy,
continue;
}
}
else if (strcmp(args[1], "type-req") == 0) {
int cur_arg = 2;
if (!*args[cur_arg]) {
memprintf(err, "'%s' expects <type>.", args[0]);
ret = -1;
goto end;
}
while (*(args[cur_arg])) {
if (comp_append_type(&comp->types_req, args[cur_arg])) {
memprintf(err, "'%s': out of memory.", args[0]);
ret = -1;
goto end;
}
cur_arg++;
continue;
}
}
else if (strcmp(args[1], "direction") == 0) {
if (!args[2]) {
memprintf(err, "'%s' expects 'request', 'response', or 'both'.", args[0]);
ret = -1;
goto end;
}
if (strcmp(args[2], "request") == 0) {
comp->flags &= ~COMP_FL_DIR_RES;
comp->flags |= COMP_FL_DIR_REQ;
} else if (strcmp(args[2], "response") == 0) {
comp->flags &= COMP_FL_DIR_REQ;
comp->flags |= COMP_FL_DIR_RES;
} else if (strcmp(args[2], "both") == 0)
comp->flags |= COMP_FL_DIR_REQ | COMP_FL_DIR_RES;
else {
memprintf(err, "'%s' expects 'request', 'response', or 'both'.", args[0]);
ret = -1;
goto end;
}
}
else {
memprintf(err, "'%s' expects 'algo', 'type' or 'offload'",
memprintf(err, "'%s' expects 'algo', 'type' 'direction' or 'offload'",
args[0]);
ret = -1;
goto end;