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