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 144
vrg_range_bytes(struct req *req, enum vdp_action act, void **priv,
49
    const void *ptr, ssize_t len)
50
{
51 144
        int retval = 0;
52
        ssize_t l;
53 144
        const char *p = ptr;
54
        struct vrg_priv *vrg_priv;
55
56 144
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
57 144
        if (act == VDP_INIT)
58 44
                return (0);
59 100
        CAST_OBJ_NOTNULL(vrg_priv, *priv, VRG_PRIV_MAGIC);
60 100
        if (act == VDP_FINI) {
61 44
                if (vrg_priv->range_off < vrg_priv->range_high)
62 4
                        Req_Fail(req, SC_RANGE_SHORT);
63 44
                *priv = NULL;   /* struct on ws, no need to free */
64 44
                return (0);
65
        }
66
67 56
        l = vrg_priv->range_low - vrg_priv->range_off;
68 56
        if (l > 0) {
69 28
                if (l > len)
70 0
                        l = len;
71 28
                vrg_priv->range_off += l;
72 28
                p += l;
73 28
                len -= l;
74
        }
75 56
        l = vrg_priv->range_high - vrg_priv->range_off;
76 56
        if (l > len)
77 16
                l = len;
78 56
        if (l > 0)
79 56
                retval = VDP_bytes(req, act, p, l);
80 0
        else if (act > VDP_NULL)
81 0
                retval = VDP_bytes(req, act, p, 0);
82 56
        vrg_priv->range_off += len;
83
        return (retval ||
84 56
            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 68
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 68
        if (strncasecmp(r, "bytes=", 6))
101 8
                return ("Not Bytes");
102 60
        r += 6;
103
104
        /* The low end of range */
105 60
        has_low = low = 0;
106 182
        while (vct_isdigit(*r)) {
107 62
                has_low = 1;
108 62
                t = low;
109 62
                low *= 10;
110 62
                low += *r++ - '0';
111 62
                if (low < t)
112 0
                        return ("Low number too big");
113
        }
114
115 60
        if (*r++ != '-')
116 0
                return ("Missing hyphen");
117
118
        /* The high end of range */
119 60
        has_high = high = 0;
120 194
        while (vct_isdigit(*r)) {
121 74
                has_high = 1;
122 74
                t = high;
123 74
                high *= 10;
124 74
                high += *r++ - '0';
125 74
                if (high < t)
126 0
                        return ("High number too big");
127
        }
128
129 60
        if (*r != '\0')
130 2
                return ("Trailing stuff");
131
132 58
        if (has_high + has_low == 0)
133 2
                return ("Neither high nor low");
134
135 56
        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 48
        } else if (req->resp_len >= 0 && (high >= req->resp_len || !has_high))
145 14
                high = req->resp_len - 1;
146 34
        else if (!has_high || req->resp_len < 0)
147 6
                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 48
        if (high < low)
156 4
                return ("high smaller than low");
157
158 44
        if (req->resp_len >= 0 && low >= req->resp_len)
159 0
                return ("low range beyond object");
160
161 44
        if (req->resp_len >= 0)
162 44
                http_PrintfHeader(req->resp, "Content-Range: bytes %jd-%jd/%jd",
163 44
                    (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 44
        req->resp_len = (intmax_t)(1 + high - low);
168
169 44
        vrg_priv = WS_Alloc(req->ws, sizeof *vrg_priv);
170 44
        if (vrg_priv == NULL)
171 0
                return ("WS too small");
172
173 44
        XXXAN(vrg_priv);
174 44
        INIT_OBJ(vrg_priv, VRG_PRIV_MAGIC);
175 44
        vrg_priv->range_off = 0;
176 44
        vrg_priv->range_low = low;
177 44
        vrg_priv->range_high = high + 1;
178 44
        if (VDP_push(req, &vrg_vdp, vrg_priv, 1))
179 0
                return ("WS too small");
180 44
        http_PutResponse(req->resp, "HTTP/1.1", 206, NULL);
181 44
        return (NULL);
182
}
183
184
void
185 68
VRG_dorange(struct req *req, const char *r)
186
{
187
        const char *err;
188
189 68
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
190 68
        CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC);
191 68
        assert(http_IsStatus(req->resp, 200));
192
193
        /* We must snapshot the length if we're streaming from the backend */
194
195 68
        err = vrg_dorange(req, r);
196 68
        if (err != NULL) {
197 18
                VSLb(req->vsl, SLT_Debug, "RANGE_FAIL %s", err);
198 18
                if (req->resp_len >= 0)
199 18
                        http_PrintfHeader(req->resp,
200
                            "Content-Range: bytes */%jd",
201 18
                            (intmax_t)req->resp_len);
202 18
                http_PutResponse(req->resp, "HTTP/1.1", 416, NULL);
203
                /*
204
                 * XXX: We ought to produce a body explaining things.
205
                 * XXX: That really calls for us to hit vcl_synth{}
206
                 */
207 18
                req->resp_len = 0;
208
        }
209 68
}