vinyl-cache/bin/vinyld/cache/cache_range.c
0
/*-
1
 * Copyright (c) 2006 Verdens Gang AS
2
 * Copyright (c) 2006-2015 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions
11
 * are met:
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
 * SUCH DAMAGE.
29
 */
30
31
#include "config.h"
32
33
#include "cache_int.h"
34
#include "cache_filter.h"
35
36
#include "vct.h"
37
#include <vtim.h>
38
39
/*--------------------------------------------------------------------*/
40
41
struct vrg_priv {
42
        unsigned                magic;
43
#define VRG_PRIV_MAGIC          0xb886e711
44
        struct req              *req;
45
        ssize_t                 range_low;
46
        ssize_t                 range_high;
47
        ssize_t                 range_off;
48
};
49
50
static int v_matchproto_(vdp_fini_f)
51 144
vrg_range_fini(struct vdp_ctx *vdc, void **priv)
52
{
53
        struct vrg_priv *vrg_priv;
54
55 144
        CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC);
56 144
        TAKE_OBJ_NOTNULL(vrg_priv, priv, VRG_PRIV_MAGIC);
57 144
        if (vrg_priv->req->resp_len >= 0 &&
58 108
            vrg_priv->range_off < vrg_priv->range_high) {
59 8
                Req_Fail(vrg_priv->req, SC_RANGE_SHORT);
60 8
                vdc->retval = -1;
61 8
        }
62
        /* struct on ws, no need to free */
63 144
        return (0);
64
}
65
66
static int v_matchproto_(vdp_bytes_f)
67 262
vrg_range_bytes(struct vdp_ctx *vdc, enum vdp_action act, void **priv,
68
    const void *ptr, ssize_t len)
69
{
70 262
        int retval = 0;
71 262
        ssize_t l = 0;
72 262
        const char *p = ptr;
73
        struct vrg_priv *vrg_priv;
74
75 262
        CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC);
76 262
        AN(priv);
77 262
        CAST_OBJ_NOTNULL(vrg_priv, *priv, VRG_PRIV_MAGIC);
78
79 262
        if (ptr != NULL) {
80 203
                l = vrg_priv->range_low - vrg_priv->range_off;
81 203
                if (l > 0) {
82 100
                        if (l > len)
83 8
                                l = len;
84 100
                        vrg_priv->range_off += l;
85 100
                        p += l;
86 100
                        len -= l;
87 100
                }
88 203
                l = vmin(vrg_priv->range_high - vrg_priv->range_off, len);
89 203
                vrg_priv->range_off += len;
90 203
                if (vrg_priv->range_off >= vrg_priv->range_high)
91 124
                        act = VDP_END;
92 203
        }
93
94 262
        if (l > 0)
95 190
                retval = VDP_bytes(vdc, act, p, l);
96 72
        else if (l == 0 && act > VDP_NULL)
97 65
                retval = VDP_bytes(vdc, act, p, 0);
98 262
        return (retval || act == VDP_END ? 1 : 0);
99
}
100
101
/*--------------------------------------------------------------------*/
102
103
static const char *
104 200
vrg_dorange(struct req *req, intmax_t *clen, void **priv)
105
{
106
        ssize_t low, high;
107
        struct vrg_priv *vrg_priv;
108
        const char *err;
109
110 200
        AN(clen);
111 200
        err = http_GetRange(req->http, &low, &high, *clen);
112 200
        if (err != NULL)
113 36
                return (err);
114
115 164
        if (low < 0 || high < 0)
116 20
                return (NULL);          // Allow 200 response
117
118 144
        http_Unset(req->resp, H_Content_Range);
119 144
        if (*clen >= 0) {
120 216
                http_PrintfHeader(req->resp, "Content-Range: bytes %jd-%jd/%jd",
121 108
                    (intmax_t)low, (intmax_t)high, *clen);
122 108
                *clen = (intmax_t)(1 + high - low);
123 108
        } else
124 72
                http_PrintfHeader(req->resp, "Content-Range: bytes %jd-%jd/*",
125 36
                    (intmax_t)low, (intmax_t)high);
126
127 144
        vrg_priv = WS_Alloc(req->ws, sizeof *vrg_priv);
128 144
        if (vrg_priv == NULL)
129 0
                return ("WS too small");
130
131 144
        INIT_OBJ(vrg_priv, VRG_PRIV_MAGIC);
132 144
        vrg_priv->req = req;
133 144
        vrg_priv->range_off = 0;
134 144
        vrg_priv->range_low = low;
135 144
        vrg_priv->range_high = high + 1;
136 144
        *priv = vrg_priv;
137 144
        http_PutResponse(req->resp, "HTTP/1.1", 206, NULL);
138 144
        return (NULL);
139 200
}
140
141
/*
142
 * return 1 if range should be observed, based on if-range value
143
 * if-range can either be a date or an ETag [RFC7233 3.2 p8]
144
 */
145
static int
146 228
vrg_ifrange(struct req *req)
147
{
148
        const char *p, *e;
149
        vtim_real ims, lm, d;
150
151 228
        if (!http_GetHdr(req->http, H_If_Range, &p))    // rfc7233,l,455,456
152 192
                return (1);
153
154
        /* strong validation needed */
155 36
        if (p[0] == 'W' && p[1] == '/')                 // rfc7233,l,500,501
156 0
                return (0);
157
158
        /* ETag */
159 36
        if (p[0] == '"') {                              // rfc7233,l,512,514
160 16
                if (!http_GetHdr(req->resp, H_ETag, &e))
161 8
                        return (0);
162 8
                if ((e[0] == 'W' && e[1] == '/'))       // rfc7232,l,547,548
163 0
                        return (0);
164
                /* XXX: should we also have http_etag_cmp() ? */
165 8
                return (vstrcmp(p, e) == 0);            // rfc7232,l,548,548
166
        }
167
168
        /* assume date, strong check [RFC7232 2.2.2 p7] */
169 20
        ims = VTIM_parse(p);
170 20
        if (!ims)                                       // rfc7233,l,502,512
171 0
                return (0);
172
173
        /* the response needs a Date */
174
        // rfc7232,l,439,440
175 20
        if (!http_GetHdr(req->resp, H_Date, &p))
176 0
                return (0);
177 20
        d = VTIM_parse(p);
178 20
        if (!d)
179 0
                return (0);
180
181
182
        /* grab the Last Modified value */
183 20
        if (!http_GetHdr(req->resp, H_Last_Modified, &p))
184 8
                return (0);
185
186 12
        lm = VTIM_parse(p);
187 12
        if (!lm)
188 0
                return (0);
189
190
        /* Last Modified must be 60 seconds older than Date */
191 12
        if (lm > d + 60)                                // rfc7232,l,442,443
192 0
                return (0);
193
194 12
        if (lm != ims)                                  // rfc7233,l,455,456
195 8
                return (0);
196 4
        return (1);
197 228
}
198
199
static int v_matchproto_(vdp_init_f)
200 228
vrg_range_init(VRT_CTX, struct vdp_ctx *vdc, void **priv)
201
{
202
        const char *err;
203
204 228
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
205 228
        CHECK_OBJ_ORNULL(ctx->req, REQ_MAGIC);
206 228
        CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC);
207 228
        AN(priv);
208
209 228
        if (ctx->req == NULL) {
210 0
                VSLb(vdc->vsl, SLT_Error,
211
                     "range can only be used on the client side");
212 0
                return (1);
213
        }
214
215 228
        AN(vdc->clen);
216
217
        // not using vdc->{hd,cl}, because range needs req anyway for Req_Fail()
218
219 228
        if (!vrg_ifrange(ctx->req))             // rfc7233,l,455,456
220 28
                return (1);
221 200
        err = vrg_dorange(ctx->req, vdc->clen, priv);
222 200
        if (err == NULL)
223 164
                return (*priv == NULL ? 1 : 0);
224
225 36
        VSLb(vdc->vsl, SLT_Debug, "RANGE_FAIL %s", err);
226 36
        http_Unset(ctx->req->resp, H_Content_Range);
227 36
        if (*vdc->clen >= 0)
228 72
                http_PrintfHeader(ctx->req->resp,
229 36
                    "Content-Range: bytes */%jd", *vdc->clen);
230 36
        http_PutResponse(ctx->req->resp, "HTTP/1.1", 416, NULL);
231
        /*
232
         * XXX: We ought to produce a body explaining things.
233
         * XXX: That really calls for us to hit vcl_synth{}
234
         */
235 36
        *vdc->clen = 0;
236 36
        return (1);
237 228
}
238
239
static int v_matchproto_(vdpio_init_f)
240 0
vrg_range_io_upgrade(VRT_CTX, struct vdp_ctx *vdc, void **priv, int capacity)
241
{
242
243 0
        (void)ctx;
244 0
        (void)vdc;
245 0
        (void)priv;
246
247 0
        return (capacity);
248
}
249
250
static int v_matchproto_(vdpio_lease_f)
251 0
vrg_range_io_lease(struct vdp_ctx *vdc, struct vdp_entry *this, struct vscarab *out)
252
{
253
        struct vrg_priv *vrg_priv;
254
        struct viov *v;
255
        ssize_t l, ll;
256
        int r;
257
258 0
        CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC);
259 0
        CHECK_OBJ_NOTNULL(this, VDP_ENTRY_MAGIC);
260 0
        VSCARAB_CHECK(out);
261
262 0
        CAST_OBJ_NOTNULL(vrg_priv, this->priv, VRG_PRIV_MAGIC);
263
264 0
        if (vrg_priv->range_off >= vrg_priv->range_high) {
265 0
                out->flags |= VSCARAB_F_END;
266 0
                return (0);
267
        }
268 0
        if (out->capacity == out->used)
269 0
                return (0);
270
271
        // ensure we do not pull more than we can return
272 0
        VSCARAB_LOCAL(in, out->capacity - out->used);
273
274 0
        while (vrg_priv->range_off < vrg_priv->range_low) {
275 0
                r = vdpio_pull(vdc, this, in);
276 0
                out->flags |= in->flags;
277 0
                if (r <= 0)
278 0
                        return (r);
279 0
                l = vrg_priv->range_low - vrg_priv->range_off;
280 0
                VSCARAB_FOREACH(v, in) {
281 0
                        ll = vmin(l, (ssize_t)v->iov.iov_len);
282 0
                        v->iov.iov_base = (char *)v->iov.iov_base + ll;
283 0
                        v->iov.iov_len -= ll;
284 0
                        l -= ll;
285 0
                        if (l == 0)
286 0
                                break;
287 0
                }
288 0
                vrg_priv->range_off = vrg_priv->range_low - l;
289
290 0
                vdpio_consolidate_vscarab(vdc, in);
291
292 0
                if (l != 0)
293 0
                        AZ(in->used);
294
                else
295 0
                        assert(vrg_priv->range_off == vrg_priv->range_low);
296
        }
297
298 0
        assert(vrg_priv->range_off >= vrg_priv->range_low);
299 0
        assert(vrg_priv->range_off <= vrg_priv->range_high);
300
301 0
        if (in->used == 0) {
302 0
                r = vdpio_pull(vdc, this, in);
303 0
                out->flags |= in->flags;
304 0
                if (r <= 0)
305 0
                        return (r);
306 0
        }
307
308 0
        AN(in->used);
309
310 0
        r = 0;
311 0
        l = vrg_priv->range_high - vrg_priv->range_off;
312 0
        VSCARAB_FOREACH(v, in) {
313 0
                vrg_priv->range_off += (ssize_t)v->iov.iov_len;
314 0
                ll = vmin(l, (ssize_t)v->iov.iov_len);
315 0
                v->iov.iov_len = ll;
316 0
                if (ll == 0)
317 0
                        vdpio_return_lease(vdc, v->lease);
318
                else {
319 0
                        VSCARAB_ADD(out, *v);
320 0
                        l -= ll;
321 0
                        r++;
322
                }
323 0
        }
324 0
        if (vrg_priv->range_off >= vrg_priv->range_high)
325 0
                out->flags |= VSCARAB_F_END;
326 0
        return (r);
327 0
}
328
329
static void v_matchproto_(vdpio_fini_f)
330 0
vrg_range_io_fini(struct vdp_ctx *vdc, void **priv)
331
{
332 0
        AZ(vrg_range_fini(vdc, priv));
333 0
}
334
335
const struct vdp VDP_range = {
336
        .name =         "range",
337
        .init =         vrg_range_init,
338
        .bytes =        vrg_range_bytes,
339
        .fini =         vrg_range_fini,
340
341
        .io_upgrade =   vrg_range_io_upgrade,
342
        .io_lease =     vrg_range_io_lease,
343
        .io_fini =      vrg_range_io_fini,
344
};
345
346
/*--------------------------------------------------------------------*/
347
348
int
349 8388
VRG_CheckBo(struct busyobj *bo)
350
{
351
        ssize_t rlo, rhi, crlo, crhi, crlen, clen;
352
        const char *err;
353
354 8388
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
355
356 8388
        if (!cache_param->http_range_support)
357 16
                return (0);
358
359 8372
        err = http_GetRange(bo->bereq, &rlo, &rhi, -1);
360 8372
        clen = http_GetContentLength(bo->beresp);
361 8372
        crlen = http_GetContentRange(bo->beresp, &crlo, &crhi);
362
363 8372
        if (err != NULL) {
364 4
                VSLb(bo->vsl, SLT_Error, "Invalid range header (%s)", err);
365 4
                return (-1);
366
        }
367
368 8368
        if (crlen < -1) {
369 8
                VSLb(bo->vsl, SLT_Error, "Invalid content-range header");
370 8
                return (-1);
371
        }
372
373 8360
        if (clen < -1) {
374 0
                VSLb(bo->vsl, SLT_Error, "Invalid content-length header");
375 0
                return (-1);
376
        }
377
378 8360
        if (crlo < 0 && crhi < 0 && crlen < 0) {
379 8332
                VSLb(bo->vsl, SLT_Debug,
380
                    "Missing content-range header or unknown range unit");
381 8332
                return (0);
382
        }
383
384 28
        if (rlo < 0 && rhi < 0) {
385 8
                VSLb(bo->vsl, SLT_Error, "Unexpected content-range header");
386 8
                return (-1);
387
        }
388
389 20
        if (crlo < 0) {         // Content-Range: bytes */123
390 4
                assert(crhi < 0);
391 4
                assert(crlen > 0);
392 4
                if (http_GetStatus(bo->beresp) == 416)
393 4
                        return (0);
394 0
                crlo = 0;
395 0
                crhi = crlen - 1;
396 0
        }
397
398
#define RANGE_CHECK(val, op, crval, what)                       \
399
        do {                                                    \
400
                if (val >= 0 && !(val op crval)) {              \
401
                        VSLb(bo->vsl, SLT_Error,                \
402
                            "Expected " what " %zd, got %zd",   \
403
                            crval, val);                        \
404
                        return (-1);                            \
405
                }                                               \
406
        } while (0)
407
408 16
        crlen = (crhi - crlo) + 1;
409 16
        RANGE_CHECK(clen, ==, crlen, "content length");
410
411
        /* NB: if the client didn't specify a low range the high range
412
         * was adjusted based on the resource length, and a high range
413
         * is allowed to be out of bounds so at this point there is
414
         * nothing left to check.
415
         */
416 8
        if (rlo < 0)
417 0
                return (0);
418
419 8
        RANGE_CHECK(rlo, ==, crlo, "low range");
420 8
        RANGE_CHECK(rhi, >=, crhi, "minimum high range");
421
#undef RANGE_CHECK
422
423 8
        return (0);
424 8388
}