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 1465
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 1465
        int error = 0;
86
87 1465
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
88 1465
        CHECK_OBJ_NOTNULL(bo->bereq, HTTP_MAGIC);
89 1465
        CHECK_OBJ_NOTNULL(bo->beresp, HTTP_MAGIC);
90 1465
        AN(psb);
91 1465
        AZ(*psb);
92
93
        /* No Vary: header, no worries */
94 1465
        if (!http_GetHdr(bo->beresp, H_Vary, &v))
95 1245
                return (0);
96
97
        /* For vary matching string */
98 220
        sb = VSB_new_auto();
99 220
        AN(sb);
100
101
        /* For header matching strings */
102 220
        sbh = VSB_new_auto();
103 220
        AN(sbh);
104
105 236
        for (p = v; *p; p++) {
106
107
                /* Find next header-name */
108 236
                if (vct_issp(*p))
109 8
                        continue;
110 3025
                for (q = p; *q && !vct_issp(*q) && *q != ','; q++)
111 2797
                        continue;
112
113 228
                if (q - p > INT8_MAX) {
114 2
                        VSLb(bo->vsl, SLT_Error,
115
                            "Vary header name length exceeded");
116 2
                        error = 1;
117 2
                        break;
118
                }
119
120
                /* Build a header-matching string out of it */
121 226
                VSB_clear(sbh);
122 226
                AZ(VSB_printf(sbh, "%c%.*s:%c",
123
                    (char)(1 + (q - p)), (int)(q - p), p, 0));
124 226
                AZ(VSB_finish(sbh));
125 226
                CAST_HDR(hdr, VSB_data(sbh));
126
127 226
                if (http_GetHdr(bo->bereq, hdr, &h)) {
128 209
                        AZ(vct_issp(*h));
129
                        /* Trim trailing space */
130 209
                        e = strchr(h, '\0');
131 209
                        while (e > h && vct_issp(e[-1]))
132 0
                                e--;
133
                        /* Encode two byte length and contents */
134 209
                        l = e - h;
135 209
                        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 209
                } else {
142 17
                        e = h;
143 17
                        l = 0xffff;
144
                }
145 226
                AZ(VSB_printf(sb, "%c%c", (int)(l >> 8), (int)(l & 0xff)));
146
                /* Append to vary matching string */
147 226
                AZ(VSB_bcat(sb, VSB_data(sbh), VSB_len(sbh)));
148 226
                if (e != h)
149 208
                        AZ(VSB_bcat(sb, h, e - h));
150
151 229
                while (vct_issp(*q))
152 3
                        q++;
153 226
                if (*q == '\0')
154 215
                        break;
155 11
                if (*q != ',') {
156 3
                        VSLb(bo->vsl, SLT_Error, "Malformed Vary header");
157 3
                        error = 1;
158 3
                        break;
159
                }
160 8
                p = q;
161 8
        }
162
163 220
        if (error) {
164 5
                VSB_destroy(&sbh);
165 5
                VSB_destroy(&sb);
166 5
                return (-1);
167
        }
168
169
        /* Terminate vary matching string */
170 215
        VSB_printf(sb, "%c%c%c", 0xff, 0xff, 0);
171
172 215
        VSB_destroy(&sbh);
173 215
        AZ(VSB_finish(sb));
174 215
        *psb = sb;
175 215
        return (VSB_len(sb));
176 1465
}
177
178
/*
179
 * Find length of a vary entry
180
 */
181
static unsigned
182 4143
VRY_Len(const uint8_t *p)
183
{
184 4143
        unsigned l = vbe16dec(p);
185
186 4143
        return (2 + p[2] + 2 + (l == 0xffff ? 0 : l));
187
}
188
189
/*
190
 * Compare two vary entries
191
 */
192
static int
193 3134
vry_cmp(const uint8_t *v1, const uint8_t *v2)
194
{
195 3134
        unsigned retval = 0;
196
197 3134
        if (!memcmp(v1, v2, VRY_Len(v1))) {
198
                /* Same same */
199 153
                retval = 0;
200 3134
        } else if (memcmp(v1 + 2, v2 + 2, v1[2] + 2)) {
201
                /* Different header */
202 303
                retval = 1;
203 2981
        } else if (cache_param->http_gzip_support &&
204 2677
            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 50
                retval = 0;
214 50
        } else {
215
                /* Same header, different content */
216 2628
                retval = 2;
217
        }
218 3134
        return (retval);
219
}
220
221
/**********************************************************************
222
 * Prepare predictive vary string
223
 */
224
225
void
226 2663
VRY_Prep(struct req *req)
227
{
228 2663
        if (req->waitinglist_gen == 0) {
229 2608
                AZ(req->vary_b);
230 2608
                AZ(req->vary_e);
231 2608
                (void)WS_ReserveAll(req->ws);
232 2608
        } else {
233 55
                AN(WS_Reservation(req->ws));
234
        }
235 2663
        req->vary_b = WS_Reservation(req->ws);
236 2663
        req->vary_e = req->vary_b + WS_ReservationSize(req->ws);
237 2663
        if (req->vary_b + 2 < req->vary_e)
238 2660
                req->vary_b[2] = '\0';
239 2663
}
240
241
void
242 26
VRY_Clear(struct req *req)
243
{
244
245 26
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
246 26
        if (req->vary_b != NULL)
247 2
                free(req->vary_b);
248 26
        req->vary_b = NULL;
249 26
        AZ(req->vary_e);
250 26
}
251
252
/**********************************************************************
253
 * Finish predictive vary processing
254
 */
255
256
void
257 2608
VRY_Finish(struct req *req, enum vry_finish_flag flg)
258
{
259 2608
        uint8_t *p = NULL;
260
        size_t l;
261
262 2608
        if (req->vary_b + 2 >= req->vary_e) {
263 1
                req->vary_b = NULL;
264 1
                req->vary_e = NULL;
265 1
                WS_Release(req->ws, 0);
266 1
                WS_MarkOverflow(req->ws);
267 1
                return;
268
        }
269
270 2607
        l = VRY_Validate(req->vary_b);
271 2607
        if (flg == KEEP && l > 3) {
272 107
                p = malloc(l);
273 107
                if (p != NULL)
274 107
                        memcpy(p, req->vary_b, l);
275 107
        }
276 2607
        WS_Release(req->ws, 0);
277 2607
        req->vary_e = NULL;
278 2607
        req->vary_b = p;
279 2608
}
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 2824
VRY_Match(const struct req *req, const uint8_t *vary)
290
{
291 2824
        uint8_t *vsp = req->vary_b;
292
        const char *h, *e;
293
        unsigned lh, ln;
294 2824
        int i, oflo = 0;
295
        hdr_t hdr;
296
297 2824
        AN(vsp);
298 2824
        AN(vary);
299 3027
        while (vary[2]) {
300 2832
                if (vsp + 2 >= req->vary_e) {
301
                        /*
302
                         * Too little workspace to find out
303
                         */
304 0
                        oflo = 1;
305 0
                        break;
306
                }
307 2832
                i = vry_cmp(vary, vsp);
308 2832
                if (i == 1) {
309
                        /*
310
                         * Different header, build a new entry,
311
                         * then compare again with that new entry.
312
                         */
313
314 303
                        CAST_HDR(hdr, vary + 2);
315 303
                        ln = 2 + vary[2] + 2;
316 303
                        i = http_GetHdr(req->http, hdr, &h);
317 303
                        if (i) {
318
                                /* Trim trailing space */
319 168
                                e = strchr(h, '\0');
320 168
                                while (e > h && vct_issp(e[-1]))
321 0
                                        e--;
322 168
                                lh = e - h;
323 168
                                assert(lh < 0xffff);
324 168
                                ln += lh;
325 168
                        } else {
326 135
                                e = h = NULL;
327 135
                                lh = 0xffff;
328
                        }
329
330 303
                        if (vsp + ln + 3 >= req->vary_e) {
331
                                /*
332
                                 * Not enough space to build new entry
333
                                 * and put terminator behind it.
334
                                 */
335 1
                                oflo = 1;
336 1
                                break;
337
                        }
338
339 302
                        vbe16enc(vsp, (uint16_t)lh);
340 302
                        memcpy(vsp + 2, vary + 2, vary[2] + 2);
341 302
                        if (h != NULL)
342 167
                                memcpy(vsp + 2 + vsp[2] + 2, h, lh);
343 302
                        vsp[ln++] = 0xff;
344 302
                        vsp[ln++] = 0xff;
345 302
                        vsp[ln++] = 0;
346 302
                        assert(VRY_Validate(vsp) == ln);
347
348 302
                        i = vry_cmp(vary, vsp);
349 302
                        assert(i == 0 || i == 2);
350 302
                }
351 2831
                if (i == 0) {
352
                        /* Same header, same contents */
353 203
                        vsp += VRY_Len(vsp);
354 203
                        vary += VRY_Len(vary);
355 2831
                } else if (i == 2) {
356
                        /* Same header, different contents, cannot match */
357 2628
                        return (0);
358
                }
359
        }
360 196
        if (oflo) {
361 1
                vsp = req->vary_b;
362 1
                if (vsp + 2 < req->vary_e) {
363 1
                        vsp[0] = 0xff;
364 1
                        vsp[1] = 0xff;
365 1
                        vsp[2] = 0;
366 1
                }
367 1
                return (0);
368
        } else {
369 195
                return (1);
370
        }
371 2824
}
372
373
/*
374
 * Check the validity of a Vary string and return its total length
375
 */
376
377
static unsigned
378 2909
VRY_Validate(const uint8_t *vary)
379
{
380 2909
        unsigned l, retval = 0;
381
382 3512
        while (vary[2] != 0) {
383 603
                assert(strlen((const char*)vary + 3) == vary[2]);
384 603
                l = VRY_Len(vary);
385 603
                retval += l;
386 603
                vary += l;
387
        }
388 2909
        return (retval + 3);
389
}