varnish-cache/bin/varnishd/cache/cache_esi_fetch.c
0
/*-
1
 * Copyright (c) 2011 Varnish Software AS
2
 * All rights reserved.
3
 *
4
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
5
 *
6
 * SPDX-License-Identifier: BSD-2-Clause
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
 * VEF Varnish Esi Fetching
30
 */
31
32
#include "config.h"
33
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 46347
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
        enum vgzret_e i;
71
72 46347
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
73 46347
        CAST_OBJ_NOTNULL(vef, priv, VEF_MAGIC);
74 46347
        assert(l >= 0);
75
76 46347
        if (vef->error) {
77 200
                vef->tot += l;
78 200
                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 46147
        if (l == 0 && flg == VGZ_NORMAL)
87 8501
                return (vef->tot);
88
89 37646
        VGZ_Ibuf(vef->vgz, vef->ibuf_o, l);
90 37646
        do {
91 42846
                dl = 0;
92 42846
                if (VFP_GetStorage(vc, &dl, &ptr) != VFP_OK) {
93 40
                        vef->error = ENOMEM;
94 40
                        vef->tot += l;
95 40
                        return (vef->tot);
96
                }
97 42806
                VGZ_Obuf(vef->vgz, ptr, dl);
98 42806
                i = VGZ_Gzip(vef->vgz, &dp, &dl, flg);
99 42806
                VGZ_UpdateObj(vc, vef->vgz, i);
100 42806
                if (dl > 0) {
101 18888
                        vef->tot += dl;
102 18888
                        VFP_Extend(vc, dl, VFP_OK);
103 18888
                }
104 85612
        } while (i != VGZ_ERROR &&
105 42806
            (!VGZ_IbufEmpty(vef->vgz) || VGZ_ObufFull(vef->vgz)));
106 37606
        assert(i == VGZ_ERROR || VGZ_IbufEmpty(vef->vgz));
107 37606
        vef->ibuf_o += l;
108 37606
        return (vef->tot);
109 46347
}
110
111
static enum vfp_status
112 8680
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 8680
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
120 8680
        CHECK_OBJ_NOTNULL(vef, VEF_MAGIC);
121
122 8680
        if (retval == VFP_ERROR) {
123 240
                if (vef->error == 0)
124 200
                        vef->error = errno ? errno : EINVAL;
125 240
        } else {
126 8440
                assert(retval == VFP_END);
127
        }
128
129 8680
        vsb = VEP_Finish(vef->vep);
130
131 8680
        if (vsb != NULL) {
132 4680
                if (retval == VFP_END) {
133 4640
                        l = VSB_len(vsb);
134 4640
                        assert(l > 0);
135 9280
                        p = ObjSetAttr(vc->wrk, vc->oc,
136 4640
                            OA_ESIDATA, l, VSB_data(vsb));
137 4640
                        if (p == NULL) {
138 0
                                retval = VFP_Error(vc,
139
                                    "Could not allocate storage for esidata");
140 0
                        }
141 4640
                }
142 4680
                VSB_destroy(&vsb);
143 4680
        }
144
145 8680
        if (vef->vgz != NULL) {
146 3040
                if (retval == VFP_END)
147 2880
                        VGZ_UpdateObj(vc, vef->vgz, VGZ_END);
148 3040
                if (VGZ_Destroy(&vef->vgz) != VGZ_END)
149 160
                        retval = VFP_Error(vc,
150
                            "ESI+Gzip Failed at the very end");
151 3040
        }
152 8680
        if (vef->ibuf != NULL)
153 3040
                free(vef->ibuf);
154 8680
        FREE_OBJ(vef);
155 8680
        return (retval);
156
}
157
158
static enum vfp_status v_matchproto_(vfp_init_f)
159 5120
vfp_esi_gzip_init(VRT_CTX, struct vfp_ctx *vc, struct vfp_entry *vfe)
160
{
161
        struct vef_priv *vef;
162
163 5120
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
164 5120
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
165 5120
        CHECK_OBJ_NOTNULL(vc->req, HTTP_MAGIC);
166 5120
        CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
167 5120
        if (http_GetStatus(vc->resp) == 206) {
168 0
                VSLb(vc->wrk->vsl, SLT_VCL_Error,
169
                    "Attempted ESI on partial (206) response");
170 0
                return (VFP_ERROR);
171
        }
172 5120
        ALLOC_OBJ(vef, VEF_MAGIC);
173 5120
        if (vef == NULL)
174 0
                return (VFP_ERROR);
175 5120
        vc->obj_flags |= OF_GZIPED | OF_CHGCE | OF_ESIPROC;
176 5120
        vef->vep = VEP_Init(vc, vc->req, vfp_vep_callback, vef);
177 5120
        if (vef->vep == NULL) {
178 2080
                FREE_OBJ(vef);
179 2080
                return (VFP_ERROR);
180
        }
181 3040
        vef->vgz = VGZ_NewGzip(vc->wrk->vsl, "G F E");
182 3040
        AN(vef->vgz);
183
184 3040
        vef->ibuf_sz = cache_param->gzip_buffer;
185 3040
        vef->ibuf = calloc(1L, vef->ibuf_sz);
186 3040
        if (vef->ibuf == NULL)
187 0
                return (vfp_esi_end(vc, vef, VFP_ERROR));
188 3040
        vef->ibuf_i = vef->ibuf;
189 3040
        vef->ibuf_o = vef->ibuf;
190 3040
        vfe->priv1 = vef;
191
192 3040
        RFC2616_Weaken_Etag(vc->resp);
193 3040
        http_Unset(vc->resp, H_Content_Length);
194 3040
        http_Unset(vc->resp, H_Content_Encoding);
195 3040
        http_SetHeader(vc->resp, "Content-Encoding: gzip");
196
197 3040
        RFC2616_Vary_AE(vc->resp);
198
199 3040
        return (VFP_OK);
200 5120
}
201
202
static enum vfp_status v_matchproto_(vfp_pull_f)
203 22296
vfp_esi_gzip_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p,
204
   ssize_t *lp)
205
{
206
        enum vfp_status vp;
207
        ssize_t d, l;
208
        struct vef_priv *vef;
209
210 22296
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
211 22296
        CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
212 22296
        CAST_OBJ_NOTNULL(vef, vfe->priv1, VEF_MAGIC);
213 22296
        AN(p);
214 22296
        AN(lp);
215 22296
        *lp = 0;
216 22296
        l = vef->ibuf_sz - (vef->ibuf_i - vef->ibuf);
217 22296
        if (DO_DEBUG(DBG_ESI_CHOP)) {
218 15696
                d = (VRND_RandomTestable() & 3) + 1;
219 15696
                if (d < l)
220 15696
                        l = d;
221 15696
        }
222 22296
        vp = VFP_Suck(vc, vef->ibuf_i, &l);
223
224 22296
        if (l > 0) {
225 19856
                VEP_Parse(vef->vep, vef->ibuf_i, l);
226 19856
                vef->ibuf_i += l;
227 19856
                assert(vef->ibuf_o >= vef->ibuf && vef->ibuf_o <= vef->ibuf_i);
228 19856
                if (vef->error) {
229 40
                        errno = vef->error;
230 40
                        return (VFP_ERROR);
231
                }
232 19816
                l = vef->ibuf_i - vef->ibuf_o;
233 19816
                if (l > 0)
234 1271
                        memmove(vef->ibuf, vef->ibuf_o, l);
235 19816
                vef->ibuf_o = vef->ibuf;
236 19816
                vef->ibuf_i = vef->ibuf + l;
237 19816
        }
238 22256
        if (vp == VFP_END) {
239 2880
                vp = vfp_esi_end(vc, vef, vp);
240 2880
                vfe->priv1 = NULL;
241 2880
        }
242 22256
        return (vp);
243 22296
}
244
245
static enum vfp_status v_matchproto_(vfp_init_f)
246 5680
vfp_esi_init(VRT_CTX, struct vfp_ctx *vc, struct vfp_entry *vfe)
247
{
248
        struct vef_priv *vef;
249
        struct vep_state *vep;
250
251 5680
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
252 5680
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
253 5680
        CHECK_OBJ_NOTNULL(vc->req, HTTP_MAGIC);
254 5680
        if (http_GetStatus(vc->resp) == 206) {
255 40
                VSLb(vc->wrk->vsl, SLT_VCL_Error,
256
                    "Attempted ESI on partial (206) response");
257 40
                return (VFP_ERROR);
258
        }
259 5640
        vep = VEP_Init(vc, vc->req, NULL, NULL);
260 5640
        if (vep == NULL)
261 0
                return (VFP_ERROR);
262 5640
        ALLOC_OBJ(vef, VEF_MAGIC);
263 5640
        if (vef == NULL)
264 0
                return (VFP_ERROR);
265 5640
        vc->obj_flags |= OF_ESIPROC;
266 5640
        vef->vep = vep;
267 5640
        vfe->priv1 = vef;
268 5640
        return (VFP_OK);
269 5680
}
270
271
static enum vfp_status v_matchproto_(vfp_pull_f)
272 2131553
vfp_esi_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p, ssize_t *lp)
273
{
274
        enum vfp_status vp;
275
        struct vef_priv *vef;
276
277 2131553
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
278 2131553
        CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
279 2131553
        CAST_OBJ_NOTNULL(vef, vfe->priv1, VEF_MAGIC);
280 2131553
        AN(p);
281 2131553
        AN(lp);
282 2131553
        if (DO_DEBUG(DBG_ESI_CHOP)) {
283 2123834
                *lp = vmin_t(size_t, *lp, (VRND_RandomTestable() & 3) + 1);
284 2123834
        }
285 2131553
        vp = VFP_Suck(vc, p, lp);
286 2131553
        if (vp != VFP_ERROR && *lp > 0)
287 2131113
                VEP_Parse(vef->vep, p, *lp);
288 2131553
        if (vp == VFP_END) {
289 5560
                vp = vfp_esi_end(vc, vef, vp);
290 5560
                vfe->priv1 = NULL;
291 5560
        }
292 2131553
        return (vp);
293
}
294
295
static void v_matchproto_(vfp_fini_f)
296 10800
vfp_esi_fini(struct vfp_ctx *vc, struct vfp_entry *vfe)
297
{
298 10800
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
299 10800
        CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
300
301 10800
        if (vfe->priv1 == NULL)
302 10560
                return;
303
304 240
        if (vc->oc->stobj->stevedore == NULL)
305 40
                errno = ENOMEM;
306
307 240
        (void)vfp_esi_end(vc, vfe->priv1, VFP_ERROR);
308 240
        vfe->priv1 = NULL;
309 10800
}
310
311
const struct vfp VFP_esi = {
312
        .name = "esi",
313
        .init = vfp_esi_init,
314
        .pull = vfp_esi_pull,
315
        .fini = vfp_esi_fini,
316
};
317
318
const struct vfp VFP_esi_gzip = {
319
        .name = "esi_gzip",
320
        .init = vfp_esi_gzip_init,
321
        .pull = vfp_esi_gzip_pull,
322
        .fini = vfp_esi_fini,
323
};