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 1440
vrg_range_fini(struct vdp_ctx *vdc, void **priv)
52
{
53
        struct vrg_priv *vrg_priv;
54
55 1440
        CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC);
56 1440
        TAKE_OBJ_NOTNULL(vrg_priv, priv, VRG_PRIV_MAGIC);
57 1440
        if (vrg_priv->req->resp_len >= 0 &&
58 1080
            vrg_priv->range_off < vrg_priv->range_high) {
59 80
                Req_Fail(vrg_priv->req, SC_RANGE_SHORT);
60 80
                vrg_priv->req->vdc->retval = -1;
61 80
        }
62
        /* struct on ws, no need to free */
63 1440
        return (0);
64
}
65
66
static int v_matchproto_(vdp_bytes_f)
67 2688
vrg_range_bytes(struct vdp_ctx *vdc, enum vdp_action act, void **priv,
68
    const void *ptr, ssize_t len)
69
{
70 2688
        int retval = 0;
71 2688
        ssize_t l = 0;
72 2688
        const char *p = ptr;
73
        struct vrg_priv *vrg_priv;
74
75 2688
        CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC);
76 2688
        AN(priv);
77 2688
        CAST_OBJ_NOTNULL(vrg_priv, *priv, VRG_PRIV_MAGIC);
78
79 2688
        if (ptr != NULL) {
80 2114
                l = vrg_priv->range_low - vrg_priv->range_off;
81 2114
                if (l > 0) {
82 1017
                        if (l > len)
83 97
                                l = len;
84 1017
                        vrg_priv->range_off += l;
85 1017
                        p += l;
86 1017
                        len -= l;
87 1017
                }
88 2114
                l = vmin(vrg_priv->range_high - vrg_priv->range_off, len);
89 2114
                vrg_priv->range_off += len;
90 2114
                if (vrg_priv->range_off >= vrg_priv->range_high)
91 1240
                        act = VDP_END;
92 2114
        }
93
94 2688
        if (l > 0)
95 1991
                retval = VDP_bytes(vdc, act, p, l);
96 697
        else if (l == 0 && act > VDP_NULL)
97 682
                retval = VDP_bytes(vdc, act, p, 0);
98 2688
        return (retval || act == VDP_END ? 1 : 0);
99
}
100
101
/*--------------------------------------------------------------------*/
102
103
static const char *
104 1960
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 1960
        err = http_GetRange(req->http, &low, &high, req->resp_len);
111 1960
        if (err != NULL)
112 360
                return (err);
113
114 1600
        if (low < 0 || high < 0)
115 160
                return (NULL);          // Allow 200 response
116
117 1440
        if (req->resp_len >= 0) {
118 2160
                http_PrintfHeader(req->resp, "Content-Range: bytes %jd-%jd/%jd",
119 1080
                    (intmax_t)low, (intmax_t)high, (intmax_t)req->resp_len);
120 1080
                req->resp_len = (intmax_t)(1 + high - low);
121 1080
        } else
122 720
                http_PrintfHeader(req->resp, "Content-Range: bytes %jd-%jd/*",
123 360
                    (intmax_t)low, (intmax_t)high);
124
125 1440
        vrg_priv = WS_Alloc(req->ws, sizeof *vrg_priv);
126 1440
        if (vrg_priv == NULL)
127 0
                return ("WS too small");
128
129 1440
        INIT_OBJ(vrg_priv, VRG_PRIV_MAGIC);
130 1440
        vrg_priv->req = req;
131 1440
        vrg_priv->range_off = 0;
132 1440
        vrg_priv->range_low = low;
133 1440
        vrg_priv->range_high = high + 1;
134 1440
        *priv = vrg_priv;
135 1440
        http_PutResponse(req->resp, "HTTP/1.1", 206, NULL);
136 1440
        return (NULL);
137 1960
}
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 2240
vrg_ifrange(struct req *req)
145
{
146
        const char *p, *e;
147
        vtim_real ims, lm, d;
148
149 2240
        if (!http_GetHdr(req->http, H_If_Range, &p))    // rfc7233,l,455,456
150 1880
                return (1);
151
152
        /* strong validation needed */
153 360
        if (p[0] == 'W' && p[1] == '/')                 // rfc7233,l,500,501
154 0
                return (0);
155
156
        /* ETag */
157 360
        if (p[0] == '"') {                              // rfc7233,l,512,514
158 160
                if (!http_GetHdr(req->resp, H_ETag, &e))
159 80
                        return (0);
160 80
                if ((e[0] == 'W' && e[1] == '/'))       // rfc7232,l,547,548
161 0
                        return (0);
162
                /* XXX: should we also have http_etag_cmp() ? */
163 80
                return (strcmp(p, e) == 0);             // rfc7232,l,548,548
164
        }
165
166
        /* assume date, strong check [RFC7232 2.2.2 p7] */
167 200
        ims = VTIM_parse(p);
168 200
        if (!ims)                                       // rfc7233,l,502,512
169 0
                return (0);
170
171
        /* the response needs a Date */
172
        // rfc7232,l,439,440
173 200
        if (!http_GetHdr(req->resp, H_Date, &p))
174 0
                return (0);
175 200
        d = VTIM_parse(p);
176 200
        if (!d)
177 0
                return (0);
178
179
180
        /* grab the Last Modified value */
181 200
        if (!http_GetHdr(req->resp, H_Last_Modified, &p))
182 80
                return (0);
183
184 120
        lm = VTIM_parse(p);
185 120
        if (!lm)
186 0
                return (0);
187
188
        /* Last Modified must be 60 seconds older than Date */
189 120
        if (lm > d + 60)                                // rfc7232,l,442,443
190 0
                return (0);
191
192 120
        if (lm != ims)                                  // rfc7233,l,455,456
193 80
                return (0);
194 40
        return (1);
195 2240
}
196
197
static int v_matchproto_(vdp_init_f)
198 2240
vrg_range_init(VRT_CTX, struct vdp_ctx *vdc, void **priv)
199
{
200
        const char *err;
201
202 2240
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
203 2240
        CHECK_OBJ_ORNULL(ctx->req, REQ_MAGIC);
204 2240
        CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC);
205 2240
        AN(priv);
206
207 2240
        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 2240
        if (!vrg_ifrange(ctx->req))             // rfc7233,l,455,456
216 280
                return (1);
217 1960
        err = vrg_dorange(ctx->req, priv);
218 1960
        if (err == NULL)
219 1600
                return (*priv == NULL ? 1 : 0);
220
221 360
        VSLb(vdc->vsl, SLT_Debug, "RANGE_FAIL %s", err);
222 360
        if (ctx->req->resp_len >= 0)
223 720
                http_PrintfHeader(ctx->req->resp,
224
                    "Content-Range: bytes */%jd",
225 360
                    (intmax_t)ctx->req->resp_len);
226 360
        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 360
        ctx->req->resp_len = 0;
232 360
        return (1);
233 2240
}
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 80719
VRG_CheckBo(struct busyobj *bo)
346
{
347
        ssize_t rlo, rhi, crlo, crhi, crlen, clen;
348
        const char *err;
349
350 80719
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
351
352 80719
        if (!cache_param->http_range_support)
353 160
                return (0);
354
355 80559
        err = http_GetRange(bo->bereq, &rlo, &rhi, -1);
356 80559
        clen = http_GetContentLength(bo->beresp);
357 80559
        crlen = http_GetContentRange(bo->beresp, &crlo, &crhi);
358
359 80559
        if (err != NULL) {
360 40
                VSLb(bo->vsl, SLT_Error, "Invalid range header (%s)", err);
361 40
                return (-1);
362
        }
363
364 80519
        if (crlen < -1) {
365 80
                VSLb(bo->vsl, SLT_Error, "Invalid content-range header");
366 80
                return (-1);
367
        }
368
369 80439
        if (clen < -1) {
370 0
                VSLb(bo->vsl, SLT_Error, "Invalid content-length header");
371 0
                return (-1);
372
        }
373
374 80439
        if (crlo < 0 && crhi < 0 && crlen < 0) {
375 80159
                VSLb(bo->vsl, SLT_Debug,
376
                    "Missing content-range header or unknown range unit");
377 80159
                return (0);
378
        }
379
380 280
        if (rlo < 0 && rhi < 0) {
381 80
                VSLb(bo->vsl, SLT_Error, "Unexpected content-range header");
382 80
                return (-1);
383
        }
384
385 200
        if (crlo < 0) {         // Content-Range: bytes */123
386 40
                assert(crhi < 0);
387 40
                assert(crlen > 0);
388 40
                if (http_GetStatus(bo->beresp) == 416)
389 40
                        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 160
        crlen = (crhi - crlo) + 1;
405 160
        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 80
        if (rlo < 0)
413 0
                return (0);
414
415 80
        RANGE_CHECK(rlo, ==, crlo, "low range");
416 80
        RANGE_CHECK(rhi, >=, crhi, "minimum high range");
417
#undef RANGE_CHECK
418
419 80
        return (0);
420 80719
}