source: bin/varnishd/cache/cache_esi_deliver.c @ ef234ec

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

Start descoping the fetch-from-backend stuff into struct busyobj.

The fetching worker has wrk->busyobj set

Push is_g[un]zip flags into busyobj as proof of concept.

  • Property mode set to 100644
Line 
1/*-
2 * Copyright (c) 2011 Varnish Software AS
3 * All rights reserved.
4 *
5 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * VED - Varnish Esi Delivery
29 */
30
31#include "config.h"
32
33#include <stdio.h>
34#include <stdlib.h>
35
36#include "cache.h"
37
38#include "cache_esi.h"
39#include "vend.h"
40#include "vgz.h"
41
42/*--------------------------------------------------------------------*/
43
44static void
45ved_include(struct sess *sp, const char *src, const char *host)
46{
47        struct object *obj;
48        struct worker *w;
49        char *sp_ws_wm;
50        char *wrk_ws_wm;
51        unsigned sxid, res_mode;
52
53        w = sp->wrk;
54
55        if (sp->esi_level >= cache_param->max_esi_depth)
56                return;
57        sp->esi_level++;
58
59        (void)WRW_FlushRelease(w);
60
61        obj = sp->wrk->obj;
62        sp->wrk->obj = NULL;
63        res_mode = sp->wrk->res_mode;
64
65        /* Reset request to status before we started messing with it */
66        HTTP_Copy(sp->http, sp->http0);
67
68        /* Take a workspace snapshot */
69        sp_ws_wm = WS_Snapshot(sp->ws);
70        wrk_ws_wm = WS_Snapshot(w->ws);
71
72        http_SetH(sp->http, HTTP_HDR_URL, src);
73        if (host != NULL && *host != '\0')  {
74                http_Unset(sp->http, H_Host);
75                http_Unset(sp->http, H_If_Modified_Since);
76                http_SetHeader(w, sp->vsl_id, sp->http, host);
77        }
78        /*
79         * XXX: We should decide if we should cache the director
80         * XXX: or not (for session/backend coupling).  Until then
81         * XXX: make sure we don't trip up the check in vcl_recv.
82         */
83        sp->director = NULL;
84        sp->step = STP_RECV;
85        http_ForceGet(sp->http);
86
87        /* Don't do conditionals */
88        sp->http->conds = 0;
89        http_Unset(sp->http, H_If_Modified_Since);
90
91        /* Client content already taken care of */
92        http_Unset(sp->http, H_Content_Length);
93
94        sp->wrk->do_esi = 0;
95        sp->wrk->do_gzip = 0;
96        sp->wrk->do_gunzip = 0;
97        sp->wrk->do_stream = 0;
98
99        sxid = sp->xid;
100        while (1) {
101                sp->wrk = w;
102                CNT_Session(sp);
103                if (sp->step == STP_DONE)
104                        break;
105                AZ(sp->wrk);
106                WSL_Flush(w, 0);
107                DSL(0x20, SLT_Debug, sp->vsl_id, "loop waiting for ESI");
108                (void)usleep(10000);
109        }
110        sp->xid = sxid;
111        AN(sp->wrk);
112        assert(sp->step == STP_DONE);
113        sp->esi_level--;
114        sp->wrk->obj = obj;
115        sp->wrk->res_mode = res_mode;
116
117        /* Reset the workspace */
118        WS_Reset(sp->ws, sp_ws_wm);
119        WS_Reset(w->ws, wrk_ws_wm);
120
121        WRW_Reserve(sp->wrk, &sp->fd);
122        if (sp->wrk->res_mode & RES_CHUNKED)
123                WRW_Chunked(sp->wrk);
124}
125
126/*--------------------------------------------------------------------*/
127
128
129//#define Debug(fmt, ...) printf(fmt, __VA_ARGS__)
130#define Debug(fmt, ...) /**/
131
132static ssize_t
133ved_decode_len(uint8_t **pp)
134{
135        uint8_t *p;
136        ssize_t l;
137
138        p = *pp;
139        switch (*p & 15) {
140        case 1:
141                l = p[1];
142                p += 2;
143                break;
144        case 2:
145                l = vbe16dec(p + 1);
146                p += 3;
147                break;
148        case 8:
149                l = vbe64dec(p + 1);
150                p += 9;
151                break;
152        default:
153                printf("Illegal Length %d %d\n", *p, (*p & 15));
154                INCOMPL();
155        }
156        *pp = p;
157        assert(l > 0);
158        return (l);
159}
160
161/*---------------------------------------------------------------------
162 * If a gzip'ed ESI object includes a ungzip'ed object, we need to make
163 * it looked like a gzip'ed data stream.  The official way to do so would
164 * be to fire up libvgz and gzip it, but we don't, we fake it.
165 *
166 * First, we cannot know if it is ungzip'ed on purpose, the admin may
167 * know something we don't.
168 *
169 * What do you mean "BS ?"
170 *
171 * All right then...
172 *
173 * The matter of the fact is that we simply will not fire up a gzip in
174 * the output path because it costs too much memory and CPU, so we simply
175 * wrap the data in very convenient "gzip copy-blocks" and send it down
176 * the stream with a bit more overhead.
177 */
178
179static void
180ved_pretend_gzip(const struct sess *sp, const uint8_t *p, ssize_t l)
181{
182        uint8_t buf1[5], buf2[5];
183        uint16_t lx;
184
185        lx = 65535;
186        buf1[0] = 0;
187        vle16enc(buf1 + 1, lx);
188        vle16enc(buf1 + 3, ~lx);
189
190        while (l > 0) {
191                if (l >= 65535) {
192                        lx = 65535;
193                        (void)WRW_Write(sp->wrk, buf1, sizeof buf1);
194                } else {
195                        lx = (uint16_t)l;
196                        buf2[0] = 0;
197                        vle16enc(buf2 + 1, lx);
198                        vle16enc(buf2 + 3, ~lx);
199                        (void)WRW_Write(sp->wrk, buf2, sizeof buf2);
200                }
201                (void)WRW_Write(sp->wrk, p, lx);
202                sp->wrk->crc = crc32(sp->wrk->crc, p, lx);
203                sp->wrk->l_crc += lx;
204                l -= lx;
205                p += lx;
206        }
207        /* buf2 is local, have to flush */
208        (void)WRW_Flush(sp->wrk);
209}
210
211/*---------------------------------------------------------------------
212 */
213
214static const uint8_t gzip_hdr[] = {
215        0x1f, 0x8b, 0x08,
216        0x00, 0x00, 0x00, 0x00,
217        0x00,
218        0x02, 0x03
219};
220
221void
222ESI_Deliver(struct sess *sp)
223{
224        struct storage *st;
225        uint8_t *p, *e, *q, *r;
226        unsigned off;
227        ssize_t l, l2, l_icrc = 0;
228        uint32_t icrc = 0;
229        uint8_t tailbuf[8 + 5];
230        int isgzip;
231        struct vgz *vgz = NULL;
232        char obuf[cache_param->gzip_stack_buffer];
233        ssize_t obufl = 0;
234        size_t dl;
235        const void *dp;
236        int i;
237
238        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
239        st = sp->wrk->obj->esidata;
240        AN(st);
241        assert(sizeof obuf >= 1024);
242
243        obuf[0] = 0;    /* For flexelint */
244
245        p = st->ptr;
246        e = st->ptr + st->len;
247
248        if (*p == VEC_GZ) {
249                isgzip = 1;
250                p++;
251        } else {
252                isgzip = 0;
253        }
254
255        if (sp->esi_level == 0) {
256                /*
257                 * Only the top level document gets to decide this.
258                 */
259                sp->wrk->gzip_resp = 0;
260                if (isgzip && !(sp->wrk->res_mode & RES_GUNZIP)) {
261                        assert(sizeof gzip_hdr == 10);
262                        /* Send out the gzip header */
263                        (void)WRW_Write(sp->wrk, gzip_hdr, 10);
264                        sp->wrk->l_crc = 0;
265                        sp->wrk->gzip_resp = 1;
266                        sp->wrk->crc = crc32(0L, Z_NULL, 0);
267                }
268        }
269
270        if (isgzip && !sp->wrk->gzip_resp) {
271                vgz = VGZ_NewUngzip(sp->wrk, "U D E");
272
273                /* Feed a gzip header to gunzip to make it happy */
274                VGZ_Ibuf(vgz, gzip_hdr, sizeof gzip_hdr);
275                VGZ_Obuf(vgz, obuf, sizeof obuf);
276                i = VGZ_Gunzip(vgz, &dp, &dl);
277                assert(i == VGZ_OK);
278                assert(VGZ_IbufEmpty(vgz));
279                assert(dl == 0);
280
281                obufl = 0;
282        }
283
284        st = VTAILQ_FIRST(&sp->wrk->obj->store);
285        off = 0;
286
287        while (p < e) {
288                switch (*p) {
289                case VEC_V1:
290                case VEC_V2:
291                case VEC_V8:
292                        l = ved_decode_len(&p);
293                        if (isgzip) {
294                                assert(*p == VEC_C1 || *p == VEC_C2 ||
295                                    *p == VEC_C8);
296                                l_icrc = ved_decode_len(&p);
297                                icrc = vbe32dec(p);
298                                p += 4;
299                                if (sp->wrk->gzip_resp) {
300                                        sp->wrk->crc = crc32_combine(
301                                            sp->wrk->crc, icrc, l_icrc);
302                                        sp->wrk->l_crc += l_icrc;
303                                }
304                        }
305                        /*
306                         * There is no guarantee that the 'l' bytes are all
307                         * in the same storage segment, so loop over storage
308                         * until we have processed them all.
309                         */
310                        while (l > 0) {
311                                l2 = l;
312                                if (l2 > st->len - off)
313                                        l2 = st->len - off;
314                                l -= l2;
315
316                                if (sp->wrk->gzip_resp && isgzip) {
317                                        /*
318                                         * We have a gzip'ed VEC and delivers
319                                         * a gzip'ed ESI response.
320                                         */
321                                        (void)WRW_Write(sp->wrk, st->ptr + off, l2);
322                                } else if (sp->wrk->gzip_resp) {
323                                        /*
324                                         * A gzip'ed ESI response, but the VEC
325                                         * was not gzip'ed.
326                                         */
327                                        ved_pretend_gzip(sp, st->ptr + off, l2);
328                                } else if (isgzip) {
329                                        /*
330                                         * A gzip'ed VEC, but ungzip'ed ESI
331                                         * response
332                                         */
333                                        AN(vgz);
334                                        i = VGZ_WrwGunzip(sp->wrk, vgz,
335                                                st->ptr + off, l2,
336                                                obuf, sizeof obuf, &obufl);
337                                        if (WRW_Error(sp->wrk)) {
338                                                SES_Close(sp, "remote closed");
339                                                p = e;
340                                                break;
341                                        }
342                                        assert (i == VGZ_OK || i == VGZ_END);
343                                } else {
344                                        /*
345                                         * Ungzip'ed VEC, ungzip'ed ESI response
346                                         */
347                                        (void)WRW_Write(sp->wrk, st->ptr + off, l2);
348                                }
349                                off += l2;
350                                if (off == st->len) {
351                                        st = VTAILQ_NEXT(st, list);
352                                        off = 0;
353                                }
354                        }
355                        break;
356                case VEC_S1:
357                case VEC_S2:
358                case VEC_S8:
359                        l = ved_decode_len(&p);
360                        Debug("SKIP1(%d)\n", (int)l);
361                        /*
362                         * There is no guarantee that the 'l' bytes are all
363                         * in the same storage segment, so loop over storage
364                         * until we have processed them all.
365                         */
366                        while (l > 0) {
367                                l2 = l;
368                                if (l2 > st->len - off)
369                                        l2 = st->len - off;
370                                l -= l2;
371                                off += l2;
372                                if (off == st->len) {
373                                        st = VTAILQ_NEXT(st, list);
374                                        off = 0;
375                                }
376                        }
377                        break;
378                case VEC_INCL:
379                        p++;
380                        q = (void*)strchr((const char*)p, '\0');
381                        AN(q);
382                        q++;
383                        r = (void*)strchr((const char*)q, '\0');
384                        AN(r);
385                        if (obufl > 0) {
386                                (void)WRW_Write(sp->wrk, obuf, obufl);
387                                obufl = 0;
388                        }
389                        if (WRW_Flush(sp->wrk)) {
390                                SES_Close(sp, "remote closed");
391                                p = e;
392                                break;
393                        }
394                        Debug("INCL [%s][%s] BEGIN\n", q, p);
395                        ved_include(sp, (const char*)q, (const char*)p);
396                        Debug("INCL [%s][%s] END\n", q, p);
397                        p = r + 1;
398                        break;
399                default:
400                        printf("XXXX 0x%02x [%s]\n", *p, p);
401                        INCOMPL();
402                }
403        }
404        if (vgz != NULL) {
405                if (obufl > 0)
406                        (void)WRW_Write(sp->wrk, obuf, obufl);
407                (void)VGZ_Destroy(&vgz, sp->vsl_id);
408        }
409        if (sp->wrk->gzip_resp && sp->esi_level == 0) {
410                /* Emit a gzip literal block with finish bit set */
411                tailbuf[0] = 0x01;
412                tailbuf[1] = 0x00;
413                tailbuf[2] = 0x00;
414                tailbuf[3] = 0xff;
415                tailbuf[4] = 0xff;
416
417                /* Emit CRC32 */
418                vle32enc(tailbuf + 5, sp->wrk->crc);
419
420                /* MOD(2^32) length */
421                vle32enc(tailbuf + 9, sp->wrk->l_crc);
422
423                (void)WRW_Write(sp->wrk, tailbuf, 13);
424        }
425        (void)WRW_Flush(sp->wrk);
426}
427
428/*---------------------------------------------------------------------
429 * Include an object in a gzip'ed ESI object delivery
430 */
431
432static uint8_t
433ved_deliver_byterange(const struct sess *sp, ssize_t low, ssize_t high)
434{
435        struct storage *st;
436        ssize_t l, lx;
437        u_char *p;
438
439//printf("BR %jd %jd\n", low, high);
440        lx = 0;
441        VTAILQ_FOREACH(st, &sp->wrk->obj->store, list) {
442                p = st->ptr;
443                l = st->len;
444//printf("[0-] %jd %jd\n", lx, lx + l);
445                if (lx + l < low) {
446                        lx += l;
447                        continue;
448                }
449                if (lx == high)
450                        return (p[0]);
451                assert(lx < high);
452                if (lx < low) {
453                        p += (low - lx);
454                        l -= (low - lx);
455                        lx = low;
456                }
457//printf("[1-] %jd %jd\n", lx, lx + l);
458                if (lx + l >= high)
459                        l = high - lx;
460//printf("[2-] %jd %jd\n", lx, lx + l);
461                assert(lx >= low && lx + l <= high);
462                if (l != 0)
463                        (void)WRW_Write(sp->wrk, p, l);
464                if (lx + st->len > high)
465                        return(p[l]);
466                lx += st->len;
467        }
468        INCOMPL();
469}
470
471void
472ESI_DeliverChild(const struct sess *sp)
473{
474        struct storage *st;
475        struct object *obj;
476        ssize_t start, last, stop, lpad;
477        u_char *p, cc;
478        uint32_t icrc;
479        uint32_t ilen;
480        uint8_t *dbits;
481
482        if (!sp->wrk->obj->gziped) {
483                VTAILQ_FOREACH(st, &sp->wrk->obj->store, list)
484                        ved_pretend_gzip(sp, st->ptr, st->len);
485                return;
486        }
487        /*
488         * This is the interesting case: Deliver all the deflate
489         * blocks, stripping the "LAST" bit of the last one and
490         * padding it, as necessary, to a byte boundary.
491         */
492
493        dbits = (void*)WS_Alloc(sp->wrk->ws, 8);
494        AN(dbits);
495        obj = sp->wrk->obj;
496        CHECK_OBJ_NOTNULL(obj, OBJECT_MAGIC);
497        start = obj->gzip_start;
498        last = obj->gzip_last;
499        stop = obj->gzip_stop;
500        assert(start > 0 && start < obj->len * 8);
501        assert(last > 0 && last < obj->len * 8);
502        assert(stop > 0 && stop < obj->len * 8);
503        assert(last >= start);
504        assert(last < stop);
505
506        /* The start bit must be byte aligned. */
507        AZ(start & 7);
508
509        /*
510         * XXX: optimize for the case where the 'last'
511         * XXX: bit is in a empty copy block
512         */
513        *dbits = ved_deliver_byterange(sp, start/8, last/8);
514        *dbits &= ~(1U << (last & 7));
515        (void)WRW_Write(sp->wrk, dbits, 1);
516        cc = ved_deliver_byterange(sp, 1 + last/8, stop/8);
517        switch((int)(stop & 7)) {
518        case 0: /* xxxxxxxx */
519                /* I think we have an off by one here, but that's OK */
520                lpad = 0;
521                break;
522        case 1: /* x000.... 00000000 00000000 11111111 11111111 */
523        case 3: /* xxx000.. 00000000 00000000 11111111 11111111 */
524        case 5: /* xxxxx000 00000000 00000000 11111111 11111111 */
525                dbits[1] = cc | 0x00;
526                dbits[2] = 0x00; dbits[3] = 0x00;
527                dbits[4] = 0xff; dbits[5] = 0xff;
528                lpad = 5;
529                break;
530        case 2: /* xx010000 00000100 00000001 00000000 */
531                dbits[1] = cc | 0x08;
532                dbits[2] = 0x20;
533                dbits[3] = 0x80;
534                dbits[4] = 0x00;
535                lpad = 4;
536                break;
537        case 4: /* xxxx0100 00000001 00000000 */
538                dbits[1] = cc | 0x20;
539                dbits[2] = 0x80;
540                dbits[3] = 0x00;
541                lpad = 3;
542                break;
543        case 6: /* xxxxxx01 00000000 */
544                dbits[1] = cc | 0x80;
545                dbits[2] = 0x00;
546                lpad = 2;
547                break;
548        case 7: /* xxxxxxx0 00...... 00000000 00000000 11111111 11111111 */
549                dbits[1] = cc | 0x00;
550                dbits[2] = 0x00;
551                dbits[3] = 0x00; dbits[4] = 0x00;
552                dbits[5] = 0xff; dbits[6] = 0xff;
553                lpad = 6;
554                break;
555        default:
556                INCOMPL();
557        }
558        if (lpad > 0)
559                (void)WRW_Write(sp->wrk, dbits + 1, lpad);
560        st = VTAILQ_LAST(&sp->wrk->obj->store, storagehead);
561        assert(st->len > 8);
562
563        p = st->ptr + st->len - 8;
564        icrc = vle32dec(p);
565        ilen = vle32dec(p + 4);
566        sp->wrk->crc = crc32_combine(sp->wrk->crc, icrc, ilen);
567        sp->wrk->l_crc += ilen;
568}
Note: See TracBrowser for help on using the repository browser.