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