vinyl-cache/bin/vinyld/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 Vinyl Esi Fetching
30
 */
31
32
#include "config.h"
33
34
#include <stdlib.h>
35
36
#include "cache_int.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 struct vep_flags
64 1084
vep_flags(const struct busyobj *bo) {
65 1084
        struct vep_flags flags = {0};
66
67 1084
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
68
#define BERESP_ESI_FLAG(lower, feature, doc) \
69
        flags.lower = bo->lower;
70
#include "tbl/beresp_esi_flags.h"
71
        return (flags);
72
}
73
74
static ssize_t
75 4636
vfp_vep_callback(struct vfp_ctx *vc, void *priv, ssize_t l, enum vgz_flag flg)
76
{
77
        struct vef_priv *vef;
78
        ssize_t dl;
79
        const void *dp;
80
        uint8_t *ptr;
81
        enum vgzret_e i;
82
83 4636
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
84 4636
        CAST_OBJ_NOTNULL(vef, priv, VEF_MAGIC);
85 4636
        assert(l >= 0);
86
87 4636
        if (vef->error) {
88 20
                vef->tot += l;
89 20
                return (vef->tot);
90
        }
91
92
        /*
93
         * l == 0 is valid when 'flg' calls for action, but in the
94
         * normal case we can just ignore a l==0 request.
95
         * (It would cause Z_BUF_ERROR anyway)
96
         */
97 4616
        if (l == 0 && flg == VGZ_NORMAL)
98 846
                return (vef->tot);
99
100 3770
        VGZ_Ibuf(vef->vgz, vef->ibuf_o, l);
101 3770
        do {
102 4290
                dl = 0;
103 4290
                if (VFP_GetStorage(vc, &dl, &ptr) != VFP_OK) {
104 4
                        vef->error = ENOMEM;
105 4
                        vef->tot += l;
106 4
                        return (vef->tot);
107
                }
108 4286
                VGZ_Obuf(vef->vgz, ptr, dl);
109 4286
                i = VGZ_Gzip(vef->vgz, &dp, &dl, flg);
110 4286
                VGZ_UpdateObj(vc, vef->vgz, i);
111 4286
                if (dl > 0) {
112 1887
                        vef->tot += dl;
113 1887
                        VFP_Extend(vc, dl, VFP_OK);
114 1887
                }
115 8572
        } while (i != VGZ_ERROR &&
116 4286
            (!VGZ_IbufEmpty(vef->vgz) || VGZ_ObufFull(vef->vgz)));
117 3766
        assert(i == VGZ_ERROR || VGZ_IbufEmpty(vef->vgz));
118 3766
        vef->ibuf_o += l;
119 3766
        return (vef->tot);
120 4636
}
121
122
static enum vfp_status
123 876
vfp_esi_end(struct vfp_ctx *vc, struct vef_priv *vef,
124
    enum vfp_status retval)
125
{
126
        struct vsb *vsb;
127
        ssize_t l;
128
        void *p;
129
130 876
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
131 876
        CHECK_OBJ_NOTNULL(vef, VEF_MAGIC);
132
133 876
        if (retval == VFP_ERROR) {
134 24
                if (vef->error == 0)
135 20
                        vef->error = errno ? errno : EINVAL;
136 24
        } else {
137 852
                assert(retval == VFP_END);
138
        }
139
140 876
        vsb = VEP_Finish(vef->vep);
141
142 876
        if (vsb != NULL) {
143 476
                if (retval == VFP_END) {
144 472
                        l = VSB_len(vsb);
145 472
                        assert(l > 0);
146 944
                        p = ObjSetAttr(vc->wrk, vc->oc,
147 472
                            OA_ESIDATA, l, VSB_data(vsb));
148 472
                        if (p == NULL) {
149 0
                                retval = VFP_Error(vc,
150
                                    "Could not allocate storage for esidata");
151 0
                        }
152 472
                }
153 476
                VSB_destroy(&vsb);
154 476
        }
155
156 876
        if (vef->vgz != NULL) {
157 304
                if (retval == VFP_END)
158 288
                        VGZ_UpdateObj(vc, vef->vgz, VGZ_END);
159 304
                if (VGZ_Destroy(vc->wrk, &vef->vgz) != VGZ_END)
160 16
                        retval = VFP_Error(vc,
161
                            "ESI+Gzip Failed at the very end");
162 304
        }
163 876
        if (vef->ibuf != NULL)
164 304
                free(vef->ibuf);
165 876
        FREE_OBJ(vef);
166 876
        return (retval);
167
}
168
169
static enum vfp_status v_matchproto_(vfp_init_f)
170 512
vfp_esi_gzip_init(VRT_CTX, struct vfp_ctx *vc, struct vfp_entry *vfe)
171
{
172
        struct vef_priv *vef;
173
174 512
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
175 512
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
176 512
        CHECK_OBJ_NOTNULL(vc->req, HTTP_MAGIC);
177 512
        CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
178 512
        if (http_GetStatus(vc->resp) == 206) {
179 0
                VSLb(vc->wrk->vsl, SLT_VCL_Error,
180
                    "Attempted ESI on partial (206) response");
181 0
                return (VFP_ERROR);
182
        }
183 512
        ALLOC_OBJ(vef, VEF_MAGIC);
184 512
        if (vef == NULL)
185 0
                return (VFP_ERROR);
186 512
        vc->obj_flags |= OF_GZIPED | OF_CHGCE | OF_ESIPROC;
187 1024
        vef->vep = VEP_Init(vc, vc->req, vfp_vep_callback, vef,
188 512
            vep_flags(ctx->bo));
189 512
        if (vef->vep == NULL) {
190 208
                FREE_OBJ(vef);
191 208
                return (VFP_ERROR);
192
        }
193 304
        vef->vgz = VGZ_NewGzip(vc->wrk->vsl, "G F E");
194 304
        AN(vef->vgz);
195
196 304
        vef->ibuf_sz = cache_param->gzip_buffer;
197 304
        vef->ibuf = calloc(1L, vef->ibuf_sz);
198 304
        if (vef->ibuf == NULL)
199 0
                return (vfp_esi_end(vc, vef, VFP_ERROR));
200 304
        vef->ibuf_i = vef->ibuf;
201 304
        vef->ibuf_o = vef->ibuf;
202 304
        vfe->priv1 = vef;
203
204 304
        RFC2616_Weaken_Etag(vc->resp);
205 304
        http_Unset(vc->resp, H_Content_Length);
206 304
        http_Unset(vc->resp, H_Content_Encoding);
207 304
        http_SetHeader(vc->resp, "Content-Encoding: gzip");
208
209 304
        RFC2616_Vary_AE(vc->resp);
210
211 304
        return (VFP_OK);
212 512
}
213
214
static enum vfp_status v_matchproto_(vfp_pull_f)
215 2217
vfp_esi_gzip_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p,
216
   ssize_t *lp)
217
{
218
        enum vfp_status vp;
219
        ssize_t d, l;
220
        struct vef_priv *vef;
221
222 2217
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
223 2217
        CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
224 2217
        CAST_OBJ_NOTNULL(vef, vfe->priv1, VEF_MAGIC);
225 2217
        AN(p);
226 2217
        AN(lp);
227 2217
        *lp = 0;
228 2217
        l = vef->ibuf_sz - (vef->ibuf_i - vef->ibuf);
229 2217
        if (DO_DEBUG(DBG_ESI_CHOP)) {
230 1565
                d = (VRND_RandomTestable() & 3) + 1;
231 1565
                if (d < l)
232 1565
                        l = d;
233 1565
        }
234 2217
        vp = VFP_Suck(vc, vef->ibuf_i, &l);
235
236 2217
        if (l > 0) {
237 1981
                VEP_Parse(vef->vep, vef->ibuf_i, l);
238 1981
                vef->ibuf_i += l;
239 1981
                assert(vef->ibuf_o >= vef->ibuf && vef->ibuf_o <= vef->ibuf_i);
240 1981
                if (vef->error) {
241 4
                        errno = vef->error;
242 4
                        return (VFP_ERROR);
243
                }
244 1977
                l = vef->ibuf_i - vef->ibuf_o;
245 1977
                if (l > 0)
246 130
                        memmove(vef->ibuf, vef->ibuf_o, l);
247 1977
                vef->ibuf_o = vef->ibuf;
248 1977
                vef->ibuf_i = vef->ibuf + l;
249 1977
        }
250 2213
        if (vp == VFP_END) {
251 288
                vp = vfp_esi_end(vc, vef, vp);
252 288
                vfe->priv1 = NULL;
253 288
        }
254 2213
        return (vp);
255 2217
}
256
257
static enum vfp_status v_matchproto_(vfp_init_f)
258 576
vfp_esi_init(VRT_CTX, struct vfp_ctx *vc, struct vfp_entry *vfe)
259
{
260
        struct vef_priv *vef;
261
        struct vep_state *vep;
262
263 576
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
264 576
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
265 576
        CHECK_OBJ_NOTNULL(vc->req, HTTP_MAGIC);
266 576
        if (http_GetStatus(vc->resp) == 206) {
267 4
                VSLb(vc->wrk->vsl, SLT_VCL_Error,
268
                    "Attempted ESI on partial (206) response");
269 4
                return (VFP_ERROR);
270
        }
271 572
        vep = VEP_Init(vc, vc->req, NULL, NULL, vep_flags(ctx->bo));
272 572
        if (vep == NULL)
273 0
                return (VFP_ERROR);
274 572
        ALLOC_OBJ(vef, VEF_MAGIC);
275 572
        if (vef == NULL)
276 0
                return (VFP_ERROR);
277 572
        vc->obj_flags |= OF_ESIPROC;
278 572
        vef->vep = vep;
279 572
        vfe->priv1 = vef;
280 572
        return (VFP_OK);
281 576
}
282
283
static enum vfp_status v_matchproto_(vfp_pull_f)
284 213438
vfp_esi_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p, ssize_t *lp)
285
{
286
        enum vfp_status vp;
287
        struct vef_priv *vef;
288
289 213438
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
290 213438
        CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
291 213438
        CAST_OBJ_NOTNULL(vef, vfe->priv1, VEF_MAGIC);
292 213438
        AN(p);
293 213438
        AN(lp);
294 213438
        if (DO_DEBUG(DBG_ESI_CHOP)) {
295 212671
                *lp = vmin_t(size_t, *lp, (VRND_RandomTestable() & 3) + 1);
296 212671
        }
297 213438
        vp = VFP_Suck(vc, p, lp);
298 213438
        if (vp != VFP_ERROR && *lp > 0)
299 213405
                VEP_Parse(vef->vep, p, *lp);
300 213438
        if (vp == VFP_END) {
301 564
                vp = vfp_esi_end(vc, vef, vp);
302 564
                vfe->priv1 = NULL;
303 564
        }
304 213438
        return (vp);
305
}
306
307
static void v_matchproto_(vfp_fini_f)
308 1088
vfp_esi_fini(struct vfp_ctx *vc, struct vfp_entry *vfe)
309
{
310 1088
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
311 1088
        CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
312
313 1088
        if (vfe->priv1 == NULL)
314 1064
                return;
315
316 24
        if (vc->oc->stobj->stevedore == NULL)
317 4
                errno = ENOMEM;
318
319 24
        (void)vfp_esi_end(vc, vfe->priv1, VFP_ERROR);
320 24
        vfe->priv1 = NULL;
321 1088
}
322
323
const struct vfp VFP_esi = {
324
        .name = "esi",
325
        .init = vfp_esi_init,
326
        .pull = vfp_esi_pull,
327
        .fini = vfp_esi_fini,
328
};
329
330
const struct vfp VFP_esi_gzip = {
331
        .name = "esi_gzip",
332
        .init = vfp_esi_gzip_init,
333
        .pull = vfp_esi_gzip_pull,
334
        .fini = vfp_esi_fini,
335
};