]>
Commit | Line | Data |
---|---|---|
7c887d1f | 1 | |
2 | /* | |
262a0e14 | 3 | * $Id$ |
7c887d1f | 4 | * |
5 | * DEBUG: section 68 HTTP Content-Range Header | |
6 | * AUTHOR: Alex Rousskov | |
7 | * | |
2b6662ba | 8 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
e25c139f | 9 | * ---------------------------------------------------------- |
7c887d1f | 10 | * |
2b6662ba | 11 | * Squid is the result of efforts by numerous individuals from |
12 | * the Internet community; see the CONTRIBUTORS file for full | |
13 | * details. Many organizations have provided support for Squid's | |
14 | * development; see the SPONSORS file for full details. Squid is | |
15 | * Copyrighted (C) 2001 by the Regents of the University of | |
16 | * California; see the COPYRIGHT file for full details. Squid | |
17 | * incorporates software developed and/or copyrighted by other | |
18 | * sources; see the CREDITS file for full details. | |
7c887d1f | 19 | * |
20 | * This program is free software; you can redistribute it and/or modify | |
21 | * it under the terms of the GNU General Public License as published by | |
22 | * the Free Software Foundation; either version 2 of the License, or | |
23 | * (at your option) any later version. | |
26ac0430 | 24 | * |
7c887d1f | 25 | * This program is distributed in the hope that it will be useful, |
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
28 | * GNU General Public License for more details. | |
26ac0430 | 29 | * |
7c887d1f | 30 | * You should have received a copy of the GNU General Public License |
31 | * along with this program; if not, write to the Free Software | |
cbdec147 | 32 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
e25c139f | 33 | * |
7c887d1f | 34 | */ |
35 | ||
f7f3304a | 36 | #include "squid-old.h" |
528b2c61 | 37 | #include "HttpHdrContRange.h" |
7c887d1f | 38 | |
b644367b | 39 | /* |
40 | * Currently only byte ranges are supported | |
41 | * | |
42 | * Content-Range = "Content-Range" ":" content-range-spec | |
43 | * content-range-spec = byte-content-range-spec | |
44 | * byte-content-range-spec = bytes-unit SP | |
45 | * ( byte-range-resp-spec | "*") "/" | |
46 | * ( entity-length | "*" ) | |
47 | * byte-range-resp-spec = first-byte-pos "-" last-byte-pos | |
48 | * entity-length = 1*DIGIT | |
49 | */ | |
7c887d1f | 50 | |
51 | ||
52 | /* local constants */ | |
ed013b6c | 53 | #define range_spec_unknown (-1) |
7c887d1f | 54 | |
55 | /* local routines */ | |
56 | #define known_spec(s) ((s) != range_spec_unknown) | |
57 | #define size_min(a,b) ((a) <= (b) ? (a) : (b)) | |
58 | #define size_diff(a,b) ((a) >= (b) ? ((a)-(b)) : 0) | |
59 | ||
60 | /* globals */ | |
61 | ||
62 | /* parses range-resp-spec and inits spec, returns true on success */ | |
63 | static int | |
b644367b | 64 | httpHdrRangeRespSpecParseInit(HttpHdrRangeSpec * spec, const char *field, int flen) |
7c887d1f | 65 | { |
66 | const char *p; | |
67 | assert(spec); | |
68 | spec->offset = spec->length = range_spec_unknown; | |
62e76326 | 69 | |
7c887d1f | 70 | if (flen < 2) |
62e76326 | 71 | return 0; |
72 | ||
7c887d1f | 73 | /* is spec given ? */ |
74 | if (*field == '*') | |
62e76326 | 75 | return 1; |
76 | ||
7c887d1f | 77 | /* check format, must be %d-%d */ |
b644367b | 78 | if (!((p = strchr(field, '-')) && (p - field < flen))) { |
bf8fe701 | 79 | debugs(68, 2, "invalid (no '-') resp-range-spec near: '" << field << "'"); |
62e76326 | 80 | return 0; |
7c887d1f | 81 | } |
62e76326 | 82 | |
7c887d1f | 83 | /* parse offset */ |
47f6e231 | 84 | if (!httpHeaderParseOffset(field, &spec->offset)) |
62e76326 | 85 | return 0; |
86 | ||
03a7e69c HN |
87 | /* Additional check for BUG2155 - there MUST BE first-byte-pos and it MUST be positive*/ |
88 | if (spec->offset < 0) { | |
89 | debugs(68, 2, "invalid (no first-byte-pos or it is negative) resp-range-spec near: '" << field << "'"); | |
90 | return 0; | |
91 | } | |
92 | ||
7c887d1f | 93 | p++; |
62e76326 | 94 | |
7c887d1f | 95 | /* do we have last-pos ? */ |
03a7e69c HN |
96 | if (p - field >= flen) { |
97 | debugs(68, 2, "invalid (no last-byte-pos) resp-range-spec near: '" << field << "'"); | |
98 | return 0; | |
99 | } | |
100 | ||
101 | int64_t last_pos; | |
62e76326 | 102 | |
03a7e69c HN |
103 | if (!httpHeaderParseOffset(p, &last_pos)) |
104 | return 0; | |
62e76326 | 105 | |
03a7e69c HN |
106 | if (last_pos < spec->offset) { |
107 | debugs(68, 2, "invalid (negative last-byte-pos) resp-range-spec near: '" << field << "'"); | |
108 | return 0; | |
7c887d1f | 109 | } |
62e76326 | 110 | |
03a7e69c HN |
111 | spec->length = size_diff(last_pos + 1, spec->offset); |
112 | ||
7c887d1f | 113 | /* we managed to parse, check if the result makes sence */ |
03a7e69c | 114 | if (spec->length <= 0) { |
4a7a3d56 | 115 | debugs(68, 2, "invalid range (" << spec->offset << " += " << |
bf8fe701 | 116 | (long int) spec->length << ") in resp-range-spec near: '" << field << "'"); |
62e76326 | 117 | return 0; |
7c887d1f | 118 | } |
62e76326 | 119 | |
7c887d1f | 120 | return 1; |
121 | } | |
122 | ||
123 | static void | |
b644367b | 124 | httpHdrRangeRespSpecPackInto(const HttpHdrRangeSpec * spec, Packer * p) |
7c887d1f | 125 | { |
e6ccf245 | 126 | /* Ensure typecast is safe */ |
127 | assert (spec->length >= 0); | |
62e76326 | 128 | |
47f6e231 | 129 | if (!known_spec(spec->offset) || !known_spec(spec->length)) |
62e76326 | 130 | packerPrintf(p, "*"); |
7c887d1f | 131 | else |
47f6e231 | 132 | packerPrintf(p, "bytes %"PRId64"-%"PRId64, |
133 | spec->offset, spec->offset + spec->length - 1); | |
7c887d1f | 134 | } |
135 | ||
136 | /* | |
137 | * Content Range | |
138 | */ | |
139 | ||
140 | HttpHdrContRange * | |
9bc73deb | 141 | httpHdrContRangeCreate(void) |
7c887d1f | 142 | { |
e6ccf245 | 143 | HttpHdrContRange *r = (HttpHdrContRange *)memAllocate(MEM_HTTP_HDR_CONTENT_RANGE); |
7c887d1f | 144 | r->spec.offset = r->spec.length = range_spec_unknown; |
145 | r->elength = range_spec_unknown; | |
146 | return r; | |
147 | } | |
148 | ||
149 | HttpHdrContRange * | |
150 | httpHdrContRangeParseCreate(const char *str) | |
151 | { | |
152 | HttpHdrContRange *r = httpHdrContRangeCreate(); | |
62e76326 | 153 | |
7c887d1f | 154 | if (!httpHdrContRangeParseInit(r, str)) { |
62e76326 | 155 | httpHdrContRangeDestroy(r); |
156 | r = NULL; | |
7c887d1f | 157 | } |
62e76326 | 158 | |
7c887d1f | 159 | return r; |
160 | } | |
161 | ||
162 | /* returns true if ranges are valid; inits HttpHdrContRange */ | |
163 | int | |
b644367b | 164 | httpHdrContRangeParseInit(HttpHdrContRange * range, const char *str) |
7c887d1f | 165 | { |
166 | const char *p; | |
167 | assert(range && str); | |
bf8fe701 | 168 | debugs(68, 8, "parsing content-range field: '" << str << "'"); |
de336bbe | 169 | /* check range type */ |
62e76326 | 170 | |
de336bbe | 171 | if (strncasecmp(str, "bytes ", 6)) |
62e76326 | 172 | return 0; |
173 | ||
de336bbe | 174 | str += 6; |
62e76326 | 175 | |
7c887d1f | 176 | /* split */ |
177 | if (!(p = strchr(str, '/'))) | |
62e76326 | 178 | return 0; |
179 | ||
7c887d1f | 180 | if (*str == '*') |
62e76326 | 181 | range->spec.offset = range->spec.length = range_spec_unknown; |
b644367b | 182 | else if (!httpHdrRangeRespSpecParseInit(&range->spec, str, p - str)) |
62e76326 | 183 | return 0; |
184 | ||
7c887d1f | 185 | p++; |
62e76326 | 186 | |
7c887d1f | 187 | if (*p == '*') |
62e76326 | 188 | range->elength = range_spec_unknown; |
47f6e231 | 189 | else if (!httpHeaderParseOffset(p, &range->elength)) |
62e76326 | 190 | return 0; |
03a7e69c HN |
191 | else if (range->elength <= 0) { |
192 | /* Additional paranoidal check for BUG2155 - entity-length MUST be > 0 */ | |
193 | debugs(68, 2, "invalid (entity-length is negative) content-range-spec near: '" << str << "'"); | |
194 | return 0; | |
195 | } else if (known_spec(range->spec.length) && range->elength < (range->spec.offset + range->spec.length)) { | |
196 | debugs(68, 2, "invalid (range is outside entity-length) content-range-spec near: '" << str << "'"); | |
197 | return 0; | |
198 | } | |
62e76326 | 199 | |
26ac0430 AJ |
200 | debugs(68, 8, "parsed content-range field: " << |
201 | (long int) range->spec.offset << "-" << | |
202 | (long int) range->spec.offset + range->spec.length - 1 << " / " << | |
203 | (long int) range->elength); | |
62e76326 | 204 | |
7c887d1f | 205 | return 1; |
206 | } | |
207 | ||
208 | void | |
b644367b | 209 | httpHdrContRangeDestroy(HttpHdrContRange * range) |
7c887d1f | 210 | { |
211 | assert(range); | |
db1cd23c | 212 | memFree(range, MEM_HTTP_HDR_CONTENT_RANGE); |
7c887d1f | 213 | } |
214 | ||
215 | HttpHdrContRange * | |
216 | httpHdrContRangeDup(const HttpHdrContRange * range) | |
217 | { | |
218 | HttpHdrContRange *dup; | |
219 | assert(range); | |
220 | dup = httpHdrContRangeCreate(); | |
221 | *dup = *range; | |
222 | return dup; | |
223 | } | |
224 | ||
225 | void | |
226 | httpHdrContRangePackInto(const HttpHdrContRange * range, Packer * p) | |
227 | { | |
228 | assert(range && p); | |
229 | httpHdrRangeRespSpecPackInto(&range->spec, p); | |
e6ccf245 | 230 | /* Ensure typecast is safe */ |
231 | assert (range->elength >= 0); | |
62e76326 | 232 | |
47f6e231 | 233 | if (!known_spec(range->elength)) |
62e76326 | 234 | packerPrintf(p, "/*"); |
7c887d1f | 235 | else |
47f6e231 | 236 | packerPrintf(p, "/%"PRId64, range->elength); |
7c887d1f | 237 | } |
d192d11f | 238 | |
239 | void | |
47f6e231 | 240 | httpHdrContRangeSet(HttpHdrContRange * cr, HttpHdrRangeSpec spec, int64_t ent_len) |
d192d11f | 241 | { |
242 | assert(cr && ent_len >= 0); | |
243 | cr->spec = spec; | |
244 | cr->elength = ent_len; | |
245 | } |