]> git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpHdrContRange.cc
Merge from trunk rev.13584
[thirdparty/squid.git] / src / HttpHdrContRange.cc
1 /*
2 * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 /* DEBUG: section 68 HTTP Content-Range Header */
10
11 #include "squid.h"
12 #include "Debug.h"
13 #include "enums.h"
14 #include "HttpHdrContRange.h"
15 #include "HttpHeaderTools.h"
16 #include "Mem.h"
17
18 /*
19 * Currently only byte ranges are supported
20 *
21 * Content-Range = "Content-Range" ":" content-range-spec
22 * content-range-spec = byte-content-range-spec
23 * byte-content-range-spec = bytes-unit SP
24 * ( byte-range-resp-spec | "*") "/"
25 * ( entity-length | "*" )
26 * byte-range-resp-spec = first-byte-pos "-" last-byte-pos
27 * entity-length = 1*DIGIT
28 */
29
30 /* local constants */
31 #define range_spec_unknown (-1)
32
33 /* local routines */
34 #define known_spec(s) ((s) != range_spec_unknown)
35 #define size_min(a,b) ((a) <= (b) ? (a) : (b))
36 #define size_diff(a,b) ((a) >= (b) ? ((a)-(b)) : 0)
37
38 /* globals */
39
40 /* parses range-resp-spec and inits spec, returns true on success */
41 static int
42 httpHdrRangeRespSpecParseInit(HttpHdrRangeSpec * spec, const char *field, int flen)
43 {
44 const char *p;
45 assert(spec);
46 spec->offset = spec->length = range_spec_unknown;
47
48 if (flen < 2)
49 return 0;
50
51 /* is spec given ? */
52 if (*field == '*')
53 return 1;
54
55 /* check format, must be %d-%d */
56 if (!((p = strchr(field, '-')) && (p - field < flen))) {
57 debugs(68, 2, "invalid (no '-') resp-range-spec near: '" << field << "'");
58 return 0;
59 }
60
61 /* parse offset */
62 if (!httpHeaderParseOffset(field, &spec->offset))
63 return 0;
64
65 /* Additional check for BUG2155 - there MUST BE first-byte-pos and it MUST be positive*/
66 if (spec->offset < 0) {
67 debugs(68, 2, "invalid (no first-byte-pos or it is negative) resp-range-spec near: '" << field << "'");
68 return 0;
69 }
70
71 ++p;
72
73 /* do we have last-pos ? */
74 if (p - field >= flen) {
75 debugs(68, 2, "invalid (no last-byte-pos) resp-range-spec near: '" << field << "'");
76 return 0;
77 }
78
79 int64_t last_pos;
80
81 if (!httpHeaderParseOffset(p, &last_pos))
82 return 0;
83
84 if (last_pos < spec->offset) {
85 debugs(68, 2, "invalid (negative last-byte-pos) resp-range-spec near: '" << field << "'");
86 return 0;
87 }
88
89 spec->length = size_diff(last_pos + 1, spec->offset);
90
91 /* we managed to parse, check if the result makes sence */
92 if (spec->length <= 0) {
93 debugs(68, 2, "invalid range (" << spec->offset << " += " <<
94 (long int) spec->length << ") in resp-range-spec near: '" << field << "'");
95 return 0;
96 }
97
98 return 1;
99 }
100
101 static void
102 httpHdrRangeRespSpecPackInto(const HttpHdrRangeSpec * spec, Packer * p)
103 {
104 /* Ensure typecast is safe */
105 assert (spec->length >= 0);
106
107 if (!known_spec(spec->offset) || !known_spec(spec->length))
108 packerPrintf(p, "*");
109 else
110 packerPrintf(p, "bytes %" PRId64 "-%" PRId64,
111 spec->offset, spec->offset + spec->length - 1);
112 }
113
114 /*
115 * Content Range
116 */
117
118 HttpHdrContRange *
119 httpHdrContRangeCreate(void)
120 {
121 HttpHdrContRange *r = (HttpHdrContRange *)memAllocate(MEM_HTTP_HDR_CONTENT_RANGE);
122 r->spec.offset = r->spec.length = range_spec_unknown;
123 r->elength = range_spec_unknown;
124 return r;
125 }
126
127 HttpHdrContRange *
128 httpHdrContRangeParseCreate(const char *str)
129 {
130 HttpHdrContRange *r = httpHdrContRangeCreate();
131
132 if (!httpHdrContRangeParseInit(r, str)) {
133 httpHdrContRangeDestroy(r);
134 r = NULL;
135 }
136
137 return r;
138 }
139
140 /* returns true if ranges are valid; inits HttpHdrContRange */
141 int
142 httpHdrContRangeParseInit(HttpHdrContRange * range, const char *str)
143 {
144 const char *p;
145 assert(range && str);
146 debugs(68, 8, "parsing content-range field: '" << str << "'");
147 /* check range type */
148
149 if (strncasecmp(str, "bytes ", 6))
150 return 0;
151
152 str += 6;
153
154 /* split */
155 if (!(p = strchr(str, '/')))
156 return 0;
157
158 if (*str == '*')
159 range->spec.offset = range->spec.length = range_spec_unknown;
160 else if (!httpHdrRangeRespSpecParseInit(&range->spec, str, p - str))
161 return 0;
162
163 ++p;
164
165 if (*p == '*')
166 range->elength = range_spec_unknown;
167 else if (!httpHeaderParseOffset(p, &range->elength))
168 return 0;
169 else if (range->elength <= 0) {
170 /* Additional paranoidal check for BUG2155 - entity-length MUST be > 0 */
171 debugs(68, 2, "invalid (entity-length is negative) content-range-spec near: '" << str << "'");
172 return 0;
173 } else if (known_spec(range->spec.length) && range->elength < (range->spec.offset + range->spec.length)) {
174 debugs(68, 2, "invalid (range is outside entity-length) content-range-spec near: '" << str << "'");
175 return 0;
176 }
177
178 debugs(68, 8, "parsed content-range field: " <<
179 (long int) range->spec.offset << "-" <<
180 (long int) range->spec.offset + range->spec.length - 1 << " / " <<
181 (long int) range->elength);
182
183 return 1;
184 }
185
186 void
187 httpHdrContRangeDestroy(HttpHdrContRange * range)
188 {
189 assert(range);
190 memFree(range, MEM_HTTP_HDR_CONTENT_RANGE);
191 }
192
193 HttpHdrContRange *
194 httpHdrContRangeDup(const HttpHdrContRange * range)
195 {
196 HttpHdrContRange *dup;
197 assert(range);
198 dup = httpHdrContRangeCreate();
199 *dup = *range;
200 return dup;
201 }
202
203 void
204 httpHdrContRangePackInto(const HttpHdrContRange * range, Packer * p)
205 {
206 assert(range && p);
207 httpHdrRangeRespSpecPackInto(&range->spec, p);
208 /* Ensure typecast is safe */
209 assert (range->elength >= 0);
210
211 if (!known_spec(range->elength))
212 packerPrintf(p, "/*");
213 else
214 packerPrintf(p, "/%" PRId64, range->elength);
215 }
216
217 void
218 httpHdrContRangeSet(HttpHdrContRange * cr, HttpHdrRangeSpec spec, int64_t ent_len)
219 {
220 assert(cr && ent_len >= 0);
221 cr->spec = spec;
222 cr->elength = ent_len;
223 }