Coverage Report

Created: 2026-08-19 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/haproxy/src/flt_http_comp.c
Line
Count
Source
1
/*
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
13
#include <haproxy/api.h>
14
#include <haproxy/cfgparse.h>
15
#include <haproxy/compression.h>
16
#include <haproxy/dynbuf.h>
17
#include <haproxy/filters.h>
18
#include <haproxy/http.h>
19
#include <haproxy/http_ana-t.h>
20
#include <haproxy/http_htx.h>
21
#include <haproxy/htx.h>
22
#include <haproxy/list.h>
23
#include <haproxy/proxy.h>
24
#include <haproxy/sample.h>
25
#include <haproxy/stream.h>
26
#include <haproxy/tools.h>
27
28
0
#define COMP_STATE_PROCESSING 0x01
29
30
const char *http_comp_req_flt_id = "comp-req filter";
31
const char *http_comp_res_flt_id = "comp-res filter";
32
33
struct flt_ops comp_req_ops;
34
struct flt_ops comp_res_ops;
35
36
struct comp_state {
37
  struct comp_ctx  *comp_ctx;   /* compression context */
38
  struct comp_algo *comp_algo;  /* compression algorithm if not NULL */
39
  unsigned int      flags;      /* COMP_STATE_* */
40
};
41
42
/* Pools used to allocate comp_state structs */
43
DECLARE_STATIC_TYPED_POOL(pool_head_comp_state, "comp_state", struct comp_state);
44
45
static int select_compression_request_header(struct comp_state *st,
46
               struct stream *s,
47
               struct http_msg *msg);
48
static int select_compression_response_header(struct comp_state *st,
49
                struct stream *s,
50
                struct http_msg *msg);
51
static int set_compression_header(struct comp_state *st,
52
                struct stream *s,
53
          struct http_msg *msg);
54
55
static int htx_compression_buffer_init(struct htx *htx, struct buffer *out);
56
static int htx_compression_buffer_add_data(struct comp_state *st, const char *data, size_t len,
57
              struct buffer *out, int dir);
58
static int htx_compression_buffer_end(struct comp_state *st, struct buffer *out, int end, int dir);
59
60
/***********************************************************************/
61
static int
62
comp_flt_init(struct proxy *px, struct flt_conf *fconf)
63
0
{
64
0
  fconf->flags |= FLT_CFG_FL_HTX;
65
0
  return 0;
66
0
}
67
68
static int
69
comp_strm_init(struct stream *s, struct filter *filter)
70
0
{
71
0
  struct comp_state *st;
72
73
0
  st = pool_alloc(pool_head_comp_state);
74
0
  if (st == NULL)
75
0
    return -1;
76
77
0
  st->comp_algo = NULL;
78
0
  st->comp_ctx = NULL;
79
0
  st->flags     = 0;
80
0
  filter->ctx   = st;
81
82
  /* Register post-analyzer on AN_RES_WAIT_HTTP because we need to
83
   * analyze response headers before http-response rules execution
84
   * to be sure we can use res.comp and res.comp_algo sample
85
   * fetches */
86
0
  filter->post_analyzers |= AN_RES_WAIT_HTTP;
87
0
  return 1;
88
0
}
89
90
static void
91
comp_strm_deinit(struct stream *s, struct filter *filter)
92
0
{
93
0
  struct comp_state *st = filter->ctx;
94
95
0
  if (!st)
96
0
    return;
97
98
  /* release any possible compression context */
99
0
  if (st->comp_algo)
100
0
    st->comp_algo->end(&st->comp_ctx);
101
0
  pool_free(pool_head_comp_state, st);
102
0
  filter->ctx = NULL;
103
0
}
104
105
static void
106
comp_prepare_compress_request(struct comp_state *st, struct stream *s, struct http_msg *msg)
107
0
{
108
0
  struct htx *htx = htxbuf(&msg->chn->buf);
109
0
  struct http_txn *txn = s->txn.http;
110
0
  struct http_hdr_ctx ctx;
111
0
  struct comp_type *comp_type;
112
0
  unsigned int comp_minsize = 0;
113
114
0
  ctx.blk = NULL;
115
  /* Already compressed, don't bother */
116
0
  if (http_find_header(htx, ist("Content-Encoding"), &ctx, 1))
117
0
    return;
118
  /* HTTP < 1.1 should not be compressed */
119
0
  if (!(msg->flags & HTTP_MSGF_VER_11) || !(txn->req.flags & HTTP_MSGF_VER_11))
120
0
    return;
121
0
  comp_type = NULL;
122
123
  /* compress only if body size is >= than the min size */
124
0
  if (((msg->flags & HTTP_MSGF_CNT_LEN) || (htx->flags & HTX_FL_EOM)) &&
125
0
      ((s->be->comp && (comp_minsize = s->be->comp->minsize_req)) ||
126
0
       (strm_fe(s)->comp && (comp_minsize = strm_fe(s)->comp->minsize_req)))) {
127
    /* small requests should not be compressed */
128
0
    if (chn_prod(msg->chn)->sedesc->kip < comp_minsize)
129
0
      goto fail;
130
0
  }
131
132
  /*
133
   * 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
134
   * requires one, we don't compress either. Backend has the priority.
135
   */
136
0
  ctx.blk = NULL;
137
0
  if (http_find_header(htx, ist("Content-Type"), &ctx, 1)) {
138
0
    if ((s->be->comp && (comp_type = s->be->comp->types_req)) ||
139
0
        (strm_fe(s)->comp && (comp_type = strm_fe(s)->comp->types_req))) {
140
0
      for (; comp_type; comp_type = comp_type->next) {
141
0
        if (ctx.value.len >= comp_type->name_len &&
142
0
            strncasecmp(ctx.value.ptr, comp_type->name, comp_type->name_len) == 0)
143
          /* this Content-Type should be compressed */
144
0
          break;
145
0
      }
146
      /* this Content-Type should not be compressed */
147
0
      if (comp_type == NULL)
148
0
        goto fail;
149
0
    }
150
0
  }
151
0
  else { /* no content-type header */
152
0
    if ((s->be->comp && s->be->comp->types_req) ||
153
0
        (strm_fe(s)->comp && strm_fe(s)->comp->types_req))
154
0
      goto fail; /* a content-type was required */
155
0
  }
156
157
  /* limit compression rate */
158
0
  if (global.comp_rate_lim > 0)
159
0
    if (read_freq_ctr(&global.comp_bps_in) > global.comp_rate_lim)
160
0
      goto fail;
161
162
  /* limit cpu usage */
163
0
  if (th_ctx->idle_pct < compress_min_idle)
164
0
    goto fail;
165
166
0
  if (txn->meth == HTTP_METH_HEAD)
167
0
    return;
168
0
  if (s->be->comp && s->be->comp->algo_req != NULL)
169
0
    st->comp_algo = s->be->comp->algo_req;
170
0
  else if (strm_fe(s)->comp && strm_fe(s)->comp->algo_req != NULL)
171
0
    st->comp_algo = strm_fe(s)->comp->algo_req;
172
0
  else
173
0
    goto fail; /* no algo selected: nothing to do */
174
175
  /* initialize compression */
176
0
  if (st->comp_algo->init(&st->comp_ctx, global.tune.comp_maxlevel) < 0)
177
0
    goto fail;
178
179
0
  return;
180
0
fail:
181
0
  st->comp_algo = NULL;
182
0
}
183
184
static int
185
comp_req_http_headers(struct stream *s, struct filter *filter, struct http_msg *msg)
186
0
{
187
0
  struct comp_state *st = filter->ctx;
188
0
  int comp_flags = 0;
189
190
0
  if (!strm_fe(s)->comp && !s->be->comp)
191
0
    goto end;
192
193
0
  if (strm_fe(s)->comp)
194
0
    comp_flags |= strm_fe(s)->comp->flags;
195
0
  if (s->be->comp)
196
0
    comp_flags |= s->be->comp->flags;
197
198
0
  if (!(comp_flags & COMP_FL_DIR_REQ))
199
0
    goto end;
200
201
0
  if (!(msg->chn->flags & CF_ISRESP)) {
202
0
    comp_prepare_compress_request(st, s, msg);
203
0
    if (st->comp_algo) {
204
0
      if (!set_compression_header(st, s, msg))
205
0
        goto end;
206
0
      register_data_filter(s, msg->chn, filter);
207
0
      st->flags |= COMP_STATE_PROCESSING;
208
0
    }
209
0
  }
210
211
0
  end:
212
0
  return 1;
213
0
}
214
215
static int
216
comp_res_http_headers(struct stream *s, struct filter *filter, struct http_msg *msg)
217
0
{
218
0
  struct comp_state *st = filter->ctx;
219
0
  int comp_flags = 0;
220
221
0
  if (!strm_fe(s)->comp && !s->be->comp)
222
0
    goto end;
223
224
0
  if (strm_fe(s)->comp)
225
0
    comp_flags |= strm_fe(s)->comp->flags;
226
0
  if (s->be->comp)
227
0
    comp_flags |= s->be->comp->flags;
228
229
0
  if (!(comp_flags & COMP_FL_DIR_RES))
230
0
    goto end;
231
232
233
0
  if (!(msg->chn->flags & CF_ISRESP))
234
0
    select_compression_request_header(st, s, msg);
235
0
  else {
236
    /* Response headers have already been checked in
237
     * comp_res_http_post_analyze callback. */
238
0
    if (st->comp_algo) {
239
0
      if (!set_compression_header(st, s, msg))
240
0
        goto end;
241
0
      register_data_filter(s, msg->chn, filter);
242
0
      st->flags |= COMP_STATE_PROCESSING;
243
0
    }
244
0
  }
245
246
0
  end:
247
0
  return 1;
248
0
}
249
250
static int
251
comp_res_http_post_analyze(struct stream *s, struct filter *filter,
252
                           struct channel *chn, unsigned an_bit)
253
0
{
254
0
  struct http_txn   *txn = s->txn.http;
255
0
  struct http_msg   *msg = &txn->rsp;
256
0
  struct comp_state *st  = filter->ctx;
257
258
0
  if (an_bit != AN_RES_WAIT_HTTP)
259
0
    goto end;
260
261
0
  if (!strm_fe(s)->comp && !s->be->comp)
262
0
    goto end;
263
264
0
  select_compression_response_header(st, s, msg);
265
266
0
  end:
267
0
  return 1;
268
0
}
269
270
static int
271
comp_http_payload(struct stream *s, struct filter *filter, struct http_msg *msg,
272
      unsigned int offset, unsigned int len, int dir)
273
0
{
274
0
  struct comp_state *st = filter->ctx;
275
0
  struct htx *htx = htxbuf(&msg->chn->buf);
276
0
  struct htx_ret htxret = htx_find_offset(htx, offset);
277
0
  struct htx_blk *blk, *next;
278
0
  int ret, consumed = 0, to_forward = 0, last = 0;
279
280
0
  blk = htxret.blk;
281
0
  offset = htxret.ret;
282
0
  for (next = NULL; blk && len; blk = next) {
283
0
    enum htx_blk_type type = htx_get_blk_type(blk);
284
0
    uint32_t sz = htx_get_blksz(blk);
285
0
    struct ist v;
286
287
0
    next = htx_get_next_blk(htx, blk);
288
0
    while (next && htx_get_blk_type(next) == HTX_BLK_UNUSED)
289
0
      next = htx_get_next_blk(htx, next);
290
291
0
    if (!(st->flags & COMP_STATE_PROCESSING))
292
0
      goto consume;
293
294
0
    if (htx_compression_buffer_init(htx, &trash) < 0) {
295
0
      msg->chn->flags |= CF_WAKE_WRITE;
296
0
      goto end;
297
0
    }
298
299
0
    switch (type) {
300
0
      case HTX_BLK_DATA:
301
        /* it is the last data block */
302
0
        last = ((!next && (htx->flags & HTX_FL_EOM)) || (next && htx_get_blk_type(next) != HTX_BLK_DATA));
303
0
        v = htx_get_blk_value(htx, blk);
304
0
        v = istadv(v, offset);
305
0
        if (v.len > len) {
306
0
          last = 0;
307
0
          v.len = len;
308
0
        }
309
0
        if (v.len > b_size(&trash)) {
310
0
          last = 0;
311
0
          v.len = b_size(&trash);
312
0
        }
313
314
0
        ret = htx_compression_buffer_add_data(st, v.ptr, v.len, &trash, dir);
315
0
        if (ret < 0 || htx_compression_buffer_end(st, &trash, last, dir) < 0)
316
0
          goto error;
317
0
        BUG_ON(v.len != ret);
318
319
0
        if (ret == sz && !b_data(&trash))
320
0
          next = htx_remove_blk(htx, blk);
321
0
        else {
322
0
          blk = htx_replace_blk_value(htx, blk, v, ist2(b_head(&trash), b_data(&trash)));
323
0
          next = htx_get_next_blk(htx, blk);
324
0
        }
325
326
0
        len -= ret;
327
0
        consumed += ret;
328
0
        to_forward += b_data(&trash);
329
0
        if (last)
330
0
          st->flags &= ~COMP_STATE_PROCESSING;
331
0
        break;
332
333
0
      case HTX_BLK_TLR:
334
0
      case HTX_BLK_EOT:
335
0
        if (htx_compression_buffer_end(st, &trash, 1, dir) < 0)
336
0
          goto error;
337
0
        if (b_data(&trash)) {
338
0
          struct htx_blk *last = htx_add_last_data(htx, ist2(b_head(&trash), b_data(&trash)));
339
0
          if (!last)
340
0
            goto error;
341
0
          blk = htx_get_next_blk(htx, last);
342
0
          if (!blk)
343
0
            goto error;
344
0
          next = htx_get_next_blk(htx, blk);
345
0
          to_forward += b_data(&trash);
346
0
        }
347
0
        st->flags &= ~COMP_STATE_PROCESSING;
348
0
        __fallthrough;
349
350
0
      default:
351
0
        consume:
352
0
        sz -= offset;
353
0
        if (sz > len)
354
0
          sz = len;
355
0
        consumed += sz;
356
0
        to_forward += sz;
357
0
        len -= sz;
358
0
        break;
359
0
    }
360
361
0
    offset = 0;
362
0
  }
363
364
0
  end:
365
0
  if (to_forward != consumed)
366
0
    flt_update_offsets(filter, msg->chn, to_forward - consumed);
367
368
0
  if (st->comp_ctx && st->comp_ctx->cur_lvl > 0) {
369
0
    update_freq_ctr(&global.comp_bps_in, consumed);
370
0
    if (s->sess->fe_tgcounters) {
371
0
      _HA_ATOMIC_ADD(&s->sess->fe_tgcounters->comp_in[dir], consumed);
372
0
      _HA_ATOMIC_ADD(&s->sess->fe_tgcounters->comp_out[dir], to_forward);
373
0
    }
374
0
    if (s->be_tgcounters) {
375
0
      _HA_ATOMIC_ADD(&s->be_tgcounters->comp_in[dir], consumed);
376
0
      _HA_ATOMIC_ADD(&s->be_tgcounters->comp_out[dir], to_forward);
377
0
    }
378
0
    update_freq_ctr(&global.comp_bps_out, to_forward);
379
0
  } else {
380
0
    if (s->sess->fe_tgcounters)
381
0
      _HA_ATOMIC_ADD(&s->sess->fe_tgcounters->comp_byp[dir], consumed);
382
0
    if (s->be_tgcounters)
383
0
      _HA_ATOMIC_ADD(&s->be_tgcounters->comp_byp[dir], consumed);
384
0
  }
385
0
  return to_forward;
386
387
0
  error:
388
0
  return -1;
389
0
}
390
391
static int
392
comp_req_http_payload(struct stream *s, struct filter *filter, struct http_msg *msg,
393
                      unsigned int offset, unsigned int len)
394
0
{
395
0
  if (msg->chn->flags & CF_ISRESP)
396
0
    return 0;
397
398
0
  return comp_http_payload(s, filter, msg, offset, len, COMP_DIR_REQ);
399
0
}
400
401
static int
402
comp_res_http_payload(struct stream *s, struct filter *filter, struct http_msg *msg,
403
                      unsigned int offset, unsigned int len)
404
0
{
405
0
  if (!(msg->chn->flags & CF_ISRESP))
406
0
    return 0;
407
408
0
  return comp_http_payload(s, filter, msg, offset, len, COMP_DIR_RES);
409
0
}
410
411
static int
412
comp_res_http_end(struct stream *s, struct filter *filter,
413
            struct http_msg *msg)
414
0
{
415
0
  struct comp_state *st = filter->ctx;
416
417
0
  if (!(msg->chn->flags & CF_ISRESP) || !st || !st->comp_algo)
418
0
    goto end;
419
420
0
  if (strm_fe(s)->mode == PR_MODE_HTTP && s->sess->fe_tgcounters)
421
0
    _HA_ATOMIC_INC(&s->sess->fe_tgcounters->p.http.comp_rsp);
422
0
  if ((s->flags & SF_BE_ASSIGNED) && (s->be->mode == PR_MODE_HTTP) &&
423
0
      s->be_tgcounters)
424
0
    _HA_ATOMIC_INC(&s->be_tgcounters->p.http.comp_rsp);
425
0
 end:
426
0
  return 1;
427
0
}
428
429
/***********************************************************************/
430
static int
431
set_compression_header(struct comp_state *st, struct stream *s, struct http_msg *msg)
432
0
{
433
0
  struct htx *htx = htxbuf(&msg->chn->buf);
434
0
  struct htx_sl *sl;
435
0
  struct http_hdr_ctx ctx, last_vary;
436
0
  struct comp_algo *comp_algo;
437
438
0
  sl = http_get_stline(htx);
439
0
  if (!sl)
440
0
    goto error;
441
442
0
  comp_algo = st->comp_algo;
443
444
  /* add "Transfer-Encoding: chunked" header */
445
0
  if (!(msg->flags & HTTP_MSGF_TE_CHNK)) {
446
0
    if (!http_add_header(htx, ist("Transfer-Encoding"), ist("chunked"), 0))
447
0
      goto error;
448
0
    msg->flags |= HTTP_MSGF_TE_CHNK;
449
0
    sl->flags |= (HTX_SL_F_XFER_ENC|HTX_SL_F_CHNK);
450
0
  }
451
452
  /* remove Content-Length header */
453
0
  if (msg->flags & HTTP_MSGF_CNT_LEN) {
454
0
    ctx.blk = NULL;
455
0
    while (http_find_header(htx, ist("Content-Length"), &ctx, 1))
456
0
      http_remove_header(htx, &ctx);
457
0
    msg->flags &= ~HTTP_MSGF_CNT_LEN;
458
0
    sl->flags &= ~HTX_SL_F_CLEN;
459
0
  }
460
461
  /* convert "ETag" header to a weak ETag */
462
0
  ctx.blk = NULL;
463
0
  if (http_find_header(htx, ist("ETag"), &ctx, 1)) {
464
0
    if (ctx.value.ptr[0] == '"') {
465
      /* This a strong ETag. Convert it to a weak one. */
466
0
      struct ist v = ist2(trash.area, 0);
467
0
      if (istcat(&v, ist("W/"), trash.size) == -1 || istcat(&v, ctx.value, trash.size) == -1)
468
0
        goto error;
469
470
0
      if (!http_replace_header_value(htx, &ctx, v, 0))
471
0
        goto error;
472
0
    }
473
0
  }
474
475
  /* Add "Vary: Accept-Encoding" header but only if it is not found. */
476
0
  ctx.blk = NULL;
477
0
  last_vary.blk = NULL;
478
0
  while (http_find_header(htx, ist("Vary"), &ctx, 0)) {
479
0
    if (isteqi(ctx.value, ist("Accept-Encoding")))
480
0
      break;
481
0
    last_vary = ctx;
482
0
  }
483
  /* No "Accept-Encoding" value found. */
484
0
  if (ctx.blk == NULL) {
485
0
    if (last_vary.blk == NULL) {
486
      /* No Vary header found at all. Add our header */
487
0
      if (!http_add_header(htx, ist("Vary"), ist("Accept-Encoding"), 0))
488
0
        goto error;
489
0
    }
490
0
    else  {
491
      /* At least one Vary header found. Append the value to
492
       * the last one.
493
       */
494
0
      if (!http_append_header_value(htx, &last_vary, ist("Accept-Encoding")))
495
0
        goto error;
496
0
    }
497
0
  }
498
499
  /*
500
   * Add Content-Encoding header when it's not identity encoding.
501
   * RFC 2616 : Identity encoding: This content-coding is used only in the
502
   * Accept-Encoding header, and SHOULD NOT be used in the Content-Encoding
503
   * header.
504
   */
505
0
  if (comp_algo->cfg_name_len != 8 || memcmp(comp_algo->cfg_name, "identity", 8) != 0) {
506
0
    struct ist v = ist2(comp_algo->ua_name, comp_algo->ua_name_len);
507
508
0
    if (!http_add_header(htx, ist("Content-Encoding"), v, 0))
509
0
      goto error;
510
0
  }
511
512
0
  chn_prod(msg->chn)->flags |= SC_FL_NO_FASTFWD;
513
0
  return 1;
514
515
0
  error:
516
0
  st->comp_algo->end(&st->comp_ctx);
517
0
  st->comp_algo = NULL;
518
0
  return 0;
519
0
}
520
521
/*
522
 * Selects a compression algorithm depending on the client request.
523
 */
524
static int
525
select_compression_request_header(struct comp_state *st, struct stream *s, struct http_msg *msg)
526
0
{
527
0
  struct htx *htx = htxbuf(&msg->chn->buf);
528
0
  struct http_hdr_ctx ctx;
529
0
  struct comp_algo *comp_algo = NULL;
530
0
  struct comp_algo *comp_algo_back = NULL;
531
532
  /* Disable compression for older user agents announcing themselves as "Mozilla/4"
533
   * unless they are known good (MSIE 6 with XP SP2, or MSIE 7 and later).
534
   * See http://zoompf.com/2012/02/lose-the-wait-http-compression for more details.
535
   */
536
0
  ctx.blk = NULL;
537
0
  if (http_find_header(htx, ist("User-Agent"), &ctx, 1) &&
538
0
      ctx.value.len >= 9 &&
539
0
      memcmp(ctx.value.ptr, "Mozilla/4", 9) == 0 &&
540
0
      (ctx.value.len < 31 ||
541
0
       memcmp(ctx.value.ptr + 25, "MSIE ", 5) != 0 ||
542
0
       *(ctx.value.ptr + 30) < '6' ||
543
0
       (*(ctx.value.ptr + 30) == '6' &&
544
0
        (ctx.value.len < 54 || memcmp(ctx.value.ptr + 51, "SV1", 3) != 0)))) {
545
0
    st->comp_algo = NULL;
546
0
    return 0;
547
0
  }
548
549
  /* search for the algo in the backend in priority or the frontend */
550
0
  if ((s->be->comp && (comp_algo_back = s->be->comp->algos_res)) ||
551
0
      (strm_fe(s)->comp && (comp_algo_back = strm_fe(s)->comp->algos_res))) {
552
0
    int best_q = 0;
553
554
0
    ctx.blk = NULL;
555
0
    while (http_find_header(htx, ist("Accept-Encoding"), &ctx, 0)) {
556
0
      const char *qval;
557
0
      int q;
558
0
      int toklen;
559
560
      /* try to isolate the token from the optional q-value */
561
0
      toklen = 0;
562
0
      while (toklen < ctx.value.len && HTTP_IS_TOKEN(*(ctx.value.ptr + toklen)))
563
0
        toklen++;
564
565
0
      qval = ctx.value.ptr + toklen;
566
0
      while (1) {
567
0
        while (qval < istend(ctx.value) && HTTP_IS_LWS(*qval))
568
0
          qval++;
569
570
0
        if (qval >= istend(ctx.value) || *qval != ';') {
571
0
          qval = NULL;
572
0
          break;
573
0
        }
574
0
        qval++;
575
576
0
        while (qval < istend(ctx.value) && HTTP_IS_LWS(*qval))
577
0
          qval++;
578
579
0
        if (qval >= istend(ctx.value)) {
580
0
          qval = NULL;
581
0
          break;
582
0
        }
583
0
        if (strncmp(qval, "q=", MIN(istend(ctx.value) - qval, 2)) == 0)
584
0
          break;
585
586
0
        while (qval < istend(ctx.value) && *qval != ';')
587
0
          qval++;
588
0
      }
589
590
      /* here we have qval pointing to the first "q=" attribute or NULL if not found */
591
0
      q = qval ? http_parse_qvalue(qval + 2, NULL) : 1000;
592
593
0
      if (q <= best_q)
594
0
        continue;
595
596
0
      for (comp_algo = comp_algo_back; comp_algo; comp_algo = comp_algo->next) {
597
0
        if (*(ctx.value.ptr) == '*' ||
598
0
            word_match(ctx.value.ptr, toklen, comp_algo->ua_name, comp_algo->ua_name_len)) {
599
0
          st->comp_algo = comp_algo;
600
0
          best_q = q;
601
0
          break;
602
0
        }
603
0
      }
604
0
    }
605
0
  }
606
607
  /* remove all occurrences of the header when "compression offload" is set */
608
0
  if (st->comp_algo) {
609
0
    if ((s->be->comp && (s->be->comp->flags & COMP_FL_OFFLOAD)) ||
610
0
        (strm_fe(s)->comp && (strm_fe(s)->comp->flags & COMP_FL_OFFLOAD))) {
611
0
      ctx.blk = NULL;
612
0
      while (http_find_header(htx, ist("Accept-Encoding"), &ctx, 1))
613
0
        http_remove_header(htx, &ctx);
614
0
    }
615
0
    return 1;
616
0
  }
617
618
  /* identity is implicit does not require headers */
619
0
  if ((s->be->comp && (comp_algo_back = s->be->comp->algos_res)) ||
620
0
      (strm_fe(s)->comp && (comp_algo_back = strm_fe(s)->comp->algos_res))) {
621
0
    for (comp_algo = comp_algo_back; comp_algo; comp_algo = comp_algo->next) {
622
0
      if (comp_algo->cfg_name_len == 8 && memcmp(comp_algo->cfg_name, "identity", 8) == 0) {
623
0
        st->comp_algo = comp_algo;
624
0
        return 1;
625
0
      }
626
0
    }
627
0
  }
628
629
0
  st->comp_algo = NULL;
630
0
  return 0;
631
0
}
632
633
/*
634
 * Selects a compression algorithm depending of the server response.
635
 */
636
static int
637
select_compression_response_header(struct comp_state *st, struct stream *s, struct http_msg *msg)
638
0
{
639
0
  struct htx *htx = htxbuf(&msg->chn->buf);
640
0
  struct http_txn *txn = s->txn.http;
641
0
  struct http_hdr_ctx ctx;
642
0
  struct comp_type *comp_type;
643
0
  unsigned int comp_minsize = 0;
644
645
  /* no common compression algorithm was found in request header */
646
0
  if (st->comp_algo == NULL)
647
0
    goto fail;
648
649
  /* compression already in progress */
650
0
  if (msg->flags & HTTP_MSGF_COMPRESSING)
651
0
    goto fail;
652
653
  /* HTTP < 1.1 should not be compressed */
654
0
  if (!(msg->flags & HTTP_MSGF_VER_11) || !(txn->req.flags & HTTP_MSGF_VER_11))
655
0
    goto fail;
656
657
0
  if (txn->meth == HTTP_METH_HEAD)
658
0
    goto fail;
659
660
  /* compress 200,201,202,203 responses only */
661
0
  if ((txn->status != 200) &&
662
0
      (txn->status != 201) &&
663
0
      (txn->status != 202) &&
664
0
      (txn->status != 203))
665
0
    goto fail;
666
667
0
  if (!(msg->flags & HTTP_MSGF_XFER_LEN) || msg->flags & HTTP_MSGF_BODYLESS)
668
0
    goto fail;
669
670
  /* compress only if body size is >= than the min size */
671
0
  if (((msg->flags & HTTP_MSGF_CNT_LEN) || (htx->flags & HTX_FL_EOM)) &&
672
0
      ((s->be->comp && (comp_minsize = s->be->comp->minsize_res)) ||
673
0
       (strm_fe(s)->comp && (comp_minsize = strm_fe(s)->comp->minsize_res)))) {
674
    /* small responses should not be compressed */
675
0
    if (chn_prod(msg->chn)->sedesc->kip < comp_minsize)
676
0
      goto fail;
677
0
  }
678
679
  /* content is already compressed */
680
0
  ctx.blk = NULL;
681
0
  if (http_find_header(htx, ist("Content-Encoding"), &ctx, 1))
682
0
    goto fail;
683
684
  /* no compression when Cache-Control: no-transform is present in the message */
685
0
  ctx.blk = NULL;
686
0
  while (http_find_header(htx, ist("Cache-Control"), &ctx, 0)) {
687
0
    if (word_match(ctx.value.ptr, ctx.value.len, "no-transform", 12))
688
0
      goto fail;
689
0
  }
690
691
  /* no compression when ETag is malformed */
692
0
  ctx.blk = NULL;
693
0
  if (http_find_header(htx, ist("ETag"), &ctx, 1)) {
694
0
    if (http_get_etag_type(ctx.value) == ETAG_INVALID)
695
0
      goto fail;
696
0
  }
697
  /* no compression when multiple ETags are present
698
   * Note: Do not reset ctx.blk!
699
   */
700
0
  if (http_find_header(htx, ist("ETag"), &ctx, 1))
701
0
    goto fail;
702
703
0
  comp_type = NULL;
704
705
  /* we don't want to compress multipart content-types, nor content-types that are
706
   * not listed in the "compression type" directive if any. If no content-type was
707
   * found but configuration requires one, we don't compress either. Backend has
708
   * the priority.
709
   */
710
0
  ctx.blk = NULL;
711
0
  if (http_find_header(htx, ist("Content-Type"), &ctx, 1)) {
712
0
    if (ctx.value.len >= 9 && strncasecmp("multipart", ctx.value.ptr, 9) == 0)
713
0
      goto fail;
714
715
0
    if ((s->be->comp && (comp_type = s->be->comp->types_res)) ||
716
0
        (strm_fe(s)->comp && (comp_type = strm_fe(s)->comp->types_res))) {
717
0
      for (; comp_type; comp_type = comp_type->next) {
718
0
        if (ctx.value.len >= comp_type->name_len &&
719
0
            strncasecmp(ctx.value.ptr, comp_type->name, comp_type->name_len) == 0)
720
          /* this Content-Type should be compressed */
721
0
          break;
722
0
      }
723
      /* this Content-Type should not be compressed */
724
0
      if (comp_type == NULL)
725
0
        goto fail;
726
0
    }
727
0
  }
728
0
  else { /* no content-type header */
729
0
    if ((s->be->comp && s->be->comp->types_res) ||
730
0
        (strm_fe(s)->comp && strm_fe(s)->comp->types_res))
731
0
      goto fail; /* a content-type was required */
732
0
  }
733
734
  /* limit compression rate */
735
0
  if (global.comp_rate_lim > 0)
736
0
    if (read_freq_ctr(&global.comp_bps_in) > global.comp_rate_lim)
737
0
      goto fail;
738
739
  /* limit cpu usage */
740
0
  if (th_ctx->idle_pct < compress_min_idle)
741
0
    goto fail;
742
743
  /* initialize compression */
744
0
  if (st->comp_algo->init(&st->comp_ctx, global.tune.comp_maxlevel) < 0)
745
0
    goto fail;
746
0
  msg->flags |= HTTP_MSGF_COMPRESSING;
747
0
  return 1;
748
749
0
  fail:
750
0
  st->comp_algo = NULL;
751
0
  return 0;
752
0
}
753
754
/***********************************************************************/
755
static int
756
htx_compression_buffer_init(struct htx *htx, struct buffer *out)
757
0
{
758
  /* output stream requires at least 10 bytes for the gzip header, plus
759
   * at least 8 bytes for the gzip trailer (crc+len), plus at most
760
   * 5 bytes per 32kB block and 2 bytes to close the stream.
761
   */
762
0
  if (htx_free_space(htx) < 20 + 5 * ((htx->data + 32767) >> 15))
763
0
    return -1;
764
0
  b_reset(out);
765
0
  return 0;
766
0
}
767
768
static int
769
htx_compression_buffer_add_data(struct comp_state *st, const char *data, size_t len,
770
        struct buffer *out, int dir)
771
0
{
772
773
0
  return st->comp_algo->add_data(st->comp_ctx, data, len, out);
774
0
}
775
776
static int
777
htx_compression_buffer_end(struct comp_state *st, struct buffer *out, int end, int dir)
778
0
{
779
780
0
  if (end)
781
0
    return st->comp_algo->finish(st->comp_ctx, out);
782
0
  else
783
0
    return st->comp_algo->flush(st->comp_ctx, out);
784
0
}
785
786
787
/***********************************************************************/
788
789
struct flt_ops comp_req_ops = {
790
  .init              = comp_flt_init,
791
792
  .attach = comp_strm_init,
793
  .detach = comp_strm_deinit,
794
795
  .http_headers          = comp_req_http_headers,
796
  .http_payload          = comp_req_http_payload,
797
};
798
799
struct flt_ops comp_res_ops = {
800
  .init              = comp_flt_init,
801
802
  .attach = comp_strm_init,
803
  .detach = comp_strm_deinit,
804
805
  .channel_post_analyze  = comp_res_http_post_analyze,
806
807
  .http_headers          = comp_res_http_headers,
808
  .http_payload          = comp_res_http_payload,
809
  .http_end              = comp_res_http_end,
810
};
811
812
/* returns compression options from <proxy> proxy or allocates them if
813
 * needed
814
 *
815
 * When compression options are created, flags will be set to <defaults>
816
 *
817
 * Returns NULL in case of memory error
818
 */
819
static inline struct comp *proxy_get_comp(struct proxy *proxy, int defaults)
820
0
{
821
0
  struct comp    *comp;
822
823
0
  if (proxy->comp == NULL) {
824
0
    comp = calloc(1, sizeof(*comp));
825
0
    if (unlikely(!comp))
826
0
      return NULL;
827
0
    comp->flags = defaults;
828
0
    proxy->comp = comp;
829
0
  }
830
0
  return proxy->comp;
831
0
}
832
833
static int
834
parse_compression_options(char **args, int section, struct proxy *proxy,
835
        const struct proxy *defpx, const char *file, int line,
836
        char **err)
837
0
{
838
0
  struct comp    *comp;
839
0
  int ret = 0;
840
0
  const char *res;
841
842
  /* always default to compress responses */
843
0
  comp = proxy_get_comp(proxy, COMP_FL_DIR_RES);
844
0
  if (comp == NULL) {
845
0
    memprintf(err, "'%s': out of memory.", args[0]);
846
0
    ret = -1;
847
0
    goto end;
848
0
  }
849
850
0
  if (strcmp(args[1], "algo") == 0 || strcmp(args[1], "algo-res") == 0) {
851
0
    struct comp_ctx *ctx;
852
0
    int              cur_arg = 2;
853
854
0
    if (!*args[cur_arg]) {
855
0
      memprintf(err, "parsing [%s:%d] : '%s' expects <algorithm>.",
856
0
          file, line, args[0]);
857
0
      ret = -1;
858
0
      goto end;
859
0
    }
860
0
    while (*(args[cur_arg])) {
861
0
      int retval = comp_append_algo(&comp->algos_res, args[cur_arg]);
862
0
      if (retval) {
863
0
        if (retval < 0)
864
0
          memprintf(err, "'%s' : '%s' is not a supported algorithm.",
865
0
              args[0], args[cur_arg]);
866
0
        else
867
0
          memprintf(err, "'%s' : out of memory while parsing algo '%s'.",
868
0
              args[0], args[cur_arg]);
869
0
        ret = -1;
870
0
        goto end;
871
0
      }
872
873
0
      if (proxy->comp->algos_res->init(&ctx, 9) == 0)
874
0
        proxy->comp->algos_res->end(&ctx);
875
0
      else {
876
0
        memprintf(err, "'%s' : Can't init '%s' algorithm.",
877
0
            args[0], args[cur_arg]);
878
0
        ret = -1;
879
0
        goto end;
880
0
      }
881
0
      cur_arg++;
882
0
      continue;
883
0
    }
884
0
  }
885
0
  else if (strcmp(args[1], "algo-req") == 0) {
886
0
    struct comp_ctx *ctx;
887
0
    int retval = comp_append_algo(&comp->algo_req, args[2]);
888
889
0
    if (retval) {
890
0
      if (retval < 0)
891
0
        memprintf(err, "'%s' : '%s' is not a supported algorithm.",
892
0
            args[0], args[2]);
893
0
      else
894
0
        memprintf(err, "'%s' : out of memory while parsing algo '%s'.",
895
0
            args[0], args[2]);
896
0
      ret = -1;
897
0
      goto end;
898
0
    }
899
900
0
    if (proxy->comp->algo_req->init(&ctx, 9) == 0)
901
0
      proxy->comp->algo_req->end(&ctx);
902
0
    else {
903
0
      memprintf(err, "'%s' : Can't init '%s' algorithm.",
904
0
          args[0], args[2]);
905
0
      ret = -1;
906
0
      goto end;
907
0
    }
908
0
  }
909
0
  else if (strcmp(args[1], "offload") == 0) {
910
0
    if (proxy->cap & PR_CAP_DEF) {
911
0
      memprintf(err, "'%s' : '%s' ignored in 'defaults' section.",
912
0
          args[0], args[1]);
913
0
      ret = 1;
914
0
    }
915
0
    comp->flags |= COMP_FL_OFFLOAD;
916
0
  }
917
0
  else if (strcmp(args[1], "type") == 0 || strcmp(args[1], "type-res") == 0) {
918
0
    int cur_arg = 2;
919
920
0
    if (!*args[cur_arg]) {
921
0
      memprintf(err, "'%s' expects <type>.", args[0]);
922
0
      ret = -1;
923
0
      goto end;
924
0
    }
925
0
    while (*(args[cur_arg])) {
926
0
      if (comp_append_type(&comp->types_res, args[cur_arg])) {
927
0
        memprintf(err, "'%s': out of memory.", args[0]);
928
0
        ret = -1;
929
0
        goto end;
930
0
      }
931
0
      cur_arg++;
932
0
      continue;
933
0
    }
934
0
  }
935
0
  else if (strcmp(args[1], "type-req") == 0) {
936
0
    int cur_arg = 2;
937
938
0
    if (!*args[cur_arg]) {
939
0
      memprintf(err, "'%s' expects <type>.", args[0]);
940
0
      ret = -1;
941
0
      goto end;
942
0
    }
943
0
    while (*(args[cur_arg])) {
944
0
      if (comp_append_type(&comp->types_req, args[cur_arg])) {
945
0
        memprintf(err, "'%s': out of memory.", args[0]);
946
0
        ret = -1;
947
0
        goto end;
948
0
      }
949
0
      cur_arg++;
950
0
      continue;
951
0
    }
952
0
  }
953
0
  else if (strcmp(args[1], "minsize-req") == 0) {
954
0
    if (*(args[2]) == 0) {
955
0
      memprintf(err, "'%s' expects an integer argument.", args[1]);
956
0
      ret = -1;
957
0
      goto end;
958
0
    }
959
0
    res = parse_size_err(args[2], &comp->minsize_req);
960
0
    if (res != NULL) {
961
0
      memprintf(err, "unexpected '%s' after size passed to '%s'", res, args[1]);
962
0
      ret = -1;
963
0
      goto end;
964
0
    }
965
0
  }
966
0
  else if (strcmp(args[1], "minsize-res") == 0) {
967
0
    if (*(args[2]) == 0) {
968
0
      memprintf(err, "'%s' expects an integer argument.", args[1]);
969
0
      ret = -1;
970
0
      goto end;
971
0
    }
972
0
    res = parse_size_err(args[2], &comp->minsize_res);
973
0
    if (res != NULL) {
974
0
      memprintf(err, "unexpected '%s' after size passed to '%s'", res, args[1]);
975
0
      ret = -1;
976
0
      goto end;
977
0
    }
978
0
  }
979
0
  else if (strcmp(args[1], "direction") == 0) {
980
0
    if (!args[2]) {
981
0
      memprintf(err, "'%s' expects 'request', 'response', or 'both'.", args[0]);
982
0
      ret = -1;
983
0
      goto end;
984
0
    }
985
0
    if (strcmp(args[2], "request") == 0) {
986
0
      comp->flags &= ~COMP_FL_DIR_RES;
987
0
      comp->flags |= COMP_FL_DIR_REQ;
988
0
    } else if (strcmp(args[2], "response") == 0) {
989
0
      comp->flags &= ~COMP_FL_DIR_REQ;
990
0
      comp->flags |= COMP_FL_DIR_RES;
991
0
    } else if (strcmp(args[2], "both") == 0)
992
0
      comp->flags |= COMP_FL_DIR_REQ | COMP_FL_DIR_RES;
993
0
    else {
994
0
      memprintf(err, "'%s' expects 'request', 'response', or 'both'.", args[0]);
995
0
      ret = -1;
996
0
      goto end;
997
0
    }
998
0
  }
999
0
  else {
1000
0
    memprintf(err, "'%s' expects 'algo', 'type', 'direction', 'offload', 'minsize-req' or 'minsize-res'.",
1001
0
        args[0]);
1002
0
    ret = -1;
1003
0
    goto end;
1004
0
  }
1005
1006
0
  end:
1007
0
  return ret;
1008
0
}
1009
1010
static int
1011
parse_http_comp_flt(char **args, int *cur_arg, struct proxy *px,
1012
                    struct flt_conf *fconf, char **err, void *private)
1013
0
{
1014
0
  struct flt_conf *fc, *back;
1015
0
  struct flt_conf *fconf_res;
1016
1017
0
  list_for_each_entry_safe(fc, back, &px->filter_configs, list) {
1018
0
    if (fc->id == http_comp_req_flt_id || fc->id == http_comp_res_flt_id) {
1019
0
      memprintf(err, "%s: Proxy supports only one compression filter\n", px->id);
1020
0
      return -1;
1021
0
    }
1022
0
  }
1023
1024
0
  fconf->id   = http_comp_req_flt_id;
1025
0
  fconf->name = "comp-req";
1026
0
  fconf->conf = NULL;
1027
0
  fconf->ops  = &comp_req_ops;
1028
1029
  /* FILTER API prepared a single filter_conf struct as it is meant to
1030
   * initialize exactly one fconf per keyword, but with the "compression"
1031
   * filter, for retro-compatibility we want to emulate the historical
1032
   * behavior which is to compress both requests and responses, so to
1033
   * emulate that we manually initialize the comp-res filter as well
1034
   */
1035
0
  fconf_res = calloc(1, sizeof(*fconf_res));
1036
0
  if (!fconf_res) {
1037
0
    memprintf(err, "'%s' : out of memory", args[0]);
1038
0
    return -1;
1039
0
  }
1040
0
  fconf_res->id = http_comp_res_flt_id;
1041
0
  fconf_res->name = "comp-res";
1042
0
  fconf_res->conf = NULL;
1043
0
  fconf_res->ops = &comp_res_ops;
1044
1045
  /* manually add the fconf_res to the list because filter API doesn't
1046
   * know about it
1047
   */
1048
0
  LIST_APPEND(&px->filter_configs, &fconf_res->list);
1049
1050
1051
0
  (*cur_arg)++;
1052
1053
0
  return 0;
1054
0
}
1055
1056
static int
1057
parse_http_comp_req_flt(char **args, int *cur_arg, struct proxy *px,
1058
                        struct flt_conf *fconf, char **err, void *private)
1059
0
{
1060
0
  struct flt_conf *fc, *back;
1061
0
  struct comp *comp;
1062
1063
0
  list_for_each_entry_safe(fc, back, &px->filter_configs, list) {
1064
0
    if (fc->id == http_comp_req_flt_id) {
1065
0
      memprintf(err, "%s: Proxy supports only one comp-req filter\n", px->id);
1066
0
      return -1;
1067
0
    }
1068
0
  }
1069
1070
0
  comp = proxy_get_comp(px, 0);
1071
0
  if (comp == NULL) {
1072
0
    memprintf(err, "memory failure\n");
1073
0
    return -1;
1074
0
  }
1075
0
  comp->flags |= COMP_FL_DIR_REQ;
1076
1077
0
  fconf->id   = http_comp_req_flt_id;
1078
0
  fconf->conf = NULL;
1079
0
  fconf->ops  = &comp_req_ops;
1080
0
  (*cur_arg)++;
1081
1082
0
  return 0;
1083
0
}
1084
1085
static int
1086
parse_http_comp_res_flt(char **args, int *cur_arg, struct proxy *px,
1087
                        struct flt_conf *fconf, char **err, void *private)
1088
0
{
1089
0
  struct flt_conf *fc, *back;
1090
0
  struct comp *comp;
1091
1092
0
  list_for_each_entry_safe(fc, back, &px->filter_configs, list) {
1093
0
    if (fc->id == http_comp_res_flt_id) {
1094
0
      memprintf(err, "%s: Proxy supports only one comp-res filter\n", px->id);
1095
0
      return -1;
1096
0
    }
1097
0
  }
1098
1099
0
  comp = proxy_get_comp(px, 0);
1100
0
  if (comp == NULL) {
1101
0
    memprintf(err, "memory failure\n");
1102
0
    return -1;
1103
0
  }
1104
0
  comp->flags |= COMP_FL_DIR_RES;
1105
1106
0
  fconf->id   = http_comp_res_flt_id;
1107
0
  fconf->conf = NULL;
1108
0
  fconf->ops  = &comp_res_ops;
1109
0
  (*cur_arg)++;
1110
1111
0
  return 0;
1112
0
}
1113
1114
int
1115
check_implicit_http_comp_flt(struct proxy *proxy)
1116
0
{
1117
0
  struct flt_conf *fconf;
1118
0
  struct flt_conf *fconf_req = NULL;
1119
0
  struct flt_conf *fconf_res = NULL;
1120
0
  int explicit = 0;
1121
0
  int comp = 0;
1122
0
  int err = 0;
1123
1124
0
  if (proxy->comp == NULL)
1125
0
    goto end;
1126
0
  if (!LIST_ISEMPTY(&proxy->filter_configs)) {
1127
0
    list_for_each_entry(fconf, &proxy->filter_configs, list) {
1128
0
      if (fconf->id == http_comp_req_flt_id || fconf->id == http_comp_res_flt_id)
1129
0
        comp = 1;
1130
0
      else if (fconf->id == cache_store_flt_id) {
1131
0
        if (comp) {
1132
0
          ha_alert("config: %s '%s': unable to enable the compression filter "
1133
0
             "before any cache filter.\n",
1134
0
             proxy_type_str(proxy), proxy->id);
1135
0
          err++;
1136
0
          goto end;
1137
0
        }
1138
0
      }
1139
0
#if defined(USE_FCGI)
1140
0
      else if (fconf->id == fcgi_flt_id)
1141
0
        continue;
1142
0
#endif
1143
0
      else
1144
0
        explicit = 1;
1145
0
    }
1146
0
  }
1147
0
  if (comp)
1148
0
    goto end;
1149
0
  else if (explicit) {
1150
0
    ha_alert("config: %s '%s': require an explicit filter declaration to use "
1151
0
       "HTTP compression\n", proxy_type_str(proxy), proxy->id);
1152
0
    err++;
1153
0
    goto end;
1154
0
  }
1155
1156
  /* Implicit declaration of the compression filter is always the last
1157
   * one */
1158
0
  fconf_req = calloc(1, sizeof(*fconf));
1159
0
  fconf_res = calloc(1, sizeof(*fconf));
1160
0
  if (!fconf_req || !fconf_res) {
1161
0
    ha_alert("config: %s '%s': out of memory\n",
1162
0
       proxy_type_str(proxy), proxy->id);
1163
0
    ha_free(&fconf_req);
1164
0
    ha_free(&fconf_res);
1165
0
    err++;
1166
0
    goto end;
1167
0
  }
1168
0
  fconf_req->id   = http_comp_req_flt_id;
1169
0
  fconf_req->conf = NULL;
1170
0
  fconf_req->ops  = &comp_req_ops;
1171
0
  LIST_APPEND(&proxy->filter_configs, &fconf_req->list);
1172
1173
0
  fconf_res->id   = http_comp_res_flt_id;
1174
0
  fconf_res->conf = NULL;
1175
0
  fconf_res->ops  = &comp_res_ops;
1176
0
  LIST_APPEND(&proxy->filter_configs, &fconf_res->list);
1177
0
 end:
1178
0
  return err;
1179
0
}
1180
1181
/*
1182
 * boolean, returns true if compression is used (either gzip or deflate) in the
1183
 * response.
1184
 */
1185
static int
1186
smp_fetch_res_comp(const struct arg *args, struct sample *smp, const char *kw,
1187
       void *private)
1188
0
{
1189
0
  struct http_txn *txn = smp->strm ? smp->strm->txn.http : NULL;
1190
1191
0
  smp->data.type = SMP_T_BOOL;
1192
0
  smp->data.u.sint = (txn && (txn->rsp.flags & HTTP_MSGF_COMPRESSING));
1193
0
  return 1;
1194
0
}
1195
1196
/*
1197
 * string, returns algo
1198
 */
1199
static int
1200
smp_fetch_res_comp_algo(const struct arg *args, struct sample *smp,
1201
      const char *kw, void *private)
1202
0
{
1203
0
  struct http_txn   *txn = smp->strm ? smp->strm->txn.http : NULL;
1204
0
  struct filter     *filter;
1205
0
  struct comp_state *st;
1206
1207
0
  if (!txn || !(txn->rsp.flags & HTTP_MSGF_COMPRESSING))
1208
0
    return 0;
1209
1210
0
  list_for_each_entry(filter, &strm_flt(smp->strm)->filters, list) {
1211
0
    if (FLT_ID(filter) != http_comp_res_flt_id)
1212
0
      continue;
1213
1214
0
    if (!(st = filter->ctx))
1215
0
      break;
1216
1217
0
    smp->data.type = SMP_T_STR;
1218
0
    smp->flags = SMP_F_CONST;
1219
0
    smp->data.u.str.area = st->comp_algo->cfg_name;
1220
0
    smp->data.u.str.data = st->comp_algo->cfg_name_len;
1221
0
    return 1;
1222
0
  }
1223
0
  return 0;
1224
0
}
1225
1226
/* Declare the config parser for "compression" keyword */
1227
static struct cfg_kw_list cfg_kws = {ILH, {
1228
    { CFG_LISTEN, "compression", parse_compression_options },
1229
    { 0, NULL, NULL },
1230
  }
1231
};
1232
1233
INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
1234
1235
/* Declare the filter parser for "compression" keyword */
1236
static struct flt_kw_list filter_kws = { "COMP", { }, {
1237
    { "compression", parse_http_comp_flt, NULL },
1238
    { "comp-req", parse_http_comp_req_flt, NULL },
1239
    { "comp-res", parse_http_comp_res_flt, NULL },
1240
    { NULL, NULL, NULL },
1241
  }
1242
};
1243
1244
INITCALL1(STG_REGISTER, flt_register_keywords, &filter_kws);
1245
1246
/* Note: must not be declared <const> as its list will be overwritten */
1247
static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
1248
    { "res.comp",      smp_fetch_res_comp,      0, NULL, SMP_T_BOOL, SMP_USE_HRSHP },
1249
    { "res.comp_algo", smp_fetch_res_comp_algo, 0, NULL, SMP_T_STR, SMP_USE_HRSHP },
1250
    { /* END */ },
1251
  }
1252
};
1253
1254
INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords);