varnish-cache/bin/varnishd/cache/cache_range.c
1
/*-
2
 * Copyright (c) 2006 Verdens Gang AS
3
 * Copyright (c) 2006-2015 Varnish Software AS
4
 * All rights reserved.
5
 *
6
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
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
30
#include "config.h"
31
32
#include "cache_varnishd.h"
33
#include "cache_filter.h"
34
35
#include "vct.h"
36
37
/*--------------------------------------------------------------------*/
38
39
struct vrg_priv {
40
        unsigned                magic;
41
#define VRG_PRIV_MAGIC          0xb886e711
42
        ssize_t                 range_low;
43
        ssize_t                 range_high;
44
        ssize_t                 range_off;
45
};
46
47
static int v_matchproto_(vdp_bytes)
48 132
vrg_range_bytes(struct req *req, enum vdp_action act, void **priv,
49
    const void *ptr, ssize_t len)
50
{
51 132
        int retval = 0;
52
        ssize_t l;
53 132
        const char *p = ptr;
54
        struct vrg_priv *vrg_priv;
55
56 132
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
57 132
        if (act == VDP_INIT)
58 40
                return (0);
59 92
        CAST_OBJ_NOTNULL(vrg_priv, *priv, VRG_PRIV_MAGIC);
60 92
        if (act == VDP_FINI) {
61 40
                if (vrg_priv->range_off < vrg_priv->range_high)
62 4
                        Req_Fail(req, SC_RANGE_SHORT);
63 40
                *priv = NULL;   /* struct on ws, no need to free */
64 40
                return (0);
65
        }
66
67 52
        l = vrg_priv->range_low - vrg_priv->range_off;
68 52
        if (l > 0) {
69 24
                if (l > len)
70 0
                        l = len;
71 24
                vrg_priv->range_off += l;
72 24
                p += l;
73 24
                len -= l;
74
        }
75 52
        l = vrg_priv->range_high - vrg_priv->range_off;
76 52
        if (l > len)
77 16
                l = len;
78 52
        if (l > 0)
79 52
                retval = VDP_bytes(req, act, p, l);
80 0
        else if (act > VDP_NULL)
81 0
                retval = VDP_bytes(req, act, p, 0);
82 52
        vrg_priv->range_off += len;
83 52
        return (retval ||
84 52
            vrg_priv->range_off >= vrg_priv->range_high ? 1 : 0);
85
}
86
87
static const struct vdp vrg_vdp = {
88
        .name =         "RNG",
89
        .func =         vrg_range_bytes,
90
};
91
92
/*--------------------------------------------------------------------*/
93
94
static const char *
95 62
vrg_dorange(struct req *req, const char *r)
96
{
97
        ssize_t low, high, has_low, has_high, t;
98
        struct vrg_priv *vrg_priv;
99
100 62
        if (strncasecmp(r, "bytes=", 6))
101 8
                return ("Not Bytes");
102 54
        r += 6;
103
104
        /* The low end of range */
105 54
        has_low = low = 0;
106 164
        while (vct_isdigit(*r)) {
107 56
                has_low = 1;
108 56
                t = low;
109 56
                low *= 10;
110 56
                low += *r++ - '0';
111 56
                if (low < t)
112 0
                        return ("Low number too big");
113
        }
114
115 54
        if (*r++ != '-')
116 0
                return ("Missing hyphen");
117
118
        /* The high end of range */
119 54
        has_high = high = 0;
120 176
        while (vct_isdigit(*r)) {
121 68
                has_high = 1;
122 68
                t = high;
123 68
                high *= 10;
124 68
                high += *r++ - '0';
125 68
                if (high < t)
126 0
                        return ("High number too big");
127
        }
128
129 54
        if (*r != '\0')
130 2
                return ("Trailing stuff");
131
132 52
        if (has_high + has_low == 0)
133 2
                return ("Neither high nor low");
134
135 50
        if (!has_low) {
136 8
                if (req->resp_len < 0)
137 0
                        return (NULL);          // Allow 200 response
138 8
                if (high == 0)
139 2
                        return ("No low, high is zero");
140 6
                low = req->resp_len - high;
141 6
                if (low < 0)
142 2
                        low = 0;
143 6
                high = req->resp_len - 1;
144 42
        } else if (req->resp_len >= 0 && (high >= req->resp_len || !has_high))
145 14
                high = req->resp_len - 1;
146 28
        else if (!has_high || req->resp_len < 0)
147 4
                return (NULL);                  // Allow 200 response
148
        /*
149
         * else (bo != NULL) {
150
         *    We assume that the client knows what it's doing and trust
151
         *    that both low and high make sense.
152
         * }
153
         */
154
155 44
        if (high < low)
156 4
                return ("high smaller than low");
157
158 40
        if (req->resp_len >= 0 && low >= req->resp_len)
159 0
                return ("low range beyond object");
160
161 40
        if (req->resp_len >= 0)
162 40
                http_PrintfHeader(req->resp, "Content-Range: bytes %jd-%jd/%jd",
163
                    (intmax_t)low, (intmax_t)high, (intmax_t)req->resp_len);
164
        else
165 0
                http_PrintfHeader(req->resp, "Content-Range: bytes %jd-%jd/*",
166
                    (intmax_t)low, (intmax_t)high);
167 40
        req->resp_len = (intmax_t)(1 + high - low);
168
169 40
        vrg_priv = WS_Alloc(req->ws, sizeof *vrg_priv);
170 40
        if (vrg_priv == NULL)
171 0
                return ("WS too small");
172
173 40
        XXXAN(vrg_priv);
174 40
        INIT_OBJ(vrg_priv, VRG_PRIV_MAGIC);
175 40
        vrg_priv->range_off = 0;
176 40
        vrg_priv->range_low = low;
177 40
        vrg_priv->range_high = high + 1;
178 40
        VDP_push(req, &vrg_vdp, vrg_priv, 1);
179 40
        http_PutResponse(req->resp, "HTTP/1.1", 206, NULL);
180 40
        return (NULL);
181
}
182
183
void
184 62
VRG_dorange(struct req *req, const char *r)
185
{
186
        const char *err;
187
188 62
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
189 62
        CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC);
190 62
        assert(http_IsStatus(req->resp, 200));
191
192
        /* We must snapshot the length if we're streaming from the backend */
193
194 62
        err = vrg_dorange(req, r);
195 62
        if (err != NULL) {
196 18
                VSLb(req->vsl, SLT_Debug, "RANGE_FAIL %s", err);
197 18
                if (req->resp_len >= 0)
198 18
                        http_PrintfHeader(req->resp,
199
                            "Content-Range: bytes */%jd",
200
                            (intmax_t)req->resp_len);
201 18
                http_PutResponse(req->resp, "HTTP/1.1", 416, NULL);
202
                /*
203
                 * XXX: We ought to produce a body explaining things.
204
                 * XXX: That really calls for us to hit vcl_synth{}
205
                 */
206 18
                req->resp_len = 0;
207
        }
208 62
}