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