varnish-cache/bin/varnishd/cache/cache_rfc2616.c
1
/*-
2
 * Copyright (c) 2006 Verdens Gang AS
3
 * Copyright (c) 2006-2011 Varnish Software AS
4
 * All rights reserved.
5
 *
6
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
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
30
#include "config.h"
31
32
#include <stdlib.h>
33
34
#include "cache_varnishd.h"
35
36
#include "vtim.h"
37
38
/*--------------------------------------------------------------------
39
 * TTL and Age calculation in Varnish
40
 *
41
 * RFC2616 has a lot to say about how caches should calculate the TTL
42
 * and expiry times of objects, but it sort of misses the case that
43
 * applies to Varnish:  the server-side cache.
44
 *
45
 * A normal cache, shared or single-client, has no symbiotic relationship
46
 * with the server, and therefore must take a very defensive attitude
47
 * if the Data/Expiry/Age/max-age data does not make sense.  Overall
48
 * the policy described in section 13 of RFC 2616 results in no caching
49
 * happening on the first little sign of trouble.
50
 *
51
 * Varnish on the other hand tries to offload as many transactions from
52
 * the backend as possible, and therefore just passing through everything
53
 * if there is a clock-skew between backend and Varnish is not a workable
54
 * choice.
55
 *
56
 * Varnish implements a policy which is RFC2616 compliant when there
57
 * is no clockskew, and falls as gracefully as possible otherwise.
58
 * Our "clockless cache" model is synthesized from the bits of RFC2616
59
 * that talks about how a cache should react to a clockless origin server,
60
 * and more or less uses the inverse logic for the opposite relationship.
61
 *
62
 */
63
64
void
65 971
RFC2616_Ttl(struct busyobj *bo, double now, double *t_origin,
66
    float *ttl, float *grace, float *keep)
67
{
68
        unsigned max_age, age;
69
        double h_date, h_expires;
70
        const char *p;
71
        const struct http *hp;
72
73 971
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
74 971
        assert(now != 0.0 && !isnan(now));
75 971
        AN(t_origin);
76 971
        AN(ttl);
77 971
        AN(grace);
78 971
        AN(keep);
79
80 971
        *t_origin = now;
81 971
        *ttl = cache_param->default_ttl;
82 971
        *grace = cache_param->default_grace;
83 971
        *keep = cache_param->default_keep;
84
85 971
        hp = bo->beresp;
86
87 971
        max_age = age = 0;
88 971
        h_expires = 0;
89 971
        h_date = 0;
90
91
        /*
92
         * Initial cacheability determination per [RFC2616, 13.4]
93
         * We do not support ranges to the backend yet, so 206 is out.
94
         */
95
96 971
        if (http_GetHdr(hp, H_Age, &p)) {
97
                /*
98
                 * We deliberately run with partial results, rather than
99
                 * reject the Age: header outright.  This will be future
100
                 * compatible with fractional seconds.
101
                 */
102 10
                age = strtoul(p, NULL, 10);
103 10
                *t_origin -= age;
104
        }
105
106 971
        if (http_GetHdr(hp, H_Expires, &p))
107 4
                h_expires = VTIM_parse(p);
108
109 971
        if (http_GetHdr(hp, H_Date, &p))
110 971
                h_date = VTIM_parse(p);
111
112 971
        switch (http_GetStatus(hp)) {
113
        default:
114 17
                *ttl = -1.;
115 17
                break;
116
        case 302: /* Moved Temporarily */
117
        case 307: /* Temporary Redirect */
118
                /*
119
                 * https://tools.ietf.org/html/rfc7231#section-6.1
120
                 *
121
                 * Do not apply the default ttl, only set a ttl if Cache-Control
122
                 * or Expires are present. Uncacheable otherwise.
123
                 */
124 1
                *ttl = -1.;
125
                /* FALL-THROUGH */
126
        case 200: /* OK */
127
        case 203: /* Non-Authoritative Information */
128
        case 204: /* No Content */
129
        case 300: /* Multiple Choices */
130
        case 301: /* Moved Permanently */
131
        case 304: /* Not Modified - handled like 200 */
132
        case 404: /* Not Found */
133
        case 410: /* Gone */
134
        case 414: /* Request-URI Too Large */
135
                /*
136
                 * First find any relative specification from the backend
137
                 * These take precedence according to RFC2616, 13.2.4
138
                 */
139
140 1908
                if ((http_GetHdrField(hp, H_Cache_Control, "s-maxage", &p) ||
141 969
                    http_GetHdrField(hp, H_Cache_Control, "max-age", &p)) &&
142 15
                    p != NULL) {
143
144 15
                        if (*p == '-')
145 1
                                max_age = 0;
146
                        else
147 14
                                max_age = strtoul(p, NULL, 0);
148
149 15
                        *ttl = max_age;
150 15
                        break;
151
                }
152
153
                /* No expire header, fall back to default */
154 939
                if (h_expires == 0)
155 936
                        break;
156
157
158
                /* If backend told us it is expired already, don't cache. */
159 3
                if (h_expires < h_date) {
160 3
                        *ttl = 0;
161 3
                        break;
162
                }
163
164 0
                if (h_date == 0 ||
165 0
                    fabs(h_date - now) < cache_param->clock_skew) {
166
                        /*
167
                         * If we have no Date: header or if it is
168
                         * sufficiently close to our clock we will
169
                         * trust Expires: relative to our own clock.
170
                         */
171 0
                        if (h_expires < now)
172 0
                                *ttl = 0;
173
                        else
174 0
                                *ttl = h_expires - now;
175 0
                        break;
176
                } else {
177
                        /*
178
                         * But even if the clocks are out of whack we can still
179
                         * derive a relative time from the two headers.
180
                         * (the negative ttl case is caught above)
181
                         */
182 0
                        *ttl = (int)(h_expires - h_date);
183
                }
184
185
        }
186
187
        /*
188
         * RFC5861 outlines a way to control the use of stale responses.
189
         * We use this to initialize the grace period.
190
         */
191 971
        if (*ttl >= 0 && http_GetHdrField(hp, H_Cache_Control,
192 3
            "stale-while-revalidate", &p) && p != NULL) {
193
194 3
                if (*p == '-')
195 0
                        *grace = 0;
196
                else
197 3
                        *grace = strtoul(p, NULL, 0);
198
        }
199
200 3884
        VSLb(bo->vsl, SLT_TTL,
201
            "RFC %.0f %.0f %.0f %.0f %.0f %.0f %.0f %u",
202 2913
            *ttl, *grace, *keep, now,
203
            *t_origin, h_date, h_expires, max_age);
204 971
}
205
206
/*--------------------------------------------------------------------
207
 * Find out if the request can receive a gzip'ed response
208
 */
209
210
unsigned
211 1837
RFC2616_Req_Gzip(const struct http *hp)
212
{
213
214
215
        /*
216
         * "x-gzip" is for http/1.0 backwards compat, final note in 14.3
217
         * p104 says to not do q values for x-gzip, so we just test
218
         * for its existence.
219
         */
220 1837
        if (http_GetHdrToken(hp, H_Accept_Encoding, "x-gzip", NULL, NULL))
221 2
                return (1);
222
223
        /*
224
         * "gzip" is the real thing, but the 'q' value must be nonzero.
225
         * We do not care a hoot if the client prefers some other
226
         * compression more than gzip: Varnish only does gzip.
227
         */
228 1835
        if (http_GetHdrQ(hp, H_Accept_Encoding, "gzip") > 0.)
229 255
                return (1);
230
231
        /* Bad client, no gzip. */
232 1580
        return (0);
233
}
234
235
/*--------------------------------------------------------------------*/
236
237
static inline int
238 4
rfc2616_strong_compare(const char *p, const char *e)
239
{
240 6
        if ((p[0] == 'W' && p[1] == '/') ||
241 3
            (e[0] == 'W' && e[1] == '/'))
242 3
                return (0);
243 1
        return (strcmp(p, e) == 0);
244
}
245
246
static inline int
247 21
rfc2616_weak_compare(const char *p, const char *e)
248
{
249 21
        if (p[0] == 'W' && p[1] == '/')
250 4
                p += 2;
251 21
        if (e[0] == 'W' && e[1] == '/')
252 4
                e += 2;
253 21
        return (strcmp(p, e) == 0);
254
}
255
256
int
257 76
RFC2616_Do_Cond(const struct req *req)
258
{
259
        const char *p, *e;
260
        double ims, lm;
261
262
        /*
263
         * We MUST ignore If-Modified-Since if we have an If-None-Match
264
         * header [RFC7232 3.3 p16].
265
         */
266 76
        if (http_GetHdr(req->http, H_If_None_Match, &p)) {
267 26
                if (!http_GetHdr(req->resp, H_ETag, &e))
268 1
                        return (0);
269 25
                if (http_GetHdr(req->http, H_Range, NULL))
270 4
                        return (rfc2616_strong_compare(p, e));
271
                else
272 21
                        return (rfc2616_weak_compare(p, e));
273
        }
274
275 50
        if (http_GetHdr(req->http, H_If_Modified_Since, &p)) {
276 24
                ims = VTIM_parse(p);
277 24
                if (!ims || ims > req->t_req)   /* [RFC7232 3.3 p16] */
278 0
                        return (0);
279 24
                if (http_GetHdr(req->resp, H_Last_Modified, &p)) {
280 22
                        lm = VTIM_parse(p);
281 22
                        if (!lm || lm > ims)
282 5
                                return (0);
283 17
                        return (1);
284
                }
285 2
                AZ(ObjGetDouble(req->wrk, req->objcore, OA_LASTMODIFIED, &lm));
286 2
                if (lm > ims)
287 1
                        return (0);
288 1
                return (1);
289
        }
290
291 26
        return (0);
292
}
293
294
/*--------------------------------------------------------------------*/
295
296
void
297 217
RFC2616_Weaken_Etag(struct http *hp)
298
{
299
        const char *p;
300
301 217
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
302
303 217
        if (!http_GetHdr(hp, H_ETag, &p))
304 416
                return;
305 10
        AN(p);
306 10
        if (p[0] == 'W' && p[1] == '/')
307 2
                return;
308 8
        http_Unset(hp, H_ETag);
309 8
        http_PrintfHeader(hp, "ETag: W/%s", p);
310
}
311
312
/*--------------------------------------------------------------------*/
313
314
void
315 72
RFC2616_Vary_AE(struct http *hp)
316
{
317
        const char *vary;
318
319 72
        if (http_GetHdrToken(hp, H_Vary, "Accept-Encoding", NULL, NULL))
320 75
                return;
321 69
        if (http_GetHdr(hp, H_Vary, &vary)) {
322 2
                http_Unset(hp, H_Vary);
323 2
                http_PrintfHeader(hp, "Vary: %s, Accept-Encoding", vary);
324
        } else {
325 67
                http_SetHeader(hp, "Vary: Accept-Encoding");
326
        }
327
}
328
329
/*--------------------------------------------------------------------*/
330
331
void
332 1271
RFC2616_Response_Body(const struct worker *wrk, const struct busyobj *bo)
333
{
334
335 1271
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
336 1271
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
337
338
        /*
339
         * Figure out how the fetch is supposed to happen, before the
340
         * headers are adultered by VCL
341
         */
342 1271
        if (!strcasecmp(http_GetMethod(bo->bereq), "head")) {
343
                /*
344
                 * A HEAD request can never have a body in the reply,
345
                 * no matter what the headers might say.
346
                 * [RFC7231 4.3.2 p25]
347
                 */
348 2
                wrk->stats->fetch_head++;
349 2
                bo->htc->body_status = BS_NONE;
350 1269
        } else if (http_GetStatus(bo->beresp) <= 199) {
351
                /*
352
                 * 1xx responses never have a body.
353
                 * [RFC7230 3.3.2 p31]
354
                 * ... but we should never see them.
355
                 */
356 0
                wrk->stats->fetch_1xx++;
357 0
                bo->htc->body_status = BS_ERROR;
358 1269
        } else if (http_IsStatus(bo->beresp, 204)) {
359
                /*
360
                 * 204 is "No Content", obviously don't expect a body.
361
                 * [RFC7230 3.3.1 p29 and 3.3.2 p31]
362
                 */
363 5
                wrk->stats->fetch_204++;
364 9
                if ((http_GetHdr(bo->beresp, H_Content_Length, NULL) &&
365 7
                    bo->htc->content_length != 0) ||
366 3
                    http_GetHdr(bo->beresp, H_Transfer_Encoding, NULL))
367 2
                        bo->htc->body_status = BS_ERROR;
368
                else
369 3
                        bo->htc->body_status = BS_NONE;
370 1264
        } else if (http_IsStatus(bo->beresp, 304)) {
371
                /*
372
                 * 304 is "Not Modified" it has no body.
373
                 * [RFC7230 3.3 p28]
374
                 */
375 18
                wrk->stats->fetch_304++;
376 18
                bo->htc->body_status = BS_NONE;
377 1246
        } else if (bo->htc->body_status == BS_CHUNKED) {
378 40
                wrk->stats->fetch_chunked++;
379 1206
        } else if (bo->htc->body_status == BS_LENGTH) {
380 698
                assert(bo->htc->content_length > 0);
381 698
                wrk->stats->fetch_length++;
382 508
        } else if (bo->htc->body_status == BS_EOF) {
383 19
                wrk->stats->fetch_eof++;
384 489
        } else if (bo->htc->body_status == BS_ERROR) {
385 4
                wrk->stats->fetch_bad++;
386 485
        } else if (bo->htc->body_status == BS_NONE) {
387 485
                wrk->stats->fetch_none++;
388
        } else {
389 0
                WRONG("wrong bodystatus");
390
        }
391 1271
}
392