HttpHdrContRange.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2023 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 "base/Packable.h"
13 #include "debug/Stream.h"
14 #include "enums.h"
15 #include "HttpHdrContRange.h"
16 #include "HttpHeaderTools.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
103 {
104  /* Ensure typecast is safe */
105  assert (spec->length >= 0);
106 
107  if (!known_spec(spec->offset) || !known_spec(spec->length))
108  p->append("*", 1);
109  else
110  p->appendf("bytes %" PRId64 "-%" PRId64, spec->offset, spec->offset + spec->length - 1);
111 }
112 
113 /*
114  * Content Range
115  */
116 
119 {
123  return r;
124 }
125 
128 {
130 
131  if (!httpHdrContRangeParseInit(r, str)) {
132  delete r;
133  return nullptr;
134  }
135 
136  return r;
137 }
138 
139 /* returns true if ranges are valid; inits HttpHdrContRange */
140 int
142 {
143  const char *p;
144  assert(range && str);
145  debugs(68, 8, "parsing content-range field: '" << str << "'");
146  /* check range type */
147 
148  if (strncasecmp(str, "bytes ", 6))
149  return 0;
150 
151  str += 6;
152 
153  /* split */
154  if (!(p = strchr(str, '/')))
155  return 0;
156 
157  if (*str == '*')
158  range->spec.offset = range->spec.length = range_spec_unknown;
159  else if (!httpHdrRangeRespSpecParseInit(&range->spec, str, p - str))
160  return 0;
161 
162  ++p;
163 
164  if (*p == '*') {
165  if (!known_spec(range->spec.offset)) {
166  debugs(68, 2, "invalid (*/*) content-range-spec near: '" << str << "'");
167  return 0;
168  }
169  range->elength = range_spec_unknown;
170  } else if (!httpHeaderParseOffset(p, &range->elength))
171  return 0;
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  }
180 
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 
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);
191 
192  return 1;
193 }
194 
197 {
198  HttpHdrContRange *dup;
199  assert(range);
200  dup = httpHdrContRangeCreate();
201  *dup = *range;
202  return dup;
203 }
204 
205 void
207 {
208  assert(range && p);
210  /* Ensure typecast is safe */
211  assert (range->elength >= 0);
212 
213  if (!known_spec(range->elength))
214  p->append("/*", 2);
215  else
216  p->appendf("/%" PRId64, range->elength);
217 }
218 
219 void
221 {
222  assert(cr && ent_len >= 0);
223  cr->spec = spec;
224  cr->elength = ent_len;
225 }
226 
HttpHdrContRange * httpHdrContRangeCreate(void)
HttpHdrRangeSpec spec
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
Definition: Packable.h:61
#define range_spec_unknown
virtual void append(const char *buf, int size)=0
Appends a c-string to existing packed data.
void httpHdrContRangeSet(HttpHdrContRange *cr, HttpHdrRangeSpec spec, int64_t ent_len)
int httpHdrContRangeParseInit(HttpHdrContRange *range, const char *str)
HttpHdrContRange * httpHdrContRangeDup(const HttpHdrContRange *range)
bool httpHeaderParseOffset(const char *start, int64_t *value, char **endPtr)
#define assert(EX)
Definition: assert.h:17
#define known_spec(s)
HttpHdrContRange * httpHdrContRangeParseCreate(const char *str)
#define size_diff(a, b)
static int httpHdrRangeRespSpecParseInit(HttpHdrRangeSpec *spec, const char *field, int flen)
#define PRId64
Definition: types.h:104
void httpHdrContRangePackInto(const HttpHdrContRange *range, Packable *p)
static void httpHdrRangeRespSpecPackInto(const HttpHdrRangeSpec *spec, Packable *p)
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192

 

Introduction

Documentation

Support

Miscellaneous