source: bin/varnishd/cache/cache_rfc2616.c @ a2921d

Revision a2921d, 8.7 KB checked in by Poul-Henning Kamp <phk@…>, 2 years ago (diff)

Move h_content_length from worker to busyobj

  • Property mode set to 100644
Line 
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 <math.h>
33#include <stdlib.h>
34
35#include "cache.h"
36
37#include "vtim.h"
38
39/*--------------------------------------------------------------------
40 * TTL and Age calculation in Varnish
41 *
42 * RFC2616 has a lot to say about how caches should calculate the TTL
43 * and expiry times of objects, but it sort of misses the case that
44 * applies to Varnish:  the server-side cache.
45 *
46 * A normal cache, shared or single-client, has no symbiotic relationship
47 * with the server, and therefore must take a very defensive attitude
48 * if the Data/Expiry/Age/max-age data does not make sense.  Overall
49 * the policy described in section 13 of RFC 2616 results in no caching
50 * happening on the first little sign of trouble.
51 *
52 * Varnish on the other hand tries to offload as many transactions from
53 * the backend as possible, and therefore just passing through everything
54 * if there is a clock-skew between backend and Varnish is not a workable
55 * choice.
56 *
57 * Varnish implements a policy which is RFC2616 compliant when there
58 * is no clockskew, and falls as gracefully as possible otherwise.
59 * Our "clockless cache" model is syntehsized from the bits of RFC2616
60 * that talks about how a cache should react to a clockless origin server,
61 * and more or less uses the inverse logic for the opposite relationship.
62 *
63 */
64
65void
66RFC2616_Ttl(const struct sess *sp)
67{
68        unsigned max_age, age;
69        double h_date, h_expires;
70        char *p;
71        const struct http *hp;
72        struct exp *expp;
73
74        expp = &sp->wrk->busyobj->exp;
75
76        hp = sp->wrk->beresp;
77
78        assert(expp->entered != 0.0 && !isnan(expp->entered));
79        /* If all else fails, cache using default ttl */
80        expp->ttl = cache_param->default_ttl;
81
82        max_age = age = 0;
83        h_expires = 0;
84        h_date = 0;
85
86        /*
87         * Initial cacheability determination per [RFC2616, 13.4]
88         * We do not support ranges yet, so 206 is out.
89         */
90
91        if (http_GetHdr(hp, H_Age, &p)) {
92                age = strtoul(p, NULL, 0);
93                expp->age = age;
94        }
95        if (http_GetHdr(hp, H_Expires, &p))
96                h_expires = VTIM_parse(p);
97
98        if (http_GetHdr(hp, H_Date, &p))
99                h_date = VTIM_parse(p);
100
101        switch (sp->err_code) {
102        default:
103                expp->ttl = -1.;
104                break;
105        case 200: /* OK */
106        case 203: /* Non-Authoritative Information */
107        case 300: /* Multiple Choices */
108        case 301: /* Moved Permanently */
109        case 302: /* Moved Temporarily */
110        case 307: /* Temporary Redirect */
111        case 410: /* Gone */
112        case 404: /* Not Found */
113                /*
114                 * First find any relative specification from the backend
115                 * These take precedence according to RFC2616, 13.2.4
116                 */
117
118                if ((http_GetHdrField(hp, H_Cache_Control, "s-maxage", &p) ||
119                    http_GetHdrField(hp, H_Cache_Control, "max-age", &p)) &&
120                    p != NULL) {
121
122                        if (*p == '-')
123                                max_age = 0;
124                        else
125                                max_age = strtoul(p, NULL, 0);
126
127                        if (age > max_age)
128                                expp->ttl = 0;
129                        else
130                                expp->ttl = max_age - age;
131                        break;
132                }
133
134                /* No expire header, fall back to default */
135                if (h_expires == 0)
136                        break;
137
138
139                /* If backend told us it is expired already, don't cache. */
140                if (h_expires < h_date) {
141                        expp->ttl = 0;
142                        break;
143                }
144
145                if (h_date == 0 ||
146                    fabs(h_date - expp->entered) < cache_param->clock_skew) {
147                        /*
148                         * If we have no Date: header or if it is
149                         * sufficiently close to our clock we will
150                         * trust Expires: relative to our own clock.
151                         */
152                        if (h_expires < expp->entered)
153                                expp->ttl = 0;
154                        else
155                                expp->ttl = h_expires -
156                                    expp->entered;
157                        break;
158                } else {
159                        /*
160                         * But even if the clocks are out of whack we can still
161                         * derive a relative time from the two headers.
162                         * (the negative ttl case is caught above)
163                         */
164                        expp->ttl = (int)(h_expires - h_date);
165                }
166
167        }
168
169        /* calculated TTL, Our time, Date, Expires, max-age, age */
170        WSP(sp, SLT_TTL,
171            "%u RFC %.0f %.0f %.0f %.0f %.0f %.0f %.0f %u",
172            sp->xid, expp->ttl, -1., -1., expp->entered,
173            expp->age, h_date, h_expires, max_age);
174}
175
176/*--------------------------------------------------------------------
177 * Body existence, fetch method and close policy.
178 */
179
180enum body_status
181RFC2616_Body(const struct sess *sp)
182{
183        struct http *hp;
184        char *b;
185
186        hp = sp->wrk->beresp;
187
188        if (hp->protover < 11 && !http_HdrIs(hp, H_Connection, "keep-alive"))
189                sp->wrk->busyobj->should_close = 1;
190        else if (http_HdrIs(hp, H_Connection, "close"))
191                sp->wrk->busyobj->should_close = 1;
192        else
193                sp->wrk->busyobj->should_close = 0;
194
195        if (!strcasecmp(http_GetReq(sp->wrk->bereq), "head")) {
196                /*
197                 * A HEAD request can never have a body in the reply,
198                 * no matter what the headers might say.
199                 * [RFC2516 4.3 p33]
200                 */
201                sp->wrk->stats.fetch_head++;
202                return (BS_NONE);
203        }
204
205        if (hp->status <= 199) {
206                /*
207                 * 1xx responses never have a body.
208                 * [RFC2616 4.3 p33]
209                 */
210                sp->wrk->stats.fetch_1xx++;
211                return (BS_NONE);
212        }
213
214        if (hp->status == 204) {
215                /*
216                 * 204 is "No Content", obviously don't expect a body.
217                 * [RFC2616 10.2.5 p60]
218                 */
219                sp->wrk->stats.fetch_204++;
220                return (BS_NONE);
221        }
222
223        if (hp->status == 304) {
224                /*
225                 * 304 is "Not Modified" it has no body.
226                 * [RFC2616 10.3.5 p63]
227                 */
228                sp->wrk->stats.fetch_304++;
229                return (BS_NONE);
230        }
231
232        if (http_HdrIs(hp, H_Transfer_Encoding, "chunked")) {
233                 sp->wrk->stats.fetch_chunked++;
234                return (BS_CHUNKED);
235        }
236
237        if (http_GetHdr(hp, H_Transfer_Encoding, &b)) {
238                sp->wrk->stats.fetch_bad++;
239                return (BS_ERROR);
240        }
241
242        if (http_GetHdr(hp, H_Content_Length,
243            &sp->wrk->busyobj->h_content_length)) {
244                sp->wrk->stats.fetch_length++;
245                return (BS_LENGTH);
246        }
247
248        if (http_HdrIs(hp, H_Connection, "keep-alive")) {
249                /*
250                 * Keep alive with neither TE=Chunked or C-Len is impossible.
251                 * We assume a zero length body.
252                 */
253                sp->wrk->stats.fetch_zero++;
254                return (BS_ZERO);
255        }
256
257        if (http_HdrIs(hp, H_Connection, "close")) {
258                /*
259                 * In this case, it is safe to just read what comes.
260                 */
261                sp->wrk->stats.fetch_close++;
262                return (BS_EOF);
263        }
264
265        if (hp->protover < 11) {
266                /*
267                 * With no Connection header, assume EOF.
268                 */
269                sp->wrk->stats.fetch_oldhttp++;
270                return (BS_EOF);
271        }
272
273        /*
274         * Fall back to EOF transfer.
275         */
276        sp->wrk->stats.fetch_eof++;
277        return (BS_EOF);
278}
279
280/*--------------------------------------------------------------------
281 * Find out if the request can receive a gzip'ed response
282 */
283
284unsigned
285RFC2616_Req_Gzip(const struct sess *sp)
286{
287
288
289        /*
290         * "x-gzip" is for http/1.0 backwards compat, final note in 14.3
291         * p104 says to not do q values for x-gzip, so we just test
292         * for its existence.
293         */
294        if (http_GetHdrData(sp->http, H_Accept_Encoding, "x-gzip", NULL))
295                return (1);
296
297        /*
298         * "gzip" is the real thing, but the 'q' value must be nonzero.
299         * We do not care a hoot if the client prefers some other
300         * compression more than gzip: Varnish only does gzip.
301         */
302        if (http_GetHdrQ(sp->http, H_Accept_Encoding, "gzip") > 0.)
303                return (1);
304
305        /* Bad client, no gzip. */
306        return (0);
307}
308
309/*--------------------------------------------------------------------*/
310
311int
312RFC2616_Do_Cond(const struct sess *sp)
313{
314        char *p, *e;
315        double ims;
316        int do_cond = 0;
317
318        /* RFC 2616 13.3.4 states we need to match both ETag
319           and If-Modified-Since if present*/
320
321        if (http_GetHdr(sp->http, H_If_Modified_Since, &p) ) {
322                if (!sp->wrk->obj->last_modified)
323                        return (0);
324                ims = VTIM_parse(p);
325                if (ims > sp->t_req)    /* [RFC2616 14.25] */
326                        return (0);
327                if (sp->wrk->obj->last_modified > ims)
328                        return (0);
329                do_cond = 1;
330        }
331
332        if (http_GetHdr(sp->http, H_If_None_Match, &p) &&
333            http_GetHdr(sp->wrk->obj->http, H_ETag, &e)) {
334                if (strcmp(p,e) != 0)
335                        return (0);
336                do_cond = 1;
337        }
338
339        return (do_cond);
340}
Note: See TracBrowser for help on using the repository browser.