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 12708
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 12708
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
74 12708
        assert(now != 0.0 && !isnan(now));
75 12708
        AN(t_origin);
76 12708
        AN(ttl);
77 12708
        AN(grace);
78 12708
        AN(keep);
79
80 12708
        *t_origin = now;
81 12708
        *ttl = cache_param->default_ttl;
82 12708
        *grace = cache_param->default_grace;
83 12708
        *keep = cache_param->default_keep;
84
85 12708
        hp = bo->beresp;
86
87 12708
        max_age = age = 0;
88 12708
        h_expires = 0;
89 12708
        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 12708
        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 120
                age = strtoul(p, NULL, 10);
103 120
                *t_origin -= age;
104
        }
105
106 12708
        if (http_GetHdr(hp, H_Expires, &p))
107 60
                h_expires = VTIM_parse(p);
108
109 12708
        if (http_GetHdr(hp, H_Date, &p))
110 12708
                h_date = VTIM_parse(p);
111
112 12708
        switch (http_GetStatus(hp)) {
113
        default:
114 228
                *ttl = -1.;
115 228
                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 12
                *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 24960
                if ((http_GetHdrField(hp, H_Cache_Control, "s-maxage", &p) ||
141 12660
                    http_GetHdrField(hp, H_Cache_Control, "max-age", &p)) &&
142 180
                    p != NULL) {
143
144 180
                        if (*p == '-')
145 12
                                max_age = 0;
146
                        else
147 168
                                max_age = strtoul(p, NULL, 0);
148
149 180
                        *ttl = max_age;
150 180
                        break;
151
                }
152
153
                /* No expire header, fall back to default */
154 12300
                if (h_expires == 0)
155 12252
                        break;
156
157
158
                /* If backend told us it is expired already, don't cache. */
159 48
                if (h_expires < h_date) {
160 36
                        *ttl = 0;
161 36
                        break;
162
                }
163
164 24
                if (h_date == 0 ||
165 12
                    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 12
                        *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 12708
        if (*ttl >= 0 && http_GetHdrField(hp, H_Cache_Control,
192 36
            "stale-while-revalidate", &p) && p != NULL) {
193
194 36
                if (*p == '-')
195 0
                        *grace = 0;
196
                else
197 36
                        *grace = strtoul(p, NULL, 0);
198
        }
199
200 50832
        VSLb(bo->vsl, SLT_TTL,
201
            "RFC %.0f %.0f %.0f %.0f %.0f %.0f %.0f %u %s",
202 38124
            *ttl, *grace, *keep, now,
203
            *t_origin, h_date, h_expires, max_age,
204 12708
            bo->uncacheable ? "uncacheable" : "cacheable");
205 12708
}
206
207
/*--------------------------------------------------------------------
208
 * Find out if the request can receive a gzip'ed response
209
 */
210
211
unsigned
212 25392
RFC2616_Req_Gzip(const struct http *hp)
213
{
214
215
216
        /*
217
         * "x-gzip" is for http/1.0 backwards compat, final note in 14.3
218
         * p104 says to not do q values for x-gzip, so we just test
219
         * for its existence.
220
         */
221 25392
        if (http_GetHdrToken(hp, H_Accept_Encoding, "x-gzip", NULL, NULL))
222 24
                return (1);
223
224
        /*
225
         * "gzip" is the real thing, but the 'q' value must be nonzero.
226
         * We do not care a hoot if the client prefers some other
227
         * compression more than gzip: Varnish only does gzip.
228
         */
229 25368
        if (http_GetHdrQ(hp, H_Accept_Encoding, "gzip") > 0.)
230 3132
                return (1);
231
232
        /* Bad client, no gzip. */
233 22236
        return (0);
234
}
235
236
/*--------------------------------------------------------------------*/
237
238
static inline int
239 48
rfc2616_strong_compare(const char *p, const char *e)
240
{
241 72
        if ((p[0] == 'W' && p[1] == '/') ||
242 36
            (e[0] == 'W' && e[1] == '/'))
243 36
                return (0);
244 12
        return (strcmp(p, e) == 0);
245
}
246
247
static inline int
248 252
rfc2616_weak_compare(const char *p, const char *e)
249
{
250 252
        if (p[0] == 'W' && p[1] == '/')
251 48
                p += 2;
252 252
        if (e[0] == 'W' && e[1] == '/')
253 48
                e += 2;
254 252
        return (strcmp(p, e) == 0);
255
}
256
257
int
258 1080
RFC2616_Do_Cond(const struct req *req)
259
{
260
        const char *p, *e;
261
        double ims, lm;
262
263
        /*
264
         * We MUST ignore If-Modified-Since if we have an If-None-Match
265
         * header [RFC7232 3.3 p16].
266
         */
267 1080
        if (http_GetHdr(req->http, H_If_None_Match, &p)) {
268 312
                if (!http_GetHdr(req->resp, H_ETag, &e))
269 12
                        return (0);
270 300
                if (http_GetHdr(req->http, H_Range, NULL))
271 48
                        return (rfc2616_strong_compare(p, e));
272
                else
273 252
                        return (rfc2616_weak_compare(p, e));
274
        }
275
276 768
        if (http_GetHdr(req->http, H_If_Modified_Since, &p)) {
277 288
                ims = VTIM_parse(p);
278 288
                if (!ims || ims > req->t_req)   /* [RFC7232 3.3 p16] */
279 0
                        return (0);
280 288
                if (http_GetHdr(req->resp, H_Last_Modified, &p)) {
281 264
                        lm = VTIM_parse(p);
282 264
                        if (!lm || lm > ims)
283 60
                                return (0);
284 204
                        return (1);
285
                }
286 24
                AZ(ObjGetDouble(req->wrk, req->objcore, OA_LASTMODIFIED, &lm));
287 24
                if (lm > ims)
288 12
                        return (0);
289 12
                return (1);
290
        }
291
292 480
        return (0);
293
}
294
295
/*--------------------------------------------------------------------*/
296
297
void
298 2772
RFC2616_Weaken_Etag(struct http *hp)
299
{
300
        const char *p;
301
302 2772
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
303
304 2772
        if (!http_GetHdr(hp, H_ETag, &p))
305 5328
                return;
306 120
        AN(p);
307 120
        if (p[0] == 'W' && p[1] == '/')
308 24
                return;
309 96
        http_Unset(hp, H_ETag);
310 96
        http_PrintfHeader(hp, "ETag: W/%s", p);
311
}
312
313
/*--------------------------------------------------------------------*/
314
315
void
316 972
RFC2616_Vary_AE(struct http *hp)
317
{
318
        const char *vary;
319
320 972
        if (http_GetHdrToken(hp, H_Vary, "Accept-Encoding", NULL, NULL))
321 72
                return;
322 900
        if (http_GetHdr(hp, H_Vary, &vary)) {
323 24
                http_Unset(hp, H_Vary);
324 24
                http_PrintfHeader(hp, "Vary: %s, Accept-Encoding", vary);
325
        } else {
326 876
                http_SetHeader(hp, "Vary: Accept-Encoding");
327
        }
328
}
329
330
/*--------------------------------------------------------------------*/
331
332
void
333 18648
RFC2616_Response_Body(const struct worker *wrk, const struct busyobj *bo)
334
{
335
336 18648
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
337 18648
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
338
339
        /*
340
         * Figure out how the fetch is supposed to happen, before the
341
         * headers are adultered by VCL
342
         */
343 18648
        if (!strcasecmp(http_GetMethod(bo->bereq), "head")) {
344
                /*
345
                 * A HEAD request can never have a body in the reply,
346
                 * no matter what the headers might say.
347
                 * [RFC7231 4.3.2 p25]
348
                 */
349 24
                wrk->stats->fetch_head++;
350 24
                bo->htc->body_status = BS_NONE;
351 18624
        } else if (http_GetStatus(bo->beresp) <= 199) {
352
                /*
353
                 * 1xx responses never have a body.
354
                 * [RFC7230 3.3.2 p31]
355
                 * ... but we should never see them.
356
                 */
357 0
                wrk->stats->fetch_1xx++;
358 0
                bo->htc->body_status = BS_ERROR;
359 18624
        } else if (http_IsStatus(bo->beresp, 204)) {
360
                /*
361
                 * 204 is "No Content", obviously don't expect a body.
362
                 * [RFC7230 3.3.1 p29 and 3.3.2 p31]
363
                 */
364 60
                wrk->stats->fetch_204++;
365 108
                if ((http_GetHdr(bo->beresp, H_Content_Length, NULL) &&
366 84
                    bo->htc->content_length != 0) ||
367 36
                    http_GetHdr(bo->beresp, H_Transfer_Encoding, NULL))
368 24
                        bo->htc->body_status = BS_ERROR;
369
                else
370 36
                        bo->htc->body_status = BS_NONE;
371 18564
        } else if (http_IsStatus(bo->beresp, 304)) {
372
                /*
373
                 * 304 is "Not Modified" it has no body.
374
                 * [RFC7230 3.3 p28]
375
                 */
376 264
                wrk->stats->fetch_304++;
377 264
                bo->htc->body_status = BS_NONE;
378 18300
        } else if (bo->htc->body_status == BS_CHUNKED) {
379 1428
                wrk->stats->fetch_chunked++;
380 16872
        } else if (bo->htc->body_status == BS_LENGTH) {
381 9684
                assert(bo->htc->content_length > 0);
382 9684
                wrk->stats->fetch_length++;
383 7188
        } else if (bo->htc->body_status == BS_EOF) {
384 252
                wrk->stats->fetch_eof++;
385 6936
        } else if (bo->htc->body_status == BS_ERROR) {
386 48
                wrk->stats->fetch_bad++;
387 6888
        } else if (bo->htc->body_status == BS_NONE) {
388 6888
                wrk->stats->fetch_none++;
389
        } else {
390 0
                WRONG("wrong bodystatus");
391
        }
392 18648
}
393