| | varnish-cache/bin/varnishd/cache/cache_vrt_filter.c |
0 |
|
/*- |
1 |
|
* Copyright (c) 2006 Verdens Gang AS |
2 |
|
* Copyright (c) 2006-2016 Varnish Software AS |
3 |
|
* All rights reserved. |
4 |
|
* |
5 |
|
* Author: Poul-Henning Kamp <phk@phk.freebsd.dk> |
6 |
|
* |
7 |
|
* SPDX-License-Identifier: BSD-2-Clause |
8 |
|
* |
9 |
|
* Redistribution and use in source and binary forms, with or without |
10 |
|
* modification, are permitted provided that the following conditions |
11 |
|
* are met: |
12 |
|
* 1. Redistributions of source code must retain the above copyright |
13 |
|
* notice, this list of conditions and the following disclaimer. |
14 |
|
* 2. Redistributions in binary form must reproduce the above copyright |
15 |
|
* notice, this list of conditions and the following disclaimer in the |
16 |
|
* documentation and/or other materials provided with the distribution. |
17 |
|
* |
18 |
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
19 |
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
20 |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
21 |
|
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE |
22 |
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
23 |
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
24 |
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
25 |
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
26 |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
27 |
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
28 |
|
* SUCH DAMAGE. |
29 |
|
* |
30 |
|
*/ |
31 |
|
|
32 |
|
#include "config.h" |
33 |
|
|
34 |
|
#include <stdio.h> |
35 |
|
#include <stdint.h> |
36 |
|
#include <stdlib.h> |
37 |
|
|
38 |
|
#include "cache_varnishd.h" |
39 |
|
#include "cache_vcl.h" |
40 |
|
#include "vrt_obj.h" |
41 |
|
|
42 |
|
#include "vct.h" |
43 |
|
|
44 |
|
#include "cache_filter.h" |
45 |
|
|
46 |
|
/*-------------------------------------------------------------------- |
47 |
|
*/ |
48 |
|
|
49 |
|
struct vfilter { |
50 |
|
unsigned magic; |
51 |
|
#define VFILTER_MAGIC 0xd40894e9 |
52 |
|
const struct vfp *vfp; |
53 |
|
const struct vdp *vdp; |
54 |
|
const char *name; |
55 |
|
int nlen; |
56 |
|
VTAILQ_ENTRY(vfilter) list; |
57 |
|
}; |
58 |
|
|
59 |
|
static struct vfilter_head vrt_filters = |
60 |
|
VTAILQ_HEAD_INITIALIZER(vrt_filters); |
61 |
|
|
62 |
|
static const char * |
63 |
187226 |
is_dup_filter(const struct vfilter_head *head, const struct vfp * vfp, |
64 |
|
const struct vdp *vdp, const char *name) |
65 |
|
{ |
66 |
|
struct vfilter *vp; |
67 |
849742 |
VTAILQ_FOREACH(vp, head, list) { |
68 |
662541 |
if (vfp != NULL && vp->vfp != NULL) { |
69 |
234120 |
if (vp->vfp == vfp) |
70 |
25 |
return ("VFP already registered"); |
71 |
234095 |
if (!strcasecmp(vp->name, name)) |
72 |
0 |
return ("VFP name already used"); |
73 |
234095 |
} |
74 |
662516 |
if (vdp != NULL && vp->vdp != NULL) { |
75 |
83466 |
if (vp->vdp == vdp) |
76 |
0 |
return ("VDP already registered"); |
77 |
83466 |
if (!strcasecmp(vp->name, name)) |
78 |
0 |
return ("VDP name already used"); |
79 |
83466 |
} |
80 |
662516 |
} |
81 |
187201 |
return (NULL); |
82 |
187226 |
} |
83 |
|
|
84 |
|
static const char * |
85 |
182401 |
vrt_addfilter(VRT_CTX, const struct vfp *vfp, const struct vdp *vdp) |
86 |
|
{ |
87 |
|
struct vfilter *vp; |
88 |
182401 |
struct vfilter_head *hd = &vrt_filters; |
89 |
182401 |
const char *err, *name = NULL; |
90 |
|
|
91 |
182401 |
CHECK_OBJ_ORNULL(ctx, VRT_CTX_MAGIC); |
92 |
182401 |
assert(vfp != NULL || vdp != NULL); |
93 |
182401 |
assert(vfp == NULL || vfp->name != NULL); |
94 |
182401 |
assert(vdp == NULL || vdp->name != NULL); |
95 |
182401 |
assert(vfp == NULL || vdp == NULL || !strcasecmp(vfp->name, vdp->name)); |
96 |
182401 |
if (vfp != NULL) |
97 |
113410 |
name = vfp->name; |
98 |
68991 |
else if (vdp != NULL) |
99 |
68991 |
name = vdp->name; |
100 |
182401 |
AN(name); |
101 |
|
|
102 |
182401 |
err = is_dup_filter(hd, vfp, vdp, name); |
103 |
182401 |
if (err != NULL) { |
104 |
0 |
if (ctx != NULL) |
105 |
0 |
VRT_fail(ctx, "%s: %s (global)", name, err); |
106 |
0 |
return (err); |
107 |
|
} |
108 |
182401 |
if (ctx != NULL) { |
109 |
4825 |
ASSERT_CLI(); |
110 |
4825 |
CHECK_OBJ_NOTNULL(ctx->vcl, VCL_MAGIC); |
111 |
4825 |
hd = &ctx->vcl->filters; |
112 |
4825 |
err = is_dup_filter(hd, vfp, vdp, name); |
113 |
4825 |
if (err != NULL) { |
114 |
25 |
VRT_fail(ctx, "%s: %s (per-vcl)", name, err); |
115 |
25 |
return (err); |
116 |
|
} |
117 |
4800 |
} |
118 |
|
|
119 |
182376 |
ALLOC_OBJ(vp, VFILTER_MAGIC); |
120 |
182376 |
AN(vp); |
121 |
182376 |
vp->vfp = vfp; |
122 |
182376 |
vp->vdp = vdp; |
123 |
182376 |
vp->name = name; |
124 |
182376 |
vp->nlen = strlen(name); |
125 |
182376 |
VTAILQ_INSERT_TAIL(hd, vp, list); |
126 |
182376 |
return (err); |
127 |
182401 |
} |
128 |
|
|
129 |
|
const char * |
130 |
4825 |
VRT_AddFilter(VRT_CTX, const struct vfp *vfp, const struct vdp *vdp) |
131 |
|
{ |
132 |
|
|
133 |
4825 |
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); |
134 |
4825 |
return (vrt_addfilter(ctx, vfp, vdp)); |
135 |
|
} |
136 |
|
|
137 |
|
void |
138 |
0 |
VRT_AddVFP(VRT_CTX, const struct vfp *filter) |
139 |
|
{ |
140 |
0 |
AZ(VRT_AddFilter(ctx, filter, NULL)); |
141 |
0 |
} |
142 |
|
|
143 |
|
void |
144 |
0 |
VRT_AddVDP(VRT_CTX, const struct vdp *filter) |
145 |
|
{ |
146 |
0 |
AZ(VRT_AddFilter(ctx, NULL, filter)); |
147 |
0 |
} |
148 |
|
|
149 |
|
void |
150 |
1150 |
VRT_RemoveFilter(VRT_CTX, const struct vfp *vfp, const struct vdp *vdp) |
151 |
|
{ |
152 |
|
struct vfilter *vp; |
153 |
|
struct vfilter_head *hd; |
154 |
|
|
155 |
1150 |
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); |
156 |
1150 |
CHECK_OBJ_NOTNULL(ctx->vcl, VCL_MAGIC); |
157 |
1150 |
hd = &ctx->vcl->filters; |
158 |
1150 |
assert(vfp != NULL || vdp != NULL); |
159 |
1150 |
assert(vfp == NULL || vfp->name != NULL); |
160 |
1150 |
assert(vdp == NULL || vdp->name != NULL); |
161 |
1150 |
assert(vfp == NULL || vdp == NULL || !strcasecmp(vfp->name, vdp->name)); |
162 |
|
|
163 |
1150 |
ASSERT_CLI(); |
164 |
1150 |
VTAILQ_FOREACH(vp, hd, list) { |
165 |
1150 |
CHECK_OBJ_NOTNULL(vp, VFILTER_MAGIC); |
166 |
1150 |
if (vp->vfp == vfp && vp->vdp == vdp) |
167 |
1150 |
break; |
168 |
0 |
} |
169 |
1150 |
AN(vp); |
170 |
1150 |
assert(vfp == NULL || !strcasecmp(vfp->name, vp->name)); |
171 |
1150 |
assert(vdp == NULL || !strcasecmp(vdp->name, vp->name)); |
172 |
1150 |
VTAILQ_REMOVE(hd, vp, list); |
173 |
1150 |
FREE_OBJ(vp); |
174 |
1150 |
} |
175 |
|
|
176 |
|
void |
177 |
0 |
VRT_RemoveVFP(VRT_CTX, const struct vfp *filter) |
178 |
|
{ |
179 |
|
|
180 |
0 |
VRT_RemoveFilter(ctx, filter, NULL); |
181 |
0 |
} |
182 |
|
|
183 |
|
void |
184 |
0 |
VRT_RemoveVDP(VRT_CTX, const struct vdp *filter) |
185 |
|
{ |
186 |
|
|
187 |
0 |
VRT_RemoveFilter(ctx, NULL, filter); |
188 |
0 |
} |
189 |
|
|
190 |
|
static const struct vfilter vfilter_error[1]; |
191 |
|
|
192 |
|
// XXX: idea(fgs): Allow filters (...) arguments in the list |
193 |
|
static const struct vfilter * |
194 |
153532 |
vcl_filter_list_iter(int want_vfp, const struct vfilter_head *h1, |
195 |
|
const struct vfilter_head *h2, const char **flp) |
196 |
|
{ |
197 |
|
const char *fl, *q; |
198 |
|
const struct vfilter *vp; |
199 |
|
|
200 |
153532 |
AN(h1); |
201 |
153532 |
AN(h2); |
202 |
153532 |
AN(flp); |
203 |
|
|
204 |
153532 |
fl = *flp; |
205 |
153532 |
AN(fl); |
206 |
|
|
207 |
180004 |
while (vct_isspace(*fl)) |
208 |
26472 |
fl++; |
209 |
153532 |
if (*fl == '\0') { |
210 |
126808 |
*flp = NULL; |
211 |
126808 |
return (NULL); |
212 |
|
} |
213 |
218011 |
for (q = fl; *q && !vct_isspace(*q); q++) |
214 |
191287 |
continue; |
215 |
26724 |
*flp = q; |
216 |
153912 |
VTAILQ_FOREACH(vp, h1, list) { |
217 |
147438 |
if (want_vfp && vp->vfp == NULL) |
218 |
375 |
continue; |
219 |
147063 |
else if (!want_vfp && vp->vdp == NULL) |
220 |
73616 |
continue; |
221 |
73447 |
if (vp->nlen == q - fl && !memcmp(fl, vp->name, vp->nlen)) |
222 |
20250 |
return (vp); |
223 |
53197 |
} |
224 |
12723 |
VTAILQ_FOREACH(vp, h2, list) { |
225 |
12673 |
if (want_vfp && vp->vfp == NULL) |
226 |
50 |
continue; |
227 |
12623 |
else if (!want_vfp && vp->vdp == NULL) |
228 |
0 |
continue; |
229 |
12623 |
if (vp->nlen == q - fl && !memcmp(fl, vp->name, vp->nlen)) |
230 |
6424 |
return (vp); |
231 |
6199 |
} |
232 |
50 |
*flp = fl; |
233 |
50 |
return (vfilter_error); |
234 |
153532 |
} |
235 |
|
|
236 |
|
int |
237 |
46423 |
VCL_StackVFP(struct vfp_ctx *vc, const struct vcl *vcl, const char *fl) |
238 |
|
{ |
239 |
|
const struct vfilter *vp; |
240 |
|
|
241 |
46423 |
AN(fl); |
242 |
46423 |
VSLbs(vc->wrk->vsl, SLT_Filters, TOSTRAND(fl)); |
243 |
|
|
244 |
57823 |
while (1) { |
245 |
57823 |
vp = vcl_filter_list_iter(1, &vrt_filters, &vcl->filters, &fl); |
246 |
57823 |
if (vp == NULL) |
247 |
45823 |
return (0); |
248 |
12000 |
if (vp == vfilter_error) |
249 |
50 |
return (VFP_Error(vc, "Filter '...%s' not found", fl)); |
250 |
11950 |
if (VFP_Push(vc, vp->vfp) == NULL) |
251 |
550 |
return (-1); |
252 |
|
} |
253 |
46423 |
} |
254 |
|
|
255 |
|
int |
256 |
84134 |
VCL_StackVDP(struct req *req, const struct vcl *vcl, const char *fl) |
257 |
|
{ |
258 |
|
const struct vfilter *vp; |
259 |
|
struct vrt_ctx ctx[1]; |
260 |
|
|
261 |
84134 |
AN(fl); |
262 |
84134 |
VSLbs(req->vsl, SLT_Filters, TOSTRAND(fl)); |
263 |
84134 |
INIT_OBJ(ctx, VRT_CTX_MAGIC); |
264 |
84134 |
VCL_Req2Ctx(ctx, req); |
265 |
|
|
266 |
95709 |
while (1) { |
267 |
95709 |
vp = vcl_filter_list_iter(0, &vrt_filters, &vcl->filters, &fl); |
268 |
95709 |
if (vp == NULL) |
269 |
80984 |
return (0); |
270 |
14725 |
if (vp == vfilter_error) { |
271 |
0 |
VSLb(req->vsl, SLT_Error, |
272 |
0 |
"Filter '...%s' not found", fl); |
273 |
0 |
return (-1); |
274 |
|
} |
275 |
14725 |
if (VDP_Push(ctx, req->vdc, req->ws, vp->vdp, NULL)) |
276 |
3150 |
return (-1); |
277 |
|
} |
278 |
84134 |
} |
279 |
|
|
280 |
|
void |
281 |
22197 |
VCL_VRT_Init(void) |
282 |
|
{ |
283 |
22197 |
AZ(vrt_addfilter(NULL, &VFP_testgunzip, NULL)); |
284 |
22197 |
AZ(vrt_addfilter(NULL, &VFP_gunzip, NULL)); |
285 |
22197 |
AZ(vrt_addfilter(NULL, &VFP_gzip, NULL)); |
286 |
22197 |
AZ(vrt_addfilter(NULL, &VFP_esi, NULL)); |
287 |
22197 |
AZ(vrt_addfilter(NULL, &VFP_esi_gzip, NULL)); |
288 |
22197 |
AZ(vrt_addfilter(NULL, NULL, &VDP_esi)); |
289 |
22197 |
AZ(vrt_addfilter(NULL, NULL, &VDP_gunzip)); |
290 |
22197 |
AZ(vrt_addfilter(NULL, NULL, &VDP_range)); |
291 |
22197 |
} |
292 |
|
|
293 |
|
/*-------------------------------------------------------------------- |
294 |
|
*/ |
295 |
|
|
296 |
|
typedef void filter_list_t(void *, struct vsb *vsb); |
297 |
|
|
298 |
|
static const char * |
299 |
114522 |
filter_on_ws(struct ws *ws, filter_list_t *func, void *arg) |
300 |
|
{ |
301 |
|
struct vsb vsb[1]; |
302 |
|
const char *p; |
303 |
|
|
304 |
114522 |
AN(func); |
305 |
114522 |
AN(arg); |
306 |
114522 |
WS_VSB_new(vsb, ws); |
307 |
114522 |
func(arg, vsb); |
308 |
114522 |
p = WS_VSB_finish(vsb, ws, NULL); |
309 |
114522 |
if (p == NULL) |
310 |
575 |
p = ""; |
311 |
114522 |
return (p); |
312 |
|
} |
313 |
|
|
314 |
|
/*-------------------------------------------------------------------- |
315 |
|
*/ |
316 |
|
|
317 |
|
static void v_matchproto_(filter_list_t) |
318 |
29525 |
vbf_default_filter_list(void *arg, struct vsb *vsb) |
319 |
|
{ |
320 |
|
const struct busyobj *bo; |
321 |
|
const char *p; |
322 |
29525 |
int do_gzip, do_gunzip, is_gzip = 0, is_gunzip = 0; |
323 |
|
|
324 |
29525 |
CAST_OBJ_NOTNULL(bo, arg, BUSYOBJ_MAGIC); |
325 |
|
|
326 |
29525 |
do_gzip = bo->do_gzip; |
327 |
29525 |
do_gunzip = bo->do_gunzip; |
328 |
|
|
329 |
|
/* |
330 |
|
* The VCL variables beresp.do_g[un]zip tells us how we want the |
331 |
|
* object processed before it is stored. |
332 |
|
* |
333 |
|
* The backend Content-Encoding header tells us what we are going |
334 |
|
* to receive, which we classify in the following three classes: |
335 |
|
* |
336 |
|
* "Content-Encoding: gzip" --> object is gzip'ed. |
337 |
|
* no Content-Encoding --> object is not gzip'ed. |
338 |
|
* anything else --> do nothing wrt gzip |
339 |
|
*/ |
340 |
|
|
341 |
|
/* No body -> done */ |
342 |
29525 |
if (bo->htc->body_status == BS_NONE || bo->htc->content_length == 0) |
343 |
6 |
return; |
344 |
|
|
345 |
29523 |
if (!cache_param->http_gzip_support) |
346 |
100 |
do_gzip = do_gunzip = 0; |
347 |
|
|
348 |
29523 |
if (http_GetHdr(bo->beresp, H_Content_Encoding, &p)) |
349 |
5000 |
is_gzip = !strcasecmp(p, "gzip"); |
350 |
|
else |
351 |
24523 |
is_gunzip = 1; |
352 |
|
|
353 |
|
/* We won't gunzip unless it is gzip'ed */ |
354 |
29523 |
if (do_gunzip && !is_gzip) |
355 |
50 |
do_gunzip = 0; |
356 |
|
|
357 |
|
/* We wont gzip unless if it already is gzip'ed */ |
358 |
29523 |
if (do_gzip && !is_gunzip) |
359 |
0 |
do_gzip = 0; |
360 |
|
|
361 |
29523 |
if (do_gunzip || (is_gzip && bo->do_esi)) |
362 |
3975 |
VSB_cat(vsb, " gunzip"); |
363 |
|
|
364 |
29523 |
if (bo->do_esi && (do_gzip || (is_gzip && !do_gunzip))) { |
365 |
4050 |
VSB_cat(vsb, " esi_gzip"); |
366 |
4050 |
return; |
367 |
|
} |
368 |
|
|
369 |
25473 |
if (bo->do_esi) { |
370 |
3675 |
VSB_cat(vsb, " esi"); |
371 |
3675 |
return; |
372 |
|
} |
373 |
|
|
374 |
21798 |
if (do_gzip) |
375 |
400 |
VSB_cat(vsb, " gzip"); |
376 |
|
|
377 |
21798 |
if (is_gzip && !do_gunzip) |
378 |
1025 |
VSB_cat(vsb, " testgunzip"); |
379 |
29523 |
} |
380 |
|
|
381 |
|
const char * |
382 |
29522 |
VBF_Get_Filter_List(struct busyobj *bo) |
383 |
|
{ |
384 |
|
|
385 |
29522 |
CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); |
386 |
29522 |
return (filter_on_ws(bo->ws, vbf_default_filter_list, bo)); |
387 |
|
} |
388 |
|
|
389 |
|
/*-------------------------------------------------------------------- |
390 |
|
*/ |
391 |
|
|
392 |
|
static void v_matchproto_(filter_list_t) |
393 |
85000 |
resp_default_filter_list(void *arg, struct vsb *vsb) |
394 |
|
{ |
395 |
|
struct req *req; |
396 |
|
|
397 |
85000 |
CAST_OBJ_NOTNULL(req, arg, REQ_MAGIC); |
398 |
|
|
399 |
85000 |
if (!req->disable_esi && req->objcore != NULL && |
400 |
84621 |
ObjHasAttr(req->wrk, req->objcore, OA_ESIDATA)) |
401 |
5550 |
VSB_cat(vsb, " esi"); |
402 |
|
|
403 |
91475 |
if (cache_param->http_gzip_support && |
404 |
84795 |
req->objcore != NULL && |
405 |
84745 |
ObjCheckFlag(req->wrk, req->objcore, OF_GZIPED) && |
406 |
6475 |
!RFC2616_Req_Gzip(req->http)) |
407 |
2150 |
VSB_cat(vsb, " gunzip"); |
408 |
|
|
409 |
155552 |
if (cache_param->http_range_support && |
410 |
84904 |
http_GetStatus(req->resp) == 200 && |
411 |
70552 |
http_GetHdr(req->http, H_Range, NULL)) |
412 |
1425 |
VSB_cat(vsb, " range"); |
413 |
85000 |
} |
414 |
|
|
415 |
|
const char * |
416 |
85003 |
resp_Get_Filter_List(struct req *req) |
417 |
|
{ |
418 |
85003 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
419 |
85003 |
return (filter_on_ws(req->ws, resp_default_filter_list, req)); |
420 |
|
} |
421 |
|
|
422 |
|
/*--------------------------------------------------------------------*/ |
423 |
|
|
424 |
|
#define FILTER_VAR(vcl, in, func, fld) \ |
425 |
|
VCL_STRING \ |
426 |
|
VRT_r_##vcl##_filters(VRT_CTX) \ |
427 |
|
{ \ |
428 |
|
\ |
429 |
|
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); \ |
430 |
|
if (ctx->in->fld != NULL) \ |
431 |
|
return(ctx->in->fld); \ |
432 |
|
return (func(ctx->in)); \ |
433 |
|
} \ |
434 |
|
\ |
435 |
|
VCL_VOID \ |
436 |
|
VRT_l_##vcl##_filters(VRT_CTX, const char *str, VCL_STRANDS s) \ |
437 |
|
{ \ |
438 |
|
const char *b; \ |
439 |
|
\ |
440 |
|
(void)str; \ |
441 |
|
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); \ |
442 |
|
b = VRT_StrandsWS(ctx->in->ws, str, s); \ |
443 |
|
if (b == NULL) \ |
444 |
|
WS_MarkOverflow(ctx->in->ws); \ |
445 |
|
else \ |
446 |
|
ctx->in->fld = b; \ |
447 |
|
} |
448 |
|
|
449 |
1050 |
FILTER_VAR(beresp, bo, VBF_Get_Filter_List, vfp_filter_list) |
450 |
13549 |
FILTER_VAR(resp, req, resp_Get_Filter_List, vdp_filter_list) |