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 1015
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 1015
        int error = 0;
84
85 1015
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
86 1015
        CHECK_OBJ_NOTNULL(bo->bereq, HTTP_MAGIC);
87 1015
        CHECK_OBJ_NOTNULL(bo->beresp, HTTP_MAGIC);
88 1015
        AN(psb);
89 1015
        AZ(*psb);
90
91
        /* No Vary: header, no worries */
92 1015
        if (!http_GetHdr(bo->beresp, H_Vary, &v))
93 831
                return (0);
94
95
        /* For vary matching string */
96 184
        sb = VSB_new_auto();
97 184
        AN(sb);
98
99
        /* For header matching strings */
100 184
        sbh = VSB_new_auto();
101 184
        AN(sbh);
102
103 200
        for (p = v; *p; p++) {
104
105
                /* Find next header-name */
106 200
                if (vct_issp(*p))
107 8
                        continue;
108 1982
                for (q = p; *q && !vct_issp(*q) && *q != ','; q++)
109 1790
                        continue;
110
111 192
                if (q - p > INT8_MAX) {
112 2
                        VSLb(bo->vsl, SLT_Error,
113
                            "Vary header name length exceeded");
114 2
                        error = 1;
115 2
                        break;
116
                }
117
118
                /* Build a header-matching string out of it */
119 190
                VSB_clear(sbh);
120 190
                AZ(VSB_printf(sbh, "%c%.*s:%c",
121
                    (char)(1 + (q - p)), (int)(q - p), p, 0));
122 190
                AZ(VSB_finish(sbh));
123
124 190
                if (http_GetHdr(bo->bereq, VSB_data(sbh), &h)) {
125 177
                        AZ(vct_issp(*h));
126
                        /* Trim trailing space */
127 177
                        e = strchr(h, '\0');
128 354
                        while (e > h && vct_issp(e[-1]))
129 0
                                e--;
130
                        /* Encode two byte length and contents */
131 177
                        l = e - h;
132 177
                        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 13
                        e = h;
140 13
                        l = 0xffff;
141
                }
142 190
                AZ(VSB_printf(sb, "%c%c", (int)(l >> 8), (int)(l & 0xff)));
143
                /* Append to vary matching string */
144 190
                AZ(VSB_bcat(sb, VSB_data(sbh), VSB_len(sbh)));
145 190
                if (e != h)
146 176
                        AZ(VSB_bcat(sb, h, e - h));
147
148 383
                while (vct_issp(*q))
149 3
                        q++;
150 190
                if (*q == '\0')
151 179
                        break;
152 11
                if (*q != ',') {
153 3
                        VSLb(bo->vsl, SLT_Error, "Malformed Vary header");
154 3
                        error = 1;
155 3
                        break;
156
                }
157 8
                p = q;
158
        }
159
160 184
        if (error) {
161 5
                VSB_destroy(&sbh);
162 5
                VSB_destroy(&sb);
163 5
                return (-1);
164
        }
165
166
        /* Terminate vary matching string */
167 179
        VSB_printf(sb, "%c%c%c", 0xff, 0xff, 0);
168
169 179
        VSB_destroy(&sbh);
170 179
        AZ(VSB_finish(sb));
171 179
        *psb = sb;
172 179
        return (VSB_len(sb));
173
}
174
175
/*
176
 * Find length of a vary entry
177
 */
178
static unsigned
179 3790
VRY_Len(const uint8_t *p)
180
{
181 3790
        unsigned l = vbe16dec(p);
182
183 3790
        return (2 + p[2] + 2 + (l == 0xffff ? 0 : l));
184
}
185
186
/*
187
 * Compare two vary entries
188
 */
189
static int
190 2828
vry_cmp(const uint8_t *v1, const uint8_t *v2)
191
{
192 2828
        unsigned retval = 0;
193
194 2828
        if (!memcmp(v1, v2, VRY_Len(v1))) {
195
                /* Same same */
196 54
                retval = 0;
197 2774
        } else if (memcmp(v1 + 2, v2 + 2, v1[2] + 2)) {
198
                /* Different header */
199 160
                retval = 1;
200 5228
        } else if (cache_param->http_gzip_support &&
201 2614
            !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 15
                retval = 0;
211
        } else {
212
                /* Same header, different content */
213 2599
                retval = 2;
214
        }
215 2828
        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 1550
VRY_Prep(struct req *req)
227
{
228 1550
        if (req->hash_objhead == NULL) {
229
                /* Not a waiting list return */
230 1529
                AZ(req->vary_b);
231 1529
                AZ(req->vary_l);
232 1529
                AZ(req->vary_e);
233 1529
                (void)WS_Reserve(req->ws, 0);
234
        } else {
235 21
                AN(req->ws->r);
236
        }
237 1550
        req->vary_b = (void*)req->ws->f;
238 1550
        req->vary_e = (void*)req->ws->r;
239 1550
        if (req->vary_b + 2 < req->vary_e)
240 1550
                req->vary_b[2] = '\0';
241 1550
}
242
243
void
244 15
VRY_Clear(struct req *req)
245
{
246
247 15
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
248 15
        if (req->vary_b != NULL)
249 2
                free(req->vary_b);
250 15
        req->vary_b = NULL;
251 15
        AZ(req->vary_e);
252 15
        AZ(req->vary_l);
253 15
}
254
255
/**********************************************************************
256
 * Finish predictive vary processing
257
 */
258
259
void
260 1529
VRY_Finish(struct req *req, enum vry_finish_flag flg)
261
{
262 1529
        uint8_t *p = NULL;
263
264 1529
        (void)VRY_Validate(req->vary_b);
265 1529
        if (flg == KEEP && req->vary_l != NULL) {
266 94
                p = malloc(req->vary_l - req->vary_b);
267 94
                if (p != NULL) {
268 94
                        memcpy(p, req->vary_b, req->vary_l - req->vary_b);
269 94
                        (void)VRY_Validate(p);
270
                }
271
        }
272 1529
        WS_Release(req->ws, 0);
273 1529
        req->vary_l = NULL;
274 1529
        req->vary_e = NULL;
275 1529
        req->vary_b = p;
276 1529
}
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 2661
VRY_Match(struct req *req, const uint8_t *vary)
287
{
288 2661
        uint8_t *vsp = req->vary_b;
289
        const char *h, *e;
290
        unsigned lh, ln;
291 2661
        int i, oflo = 0;
292
293 2661
        AN(vsp);
294 2661
        AN(vary);
295 5391
        while (vary[2]) {
296 2669
                if (vsp + 2 >= req->vary_e) {
297
                        /*
298
                         * Too little workspace to find out
299
                         */
300 0
                        oflo = 1;
301 0
                        break;
302
                }
303 2669
                i = vry_cmp(vary, vsp);
304 2669
                if (i == 1) {
305
                        /*
306
                         * Different header, build a new entry,
307
                         * then compare again with that new entry.
308
                         */
309
310 160
                        ln = 2 + vary[2] + 2;
311 160
                        i = http_GetHdr(req->http, (const char*)(vary+2), &h);
312 160
                        if (i) {
313
                                /* Trim trailing space */
314 134
                                e = strchr(h, '\0');
315 268
                                while (e > h && vct_issp(e[-1]))
316 0
                                        e--;
317 134
                                lh = e - h;
318 134
                                assert(lh < 0xffff);
319 134
                                ln += lh;
320
                        } else {
321 26
                                e = h = NULL;
322 26
                                lh = 0xffff;
323
                        }
324
325 160
                        if (vsp + ln + 3 >= req->vary_e) {
326
                                /*
327
                                 * Not enough space to build new entry
328
                                 * and put terminator behind it.
329
                                 */
330 1
                                oflo = 1;
331 1
                                break;
332
                        }
333
334 159
                        vbe16enc(vsp, (uint16_t)lh);
335 159
                        memcpy(vsp + 2, vary + 2, vary[2] + 2);
336 159
                        if (h != NULL)
337 133
                                memcpy(vsp + 2 + vsp[2] + 2, h, lh);
338 159
                        vsp[ln + 0] = 0xff;
339 159
                        vsp[ln + 1] = 0xff;
340 159
                        vsp[ln + 2] = 0;
341 159
                        (void)VRY_Validate(vsp);
342 159
                        req->vary_l = vsp + ln + 3;
343
344 159
                        i = vry_cmp(vary, vsp);
345 159
                        assert(i == 0 || i == 2);
346
                }
347 2668
                if (i == 0) {
348
                        /* Same header, same contents */
349 69
                        vsp += VRY_Len(vsp);
350 69
                        vary += VRY_Len(vary);
351 2599
                } else if (i == 2) {
352
                        /* Same header, different contents, cannot match */
353 2599
                        return (0);
354
                }
355
        }
356 62
        if (oflo) {
357 1
                vsp = req->vary_b;
358 1
                req->vary_l = NULL;
359 1
                if (vsp + 2 < req->vary_e) {
360 1
                        vsp[0] = 0xff;
361 1
                        vsp[1] = 0xff;
362 1
                        vsp[2] = 0;
363
                }
364 1
                return (0);
365
        } else {
366 61
                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 1782
VRY_Validate(const uint8_t *vary)
376
{
377 1782
        unsigned retval = 0;
378
379 3976
        while (vary[2] != 0) {
380 412
                assert(strlen((const char*)vary+3) == vary[2]);
381 412
                retval += VRY_Len(vary);
382 412
                vary += VRY_Len(vary);
383
        }
384 1782
        return (retval + 3);
385
}