]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpHdrRange.cc
3 * $Id: HttpHdrRange.cc,v 1.6 1998/04/06 22:32:07 wessels Exp $
5 * DEBUG: section 64 HTTP Range Header
6 * AUTHOR: Alex Rousskov
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * --------------------------------------------------------
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by
14 * the National Science Foundation.
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 * Currently only byte ranges are supported
37 * Essentially, there are three types of byte ranges:
39 * 1) first-byte-pos "-" last-byte-pos // range
40 * 2) first-byte-pos "-" // trailer
41 * 3) "-" suffix-length // suffix (last length bytes)
44 * When Range field is parsed, we have no clue about the content
45 * length of the document. Thus, we simply code an "absent" part
46 * using range_spec_unknown constant.
48 * Note: when response length becomes known, we convert any range
49 * spec into type one above. (Canonization process).
54 #define range_spec_unknown ((size_t)-1)
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 static HttpHdrRangeSpec
*httpHdrRangeSpecDup(const HttpHdrRangeSpec
* spec
);
61 static int httpHdrRangeSpecCanonize(HttpHdrRangeSpec
* spec
, size_t clen
);
62 static void httpHdrRangeSpecPackInto(const HttpHdrRangeSpec
* spec
, Packer
* p
);
65 static int RangeParsedCount
= 0;
71 static HttpHdrRangeSpec
*
72 httpHdrRangeSpecCreate()
74 return memAllocate(MEM_HTTP_HDR_RANGE_SPEC
);
77 /* parses range-spec and returns new object on success */
78 static HttpHdrRangeSpec
*
79 httpHdrRangeSpecParseCreate(const char *field
, int flen
)
81 HttpHdrRangeSpec spec
=
82 {range_spec_unknown
, range_spec_unknown
};
86 /* is it a suffix-byte-range-spec ? */
88 if (!httpHeaderParseSize(field
+ 1, &spec
.length
))
91 /* must have a '-' somewhere in _this_ field */
92 if (!((p
= strchr(field
, '-')) || (p
- field
>= flen
))) {
93 debug(64, 2) ("ignoring invalid (missing '-') range-spec near: '%s'\n", field
);
96 if (!httpHeaderParseSize(field
, &spec
.offset
))
99 /* do we have last-pos ? */
100 if (p
- field
< flen
) {
102 if (!httpHeaderParseSize(p
, &last_pos
))
104 spec
.length
= size_diff(last_pos
+ 1, spec
.offset
);
107 /* we managed to parse, check if the result makes sence */
108 if (known_spec(spec
.length
) && !spec
.length
) {
109 debug(64, 2) ("ignoring invalid (zero length) range-spec near: '%s'\n", field
);
112 return httpHdrRangeSpecDup(&spec
);
116 httpHdrRangeSpecDestroy(HttpHdrRangeSpec
* spec
)
118 memFree(MEM_HTTP_HDR_RANGE_SPEC
, spec
);
122 static HttpHdrRangeSpec
*
123 httpHdrRangeSpecDup(const HttpHdrRangeSpec
* spec
)
125 HttpHdrRangeSpec
*dup
= httpHdrRangeSpecCreate();
126 dup
->offset
= spec
->offset
;
127 dup
->length
= spec
->length
;
132 httpHdrRangeSpecPackInto(const HttpHdrRangeSpec
* spec
, Packer
* p
)
134 if (!known_spec(spec
->offset
)) /* suffix */
135 packerPrintf(p
, "-%d", spec
->length
);
136 else if (!known_spec(spec
->length
)) /* trailer */
137 packerPrintf(p
, "%d-", spec
->offset
);
139 packerPrintf(p
, "%d-%d",
140 spec
->offset
, spec
->offset
+ spec
->length
- 1);
143 /* fills "absent" positions in range specification based on response body size
144 * returns true if the range is still valid
145 * range is valid if its intersection with [0,length-1] is not empty
148 httpHdrRangeSpecCanonize(HttpHdrRangeSpec
* spec
, size_t clen
)
150 if (!known_spec(spec
->offset
)) /* suffix */
151 spec
->offset
= size_diff(clen
, spec
->length
);
152 else if (!known_spec(spec
->length
)) /* trailer */
153 spec
->length
= size_diff(clen
, spec
->offset
);
154 /* we have a "range" now, adjust length if needed */
155 assert(known_spec(spec
->length
));
156 assert(known_spec(spec
->offset
));
157 spec
->length
= size_min(size_diff(clen
, spec
->offset
), spec
->length
);
158 /* check range validity */
159 return spec
->length
> 0;
169 HttpHdrRange
*r
= memAllocate(MEM_HTTP_HDR_RANGE
);
170 stackInit(&r
->specs
);
175 httpHdrRangeParseCreate(const char *str
)
177 HttpHdrRange
*r
= httpHdrRangeCreate();
178 if (!httpHdrRangeParseInit(r
, str
)) {
179 httpHdrRangeDestroy(r
);
185 /* returns true if ranges are valid; inits HttpHdrRange */
187 httpHdrRangeParseInit(HttpHdrRange
* range
, const char *str
)
190 const char *pos
= NULL
;
192 assert(range
&& str
);
194 debug(64, 8) ("parsing range field: '%s'\n", str
);
195 /* check range type */
196 if (strncasecmp(str
, "bytes=", 6))
199 /* iterate through comma separated list */
200 while (strListGetItem(str
, ',', &item
, &ilen
, &pos
)) {
201 HttpHdrRangeSpec
*spec
= httpHdrRangeSpecParseCreate(item
, ilen
);
203 * HTTP/1.1 draft says we must ignore the whole header field if one spec
204 * is invalid. However, RFC 2068 just says that we must ignore that spec.
207 stackPush(&range
->specs
, spec
);
209 debug(68, 8) ("parsed range range count: %d\n", range
->specs
.count
);
210 return range
->specs
.count
;
214 httpHdrRangeDestroy(HttpHdrRange
* range
)
217 while (range
->specs
.count
)
218 httpHdrRangeSpecDestroy(stackPop(&range
->specs
));
219 stackClean(&range
->specs
);
220 memFree(MEM_HTTP_HDR_RANGE
, range
);
224 httpHdrRangeDup(const HttpHdrRange
* range
)
229 dup
= httpHdrRangeCreate();
230 stackPrePush(&dup
->specs
, range
->specs
.count
);
231 for (i
= 0; i
< range
->specs
.count
; i
++)
232 stackPush(&dup
->specs
, httpHdrRangeSpecDup(range
->specs
.items
[i
]));
233 assert(range
->specs
.count
== dup
->specs
.count
);
238 httpHdrRangePackInto(const HttpHdrRange
* range
, Packer
* p
)
240 HttpHdrRangePos pos
= HttpHdrRangeInitPos
;
241 HttpHdrRangeSpec spec
;
243 while (httpHdrRangeGetSpec(range
, &spec
, &pos
)) {
244 if (pos
!= HttpHdrRangeInitPos
)
245 packerAppend(p
, ",", 1);
246 httpHdrRangeSpecPackInto(&spec
, p
);
251 * canonizes all range specs within a set preserving the order
252 * returns true if the set is valid after canonization;
253 * the set is valid if
254 * - all range specs are valid and
255 * - there is at least one range spec
258 httpHdrRangeCanonize(HttpHdrRange
* range
, size_t clen
)
262 for (i
= 0; i
< range
->specs
.count
; i
++)
263 if (!httpHdrRangeSpecCanonize(range
->specs
.items
[i
], clen
))
265 return range
->specs
.count
;
268 /* searches for next range, returns true if found */
270 httpHdrRangeGetSpec(const HttpHdrRange
* range
, HttpHdrRangeSpec
* spec
, int *pos
)
272 assert(range
&& spec
);
273 assert(pos
&& *pos
>= -1 && *pos
< range
->specs
.count
);
275 if (*pos
< range
->specs
.count
) {
276 *spec
= *(HttpHdrRangeSpec
*) range
->specs
.items
[*pos
];
279 spec
->offset
= spec
->length
= 0;