source: bin/varnishd/cache/cache_response.c @ 2c8894

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

Move fetch_object 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 "cache.h"
33
34#include "vct.h"
35#include "vtim.h"
36
37/*--------------------------------------------------------------------*/
38
39static void
40res_dorange(const struct sess *sp, const char *r, ssize_t *plow, ssize_t *phigh)
41{
42        ssize_t low, high, has_low;
43
44        assert(sp->wrk->obj->response == 200);
45        if (strncmp(r, "bytes=", 6))
46                return;
47        r += 6;
48
49        /* The low end of range */
50        has_low = low = 0;
51        if (!vct_isdigit(*r) && *r != '-')
52                return;
53        while (vct_isdigit(*r)) {
54                has_low = 1;
55                low *= 10;
56                low += *r - '0';
57                r++;
58        }
59
60        if (low >= sp->wrk->obj->len)
61                return;
62
63        if (*r != '-')
64                return;
65        r++;
66
67        /* The high end of range */
68        if (vct_isdigit(*r)) {
69                high = 0;
70                while (vct_isdigit(*r)) {
71                        high *= 10;
72                        high += *r - '0';
73                        r++;
74                }
75                if (!has_low) {
76                        low = sp->wrk->obj->len - high;
77                        high = sp->wrk->obj->len - 1;
78                }
79        } else
80                high = sp->wrk->obj->len - 1;
81        if (*r != '\0')
82                return;
83
84        if (high >= sp->wrk->obj->len)
85                high = sp->wrk->obj->len - 1;
86
87        if (low > high)
88                return;
89
90        http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->resp,
91            "Content-Range: bytes %jd-%jd/%jd",
92            (intmax_t)low, (intmax_t)high, (intmax_t)sp->wrk->obj->len);
93        http_Unset(sp->wrk->resp, H_Content_Length);
94        assert(sp->wrk->res_mode & RES_LEN);
95        http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->resp,
96            "Content-Length: %jd", (intmax_t)(1 + high - low));
97        http_SetResp(sp->wrk->resp, "HTTP/1.1", 206, "Partial Content");
98
99        *plow = low;
100        *phigh = high;
101}
102
103/*--------------------------------------------------------------------*/
104
105void
106RES_BuildHttp(const struct sess *sp)
107{
108        char time_str[30];
109
110        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
111
112
113        http_ClrHeader(sp->wrk->resp);
114        sp->wrk->resp->logtag = HTTP_Tx;
115        http_CopyResp(sp->wrk->resp, sp->wrk->obj->http);
116        http_FilterFields(sp->wrk, sp->vsl_id, sp->wrk->resp, sp->wrk->obj->http,
117            HTTPH_A_DELIVER);
118
119        if (!(sp->wrk->res_mode & RES_LEN)) {
120                http_Unset(sp->wrk->resp, H_Content_Length);
121        } else if (cache_param->http_range_support) {
122                /* We only accept ranges if we know the length */
123                http_SetHeader(sp->wrk, sp->vsl_id, sp->wrk->resp,
124                    "Accept-Ranges: bytes");
125        }
126
127        if (sp->wrk->res_mode & RES_CHUNKED)
128                http_SetHeader(sp->wrk, sp->vsl_id, sp->wrk->resp,
129                    "Transfer-Encoding: chunked");
130
131        VTIM_format(VTIM_real(), time_str);
132        http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->resp, "Date: %s", time_str);
133
134        if (sp->xid != sp->wrk->obj->xid)
135                http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->resp,
136                    "X-Varnish: %u %u", sp->xid, sp->wrk->obj->xid);
137        else
138                http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->resp,
139                    "X-Varnish: %u", sp->xid);
140        http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->resp, "Age: %.0f",
141            sp->wrk->obj->exp.age + sp->t_resp - sp->wrk->obj->exp.entered);
142        http_SetHeader(sp->wrk, sp->vsl_id, sp->wrk->resp, "Via: 1.1 varnish");
143        http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->resp, "Connection: %s",
144            sp->doclose ? "close" : "keep-alive");
145}
146
147/*--------------------------------------------------------------------
148 * We have a gzip'ed object and need to ungzip it for a client which
149 * does not understand gzip.
150 * XXX: handle invalid gzip data better (how ?)
151 */
152
153static void
154res_WriteGunzipObj(const struct sess *sp)
155{
156        struct storage *st;
157        unsigned u = 0;
158        struct vgz *vg;
159        char obuf[cache_param->gzip_stack_buffer];
160        ssize_t obufl = 0;
161        int i;
162
163        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
164
165        vg = VGZ_NewUngzip(sp->wrk, "U D -");
166
167        VGZ_Obuf(vg, obuf, sizeof obuf);
168        VTAILQ_FOREACH(st, &sp->wrk->obj->store, list) {
169                CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
170                CHECK_OBJ_NOTNULL(st, STORAGE_MAGIC);
171                u += st->len;
172
173                VSC_C_main->n_objwrite++;
174
175                i = VGZ_WrwGunzip(sp->wrk, vg,
176                    st->ptr, st->len,
177                    obuf, sizeof obuf, &obufl);
178                /* XXX: error check */
179                (void)i;
180        }
181        if (obufl) {
182                (void)WRW_Write(sp->wrk, obuf, obufl);
183                (void)WRW_Flush(sp->wrk);
184        }
185        (void)VGZ_Destroy(&vg, sp->vsl_id);
186        assert(u == sp->wrk->obj->len);
187}
188
189/*--------------------------------------------------------------------*/
190
191static void
192res_WriteDirObj(const struct sess *sp, ssize_t low, ssize_t high)
193{
194        ssize_t u = 0;
195        size_t ptr, off, len;
196        struct storage *st;
197
198        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
199
200        ptr = 0;
201        VTAILQ_FOREACH(st, &sp->wrk->obj->store, list) {
202                CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
203                CHECK_OBJ_NOTNULL(st, STORAGE_MAGIC);
204                u += st->len;
205                len = st->len;
206                off = 0;
207                if (ptr + len <= low) {
208                        /* This segment is too early */
209                        ptr += len;
210                        continue;
211                }
212                if (ptr < low) {
213                        /* Chop front of segment off */
214                        off += (low - ptr);
215                        len -= (low - ptr);
216                        ptr += (low - ptr);
217                }
218                if (ptr + len > high)
219                        /* Chop tail of segment off */
220                        len = 1 + high - ptr;
221
222                ptr += len;
223
224                sp->wrk->acct_tmp.bodybytes += len;
225#ifdef SENDFILE_WORKS
226                /*
227                 * XXX: the overhead of setting up sendfile is not
228                 * XXX: epsilon and maybe not even delta, so avoid
229                 * XXX: engaging sendfile for small objects.
230                 * XXX: Should use getpagesize() ?
231                 */
232                if (st->fd >= 0 &&
233                    st->len >= cache_param->sendfile_threshold) {
234                        VSC_C_main->n_objsendfile++;
235                        WRW_Sendfile(sp->wrk, st->fd, st->where + off, len);
236                        continue;
237                }
238#endif /* SENDFILE_WORKS */
239                VSC_C_main->n_objwrite++;
240                (void)WRW_Write(sp->wrk, st->ptr + off, len);
241        }
242        assert(u == sp->wrk->obj->len);
243}
244
245/*--------------------------------------------------------------------
246 * Deliver an object.
247 * Attempt optimizations like 304 and 206 here.
248 */
249
250void
251RES_WriteObj(struct sess *sp)
252{
253        char *r;
254        ssize_t low, high;
255
256        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
257
258        WRW_Reserve(sp->wrk, &sp->fd);
259
260        if (sp->wrk->obj->response == 200 &&
261            sp->http->conds &&
262            RFC2616_Do_Cond(sp)) {
263                sp->wantbody = 0;
264                http_SetResp(sp->wrk->resp, "HTTP/1.1", 304, "Not Modified");
265                http_Unset(sp->wrk->resp, H_Content_Length);
266                http_Unset(sp->wrk->resp, H_Transfer_Encoding);
267        }
268
269        /*
270         * If nothing special planned, we can attempt Range support
271         */
272        low = 0;
273        high = sp->wrk->obj->len - 1;
274        if (
275            sp->wantbody &&
276            (sp->wrk->res_mode & RES_LEN) &&
277            !(sp->wrk->res_mode & (RES_ESI|RES_ESI_CHILD|RES_GUNZIP)) &&
278            cache_param->http_range_support &&
279            sp->wrk->obj->response == 200 &&
280            http_GetHdr(sp->http, H_Range, &r))
281                res_dorange(sp, r, &low, &high);
282
283        /*
284         * Always remove C-E if client don't grok it
285         */
286        if (sp->wrk->res_mode & RES_GUNZIP)
287                http_Unset(sp->wrk->resp, H_Content_Encoding);
288
289        /*
290         * Send HTTP protocol header, unless interior ESI object
291         */
292        if (!(sp->wrk->res_mode & RES_ESI_CHILD))
293                sp->wrk->acct_tmp.hdrbytes +=
294                    http_Write(sp->wrk, sp->vsl_id, sp->wrk->resp, 1);
295
296        if (!sp->wantbody)
297                sp->wrk->res_mode &= ~RES_CHUNKED;
298
299        if (sp->wrk->res_mode & RES_CHUNKED)
300                WRW_Chunked(sp->wrk);
301
302        if (!sp->wantbody) {
303                /* This was a HEAD or conditional request */
304        } else if (sp->wrk->obj->len == 0) {
305                /* Nothing to do here */
306        } else if (sp->wrk->res_mode & RES_ESI) {
307                ESI_Deliver(sp);
308        } else if (sp->wrk->res_mode & RES_ESI_CHILD && sp->wrk->gzip_resp) {
309                ESI_DeliverChild(sp);
310        } else if (sp->wrk->res_mode & RES_ESI_CHILD &&
311            !sp->wrk->gzip_resp && sp->wrk->obj->gziped) {
312                res_WriteGunzipObj(sp);
313        } else if (sp->wrk->res_mode & RES_GUNZIP) {
314                res_WriteGunzipObj(sp);
315        } else {
316                res_WriteDirObj(sp, low, high);
317        }
318
319        if (sp->wrk->res_mode & RES_CHUNKED &&
320            !(sp->wrk->res_mode & RES_ESI_CHILD))
321                WRW_EndChunk(sp->wrk);
322
323        if (WRW_FlushRelease(sp->wrk) && sp->fd >= 0)
324                SES_Close(sp, "remote closed");
325}
326
327/*--------------------------------------------------------------------*/
328
329void
330RES_StreamStart(struct sess *sp)
331{
332        struct stream_ctx *sctx;
333
334        sctx = sp->wrk->sctx;
335        CHECK_OBJ_NOTNULL(sctx, STREAM_CTX_MAGIC);
336
337        AZ(sp->wrk->res_mode & RES_ESI_CHILD);
338        AN(sp->wantbody);
339
340        WRW_Reserve(sp->wrk, &sp->fd);
341        /*
342         * Always remove C-E if client don't grok it
343         */
344        if (sp->wrk->res_mode & RES_GUNZIP)
345                http_Unset(sp->wrk->resp, H_Content_Encoding);
346
347        if (!(sp->wrk->res_mode & RES_CHUNKED) &&
348            sp->wrk->h_content_length != NULL)
349                http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->resp,
350                    "Content-Length: %s", sp->wrk->h_content_length);
351
352        sp->wrk->acct_tmp.hdrbytes +=
353            http_Write(sp->wrk, sp->vsl_id, sp->wrk->resp, 1);
354
355        if (sp->wrk->res_mode & RES_CHUNKED)
356                WRW_Chunked(sp->wrk);
357}
358
359void
360RES_StreamPoll(struct worker *w)
361{
362        struct stream_ctx *sctx;
363        struct storage *st;
364        ssize_t l, l2;
365        void *ptr;
366
367        CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
368        CHECK_OBJ_NOTNULL(w->busyobj->fetch_obj, OBJECT_MAGIC);
369        sctx = w->sctx;
370        CHECK_OBJ_NOTNULL(sctx, STREAM_CTX_MAGIC);
371        if (w->busyobj->fetch_obj->len == sctx->stream_next)
372                return;
373        assert(w->busyobj->fetch_obj->len > sctx->stream_next);
374        l = sctx->stream_front;
375        VTAILQ_FOREACH(st, &w->busyobj->fetch_obj->store, list) {
376                if (st->len + l <= sctx->stream_next) {
377                        l += st->len;
378                        continue;
379                }
380                l2 = st->len + l - sctx->stream_next;
381                ptr = st->ptr + (sctx->stream_next - l);
382                if (w->res_mode & RES_GUNZIP) {
383                        (void)VGZ_WrwGunzip(w, sctx->vgz, ptr, l2,
384                            sctx->obuf, sctx->obuf_len, &sctx->obuf_ptr);
385                } else {
386                        (void)WRW_Write(w, ptr, l2);
387                }
388                l += st->len;
389                sctx->stream_next += l2;
390        }
391        if (!(w->res_mode & RES_GUNZIP))
392                (void)WRW_Flush(w);
393
394        if (w->busyobj->fetch_obj->objcore == NULL ||
395            (w->busyobj->fetch_obj->objcore->flags & OC_F_PASS)) {
396                /*
397                 * This is a pass object, release storage as soon as we
398                 * have delivered it.
399                 */
400                while (1) {
401                        st = VTAILQ_FIRST(&w->busyobj->fetch_obj->store);
402                        if (st == NULL ||
403                            sctx->stream_front + st->len > sctx->stream_next)
404                                break;
405                        VTAILQ_REMOVE(&w->busyobj->fetch_obj->store, st, list);
406                        sctx->stream_front += st->len;
407                        STV_free(st);
408                }
409        }
410}
411
412void
413RES_StreamEnd(struct sess *sp)
414{
415        struct stream_ctx *sctx;
416
417        sctx = sp->wrk->sctx;
418        CHECK_OBJ_NOTNULL(sctx, STREAM_CTX_MAGIC);
419
420        if (sp->wrk->res_mode & RES_GUNZIP && sctx->obuf_ptr > 0)
421                (void)WRW_Write(sp->wrk, sctx->obuf, sctx->obuf_ptr);
422        if (sp->wrk->res_mode & RES_CHUNKED &&
423            !(sp->wrk->res_mode & RES_ESI_CHILD))
424                WRW_EndChunk(sp->wrk);
425        if (WRW_FlushRelease(sp->wrk))
426                SES_Close(sp, "remote closed");
427}
Note: See TracBrowser for help on using the repository browser.