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