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