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 53
rfc2616_time(const char *p)
68
{
69
        char *ep;
70
        unsigned long val;
71 53
        if (*p == '-')
72 2
                return (0);
73 51
        val = strtoul(p, &ep, 10);
74 51
        if (val > UINT_MAX)
75 0
                return (UINT_MAX);
76 51
        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
         * compatible with fractional seconds. */
81 51
        if (*ep == '\0' || *ep == ',' || *ep == '.')
82 47
                return (val);
83 4
        return (0);
84 53
}
85
86
void
87 1994
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 1994
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
96 1994
        CHECK_OBJ_NOTNULL(bo->fetch_objcore, OBJCORE_MAGIC);
97 1994
        assert(now != 0.0 && !isnan(now));
98 1994
        AN(t_origin);
99 1994
        AN(ttl);
100 1994
        AN(grace);
101 1994
        AN(keep);
102
103 1994
        *t_origin = now;
104 1994
        *ttl = cache_param->default_ttl;
105 1994
        *grace = cache_param->default_grace;
106 1994
        *keep = cache_param->default_keep;
107
108 1994
        hp = bo->beresp;
109
110 1994
        max_age = age = 0;
111 1994
        h_expires = 0;
112 1994
        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 1994
        if (http_GetHdr(hp, H_Age, &p)) {
120 21
                age = rfc2616_time(p);
121 21
                *t_origin -= age;
122 21
        }
123
124 1994
        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
                 * deliberately skipped to avoid confusion when reading
130
                 * the log.*/
131 666
                *ttl = -1;
132 666
                *grace = 0;
133 666
                *keep = 0;
134 666
                return;
135
        }
136
137 1328
        if (http_GetHdr(hp, H_Expires, &p))
138 8
                h_expires = VTIM_parse(p);
139
140 1328
        if (http_GetHdr(hp, H_Date, &p))
141 1328
                h_date = VTIM_parse(p);
142
143 1328
        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 1
                *ttl = -1.;
153
                /* FALLTHROUGH */
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 2600
                if ((http_GetHdrField(hp, H_Cache_Control, "s-maxage", &p) ||
169 1297
                    http_GetHdrField(hp, H_Cache_Control, "max-age", &p)) &&
170 1303
                    p != NULL) {
171 28
                        max_age = rfc2616_time(p);
172 28
                        *ttl = max_age;
173 28
                        break;
174
                }
175
176
                /* No expire header, fall back to default */
177 1275
                if (h_expires == 0)
178 1271
                        break;
179
180
181
                /* If backend told us it is expired already, don't cache. */
182 4
                if (h_expires < h_date) {
183 3
                        *ttl = 0;
184 3
                        break;
185
                }
186
187 1
                if (h_date == 0 ||
188 1
                    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 1
                        *ttl = (int)(h_expires - h_date);
206
                }
207 1
                break;
208
        default:
209 25
                *ttl = -1.;
210 25
                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 1328
        if (*ttl >= 0 && http_GetHdrField(hp, H_Cache_Control,
218 1302
            "stale-while-revalidate", &p) && p != NULL) {
219 4
                *grace = rfc2616_time(p);
220 4
        }
221
222 2656
        VSLb(bo->vsl, SLT_TTL,
223
            "RFC %.0f %.0f %.0f %.0f %.0f %.0f %.0f %u %s",
224 1328
            *ttl, *grace, *keep, now,
225 1328
            *t_origin, h_date, h_expires, max_age,
226 1328
            bo->uncacheable ? "uncacheable" : "cacheable");
227 1994
}
228
229
/*--------------------------------------------------------------------
230
 * Find out if the request can receive a gzipped response
231
 */
232
233
unsigned
234 3370
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 3370
        if (http_GetHdrToken(hp, H_Accept_Encoding, "x-gzip", NULL, NULL))
244 2
                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 3368
        if (http_GetHdrQ(hp, H_Accept_Encoding, "gzip") > 0.)
252 539
                return (1);
253
254
        /* Bad client, no gzip. */
255 2829
        return (0);
256 3370
}
257
258
/*--------------------------------------------------------------------*/
259
260
// rfc7232,l,547,548
261
static inline int
262 4
rfc2616_strong_compare(const char *p, const char *e)
263
{
264 5
        if ((p[0] == 'W' && p[1] == '/') ||
265 2
            (e[0] == 'W' && e[1] == '/'))
266 3
                return (0);
267
        /* XXX: should we also have http_etag_cmp() ? */
268 1
        return (strcmp(p, e) == 0);
269 4
}
270
271
// rfc7232,l,550,552
272
static inline int
273 21
rfc2616_weak_compare(const char *p, const char *e)
274
{
275 21
        if (p[0] == 'W' && p[1] == '/')
276 4
                p += 2;
277 21
        if (e[0] == 'W' && e[1] == '/')
278 4
                e += 2;
279
        /* XXX: should we also have http_etag_cmp() ? */
280 21
        return (strcmp(p, e) == 0);
281
}
282
283
int
284 2729
RFC2616_Do_Cond(const struct req *req)
285
{
286
        const char *p, *e;
287
        vtim_real ims, lm;
288
289 2729
        if (!http_IsStatus(req->resp, 200))
290 335
                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 2394
        if (http_GetHdr(req->http, H_If_None_Match, &p)) {
297 26
                if (!http_GetHdr(req->resp, H_ETag, &e))
298 1
                        return (0);
299 25
                if (http_GetHdr(req->http, H_Range, NULL))
300 4
                        return (rfc2616_strong_compare(p, e));
301
                else
302 21
                        return (rfc2616_weak_compare(p, e));
303
        }
304
305 2368
        if (http_GetHdr(req->http, H_If_Modified_Since, &p)) {
306 26
                ims = VTIM_parse(p);
307 26
                if (!ims || ims > req->t_req)   // rfc7232,l,868,869
308 0
                        return (0);
309 26
                if (http_GetHdr(req->resp, H_Last_Modified, &p)) {
310 24
                        lm = VTIM_parse(p);
311 24
                        if (!lm || lm > ims)
312 7
                                return (0);
313 17
                        return (1);
314
                }
315 2
                AZ(ObjGetDouble(req->wrk, req->objcore, OA_LASTMODIFIED, &lm));
316 2
                if (lm > ims)
317 1
                        return (0);
318 1
                return (1);
319
        }
320
321 2342
        return (0);
322 2729
}
323
324
/*--------------------------------------------------------------------*/
325
326
void
327 503
RFC2616_Weaken_Etag(struct http *hp)
328
{
329
        const char *p;
330
331 503
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
332
333 503
        if (!http_GetHdr(hp, H_ETag, &p))
334 493
                return;
335 10
        AN(p);
336 10
        if (p[0] == 'W' && p[1] == '/')
337 2
                return;
338 8
        http_Unset(hp, H_ETag);
339 8
        http_PrintfHeader(hp, "ETag: W/%s", p);
340 503
}
341
342
/*--------------------------------------------------------------------*/
343
344
void
345 122
RFC2616_Vary_AE(struct http *hp)
346
{
347
        const char *vary;
348
349 122
        if (http_GetHdrToken(hp, H_Vary, "Accept-Encoding", NULL, NULL))
350 7
                return;
351 115
        if (http_GetHdr(hp, H_Vary, &vary)) {
352 2
                http_Unset(hp, H_Vary);
353 2
                http_PrintfHeader(hp, "Vary: %s, Accept-Encoding", vary);
354 2
        } else {
355 113
                http_SetHeader(hp, "Vary: Accept-Encoding");
356
        }
357 122
}
358
359
/*--------------------------------------------------------------------*/
360
361
const char *
362 1180
RFC2616_Strong_LM(const struct http *hp, struct worker *wrk,
363
    struct objcore *oc)
364
{
365 1180
        const char *p = NULL, *e = NULL;
366
        vtim_real lm, d;
367
368 1180
        CHECK_OBJ_ORNULL(wrk, WORKER_MAGIC);
369 1180
        CHECK_OBJ_ORNULL(oc, OBJCORE_MAGIC);
370 1180
        CHECK_OBJ_ORNULL(hp, HTTP_MAGIC);
371
372 1180
        if (hp != NULL) {
373 1149
                (void)http_GetHdr(hp, H_Last_Modified, &p);
374 1149
                (void)http_GetHdr(hp, H_Date, &e);
375 1180
        } else if (wrk != NULL && oc != NULL) {
376 31
                p = HTTP_GetHdrPack(wrk, oc, H_Last_Modified);
377 31
                e = HTTP_GetHdrPack(wrk, oc, H_Date);
378 31
        }
379
380 1180
        if (p == NULL || e == NULL)
381 1141
                return (NULL);
382
383 39
        lm = VTIM_parse(p);
384 39
        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 39
        return ((lm && d && lm + 1 <= d) ? p : NULL);
391 1180
}