blob: 08f684e511fda5d982f96c08bcf09d70ac7a810f [file] [log] [blame]
Christopher Faulet3d97c902015-12-09 14:59:38 +01001/*
2 * Stream filters related variables and functions.
3 *
4 * Copyright (C) 2015 Qualys Inc., Christopher Faulet <cfaulet@qualys.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020013#include <haproxy/api.h>
Willy Tarreau6be78492020-06-05 00:00:29 +020014#include <haproxy/cfgparse.h>
Willy Tarreau0a3bd392020-06-04 08:52:38 +020015#include <haproxy/compression.h>
Willy Tarreau2741c8c2020-06-02 11:28:02 +020016#include <haproxy/dynbuf.h>
Willy Tarreauc7babd82020-06-04 21:29:29 +020017#include <haproxy/filters.h>
Willy Tarreaucd72d8c2020-06-02 19:11:26 +020018#include <haproxy/http.h>
Willy Tarreauc2b1ff02020-06-04 21:21:03 +020019#include <haproxy/http_ana-t.h>
Willy Tarreau87735332020-06-04 09:08:41 +020020#include <haproxy/http_htx.h>
Willy Tarreau16f958c2020-06-03 08:44:35 +020021#include <haproxy/htx.h>
Willy Tarreau853b2972020-05-27 18:01:47 +020022#include <haproxy/list.h>
Willy Tarreau202f93d2021-05-08 20:34:16 +020023#include <haproxy/proxy.h>
Willy Tarreaue6ce10b2020-06-04 15:33:47 +020024#include <haproxy/sample.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020025#include <haproxy/stream.h>
Willy Tarreau48fbcae2020-06-03 18:09:46 +020026#include <haproxy/tools.h>
Christopher Faulet3d97c902015-12-09 14:59:38 +010027
Christopher Faulet12554d02021-06-09 17:12:44 +020028#define COMP_STATE_PROCESSING 0x01
29
Christopher Fauletf4a4ef72018-12-07 17:39:53 +010030const char *http_comp_flt_id = "compression filter";
Christopher Faulet92d36382015-11-05 13:35:03 +010031
32struct flt_ops comp_ops;
33
Christopher Faulet92d36382015-11-05 13:35:03 +010034struct comp_state {
35 struct comp_ctx *comp_ctx; /* compression context */
36 struct comp_algo *comp_algo; /* compression algorithm if not NULL */
Christopher Faulet12554d02021-06-09 17:12:44 +020037 unsigned int flags; /* COMP_STATE_* */
Christopher Faulet92d36382015-11-05 13:35:03 +010038};
39
Willy Tarreau8ceae722018-11-26 11:58:30 +010040/* Pools used to allocate comp_state structs */
41DECLARE_STATIC_POOL(pool_head_comp_state, "comp_state", sizeof(struct comp_state));
42
43static THREAD_LOCAL struct buffer tmpbuf;
44static THREAD_LOCAL struct buffer zbuf;
Willy Tarreau8ceae722018-11-26 11:58:30 +010045
Christopher Faulet92d36382015-11-05 13:35:03 +010046static int select_compression_request_header(struct comp_state *st,
47 struct stream *s,
48 struct http_msg *msg);
49static int select_compression_response_header(struct comp_state *st,
50 struct stream *s,
51 struct http_msg *msg);
Christopher Faulet27d93c32018-12-15 22:32:02 +010052static int set_compression_response_header(struct comp_state *st,
53 struct stream *s,
54 struct http_msg *msg);
Christopher Faulet92d36382015-11-05 13:35:03 +010055
Christopher Faulete6902cd2018-11-30 22:29:48 +010056static int htx_compression_buffer_init(struct htx *htx, struct buffer *out);
57static int htx_compression_buffer_add_data(struct comp_state *st, const char *data, size_t len,
58 struct buffer *out);
59static int htx_compression_buffer_end(struct comp_state *st, struct buffer *out, int end);
60
Christopher Faulet92d36382015-11-05 13:35:03 +010061/***********************************************************************/
62static int
Christopher Faulete6902cd2018-11-30 22:29:48 +010063comp_flt_init(struct proxy *px, struct flt_conf *fconf)
64{
Christopher Faulet6e540952018-12-03 22:43:41 +010065 fconf->flags |= FLT_CFG_FL_HTX;
Christopher Faulete6902cd2018-11-30 22:29:48 +010066 return 0;
67}
68
69static int
Christopher Faulet8ca3b4b2017-07-25 11:07:15 +020070comp_flt_init_per_thread(struct proxy *px, struct flt_conf *fconf)
Christopher Faulet92d36382015-11-05 13:35:03 +010071{
Willy Tarreau862ad822021-03-22 16:16:22 +010072 if (b_alloc(&tmpbuf) == NULL)
Christopher Fauletb77c5c22015-12-07 16:48:42 +010073 return -1;
Willy Tarreau862ad822021-03-22 16:16:22 +010074 if (b_alloc(&zbuf) == NULL)
Christopher Fauletb77c5c22015-12-07 16:48:42 +010075 return -1;
Christopher Faulet92d36382015-11-05 13:35:03 +010076 return 0;
77}
78
79static void
Christopher Faulet8ca3b4b2017-07-25 11:07:15 +020080comp_flt_deinit_per_thread(struct proxy *px, struct flt_conf *fconf)
Christopher Faulet92d36382015-11-05 13:35:03 +010081{
Willy Tarreauc9fa0482018-07-10 17:43:27 +020082 if (tmpbuf.size)
Christopher Faulet92d36382015-11-05 13:35:03 +010083 b_free(&tmpbuf);
Willy Tarreauc9fa0482018-07-10 17:43:27 +020084 if (zbuf.size)
Christopher Fauletb77c5c22015-12-07 16:48:42 +010085 b_free(&zbuf);
Christopher Faulet92d36382015-11-05 13:35:03 +010086}
87
88static int
Christopher Faulet5e896512020-03-06 14:59:05 +010089comp_strm_init(struct stream *s, struct filter *filter)
Christopher Faulet92d36382015-11-05 13:35:03 +010090{
Christopher Faulet5e896512020-03-06 14:59:05 +010091 struct comp_state *st;
Christopher Faulet8ca3b4b2017-07-25 11:07:15 +020092
Willy Tarreau5bfeb212021-03-22 15:08:17 +010093 st = pool_alloc(pool_head_comp_state);
Christopher Faulet5e896512020-03-06 14:59:05 +010094 if (st == NULL)
95 return -1;
Christopher Faulet92d36382015-11-05 13:35:03 +010096
Christopher Faulet5e896512020-03-06 14:59:05 +010097 st->comp_algo = NULL;
98 st->comp_ctx = NULL;
Christopher Faulet12554d02021-06-09 17:12:44 +020099 st->flags = 0;
Christopher Faulet5e896512020-03-06 14:59:05 +0100100 filter->ctx = st;
Christopher Faulet3dc860d2017-09-15 11:39:36 +0200101
Christopher Faulet5e896512020-03-06 14:59:05 +0100102 /* Register post-analyzer on AN_RES_WAIT_HTTP because we need to
103 * analyze response headers before http-response rules execution
104 * to be sure we can use res.comp and res.comp_algo sample
105 * fetches */
106 filter->post_analyzers |= AN_RES_WAIT_HTTP;
Christopher Faulet92d36382015-11-05 13:35:03 +0100107 return 1;
108}
109
Christopher Faulet5e896512020-03-06 14:59:05 +0100110static void
111comp_strm_deinit(struct stream *s, struct filter *filter)
Christopher Faulet92d36382015-11-05 13:35:03 +0100112{
113 struct comp_state *st = filter->ctx;
Christopher Faulet92d36382015-11-05 13:35:03 +0100114
Christopher Fauletd60b3cf2017-06-26 11:47:13 +0200115 if (!st)
Christopher Faulet5e896512020-03-06 14:59:05 +0100116 return;
Christopher Faulet92d36382015-11-05 13:35:03 +0100117
Christopher Faulet92d36382015-11-05 13:35:03 +0100118 /* release any possible compression context */
Christopher Fauletd60b3cf2017-06-26 11:47:13 +0200119 if (st->comp_algo)
120 st->comp_algo->end(&st->comp_ctx);
Willy Tarreaubafbe012017-11-24 17:34:44 +0100121 pool_free(pool_head_comp_state, st);
Christopher Faulet92d36382015-11-05 13:35:03 +0100122 filter->ctx = NULL;
Christopher Faulet92d36382015-11-05 13:35:03 +0100123}
124
125static int
Christopher Faulet1339d742016-05-11 16:48:33 +0200126comp_http_headers(struct stream *s, struct filter *filter, struct http_msg *msg)
127{
128 struct comp_state *st = filter->ctx;
129
130 if (!strm_fe(s)->comp && !s->be->comp)
131 goto end;
132
133 if (!(msg->chn->flags & CF_ISRESP))
134 select_compression_request_header(st, s, msg);
135 else {
Christopher Faulet3dc860d2017-09-15 11:39:36 +0200136 /* Response headers have already been checked in
137 * comp_http_post_analyze callback. */
Christopher Faulet1339d742016-05-11 16:48:33 +0200138 if (st->comp_algo) {
Christopher Faulet27d93c32018-12-15 22:32:02 +0100139 if (!set_compression_response_header(st, s, msg))
140 goto end;
Christopher Faulet1339d742016-05-11 16:48:33 +0200141 register_data_filter(s, msg->chn, filter);
Christopher Faulet12554d02021-06-09 17:12:44 +0200142 st->flags |= COMP_STATE_PROCESSING;
Christopher Faulet1339d742016-05-11 16:48:33 +0200143 }
144 }
145
146 end:
147 return 1;
148}
149
150static int
Christopher Faulet3dc860d2017-09-15 11:39:36 +0200151comp_http_post_analyze(struct stream *s, struct filter *filter,
152 struct channel *chn, unsigned an_bit)
153{
154 struct http_txn *txn = s->txn;
155 struct http_msg *msg = &txn->rsp;
156 struct comp_state *st = filter->ctx;
157
158 if (an_bit != AN_RES_WAIT_HTTP)
159 goto end;
160
161 if (!strm_fe(s)->comp && !s->be->comp)
162 goto end;
163
164 select_compression_response_header(st, s, msg);
165
166 end:
167 return 1;
168}
169
170static int
Christopher Faulete6902cd2018-11-30 22:29:48 +0100171comp_http_payload(struct stream *s, struct filter *filter, struct http_msg *msg,
172 unsigned int offset, unsigned int len)
173{
174 struct comp_state *st = filter->ctx;
Christopher Faulet27ba2dc2018-12-05 11:53:24 +0100175 struct htx *htx = htxbuf(&msg->chn->buf);
Christopher Faulete6a62bf2020-03-02 16:20:05 +0100176 struct htx_ret htxret = htx_find_offset(htx, offset);
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100177 struct htx_blk *blk, *next;
178 int ret, consumed = 0, to_forward = 0, last = 0;
Christopher Faulete6902cd2018-11-30 22:29:48 +0100179
Christopher Faulete6a62bf2020-03-02 16:20:05 +0100180 blk = htxret.blk;
181 offset = htxret.ret;
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100182 for (next = NULL; blk && len; blk = next) {
Christopher Faulete6902cd2018-11-30 22:29:48 +0100183 enum htx_blk_type type = htx_get_blk_type(blk);
184 uint32_t sz = htx_get_blksz(blk);
185 struct ist v;
186
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100187 next = htx_get_next_blk(htx, blk);
188 while (next && htx_get_blk_type(next) == HTX_BLK_UNUSED)
Christopher Faulet86ca0e52021-06-09 16:59:02 +0200189 next = htx_get_next_blk(htx, next);
Christopher Faulete6902cd2018-11-30 22:29:48 +0100190
Christopher Faulet12554d02021-06-09 17:12:44 +0200191 if (!(st->flags & COMP_STATE_PROCESSING))
192 goto consume;
193
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100194 if (htx_compression_buffer_init(htx, &trash) < 0) {
195 msg->chn->flags |= CF_WAKE_WRITE;
196 goto end;
197 }
198
199 switch (type) {
Christopher Faulete6902cd2018-11-30 22:29:48 +0100200 case HTX_BLK_DATA:
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100201 /* it is the last data block */
202 last = ((!next && (htx->flags & HTX_FL_EOM)) || (next && htx_get_blk_type(next) != HTX_BLK_DATA));
Christopher Faulete6902cd2018-11-30 22:29:48 +0100203 v = htx_get_blk_value(htx, blk);
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100204 v = istadv(v, offset);
205 if (v.len > len) {
206 last = 0;
Christopher Faulete6902cd2018-11-30 22:29:48 +0100207 v.len = len;
Christopher Faulete6902cd2018-11-30 22:29:48 +0100208 }
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100209
Christopher Faulete6902cd2018-11-30 22:29:48 +0100210 ret = htx_compression_buffer_add_data(st, v.ptr, v.len, &trash);
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100211 if (ret < 0 || htx_compression_buffer_end(st, &trash, last) < 0)
Christopher Faulete6902cd2018-11-30 22:29:48 +0100212 goto error;
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100213 BUG_ON(v.len != ret);
214
215 if (ret == sz && !b_data(&trash))
216 next = htx_remove_blk(htx, blk);
Christopher Faulet402740c2021-06-09 17:04:37 +0200217 else {
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100218 blk = htx_replace_blk_value(htx, blk, v, ist2(b_head(&trash), b_data(&trash)));
Christopher Faulet402740c2021-06-09 17:04:37 +0200219 next = htx_get_next_blk(htx, blk);
220 }
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100221
Christopher Faulete6902cd2018-11-30 22:29:48 +0100222 len -= ret;
223 consumed += ret;
224 to_forward += b_data(&trash);
Christopher Faulet12554d02021-06-09 17:12:44 +0200225 if (last)
226 st->flags &= ~COMP_STATE_PROCESSING;
Christopher Faulete6902cd2018-11-30 22:29:48 +0100227 break;
228
Christopher Faulete6902cd2018-11-30 22:29:48 +0100229 case HTX_BLK_TLR:
Christopher Faulet2d7c5392019-06-03 10:41:26 +0200230 case HTX_BLK_EOT:
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100231 if (htx_compression_buffer_end(st, &trash, 1) < 0)
232 goto error;
233 if (b_data(&trash)) {
234 struct htx_blk *last = htx_add_last_data(htx, ist2(b_head(&trash), b_data(&trash)));
235 if (!last)
Christopher Faulete6902cd2018-11-30 22:29:48 +0100236 goto error;
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100237 blk = htx_get_next_blk(htx, last);
238 if (!blk)
239 goto error;
Christopher Faulet402740c2021-06-09 17:04:37 +0200240 next = htx_get_next_blk(htx, blk);
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100241 to_forward += b_data(&trash);
Christopher Faulete6902cd2018-11-30 22:29:48 +0100242 }
Christopher Faulet12554d02021-06-09 17:12:44 +0200243 st->flags &= ~COMP_STATE_PROCESSING;
Christopher Faulete6902cd2018-11-30 22:29:48 +0100244 /* fall through */
245
246 default:
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100247 consume:
Christopher Faulete6902cd2018-11-30 22:29:48 +0100248 sz -= offset;
249 if (sz > len)
250 sz = len;
251 consumed += sz;
252 to_forward += sz;
253 len -= sz;
254 break;
255 }
256
257 offset = 0;
Christopher Faulete6902cd2018-11-30 22:29:48 +0100258 }
259
260 end:
261 if (to_forward != consumed)
262 flt_update_offsets(filter, msg->chn, to_forward - consumed);
263
264 if (st->comp_ctx && st->comp_ctx->cur_lvl > 0) {
Willy Tarreauef6fd852019-02-04 11:48:03 +0100265 update_freq_ctr(&global.comp_bps_in, consumed);
Olivier Houchard43da3432019-03-08 18:50:27 +0100266 _HA_ATOMIC_ADD(&strm_fe(s)->fe_counters.comp_in, consumed);
267 _HA_ATOMIC_ADD(&s->be->be_counters.comp_in, consumed);
Christopher Faulete6902cd2018-11-30 22:29:48 +0100268 update_freq_ctr(&global.comp_bps_out, to_forward);
Olivier Houchard43da3432019-03-08 18:50:27 +0100269 _HA_ATOMIC_ADD(&strm_fe(s)->fe_counters.comp_out, to_forward);
270 _HA_ATOMIC_ADD(&s->be->be_counters.comp_out, to_forward);
Willy Tarreauef6fd852019-02-04 11:48:03 +0100271 } else {
Olivier Houchard43da3432019-03-08 18:50:27 +0100272 _HA_ATOMIC_ADD(&strm_fe(s)->fe_counters.comp_byp, consumed);
273 _HA_ATOMIC_ADD(&s->be->be_counters.comp_byp, consumed);
Christopher Faulete6902cd2018-11-30 22:29:48 +0100274 }
275 return to_forward;
276
277 error:
278 return -1;
279}
280
Christopher Faulet2fb28802015-12-01 10:40:57 +0100281
Christopher Faulet92d36382015-11-05 13:35:03 +0100282static int
Christopher Fauletd60b3cf2017-06-26 11:47:13 +0200283comp_http_end(struct stream *s, struct filter *filter,
284 struct http_msg *msg)
285{
286 struct comp_state *st = filter->ctx;
287
288 if (!(msg->chn->flags & CF_ISRESP) || !st || !st->comp_algo)
289 goto end;
290
291 if (strm_fe(s)->mode == PR_MODE_HTTP)
Willy Tarreau4781b152021-04-06 13:53:36 +0200292 _HA_ATOMIC_INC(&strm_fe(s)->fe_counters.p.http.comp_rsp);
Christopher Fauletd60b3cf2017-06-26 11:47:13 +0200293 if ((s->flags & SF_BE_ASSIGNED) && (s->be->mode == PR_MODE_HTTP))
Willy Tarreau4781b152021-04-06 13:53:36 +0200294 _HA_ATOMIC_INC(&s->be->be_counters.p.http.comp_rsp);
Christopher Fauletd60b3cf2017-06-26 11:47:13 +0200295 end:
296 return 1;
297}
Christopher Faulet27d93c32018-12-15 22:32:02 +0100298
Christopher Faulet89f2b162019-07-15 21:16:04 +0200299/***********************************************************************/
Christopher Faulet27d93c32018-12-15 22:32:02 +0100300static int
Christopher Faulet89f2b162019-07-15 21:16:04 +0200301set_compression_response_header(struct comp_state *st, struct stream *s, struct http_msg *msg)
Christopher Faulet27d93c32018-12-15 22:32:02 +0100302{
303 struct htx *htx = htxbuf(&msg->chn->buf);
Tim Duesterhusb229f012019-01-29 16:38:56 +0100304 struct http_hdr_ctx ctx;
Christopher Faulet27d93c32018-12-15 22:32:02 +0100305
306 /*
307 * Add Content-Encoding header when it's not identity encoding.
308 * RFC 2616 : Identity encoding: This content-coding is used only in the
309 * Accept-Encoding header, and SHOULD NOT be used in the Content-Encoding
310 * header.
311 */
312 if (st->comp_algo->cfg_name_len != 8 || memcmp(st->comp_algo->cfg_name, "identity", 8) != 0) {
313 struct ist v = ist2(st->comp_algo->ua_name, st->comp_algo->ua_name_len);
314
315 if (!http_add_header(htx, ist("Content-Encoding"), v))
316 goto error;
317 }
318
319 /* remove Content-Length header */
320 if (msg->flags & HTTP_MSGF_CNT_LEN) {
Christopher Faulet27d93c32018-12-15 22:32:02 +0100321 ctx.blk = NULL;
322 while (http_find_header(htx, ist("Content-Length"), &ctx, 1))
323 http_remove_header(htx, &ctx);
324 }
325
326 /* add "Transfer-Encoding: chunked" header */
327 if (!(msg->flags & HTTP_MSGF_TE_CHNK)) {
328 if (!http_add_header(htx, ist("Transfer-Encoding"), ist("chunked")))
329 goto error;
330 }
331
Tim Duesterhusb229f012019-01-29 16:38:56 +0100332 /* convert "ETag" header to a weak ETag */
333 ctx.blk = NULL;
334 if (http_find_header(htx, ist("ETag"), &ctx, 1)) {
335 if (ctx.value.ptr[0] == '"') {
336 /* This a strong ETag. Convert it to a weak one. */
337 struct ist v = ist2(trash.area, 0);
338 if (istcat(&v, ist("W/"), trash.size) == -1 || istcat(&v, ctx.value, trash.size) == -1)
339 goto error;
340
341 if (!http_replace_header_value(htx, &ctx, v))
342 goto error;
343 }
344 }
345
Tim Duesterhus721d6862019-06-17 16:10:07 +0200346 if (!http_add_header(htx, ist("Vary"), ist("Accept-Encoding")))
347 goto error;
348
Christopher Faulet27d93c32018-12-15 22:32:02 +0100349 return 1;
350
351 error:
352 st->comp_algo->end(&st->comp_ctx);
353 st->comp_algo = NULL;
354 return 0;
355}
356
Christopher Faulet3d97c902015-12-09 14:59:38 +0100357/*
358 * Selects a compression algorithm depending on the client request.
359 */
Christopher Faulete6902cd2018-11-30 22:29:48 +0100360static int
Christopher Faulet89f2b162019-07-15 21:16:04 +0200361select_compression_request_header(struct comp_state *st, struct stream *s, struct http_msg *msg)
Christopher Faulete6902cd2018-11-30 22:29:48 +0100362{
Christopher Faulet27ba2dc2018-12-05 11:53:24 +0100363 struct htx *htx = htxbuf(&msg->chn->buf);
Christopher Faulete6902cd2018-11-30 22:29:48 +0100364 struct http_hdr_ctx ctx;
365 struct comp_algo *comp_algo = NULL;
366 struct comp_algo *comp_algo_back = NULL;
367
368 /* Disable compression for older user agents announcing themselves as "Mozilla/4"
369 * unless they are known good (MSIE 6 with XP SP2, or MSIE 7 and later).
370 * See http://zoompf.com/2012/02/lose-the-wait-http-compression for more details.
371 */
372 ctx.blk = NULL;
373 if (http_find_header(htx, ist("User-Agent"), &ctx, 1) &&
374 ctx.value.len >= 9 &&
375 memcmp(ctx.value.ptr, "Mozilla/4", 9) == 0 &&
376 (ctx.value.len < 31 ||
377 memcmp(ctx.value.ptr + 25, "MSIE ", 5) != 0 ||
378 *(ctx.value.ptr + 30) < '6' ||
379 (*(ctx.value.ptr + 30) == '6' &&
380 (ctx.value.len < 54 || memcmp(ctx.value.ptr + 51, "SV1", 3) != 0)))) {
381 st->comp_algo = NULL;
382 return 0;
383 }
384
385 /* search for the algo in the backend in priority or the frontend */
386 if ((s->be->comp && (comp_algo_back = s->be->comp->algos)) ||
387 (strm_fe(s)->comp && (comp_algo_back = strm_fe(s)->comp->algos))) {
388 int best_q = 0;
389
390 ctx.blk = NULL;
391 while (http_find_header(htx, ist("Accept-Encoding"), &ctx, 0)) {
392 const char *qval;
393 int q;
394 int toklen;
395
396 /* try to isolate the token from the optional q-value */
397 toklen = 0;
398 while (toklen < ctx.value.len && HTTP_IS_TOKEN(*(ctx.value.ptr + toklen)))
399 toklen++;
400
401 qval = ctx.value.ptr + toklen;
402 while (1) {
Tim Duesterhus77508502022-03-15 13:11:06 +0100403 while (qval < istend(ctx.value) && HTTP_IS_LWS(*qval))
Christopher Faulete6902cd2018-11-30 22:29:48 +0100404 qval++;
405
Tim Duesterhus77508502022-03-15 13:11:06 +0100406 if (qval >= istend(ctx.value) || *qval != ';') {
Christopher Faulete6902cd2018-11-30 22:29:48 +0100407 qval = NULL;
408 break;
409 }
410 qval++;
411
Tim Duesterhus77508502022-03-15 13:11:06 +0100412 while (qval < istend(ctx.value) && HTTP_IS_LWS(*qval))
Christopher Faulete6902cd2018-11-30 22:29:48 +0100413 qval++;
414
Tim Duesterhus77508502022-03-15 13:11:06 +0100415 if (qval >= istend(ctx.value)) {
Christopher Faulete6902cd2018-11-30 22:29:48 +0100416 qval = NULL;
417 break;
418 }
Tim Duesterhus77508502022-03-15 13:11:06 +0100419 if (strncmp(qval, "q=", MIN(istend(ctx.value) - qval, 2)) == 0)
Christopher Faulete6902cd2018-11-30 22:29:48 +0100420 break;
421
Tim Duesterhus77508502022-03-15 13:11:06 +0100422 while (qval < istend(ctx.value) && *qval != ';')
Christopher Faulete6902cd2018-11-30 22:29:48 +0100423 qval++;
424 }
425
426 /* here we have qval pointing to the first "q=" attribute or NULL if not found */
427 q = qval ? http_parse_qvalue(qval + 2, NULL) : 1000;
428
429 if (q <= best_q)
430 continue;
431
432 for (comp_algo = comp_algo_back; comp_algo; comp_algo = comp_algo->next) {
433 if (*(ctx.value.ptr) == '*' ||
434 word_match(ctx.value.ptr, toklen, comp_algo->ua_name, comp_algo->ua_name_len)) {
435 st->comp_algo = comp_algo;
436 best_q = q;
437 break;
438 }
439 }
440 }
441 }
442
443 /* remove all occurrences of the header when "compression offload" is set */
444 if (st->comp_algo) {
445 if ((s->be->comp && s->be->comp->offload) ||
446 (strm_fe(s)->comp && strm_fe(s)->comp->offload)) {
447 http_remove_header(htx, &ctx);
448 ctx.blk = NULL;
449 while (http_find_header(htx, ist("Accept-Encoding"), &ctx, 1))
450 http_remove_header(htx, &ctx);
451 }
Christopher Faulet3d97c902015-12-09 14:59:38 +0100452 return 1;
453 }
454
455 /* identity is implicit does not require headers */
Christopher Faulet92d36382015-11-05 13:35:03 +0100456 if ((s->be->comp && (comp_algo_back = s->be->comp->algos)) ||
457 (strm_fe(s)->comp && (comp_algo_back = strm_fe(s)->comp->algos))) {
Christopher Faulet3d97c902015-12-09 14:59:38 +0100458 for (comp_algo = comp_algo_back; comp_algo; comp_algo = comp_algo->next) {
459 if (comp_algo->cfg_name_len == 8 && memcmp(comp_algo->cfg_name, "identity", 8) == 0) {
Christopher Faulet92d36382015-11-05 13:35:03 +0100460 st->comp_algo = comp_algo;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100461 return 1;
462 }
463 }
464 }
465
Christopher Faulet92d36382015-11-05 13:35:03 +0100466 st->comp_algo = NULL;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100467 return 0;
468}
469
470/*
Ilya Shipitsin46a030c2020-07-05 16:36:08 +0500471 * Selects a compression algorithm depending of the server response.
Christopher Faulet3d97c902015-12-09 14:59:38 +0100472 */
Christopher Faulet92d36382015-11-05 13:35:03 +0100473static int
Christopher Faulet89f2b162019-07-15 21:16:04 +0200474select_compression_response_header(struct comp_state *st, struct stream *s, struct http_msg *msg)
Christopher Faulete6902cd2018-11-30 22:29:48 +0100475{
Christopher Faulet27ba2dc2018-12-05 11:53:24 +0100476 struct htx *htx = htxbuf(&msg->chn->buf);
Christopher Faulete6902cd2018-11-30 22:29:48 +0100477 struct http_txn *txn = s->txn;
478 struct http_hdr_ctx ctx;
479 struct comp_type *comp_type;
480
481 /* no common compression algorithm was found in request header */
482 if (st->comp_algo == NULL)
483 goto fail;
484
Christopher Faulet1d3613a2019-01-07 14:41:59 +0100485 /* compression already in progress */
486 if (msg->flags & HTTP_MSGF_COMPRESSING)
487 goto fail;
488
Christopher Faulete6902cd2018-11-30 22:29:48 +0100489 /* HTTP < 1.1 should not be compressed */
490 if (!(msg->flags & HTTP_MSGF_VER_11) || !(txn->req.flags & HTTP_MSGF_VER_11))
491 goto fail;
492
493 if (txn->meth == HTTP_METH_HEAD)
494 goto fail;
495
496 /* compress 200,201,202,203 responses only */
497 if ((txn->status != 200) &&
498 (txn->status != 201) &&
499 (txn->status != 202) &&
500 (txn->status != 203))
501 goto fail;
502
Christopher Fauletc963eb22018-12-21 14:53:54 +0100503 if (!(msg->flags & HTTP_MSGF_XFER_LEN) || msg->flags & HTTP_MSGF_BODYLESS)
Christopher Faulete6902cd2018-11-30 22:29:48 +0100504 goto fail;
505
506 /* content is already compressed */
507 ctx.blk = NULL;
508 if (http_find_header(htx, ist("Content-Encoding"), &ctx, 1))
509 goto fail;
510
511 /* no compression when Cache-Control: no-transform is present in the message */
512 ctx.blk = NULL;
513 while (http_find_header(htx, ist("Cache-Control"), &ctx, 0)) {
514 if (word_match(ctx.value.ptr, ctx.value.len, "no-transform", 12))
515 goto fail;
516 }
517
Tim Duesterhusb229f012019-01-29 16:38:56 +0100518 /* no compression when ETag is malformed */
519 ctx.blk = NULL;
520 if (http_find_header(htx, ist("ETag"), &ctx, 1)) {
Tim Duesterhus6414cd12020-09-01 18:32:35 +0200521 if (http_get_etag_type(ctx.value) == ETAG_INVALID)
Tim Duesterhusb229f012019-01-29 16:38:56 +0100522 goto fail;
Tim Duesterhusb229f012019-01-29 16:38:56 +0100523 }
524 /* no compression when multiple ETags are present
525 * Note: Do not reset ctx.blk!
526 */
527 if (http_find_header(htx, ist("ETag"), &ctx, 1))
528 goto fail;
529
Christopher Faulete6902cd2018-11-30 22:29:48 +0100530 comp_type = NULL;
531
532 /* we don't want to compress multipart content-types, nor content-types that are
533 * not listed in the "compression type" directive if any. If no content-type was
534 * found but configuration requires one, we don't compress either. Backend has
535 * the priority.
536 */
537 ctx.blk = NULL;
538 if (http_find_header(htx, ist("Content-Type"), &ctx, 1)) {
539 if (ctx.value.len >= 9 && strncasecmp("multipart", ctx.value.ptr, 9) == 0)
540 goto fail;
541
542 if ((s->be->comp && (comp_type = s->be->comp->types)) ||
543 (strm_fe(s)->comp && (comp_type = strm_fe(s)->comp->types))) {
544 for (; comp_type; comp_type = comp_type->next) {
545 if (ctx.value.len >= comp_type->name_len &&
546 strncasecmp(ctx.value.ptr, comp_type->name, comp_type->name_len) == 0)
547 /* this Content-Type should be compressed */
548 break;
549 }
550 /* this Content-Type should not be compressed */
551 if (comp_type == NULL)
552 goto fail;
553 }
554 }
555 else { /* no content-type header */
556 if ((s->be->comp && s->be->comp->types) ||
557 (strm_fe(s)->comp && strm_fe(s)->comp->types))
558 goto fail; /* a content-type was required */
559 }
560
561 /* limit compression rate */
562 if (global.comp_rate_lim > 0)
563 if (read_freq_ctr(&global.comp_bps_in) > global.comp_rate_lim)
564 goto fail;
565
566 /* limit cpu usage */
Willy Tarreau45c38e22021-09-30 18:28:49 +0200567 if (th_ctx->idle_pct < compress_min_idle)
Christopher Faulete6902cd2018-11-30 22:29:48 +0100568 goto fail;
569
570 /* initialize compression */
571 if (st->comp_algo->init(&st->comp_ctx, global.tune.comp_maxlevel) < 0)
572 goto fail;
Christopher Faulete6902cd2018-11-30 22:29:48 +0100573 msg->flags |= HTTP_MSGF_COMPRESSING;
574 return 1;
575
Christopher Faulete6902cd2018-11-30 22:29:48 +0100576 fail:
577 st->comp_algo = NULL;
578 return 0;
579}
580
Christopher Faulet3d97c902015-12-09 14:59:38 +0100581/***********************************************************************/
Christopher Faulete6902cd2018-11-30 22:29:48 +0100582static int
583htx_compression_buffer_init(struct htx *htx, struct buffer *out)
584{
585 /* output stream requires at least 10 bytes for the gzip header, plus
586 * at least 8 bytes for the gzip trailer (crc+len), plus a possible
587 * plus at most 5 bytes per 32kB block and 2 bytes to close the stream.
588 */
589 if (htx_free_space(htx) < 20 + 5 * ((htx->data + 32767) >> 15))
590 return -1;
591 b_reset(out);
592 return 0;
593}
594
Christopher Faulete6902cd2018-11-30 22:29:48 +0100595static int
596htx_compression_buffer_add_data(struct comp_state *st, const char *data, size_t len,
597 struct buffer *out)
598{
599 return st->comp_algo->add_data(st->comp_ctx, data, len, out);
600}
601
Christopher Faulete6902cd2018-11-30 22:29:48 +0100602static int
603htx_compression_buffer_end(struct comp_state *st, struct buffer *out, int end)
604{
605 if (end)
606 return st->comp_algo->finish(st->comp_ctx, out);
607 else
608 return st->comp_algo->flush(st->comp_ctx, out);
609}
610
Christopher Faulet3d97c902015-12-09 14:59:38 +0100611
612/***********************************************************************/
Christopher Faulet92d36382015-11-05 13:35:03 +0100613struct flt_ops comp_ops = {
Christopher Faulete6902cd2018-11-30 22:29:48 +0100614 .init = comp_flt_init,
Christopher Faulet8ca3b4b2017-07-25 11:07:15 +0200615 .init_per_thread = comp_flt_init_per_thread,
616 .deinit_per_thread = comp_flt_deinit_per_thread,
Christopher Faulet92d36382015-11-05 13:35:03 +0100617
Christopher Faulet5e896512020-03-06 14:59:05 +0100618 .attach = comp_strm_init,
619 .detach = comp_strm_deinit,
620
Christopher Faulet3dc860d2017-09-15 11:39:36 +0200621 .channel_post_analyze = comp_http_post_analyze,
Christopher Faulet92d36382015-11-05 13:35:03 +0100622
Christopher Faulet1339d742016-05-11 16:48:33 +0200623 .http_headers = comp_http_headers,
Christopher Faulete6902cd2018-11-30 22:29:48 +0100624 .http_payload = comp_http_payload,
625 .http_end = comp_http_end,
Christopher Faulet92d36382015-11-05 13:35:03 +0100626};
627
Christopher Faulet3d97c902015-12-09 14:59:38 +0100628static int
629parse_compression_options(char **args, int section, struct proxy *proxy,
Willy Tarreau01825162021-03-09 09:53:46 +0100630 const struct proxy *defpx, const char *file, int line,
Christopher Faulet3d97c902015-12-09 14:59:38 +0100631 char **err)
632{
Christopher Faulet92d36382015-11-05 13:35:03 +0100633 struct comp *comp;
Christopher Faulet44d34bf2021-11-05 12:06:14 +0100634 int ret = 0;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100635
636 if (proxy->comp == NULL) {
Vincent Bernat02779b62016-04-03 13:48:43 +0200637 comp = calloc(1, sizeof(*comp));
Christopher Faulet3d97c902015-12-09 14:59:38 +0100638 proxy->comp = comp;
639 }
640 else
641 comp = proxy->comp;
642
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100643 if (strcmp(args[1], "algo") == 0) {
Christopher Faulet3d97c902015-12-09 14:59:38 +0100644 struct comp_ctx *ctx;
645 int cur_arg = 2;
646
647 if (!*args[cur_arg]) {
Christopher Faulet44d34bf2021-11-05 12:06:14 +0100648 memprintf(err, "parsing [%s:%d] : '%s' expects <algorithm>.",
Christopher Faulet3d97c902015-12-09 14:59:38 +0100649 file, line, args[0]);
Christopher Faulet44d34bf2021-11-05 12:06:14 +0100650 ret = -1;
651 goto end;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100652 }
653 while (*(args[cur_arg])) {
Remi Tricot-Le Breton6443bcc2021-05-17 10:35:08 +0200654 int retval = comp_append_algo(comp, args[cur_arg]);
655 if (retval) {
656 if (retval < 0)
Christopher Faulet44d34bf2021-11-05 12:06:14 +0100657 memprintf(err, "'%s' : '%s' is not a supported algorithm.",
Remi Tricot-Le Breton6443bcc2021-05-17 10:35:08 +0200658 args[0], args[cur_arg]);
659 else
Christopher Faulet44d34bf2021-11-05 12:06:14 +0100660 memprintf(err, "'%s' : out of memory while parsing algo '%s'.",
Remi Tricot-Le Breton6443bcc2021-05-17 10:35:08 +0200661 args[0], args[cur_arg]);
Christopher Faulet44d34bf2021-11-05 12:06:14 +0100662 ret = -1;
663 goto end;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100664 }
Remi Tricot-Le Breton6443bcc2021-05-17 10:35:08 +0200665
Christopher Faulet3d97c902015-12-09 14:59:38 +0100666 if (proxy->comp->algos->init(&ctx, 9) == 0)
667 proxy->comp->algos->end(&ctx);
668 else {
Christopher Faulet44d34bf2021-11-05 12:06:14 +0100669 memprintf(err, "'%s' : Can't init '%s' algorithm.",
Christopher Faulet3d97c902015-12-09 14:59:38 +0100670 args[0], args[cur_arg]);
Christopher Faulet44d34bf2021-11-05 12:06:14 +0100671 ret = -1;
672 goto end;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100673 }
674 cur_arg++;
675 continue;
676 }
677 }
Christopher Faulet44d34bf2021-11-05 12:06:14 +0100678 else if (strcmp(args[1], "offload") == 0) {
679 if (proxy->cap & PR_CAP_DEF) {
680 memprintf(err, "'%s' : '%s' ignored in 'defaults' section.",
681 args[0], args[1]);
682 ret = 1;
683 }
Christopher Faulet3d97c902015-12-09 14:59:38 +0100684 comp->offload = 1;
Christopher Faulet44d34bf2021-11-05 12:06:14 +0100685 }
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100686 else if (strcmp(args[1], "type") == 0) {
Christopher Faulet3d97c902015-12-09 14:59:38 +0100687 int cur_arg = 2;
688
689 if (!*args[cur_arg]) {
Christopher Faulet44d34bf2021-11-05 12:06:14 +0100690 memprintf(err, "'%s' expects <type>.", args[0]);
691 ret = -1;
692 goto end;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100693 }
694 while (*(args[cur_arg])) {
Remi Tricot-Le Breton6443bcc2021-05-17 10:35:08 +0200695 if (comp_append_type(comp, args[cur_arg])) {
696 memprintf(err, "'%s': out of memory.", args[0]);
Christopher Faulet44d34bf2021-11-05 12:06:14 +0100697 ret = -1;
698 goto end;
Remi Tricot-Le Breton6443bcc2021-05-17 10:35:08 +0200699 }
Christopher Faulet3d97c902015-12-09 14:59:38 +0100700 cur_arg++;
701 continue;
702 }
703 }
704 else {
Christopher Faulet44d34bf2021-11-05 12:06:14 +0100705 memprintf(err, "'%s' expects 'algo', 'type' or 'offload'",
Christopher Faulet3d97c902015-12-09 14:59:38 +0100706 args[0]);
Christopher Faulet44d34bf2021-11-05 12:06:14 +0100707 ret = -1;
708 goto end;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100709 }
710
Christopher Faulet44d34bf2021-11-05 12:06:14 +0100711 end:
712 return ret;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100713}
714
Christopher Faulet92d36382015-11-05 13:35:03 +0100715static int
716parse_http_comp_flt(char **args, int *cur_arg, struct proxy *px,
Thierry Fournier3610c392016-04-13 18:27:51 +0200717 struct flt_conf *fconf, char **err, void *private)
Christopher Faulet92d36382015-11-05 13:35:03 +0100718{
Christopher Faulet443ea1a2016-02-04 13:40:26 +0100719 struct flt_conf *fc, *back;
Christopher Faulet92d36382015-11-05 13:35:03 +0100720
Christopher Faulet443ea1a2016-02-04 13:40:26 +0100721 list_for_each_entry_safe(fc, back, &px->filter_configs, list) {
722 if (fc->id == http_comp_flt_id) {
Christopher Faulet92d36382015-11-05 13:35:03 +0100723 memprintf(err, "%s: Proxy supports only one compression filter\n", px->id);
724 return -1;
725 }
726 }
727
Christopher Faulet443ea1a2016-02-04 13:40:26 +0100728 fconf->id = http_comp_flt_id;
729 fconf->conf = NULL;
730 fconf->ops = &comp_ops;
Christopher Faulet92d36382015-11-05 13:35:03 +0100731 (*cur_arg)++;
732
733 return 0;
734}
735
736
737int
Christopher Fauletc9df7f72018-12-10 16:14:04 +0100738check_implicit_http_comp_flt(struct proxy *proxy)
Christopher Faulet92d36382015-11-05 13:35:03 +0100739{
Christopher Faulet443ea1a2016-02-04 13:40:26 +0100740 struct flt_conf *fconf;
Christopher Faulet27d93c32018-12-15 22:32:02 +0100741 int explicit = 0;
742 int comp = 0;
Christopher Faulet92d36382015-11-05 13:35:03 +0100743 int err = 0;
744
745 if (proxy->comp == NULL)
746 goto end;
Christopher Faulet443ea1a2016-02-04 13:40:26 +0100747 if (!LIST_ISEMPTY(&proxy->filter_configs)) {
748 list_for_each_entry(fconf, &proxy->filter_configs, list) {
749 if (fconf->id == http_comp_flt_id)
Christopher Faulet27d93c32018-12-15 22:32:02 +0100750 comp = 1;
751 else if (fconf->id == cache_store_flt_id) {
752 if (comp) {
753 ha_alert("config: %s '%s': unable to enable the compression filter "
754 "before any cache filter.\n",
755 proxy_type_str(proxy), proxy->id);
756 err++;
757 goto end;
758 }
759 }
Christopher Faulet78fbb9f2019-08-11 23:11:03 +0200760 else if (fconf->id == fcgi_flt_id)
761 continue;
Christopher Faulet27d93c32018-12-15 22:32:02 +0100762 else
763 explicit = 1;
Christopher Faulet92d36382015-11-05 13:35:03 +0100764 }
Christopher Faulet27d93c32018-12-15 22:32:02 +0100765 }
766 if (comp)
767 goto end;
768 else if (explicit) {
769 ha_alert("config: %s '%s': require an explicit filter declaration to use "
770 "HTTP compression\n", proxy_type_str(proxy), proxy->id);
Christopher Faulet92d36382015-11-05 13:35:03 +0100771 err++;
772 goto end;
773 }
774
Christopher Faulet27d93c32018-12-15 22:32:02 +0100775 /* Implicit declaration of the compression filter is always the last
776 * one */
Christopher Faulet443ea1a2016-02-04 13:40:26 +0100777 fconf = calloc(1, sizeof(*fconf));
778 if (!fconf) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100779 ha_alert("config: %s '%s': out of memory\n",
780 proxy_type_str(proxy), proxy->id);
Christopher Faulet92d36382015-11-05 13:35:03 +0100781 err++;
782 goto end;
783 }
Christopher Faulet443ea1a2016-02-04 13:40:26 +0100784 fconf->id = http_comp_flt_id;
785 fconf->conf = NULL;
786 fconf->ops = &comp_ops;
Willy Tarreau2b718102021-04-21 07:32:39 +0200787 LIST_APPEND(&proxy->filter_configs, &fconf->list);
Christopher Faulet92d36382015-11-05 13:35:03 +0100788 end:
789 return err;
790}
791
792/*
793 * boolean, returns true if compression is used (either gzip or deflate) in the
794 * response.
795 */
Christopher Faulet3d97c902015-12-09 14:59:38 +0100796static int
Christopher Faulet92d36382015-11-05 13:35:03 +0100797smp_fetch_res_comp(const struct arg *args, struct sample *smp, const char *kw,
798 void *private)
Christopher Faulet3d97c902015-12-09 14:59:38 +0100799{
Willy Tarreaube508f12016-03-10 11:47:01 +0100800 struct http_txn *txn = smp->strm ? smp->strm->txn : NULL;
Christopher Faulet92d36382015-11-05 13:35:03 +0100801
Christopher Faulet3d97c902015-12-09 14:59:38 +0100802 smp->data.type = SMP_T_BOOL;
Christopher Faulet92d36382015-11-05 13:35:03 +0100803 smp->data.u.sint = (txn && (txn->rsp.flags & HTTP_MSGF_COMPRESSING));
Christopher Faulet3d97c902015-12-09 14:59:38 +0100804 return 1;
805}
806
Christopher Faulet92d36382015-11-05 13:35:03 +0100807/*
808 * string, returns algo
809 */
Christopher Faulet3d97c902015-12-09 14:59:38 +0100810static int
Christopher Faulet92d36382015-11-05 13:35:03 +0100811smp_fetch_res_comp_algo(const struct arg *args, struct sample *smp,
812 const char *kw, void *private)
Christopher Faulet3d97c902015-12-09 14:59:38 +0100813{
Willy Tarreaube508f12016-03-10 11:47:01 +0100814 struct http_txn *txn = smp->strm ? smp->strm->txn : NULL;
Christopher Faulet92d36382015-11-05 13:35:03 +0100815 struct filter *filter;
816 struct comp_state *st;
817
Christopher Faulet03d85532017-09-15 10:14:43 +0200818 if (!txn || !(txn->rsp.flags & HTTP_MSGF_COMPRESSING))
Christopher Faulet3d97c902015-12-09 14:59:38 +0100819 return 0;
820
Christopher Fauletfcf035c2015-12-03 11:48:03 +0100821 list_for_each_entry(filter, &strm_flt(smp->strm)->filters, list) {
Christopher Faulet443ea1a2016-02-04 13:40:26 +0100822 if (FLT_ID(filter) != http_comp_flt_id)
Christopher Faulet92d36382015-11-05 13:35:03 +0100823 continue;
824
825 if (!(st = filter->ctx))
826 break;
827
828 smp->data.type = SMP_T_STR;
829 smp->flags = SMP_F_CONST;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200830 smp->data.u.str.area = st->comp_algo->cfg_name;
831 smp->data.u.str.data = st->comp_algo->cfg_name_len;
Christopher Faulet92d36382015-11-05 13:35:03 +0100832 return 1;
833 }
834 return 0;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100835}
836
837/* Declare the config parser for "compression" keyword */
838static struct cfg_kw_list cfg_kws = {ILH, {
839 { CFG_LISTEN, "compression", parse_compression_options },
840 { 0, NULL, NULL },
841 }
842};
843
Willy Tarreau0108d902018-11-25 19:14:37 +0100844INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
845
Christopher Faulet92d36382015-11-05 13:35:03 +0100846/* Declare the filter parser for "compression" keyword */
847static struct flt_kw_list filter_kws = { "COMP", { }, {
Thierry Fournier3610c392016-04-13 18:27:51 +0200848 { "compression", parse_http_comp_flt, NULL },
849 { NULL, NULL, NULL },
Christopher Faulet92d36382015-11-05 13:35:03 +0100850 }
851};
852
Willy Tarreau0108d902018-11-25 19:14:37 +0100853INITCALL1(STG_REGISTER, flt_register_keywords, &filter_kws);
854
Christopher Faulet3d97c902015-12-09 14:59:38 +0100855/* Note: must not be declared <const> as its list will be overwritten */
856static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
Christopher Faulet92d36382015-11-05 13:35:03 +0100857 { "res.comp", smp_fetch_res_comp, 0, NULL, SMP_T_BOOL, SMP_USE_HRSHP },
858 { "res.comp_algo", smp_fetch_res_comp_algo, 0, NULL, SMP_T_STR, SMP_USE_HRSHP },
859 { /* END */ },
860 }
861};
Christopher Faulet3d97c902015-12-09 14:59:38 +0100862
Willy Tarreau0108d902018-11-25 19:14:37 +0100863INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords);