]>
Commit | Line | Data |
---|---|---|
7c887d1f | 1 | /* |
f70aedc4 | 2 | * Copyright (C) 1996-2021 The Squid Software Foundation and contributors |
e25c139f | 3 | * |
bbc27441 AJ |
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. | |
7c887d1f | 7 | */ |
8 | ||
bbc27441 AJ |
9 | /* DEBUG: section 68 HTTP Content-Range Header */ |
10 | ||
582c2af2 | 11 | #include "squid.h" |
0a647ffb | 12 | #include "base/Packable.h" |
582c2af2 FC |
13 | #include "Debug.h" |
14 | #include "enums.h" | |
528b2c61 | 15 | #include "HttpHdrContRange.h" |
582c2af2 | 16 | #include "HttpHeaderTools.h" |
7c887d1f | 17 | |
b644367b | 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 | */ | |
7c887d1f | 29 | |
7c887d1f | 30 | /* local constants */ |
ed013b6c | 31 | #define range_spec_unknown (-1) |
7c887d1f | 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 | |
b644367b | 42 | httpHdrRangeRespSpecParseInit(HttpHdrRangeSpec * spec, const char *field, int flen) |
7c887d1f | 43 | { |
44 | const char *p; | |
45 | assert(spec); | |
46 | spec->offset = spec->length = range_spec_unknown; | |
62e76326 | 47 | |
7c887d1f | 48 | if (flen < 2) |
62e76326 | 49 | return 0; |
50 | ||
7c887d1f | 51 | /* is spec given ? */ |
52 | if (*field == '*') | |
62e76326 | 53 | return 1; |
54 | ||
7c887d1f | 55 | /* check format, must be %d-%d */ |
b644367b | 56 | if (!((p = strchr(field, '-')) && (p - field < flen))) { |
bf8fe701 | 57 | debugs(68, 2, "invalid (no '-') resp-range-spec near: '" << field << "'"); |
62e76326 | 58 | return 0; |
7c887d1f | 59 | } |
62e76326 | 60 | |
7c887d1f | 61 | /* parse offset */ |
47f6e231 | 62 | if (!httpHeaderParseOffset(field, &spec->offset)) |
62e76326 | 63 | return 0; |
64 | ||
03a7e69c HN |
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 | ||
95dc7ff4 | 71 | ++p; |
62e76326 | 72 | |
7c887d1f | 73 | /* do we have last-pos ? */ |
03a7e69c HN |
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; | |
62e76326 | 80 | |
03a7e69c HN |
81 | if (!httpHeaderParseOffset(p, &last_pos)) |
82 | return 0; | |
62e76326 | 83 | |
03a7e69c HN |
84 | if (last_pos < spec->offset) { |
85 | debugs(68, 2, "invalid (negative last-byte-pos) resp-range-spec near: '" << field << "'"); | |
86 | return 0; | |
7c887d1f | 87 | } |
62e76326 | 88 | |
03a7e69c HN |
89 | spec->length = size_diff(last_pos + 1, spec->offset); |
90 | ||
7c887d1f | 91 | /* we managed to parse, check if the result makes sence */ |
03a7e69c | 92 | if (spec->length <= 0) { |
4a7a3d56 | 93 | debugs(68, 2, "invalid range (" << spec->offset << " += " << |
bf8fe701 | 94 | (long int) spec->length << ") in resp-range-spec near: '" << field << "'"); |
62e76326 | 95 | return 0; |
7c887d1f | 96 | } |
62e76326 | 97 | |
7c887d1f | 98 | return 1; |
99 | } | |
100 | ||
101 | static void | |
17802cf1 | 102 | httpHdrRangeRespSpecPackInto(const HttpHdrRangeSpec * spec, Packable * p) |
7c887d1f | 103 | { |
e6ccf245 | 104 | /* Ensure typecast is safe */ |
105 | assert (spec->length >= 0); | |
62e76326 | 106 | |
47f6e231 | 107 | if (!known_spec(spec->offset) || !known_spec(spec->length)) |
4391cd15 | 108 | p->append("*", 1); |
7c887d1f | 109 | else |
4391cd15 | 110 | p->appendf("bytes %" PRId64 "-%" PRId64, spec->offset, spec->offset + spec->length - 1); |
7c887d1f | 111 | } |
112 | ||
113 | /* | |
114 | * Content Range | |
115 | */ | |
116 | ||
117 | HttpHdrContRange * | |
9bc73deb | 118 | httpHdrContRangeCreate(void) |
7c887d1f | 119 | { |
3c670b50 | 120 | HttpHdrContRange *r = new HttpHdrContRange; |
7c887d1f | 121 | r->spec.offset = r->spec.length = range_spec_unknown; |
122 | r->elength = range_spec_unknown; | |
123 | return r; | |
124 | } | |
125 | ||
126 | HttpHdrContRange * | |
127 | httpHdrContRangeParseCreate(const char *str) | |
128 | { | |
129 | HttpHdrContRange *r = httpHdrContRangeCreate(); | |
62e76326 | 130 | |
7c887d1f | 131 | if (!httpHdrContRangeParseInit(r, str)) { |
3c670b50 AJ |
132 | delete r; |
133 | return nullptr; | |
7c887d1f | 134 | } |
62e76326 | 135 | |
7c887d1f | 136 | return r; |
137 | } | |
138 | ||
139 | /* returns true if ranges are valid; inits HttpHdrContRange */ | |
140 | int | |
b644367b | 141 | httpHdrContRangeParseInit(HttpHdrContRange * range, const char *str) |
7c887d1f | 142 | { |
143 | const char *p; | |
144 | assert(range && str); | |
bf8fe701 | 145 | debugs(68, 8, "parsing content-range field: '" << str << "'"); |
de336bbe | 146 | /* check range type */ |
62e76326 | 147 | |
de336bbe | 148 | if (strncasecmp(str, "bytes ", 6)) |
62e76326 | 149 | return 0; |
150 | ||
de336bbe | 151 | str += 6; |
62e76326 | 152 | |
7c887d1f | 153 | /* split */ |
154 | if (!(p = strchr(str, '/'))) | |
62e76326 | 155 | return 0; |
156 | ||
7c887d1f | 157 | if (*str == '*') |
62e76326 | 158 | range->spec.offset = range->spec.length = range_spec_unknown; |
b644367b | 159 | else if (!httpHdrRangeRespSpecParseInit(&range->spec, str, p - str)) |
62e76326 | 160 | return 0; |
161 | ||
95dc7ff4 | 162 | ++p; |
62e76326 | 163 | |
6c9c44d0 AR |
164 | if (*p == '*') { |
165 | if (!known_spec(range->spec.offset)) { | |
166 | debugs(68, 2, "invalid (*/*) content-range-spec near: '" << str << "'"); | |
167 | return 0; | |
168 | } | |
62e76326 | 169 | range->elength = range_spec_unknown; |
6c9c44d0 | 170 | } else if (!httpHeaderParseOffset(p, &range->elength)) |
62e76326 | 171 | return 0; |
03a7e69c HN |
172 | else if (range->elength <= 0) { |
173 | /* Additional paranoidal check for BUG2155 - entity-length MUST be > 0 */ | |
174 | debugs(68, 2, "invalid (entity-length is negative) content-range-spec near: '" << str << "'"); | |
175 | return 0; | |
176 | } else if (known_spec(range->spec.length) && range->elength < (range->spec.offset + range->spec.length)) { | |
177 | debugs(68, 2, "invalid (range is outside entity-length) content-range-spec near: '" << str << "'"); | |
178 | return 0; | |
179 | } | |
62e76326 | 180 | |
6c9c44d0 AR |
181 | // reject unsatisfied-range and such; we only use well-defined ranges today |
182 | if (!known_spec(range->spec.offset) || !known_spec(range->spec.length)) { | |
183 | debugs(68, 2, "unwanted content-range-spec near: '" << str << "'"); | |
184 | return 0; | |
185 | } | |
186 | ||
26ac0430 AJ |
187 | debugs(68, 8, "parsed content-range field: " << |
188 | (long int) range->spec.offset << "-" << | |
189 | (long int) range->spec.offset + range->spec.length - 1 << " / " << | |
190 | (long int) range->elength); | |
62e76326 | 191 | |
7c887d1f | 192 | return 1; |
193 | } | |
194 | ||
7c887d1f | 195 | HttpHdrContRange * |
196 | httpHdrContRangeDup(const HttpHdrContRange * range) | |
197 | { | |
198 | HttpHdrContRange *dup; | |
199 | assert(range); | |
200 | dup = httpHdrContRangeCreate(); | |
201 | *dup = *range; | |
202 | return dup; | |
203 | } | |
204 | ||
205 | void | |
17802cf1 | 206 | httpHdrContRangePackInto(const HttpHdrContRange * range, Packable * p) |
7c887d1f | 207 | { |
208 | assert(range && p); | |
209 | httpHdrRangeRespSpecPackInto(&range->spec, p); | |
e6ccf245 | 210 | /* Ensure typecast is safe */ |
211 | assert (range->elength >= 0); | |
62e76326 | 212 | |
47f6e231 | 213 | if (!known_spec(range->elength)) |
4391cd15 | 214 | p->append("/*", 2); |
7c887d1f | 215 | else |
4391cd15 | 216 | p->appendf("/%" PRId64, range->elength); |
7c887d1f | 217 | } |
d192d11f | 218 | |
219 | void | |
47f6e231 | 220 | httpHdrContRangeSet(HttpHdrContRange * cr, HttpHdrRangeSpec spec, int64_t ent_len) |
d192d11f | 221 | { |
222 | assert(cr && ent_len >= 0); | |
223 | cr->spec = spec; | |
224 | cr->elength = ent_len; | |
225 | } | |
f53969cc | 226 |