varnish-cache/bin/varnishd/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_varnishd.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 36
vrg_range_fini(struct vdp_ctx *vdc, void **priv)
52
{
53
        struct vrg_priv *vrg_priv;
54
55 36
        CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC);
56 36
        TAKE_OBJ_NOTNULL(vrg_priv, priv, VRG_PRIV_MAGIC);
57 36
        if (vrg_priv->req->resp_len >= 0 &&
58 27
            vrg_priv->range_off < vrg_priv->range_high) {
59 2
                Req_Fail(vrg_priv->req, SC_RANGE_SHORT);
60 2
                vrg_priv->req->vdc->retval = -1;
61 2
        }
62
        /* struct on ws, no need to free */
63 36
        return (0);
64
}
65
66
static int v_matchproto_(vdp_bytes_f)
67 66
vrg_range_bytes(struct vdp_ctx *vdc, enum vdp_action act, void **priv,
68
    const void *ptr, ssize_t len)
69
{
70 66
        int retval = 0;
71 66
        ssize_t l = 0;
72 66
        const char *p = ptr;
73
        struct vrg_priv *vrg_priv;
74
75 66
        CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC);
76 66
        AN(priv);
77 66
        CAST_OBJ_NOTNULL(vrg_priv, *priv, VRG_PRIV_MAGIC);
78
79 66
        if (ptr != NULL) {
80 52
                l = vrg_priv->range_low - vrg_priv->range_off;
81 52
                if (l > 0) {
82 26
                        if (l > len)
83 3
                                l = len;
84 26
                        vrg_priv->range_off += l;
85 26
                        p += l;
86 26
                        len -= l;
87 26
                }
88 52
                l = vmin(vrg_priv->range_high - vrg_priv->range_off, len);
89 52
                vrg_priv->range_off += len;
90 52
                if (vrg_priv->range_off >= vrg_priv->range_high)
91 31
                        act = VDP_END;
92 52
        }
93
94 66
        if (l > 0)
95 49
                retval = VDP_bytes(vdc, act, p, l);
96 17
        else if (l == 0 && act > VDP_NULL)
97 17
                retval = VDP_bytes(vdc, act, p, 0);
98 66
        return (retval || act == VDP_END ? 1 : 0);
99
}
100
101
/*--------------------------------------------------------------------*/
102
103
static const char *
104 49
vrg_dorange(struct req *req, void **priv)
105
{
106
        ssize_t low, high;
107
        struct vrg_priv *vrg_priv;
108
        const char *err;
109
110 49
        err = http_GetRange(req->http, &low, &high, req->resp_len);
111 49
        if (err != NULL)
112 9
                return (err);
113
114 40
        if (low < 0 || high < 0)
115 4
                return (NULL);          // Allow 200 response
116
117 36
        if (req->resp_len >= 0) {
118 54
                http_PrintfHeader(req->resp, "Content-Range: bytes %jd-%jd/%jd",
119 27
                    (intmax_t)low, (intmax_t)high, (intmax_t)req->resp_len);
120 27
                req->resp_len = (intmax_t)(1 + high - low);
121 27
        } else
122 18
                http_PrintfHeader(req->resp, "Content-Range: bytes %jd-%jd/*",
123 9
                    (intmax_t)low, (intmax_t)high);
124
125 36
        vrg_priv = WS_Alloc(req->ws, sizeof *vrg_priv);
126 36
        if (vrg_priv == NULL)
127 0
                return ("WS too small");
128
129 36
        INIT_OBJ(vrg_priv, VRG_PRIV_MAGIC);
130 36
        vrg_priv->req = req;
131 36
        vrg_priv->range_off = 0;
132 36
        vrg_priv->range_low = low;
133 36
        vrg_priv->range_high = high + 1;
134 36
        *priv = vrg_priv;
135 36
        http_PutResponse(req->resp, "HTTP/1.1", 206, NULL);
136 36
        return (NULL);
137 49
}
138
139
/*
140
 * return 1 if range should be observed, based on if-range value
141
 * if-range can either be a date or an ETag [RFC7233 3.2 p8]
142
 */
143
static int
144 56
vrg_ifrange(struct req *req)
145
{
146
        const char *p, *e;
147
        vtim_real ims, lm, d;
148
149 56
        if (!http_GetHdr(req->http, H_If_Range, &p))    // rfc7233,l,455,456
150 47
                return (1);
151
152
        /* strong validation needed */
153 9
        if (p[0] == 'W' && p[1] == '/')                 // rfc7233,l,500,501
154 0
                return (0);
155
156
        /* ETag */
157 9
        if (p[0] == '"') {                              // rfc7233,l,512,514
158 4
                if (!http_GetHdr(req->resp, H_ETag, &e))
159 2
                        return (0);
160 2
                if ((e[0] == 'W' && e[1] == '/'))       // rfc7232,l,547,548
161 0
                        return (0);
162
                /* XXX: should we also have http_etag_cmp() ? */
163 2
                return (strcmp(p, e) == 0);             // rfc7232,l,548,548
164
        }
165
166
        /* assume date, strong check [RFC7232 2.2.2 p7] */
167 5
        ims = VTIM_parse(p);
168 5
        if (!ims)                                       // rfc7233,l,502,512
169 0
                return (0);
170
171
        /* the response needs a Date */
172
        // rfc7232,l,439,440
173 5
        if (!http_GetHdr(req->resp, H_Date, &p))
174 0
                return (0);
175 5
        d = VTIM_parse(p);
176 5
        if (!d)
177 0
                return (0);
178
179
180
        /* grab the Last Modified value */
181 5
        if (!http_GetHdr(req->resp, H_Last_Modified, &p))
182 2
                return (0);
183
184 3
        lm = VTIM_parse(p);
185 3
        if (!lm)
186 0
                return (0);
187
188
        /* Last Modified must be 60 seconds older than Date */
189 3
        if (lm > d + 60)                                // rfc7232,l,442,443
190 0
                return (0);
191
192 3
        if (lm != ims)                                  // rfc7233,l,455,456
193 2
                return (0);
194 1
        return (1);
195 56
}
196
197
static int v_matchproto_(vdp_init_f)
198 56
vrg_range_init(VRT_CTX, struct vdp_ctx *vdc, void **priv)
199
{
200
        const char *err;
201
202 56
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
203 56
        CHECK_OBJ_ORNULL(ctx->req, REQ_MAGIC);
204 56
        CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC);
205 56
        AN(priv);
206
207 56
        if (ctx->req == NULL) {
208 0
                VSLb(vdc->vsl, SLT_Error,
209
                     "range can only be used on the client side");
210 0
                return (1);
211
        }
212
213
        // not using vdc->{hd,cl}, because range needs req anyway for Req_Fail()
214
215 56
        if (!vrg_ifrange(ctx->req))             // rfc7233,l,455,456
216 7
                return (1);
217 49
        err = vrg_dorange(ctx->req, priv);
218 49
        if (err == NULL)
219 40
                return (*priv == NULL ? 1 : 0);
220
221 9
        VSLb(vdc->vsl, SLT_Debug, "RANGE_FAIL %s", err);
222 9
        if (ctx->req->resp_len >= 0)
223 18
                http_PrintfHeader(ctx->req->resp,
224
                    "Content-Range: bytes */%jd",
225 9
                    (intmax_t)ctx->req->resp_len);
226 9
        http_PutResponse(ctx->req->resp, "HTTP/1.1", 416, NULL);
227
        /*
228
         * XXX: We ought to produce a body explaining things.
229
         * XXX: That really calls for us to hit vcl_synth{}
230
         */
231 9
        ctx->req->resp_len = 0;
232 9
        return (1);
233 56
}
234
235
static int v_matchproto_(vdpio_init_f)
236 0
vrg_range_io_upgrade(VRT_CTX, struct vdp_ctx *vdc, void **priv, int capacity)
237
{
238
239 0
        (void)ctx;
240 0
        (void)vdc;
241 0
        (void)priv;
242
243 0
        return (capacity);
244
}
245
246
static int v_matchproto_(vdpio_lease_f)
247 0
vrg_range_io_lease(struct vdp_ctx *vdc, struct vdp_entry *this, struct vscarab *out)
248
{
249
        struct vrg_priv *vrg_priv;
250
        struct viov *v;
251
        ssize_t l, ll;
252
        int r;
253
254 0
        CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC);
255 0
        CHECK_OBJ_NOTNULL(this, VDP_ENTRY_MAGIC);
256 0
        VSCARAB_CHECK(out);
257
258 0
        CAST_OBJ_NOTNULL(vrg_priv, this->priv, VRG_PRIV_MAGIC);
259
260 0
        if (vrg_priv->range_off >= vrg_priv->range_high) {
261 0
                out->flags |= VSCARAB_F_END;
262 0
                return (0);
263
        }
264 0
        if (out->capacity == out->used)
265 0
                return (0);
266
267
        // ensure we do not pull more than we can return
268 0
        VSCARAB_LOCAL(in, out->capacity - out->used);
269
270 0
        while (vrg_priv->range_off < vrg_priv->range_low) {
271 0
                r = vdpio_pull(vdc, this, in);
272 0
                out->flags |= in->flags;
273 0
                if (r <= 0)
274 0
                        return (r);
275 0
                l = vrg_priv->range_low - vrg_priv->range_off;
276 0
                VSCARAB_FOREACH(v, in) {
277 0
                        ll = vmin(l, (ssize_t)v->iov.iov_len);
278 0
                        v->iov.iov_base = (char *)v->iov.iov_base + ll;
279 0
                        v->iov.iov_len -= ll;
280 0
                        l -= ll;
281 0
                        if (l == 0)
282 0
                                break;
283 0
                }
284 0
                vrg_priv->range_off = vrg_priv->range_low - l;
285
286 0
                vdpio_consolidate_vscarab(vdc, in);
287
288 0
                if (l != 0)
289 0
                        AZ(in->used);
290
                else
291 0
                        assert(vrg_priv->range_off == vrg_priv->range_low);
292
        }
293
294 0
        assert(vrg_priv->range_off >= vrg_priv->range_low);
295 0
        assert(vrg_priv->range_off <= vrg_priv->range_high);
296
297 0
        if (in->used == 0) {
298 0
                r = vdpio_pull(vdc, this, in);
299 0
                out->flags |= in->flags;
300 0
                if (r <= 0)
301 0
                        return (r);
302 0
        }
303
304 0
        AN(in->used);
305
306 0
        r = 0;
307 0
        l = vrg_priv->range_high - vrg_priv->range_off;
308 0
        VSCARAB_FOREACH(v, in) {
309 0
                vrg_priv->range_off += (ssize_t)v->iov.iov_len;
310 0
                ll = vmin(l, (ssize_t)v->iov.iov_len);
311 0
                v->iov.iov_len = ll;
312 0
                if (ll == 0)
313 0
                        vdpio_return_lease(vdc, v->lease);
314
                else {
315 0
                        VSCARAB_ADD(out, *v);
316 0
                        l -= ll;
317 0
                        r++;
318
                }
319 0
        }
320 0
        if (vrg_priv->range_off >= vrg_priv->range_high)
321 0
                out->flags |= VSCARAB_F_END;
322 0
        return (r);
323 0
}
324
325
static void v_matchproto_(vdpio_fini_f)
326 0
vrg_range_io_fini(struct vdp_ctx *vdc, void **priv)
327
{
328 0
        AZ(vrg_range_fini(vdc, priv));
329 0
}
330
331
const struct vdp VDP_range = {
332
        .name =         "range",
333
        .init =         vrg_range_init,
334
        .bytes =        vrg_range_bytes,
335
        .fini =         vrg_range_fini,
336
337
        .io_upgrade =   vrg_range_io_upgrade,
338
        .io_lease =     vrg_range_io_lease,
339
        .io_fini =      vrg_range_io_fini,
340
};
341
342
/*--------------------------------------------------------------------*/
343
344
int
345 1990
VRG_CheckBo(struct busyobj *bo)
346
{
347
        ssize_t rlo, rhi, crlo, crhi, crlen, clen;
348
        const char *err;
349
350 1990
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
351
352 1990
        if (!cache_param->http_range_support)
353 4
                return (0);
354
355 1986
        err = http_GetRange(bo->bereq0, &rlo, &rhi, -1);
356 1986
        clen = http_GetContentLength(bo->beresp);
357 1986
        crlen = http_GetContentRange(bo->beresp, &crlo, &crhi);
358
359 1986
        if (err != NULL) {
360 1
                VSLb(bo->vsl, SLT_Error, "Invalid range header (%s)", err);
361 1
                return (-1);
362
        }
363
364 1985
        if (crlen < -1) {
365 2
                VSLb(bo->vsl, SLT_Error, "Invalid content-range header");
366 2
                return (-1);
367
        }
368
369 1983
        if (clen < -1) {
370 0
                VSLb(bo->vsl, SLT_Error, "Invalid content-length header");
371 0
                return (-1);
372
        }
373
374 1983
        if (crlo < 0 && crhi < 0 && crlen < 0) {
375 1977
                VSLb(bo->vsl, SLT_Debug,
376
                    "Missing content-range header or unknown range unit");
377 1977
                return (0);
378
        }
379
380 6
        if (rlo < 0 && rhi < 0) {
381 2
                VSLb(bo->vsl, SLT_Error, "Unexpected content-range header");
382 2
                return (-1);
383
        }
384
385 4
        if (crlo < 0) {         // Content-Range: bytes */123
386 1
                assert(crhi < 0);
387 1
                assert(crlen > 0);
388 1
                if (http_GetStatus(bo->beresp) == 416)
389 1
                        return (0);
390 0
                crlo = 0;
391 0
                crhi = crlen - 1;
392 0
        }
393
394
#define RANGE_CHECK(val, op, crval, what)                       \
395
        do {                                                    \
396
                if (val >= 0 && !(val op crval)) {              \
397
                        VSLb(bo->vsl, SLT_Error,                \
398
                            "Expected " what " %zd, got %zd",   \
399
                            crval, val);                        \
400
                        return (-1);                            \
401
                }                                               \
402
        } while (0)
403
404 3
        crlen = (crhi - crlo) + 1;
405 3
        RANGE_CHECK(clen, ==, crlen, "content length");
406
407
        /* NB: if the client didn't specify a low range the high range
408
         * was adjusted based on the resource length, and a high range
409
         * is allowed to be out of bounds so at this point there is
410
         * nothing left to check.
411
         */
412 1
        if (rlo < 0)
413 0
                return (0);
414
415 1
        RANGE_CHECK(rlo, ==, crlo, "low range");
416 1
        RANGE_CHECK(rhi, >=, crhi, "minimum high range");
417
#undef RANGE_CHECK
418
419 1
        return (0);
420 1990
}