varnish-cache/bin/varnishd/cache/cache_esi_fetch.c
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
 * VEF Varnish Esi Fetching
29
 */
30
31
#include "config.h"
32
33
#include <errno.h>
34
#include <stdlib.h>
35
36
#include "cache_varnishd.h"
37
#include "cache_filter.h"
38
#include "cache_vgz.h"
39
40
#include "cache_esi.h"
41
42
#include "vrnd.h"
43
44
/*---------------------------------------------------------------------
45
 */
46
47
struct vef_priv {
48
        unsigned                magic;
49
#define VEF_MAGIC               0xf104b51f
50
        int                     error;
51
        ssize_t                 tot;
52
53
        struct vgz              *vgz;
54
55
        struct vep_state        *vep;
56
57
        char                    *ibuf;
58
        char                    *ibuf_i;
59
        char                    *ibuf_o;
60
        ssize_t                 ibuf_sz;
61
};
62
63
static ssize_t
64 10149
vfp_vep_callback(struct vfp_ctx *vc, void *priv, ssize_t l, enum vgz_flag flg)
65
{
66
        struct vef_priv *vef;
67
        ssize_t dl;
68
        const void *dp;
69
        uint8_t *ptr;
70
        int i;
71
72 10149
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
73 10149
        CAST_OBJ_NOTNULL(vef, priv, VEF_MAGIC);
74 10149
        assert(l >= 0);
75
76 10149
        if (vef->error) {
77 24
                vef->tot += l;
78 24
                return (vef->tot);
79
        }
80
81
        /*
82
         * l == 0 is valid when 'flg' calls for action, but in the
83
         * normal case we can just ignore a l==0 request.
84
         * (It would cause Z_BUF_ERROR anyway)
85
         */
86 10125
        if (l == 0 && flg == VGZ_NORMAL)
87 1331
                return (vef->tot);
88
89 8794
        VGZ_Ibuf(vef->vgz, vef->ibuf_o, l);
90
        do {
91 9946
                dl = 0;
92 9946
                if (VFP_GetStorage(vc, &dl, &ptr) != VFP_OK) {
93 24
                        vef->error = ENOMEM;
94 24
                        vef->tot += l;
95 24
                        return (vef->tot);
96
                }
97 9922
                VGZ_Obuf(vef->vgz, ptr, dl);
98 9922
                i = VGZ_Gzip(vef->vgz, &dp, &dl, flg);
99 9922
                VGZ_UpdateObj(vc, vef->vgz, VUA_UPDATE);
100 9922
                if (dl > 0) {
101 3783
                        vef->tot += dl;
102 3783
                        VFP_Extend(vc, dl);
103
                }
104 9922
        } while (i != VGZ_ERROR &&
105 19844
            (!VGZ_IbufEmpty(vef->vgz) || VGZ_ObufFull(vef->vgz)));
106 8770
        assert(i == VGZ_ERROR || VGZ_IbufEmpty(vef->vgz));
107 8770
        vef->ibuf_o += l;
108 8770
        return (vef->tot);
109
}
110
111
static enum vfp_status
112 1908
vfp_esi_end(struct vfp_ctx *vc, struct vef_priv *vef,
113
    enum vfp_status retval)
114
{
115
        struct vsb *vsb;
116
        ssize_t l;
117
        void *p;
118
119 1908
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
120 1908
        CHECK_OBJ_NOTNULL(vef, VEF_MAGIC);
121
122 1908
        vsb = VEP_Finish(vef->vep);
123
124 1908
        if (vsb != NULL) {
125 1104
                if (retval == VFP_END) {
126 1092
                        l = VSB_len(vsb);
127 1092
                        assert(l > 0);
128 1092
                        p = ObjSetAttr(vc->wrk, vc->oc,
129 1092
                            OA_ESIDATA, l, VSB_data(vsb));
130 1092
                        if (p == NULL) {
131 0
                                retval = VFP_Error(vc,
132
                                    "Could not allocate storage for esidata");
133
                        }
134
                }
135 1104
                VSB_destroy(&vsb);
136
        }
137
138 1908
        if (vef->vgz != NULL) {
139 468
                VGZ_UpdateObj(vc, vef->vgz, VUA_END_GZIP);
140 468
                if (VGZ_Destroy(&vef->vgz) != VGZ_END)
141 24
                        retval = VFP_Error(vc,
142
                            "ESI+Gzip Failed at the very end");
143
        }
144 1908
        if (vef->ibuf != NULL)
145 468
                free(vef->ibuf);
146 1908
        FREE_OBJ(vef);
147 1908
        return (retval);
148
}
149
150
static enum vfp_status v_matchproto_(vfp_init_f)
151 468
vfp_esi_gzip_init(struct vfp_ctx *vc, struct vfp_entry *vfe)
152
{
153
        struct vef_priv *vef;
154
155 468
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
156 468
        CHECK_OBJ_NOTNULL(vc->req, HTTP_MAGIC);
157 468
        CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
158 468
        if (http_GetStatus(vc->resp) == 206) {
159 0
                VSLb(vc->wrk->vsl, SLT_VCL_Error,
160
                    "Attempted ESI on partial (206) response");
161 0
                return (VFP_ERROR);
162
        }
163 468
        ALLOC_OBJ(vef, VEF_MAGIC);
164 468
        if (vef == NULL)
165 0
                return (VFP_ERROR);
166 468
        vc->obj_flags |= OF_GZIPED | OF_CHGGZIP | OF_ESIPROC;
167 468
        vef->vgz = VGZ_NewGzip(vc->wrk->vsl, "G F E");
168 468
        vef->vep = VEP_Init(vc, vc->req, vfp_vep_callback, vef);
169 468
        vef->ibuf_sz = cache_param->gzip_buffer;
170 468
        vef->ibuf = calloc(1L, vef->ibuf_sz);
171 468
        if (vef->ibuf == NULL)
172 0
                return (vfp_esi_end(vc, vef, VFP_ERROR));
173 468
        vef->ibuf_i = vef->ibuf;
174 468
        vef->ibuf_o = vef->ibuf;
175 468
        vfe->priv1 = vef;
176
177 468
        RFC2616_Weaken_Etag(vc->resp);
178 468
        http_Unset(vc->resp, H_Content_Length);
179 468
        http_Unset(vc->resp, H_Content_Encoding);
180 468
        http_SetHeader(vc->resp, "Content-Encoding: gzip");
181
182 468
        RFC2616_Vary_AE(vc->resp);
183
184 468
        return (VFP_OK);
185
}
186
187
static enum vfp_status v_matchproto_(vfp_pull_f)
188 5766
vfp_esi_gzip_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p,
189
   ssize_t *lp)
190
{
191
        enum vfp_status vp;
192
        ssize_t d, l;
193
        struct vef_priv *vef;
194
195 5766
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
196 5766
        CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
197 5766
        CAST_OBJ_NOTNULL(vef, vfe->priv1, VEF_MAGIC);
198 5766
        AN(p);
199 5766
        AN(lp);
200 5766
        *lp = 0;
201 5766
        l = vef->ibuf_sz - (vef->ibuf_i - vef->ibuf);
202 5766
        if (DO_DEBUG(DBG_ESI_CHOP)) {
203 4710
                d = (VRND_RandomTestable() & 3) + 1;
204 4710
                if (d < l)
205 4710
                        l = d;
206
        }
207 5766
        vp = VFP_Suck(vc, vef->ibuf_i, &l);
208
209 5766
        if (l > 0) {
210 5454
                VEP_Parse(vef->vep, vef->ibuf_i, l);
211 5454
                vef->ibuf_i += l;
212 5454
                assert(vef->ibuf_o >= vef->ibuf && vef->ibuf_o <= vef->ibuf_i);
213 5454
                if (vef->error) {
214 12
                        errno = vef->error;
215 12
                        return (VFP_ERROR);
216
                }
217 5442
                l = vef->ibuf_i - vef->ibuf_o;
218 5442
                if (l > 0)
219 374
                        memmove(vef->ibuf, vef->ibuf_o, l);
220 5442
                vef->ibuf_o = vef->ibuf;
221 5442
                vef->ibuf_i = vef->ibuf + l;
222
        }
223 5754
        if (vp == VFP_END) {
224 432
                vp = vfp_esi_end(vc, vef, vp);
225 432
                vfe->priv1 = NULL;
226
        }
227 5754
        return (vp);
228
}
229
230
static enum vfp_status v_matchproto_(vfp_init_f)
231 1452
vfp_esi_init(struct vfp_ctx *vc, struct vfp_entry *vfe)
232
{
233
        struct vef_priv *vef;
234
235 1452
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
236 1452
        CHECK_OBJ_NOTNULL(vc->req, HTTP_MAGIC);
237 1452
        if (http_GetStatus(vc->resp) == 206) {
238 12
                VSLb(vc->wrk->vsl, SLT_VCL_Error,
239
                    "Attempted ESI on partial (206) response");
240 12
                return (VFP_ERROR);
241
        }
242 1440
        ALLOC_OBJ(vef, VEF_MAGIC);
243 1440
        if (vef == NULL)
244 0
                return (VFP_ERROR);
245 1440
        vc->obj_flags |= OF_ESIPROC;
246 1440
        vef->vep = VEP_Init(vc, vc->req, NULL, NULL);
247 1440
        vfe->priv1 = vef;
248 1440
        return (VFP_OK);
249
}
250
251
static enum vfp_status v_matchproto_(vfp_pull_f)
252 640005
vfp_esi_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p, ssize_t *lp)
253
{
254
        enum vfp_status vp;
255
        ssize_t d;
256
        struct vef_priv *vef;
257
258 640005
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
259 640005
        CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
260 640005
        CAST_OBJ_NOTNULL(vef, vfe->priv1, VEF_MAGIC);
261 640005
        AN(p);
262 640005
        AN(lp);
263 640005
        if (DO_DEBUG(DBG_ESI_CHOP)) {
264 637925
                d = (VRND_RandomTestable() & 3) + 1;
265 637925
                if (d < *lp)
266 637769
                        *lp = d;
267
        }
268 640005
        vp = VFP_Suck(vc, p, lp);
269 640005
        if (vp != VFP_ERROR && *lp > 0)
270 639873
                VEP_Parse(vef->vep, p, *lp);
271 640005
        if (vp == VFP_END) {
272 1416
                vp = vfp_esi_end(vc, vef, vp);
273 1416
                vfe->priv1 = NULL;
274
        }
275 640005
        return (vp);
276
}
277
278
static void v_matchproto_(vfp_fini_f)
279 1920
vfp_esi_fini(struct vfp_ctx *vc, struct vfp_entry *vfe)
280
{
281 1920
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
282 1920
        CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
283
284 1920
        if (vfe->priv1 != NULL)
285 60
                (void)vfp_esi_end(vc, vfe->priv1, VFP_ERROR);
286 1920
        vfe->priv1 = NULL;
287 1920
}
288
289
const struct vfp VFP_esi = {
290
        .name = "esi",
291
        .init = vfp_esi_init,
292
        .pull = vfp_esi_pull,
293
        .fini = vfp_esi_fini,
294
};
295
296
const struct vfp VFP_esi_gzip = {
297
        .name = "esi_gzip",
298
        .init = vfp_esi_gzip_init,
299
        .pull = vfp_esi_gzip_pull,
300
        .fini = vfp_esi_fini,
301
};