varnish-cache/bin/varnishd/cache/cache_rfc2616.c
0
/*-
1
 * Copyright (c) 2006 Verdens Gang AS
2
 * Copyright (c) 2006-2011 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions
11
 * are met:
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
 * SUCH DAMAGE.
29
 */
30
31
#include "config.h"
32
33
#include <stdlib.h>
34
35
#include "cache_varnishd.h"
36
37
#include "vtim.h"
38
#include "vct.h"
39
40
/*--------------------------------------------------------------------
41
 * TTL and Age calculation in Varnish
42
 *
43
 * RFC2616 has a lot to say about how caches should calculate the TTL
44
 * and expiry times of objects, but it sort of misses the case that
45
 * applies to Varnish:  the server-side cache.
46
 *
47
 * A normal cache, shared or single-client, has no symbiotic relationship
48
 * with the server, and therefore must take a very defensive attitude
49
 * if the Data/Expiry/Age/max-age data does not make sense.  Overall
50
 * the policy described in section 13 of RFC 2616 results in no caching
51
 * happening on the first little sign of trouble.
52
 *
53
 * Varnish on the other hand tries to offload as many transactions from
54
 * the backend as possible, and therefore just passing through everything
55
 * if there is a clock-skew between backend and Varnish is not a workable
56
 * choice.
57
 *
58
 * Varnish implements a policy which is RFC2616 compliant when there
59
 * is no clockskew, and falls as gracefully as possible otherwise.
60
 * Our "clockless cache" model is synthesized from the bits of RFC2616
61
 * that talks about how a cache should react to a clockless origin server,
62
 * and more or less uses the inverse logic for the opposite relationship.
63
 *
64
 */
65
66
static inline unsigned
67 1325
rfc2616_time(const char *p)
68
{
69
        char *ep;
70
        unsigned long val;
71 1325
        if (*p == '-')
72 50
                return (0);
73 1275
        val = strtoul(p, &ep, 10);
74 1275
        if (val > UINT_MAX)
75 0
                return (UINT_MAX);
76 1275
        while (vct_issp(*ep))
77 0
                ep++;
78
        /* We accept ',' as an end character because we may be parsing a
79
         * multi-element Cache-Control part. We accept '.' to be future
80
         * compatble with fractional seconds. */
81 1275
        if (*ep == '\0' || *ep == ',' || *ep == '.')
82 1175
                return (val);
83 100
        return (0);
84 1325
}
85
86
void
87 48274
RFC2616_Ttl(struct busyobj *bo, vtim_real now, vtim_real *t_origin,
88
    float *ttl, float *grace, float *keep)
89
{
90
        unsigned max_age, age;
91
        vtim_real h_date, h_expires;
92
        const char *p;
93
        const struct http *hp;
94
95 48274
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
96 48274
        CHECK_OBJ_NOTNULL(bo->fetch_objcore, OBJCORE_MAGIC);
97 48274
        assert(now != 0.0 && !isnan(now));
98 48274
        AN(t_origin);
99 48274
        AN(ttl);
100 48274
        AN(grace);
101 48274
        AN(keep);
102
103 48274
        *t_origin = now;
104 48274
        *ttl = cache_param->default_ttl;
105 48274
        *grace = cache_param->default_grace;
106 48274
        *keep = cache_param->default_keep;
107
108 48274
        hp = bo->beresp;
109
110 48274
        max_age = age = 0;
111 48274
        h_expires = 0;
112 48274
        h_date = 0;
113
114
        /*
115
         * Initial cacheability determination per [RFC2616, 13.4]
116
         * We do not support ranges to the backend yet, so 206 is out.
117
         */
118
119 48274
        if (http_GetHdr(hp, H_Age, &p)) {
120 525
                age = rfc2616_time(p);
121 525
                *t_origin -= age;
122 525
        }
123
124 48274
        if (bo->fetch_objcore->flags & OC_F_PRIVATE) {
125
                /* Pass object. Halt the processing here, keeping only the
126
                 * parsed value of t_origin, as that will be needed to
127
                 * synthesize a correct Age header in delivery. The
128
                 * SLT_TTL log tag at the end of this function is
129
                 * deliberetaly skipped to avoid confusion when reading
130
                 * the log.*/
131 16249
                *ttl = -1;
132 16249
                *grace = 0;
133 16249
                *keep = 0;
134 16249
                return;
135
        }
136
137 32025
        if (http_GetHdr(hp, H_Expires, &p))
138 200
                h_expires = VTIM_parse(p);
139
140 32025
        if (http_GetHdr(hp, H_Date, &p))
141 32024
                h_date = VTIM_parse(p);
142
143 32025
        switch (http_GetStatus(hp)) {
144
        case 302: /* Moved Temporarily */
145
        case 307: /* Temporary Redirect */
146
                /*
147
                 * https://tools.ietf.org/html/rfc7231#section-6.1
148
                 *
149
                 * Do not apply the default ttl, only set a ttl if Cache-Control
150
                 * or Expires are present. Uncacheable otherwise.
151
                 */
152 25
                *ttl = -1.;
153
                /* FALL-THROUGH */
154
        case 200: /* OK */
155
        case 203: /* Non-Authoritative Information */
156
        case 204: /* No Content */
157
        case 300: /* Multiple Choices */
158
        case 301: /* Moved Permanently */
159
        case 304: /* Not Modified - handled like 200 */
160
        case 404: /* Not Found */
161
        case 410: /* Gone */
162
        case 414: /* Request-URI Too Large */
163
                /*
164
                 * First find any relative specification from the backend
165
                 * These take precedence according to RFC2616, 13.2.4
166
                 */
167
168 62796
                if ((http_GetHdrField(hp, H_Cache_Control, "s-maxage", &p) ||
169 31323
                    http_GetHdrField(hp, H_Cache_Control, "max-age", &p)) &&
170 31473
                    p != NULL) {
171 700
                        max_age = rfc2616_time(p);
172 700
                        *ttl = max_age;
173 700
                        break;
174
                }
175
176
                /* No expire header, fall back to default */
177 30773
                if (h_expires == 0)
178 30673
                        break;
179
180
181
                /* If backend told us it is expired already, don't cache. */
182 100
                if (h_expires < h_date) {
183 75
                        *ttl = 0;
184 75
                        break;
185
                }
186
187 25
                if (h_date == 0 ||
188 25
                    fabs(h_date - now) < cache_param->clock_skew) {
189
                        /*
190
                         * If we have no Date: header or if it is
191
                         * sufficiently close to our clock we will
192
                         * trust Expires: relative to our own clock.
193
                         */
194 0
                        if (h_expires < now)
195 0
                                *ttl = 0;
196
                        else
197 0
                                *ttl = h_expires - now;
198 0
                        break;
199
                } else {
200
                        /*
201
                         * But even if the clocks are out of whack we can still
202
                         * derive a relative time from the two headers.
203
                         * (the negative ttl case is caught above)
204
                         */
205 25
                        *ttl = (int)(h_expires - h_date);
206
                }
207 25
                break;
208
        default:
209 550
                *ttl = -1.;
210 550
                break;
211
        }
212
213
        /*
214
         * RFC5861 outlines a way to control the use of stale responses.
215
         * We use this to initialize the grace period.
216
         */
217 32023
        if (*ttl >= 0 && http_GetHdrField(hp, H_Cache_Control,
218 31448
            "stale-while-revalidate", &p) && p != NULL) {
219 100
                *grace = rfc2616_time(p);
220 100
        }
221
222 64046
        VSLb(bo->vsl, SLT_TTL,
223
            "RFC %.0f %.0f %.0f %.0f %.0f %.0f %.0f %u %s",
224 32023
            *ttl, *grace, *keep, now,
225 32023
            *t_origin, h_date, h_expires, max_age,
226 32023
            bo->uncacheable ? "uncacheable" : "cacheable");
227 48272
}
228
229
/*--------------------------------------------------------------------
230
 * Find out if the request can receive a gzip'ed response
231
 */
232
233
unsigned
234 79482
RFC2616_Req_Gzip(const struct http *hp)
235
{
236
237
238
        /*
239
         * "x-gzip" is for http/1.0 backwards compat, final note in 14.3
240
         * p104 says to not do q values for x-gzip, so we just test
241
         * for its existence.
242
         */
243 79482
        if (http_GetHdrToken(hp, H_Accept_Encoding, "x-gzip", NULL, NULL))
244 50
                return (1);
245
246
        /*
247
         * "gzip" is the real thing, but the 'q' value must be nonzero.
248
         * We do not care a hoot if the client prefers some other
249
         * compression more than gzip: Varnish only does gzip.
250
         */
251 79432
        if (http_GetHdrQ(hp, H_Accept_Encoding, "gzip") > 0.)
252 13475
                return (1);
253
254
        /* Bad client, no gzip. */
255 65957
        return (0);
256 79482
}
257
258
/*--------------------------------------------------------------------*/
259
260
// rfc7232,l,547,548
261
static inline int
262 100
rfc2616_strong_compare(const char *p, const char *e)
263
{
264 125
        if ((p[0] == 'W' && p[1] == '/') ||
265 50
            (e[0] == 'W' && e[1] == '/'))
266 75
                return (0);
267
        /* XXX: should we also have http_etag_cmp() ? */
268 25
        return (strcmp(p, e) == 0);
269 100
}
270
271
// rfc7232,l,550,552
272
static inline int
273 524
rfc2616_weak_compare(const char *p, const char *e)
274
{
275 524
        if (p[0] == 'W' && p[1] == '/')
276 100
                p += 2;
277 524
        if (e[0] == 'W' && e[1] == '/')
278 100
                e += 2;
279
        /* XXX: should we also have http_etag_cmp() ? */
280 524
        return (strcmp(p, e) == 0);
281
}
282
283
int
284 64069
RFC2616_Do_Cond(const struct req *req)
285
{
286
        const char *p, *e;
287
        vtim_real ims, lm;
288
289 64069
        if (!http_IsStatus(req->resp, 200))
290 7400
                return (0);
291
292
        /*
293
         * rfc7232,l,861,866
294
         * We MUST ignore If-Modified-Since if we have an If-None-Match header
295
         */
296 56669
        if (http_GetHdr(req->http, H_If_None_Match, &p)) {
297 649
                if (!http_GetHdr(req->resp, H_ETag, &e))
298 25
                        return (0);
299 624
                if (http_GetHdr(req->http, H_Range, NULL))
300 100
                        return (rfc2616_strong_compare(p, e));
301
                else
302 524
                        return (rfc2616_weak_compare(p, e));
303
        }
304
305 56020
        if (http_GetHdr(req->http, H_If_Modified_Since, &p)) {
306 650
                ims = VTIM_parse(p);
307 650
                if (!ims || ims > req->t_req)   // rfc7232,l,868,869
308 0
                        return (0);
309 650
                if (http_GetHdr(req->resp, H_Last_Modified, &p)) {
310 600
                        lm = VTIM_parse(p);
311 600
                        if (!lm || lm > ims)
312 175
                                return (0);
313 425
                        return (1);
314
                }
315 50
                AZ(ObjGetDouble(req->wrk, req->objcore, OA_LASTMODIFIED, &lm));
316 50
                if (lm > ims)
317 25
                        return (0);
318 25
                return (1);
319
        }
320
321 55370
        return (0);
322 64069
}
323
324
/*--------------------------------------------------------------------*/
325
326
void
327 12500
RFC2616_Weaken_Etag(struct http *hp)
328
{
329
        const char *p;
330
331 12500
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
332
333 12500
        if (!http_GetHdr(hp, H_ETag, &p))
334 12250
                return;
335 250
        AN(p);
336 250
        if (p[0] == 'W' && p[1] == '/')
337 50
                return;
338 200
        http_Unset(hp, H_ETag);
339 200
        http_PrintfHeader(hp, "ETag: W/%s", p);
340 12500
}
341
342
/*--------------------------------------------------------------------*/
343
344
void
345 3050
RFC2616_Vary_AE(struct http *hp)
346
{
347
        const char *vary;
348
349 3050
        if (http_GetHdrToken(hp, H_Vary, "Accept-Encoding", NULL, NULL))
350 175
                return;
351 2875
        if (http_GetHdr(hp, H_Vary, &vary)) {
352 50
                http_Unset(hp, H_Vary);
353 50
                http_PrintfHeader(hp, "Vary: %s, Accept-Encoding", vary);
354 50
        } else {
355 2825
                http_SetHeader(hp, "Vary: Accept-Encoding");
356
        }
357 3050
}
358
359
/*--------------------------------------------------------------------*/
360
361
const char *
362 28524
RFC2616_Strong_LM(const struct http *hp, struct worker *wrk,
363
    struct objcore *oc)
364
{
365 28524
        const char *p = NULL, *e = NULL;
366
        vtim_real lm, d;
367
368 28524
        CHECK_OBJ_ORNULL(wrk, WORKER_MAGIC);
369 28524
        CHECK_OBJ_ORNULL(oc, OBJCORE_MAGIC);
370 28524
        CHECK_OBJ_ORNULL(hp, HTTP_MAGIC);
371
372 28524
        if (hp != NULL) {
373 27799
                (void)http_GetHdr(hp, H_Last_Modified, &p);
374 27799
                (void)http_GetHdr(hp, H_Date, &e);
375 28524
        } else if (wrk != NULL && oc != NULL) {
376 725
                p = HTTP_GetHdrPack(wrk, oc, H_Last_Modified);
377 725
                e = HTTP_GetHdrPack(wrk, oc, H_Date);
378 725
        }
379
380 28524
        if (p == NULL || e == NULL)
381 27599
                return (NULL);
382
383 925
        lm = VTIM_parse(p);
384 925
        d = VTIM_parse(e);
385
386
        /* The cache entry includes a Date value which is at least one second
387
         * after the Last-Modified value.
388
         * [RFC9110 8.8.2.2-6.2]
389
         */
390 925
        return ((lm && d && lm + 1 <= d) ? p : NULL);
391 28524
}