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 2930
VRY_Create(struct busyobj *bo, struct vsb **psb)
80
{
81
        const char *v, *p, *q, *h, *e;
82
        struct vsb *sb, *sbh;
83
        hdr_t hdr;
84
        unsigned l;
85 2930
        int error = 0;
86
87 2930
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
88 2930
        CHECK_OBJ_NOTNULL(bo->bereq, HTTP_MAGIC);
89 2930
        CHECK_OBJ_NOTNULL(bo->beresp, HTTP_MAGIC);
90 2930
        AN(psb);
91 2930
        AZ(*psb);
92
93
        /* No Vary: header, no worries */
94 2930
        if (!http_GetHdr(bo->beresp, H_Vary, &v))
95 2490
                return (0);
96
97
        /* For vary matching string */
98 440
        sb = VSB_new_auto();
99 440
        AN(sb);
100
101
        /* For header matching strings */
102 440
        sbh = VSB_new_auto();
103 440
        AN(sbh);
104
105 472
        for (p = v; *p; p++) {
106
107
                /* Find next header-name */
108 472
                if (vct_issp(*p))
109 16
                        continue;
110 6050
                for (q = p; *q && !vct_issp(*q) && *q != ','; q++)
111 5594
                        continue;
112
113 456
                if (q - p > INT8_MAX) {
114 4
                        VSLb(bo->vsl, SLT_Error,
115
                            "Vary header name length exceeded");
116 4
                        error = 1;
117 4
                        break;
118
                }
119
120
                /* Build a header-matching string out of it */
121 452
                VSB_clear(sbh);
122 452
                AZ(VSB_printf(sbh, "%c%.*s:%c",
123
                    (char)(1 + (q - p)), (int)(q - p), p, 0));
124 452
                AZ(VSB_finish(sbh));
125 452
                CAST_HDR(hdr, VSB_data(sbh));
126
127 452
                if (http_GetHdr(bo->bereq, hdr, &h)) {
128 418
                        AZ(vct_issp(*h));
129
                        /* Trim trailing space */
130 418
                        e = strchr(h, '\0');
131 418
                        while (e > h && vct_issp(e[-1]))
132 0
                                e--;
133
                        /* Encode two byte length and contents */
134 418
                        l = e - h;
135 418
                        if (l > 0xffff - 1) {
136 0
                                VSLb(bo->vsl, SLT_Error,
137
                                    "Vary header maximum length exceeded");
138 0
                                error = 1;
139 0
                                break;
140
                        }
141 418
                } else {
142 34
                        e = h;
143 34
                        l = 0xffff;
144
                }
145 452
                AZ(VSB_printf(sb, "%c%c", (int)(l >> 8), (int)(l & 0xff)));
146
                /* Append to vary matching string */
147 452
                AZ(VSB_bcat(sb, VSB_data(sbh), VSB_len(sbh)));
148 452
                if (e != h)
149 416
                        AZ(VSB_bcat(sb, h, e - h));
150
151 458
                while (vct_issp(*q))
152 6
                        q++;
153 452
                if (*q == '\0')
154 430
                        break;
155 22
                if (*q != ',') {
156 6
                        VSLb(bo->vsl, SLT_Error, "Malformed Vary header");
157 6
                        error = 1;
158 6
                        break;
159
                }
160 16
                p = q;
161 16
        }
162
163 440
        if (error) {
164 10
                VSB_destroy(&sbh);
165 10
                VSB_destroy(&sb);
166 10
                return (-1);
167
        }
168
169
        /* Terminate vary matching string */
170 430
        VSB_printf(sb, "%c%c%c", 0xff, 0xff, 0);
171
172 430
        VSB_destroy(&sbh);
173 430
        AZ(VSB_finish(sb));
174 430
        *psb = sb;
175 430
        return (VSB_len(sb));
176 2930
}
177
178
/*
179
 * Find length of a vary entry
180
 */
181
static unsigned
182 8286
VRY_Len(const uint8_t *p)
183
{
184 8286
        unsigned l = vbe16dec(p);
185
186 8286
        return (2 + p[2] + 2 + (l == 0xffff ? 0 : l));
187
}
188
189
/*
190
 * Compare two vary entries
191
 */
192
static int
193 6268
vry_cmp(const uint8_t *v1, const uint8_t *v2)
194
{
195 6268
        unsigned retval = 0;
196
197 6268
        if (!memcmp(v1, v2, VRY_Len(v1))) {
198
                /* Same same */
199 307
                retval = 0;
200 6268
        } else if (memcmp(v1 + 2, v2 + 2, v1[2] + 2)) {
201
                /* Different header */
202 606
                retval = 1;
203 5961
        } else if (cache_param->http_gzip_support &&
204 5353
            http_hdr_eq(H_Accept_Encoding, (const char*) v1 + 2)) {
205
                /*
206
                 * If we do gzip processing, we do not vary on Accept-Encoding,
207
                 * because we want everybody to get the gzipped object, and
208
                 * varnish will gunzip as necessary.  We implement the skip at
209
                 * check time, rather than create time, so that object in
210
                 * persistent storage can be used with either setting of
211
                 * http_gzip_support.
212
                 */
213 99
                retval = 0;
214 99
        } else {
215
                /* Same header, different content */
216 5256
                retval = 2;
217
        }
218 6268
        return (retval);
219
}
220
221
/**********************************************************************
222
 * Prepare predictive vary string
223
 */
224
225
void
226 5330
VRY_Prep(struct req *req)
227
{
228 5330
        if (req->waitinglist_gen == 0) {
229 5217
                AZ(req->vary_b);
230 5217
                AZ(req->vary_e);
231 5217
                (void)WS_ReserveAll(req->ws);
232 5217
        } else {
233 113
                AN(WS_Reservation(req->ws));
234
        }
235 5330
        req->vary_b = WS_Reservation(req->ws);
236 5330
        req->vary_e = req->vary_b + WS_ReservationSize(req->ws);
237 5330
        if (req->vary_b + 2 < req->vary_e)
238 5327
                req->vary_b[2] = '\0';
239 5330
}
240
241
void
242 51
VRY_Clear(struct req *req)
243
{
244
245 51
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
246 51
        if (req->vary_b != NULL)
247 4
                free(req->vary_b);
248 51
        req->vary_b = NULL;
249 51
        AZ(req->vary_e);
250 51
}
251
252
/**********************************************************************
253
 * Finish predictive vary processing
254
 */
255
256
void
257 5217
VRY_Finish(struct req *req, enum vry_finish_flag flg)
258
{
259 5217
        uint8_t *p = NULL;
260
        size_t l;
261
262 5217
        if (req->vary_b + 2 >= req->vary_e) {
263 2
                req->vary_b = NULL;
264 2
                req->vary_e = NULL;
265 2
                WS_Release(req->ws, 0);
266 2
                WS_MarkOverflow(req->ws);
267 2
                return;
268
        }
269
270 5215
        l = VRY_Validate(req->vary_b);
271 5215
        if (flg == KEEP && l > 3) {
272 214
                p = malloc(l);
273 214
                if (p != NULL)
274 214
                        memcpy(p, req->vary_b, l);
275 214
        }
276 5215
        WS_Release(req->ws, 0);
277 5215
        req->vary_e = NULL;
278 5215
        req->vary_b = p;
279 5217
}
280
281
/**********************************************************************
282
 * Match vary strings, and build a new cached string if possible.
283
 *
284
 * Return zero if there is certainly no match.
285
 * Return non-zero if there could be a match or if we couldn't tell.
286
 */
287
288
int
289 5648
VRY_Match(const struct req *req, const uint8_t *vary)
290
{
291 5648
        uint8_t *vsp = req->vary_b;
292
        const char *h, *e;
293
        unsigned lh, ln;
294 5648
        int i, oflo = 0;
295
        hdr_t hdr;
296
297 5648
        AN(vsp);
298 5648
        AN(vary);
299 6054
        while (vary[2]) {
300 5664
                if (vsp + 2 >= req->vary_e) {
301
                        /*
302
                         * Too little workspace to find out
303
                         */
304 0
                        oflo = 1;
305 0
                        break;
306
                }
307 5664
                i = vry_cmp(vary, vsp);
308 5664
                if (i == 1) {
309
                        /*
310
                         * Different header, build a new entry,
311
                         * then compare again with that new entry.
312
                         */
313
314 606
                        CAST_HDR(hdr, vary + 2);
315 606
                        ln = 2 + vary[2] + 2;
316 606
                        i = http_GetHdr(req->http, hdr, &h);
317 606
                        if (i) {
318
                                /* Trim trailing space */
319 337
                                e = strchr(h, '\0');
320 337
                                while (e > h && vct_issp(e[-1]))
321 0
                                        e--;
322 337
                                lh = e - h;
323 337
                                assert(lh < 0xffff);
324 337
                                ln += lh;
325 337
                        } else {
326 269
                                e = h = NULL;
327 269
                                lh = 0xffff;
328
                        }
329
330 606
                        if (vsp + ln + 3 >= req->vary_e) {
331
                                /*
332
                                 * Not enough space to build new entry
333
                                 * and put terminator behind it.
334
                                 */
335 2
                                oflo = 1;
336 2
                                break;
337
                        }
338
339 604
                        vbe16enc(vsp, (uint16_t)lh);
340 604
                        memcpy(vsp + 2, vary + 2, vary[2] + 2);
341 604
                        if (h != NULL)
342 335
                                memcpy(vsp + 2 + vsp[2] + 2, h, lh);
343 604
                        vsp[ln++] = 0xff;
344 604
                        vsp[ln++] = 0xff;
345 604
                        vsp[ln++] = 0;
346 604
                        assert(VRY_Validate(vsp) == ln);
347
348 604
                        i = vry_cmp(vary, vsp);
349 604
                        assert(i == 0 || i == 2);
350 604
                }
351 5662
                if (i == 0) {
352
                        /* Same header, same contents */
353 406
                        vsp += VRY_Len(vsp);
354 406
                        vary += VRY_Len(vary);
355 5662
                } else if (i == 2) {
356
                        /* Same header, different contents, cannot match */
357 5256
                        return (0);
358
                }
359
        }
360 392
        if (oflo) {
361 2
                vsp = req->vary_b;
362 2
                if (vsp + 2 < req->vary_e) {
363 2
                        vsp[0] = 0xff;
364 2
                        vsp[1] = 0xff;
365 2
                        vsp[2] = 0;
366 2
                }
367 2
                return (0);
368
        } else {
369 390
                return (1);
370
        }
371 5648
}
372
373
/*
374
 * Check the validity of a Vary string and return its total length
375
 */
376
377
static unsigned
378 5819
VRY_Validate(const uint8_t *vary)
379
{
380 5819
        unsigned l, retval = 0;
381
382 7025
        while (vary[2] != 0) {
383 1206
                assert(strlen((const char*)vary + 3) == vary[2]);
384 1206
                l = VRY_Len(vary);
385 1206
                retval += l;
386 1206
                vary += l;
387
        }
388 5819
        return (retval + 3);
389
}