blob: 20843973825760cd982899f0be6d230ef0619332 [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
13#include <common/buffer.h>
14#include <common/cfgparse.h>
15#include <common/mini-clist.h>
16#include <common/standard.h>
17
18#include <types/compression.h>
19#include <types/filters.h>
20#include <types/proto_http.h>
21#include <types/proxy.h>
22#include <types/sample.h>
23
24#include <proto/compression.h>
Christopher Faulet92d36382015-11-05 13:35:03 +010025#include <proto/filters.h>
Christopher Faulet3d97c902015-12-09 14:59:38 +010026#include <proto/hdr_idx.h>
27#include <proto/proto_http.h>
28#include <proto/sample.h>
29#include <proto/stream.h>
30
Christopher Faulet92d36382015-11-05 13:35:03 +010031static const char *http_comp_flt_id = "compression filter";
32
33struct flt_ops comp_ops;
34
Christopher Fauleta03d4ad2017-06-26 16:53:33 +020035
36/* Pools used to allocate comp_state structs */
Willy Tarreaubafbe012017-11-24 17:34:44 +010037static struct pool_head *pool_head_comp_state = NULL;
Christopher Fauleta03d4ad2017-06-26 16:53:33 +020038
Christopher Faulet8ca3b4b2017-07-25 11:07:15 +020039static THREAD_LOCAL struct buffer *tmpbuf = &buf_empty;
40static THREAD_LOCAL struct buffer *zbuf = &buf_empty;
Christopher Faulet92d36382015-11-05 13:35:03 +010041
Christopher Faulet92d36382015-11-05 13:35:03 +010042struct comp_state {
43 struct comp_ctx *comp_ctx; /* compression context */
44 struct comp_algo *comp_algo; /* compression algorithm if not NULL */
Christopher Fauletb77c5c22015-12-07 16:48:42 +010045 int hdrs_len;
46 int tlrs_len;
Christopher Faulet2fb28802015-12-01 10:40:57 +010047 int consumed;
48 int initialized;
Christopher Fauletb77c5c22015-12-07 16:48:42 +010049 int finished;
Christopher Faulet92d36382015-11-05 13:35:03 +010050};
51
Christopher Faulet92d36382015-11-05 13:35:03 +010052static int select_compression_request_header(struct comp_state *st,
53 struct stream *s,
54 struct http_msg *msg);
55static int select_compression_response_header(struct comp_state *st,
56 struct stream *s,
57 struct http_msg *msg);
58
59static int http_compression_buffer_init(struct buffer *in, struct buffer *out);
60static int http_compression_buffer_add_data(struct comp_state *st,
61 struct buffer *in,
62 struct buffer *out, int sz);
63static int http_compression_buffer_end(struct comp_state *st, struct stream *s,
Willy Tarreau4d452382018-06-06 07:15:47 +020064 struct channel *chn, struct buffer **out,
Christopher Faulet2fb28802015-12-01 10:40:57 +010065 int end);
Christopher Faulet92d36382015-11-05 13:35:03 +010066
67/***********************************************************************/
68static int
Christopher Faulet8ca3b4b2017-07-25 11:07:15 +020069comp_flt_init_per_thread(struct proxy *px, struct flt_conf *fconf)
Christopher Faulet92d36382015-11-05 13:35:03 +010070{
Christopher Fauletb77c5c22015-12-07 16:48:42 +010071 if (!tmpbuf->size && b_alloc(&tmpbuf) == NULL)
72 return -1;
73 if (!zbuf->size && b_alloc(&zbuf) == NULL)
74 return -1;
Christopher Faulet92d36382015-11-05 13:35:03 +010075 return 0;
76}
77
78static void
Christopher Faulet8ca3b4b2017-07-25 11:07:15 +020079comp_flt_deinit_per_thread(struct proxy *px, struct flt_conf *fconf)
Christopher Faulet92d36382015-11-05 13:35:03 +010080{
81 if (tmpbuf->size)
82 b_free(&tmpbuf);
Christopher Fauletb77c5c22015-12-07 16:48:42 +010083 if (zbuf->size)
84 b_free(&zbuf);
Christopher Faulet92d36382015-11-05 13:35:03 +010085}
86
87static int
88comp_start_analyze(struct stream *s, struct filter *filter, struct channel *chn)
89{
Christopher Faulet8ca3b4b2017-07-25 11:07:15 +020090
Christopher Faulet92d36382015-11-05 13:35:03 +010091 if (filter->ctx == NULL) {
92 struct comp_state *st;
93
Willy Tarreaubafbe012017-11-24 17:34:44 +010094 st = pool_alloc_dirty(pool_head_comp_state);
Christopher Fauleta03d4ad2017-06-26 16:53:33 +020095 if (st == NULL)
Christopher Faulet92d36382015-11-05 13:35:03 +010096 return -1;
97
Christopher Faulet2fb28802015-12-01 10:40:57 +010098 st->comp_algo = NULL;
99 st->comp_ctx = NULL;
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100100 st->hdrs_len = 0;
101 st->tlrs_len = 0;
Christopher Faulet2fb28802015-12-01 10:40:57 +0100102 st->consumed = 0;
103 st->initialized = 0;
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100104 st->finished = 0;
Christopher Faulet2fb28802015-12-01 10:40:57 +0100105 filter->ctx = st;
Christopher Faulet3dc860d2017-09-15 11:39:36 +0200106
107 /* Register post-analyzer on AN_RES_WAIT_HTTP because we need to
108 * analyze response headers before http-response rules execution
109 * to be sure we can use res.comp and res.comp_algo sample
110 * fetches */
111 filter->post_analyzers |= AN_RES_WAIT_HTTP;
Christopher Faulet92d36382015-11-05 13:35:03 +0100112 }
113 return 1;
114}
115
116static int
Christopher Faulet92d36382015-11-05 13:35:03 +0100117comp_end_analyze(struct stream *s, struct filter *filter, struct channel *chn)
118{
119 struct comp_state *st = filter->ctx;
Christopher Faulet92d36382015-11-05 13:35:03 +0100120
Christopher Fauletd60b3cf2017-06-26 11:47:13 +0200121 if (!st)
Christopher Faulet92d36382015-11-05 13:35:03 +0100122 goto end;
123
Christopher Faulet92d36382015-11-05 13:35:03 +0100124 /* release any possible compression context */
Christopher Fauletd60b3cf2017-06-26 11:47:13 +0200125 if (st->comp_algo)
126 st->comp_algo->end(&st->comp_ctx);
Willy Tarreaubafbe012017-11-24 17:34:44 +0100127 pool_free(pool_head_comp_state, st);
Christopher Faulet92d36382015-11-05 13:35:03 +0100128 filter->ctx = NULL;
129 end:
130 return 1;
131}
132
133static int
Christopher Faulet1339d742016-05-11 16:48:33 +0200134comp_http_headers(struct stream *s, struct filter *filter, struct http_msg *msg)
135{
136 struct comp_state *st = filter->ctx;
137
138 if (!strm_fe(s)->comp && !s->be->comp)
139 goto end;
140
141 if (!(msg->chn->flags & CF_ISRESP))
142 select_compression_request_header(st, s, msg);
143 else {
Christopher Faulet3dc860d2017-09-15 11:39:36 +0200144 /* Response headers have already been checked in
145 * comp_http_post_analyze callback. */
Christopher Faulet1339d742016-05-11 16:48:33 +0200146 if (st->comp_algo) {
147 register_data_filter(s, msg->chn, filter);
148 st->hdrs_len = s->txn->rsp.sov;
149 }
150 }
151
152 end:
153 return 1;
154}
155
156static int
Christopher Faulet3dc860d2017-09-15 11:39:36 +0200157comp_http_post_analyze(struct stream *s, struct filter *filter,
158 struct channel *chn, unsigned an_bit)
159{
160 struct http_txn *txn = s->txn;
161 struct http_msg *msg = &txn->rsp;
162 struct comp_state *st = filter->ctx;
163
164 if (an_bit != AN_RES_WAIT_HTTP)
165 goto end;
166
167 if (!strm_fe(s)->comp && !s->be->comp)
168 goto end;
169
170 select_compression_response_header(st, s, msg);
171
172 end:
173 return 1;
174}
175
176static int
Christopher Faulet2fb28802015-12-01 10:40:57 +0100177comp_http_data(struct stream *s, struct filter *filter, struct http_msg *msg)
Christopher Faulet92d36382015-11-05 13:35:03 +0100178{
179 struct comp_state *st = filter->ctx;
Willy Tarreaubcbd3932018-06-06 07:13:22 +0200180 struct channel *chn = msg->chn;
181 struct buffer *buf = chn->buf;
Christopher Faulet3e7bc672015-12-07 13:39:08 +0100182 unsigned int *nxt = &flt_rsp_nxt(filter);
Christopher Faulet2fb28802015-12-01 10:40:57 +0100183 unsigned int len;
Christopher Faulet92d36382015-11-05 13:35:03 +0100184 int ret;
185
Christopher Faulet3e7bc672015-12-07 13:39:08 +0100186 len = MIN(msg->chunk_len + msg->next, buf->i) - *nxt;
Christopher Faulet2fb28802015-12-01 10:40:57 +0100187 if (!len)
188 return len;
189
190 if (!st->initialized) {
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100191 unsigned int fwd = flt_rsp_fwd(filter) + st->hdrs_len;
Christopher Faulet3e7bc672015-12-07 13:39:08 +0100192
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100193 b_reset(tmpbuf);
Willy Tarreaubcbd3932018-06-06 07:13:22 +0200194 c_adv(chn, fwd);
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100195 ret = http_compression_buffer_init(buf, zbuf);
Willy Tarreaubcbd3932018-06-06 07:13:22 +0200196 c_rew(chn, fwd);
Christopher Faulet2fb28802015-12-01 10:40:57 +0100197 if (ret < 0) {
198 msg->chn->flags |= CF_WAKE_WRITE;
199 return 0;
200 }
201 }
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100202
203 if (msg->flags & HTTP_MSGF_TE_CHNK) {
Christopher Faulet06ecf3a2016-09-22 15:31:43 +0200204 int block;
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100205
206 len = MIN(tmpbuf->size - buffer_len(tmpbuf), len);
Christopher Faulet06ecf3a2016-09-22 15:31:43 +0200207
Willy Tarreaubcbd3932018-06-06 07:13:22 +0200208 c_adv(chn, *nxt);
Willy Tarreau7194d3c2018-06-06 16:55:45 +0200209 block = ci_contig_data(chn);
Willy Tarreaudda2e412018-06-07 18:08:04 +0200210 memcpy(bi_end(tmpbuf), ci_head(chn), block);
Christopher Faulet06ecf3a2016-09-22 15:31:43 +0200211 if (len > block)
212 memcpy(bi_end(tmpbuf)+block, buf->data, len-block);
Willy Tarreaubcbd3932018-06-06 07:13:22 +0200213 c_rew(chn, *nxt);
Christopher Faulet06ecf3a2016-09-22 15:31:43 +0200214
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100215 tmpbuf->i += len;
216 ret = len;
217 }
218 else {
Willy Tarreaubcbd3932018-06-06 07:13:22 +0200219 c_adv(chn, *nxt);
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100220 ret = http_compression_buffer_add_data(st, buf, zbuf, len);
Willy Tarreaubcbd3932018-06-06 07:13:22 +0200221 c_rew(chn, *nxt);
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100222 if (ret < 0)
223 return ret;
224 }
Christopher Faulet92d36382015-11-05 13:35:03 +0100225
Christopher Faulet2fb28802015-12-01 10:40:57 +0100226 st->initialized = 1;
227 msg->next += ret;
228 msg->chunk_len -= ret;
Christopher Faulet3e7bc672015-12-07 13:39:08 +0100229 *nxt = msg->next;
Christopher Faulet2fb28802015-12-01 10:40:57 +0100230 return 0;
Christopher Faulet92d36382015-11-05 13:35:03 +0100231}
232
233static int
Christopher Faulet2fb28802015-12-01 10:40:57 +0100234comp_http_chunk_trailers(struct stream *s, struct filter *filter,
235 struct http_msg *msg)
Christopher Faulet92d36382015-11-05 13:35:03 +0100236{
237 struct comp_state *st = filter->ctx;
Christopher Faulet92d36382015-11-05 13:35:03 +0100238
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100239 if (!st->initialized) {
240 if (!st->finished) {
Willy Tarreaubcbd3932018-06-06 07:13:22 +0200241 struct channel *chn = msg->chn;
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100242 unsigned int fwd = flt_rsp_fwd(filter) + st->hdrs_len;
Christopher Faulet2fb28802015-12-01 10:40:57 +0100243
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100244 b_reset(tmpbuf);
Willy Tarreaubcbd3932018-06-06 07:13:22 +0200245 c_adv(chn, fwd);
246 http_compression_buffer_init(chn->buf, zbuf);
247 c_rew(chn, fwd);
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100248 st->initialized = 1;
249 }
250 }
251 st->tlrs_len = msg->sol;
Christopher Faulet2fb28802015-12-01 10:40:57 +0100252 return 1;
Christopher Faulet92d36382015-11-05 13:35:03 +0100253}
254
Christopher Faulet2fb28802015-12-01 10:40:57 +0100255
Christopher Faulet92d36382015-11-05 13:35:03 +0100256static int
257comp_http_forward_data(struct stream *s, struct filter *filter,
258 struct http_msg *msg, unsigned int len)
259{
260 struct comp_state *st = filter->ctx;
Christopher Faulet2fb28802015-12-01 10:40:57 +0100261 int ret;
Christopher Faulet92d36382015-11-05 13:35:03 +0100262
Christopher Faulet2fb28802015-12-01 10:40:57 +0100263 /* To work, previous filters MUST forward all data */
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100264 if (flt_rsp_fwd(filter) + len != flt_rsp_nxt(filter)) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100265 ha_warning("HTTP compression failed: unexpected behavior of previous filters\n");
Christopher Faulet2fb28802015-12-01 10:40:57 +0100266 return -1;
Christopher Faulet92d36382015-11-05 13:35:03 +0100267 }
268
Christopher Faulet2fb28802015-12-01 10:40:57 +0100269 if (!st->initialized) {
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100270 if (!len) {
271 /* Nothing to foward */
272 ret = len;
273 }
274 else if (st->hdrs_len > len) {
275 /* Forward part of headers */
276 ret = len;
277 st->hdrs_len -= len;
278 }
279 else if (st->hdrs_len > 0) {
280 /* Forward remaining headers */
281 ret = st->hdrs_len;
282 st->hdrs_len = 0;
283 }
284 else if (msg->msg_state < HTTP_MSG_TRAILERS) {
285 /* Do not forward anything for now. This only happens
286 * with chunk-encoded responses. Waiting data are part
287 * of the chunk envelope (the chunk size or the chunk
288 * CRLF). These data will be skipped during the
289 * compression. */
290 ret = 0;
291 }
292 else {
293 /* Forward trailers data */
294 ret = len;
295 }
Christopher Faulet2fb28802015-12-01 10:40:57 +0100296 return ret;
Christopher Faulet92d36382015-11-05 13:35:03 +0100297 }
298
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100299 if (msg->flags & HTTP_MSGF_TE_CHNK) {
300 ret = http_compression_buffer_add_data(st, tmpbuf, zbuf, tmpbuf->i);
301 if (ret != tmpbuf->i) {
Willy Tarreau506a29a2018-07-18 10:07:58 +0200302 ha_warning("HTTP compression failed: Must consume %u bytes but only %d bytes consumed\n",
303 (unsigned int)tmpbuf->i, ret);
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100304 return -1;
305 }
306 }
307
308 st->consumed = len - st->hdrs_len - st->tlrs_len;
Willy Tarreaubcbd3932018-06-06 07:13:22 +0200309 c_adv(msg->chn, flt_rsp_fwd(filter) + st->hdrs_len);
Willy Tarreau4d452382018-06-06 07:15:47 +0200310 ret = http_compression_buffer_end(st, s, msg->chn, &zbuf, msg->msg_state >= HTTP_MSG_TRAILERS);
Willy Tarreaubcbd3932018-06-06 07:13:22 +0200311 c_rew(msg->chn, flt_rsp_fwd(filter) + st->hdrs_len);
Christopher Faulet2fb28802015-12-01 10:40:57 +0100312 if (ret < 0)
313 return ret;
Christopher Faulet92d36382015-11-05 13:35:03 +0100314
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100315 flt_change_forward_size(filter, msg->chn, ret - st->consumed);
316 msg->next += (ret - st->consumed);
317 ret += st->hdrs_len + st->tlrs_len;
318
Christopher Faulet2fb28802015-12-01 10:40:57 +0100319 st->initialized = 0;
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100320 st->finished = (msg->msg_state >= HTTP_MSG_TRAILERS);
321 st->hdrs_len = 0;
322 st->tlrs_len = 0;
Christopher Faulet92d36382015-11-05 13:35:03 +0100323 return ret;
324}
Christopher Faulet3d97c902015-12-09 14:59:38 +0100325
Christopher Fauletd60b3cf2017-06-26 11:47:13 +0200326static int
327comp_http_end(struct stream *s, struct filter *filter,
328 struct http_msg *msg)
329{
330 struct comp_state *st = filter->ctx;
331
332 if (!(msg->chn->flags & CF_ISRESP) || !st || !st->comp_algo)
333 goto end;
334
335 if (strm_fe(s)->mode == PR_MODE_HTTP)
Christopher Fauletff8abcd2017-06-02 15:33:24 +0200336 HA_ATOMIC_ADD(&strm_fe(s)->fe_counters.p.http.comp_rsp, 1);
Christopher Fauletd60b3cf2017-06-26 11:47:13 +0200337 if ((s->flags & SF_BE_ASSIGNED) && (s->be->mode == PR_MODE_HTTP))
Christopher Fauletff8abcd2017-06-02 15:33:24 +0200338 HA_ATOMIC_ADD(&s->be->be_counters.p.http.comp_rsp, 1);
Christopher Fauletd60b3cf2017-06-26 11:47:13 +0200339 end:
340 return 1;
341}
Christopher Faulet3d97c902015-12-09 14:59:38 +0100342/***********************************************************************/
343/*
344 * Selects a compression algorithm depending on the client request.
345 */
346int
Christopher Faulet92d36382015-11-05 13:35:03 +0100347select_compression_request_header(struct comp_state *st, struct stream *s,
348 struct http_msg *msg)
Christopher Faulet3d97c902015-12-09 14:59:38 +0100349{
350 struct http_txn *txn = s->txn;
Christopher Faulet92d36382015-11-05 13:35:03 +0100351 struct buffer *req = msg->chn->buf;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100352 struct hdr_ctx ctx;
353 struct comp_algo *comp_algo = NULL;
354 struct comp_algo *comp_algo_back = NULL;
355
356 /* Disable compression for older user agents announcing themselves as "Mozilla/4"
357 * unless they are known good (MSIE 6 with XP SP2, or MSIE 7 and later).
358 * See http://zoompf.com/2012/02/lose-the-wait-http-compression for more details.
359 */
360 ctx.idx = 0;
361 if (http_find_header2("User-Agent", 10, req->p, &txn->hdr_idx, &ctx) &&
362 ctx.vlen >= 9 &&
363 memcmp(ctx.line + ctx.val, "Mozilla/4", 9) == 0 &&
364 (ctx.vlen < 31 ||
365 memcmp(ctx.line + ctx.val + 25, "MSIE ", 5) != 0 ||
366 ctx.line[ctx.val + 30] < '6' ||
367 (ctx.line[ctx.val + 30] == '6' &&
368 (ctx.vlen < 54 || memcmp(ctx.line + 51, "SV1", 3) != 0)))) {
Christopher Faulet92d36382015-11-05 13:35:03 +0100369 st->comp_algo = NULL;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100370 return 0;
371 }
372
373 /* search for the algo in the backend in priority or the frontend */
Christopher Faulet92d36382015-11-05 13:35:03 +0100374 if ((s->be->comp && (comp_algo_back = s->be->comp->algos)) ||
375 (strm_fe(s)->comp && (comp_algo_back = strm_fe(s)->comp->algos))) {
Christopher Faulet3d97c902015-12-09 14:59:38 +0100376 int best_q = 0;
377
378 ctx.idx = 0;
379 while (http_find_header2("Accept-Encoding", 15, req->p, &txn->hdr_idx, &ctx)) {
380 const char *qval;
381 int q;
382 int toklen;
383
384 /* try to isolate the token from the optional q-value */
385 toklen = 0;
Willy Tarreau2235b262016-11-05 15:50:20 +0100386 while (toklen < ctx.vlen && HTTP_IS_TOKEN(*(ctx.line + ctx.val + toklen)))
Christopher Faulet3d97c902015-12-09 14:59:38 +0100387 toklen++;
388
389 qval = ctx.line + ctx.val + toklen;
390 while (1) {
Willy Tarreau2235b262016-11-05 15:50:20 +0100391 while (qval < ctx.line + ctx.val + ctx.vlen && HTTP_IS_LWS(*qval))
Christopher Faulet3d97c902015-12-09 14:59:38 +0100392 qval++;
393
394 if (qval >= ctx.line + ctx.val + ctx.vlen || *qval != ';') {
395 qval = NULL;
396 break;
397 }
398 qval++;
399
Willy Tarreau2235b262016-11-05 15:50:20 +0100400 while (qval < ctx.line + ctx.val + ctx.vlen && HTTP_IS_LWS(*qval))
Christopher Faulet3d97c902015-12-09 14:59:38 +0100401 qval++;
402
403 if (qval >= ctx.line + ctx.val + ctx.vlen) {
404 qval = NULL;
405 break;
406 }
407 if (strncmp(qval, "q=", MIN(ctx.line + ctx.val + ctx.vlen - qval, 2)) == 0)
408 break;
409
410 while (qval < ctx.line + ctx.val + ctx.vlen && *qval != ';')
411 qval++;
412 }
413
414 /* here we have qval pointing to the first "q=" attribute or NULL if not found */
415 q = qval ? parse_qvalue(qval + 2, NULL) : 1000;
416
417 if (q <= best_q)
418 continue;
419
420 for (comp_algo = comp_algo_back; comp_algo; comp_algo = comp_algo->next) {
421 if (*(ctx.line + ctx.val) == '*' ||
422 word_match(ctx.line + ctx.val, toklen, comp_algo->ua_name, comp_algo->ua_name_len)) {
Christopher Faulet92d36382015-11-05 13:35:03 +0100423 st->comp_algo = comp_algo;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100424 best_q = q;
425 break;
426 }
427 }
428 }
429 }
430
431 /* remove all occurrences of the header when "compression offload" is set */
Christopher Faulet92d36382015-11-05 13:35:03 +0100432 if (st->comp_algo) {
433 if ((s->be->comp && s->be->comp->offload) ||
434 (strm_fe(s)->comp && strm_fe(s)->comp->offload)) {
Christopher Faulet3d97c902015-12-09 14:59:38 +0100435 http_remove_header2(msg, &txn->hdr_idx, &ctx);
436 ctx.idx = 0;
437 while (http_find_header2("Accept-Encoding", 15, req->p, &txn->hdr_idx, &ctx)) {
438 http_remove_header2(msg, &txn->hdr_idx, &ctx);
439 }
440 }
441 return 1;
442 }
443
444 /* identity is implicit does not require headers */
Christopher Faulet92d36382015-11-05 13:35:03 +0100445 if ((s->be->comp && (comp_algo_back = s->be->comp->algos)) ||
446 (strm_fe(s)->comp && (comp_algo_back = strm_fe(s)->comp->algos))) {
Christopher Faulet3d97c902015-12-09 14:59:38 +0100447 for (comp_algo = comp_algo_back; comp_algo; comp_algo = comp_algo->next) {
448 if (comp_algo->cfg_name_len == 8 && memcmp(comp_algo->cfg_name, "identity", 8) == 0) {
Christopher Faulet92d36382015-11-05 13:35:03 +0100449 st->comp_algo = comp_algo;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100450 return 1;
451 }
452 }
453 }
454
Christopher Faulet92d36382015-11-05 13:35:03 +0100455 st->comp_algo = NULL;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100456 return 0;
457}
458
Christopher Faulet92d36382015-11-05 13:35:03 +0100459
Christopher Faulet3d97c902015-12-09 14:59:38 +0100460/*
461 * Selects a comression algorithm depending of the server response.
462 */
Christopher Faulet92d36382015-11-05 13:35:03 +0100463static int
464select_compression_response_header(struct comp_state *st, struct stream *s, struct http_msg *msg)
Christopher Faulet3d97c902015-12-09 14:59:38 +0100465{
466 struct http_txn *txn = s->txn;
Christopher Faulet92d36382015-11-05 13:35:03 +0100467 struct buffer *res = msg->chn->buf;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100468 struct hdr_ctx ctx;
469 struct comp_type *comp_type;
470
471 /* no common compression algorithm was found in request header */
Christopher Faulet92d36382015-11-05 13:35:03 +0100472 if (st->comp_algo == NULL)
Christopher Faulet3d97c902015-12-09 14:59:38 +0100473 goto fail;
474
475 /* HTTP < 1.1 should not be compressed */
476 if (!(msg->flags & HTTP_MSGF_VER_11) || !(txn->req.flags & HTTP_MSGF_VER_11))
477 goto fail;
478
Christopher Faulet92d36382015-11-05 13:35:03 +0100479 if (txn->meth == HTTP_METH_HEAD)
480 goto fail;
481
Christopher Faulet3d97c902015-12-09 14:59:38 +0100482 /* compress 200,201,202,203 responses only */
483 if ((txn->status != 200) &&
484 (txn->status != 201) &&
485 (txn->status != 202) &&
486 (txn->status != 203))
487 goto fail;
488
489
490 /* Content-Length is null */
491 if (!(msg->flags & HTTP_MSGF_TE_CHNK) && msg->body_len == 0)
492 goto fail;
493
494 /* content is already compressed */
495 ctx.idx = 0;
496 if (http_find_header2("Content-Encoding", 16, res->p, &txn->hdr_idx, &ctx))
497 goto fail;
498
499 /* no compression when Cache-Control: no-transform is present in the message */
500 ctx.idx = 0;
501 while (http_find_header2("Cache-Control", 13, res->p, &txn->hdr_idx, &ctx)) {
502 if (word_match(ctx.line + ctx.val, ctx.vlen, "no-transform", 12))
503 goto fail;
504 }
505
506 comp_type = NULL;
507
508 /* we don't want to compress multipart content-types, nor content-types that are
509 * not listed in the "compression type" directive if any. If no content-type was
510 * found but configuration requires one, we don't compress either. Backend has
511 * the priority.
512 */
513 ctx.idx = 0;
514 if (http_find_header2("Content-Type", 12, res->p, &txn->hdr_idx, &ctx)) {
515 if (ctx.vlen >= 9 && strncasecmp("multipart", ctx.line+ctx.val, 9) == 0)
516 goto fail;
517
518 if ((s->be->comp && (comp_type = s->be->comp->types)) ||
519 (strm_fe(s)->comp && (comp_type = strm_fe(s)->comp->types))) {
520 for (; comp_type; comp_type = comp_type->next) {
521 if (ctx.vlen >= comp_type->name_len &&
522 strncasecmp(ctx.line+ctx.val, comp_type->name, comp_type->name_len) == 0)
523 /* this Content-Type should be compressed */
524 break;
525 }
526 /* this Content-Type should not be compressed */
527 if (comp_type == NULL)
528 goto fail;
529 }
530 }
531 else { /* no content-type header */
Christopher Faulet92d36382015-11-05 13:35:03 +0100532 if ((s->be->comp && s->be->comp->types) ||
533 (strm_fe(s)->comp && strm_fe(s)->comp->types))
Christopher Faulet3d97c902015-12-09 14:59:38 +0100534 goto fail; /* a content-type was required */
535 }
536
537 /* limit compression rate */
538 if (global.comp_rate_lim > 0)
539 if (read_freq_ctr(&global.comp_bps_in) > global.comp_rate_lim)
540 goto fail;
541
542 /* limit cpu usage */
543 if (idle_pct < compress_min_idle)
544 goto fail;
545
546 /* initialize compression */
Christopher Faulet92d36382015-11-05 13:35:03 +0100547 if (st->comp_algo->init(&st->comp_ctx, global.tune.comp_maxlevel) < 0)
Christopher Faulet3d97c902015-12-09 14:59:38 +0100548 goto fail;
549
Christopher Faulet3d97c902015-12-09 14:59:38 +0100550 /* remove Content-Length header */
551 ctx.idx = 0;
552 if ((msg->flags & HTTP_MSGF_CNT_LEN) && http_find_header2("Content-Length", 14, res->p, &txn->hdr_idx, &ctx))
553 http_remove_header2(msg, &txn->hdr_idx, &ctx);
554
555 /* add Transfer-Encoding header */
556 if (!(msg->flags & HTTP_MSGF_TE_CHNK))
557 http_header_add_tail2(&txn->rsp, &txn->hdr_idx, "Transfer-Encoding: chunked", 26);
558
559 /*
560 * Add Content-Encoding header when it's not identity encoding.
561 * RFC 2616 : Identity encoding: This content-coding is used only in the
562 * Accept-Encoding header, and SHOULD NOT be used in the Content-Encoding
563 * header.
564 */
Christopher Faulet92d36382015-11-05 13:35:03 +0100565 if (st->comp_algo->cfg_name_len != 8 || memcmp(st->comp_algo->cfg_name, "identity", 8) != 0) {
Christopher Faulet3d97c902015-12-09 14:59:38 +0100566 trash.len = 18;
567 memcpy(trash.str, "Content-Encoding: ", trash.len);
Christopher Faulet92d36382015-11-05 13:35:03 +0100568 memcpy(trash.str + trash.len, st->comp_algo->ua_name, st->comp_algo->ua_name_len);
569 trash.len += st->comp_algo->ua_name_len;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100570 trash.str[trash.len] = '\0';
571 http_header_add_tail2(&txn->rsp, &txn->hdr_idx, trash.str, trash.len);
572 }
Christopher Faulet92d36382015-11-05 13:35:03 +0100573 msg->flags |= HTTP_MSGF_COMPRESSING;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100574 return 1;
575
576fail:
Christopher Faulet92d36382015-11-05 13:35:03 +0100577 st->comp_algo = NULL;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100578 return 0;
579}
580
581/***********************************************************************/
582/* emit the chunksize followed by a CRLF on the output and return the number of
583 * bytes written. It goes backwards and starts with the byte before <end>. It
584 * returns the number of bytes written which will not exceed 10 (8 digits, CR,
585 * and LF). The caller is responsible for ensuring there is enough room left in
586 * the output buffer for the string.
587 */
588static int
589http_emit_chunk_size(char *end, unsigned int chksz)
590{
591 char *beg = end;
592
593 *--beg = '\n';
594 *--beg = '\r';
595 do {
596 *--beg = hextab[chksz & 0xF];
597 } while (chksz >>= 4);
598 return end - beg;
599}
600
601/*
602 * Init HTTP compression
603 */
Christopher Faulet92d36382015-11-05 13:35:03 +0100604static int
605http_compression_buffer_init(struct buffer *in, struct buffer *out)
Christopher Faulet3d97c902015-12-09 14:59:38 +0100606{
607 /* output stream requires at least 10 bytes for the gzip header, plus
608 * at least 8 bytes for the gzip trailer (crc+len), plus a possible
609 * plus at most 5 bytes per 32kB block and 2 bytes to close the stream.
610 */
611 if (in->size - buffer_len(in) < 20 + 5 * ((in->i + 32767) >> 15))
612 return -1;
613
614 /* prepare an empty output buffer in which we reserve enough room for
615 * copying the output bytes from <in>, plus 10 extra bytes to write
616 * the chunk size. We don't copy the bytes yet so that if we have to
617 * cancel the operation later, it's cheap.
618 */
619 b_reset(out);
620 out->o = in->o;
621 out->p += out->o;
622 out->i = 10;
623 return 0;
624}
625
626/*
627 * Add data to compress
628 */
Christopher Faulet92d36382015-11-05 13:35:03 +0100629static int
630http_compression_buffer_add_data(struct comp_state *st, struct buffer *in,
631 struct buffer *out, int sz)
Christopher Faulet3d97c902015-12-09 14:59:38 +0100632{
Christopher Faulet3d97c902015-12-09 14:59:38 +0100633 int consumed_data = 0;
634 int data_process_len;
635 int block1, block2;
636
Christopher Faulet92d36382015-11-05 13:35:03 +0100637 if (!sz)
Christopher Faulet3e7bc672015-12-07 13:39:08 +0100638 goto end;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100639
Christopher Faulet92d36382015-11-05 13:35:03 +0100640 /* select the smallest size between the announced chunk size, the input
Christopher Faulet3d97c902015-12-09 14:59:38 +0100641 * data, and the available output buffer size. The compressors are
Christopher Faulet92d36382015-11-05 13:35:03 +0100642 * assumed to be able to process all the bytes we pass to them at
643 * once. */
Christopher Faulet3e7bc672015-12-07 13:39:08 +0100644 data_process_len = MIN(out->size - buffer_len(out), sz);
Christopher Faulet92d36382015-11-05 13:35:03 +0100645
Christopher Faulet3d97c902015-12-09 14:59:38 +0100646 block1 = data_process_len;
Willy Tarreau7194d3c2018-06-06 16:55:45 +0200647 if (block1 > b_contig_data(in, in->o))
648 block1 = b_contig_data(in, in->o);
Christopher Faulet3d97c902015-12-09 14:59:38 +0100649 block2 = data_process_len - block1;
650
651 /* compressors return < 0 upon error or the amount of bytes read */
Willy Tarreaudda2e412018-06-07 18:08:04 +0200652 consumed_data = st->comp_algo->add_data(st->comp_ctx, b_peek(in, in->o), block1, out);
Christopher Faulet3e7bc672015-12-07 13:39:08 +0100653 if (consumed_data != block1 || !block2)
654 goto end;
655 consumed_data = st->comp_algo->add_data(st->comp_ctx, in->data, block2, out);
656 if (consumed_data < 0)
657 goto end;
658 consumed_data += block1;
659
660 end:
Christopher Faulet3d97c902015-12-09 14:59:38 +0100661 return consumed_data;
662}
663
664/*
665 * Flush data in process, and write the header and footer of the chunk. Upon
666 * success, in and out buffers are swapped to avoid a copy.
667 */
Christopher Faulet92d36382015-11-05 13:35:03 +0100668static int
669http_compression_buffer_end(struct comp_state *st, struct stream *s,
Willy Tarreau4d452382018-06-06 07:15:47 +0200670 struct channel *chn, struct buffer **out,
Christopher Faulet2fb28802015-12-01 10:40:57 +0100671 int end)
Christopher Faulet3d97c902015-12-09 14:59:38 +0100672{
Willy Tarreau4d452382018-06-06 07:15:47 +0200673 struct buffer *ib = chn->buf, *ob = *out;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100674 char *tail;
Christopher Faulet92d36382015-11-05 13:35:03 +0100675 int to_forward, left;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100676
677#if defined(USE_SLZ) || defined(USE_ZLIB)
678 int ret;
679
680 /* flush data here */
Christopher Faulet3d97c902015-12-09 14:59:38 +0100681 if (end)
Christopher Faulet92d36382015-11-05 13:35:03 +0100682 ret = st->comp_algo->finish(st->comp_ctx, ob); /* end of data */
Christopher Faulet3d97c902015-12-09 14:59:38 +0100683 else
Christopher Faulet92d36382015-11-05 13:35:03 +0100684 ret = st->comp_algo->flush(st->comp_ctx, ob); /* end of buffer */
Christopher Faulet3d97c902015-12-09 14:59:38 +0100685
686 if (ret < 0)
687 return -1; /* flush failed */
688
689#endif /* USE_ZLIB */
Christopher Faulet3d97c902015-12-09 14:59:38 +0100690 if (ob->i == 10) {
691 /* No data were appended, let's drop the output buffer and
692 * keep the input buffer unchanged.
693 */
694 return 0;
695 }
696
697 /* OK so at this stage, we have an output buffer <ob> looking like this :
698 *
699 * <-- o --> <------ i ----->
700 * +---------+---+------------+-----------+
701 * | out | c | comp_in | empty |
702 * +---------+---+------------+-----------+
703 * data p size
704 *
705 * <out> is the room reserved to copy ib->o. It starts at ob->data and
706 * has not yet been filled. <c> is the room reserved to write the chunk
707 * size (10 bytes). <comp_in> is the compressed equivalent of the data
708 * part of ib->i. <empty> is the amount of empty bytes at the end of
709 * the buffer, into which we may have to copy the remaining bytes from
710 * ib->i after the data (chunk size, trailers, ...).
711 */
712
713 /* Write real size at the begining of the chunk, no need of wrapping.
714 * We write the chunk using a dynamic length and adjust ob->p and ob->i
715 * accordingly afterwards. That will move <out> away from <data>.
716 */
717 left = 10 - http_emit_chunk_size(ob->p + 10, ob->i - 10);
718 ob->p += left;
719 ob->i -= left;
720
721 /* Copy previous data from ib->o into ob->o */
722 if (ib->o > 0) {
723 left = bo_contig_data(ib);
724 memcpy(ob->p - ob->o, bo_ptr(ib), left);
725 if (ib->o - left) /* second part of the buffer */
726 memcpy(ob->p - ob->o + left, ib->data, ib->o - left);
727 }
728
729 /* chunked encoding requires CRLF after data */
730 tail = ob->p + ob->i;
731 *tail++ = '\r';
732 *tail++ = '\n';
733
Christopher Faulet2fb28802015-12-01 10:40:57 +0100734 /* At the end of data, we must write the empty chunk 0<CRLF>,
735 * and terminate the trailers section with a last <CRLF>. If
736 * we're forwarding a chunked-encoded response, we'll have a
737 * trailers section after the empty chunk which needs to be
738 * forwarded and which will provide the last CRLF. Otherwise
739 * we write it ourselves.
740 */
741 if (end) {
742 struct http_msg *msg = &s->txn->rsp;
743
744 memcpy(tail, "0\r\n", 3);
745 tail += 3;
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100746 if (!(msg->flags & HTTP_MSGF_TE_CHNK)) {
Christopher Faulet2fb28802015-12-01 10:40:57 +0100747 memcpy(tail, "\r\n", 2);
748 tail += 2;
749 }
750 }
751
Christopher Faulet3d97c902015-12-09 14:59:38 +0100752 ob->i = tail - ob->p;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100753 to_forward = ob->i;
754
755 /* update input rate */
Christopher Faulet92d36382015-11-05 13:35:03 +0100756 if (st->comp_ctx && st->comp_ctx->cur_lvl > 0) {
Christopher Faulet2fb28802015-12-01 10:40:57 +0100757 update_freq_ctr(&global.comp_bps_in, st->consumed);
Christopher Fauletff8abcd2017-06-02 15:33:24 +0200758 HA_ATOMIC_ADD(&strm_fe(s)->fe_counters.comp_in, st->consumed);
759 HA_ATOMIC_ADD(&s->be->be_counters.comp_in, st->consumed);
Christopher Faulet3d97c902015-12-09 14:59:38 +0100760 } else {
Christopher Fauletff8abcd2017-06-02 15:33:24 +0200761 HA_ATOMIC_ADD(&strm_fe(s)->fe_counters.comp_byp, st->consumed);
762 HA_ATOMIC_ADD(&s->be->be_counters.comp_byp, st->consumed);
Christopher Faulet3d97c902015-12-09 14:59:38 +0100763 }
764
765 /* copy the remaining data in the tmp buffer. */
Willy Tarreaubcbd3932018-06-06 07:13:22 +0200766 c_adv(chn, st->consumed);
Christopher Faulet3d97c902015-12-09 14:59:38 +0100767 if (ib->i > 0) {
Willy Tarreau7194d3c2018-06-06 16:55:45 +0200768 left = ci_contig_data(chn);
Willy Tarreaudda2e412018-06-07 18:08:04 +0200769 memcpy(ob->p + ob->i, ci_head(chn), left);
Christopher Faulet3d97c902015-12-09 14:59:38 +0100770 ob->i += left;
771 if (ib->i - left) {
772 memcpy(ob->p + ob->i, ib->data, ib->i - left);
773 ob->i += ib->i - left;
774 }
775 }
776
777 /* swap the buffers */
Willy Tarreau4d452382018-06-06 07:15:47 +0200778 chn->buf = ob;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100779 *out = ib;
780
Christopher Faulet92d36382015-11-05 13:35:03 +0100781
782 if (st->comp_ctx && st->comp_ctx->cur_lvl > 0) {
Christopher Faulet3d97c902015-12-09 14:59:38 +0100783 update_freq_ctr(&global.comp_bps_out, to_forward);
Christopher Fauletff8abcd2017-06-02 15:33:24 +0200784 HA_ATOMIC_ADD(&strm_fe(s)->fe_counters.comp_out, to_forward);
785 HA_ATOMIC_ADD(&s->be->be_counters.comp_out, to_forward);
Christopher Faulet3d97c902015-12-09 14:59:38 +0100786 }
787
Christopher Faulet3d97c902015-12-09 14:59:38 +0100788 return to_forward;
789}
790
791
792/***********************************************************************/
Christopher Faulet92d36382015-11-05 13:35:03 +0100793struct flt_ops comp_ops = {
Christopher Faulet8ca3b4b2017-07-25 11:07:15 +0200794 .init_per_thread = comp_flt_init_per_thread,
795 .deinit_per_thread = comp_flt_deinit_per_thread,
Christopher Faulet92d36382015-11-05 13:35:03 +0100796
797 .channel_start_analyze = comp_start_analyze,
Christopher Faulet92d36382015-11-05 13:35:03 +0100798 .channel_end_analyze = comp_end_analyze,
Christopher Faulet3dc860d2017-09-15 11:39:36 +0200799 .channel_post_analyze = comp_http_post_analyze,
Christopher Faulet92d36382015-11-05 13:35:03 +0100800
Christopher Faulet1339d742016-05-11 16:48:33 +0200801 .http_headers = comp_http_headers,
Christopher Faulet309c6412015-12-02 09:57:32 +0100802 .http_data = comp_http_data,
803 .http_chunk_trailers = comp_http_chunk_trailers,
804 .http_forward_data = comp_http_forward_data,
Christopher Fauletd60b3cf2017-06-26 11:47:13 +0200805 .http_end = comp_http_end,
Christopher Faulet92d36382015-11-05 13:35:03 +0100806};
807
Christopher Faulet3d97c902015-12-09 14:59:38 +0100808static int
809parse_compression_options(char **args, int section, struct proxy *proxy,
810 struct proxy *defpx, const char *file, int line,
811 char **err)
812{
Christopher Faulet92d36382015-11-05 13:35:03 +0100813 struct comp *comp;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100814
815 if (proxy->comp == NULL) {
Vincent Bernat02779b62016-04-03 13:48:43 +0200816 comp = calloc(1, sizeof(*comp));
Christopher Faulet3d97c902015-12-09 14:59:38 +0100817 proxy->comp = comp;
818 }
819 else
820 comp = proxy->comp;
821
822 if (!strcmp(args[1], "algo")) {
823 struct comp_ctx *ctx;
824 int cur_arg = 2;
825
826 if (!*args[cur_arg]) {
827 memprintf(err, "parsing [%s:%d] : '%s' expects <algorithm>\n",
828 file, line, args[0]);
829 return -1;
830 }
831 while (*(args[cur_arg])) {
832 if (comp_append_algo(comp, args[cur_arg]) < 0) {
833 memprintf(err, "'%s' : '%s' is not a supported algorithm.\n",
834 args[0], args[cur_arg]);
835 return -1;
836 }
837 if (proxy->comp->algos->init(&ctx, 9) == 0)
838 proxy->comp->algos->end(&ctx);
839 else {
840 memprintf(err, "'%s' : Can't init '%s' algorithm.\n",
841 args[0], args[cur_arg]);
842 return -1;
843 }
844 cur_arg++;
845 continue;
846 }
847 }
848 else if (!strcmp(args[1], "offload"))
849 comp->offload = 1;
850 else if (!strcmp(args[1], "type")) {
851 int cur_arg = 2;
852
853 if (!*args[cur_arg]) {
854 memprintf(err, "'%s' expects <type>\n", args[0]);
855 return -1;
856 }
857 while (*(args[cur_arg])) {
858 comp_append_type(comp, args[cur_arg]);
859 cur_arg++;
860 continue;
861 }
862 }
863 else {
864 memprintf(err, "'%s' expects 'algo', 'type' or 'offload'\n",
865 args[0]);
866 return -1;
867 }
868
869 return 0;
870}
871
Christopher Faulet92d36382015-11-05 13:35:03 +0100872static int
873parse_http_comp_flt(char **args, int *cur_arg, struct proxy *px,
Thierry Fournier3610c392016-04-13 18:27:51 +0200874 struct flt_conf *fconf, char **err, void *private)
Christopher Faulet92d36382015-11-05 13:35:03 +0100875{
Christopher Faulet443ea1a2016-02-04 13:40:26 +0100876 struct flt_conf *fc, *back;
Christopher Faulet92d36382015-11-05 13:35:03 +0100877
Christopher Faulet443ea1a2016-02-04 13:40:26 +0100878 list_for_each_entry_safe(fc, back, &px->filter_configs, list) {
879 if (fc->id == http_comp_flt_id) {
Christopher Faulet92d36382015-11-05 13:35:03 +0100880 memprintf(err, "%s: Proxy supports only one compression filter\n", px->id);
881 return -1;
882 }
883 }
884
Christopher Faulet443ea1a2016-02-04 13:40:26 +0100885 fconf->id = http_comp_flt_id;
886 fconf->conf = NULL;
887 fconf->ops = &comp_ops;
Christopher Faulet92d36382015-11-05 13:35:03 +0100888 (*cur_arg)++;
889
890 return 0;
891}
892
893
894int
895check_legacy_http_comp_flt(struct proxy *proxy)
896{
Christopher Faulet443ea1a2016-02-04 13:40:26 +0100897 struct flt_conf *fconf;
Christopher Faulet92d36382015-11-05 13:35:03 +0100898 int err = 0;
899
900 if (proxy->comp == NULL)
901 goto end;
Christopher Faulet443ea1a2016-02-04 13:40:26 +0100902 if (!LIST_ISEMPTY(&proxy->filter_configs)) {
903 list_for_each_entry(fconf, &proxy->filter_configs, list) {
904 if (fconf->id == http_comp_flt_id)
Christopher Faulet92d36382015-11-05 13:35:03 +0100905 goto end;
906 }
Christopher Faulet767a84b2017-11-24 16:50:31 +0100907 ha_alert("config: %s '%s': require an explicit filter declaration to use HTTP compression\n",
908 proxy_type_str(proxy), proxy->id);
Christopher Faulet92d36382015-11-05 13:35:03 +0100909 err++;
910 goto end;
911 }
912
Christopher Faulet443ea1a2016-02-04 13:40:26 +0100913 fconf = calloc(1, sizeof(*fconf));
914 if (!fconf) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100915 ha_alert("config: %s '%s': out of memory\n",
916 proxy_type_str(proxy), proxy->id);
Christopher Faulet92d36382015-11-05 13:35:03 +0100917 err++;
918 goto end;
919 }
Christopher Faulet443ea1a2016-02-04 13:40:26 +0100920 fconf->id = http_comp_flt_id;
921 fconf->conf = NULL;
922 fconf->ops = &comp_ops;
923 LIST_ADDQ(&proxy->filter_configs, &fconf->list);
Christopher Faulet92d36382015-11-05 13:35:03 +0100924
925 end:
926 return err;
927}
928
929/*
930 * boolean, returns true if compression is used (either gzip or deflate) in the
931 * response.
932 */
Christopher Faulet3d97c902015-12-09 14:59:38 +0100933static int
Christopher Faulet92d36382015-11-05 13:35:03 +0100934smp_fetch_res_comp(const struct arg *args, struct sample *smp, const char *kw,
935 void *private)
Christopher Faulet3d97c902015-12-09 14:59:38 +0100936{
Willy Tarreaube508f12016-03-10 11:47:01 +0100937 struct http_txn *txn = smp->strm ? smp->strm->txn : NULL;
Christopher Faulet92d36382015-11-05 13:35:03 +0100938
Christopher Faulet3d97c902015-12-09 14:59:38 +0100939 smp->data.type = SMP_T_BOOL;
Christopher Faulet92d36382015-11-05 13:35:03 +0100940 smp->data.u.sint = (txn && (txn->rsp.flags & HTTP_MSGF_COMPRESSING));
Christopher Faulet3d97c902015-12-09 14:59:38 +0100941 return 1;
942}
943
Christopher Faulet92d36382015-11-05 13:35:03 +0100944/*
945 * string, returns algo
946 */
Christopher Faulet3d97c902015-12-09 14:59:38 +0100947static int
Christopher Faulet92d36382015-11-05 13:35:03 +0100948smp_fetch_res_comp_algo(const struct arg *args, struct sample *smp,
949 const char *kw, void *private)
Christopher Faulet3d97c902015-12-09 14:59:38 +0100950{
Willy Tarreaube508f12016-03-10 11:47:01 +0100951 struct http_txn *txn = smp->strm ? smp->strm->txn : NULL;
Christopher Faulet92d36382015-11-05 13:35:03 +0100952 struct filter *filter;
953 struct comp_state *st;
954
Christopher Faulet03d85532017-09-15 10:14:43 +0200955 if (!txn || !(txn->rsp.flags & HTTP_MSGF_COMPRESSING))
Christopher Faulet3d97c902015-12-09 14:59:38 +0100956 return 0;
957
Christopher Fauletfcf035c2015-12-03 11:48:03 +0100958 list_for_each_entry(filter, &strm_flt(smp->strm)->filters, list) {
Christopher Faulet443ea1a2016-02-04 13:40:26 +0100959 if (FLT_ID(filter) != http_comp_flt_id)
Christopher Faulet92d36382015-11-05 13:35:03 +0100960 continue;
961
962 if (!(st = filter->ctx))
963 break;
964
965 smp->data.type = SMP_T_STR;
966 smp->flags = SMP_F_CONST;
967 smp->data.u.str.str = st->comp_algo->cfg_name;
968 smp->data.u.str.len = st->comp_algo->cfg_name_len;
969 return 1;
970 }
971 return 0;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100972}
973
974/* Declare the config parser for "compression" keyword */
975static struct cfg_kw_list cfg_kws = {ILH, {
976 { CFG_LISTEN, "compression", parse_compression_options },
977 { 0, NULL, NULL },
978 }
979};
980
Christopher Faulet92d36382015-11-05 13:35:03 +0100981/* Declare the filter parser for "compression" keyword */
982static struct flt_kw_list filter_kws = { "COMP", { }, {
Thierry Fournier3610c392016-04-13 18:27:51 +0200983 { "compression", parse_http_comp_flt, NULL },
984 { NULL, NULL, NULL },
Christopher Faulet92d36382015-11-05 13:35:03 +0100985 }
986};
987
Christopher Faulet3d97c902015-12-09 14:59:38 +0100988/* Note: must not be declared <const> as its list will be overwritten */
989static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
Christopher Faulet92d36382015-11-05 13:35:03 +0100990 { "res.comp", smp_fetch_res_comp, 0, NULL, SMP_T_BOOL, SMP_USE_HRSHP },
991 { "res.comp_algo", smp_fetch_res_comp_algo, 0, NULL, SMP_T_STR, SMP_USE_HRSHP },
992 { /* END */ },
993 }
994};
Christopher Faulet3d97c902015-12-09 14:59:38 +0100995
996__attribute__((constructor))
Christopher Faulet92d36382015-11-05 13:35:03 +0100997static void
998__flt_http_comp_init(void)
Christopher Faulet3d97c902015-12-09 14:59:38 +0100999{
1000 cfg_register_keywords(&cfg_kws);
Christopher Faulet92d36382015-11-05 13:35:03 +01001001 flt_register_keywords(&filter_kws);
Christopher Faulet3d97c902015-12-09 14:59:38 +01001002 sample_register_fetches(&sample_fetch_keywords);
Willy Tarreaubafbe012017-11-24 17:34:44 +01001003 pool_head_comp_state = create_pool("comp_state", sizeof(struct comp_state), MEM_F_SHARED);
Christopher Faulet3d97c902015-12-09 14:59:38 +01001004}