varnish-cache/bin/varnishd/cache/cache_vary.c
1
/*-
2
 * Copyright (c) 2006-2015 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 * 1. Redistributions of source code must retain the above copyright
11
 *    notice, this list of conditions and the following disclaimer.
12
 * 2. Redistributions in binary form must reproduce the above copyright
13
 *    notice, this list of conditions and the following disclaimer in the
14
 *    documentation and/or other materials provided with the distribution.
15
 *
16
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
20
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
 * SUCH DAMAGE.
27
 *
28
 * Do Vary processing.
29
 *
30
 * When we insert an object into the cache which has a Vary: header,
31
 * we encode a vary matching string containing the headers mentioned
32
 * and their value.
33
 *
34
 * When we match an object in the cache, we check the present request
35
 * against the vary matching string.
36
 *
37
 * The only kind of header-munging we do is leading & trailing space
38
 * removal.  All the potential "q=foo" gymnastics is not worth the
39
 * effort.
40
 *
41
 * The vary matching string has the following format:
42
 *
43
 * Sequence of: {
44
 *      <msb>                   \   Length of header contents.
45
 *      <lsb>                   /
46
 *      <length of header + 1>  \
47
 *      <header>                 \  Same format as argument to http_GetHdr()
48
 *      ':'                      /
49
 *      '\0'                    /
50
 *      <header>                >   Only present if length != 0xffff
51
 * }
52
 *      0xff,                   \   Length field
53
 *      0xff,                   /
54
 *      '\0'                    >   Terminator
55
 */
56
57
#include "config.h"
58
59
#include <stdlib.h>
60
61
#include "cache_varnishd.h"
62
63
#include "vct.h"
64
#include "vend.h"
65
66
static unsigned VRY_Validate(const uint8_t *vary);
67
68
/**********************************************************************
69
 * Create a Vary matching string from a Vary header
70
 *
71
 * Return value:
72
 * <0: Parse error
73
 *  0: No Vary header on object
74
 * >0: Length of Vary matching string in *psb
75
 */
76
77
int
78 2214
VRY_Create(struct busyobj *bo, struct vsb **psb)
79
{
80
        const char *v, *p, *q, *h, *e;
81
        struct vsb *sb, *sbh;
82
        unsigned l;
83 2214
        int error = 0;
84
85 2214
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
86 2214
        CHECK_OBJ_NOTNULL(bo->bereq, HTTP_MAGIC);
87 2214
        CHECK_OBJ_NOTNULL(bo->beresp, HTTP_MAGIC);
88 2214
        AN(psb);
89 2214
        AZ(*psb);
90
91
        /* No Vary: header, no worries */
92 2214
        if (!http_GetHdr(bo->beresp, H_Vary, &v))
93 1830
                return (0);
94
95
        /* For vary matching string */
96 384
        sb = VSB_new_auto();
97 384
        AN(sb);
98
99
        /* For header matching strings */
100 384
        sbh = VSB_new_auto();
101 384
        AN(sbh);
102
103 416
        for (p = v; *p; p++) {
104
105
                /* Find next header-name */
106 416
                if (vct_issp(*p))
107 16
                        continue;
108 4196
                for (q = p; *q && !vct_issp(*q) && *q != ','; q++)
109 3796
                        continue;
110
111 400
                if (q - p > INT8_MAX) {
112 4
                        VSLb(bo->vsl, SLT_Error,
113
                            "Vary header name length exceeded");
114 4
                        error = 1;
115 4
                        break;
116
                }
117
118
                /* Build a header-matching string out of it */
119 396
                VSB_clear(sbh);
120 396
                AZ(VSB_printf(sbh, "%c%.*s:%c",
121
                    (char)(1 + (q - p)), (int)(q - p), p, 0));
122 396
                AZ(VSB_finish(sbh));
123
124 396
                if (http_GetHdr(bo->bereq, VSB_data(sbh), &h)) {
125 364
                        AZ(vct_issp(*h));
126
                        /* Trim trailing space */
127 364
                        e = strchr(h, '\0');
128 728
                        while (e > h && vct_issp(e[-1]))
129 0
                                e--;
130
                        /* Encode two byte length and contents */
131 364
                        l = e - h;
132 364
                        if (l > 0xffff - 1) {
133 0
                                VSLb(bo->vsl, SLT_Error,
134
                                    "Vary header maximum length exceeded");
135 0
                                error = 1;
136 0
                                break;
137
                        }
138
                } else {
139 32
                        e = h;
140 32
                        l = 0xffff;
141
                }
142 396
                AZ(VSB_printf(sb, "%c%c", (int)(l >> 8), (int)(l & 0xff)));
143
                /* Append to vary matching string */
144 396
                AZ(VSB_bcat(sb, VSB_data(sbh), VSB_len(sbh)));
145 396
                if (e != h)
146 362
                        AZ(VSB_bcat(sb, h, e - h));
147
148 798
                while (vct_issp(*q))
149 6
                        q++;
150 396
                if (*q == '\0')
151 374
                        break;
152 22
                if (*q != ',') {
153 6
                        VSLb(bo->vsl, SLT_Error, "Malformed Vary header");
154 6
                        error = 1;
155 6
                        break;
156
                }
157 16
                p = q;
158
        }
159
160 384
        if (error) {
161 10
                VSB_destroy(&sbh);
162 10
                VSB_destroy(&sb);
163 10
                return (-1);
164
        }
165
166
        /* Terminate vary matching string */
167 374
        VSB_printf(sb, "%c%c%c", 0xff, 0xff, 0);
168
169 374
        VSB_destroy(&sbh);
170 374
        AZ(VSB_finish(sb));
171 374
        *psb = sb;
172 374
        return (VSB_len(sb));
173
}
174
175
/*
176
 * Find length of a vary entry
177
 */
178
static unsigned
179 7636
VRY_Len(const uint8_t *p)
180
{
181 7636
        unsigned l = vbe16dec(p);
182
183 7636
        return (2 + p[2] + 2 + (l == 0xffff ? 0 : l));
184
}
185
186
/*
187
 * Compare two vary entries
188
 */
189
static int
190 5672
vry_cmp(const uint8_t *v1, const uint8_t *v2)
191
{
192 5672
        unsigned retval = 0;
193
194 5672
        if (!memcmp(v1, v2, VRY_Len(v1))) {
195
                /* Same same */
196 108
                retval = 0;
197 5564
        } else if (memcmp(v1 + 2, v2 + 2, v1[2] + 2)) {
198
                /* Different header */
199 326
                retval = 1;
200 10474
        } else if (cache_param->http_gzip_support &&
201 5236
            !strcasecmp(H_Accept_Encoding, (const char*) v1 + 2)) {
202
                /*
203
                 * If we do gzip processing, we do not vary on Accept-Encoding,
204
                 * because we want everybody to get the gzip'ed object, and
205
                 * varnish will gunzip as necessary.  We implement the skip at
206
                 * check time, rather than create time, so that object in
207
                 * persistent storage can be used with either setting of
208
                 * http_gzip_support.
209
                 */
210 30
                retval = 0;
211
        } else {
212
                /* Same header, different content */
213 5208
                retval = 2;
214
        }
215 5672
        return (retval);
216
}
217
218
/**********************************************************************
219
 * Prepare predictive vary string
220
 *
221
 * XXX: Strictly speaking vary_b and vary_e could be replaced with
222
 * XXX: req->ws->{f,r}.   Space in struct req vs. code-readability...
223
 */
224
225
void
226 3596
VRY_Prep(struct req *req)
227
{
228 3596
        if (req->hash_objhead == NULL) {
229
                /* Not a waiting list return */
230 3542
                AZ(req->vary_b);
231 3542
                AZ(req->vary_l);
232 3542
                AZ(req->vary_e);
233 3542
                (void)WS_Reserve(req->ws, 0);
234
        } else {
235 54
                AN(req->ws->r);
236
        }
237 3596
        req->vary_b = (void*)req->ws->f;
238 3596
        req->vary_e = (void*)req->ws->r;
239 3596
        if (req->vary_b + 2 < req->vary_e)
240 3596
                req->vary_b[2] = '\0';
241 3596
}
242
243
void
244 42
VRY_Clear(struct req *req)
245
{
246
247 42
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
248 42
        if (req->vary_b != NULL)
249 4
                free(req->vary_b);
250 42
        req->vary_b = NULL;
251 42
        AZ(req->vary_e);
252 42
        AZ(req->vary_l);
253 42
}
254
255
/**********************************************************************
256
 * Finish predictive vary processing
257
 */
258
259
void
260 3542
VRY_Finish(struct req *req, enum vry_finish_flag flg)
261
{
262 3542
        uint8_t *p = NULL;
263
264 3542
        (void)VRY_Validate(req->vary_b);
265 3542
        if (flg == KEEP && req->vary_l != NULL) {
266 196
                p = malloc(req->vary_l - req->vary_b);
267 196
                if (p != NULL) {
268 196
                        memcpy(p, req->vary_b, req->vary_l - req->vary_b);
269 196
                        (void)VRY_Validate(p);
270
                }
271
        }
272 3542
        WS_Release(req->ws, 0);
273 3542
        req->vary_l = NULL;
274 3542
        req->vary_e = NULL;
275 3542
        req->vary_b = p;
276 3542
}
277
278
/**********************************************************************
279
 * Match vary strings, and build a new cached string if possible.
280
 *
281
 * Return zero if there is certainly no match.
282
 * Return non-zero if there could be a match or if we couldn't tell.
283
 */
284
285
int
286 5332
VRY_Match(struct req *req, const uint8_t *vary)
287
{
288 5332
        uint8_t *vsp = req->vary_b;
289
        const char *h, *e;
290
        unsigned lh, ln;
291 5332
        int i, oflo = 0;
292
293 5332
        AN(vsp);
294 5332
        AN(vary);
295 10802
        while (vary[2]) {
296 5348
                if (vsp + 2 >= req->vary_e) {
297
                        /*
298
                         * Too little workspace to find out
299
                         */
300 0
                        oflo = 1;
301 0
                        break;
302
                }
303 5348
                i = vry_cmp(vary, vsp);
304 5348
                if (i == 1) {
305
                        /*
306
                         * Different header, build a new entry,
307
                         * then compare again with that new entry.
308
                         */
309
310 326
                        ln = 2 + vary[2] + 2;
311 326
                        i = http_GetHdr(req->http, (const char*)(vary+2), &h);
312 326
                        if (i) {
313
                                /* Trim trailing space */
314 272
                                e = strchr(h, '\0');
315 544
                                while (e > h && vct_issp(e[-1]))
316 0
                                        e--;
317 272
                                lh = e - h;
318 272
                                assert(lh < 0xffff);
319 272
                                ln += lh;
320
                        } else {
321 54
                                e = h = NULL;
322 54
                                lh = 0xffff;
323
                        }
324
325 326
                        if (vsp + ln + 3 >= req->vary_e) {
326
                                /*
327
                                 * Not enough space to build new entry
328
                                 * and put terminator behind it.
329
                                 */
330 2
                                oflo = 1;
331 2
                                break;
332
                        }
333
334 324
                        vbe16enc(vsp, (uint16_t)lh);
335 324
                        memcpy(vsp + 2, vary + 2, vary[2] + 2);
336 324
                        if (h != NULL)
337 270
                                memcpy(vsp + 2 + vsp[2] + 2, h, lh);
338 324
                        vsp[ln] = 0xff;
339 324
                        vsp[ln + 1] = 0xff;
340 324
                        vsp[ln + 2] = 0;
341 324
                        (void)VRY_Validate(vsp);
342 324
                        req->vary_l = vsp + ln + 3;
343
344 324
                        i = vry_cmp(vary, vsp);
345 324
                        assert(i == 0 || i == 2);
346
                }
347 5346
                if (i == 0) {
348
                        /* Same header, same contents */
349 138
                        vsp += VRY_Len(vsp);
350 138
                        vary += VRY_Len(vary);
351 5208
                } else if (i == 2) {
352
                        /* Same header, different contents, cannot match */
353 5208
                        return (0);
354
                }
355
        }
356 124
        if (oflo) {
357 2
                vsp = req->vary_b;
358 2
                req->vary_l = NULL;
359 2
                if (vsp + 2 < req->vary_e) {
360 2
                        vsp[0] = 0xff;
361 2
                        vsp[1] = 0xff;
362 2
                        vsp[2] = 0;
363
                }
364 2
                return (0);
365
        } else {
366 122
                return (1);
367
        }
368
}
369
370
/*
371
 * Check the validity of a Vary string and return its total length
372
 */
373
374
static unsigned
375 4062
VRY_Validate(const uint8_t *vary)
376
{
377 4062
        unsigned retval = 0;
378
379 8968
        while (vary[2] != 0) {
380 844
                assert(strlen((const char*)vary+3) == vary[2]);
381 844
                retval += VRY_Len(vary);
382 844
                vary += VRY_Len(vary);
383
        }
384 4062
        return (retval + 3);
385
}