blob: 017080961e9cf7b1d6ac3f17a1e9fafc80e234b5 [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>
Willy Tarreaub96b77e2018-12-11 10:22:41 +010015#include <common/htx.h>
Willy Tarreau0108d902018-11-25 19:14:37 +010016#include <common/initcall.h>
Christopher Faulet3d97c902015-12-09 14:59:38 +010017#include <common/mini-clist.h>
18#include <common/standard.h>
19
20#include <types/compression.h>
21#include <types/filters.h>
Christopher Faulet3d97c902015-12-09 14:59:38 +010022#include <types/proxy.h>
23#include <types/sample.h>
24
25#include <proto/compression.h>
Christopher Faulet92d36382015-11-05 13:35:03 +010026#include <proto/filters.h>
Christopher Faulet3d97c902015-12-09 14:59:38 +010027#include <proto/hdr_idx.h>
Christopher Faulete6902cd2018-11-30 22:29:48 +010028#include <proto/http_htx.h>
Christopher Faulet3d97c902015-12-09 14:59:38 +010029#include <proto/proto_http.h>
30#include <proto/sample.h>
31#include <proto/stream.h>
32
Christopher Fauletf4a4ef72018-12-07 17:39:53 +010033const char *http_comp_flt_id = "compression filter";
Christopher Faulet92d36382015-11-05 13:35:03 +010034
35struct flt_ops comp_ops;
36
Christopher Faulet92d36382015-11-05 13:35:03 +010037struct comp_state {
38 struct comp_ctx *comp_ctx; /* compression context */
39 struct comp_algo *comp_algo; /* compression algorithm if not NULL */
Christopher Faulete6902cd2018-11-30 22:29:48 +010040
41 /* Following fields are used by the legacy code only: */
Christopher Fauletb77c5c22015-12-07 16:48:42 +010042 int hdrs_len;
43 int tlrs_len;
Christopher Faulet2fb28802015-12-01 10:40:57 +010044 int consumed;
45 int initialized;
Christopher Fauletb77c5c22015-12-07 16:48:42 +010046 int finished;
Christopher Faulet92d36382015-11-05 13:35:03 +010047};
48
Willy Tarreau8ceae722018-11-26 11:58:30 +010049/* Pools used to allocate comp_state structs */
50DECLARE_STATIC_POOL(pool_head_comp_state, "comp_state", sizeof(struct comp_state));
51
52static THREAD_LOCAL struct buffer tmpbuf;
53static THREAD_LOCAL struct buffer zbuf;
54static THREAD_LOCAL unsigned int buf_output;
55
Christopher Faulet92d36382015-11-05 13:35:03 +010056static int select_compression_request_header(struct comp_state *st,
57 struct stream *s,
58 struct http_msg *msg);
59static int select_compression_response_header(struct comp_state *st,
60 struct stream *s,
61 struct http_msg *msg);
Christopher Faulet27d93c32018-12-15 22:32:02 +010062static int set_compression_response_header(struct comp_state *st,
63 struct stream *s,
64 struct http_msg *msg);
Christopher Faulet92d36382015-11-05 13:35:03 +010065
Christopher Faulete6902cd2018-11-30 22:29:48 +010066static int htx_compression_buffer_init(struct htx *htx, struct buffer *out);
67static int htx_compression_buffer_add_data(struct comp_state *st, const char *data, size_t len,
68 struct buffer *out);
69static int htx_compression_buffer_end(struct comp_state *st, struct buffer *out, int end);
70
Willy Tarreaud54a8ce2018-06-29 18:42:02 +020071static int http_compression_buffer_init(struct channel *inc, struct buffer *out, unsigned int *out_len);
Christopher Faulet92d36382015-11-05 13:35:03 +010072static int http_compression_buffer_add_data(struct comp_state *st,
73 struct buffer *in,
Willy Tarreaud54a8ce2018-06-29 18:42:02 +020074 int in_out,
Christopher Faulet92d36382015-11-05 13:35:03 +010075 struct buffer *out, int sz);
76static int http_compression_buffer_end(struct comp_state *st, struct stream *s,
Willy Tarreauc9fa0482018-07-10 17:43:27 +020077 struct channel *chn, struct buffer *out,
Willy Tarreaud54a8ce2018-06-29 18:42:02 +020078 unsigned int *out_len, int end);
Christopher Faulet92d36382015-11-05 13:35:03 +010079
80/***********************************************************************/
81static int
Christopher Faulete6902cd2018-11-30 22:29:48 +010082comp_flt_init(struct proxy *px, struct flt_conf *fconf)
83{
Christopher Faulet6e540952018-12-03 22:43:41 +010084 fconf->flags |= FLT_CFG_FL_HTX;
Christopher Faulete6902cd2018-11-30 22:29:48 +010085 return 0;
86}
87
88static int
Christopher Faulet8ca3b4b2017-07-25 11:07:15 +020089comp_flt_init_per_thread(struct proxy *px, struct flt_conf *fconf)
Christopher Faulet92d36382015-11-05 13:35:03 +010090{
Willy Tarreauc9fa0482018-07-10 17:43:27 +020091 if (!tmpbuf.size && b_alloc(&tmpbuf) == NULL)
Christopher Fauletb77c5c22015-12-07 16:48:42 +010092 return -1;
Willy Tarreauc9fa0482018-07-10 17:43:27 +020093 if (!zbuf.size && b_alloc(&zbuf) == NULL)
Christopher Fauletb77c5c22015-12-07 16:48:42 +010094 return -1;
Christopher Faulet92d36382015-11-05 13:35:03 +010095 return 0;
96}
97
98static void
Christopher Faulet8ca3b4b2017-07-25 11:07:15 +020099comp_flt_deinit_per_thread(struct proxy *px, struct flt_conf *fconf)
Christopher Faulet92d36382015-11-05 13:35:03 +0100100{
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200101 if (tmpbuf.size)
Christopher Faulet92d36382015-11-05 13:35:03 +0100102 b_free(&tmpbuf);
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200103 if (zbuf.size)
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100104 b_free(&zbuf);
Christopher Faulet92d36382015-11-05 13:35:03 +0100105}
106
107static int
108comp_start_analyze(struct stream *s, struct filter *filter, struct channel *chn)
109{
Christopher Faulet8ca3b4b2017-07-25 11:07:15 +0200110
Christopher Faulet92d36382015-11-05 13:35:03 +0100111 if (filter->ctx == NULL) {
112 struct comp_state *st;
113
Willy Tarreaubafbe012017-11-24 17:34:44 +0100114 st = pool_alloc_dirty(pool_head_comp_state);
Christopher Fauleta03d4ad2017-06-26 16:53:33 +0200115 if (st == NULL)
Christopher Faulet92d36382015-11-05 13:35:03 +0100116 return -1;
117
Christopher Faulet2fb28802015-12-01 10:40:57 +0100118 st->comp_algo = NULL;
119 st->comp_ctx = NULL;
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100120 st->hdrs_len = 0;
121 st->tlrs_len = 0;
Christopher Faulet2fb28802015-12-01 10:40:57 +0100122 st->consumed = 0;
123 st->initialized = 0;
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100124 st->finished = 0;
Christopher Faulet2fb28802015-12-01 10:40:57 +0100125 filter->ctx = st;
Christopher Faulet3dc860d2017-09-15 11:39:36 +0200126
127 /* Register post-analyzer on AN_RES_WAIT_HTTP because we need to
128 * analyze response headers before http-response rules execution
129 * to be sure we can use res.comp and res.comp_algo sample
130 * fetches */
131 filter->post_analyzers |= AN_RES_WAIT_HTTP;
Christopher Faulet92d36382015-11-05 13:35:03 +0100132 }
133 return 1;
134}
135
136static int
Christopher Faulet92d36382015-11-05 13:35:03 +0100137comp_end_analyze(struct stream *s, struct filter *filter, struct channel *chn)
138{
139 struct comp_state *st = filter->ctx;
Christopher Faulet92d36382015-11-05 13:35:03 +0100140
Christopher Fauletd60b3cf2017-06-26 11:47:13 +0200141 if (!st)
Christopher Faulet92d36382015-11-05 13:35:03 +0100142 goto end;
143
Christopher Faulet92d36382015-11-05 13:35:03 +0100144 /* release any possible compression context */
Christopher Fauletd60b3cf2017-06-26 11:47:13 +0200145 if (st->comp_algo)
146 st->comp_algo->end(&st->comp_ctx);
Willy Tarreaubafbe012017-11-24 17:34:44 +0100147 pool_free(pool_head_comp_state, st);
Christopher Faulet92d36382015-11-05 13:35:03 +0100148 filter->ctx = NULL;
149 end:
150 return 1;
151}
152
153static int
Christopher Faulet1339d742016-05-11 16:48:33 +0200154comp_http_headers(struct stream *s, struct filter *filter, struct http_msg *msg)
155{
156 struct comp_state *st = filter->ctx;
157
158 if (!strm_fe(s)->comp && !s->be->comp)
159 goto end;
160
161 if (!(msg->chn->flags & CF_ISRESP))
162 select_compression_request_header(st, s, msg);
163 else {
Christopher Faulet3dc860d2017-09-15 11:39:36 +0200164 /* Response headers have already been checked in
165 * comp_http_post_analyze callback. */
Christopher Faulet1339d742016-05-11 16:48:33 +0200166 if (st->comp_algo) {
Christopher Faulet27d93c32018-12-15 22:32:02 +0100167 if (!set_compression_response_header(st, s, msg))
168 goto end;
Christopher Faulet1339d742016-05-11 16:48:33 +0200169 register_data_filter(s, msg->chn, filter);
Christopher Faulete6902cd2018-11-30 22:29:48 +0100170 if (!IS_HTX_STRM(s))
171 st->hdrs_len = s->txn->rsp.sov;
Christopher Faulet1339d742016-05-11 16:48:33 +0200172 }
173 }
174
175 end:
176 return 1;
177}
178
179static int
Christopher Faulet3dc860d2017-09-15 11:39:36 +0200180comp_http_post_analyze(struct stream *s, struct filter *filter,
181 struct channel *chn, unsigned an_bit)
182{
183 struct http_txn *txn = s->txn;
184 struct http_msg *msg = &txn->rsp;
185 struct comp_state *st = filter->ctx;
186
187 if (an_bit != AN_RES_WAIT_HTTP)
188 goto end;
189
190 if (!strm_fe(s)->comp && !s->be->comp)
191 goto end;
192
193 select_compression_response_header(st, s, msg);
194
195 end:
196 return 1;
197}
198
199static int
Christopher Faulete6902cd2018-11-30 22:29:48 +0100200comp_http_payload(struct stream *s, struct filter *filter, struct http_msg *msg,
201 unsigned int offset, unsigned int len)
202{
203 struct comp_state *st = filter->ctx;
Christopher Faulet27ba2dc2018-12-05 11:53:24 +0100204 struct htx *htx = htxbuf(&msg->chn->buf);
Christopher Faulete6902cd2018-11-30 22:29:48 +0100205 struct htx_blk *blk;
206 struct htx_ret htx_ret;
207 int ret, consumed = 0, to_forward = 0;
208
209 htx_ret = htx_find_blk(htx, offset);
210 blk = htx_ret.blk;
211 offset = htx_ret.ret;
212
213 while (blk && len) {
214 enum htx_blk_type type = htx_get_blk_type(blk);
215 uint32_t sz = htx_get_blksz(blk);
216 struct ist v;
217
218 switch (type) {
219 case HTX_BLK_UNUSED:
220 break;
221
222 case HTX_BLK_DATA:
223 v = htx_get_blk_value(htx, blk);
224 v.ptr += offset;
225 v.len -= offset;
226 if (v.len > len)
227 v.len = len;
228 if (htx_compression_buffer_init(htx, &trash) < 0) {
229 msg->chn->flags |= CF_WAKE_WRITE;
230 goto end;
231 }
232 ret = htx_compression_buffer_add_data(st, v.ptr, v.len, &trash);
233 if (ret < 0)
234 goto error;
235 if (htx_compression_buffer_end(st, &trash, 0) < 0)
236 goto error;
237 len -= ret;
238 consumed += ret;
239 to_forward += b_data(&trash);
240 if (ret == sz && !b_data(&trash)) {
241 offset = 0;
242 blk = htx_remove_blk(htx, blk);
243 continue;
244 }
245 v.len = ret;
246 blk = htx_replace_blk_value(htx, blk, v, ist2(b_head(&trash), b_data(&trash)));
247 break;
248
249 case HTX_BLK_EOD:
250 case HTX_BLK_TLR:
251 case HTX_BLK_EOM:
252 if (msg->flags & HTTP_MSGF_COMPRESSING) {
253 if (htx_compression_buffer_init(htx, &trash) < 0) {
254 msg->chn->flags |= CF_WAKE_WRITE;
255 goto end;
256 }
257 if (htx_compression_buffer_end(st, &trash, 1) < 0)
258 goto error;
259 blk = htx_add_data_before(htx, blk, ist2(b_head(&trash), b_data(&trash)));
260 if (!blk)
261 goto error;
262 to_forward += b_data(&trash);
263 msg->flags &= ~HTTP_MSGF_COMPRESSING;
264 /* We let the mux add last empty chunk and empty trailers */
265 }
266 /* fall through */
267
268 default:
269 sz -= offset;
270 if (sz > len)
271 sz = len;
272 consumed += sz;
273 to_forward += sz;
274 len -= sz;
275 break;
276 }
277
278 offset = 0;
279 blk = htx_get_next_blk(htx, blk);
280 }
281
282 end:
283 if (to_forward != consumed)
284 flt_update_offsets(filter, msg->chn, to_forward - consumed);
285
286 if (st->comp_ctx && st->comp_ctx->cur_lvl > 0) {
287 update_freq_ctr(&global.comp_bps_out, to_forward);
288 HA_ATOMIC_ADD(&strm_fe(s)->fe_counters.comp_out, to_forward);
289 HA_ATOMIC_ADD(&s->be->be_counters.comp_out, to_forward);
290 }
291 return to_forward;
292
293 error:
294 return -1;
295}
296
297static int
Christopher Faulet2fb28802015-12-01 10:40:57 +0100298comp_http_data(struct stream *s, struct filter *filter, struct http_msg *msg)
Christopher Faulet92d36382015-11-05 13:35:03 +0100299{
300 struct comp_state *st = filter->ctx;
Willy Tarreaubcbd3932018-06-06 07:13:22 +0200301 struct channel *chn = msg->chn;
Christopher Faulet3e7bc672015-12-07 13:39:08 +0100302 unsigned int *nxt = &flt_rsp_nxt(filter);
Christopher Faulet2fb28802015-12-01 10:40:57 +0100303 unsigned int len;
Christopher Faulet92d36382015-11-05 13:35:03 +0100304 int ret;
305
Olivier Houchard0b662842018-06-29 18:16:31 +0200306 len = MIN(msg->chunk_len + msg->next, ci_data(chn)) - *nxt;
Christopher Faulet2fb28802015-12-01 10:40:57 +0100307 if (!len)
308 return len;
309
310 if (!st->initialized) {
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100311 unsigned int fwd = flt_rsp_fwd(filter) + st->hdrs_len;
Christopher Faulet3e7bc672015-12-07 13:39:08 +0100312
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200313 b_reset(&tmpbuf);
Willy Tarreaubcbd3932018-06-06 07:13:22 +0200314 c_adv(chn, fwd);
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200315 ret = http_compression_buffer_init(chn, &zbuf, &buf_output);
Willy Tarreaubcbd3932018-06-06 07:13:22 +0200316 c_rew(chn, fwd);
Christopher Faulet2fb28802015-12-01 10:40:57 +0100317 if (ret < 0) {
318 msg->chn->flags |= CF_WAKE_WRITE;
319 return 0;
320 }
321 }
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100322
323 if (msg->flags & HTTP_MSGF_TE_CHNK) {
Christopher Faulet06ecf3a2016-09-22 15:31:43 +0200324 int block;
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100325
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200326 len = MIN(b_room(&tmpbuf), len);
Christopher Faulet06ecf3a2016-09-22 15:31:43 +0200327
Willy Tarreaubcbd3932018-06-06 07:13:22 +0200328 c_adv(chn, *nxt);
Willy Tarreau7194d3c2018-06-06 16:55:45 +0200329 block = ci_contig_data(chn);
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200330 memcpy(b_tail(&tmpbuf), ci_head(chn), block);
Christopher Faulet06ecf3a2016-09-22 15:31:43 +0200331 if (len > block)
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200332 memcpy(b_tail(&tmpbuf)+block, b_orig(&chn->buf), len-block);
Willy Tarreaubcbd3932018-06-06 07:13:22 +0200333 c_rew(chn, *nxt);
Christopher Faulet06ecf3a2016-09-22 15:31:43 +0200334
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200335 b_add(&tmpbuf, len);
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100336 ret = len;
337 }
338 else {
Willy Tarreaubcbd3932018-06-06 07:13:22 +0200339 c_adv(chn, *nxt);
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200340 ret = http_compression_buffer_add_data(st, &chn->buf, co_data(chn), &zbuf, len);
Willy Tarreaubcbd3932018-06-06 07:13:22 +0200341 c_rew(chn, *nxt);
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100342 if (ret < 0)
343 return ret;
344 }
Christopher Faulet92d36382015-11-05 13:35:03 +0100345
Christopher Faulet2fb28802015-12-01 10:40:57 +0100346 st->initialized = 1;
347 msg->next += ret;
348 msg->chunk_len -= ret;
Christopher Faulet3e7bc672015-12-07 13:39:08 +0100349 *nxt = msg->next;
Christopher Faulet2fb28802015-12-01 10:40:57 +0100350 return 0;
Christopher Faulet92d36382015-11-05 13:35:03 +0100351}
352
353static int
Christopher Faulet2fb28802015-12-01 10:40:57 +0100354comp_http_chunk_trailers(struct stream *s, struct filter *filter,
355 struct http_msg *msg)
Christopher Faulet92d36382015-11-05 13:35:03 +0100356{
357 struct comp_state *st = filter->ctx;
Christopher Faulet92d36382015-11-05 13:35:03 +0100358
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100359 if (!st->initialized) {
360 if (!st->finished) {
Willy Tarreaubcbd3932018-06-06 07:13:22 +0200361 struct channel *chn = msg->chn;
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100362 unsigned int fwd = flt_rsp_fwd(filter) + st->hdrs_len;
Christopher Faulet2fb28802015-12-01 10:40:57 +0100363
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200364 b_reset(&tmpbuf);
Willy Tarreaubcbd3932018-06-06 07:13:22 +0200365 c_adv(chn, fwd);
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200366 http_compression_buffer_init(chn, &zbuf, &buf_output);
Willy Tarreaubcbd3932018-06-06 07:13:22 +0200367 c_rew(chn, fwd);
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100368 st->initialized = 1;
369 }
370 }
371 st->tlrs_len = msg->sol;
Christopher Faulet2fb28802015-12-01 10:40:57 +0100372 return 1;
Christopher Faulet92d36382015-11-05 13:35:03 +0100373}
374
Christopher Faulet2fb28802015-12-01 10:40:57 +0100375
Christopher Faulet92d36382015-11-05 13:35:03 +0100376static int
377comp_http_forward_data(struct stream *s, struct filter *filter,
378 struct http_msg *msg, unsigned int len)
379{
380 struct comp_state *st = filter->ctx;
Christopher Faulet2fb28802015-12-01 10:40:57 +0100381 int ret;
Christopher Faulet92d36382015-11-05 13:35:03 +0100382
Christopher Faulet2fb28802015-12-01 10:40:57 +0100383 /* To work, previous filters MUST forward all data */
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100384 if (flt_rsp_fwd(filter) + len != flt_rsp_nxt(filter)) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100385 ha_warning("HTTP compression failed: unexpected behavior of previous filters\n");
Christopher Faulet2fb28802015-12-01 10:40:57 +0100386 return -1;
Christopher Faulet92d36382015-11-05 13:35:03 +0100387 }
388
Christopher Faulet2fb28802015-12-01 10:40:57 +0100389 if (!st->initialized) {
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100390 if (!len) {
Joseph Herlant942eea32018-11-15 13:57:22 -0800391 /* Nothing to forward */
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100392 ret = len;
393 }
394 else if (st->hdrs_len > len) {
395 /* Forward part of headers */
396 ret = len;
397 st->hdrs_len -= len;
398 }
399 else if (st->hdrs_len > 0) {
400 /* Forward remaining headers */
401 ret = st->hdrs_len;
402 st->hdrs_len = 0;
403 }
404 else if (msg->msg_state < HTTP_MSG_TRAILERS) {
405 /* Do not forward anything for now. This only happens
406 * with chunk-encoded responses. Waiting data are part
407 * of the chunk envelope (the chunk size or the chunk
408 * CRLF). These data will be skipped during the
409 * compression. */
410 ret = 0;
411 }
412 else {
413 /* Forward trailers data */
414 ret = len;
415 }
Christopher Faulet2fb28802015-12-01 10:40:57 +0100416 return ret;
Christopher Faulet92d36382015-11-05 13:35:03 +0100417 }
418
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100419 if (msg->flags & HTTP_MSGF_TE_CHNK) {
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200420 ret = http_compression_buffer_add_data(st, &tmpbuf, 0,
421 &zbuf, b_data(&tmpbuf));
422 if (ret != b_data(&tmpbuf)) {
Willy Tarreau506a29a2018-07-18 10:07:58 +0200423 ha_warning("HTTP compression failed: Must consume %u bytes but only %d bytes consumed\n",
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200424 (unsigned int)b_data(&tmpbuf), ret);
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100425 return -1;
426 }
427 }
428
429 st->consumed = len - st->hdrs_len - st->tlrs_len;
Willy Tarreaubcbd3932018-06-06 07:13:22 +0200430 c_adv(msg->chn, flt_rsp_fwd(filter) + st->hdrs_len);
Willy Tarreaud54a8ce2018-06-29 18:42:02 +0200431 ret = http_compression_buffer_end(st, s, msg->chn, &zbuf, &buf_output, msg->msg_state >= HTTP_MSG_TRAILERS);
Willy Tarreaubcbd3932018-06-06 07:13:22 +0200432 c_rew(msg->chn, flt_rsp_fwd(filter) + st->hdrs_len);
Christopher Faulet2fb28802015-12-01 10:40:57 +0100433 if (ret < 0)
434 return ret;
Christopher Faulet92d36382015-11-05 13:35:03 +0100435
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100436 flt_change_forward_size(filter, msg->chn, ret - st->consumed);
437 msg->next += (ret - st->consumed);
438 ret += st->hdrs_len + st->tlrs_len;
439
Christopher Faulet2fb28802015-12-01 10:40:57 +0100440 st->initialized = 0;
Christopher Fauletb77c5c22015-12-07 16:48:42 +0100441 st->finished = (msg->msg_state >= HTTP_MSG_TRAILERS);
442 st->hdrs_len = 0;
443 st->tlrs_len = 0;
Christopher Faulet92d36382015-11-05 13:35:03 +0100444 return ret;
445}
Christopher Faulet3d97c902015-12-09 14:59:38 +0100446
Christopher Fauletd60b3cf2017-06-26 11:47:13 +0200447static int
448comp_http_end(struct stream *s, struct filter *filter,
449 struct http_msg *msg)
450{
451 struct comp_state *st = filter->ctx;
452
453 if (!(msg->chn->flags & CF_ISRESP) || !st || !st->comp_algo)
454 goto end;
455
456 if (strm_fe(s)->mode == PR_MODE_HTTP)
Christopher Fauletff8abcd2017-06-02 15:33:24 +0200457 HA_ATOMIC_ADD(&strm_fe(s)->fe_counters.p.http.comp_rsp, 1);
Christopher Fauletd60b3cf2017-06-26 11:47:13 +0200458 if ((s->flags & SF_BE_ASSIGNED) && (s->be->mode == PR_MODE_HTTP))
Christopher Fauletff8abcd2017-06-02 15:33:24 +0200459 HA_ATOMIC_ADD(&s->be->be_counters.p.http.comp_rsp, 1);
Christopher Fauletd60b3cf2017-06-26 11:47:13 +0200460 end:
461 return 1;
462}
Christopher Faulet3d97c902015-12-09 14:59:38 +0100463/***********************************************************************/
Christopher Faulet27d93c32018-12-15 22:32:02 +0100464static int
465http_set_comp_reshdr(struct comp_state *st, struct stream *s, struct http_msg *msg)
466{
467 struct http_txn *txn = s->txn;
468
469 /*
470 * Add Content-Encoding header when it's not identity encoding.
471 * RFC 2616 : Identity encoding: This content-coding is used only in the
472 * Accept-Encoding header, and SHOULD NOT be used in the Content-Encoding
473 * header.
474 */
475 if (st->comp_algo->cfg_name_len != 8 || memcmp(st->comp_algo->cfg_name, "identity", 8) != 0) {
476 trash.data = 18;
477 memcpy(trash.area, "Content-Encoding: ", trash.data);
478 memcpy(trash.area + trash.data, st->comp_algo->ua_name,
479 st->comp_algo->ua_name_len);
480 trash.data += st->comp_algo->ua_name_len;
481 trash.area[trash.data] = '\0';
482 if (http_header_add_tail2(msg, &txn->hdr_idx, trash.area, trash.data) < 0)
483 goto error;
484 }
485
486 /* remove Content-Length header */
487 if (msg->flags & HTTP_MSGF_CNT_LEN) {
488 struct hdr_ctx ctx;
489
490 ctx.idx = 0;
491 while (http_find_header2("Content-Length", 14, ci_head(&s->res), &txn->hdr_idx, &ctx))
492 http_remove_header2(msg, &txn->hdr_idx, &ctx);
493 }
494
495 /* add Transfer-Encoding header */
496 if (!(msg->flags & HTTP_MSGF_TE_CHNK)) {
497 if (http_header_add_tail2(msg, &txn->hdr_idx, "Transfer-Encoding: chunked", 26) < 0)
498 goto error;
499 }
500
501
502 return 1;
503
504 error:
505 st->comp_algo->end(&st->comp_ctx);
506 st->comp_algo = NULL;
507 return 0;
508}
509
510static int
511htx_set_comp_reshdr(struct comp_state *st, struct stream *s, struct http_msg *msg)
512{
513 struct htx *htx = htxbuf(&msg->chn->buf);
514
515 /*
516 * Add Content-Encoding header when it's not identity encoding.
517 * RFC 2616 : Identity encoding: This content-coding is used only in the
518 * Accept-Encoding header, and SHOULD NOT be used in the Content-Encoding
519 * header.
520 */
521 if (st->comp_algo->cfg_name_len != 8 || memcmp(st->comp_algo->cfg_name, "identity", 8) != 0) {
522 struct ist v = ist2(st->comp_algo->ua_name, st->comp_algo->ua_name_len);
523
524 if (!http_add_header(htx, ist("Content-Encoding"), v))
525 goto error;
526 }
527
528 /* remove Content-Length header */
529 if (msg->flags & HTTP_MSGF_CNT_LEN) {
530 struct http_hdr_ctx ctx;
531
532 ctx.blk = NULL;
533 while (http_find_header(htx, ist("Content-Length"), &ctx, 1))
534 http_remove_header(htx, &ctx);
535 }
536
537 /* add "Transfer-Encoding: chunked" header */
538 if (!(msg->flags & HTTP_MSGF_TE_CHNK)) {
539 if (!http_add_header(htx, ist("Transfer-Encoding"), ist("chunked")))
540 goto error;
541 }
542
543 return 1;
544
545 error:
546 st->comp_algo->end(&st->comp_ctx);
547 st->comp_algo = NULL;
548 return 0;
549}
550
551static int
552set_compression_response_header(struct comp_state *st, struct stream *s, struct http_msg *msg)
553{
554 if (IS_HTX_STRM(s))
555 return htx_set_comp_reshdr(st, s, msg);
556 else
557 return http_set_comp_reshdr(st, s, msg);
558}
559
Christopher Faulet3d97c902015-12-09 14:59:38 +0100560/*
561 * Selects a compression algorithm depending on the client request.
562 */
Christopher Faulete6902cd2018-11-30 22:29:48 +0100563static int
564http_select_comp_reqhdr(struct comp_state *st, struct stream *s, struct http_msg *msg)
Christopher Faulet3d97c902015-12-09 14:59:38 +0100565{
566 struct http_txn *txn = s->txn;
Olivier Houchard0b662842018-06-29 18:16:31 +0200567 struct channel *req = msg->chn;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100568 struct hdr_ctx ctx;
569 struct comp_algo *comp_algo = NULL;
570 struct comp_algo *comp_algo_back = NULL;
571
572 /* Disable compression for older user agents announcing themselves as "Mozilla/4"
573 * unless they are known good (MSIE 6 with XP SP2, or MSIE 7 and later).
574 * See http://zoompf.com/2012/02/lose-the-wait-http-compression for more details.
575 */
576 ctx.idx = 0;
Olivier Houchard0b662842018-06-29 18:16:31 +0200577 if (http_find_header2("User-Agent", 10, ci_head(req), &txn->hdr_idx, &ctx) &&
Christopher Faulet3d97c902015-12-09 14:59:38 +0100578 ctx.vlen >= 9 &&
579 memcmp(ctx.line + ctx.val, "Mozilla/4", 9) == 0 &&
580 (ctx.vlen < 31 ||
581 memcmp(ctx.line + ctx.val + 25, "MSIE ", 5) != 0 ||
582 ctx.line[ctx.val + 30] < '6' ||
583 (ctx.line[ctx.val + 30] == '6' &&
584 (ctx.vlen < 54 || memcmp(ctx.line + 51, "SV1", 3) != 0)))) {
Christopher Faulet92d36382015-11-05 13:35:03 +0100585 st->comp_algo = NULL;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100586 return 0;
587 }
588
589 /* search for the algo in the backend in priority or the frontend */
Christopher Faulet92d36382015-11-05 13:35:03 +0100590 if ((s->be->comp && (comp_algo_back = s->be->comp->algos)) ||
591 (strm_fe(s)->comp && (comp_algo_back = strm_fe(s)->comp->algos))) {
Christopher Faulet3d97c902015-12-09 14:59:38 +0100592 int best_q = 0;
593
594 ctx.idx = 0;
Olivier Houchard0b662842018-06-29 18:16:31 +0200595 while (http_find_header2("Accept-Encoding", 15, ci_head(req), &txn->hdr_idx, &ctx)) {
Christopher Faulet3d97c902015-12-09 14:59:38 +0100596 const char *qval;
597 int q;
598 int toklen;
599
600 /* try to isolate the token from the optional q-value */
601 toklen = 0;
Willy Tarreau2235b262016-11-05 15:50:20 +0100602 while (toklen < ctx.vlen && HTTP_IS_TOKEN(*(ctx.line + ctx.val + toklen)))
Christopher Faulet3d97c902015-12-09 14:59:38 +0100603 toklen++;
604
605 qval = ctx.line + ctx.val + toklen;
606 while (1) {
Willy Tarreau2235b262016-11-05 15:50:20 +0100607 while (qval < ctx.line + ctx.val + ctx.vlen && HTTP_IS_LWS(*qval))
Christopher Faulet3d97c902015-12-09 14:59:38 +0100608 qval++;
609
610 if (qval >= ctx.line + ctx.val + ctx.vlen || *qval != ';') {
611 qval = NULL;
612 break;
613 }
614 qval++;
615
Willy Tarreau2235b262016-11-05 15:50:20 +0100616 while (qval < ctx.line + ctx.val + ctx.vlen && HTTP_IS_LWS(*qval))
Christopher Faulet3d97c902015-12-09 14:59:38 +0100617 qval++;
618
619 if (qval >= ctx.line + ctx.val + ctx.vlen) {
620 qval = NULL;
621 break;
622 }
623 if (strncmp(qval, "q=", MIN(ctx.line + ctx.val + ctx.vlen - qval, 2)) == 0)
624 break;
625
626 while (qval < ctx.line + ctx.val + ctx.vlen && *qval != ';')
627 qval++;
628 }
629
630 /* here we have qval pointing to the first "q=" attribute or NULL if not found */
Willy Tarreauab813a42018-09-10 18:41:28 +0200631 q = qval ? http_parse_qvalue(qval + 2, NULL) : 1000;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100632
633 if (q <= best_q)
634 continue;
635
636 for (comp_algo = comp_algo_back; comp_algo; comp_algo = comp_algo->next) {
637 if (*(ctx.line + ctx.val) == '*' ||
638 word_match(ctx.line + ctx.val, toklen, comp_algo->ua_name, comp_algo->ua_name_len)) {
Christopher Faulet92d36382015-11-05 13:35:03 +0100639 st->comp_algo = comp_algo;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100640 best_q = q;
641 break;
642 }
643 }
644 }
645 }
646
647 /* remove all occurrences of the header when "compression offload" is set */
Christopher Faulet92d36382015-11-05 13:35:03 +0100648 if (st->comp_algo) {
649 if ((s->be->comp && s->be->comp->offload) ||
650 (strm_fe(s)->comp && strm_fe(s)->comp->offload)) {
Christopher Faulet3d97c902015-12-09 14:59:38 +0100651 http_remove_header2(msg, &txn->hdr_idx, &ctx);
652 ctx.idx = 0;
Olivier Houchard0b662842018-06-29 18:16:31 +0200653 while (http_find_header2("Accept-Encoding", 15, ci_head(req), &txn->hdr_idx, &ctx)) {
Christopher Faulet3d97c902015-12-09 14:59:38 +0100654 http_remove_header2(msg, &txn->hdr_idx, &ctx);
655 }
656 }
Christopher Faulete6902cd2018-11-30 22:29:48 +0100657 return 1;
658 }
659
660 /* identity is implicit does not require headers */
661 if ((s->be->comp && (comp_algo_back = s->be->comp->algos)) ||
662 (strm_fe(s)->comp && (comp_algo_back = strm_fe(s)->comp->algos))) {
663 for (comp_algo = comp_algo_back; comp_algo; comp_algo = comp_algo->next) {
664 if (comp_algo->cfg_name_len == 8 && memcmp(comp_algo->cfg_name, "identity", 8) == 0) {
665 st->comp_algo = comp_algo;
666 return 1;
667 }
668 }
669 }
670
671 st->comp_algo = NULL;
672 return 0;
673}
674
675static int
676htx_select_comp_reqhdr(struct comp_state *st, struct stream *s, struct http_msg *msg)
677{
Christopher Faulet27ba2dc2018-12-05 11:53:24 +0100678 struct htx *htx = htxbuf(&msg->chn->buf);
Christopher Faulete6902cd2018-11-30 22:29:48 +0100679 struct http_hdr_ctx ctx;
680 struct comp_algo *comp_algo = NULL;
681 struct comp_algo *comp_algo_back = NULL;
682
683 /* Disable compression for older user agents announcing themselves as "Mozilla/4"
684 * unless they are known good (MSIE 6 with XP SP2, or MSIE 7 and later).
685 * See http://zoompf.com/2012/02/lose-the-wait-http-compression for more details.
686 */
687 ctx.blk = NULL;
688 if (http_find_header(htx, ist("User-Agent"), &ctx, 1) &&
689 ctx.value.len >= 9 &&
690 memcmp(ctx.value.ptr, "Mozilla/4", 9) == 0 &&
691 (ctx.value.len < 31 ||
692 memcmp(ctx.value.ptr + 25, "MSIE ", 5) != 0 ||
693 *(ctx.value.ptr + 30) < '6' ||
694 (*(ctx.value.ptr + 30) == '6' &&
695 (ctx.value.len < 54 || memcmp(ctx.value.ptr + 51, "SV1", 3) != 0)))) {
696 st->comp_algo = NULL;
697 return 0;
698 }
699
700 /* search for the algo in the backend in priority or the frontend */
701 if ((s->be->comp && (comp_algo_back = s->be->comp->algos)) ||
702 (strm_fe(s)->comp && (comp_algo_back = strm_fe(s)->comp->algos))) {
703 int best_q = 0;
704
705 ctx.blk = NULL;
706 while (http_find_header(htx, ist("Accept-Encoding"), &ctx, 0)) {
707 const char *qval;
708 int q;
709 int toklen;
710
711 /* try to isolate the token from the optional q-value */
712 toklen = 0;
713 while (toklen < ctx.value.len && HTTP_IS_TOKEN(*(ctx.value.ptr + toklen)))
714 toklen++;
715
716 qval = ctx.value.ptr + toklen;
717 while (1) {
718 while (qval < ctx.value.ptr + ctx.value.len && HTTP_IS_LWS(*qval))
719 qval++;
720
721 if (qval >= ctx.value.ptr + ctx.value.len || *qval != ';') {
722 qval = NULL;
723 break;
724 }
725 qval++;
726
727 while (qval < ctx.value.ptr + ctx.value.len && HTTP_IS_LWS(*qval))
728 qval++;
729
730 if (qval >= ctx.value.ptr + ctx.value.len) {
731 qval = NULL;
732 break;
733 }
734 if (strncmp(qval, "q=", MIN(ctx.value.ptr + ctx.value.len - qval, 2)) == 0)
735 break;
736
737 while (qval < ctx.value.ptr + ctx.value.len && *qval != ';')
738 qval++;
739 }
740
741 /* here we have qval pointing to the first "q=" attribute or NULL if not found */
742 q = qval ? http_parse_qvalue(qval + 2, NULL) : 1000;
743
744 if (q <= best_q)
745 continue;
746
747 for (comp_algo = comp_algo_back; comp_algo; comp_algo = comp_algo->next) {
748 if (*(ctx.value.ptr) == '*' ||
749 word_match(ctx.value.ptr, toklen, comp_algo->ua_name, comp_algo->ua_name_len)) {
750 st->comp_algo = comp_algo;
751 best_q = q;
752 break;
753 }
754 }
755 }
756 }
757
758 /* remove all occurrences of the header when "compression offload" is set */
759 if (st->comp_algo) {
760 if ((s->be->comp && s->be->comp->offload) ||
761 (strm_fe(s)->comp && strm_fe(s)->comp->offload)) {
762 http_remove_header(htx, &ctx);
763 ctx.blk = NULL;
764 while (http_find_header(htx, ist("Accept-Encoding"), &ctx, 1))
765 http_remove_header(htx, &ctx);
766 }
Christopher Faulet3d97c902015-12-09 14:59:38 +0100767 return 1;
768 }
769
770 /* identity is implicit does not require headers */
Christopher Faulet92d36382015-11-05 13:35:03 +0100771 if ((s->be->comp && (comp_algo_back = s->be->comp->algos)) ||
772 (strm_fe(s)->comp && (comp_algo_back = strm_fe(s)->comp->algos))) {
Christopher Faulet3d97c902015-12-09 14:59:38 +0100773 for (comp_algo = comp_algo_back; comp_algo; comp_algo = comp_algo->next) {
774 if (comp_algo->cfg_name_len == 8 && memcmp(comp_algo->cfg_name, "identity", 8) == 0) {
Christopher Faulet92d36382015-11-05 13:35:03 +0100775 st->comp_algo = comp_algo;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100776 return 1;
777 }
778 }
779 }
780
Christopher Faulet92d36382015-11-05 13:35:03 +0100781 st->comp_algo = NULL;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100782 return 0;
783}
784
Christopher Faulete6902cd2018-11-30 22:29:48 +0100785static int
786select_compression_request_header(struct comp_state *st, struct stream *s,
787 struct http_msg *msg)
788{
789 if (IS_HTX_STRM(s))
790 return htx_select_comp_reqhdr(st, s, msg);
791 else
792 return http_select_comp_reqhdr(st, s, msg);
793}
Christopher Faulet92d36382015-11-05 13:35:03 +0100794
Christopher Faulet3d97c902015-12-09 14:59:38 +0100795/*
796 * Selects a comression algorithm depending of the server response.
797 */
Christopher Faulet92d36382015-11-05 13:35:03 +0100798static int
Christopher Faulete6902cd2018-11-30 22:29:48 +0100799http_select_comp_reshdr(struct comp_state *st, struct stream *s, struct http_msg *msg)
Christopher Faulet3d97c902015-12-09 14:59:38 +0100800{
801 struct http_txn *txn = s->txn;
Olivier Houchard0b662842018-06-29 18:16:31 +0200802 struct channel *c = msg->chn;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100803 struct hdr_ctx ctx;
804 struct comp_type *comp_type;
805
806 /* no common compression algorithm was found in request header */
Christopher Faulet92d36382015-11-05 13:35:03 +0100807 if (st->comp_algo == NULL)
Christopher Faulet3d97c902015-12-09 14:59:38 +0100808 goto fail;
809
810 /* HTTP < 1.1 should not be compressed */
811 if (!(msg->flags & HTTP_MSGF_VER_11) || !(txn->req.flags & HTTP_MSGF_VER_11))
812 goto fail;
813
Christopher Faulet92d36382015-11-05 13:35:03 +0100814 if (txn->meth == HTTP_METH_HEAD)
815 goto fail;
816
Christopher Faulet3d97c902015-12-09 14:59:38 +0100817 /* compress 200,201,202,203 responses only */
818 if ((txn->status != 200) &&
819 (txn->status != 201) &&
820 (txn->status != 202) &&
821 (txn->status != 203))
822 goto fail;
823
824
825 /* Content-Length is null */
826 if (!(msg->flags & HTTP_MSGF_TE_CHNK) && msg->body_len == 0)
827 goto fail;
828
829 /* content is already compressed */
830 ctx.idx = 0;
Olivier Houchard0b662842018-06-29 18:16:31 +0200831 if (http_find_header2("Content-Encoding", 16, ci_head(c), &txn->hdr_idx, &ctx))
Christopher Faulet3d97c902015-12-09 14:59:38 +0100832 goto fail;
833
834 /* no compression when Cache-Control: no-transform is present in the message */
835 ctx.idx = 0;
Olivier Houchard0b662842018-06-29 18:16:31 +0200836 while (http_find_header2("Cache-Control", 13, ci_head(c), &txn->hdr_idx, &ctx)) {
Christopher Faulet3d97c902015-12-09 14:59:38 +0100837 if (word_match(ctx.line + ctx.val, ctx.vlen, "no-transform", 12))
838 goto fail;
839 }
840
841 comp_type = NULL;
842
843 /* we don't want to compress multipart content-types, nor content-types that are
844 * not listed in the "compression type" directive if any. If no content-type was
845 * found but configuration requires one, we don't compress either. Backend has
846 * the priority.
847 */
848 ctx.idx = 0;
Olivier Houchard0b662842018-06-29 18:16:31 +0200849 if (http_find_header2("Content-Type", 12, ci_head(c), &txn->hdr_idx, &ctx)) {
Christopher Faulet3d97c902015-12-09 14:59:38 +0100850 if (ctx.vlen >= 9 && strncasecmp("multipart", ctx.line+ctx.val, 9) == 0)
851 goto fail;
852
853 if ((s->be->comp && (comp_type = s->be->comp->types)) ||
854 (strm_fe(s)->comp && (comp_type = strm_fe(s)->comp->types))) {
855 for (; comp_type; comp_type = comp_type->next) {
856 if (ctx.vlen >= comp_type->name_len &&
857 strncasecmp(ctx.line+ctx.val, comp_type->name, comp_type->name_len) == 0)
858 /* this Content-Type should be compressed */
859 break;
860 }
861 /* this Content-Type should not be compressed */
862 if (comp_type == NULL)
863 goto fail;
864 }
865 }
866 else { /* no content-type header */
Christopher Faulet92d36382015-11-05 13:35:03 +0100867 if ((s->be->comp && s->be->comp->types) ||
868 (strm_fe(s)->comp && strm_fe(s)->comp->types))
Christopher Faulet3d97c902015-12-09 14:59:38 +0100869 goto fail; /* a content-type was required */
870 }
871
872 /* limit compression rate */
873 if (global.comp_rate_lim > 0)
874 if (read_freq_ctr(&global.comp_bps_in) > global.comp_rate_lim)
875 goto fail;
876
877 /* limit cpu usage */
878 if (idle_pct < compress_min_idle)
879 goto fail;
880
881 /* initialize compression */
Christopher Faulet92d36382015-11-05 13:35:03 +0100882 if (st->comp_algo->init(&st->comp_ctx, global.tune.comp_maxlevel) < 0)
Christopher Faulet3d97c902015-12-09 14:59:38 +0100883 goto fail;
Christopher Faulet92d36382015-11-05 13:35:03 +0100884 msg->flags |= HTTP_MSGF_COMPRESSING;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100885 return 1;
886
887fail:
Christopher Faulet92d36382015-11-05 13:35:03 +0100888 st->comp_algo = NULL;
Christopher Faulet3d97c902015-12-09 14:59:38 +0100889 return 0;
890}
891
Christopher Faulete6902cd2018-11-30 22:29:48 +0100892static int
893htx_select_comp_reshdr(struct comp_state *st, struct stream *s, struct http_msg *msg)
894{
Christopher Faulet27ba2dc2018-12-05 11:53:24 +0100895 struct htx *htx = htxbuf(&msg->chn->buf);
Christopher Faulete6902cd2018-11-30 22:29:48 +0100896 struct http_txn *txn = s->txn;
897 struct http_hdr_ctx ctx;
898 struct comp_type *comp_type;
899
900 /* no common compression algorithm was found in request header */
901 if (st->comp_algo == NULL)
902 goto fail;
903
904 /* HTTP < 1.1 should not be compressed */
905 if (!(msg->flags & HTTP_MSGF_VER_11) || !(txn->req.flags & HTTP_MSGF_VER_11))
906 goto fail;
907
908 if (txn->meth == HTTP_METH_HEAD)
909 goto fail;
910
911 /* compress 200,201,202,203 responses only */
912 if ((txn->status != 200) &&
913 (txn->status != 201) &&
914 (txn->status != 202) &&
915 (txn->status != 203))
916 goto fail;
917
918 if (msg->flags & HTTP_MSGF_BODYLESS)
919 goto fail;
920
921 /* content is already compressed */
922 ctx.blk = NULL;
923 if (http_find_header(htx, ist("Content-Encoding"), &ctx, 1))
924 goto fail;
925
926 /* no compression when Cache-Control: no-transform is present in the message */
927 ctx.blk = NULL;
928 while (http_find_header(htx, ist("Cache-Control"), &ctx, 0)) {
929 if (word_match(ctx.value.ptr, ctx.value.len, "no-transform", 12))
930 goto fail;
931 }
932
933 comp_type = NULL;
934
935 /* we don't want to compress multipart content-types, nor content-types that are
936 * not listed in the "compression type" directive if any. If no content-type was
937 * found but configuration requires one, we don't compress either. Backend has
938 * the priority.
939 */
940 ctx.blk = NULL;
941 if (http_find_header(htx, ist("Content-Type"), &ctx, 1)) {
942 if (ctx.value.len >= 9 && strncasecmp("multipart", ctx.value.ptr, 9) == 0)
943 goto fail;
944
945 if ((s->be->comp && (comp_type = s->be->comp->types)) ||
946 (strm_fe(s)->comp && (comp_type = strm_fe(s)->comp->types))) {
947 for (; comp_type; comp_type = comp_type->next) {
948 if (ctx.value.len >= comp_type->name_len &&
949 strncasecmp(ctx.value.ptr, comp_type->name, comp_type->name_len) == 0)
950 /* this Content-Type should be compressed */
951 break;
952 }
953 /* this Content-Type should not be compressed */
954 if (comp_type == NULL)
955 goto fail;
956 }
957 }
958 else { /* no content-type header */
959 if ((s->be->comp && s->be->comp->types) ||
960 (strm_fe(s)->comp && strm_fe(s)->comp->types))
961 goto fail; /* a content-type was required */
962 }
963
964 /* limit compression rate */
965 if (global.comp_rate_lim > 0)
966 if (read_freq_ctr(&global.comp_bps_in) > global.comp_rate_lim)
967 goto fail;
968
969 /* limit cpu usage */
970 if (idle_pct < compress_min_idle)
971 goto fail;
972
973 /* initialize compression */
974 if (st->comp_algo->init(&st->comp_ctx, global.tune.comp_maxlevel) < 0)
975 goto fail;
Christopher Faulete6902cd2018-11-30 22:29:48 +0100976 msg->flags |= HTTP_MSGF_COMPRESSING;
977 return 1;
978
979 deinit_comp_ctx:
980 st->comp_algo->end(&st->comp_ctx);
981 fail:
982 st->comp_algo = NULL;
983 return 0;
984}
985
986static int
987select_compression_response_header(struct comp_state *st, struct stream *s, struct http_msg *msg)
988{
989 if (IS_HTX_STRM(s))
990 return htx_select_comp_reshdr(st, s, msg);
991 else
992 return http_select_comp_reshdr(st, s, msg);
993}
Christopher Faulet3d97c902015-12-09 14:59:38 +0100994/***********************************************************************/
995/* emit the chunksize followed by a CRLF on the output and return the number of
996 * bytes written. It goes backwards and starts with the byte before <end>. It
997 * returns the number of bytes written which will not exceed 10 (8 digits, CR,
998 * and LF). The caller is responsible for ensuring there is enough room left in
999 * the output buffer for the string.
1000 */
1001static int
1002http_emit_chunk_size(char *end, unsigned int chksz)
1003{
1004 char *beg = end;
1005
1006 *--beg = '\n';
1007 *--beg = '\r';
1008 do {
1009 *--beg = hextab[chksz & 0xF];
1010 } while (chksz >>= 4);
1011 return end - beg;
1012}
1013
1014/*
1015 * Init HTTP compression
1016 */
Christopher Faulet92d36382015-11-05 13:35:03 +01001017static int
Willy Tarreaud54a8ce2018-06-29 18:42:02 +02001018http_compression_buffer_init(struct channel *inc, struct buffer *out, unsigned int *out_len)
Christopher Faulet3d97c902015-12-09 14:59:38 +01001019{
1020 /* output stream requires at least 10 bytes for the gzip header, plus
1021 * at least 8 bytes for the gzip trailer (crc+len), plus a possible
1022 * plus at most 5 bytes per 32kB block and 2 bytes to close the stream.
1023 */
Olivier Houchard0b662842018-06-29 18:16:31 +02001024 if (c_room(inc) < 20 + 5 * ((ci_data(inc) + 32767) >> 15))
Christopher Faulet3d97c902015-12-09 14:59:38 +01001025 return -1;
1026
1027 /* prepare an empty output buffer in which we reserve enough room for
1028 * copying the output bytes from <in>, plus 10 extra bytes to write
1029 * the chunk size. We don't copy the bytes yet so that if we have to
1030 * cancel the operation later, it's cheap.
1031 */
1032 b_reset(out);
Willy Tarreaud54a8ce2018-06-29 18:42:02 +02001033 *out_len = co_data(inc);
1034 out->head += *out_len + 10;
Christopher Faulet3d97c902015-12-09 14:59:38 +01001035 return 0;
1036}
1037
Christopher Faulete6902cd2018-11-30 22:29:48 +01001038static int
1039htx_compression_buffer_init(struct htx *htx, struct buffer *out)
1040{
1041 /* output stream requires at least 10 bytes for the gzip header, plus
1042 * at least 8 bytes for the gzip trailer (crc+len), plus a possible
1043 * plus at most 5 bytes per 32kB block and 2 bytes to close the stream.
1044 */
1045 if (htx_free_space(htx) < 20 + 5 * ((htx->data + 32767) >> 15))
1046 return -1;
1047 b_reset(out);
1048 return 0;
1049}
1050
Christopher Faulet3d97c902015-12-09 14:59:38 +01001051/*
1052 * Add data to compress
1053 */
Christopher Faulet92d36382015-11-05 13:35:03 +01001054static int
1055http_compression_buffer_add_data(struct comp_state *st, struct buffer *in,
Willy Tarreaud54a8ce2018-06-29 18:42:02 +02001056 int in_out, struct buffer *out, int sz)
Christopher Faulet3d97c902015-12-09 14:59:38 +01001057{
Christopher Faulet3d97c902015-12-09 14:59:38 +01001058 int consumed_data = 0;
1059 int data_process_len;
1060 int block1, block2;
1061
Christopher Faulet92d36382015-11-05 13:35:03 +01001062 if (!sz)
Christopher Faulet3e7bc672015-12-07 13:39:08 +01001063 goto end;
Christopher Faulet3d97c902015-12-09 14:59:38 +01001064
Christopher Faulet92d36382015-11-05 13:35:03 +01001065 /* select the smallest size between the announced chunk size, the input
Christopher Faulet3d97c902015-12-09 14:59:38 +01001066 * data, and the available output buffer size. The compressors are
Christopher Faulet92d36382015-11-05 13:35:03 +01001067 * assumed to be able to process all the bytes we pass to them at
1068 * once. */
Willy Tarreaueac52592018-06-15 13:59:36 +02001069 data_process_len = MIN(b_room(out), sz);
Christopher Faulet92d36382015-11-05 13:35:03 +01001070
Christopher Faulet3d97c902015-12-09 14:59:38 +01001071 block1 = data_process_len;
Willy Tarreaud54a8ce2018-06-29 18:42:02 +02001072 if (block1 > b_contig_data(in, in_out))
1073 block1 = b_contig_data(in, in_out);
Christopher Faulet3d97c902015-12-09 14:59:38 +01001074 block2 = data_process_len - block1;
1075
1076 /* compressors return < 0 upon error or the amount of bytes read */
Willy Tarreaud54a8ce2018-06-29 18:42:02 +02001077 consumed_data = st->comp_algo->add_data(st->comp_ctx, b_head(in) + in_out, block1, out);
Christopher Faulet3e7bc672015-12-07 13:39:08 +01001078 if (consumed_data != block1 || !block2)
1079 goto end;
Willy Tarreaud54a8ce2018-06-29 18:42:02 +02001080 consumed_data = st->comp_algo->add_data(st->comp_ctx, b_peek(in, 0), block2, out);
Christopher Faulet3e7bc672015-12-07 13:39:08 +01001081 if (consumed_data < 0)
1082 goto end;
1083 consumed_data += block1;
1084
1085 end:
Christopher Faulet3d97c902015-12-09 14:59:38 +01001086 return consumed_data;
1087}
1088
Christopher Faulete6902cd2018-11-30 22:29:48 +01001089static int
1090htx_compression_buffer_add_data(struct comp_state *st, const char *data, size_t len,
1091 struct buffer *out)
1092{
1093 return st->comp_algo->add_data(st->comp_ctx, data, len, out);
1094}
1095
Christopher Faulet3d97c902015-12-09 14:59:38 +01001096/*
1097 * Flush data in process, and write the header and footer of the chunk. Upon
1098 * success, in and out buffers are swapped to avoid a copy.
1099 */
Christopher Faulet92d36382015-11-05 13:35:03 +01001100static int
1101http_compression_buffer_end(struct comp_state *st, struct stream *s,
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001102 struct channel *chn, struct buffer *out,
Willy Tarreaud54a8ce2018-06-29 18:42:02 +02001103 unsigned int *buf_out, int end)
Christopher Faulet3d97c902015-12-09 14:59:38 +01001104{
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001105 struct buffer tmp_buf;
Christopher Faulet3d97c902015-12-09 14:59:38 +01001106 char *tail;
Christopher Faulet92d36382015-11-05 13:35:03 +01001107 int to_forward, left;
Willy Tarreaud54a8ce2018-06-29 18:42:02 +02001108 unsigned int tmp_out;
Christopher Faulet3d97c902015-12-09 14:59:38 +01001109
1110#if defined(USE_SLZ) || defined(USE_ZLIB)
1111 int ret;
1112
1113 /* flush data here */
Christopher Faulet3d97c902015-12-09 14:59:38 +01001114 if (end)
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001115 ret = st->comp_algo->finish(st->comp_ctx, out); /* end of data */
Christopher Faulet3d97c902015-12-09 14:59:38 +01001116 else
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001117 ret = st->comp_algo->flush(st->comp_ctx, out); /* end of buffer */
Christopher Faulet3d97c902015-12-09 14:59:38 +01001118
1119 if (ret < 0)
1120 return -1; /* flush failed */
1121
1122#endif /* USE_ZLIB */
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001123 if (b_data(out) == 0) {
Christopher Faulet3d97c902015-12-09 14:59:38 +01001124 /* No data were appended, let's drop the output buffer and
1125 * keep the input buffer unchanged.
1126 */
1127 return 0;
1128 }
1129
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001130 /* OK so at this stage, we have an output buffer <out> looking like this :
Christopher Faulet3d97c902015-12-09 14:59:38 +01001131 *
1132 * <-- o --> <------ i ----->
1133 * +---------+---+------------+-----------+
1134 * | out | c | comp_in | empty |
1135 * +---------+---+------------+-----------+
1136 * data p size
1137 *
Willy Tarreaud54a8ce2018-06-29 18:42:02 +02001138 * <out> is the room reserved to copy the channel output. It starts at
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001139 * out->area and has not yet been filled. <c> is the room reserved to
Willy Tarreaud54a8ce2018-06-29 18:42:02 +02001140 * write the chunk size (10 bytes). <comp_in> is the compressed
1141 * equivalent of the data part of ib->len. <empty> is the amount of
1142 * empty bytes at the end of the buffer, into which we may have to
1143 * copy the remaining bytes from ib->len after the data
1144 * (chunk size, trailers, ...).
Christopher Faulet3d97c902015-12-09 14:59:38 +01001145 */
1146
Joseph Herlant942eea32018-11-15 13:57:22 -08001147 /* Write real size at the beginning of the chunk, no need of wrapping.
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001148 * We write the chunk using a dynamic length and adjust out->p and out->i
Christopher Faulet3d97c902015-12-09 14:59:38 +01001149 * accordingly afterwards. That will move <out> away from <data>.
1150 */
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001151 left = http_emit_chunk_size(b_head(out), b_data(out));
1152 b_add(out, left);
1153 out->head -= *buf_out + (left);
1154 /* Copy previous data from chn into out */
Willy Tarreaud54a8ce2018-06-29 18:42:02 +02001155 if (co_data(chn) > 0) {
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001156 left = b_contig_data(&chn->buf, 0);
Willy Tarreaud54a8ce2018-06-29 18:42:02 +02001157 if (left > *buf_out)
1158 left = *buf_out;
Christopher Faulet3d97c902015-12-09 14:59:38 +01001159
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001160 memcpy(b_head(out), co_head(chn), left);
1161 b_add(out, left);
Willy Tarreaud54a8ce2018-06-29 18:42:02 +02001162 if (co_data(chn) - left) {/* second part of the buffer */
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001163 memcpy(b_head(out) + left, b_orig(&chn->buf), co_data(chn) - left);
1164 b_add(out, co_data(chn) - left);
Willy Tarreaud54a8ce2018-06-29 18:42:02 +02001165 }
Christopher Faulet3d97c902015-12-09 14:59:38 +01001166 }
1167
1168 /* chunked encoding requires CRLF after data */
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001169 tail = b_tail(out);
Christopher Faulet3d97c902015-12-09 14:59:38 +01001170 *tail++ = '\r';
1171 *tail++ = '\n';
1172
Christopher Faulet2fb28802015-12-01 10:40:57 +01001173 /* At the end of data, we must write the empty chunk 0<CRLF>,
1174 * and terminate the trailers section with a last <CRLF>. If
1175 * we're forwarding a chunked-encoded response, we'll have a
1176 * trailers section after the empty chunk which needs to be
1177 * forwarded and which will provide the last CRLF. Otherwise
1178 * we write it ourselves.
1179 */
1180 if (end) {
1181 struct http_msg *msg = &s->txn->rsp;
1182
1183 memcpy(tail, "0\r\n", 3);
1184 tail += 3;
Christopher Fauletb77c5c22015-12-07 16:48:42 +01001185 if (!(msg->flags & HTTP_MSGF_TE_CHNK)) {
Christopher Faulet2fb28802015-12-01 10:40:57 +01001186 memcpy(tail, "\r\n", 2);
1187 tail += 2;
1188 }
1189 }
1190
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001191 b_add(out, tail - b_tail(out));
1192 to_forward = b_data(out) - *buf_out;
Christopher Faulet3d97c902015-12-09 14:59:38 +01001193
1194 /* update input rate */
Christopher Faulet92d36382015-11-05 13:35:03 +01001195 if (st->comp_ctx && st->comp_ctx->cur_lvl > 0) {
Christopher Faulet2fb28802015-12-01 10:40:57 +01001196 update_freq_ctr(&global.comp_bps_in, st->consumed);
Christopher Fauletff8abcd2017-06-02 15:33:24 +02001197 HA_ATOMIC_ADD(&strm_fe(s)->fe_counters.comp_in, st->consumed);
1198 HA_ATOMIC_ADD(&s->be->be_counters.comp_in, st->consumed);
Christopher Faulet3d97c902015-12-09 14:59:38 +01001199 } else {
Christopher Fauletff8abcd2017-06-02 15:33:24 +02001200 HA_ATOMIC_ADD(&strm_fe(s)->fe_counters.comp_byp, st->consumed);
1201 HA_ATOMIC_ADD(&s->be->be_counters.comp_byp, st->consumed);
Christopher Faulet3d97c902015-12-09 14:59:38 +01001202 }
1203
1204 /* copy the remaining data in the tmp buffer. */
Willy Tarreaubcbd3932018-06-06 07:13:22 +02001205 c_adv(chn, st->consumed);
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001206 if (b_data(&chn->buf) - co_data(chn) > 0) {
Willy Tarreau7194d3c2018-06-06 16:55:45 +02001207 left = ci_contig_data(chn);
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001208 memcpy(b_tail(out), ci_head(chn), left);
1209 b_add(out, left);
1210 if (b_data(&chn->buf) - (co_data(chn) + left)) {
1211 memcpy(b_tail(out), b_orig(&chn->buf), b_data(&chn->buf) - left);
1212 b_add(out, b_data(&chn->buf) - left);
Christopher Faulet3d97c902015-12-09 14:59:38 +01001213 }
1214 }
Christopher Faulet3d97c902015-12-09 14:59:38 +01001215 /* swap the buffers */
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001216 tmp_buf = chn->buf;
1217 chn->buf = *out;
1218 *out = tmp_buf;
1219
Olivier Houchard08afac02018-06-22 19:26:39 +02001220 tmp_out = chn->output;
1221 chn->output = *buf_out;
Willy Tarreaud54a8ce2018-06-29 18:42:02 +02001222 *buf_out = tmp_out;
1223
Christopher Faulet3d97c902015-12-09 14:59:38 +01001224
Christopher Faulet92d36382015-11-05 13:35:03 +01001225
1226 if (st->comp_ctx && st->comp_ctx->cur_lvl > 0) {
Christopher Faulet3d97c902015-12-09 14:59:38 +01001227 update_freq_ctr(&global.comp_bps_out, to_forward);
Christopher Fauletff8abcd2017-06-02 15:33:24 +02001228 HA_ATOMIC_ADD(&strm_fe(s)->fe_counters.comp_out, to_forward);
1229 HA_ATOMIC_ADD(&s->be->be_counters.comp_out, to_forward);
Christopher Faulet3d97c902015-12-09 14:59:38 +01001230 }
1231
Christopher Faulet3d97c902015-12-09 14:59:38 +01001232 return to_forward;
1233}
1234
Christopher Faulete6902cd2018-11-30 22:29:48 +01001235static int
1236htx_compression_buffer_end(struct comp_state *st, struct buffer *out, int end)
1237{
1238 if (end)
1239 return st->comp_algo->finish(st->comp_ctx, out);
1240 else
1241 return st->comp_algo->flush(st->comp_ctx, out);
1242}
1243
Christopher Faulet3d97c902015-12-09 14:59:38 +01001244
1245/***********************************************************************/
Christopher Faulet92d36382015-11-05 13:35:03 +01001246struct flt_ops comp_ops = {
Christopher Faulete6902cd2018-11-30 22:29:48 +01001247 .init = comp_flt_init,
Christopher Faulet8ca3b4b2017-07-25 11:07:15 +02001248 .init_per_thread = comp_flt_init_per_thread,
1249 .deinit_per_thread = comp_flt_deinit_per_thread,
Christopher Faulet92d36382015-11-05 13:35:03 +01001250
1251 .channel_start_analyze = comp_start_analyze,
Christopher Faulet92d36382015-11-05 13:35:03 +01001252 .channel_end_analyze = comp_end_analyze,
Christopher Faulet3dc860d2017-09-15 11:39:36 +02001253 .channel_post_analyze = comp_http_post_analyze,
Christopher Faulet92d36382015-11-05 13:35:03 +01001254
Christopher Faulet1339d742016-05-11 16:48:33 +02001255 .http_headers = comp_http_headers,
Christopher Faulete6902cd2018-11-30 22:29:48 +01001256 .http_payload = comp_http_payload,
1257 .http_end = comp_http_end,
1258
Christopher Faulet309c6412015-12-02 09:57:32 +01001259 .http_data = comp_http_data,
1260 .http_chunk_trailers = comp_http_chunk_trailers,
1261 .http_forward_data = comp_http_forward_data,
Christopher Faulet92d36382015-11-05 13:35:03 +01001262};
1263
Christopher Faulet3d97c902015-12-09 14:59:38 +01001264static int
1265parse_compression_options(char **args, int section, struct proxy *proxy,
1266 struct proxy *defpx, const char *file, int line,
1267 char **err)
1268{
Christopher Faulet92d36382015-11-05 13:35:03 +01001269 struct comp *comp;
Christopher Faulet3d97c902015-12-09 14:59:38 +01001270
1271 if (proxy->comp == NULL) {
Vincent Bernat02779b62016-04-03 13:48:43 +02001272 comp = calloc(1, sizeof(*comp));
Christopher Faulet3d97c902015-12-09 14:59:38 +01001273 proxy->comp = comp;
1274 }
1275 else
1276 comp = proxy->comp;
1277
1278 if (!strcmp(args[1], "algo")) {
1279 struct comp_ctx *ctx;
1280 int cur_arg = 2;
1281
1282 if (!*args[cur_arg]) {
1283 memprintf(err, "parsing [%s:%d] : '%s' expects <algorithm>\n",
1284 file, line, args[0]);
1285 return -1;
1286 }
1287 while (*(args[cur_arg])) {
1288 if (comp_append_algo(comp, args[cur_arg]) < 0) {
1289 memprintf(err, "'%s' : '%s' is not a supported algorithm.\n",
1290 args[0], args[cur_arg]);
1291 return -1;
1292 }
1293 if (proxy->comp->algos->init(&ctx, 9) == 0)
1294 proxy->comp->algos->end(&ctx);
1295 else {
1296 memprintf(err, "'%s' : Can't init '%s' algorithm.\n",
1297 args[0], args[cur_arg]);
1298 return -1;
1299 }
1300 cur_arg++;
1301 continue;
1302 }
1303 }
1304 else if (!strcmp(args[1], "offload"))
1305 comp->offload = 1;
1306 else if (!strcmp(args[1], "type")) {
1307 int cur_arg = 2;
1308
1309 if (!*args[cur_arg]) {
1310 memprintf(err, "'%s' expects <type>\n", args[0]);
1311 return -1;
1312 }
1313 while (*(args[cur_arg])) {
1314 comp_append_type(comp, args[cur_arg]);
1315 cur_arg++;
1316 continue;
1317 }
1318 }
1319 else {
1320 memprintf(err, "'%s' expects 'algo', 'type' or 'offload'\n",
1321 args[0]);
1322 return -1;
1323 }
1324
1325 return 0;
1326}
1327
Christopher Faulet92d36382015-11-05 13:35:03 +01001328static int
1329parse_http_comp_flt(char **args, int *cur_arg, struct proxy *px,
Thierry Fournier3610c392016-04-13 18:27:51 +02001330 struct flt_conf *fconf, char **err, void *private)
Christopher Faulet92d36382015-11-05 13:35:03 +01001331{
Christopher Faulet443ea1a2016-02-04 13:40:26 +01001332 struct flt_conf *fc, *back;
Christopher Faulet92d36382015-11-05 13:35:03 +01001333
Christopher Faulet443ea1a2016-02-04 13:40:26 +01001334 list_for_each_entry_safe(fc, back, &px->filter_configs, list) {
1335 if (fc->id == http_comp_flt_id) {
Christopher Faulet92d36382015-11-05 13:35:03 +01001336 memprintf(err, "%s: Proxy supports only one compression filter\n", px->id);
1337 return -1;
1338 }
1339 }
1340
Christopher Faulet443ea1a2016-02-04 13:40:26 +01001341 fconf->id = http_comp_flt_id;
1342 fconf->conf = NULL;
1343 fconf->ops = &comp_ops;
Christopher Faulet92d36382015-11-05 13:35:03 +01001344 (*cur_arg)++;
1345
1346 return 0;
1347}
1348
1349
1350int
Christopher Fauletc9df7f72018-12-10 16:14:04 +01001351check_implicit_http_comp_flt(struct proxy *proxy)
Christopher Faulet92d36382015-11-05 13:35:03 +01001352{
Christopher Faulet443ea1a2016-02-04 13:40:26 +01001353 struct flt_conf *fconf;
Christopher Faulet27d93c32018-12-15 22:32:02 +01001354 int explicit = 0;
1355 int comp = 0;
Christopher Faulet92d36382015-11-05 13:35:03 +01001356 int err = 0;
1357
1358 if (proxy->comp == NULL)
1359 goto end;
Christopher Faulet443ea1a2016-02-04 13:40:26 +01001360 if (!LIST_ISEMPTY(&proxy->filter_configs)) {
1361 list_for_each_entry(fconf, &proxy->filter_configs, list) {
1362 if (fconf->id == http_comp_flt_id)
Christopher Faulet27d93c32018-12-15 22:32:02 +01001363 comp = 1;
1364 else if (fconf->id == cache_store_flt_id) {
1365 if (comp) {
1366 ha_alert("config: %s '%s': unable to enable the compression filter "
1367 "before any cache filter.\n",
1368 proxy_type_str(proxy), proxy->id);
1369 err++;
1370 goto end;
1371 }
1372 }
1373 else
1374 explicit = 1;
Christopher Faulet92d36382015-11-05 13:35:03 +01001375 }
Christopher Faulet27d93c32018-12-15 22:32:02 +01001376 }
1377 if (comp)
1378 goto end;
1379 else if (explicit) {
1380 ha_alert("config: %s '%s': require an explicit filter declaration to use "
1381 "HTTP compression\n", proxy_type_str(proxy), proxy->id);
Christopher Faulet92d36382015-11-05 13:35:03 +01001382 err++;
1383 goto end;
1384 }
1385
Christopher Faulet27d93c32018-12-15 22:32:02 +01001386 /* Implicit declaration of the compression filter is always the last
1387 * one */
Christopher Faulet443ea1a2016-02-04 13:40:26 +01001388 fconf = calloc(1, sizeof(*fconf));
1389 if (!fconf) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01001390 ha_alert("config: %s '%s': out of memory\n",
1391 proxy_type_str(proxy), proxy->id);
Christopher Faulet92d36382015-11-05 13:35:03 +01001392 err++;
1393 goto end;
1394 }
Christopher Faulet443ea1a2016-02-04 13:40:26 +01001395 fconf->id = http_comp_flt_id;
1396 fconf->conf = NULL;
1397 fconf->ops = &comp_ops;
1398 LIST_ADDQ(&proxy->filter_configs, &fconf->list);
Christopher Faulet92d36382015-11-05 13:35:03 +01001399
1400 end:
1401 return err;
1402}
1403
1404/*
1405 * boolean, returns true if compression is used (either gzip or deflate) in the
1406 * response.
1407 */
Christopher Faulet3d97c902015-12-09 14:59:38 +01001408static int
Christopher Faulet92d36382015-11-05 13:35:03 +01001409smp_fetch_res_comp(const struct arg *args, struct sample *smp, const char *kw,
1410 void *private)
Christopher Faulet3d97c902015-12-09 14:59:38 +01001411{
Willy Tarreaube508f12016-03-10 11:47:01 +01001412 struct http_txn *txn = smp->strm ? smp->strm->txn : NULL;
Christopher Faulet92d36382015-11-05 13:35:03 +01001413
Christopher Faulet3d97c902015-12-09 14:59:38 +01001414 smp->data.type = SMP_T_BOOL;
Christopher Faulet92d36382015-11-05 13:35:03 +01001415 smp->data.u.sint = (txn && (txn->rsp.flags & HTTP_MSGF_COMPRESSING));
Christopher Faulet3d97c902015-12-09 14:59:38 +01001416 return 1;
1417}
1418
Christopher Faulet92d36382015-11-05 13:35:03 +01001419/*
1420 * string, returns algo
1421 */
Christopher Faulet3d97c902015-12-09 14:59:38 +01001422static int
Christopher Faulet92d36382015-11-05 13:35:03 +01001423smp_fetch_res_comp_algo(const struct arg *args, struct sample *smp,
1424 const char *kw, void *private)
Christopher Faulet3d97c902015-12-09 14:59:38 +01001425{
Willy Tarreaube508f12016-03-10 11:47:01 +01001426 struct http_txn *txn = smp->strm ? smp->strm->txn : NULL;
Christopher Faulet92d36382015-11-05 13:35:03 +01001427 struct filter *filter;
1428 struct comp_state *st;
1429
Christopher Faulet03d85532017-09-15 10:14:43 +02001430 if (!txn || !(txn->rsp.flags & HTTP_MSGF_COMPRESSING))
Christopher Faulet3d97c902015-12-09 14:59:38 +01001431 return 0;
1432
Christopher Fauletfcf035c2015-12-03 11:48:03 +01001433 list_for_each_entry(filter, &strm_flt(smp->strm)->filters, list) {
Christopher Faulet443ea1a2016-02-04 13:40:26 +01001434 if (FLT_ID(filter) != http_comp_flt_id)
Christopher Faulet92d36382015-11-05 13:35:03 +01001435 continue;
1436
1437 if (!(st = filter->ctx))
1438 break;
1439
1440 smp->data.type = SMP_T_STR;
1441 smp->flags = SMP_F_CONST;
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001442 smp->data.u.str.area = st->comp_algo->cfg_name;
1443 smp->data.u.str.data = st->comp_algo->cfg_name_len;
Christopher Faulet92d36382015-11-05 13:35:03 +01001444 return 1;
1445 }
1446 return 0;
Christopher Faulet3d97c902015-12-09 14:59:38 +01001447}
1448
1449/* Declare the config parser for "compression" keyword */
1450static struct cfg_kw_list cfg_kws = {ILH, {
1451 { CFG_LISTEN, "compression", parse_compression_options },
1452 { 0, NULL, NULL },
1453 }
1454};
1455
Willy Tarreau0108d902018-11-25 19:14:37 +01001456INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
1457
Christopher Faulet92d36382015-11-05 13:35:03 +01001458/* Declare the filter parser for "compression" keyword */
1459static struct flt_kw_list filter_kws = { "COMP", { }, {
Thierry Fournier3610c392016-04-13 18:27:51 +02001460 { "compression", parse_http_comp_flt, NULL },
1461 { NULL, NULL, NULL },
Christopher Faulet92d36382015-11-05 13:35:03 +01001462 }
1463};
1464
Willy Tarreau0108d902018-11-25 19:14:37 +01001465INITCALL1(STG_REGISTER, flt_register_keywords, &filter_kws);
1466
Christopher Faulet3d97c902015-12-09 14:59:38 +01001467/* Note: must not be declared <const> as its list will be overwritten */
1468static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
Christopher Faulet92d36382015-11-05 13:35:03 +01001469 { "res.comp", smp_fetch_res_comp, 0, NULL, SMP_T_BOOL, SMP_USE_HRSHP },
1470 { "res.comp_algo", smp_fetch_res_comp_algo, 0, NULL, SMP_T_STR, SMP_USE_HRSHP },
1471 { /* END */ },
1472 }
1473};
Christopher Faulet3d97c902015-12-09 14:59:38 +01001474
Willy Tarreau0108d902018-11-25 19:14:37 +01001475INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords);