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