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