]>
Commit | Line | Data |
---|---|---|
4d51c0eb AM |
1 | Index: modules/http/byterange_filter.c |
2 | =================================================================== | |
3 | --- modules/http/byterange_filter.c (Revision 1162579) | |
4 | +++ modules/http/byterange_filter.c (Arbeitskopie) | |
5 | @@ -55,66 +55,9 @@ | |
6 | #include <unistd.h> | |
7 | #endif | |
8 | ||
9 | -static int parse_byterange(char *range, apr_off_t clength, | |
10 | - apr_off_t *start, apr_off_t *end) | |
11 | -{ | |
12 | - char *dash = strchr(range, '-'); | |
13 | - char *errp; | |
14 | - apr_off_t number; | |
15 | +static int ap_set_byterange(request_rec *r, apr_off_t clength, | |
16 | + apr_array_header_t **indexes); | |
17 | ||
18 | - if (!dash) { | |
19 | - return 0; | |
20 | - } | |
21 | - | |
22 | - if ((dash == range)) { | |
23 | - /* In the form "-5" */ | |
24 | - if (apr_strtoff(&number, dash+1, &errp, 10) || *errp) { | |
25 | - return 0; | |
26 | - } | |
27 | - *start = clength - number; | |
28 | - *end = clength - 1; | |
29 | - } | |
30 | - else { | |
31 | - *dash++ = '\0'; | |
32 | - if (apr_strtoff(&number, range, &errp, 10) || *errp) { | |
33 | - return 0; | |
34 | - } | |
35 | - *start = number; | |
36 | - if (*dash) { | |
37 | - if (apr_strtoff(&number, dash, &errp, 10) || *errp) { | |
38 | - return 0; | |
39 | - } | |
40 | - *end = number; | |
41 | - } | |
42 | - else { /* "5-" */ | |
43 | - *end = clength - 1; | |
44 | - } | |
45 | - } | |
46 | - | |
47 | - if (*start < 0) { | |
48 | - *start = 0; | |
49 | - } | |
50 | - | |
51 | - if (*end >= clength) { | |
52 | - *end = clength - 1; | |
53 | - } | |
54 | - | |
55 | - if (*start > *end) { | |
56 | - return -1; | |
57 | - } | |
58 | - | |
59 | - return (*start > 0 || *end < clength); | |
60 | -} | |
61 | - | |
62 | -static int ap_set_byterange(request_rec *r); | |
63 | - | |
64 | -typedef struct byterange_ctx { | |
65 | - apr_bucket_brigade *bb; | |
66 | - int num_ranges; | |
67 | - char *boundary; | |
68 | - char *bound_head; | |
69 | -} byterange_ctx; | |
70 | - | |
71 | /* | |
72 | * Here we try to be compatible with clients that want multipart/x-byteranges | |
73 | * instead of multipart/byteranges (also see above), as per HTTP/1.1. We | |
74 | @@ -131,28 +74,205 @@ | |
75 | } | |
76 | ||
77 | #define BYTERANGE_FMT "%" APR_OFF_T_FMT "-%" APR_OFF_T_FMT "/%" APR_OFF_T_FMT | |
78 | -#define PARTITION_ERR_FMT "apr_brigade_partition() failed " \ | |
79 | - "[%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT "]" | |
80 | ||
81 | +static apr_status_t copy_brigade_range(apr_bucket_brigade *bb, | |
82 | + apr_bucket_brigade *bbout, | |
83 | + apr_off_t start, | |
84 | + apr_off_t end) | |
85 | +{ | |
86 | + apr_bucket *first = NULL, *last = NULL, *out_first = NULL, *e; | |
87 | + apr_uint64_t pos = 0, off_first = 0, off_last = 0; | |
88 | + apr_status_t rv; | |
89 | + const char *s; | |
90 | + apr_size_t len; | |
91 | + apr_uint64_t start64, end64; | |
92 | + apr_off_t pofft = 0; | |
93 | + | |
94 | + /* | |
95 | + * Once we know that start and end are >= 0 convert everything to apr_uint64_t. | |
96 | + * See the comments in apr_brigade_partition why. | |
97 | + * In short apr_off_t (for values >= 0)and apr_size_t fit into apr_uint64_t. | |
98 | + */ | |
99 | + start64 = (apr_uint64_t)start; | |
100 | + end64 = (apr_uint64_t)end; | |
101 | + | |
102 | + if (start < 0 || end < 0 || start64 > end64) | |
103 | + return APR_EINVAL; | |
104 | + | |
105 | + for (e = APR_BRIGADE_FIRST(bb); | |
106 | + e != APR_BRIGADE_SENTINEL(bb); | |
107 | + e = APR_BUCKET_NEXT(e)) | |
108 | + { | |
109 | + apr_uint64_t elen64; | |
110 | + /* we know that no bucket has undefined length (-1) */ | |
111 | + AP_DEBUG_ASSERT(e->length != (apr_size_t)(-1)); | |
112 | + elen64 = (apr_uint64_t)e->length; | |
113 | + if (!first && (elen64 + pos > start64)) { | |
114 | + first = e; | |
115 | + off_first = pos; | |
116 | + } | |
117 | + if (elen64 + pos > end64) { | |
118 | + last = e; | |
119 | + off_last = pos; | |
120 | + break; | |
121 | + } | |
122 | + pos += elen64; | |
123 | + } | |
124 | + if (!first || !last) | |
125 | + return APR_EINVAL; | |
126 | + | |
127 | + e = first; | |
128 | + while (1) | |
129 | + { | |
130 | + apr_bucket *copy; | |
131 | + AP_DEBUG_ASSERT(e != APR_BRIGADE_SENTINEL(bb)); | |
132 | + rv = apr_bucket_copy(e, ©); | |
133 | + if (rv != APR_SUCCESS) { | |
134 | + apr_brigade_cleanup(bbout); | |
135 | + return rv; | |
136 | + } | |
137 | + | |
138 | + APR_BRIGADE_INSERT_TAIL(bbout, copy); | |
139 | + if (e == first) { | |
140 | + if (off_first != start64) { | |
141 | + rv = apr_bucket_split(copy, (apr_size_t)(start64 - off_first)); | |
142 | + if (rv == APR_ENOTIMPL) { | |
143 | + rv = apr_bucket_read(copy, &s, &len, APR_BLOCK_READ); | |
144 | + if (rv != APR_SUCCESS) { | |
145 | + apr_brigade_cleanup(bbout); | |
146 | + return rv; | |
147 | + } | |
148 | + /* | |
149 | + * The read above might have morphed copy in a bucket | |
150 | + * of shorter length. So read and delete until we reached | |
151 | + * the correct bucket for splitting. | |
152 | + */ | |
153 | + while (start64 - off_first > (apr_uint64_t)copy->length) { | |
154 | + apr_bucket *tmp; | |
155 | + int i = 0; | |
156 | + if (i++ >= 99999) | |
157 | + return APR_EINVAL; | |
158 | + | |
159 | + tmp = APR_BUCKET_NEXT(copy); | |
160 | + off_first += (apr_uint64_t)copy->length; | |
161 | + APR_BUCKET_REMOVE(copy); | |
162 | + apr_bucket_destroy(copy); | |
163 | + copy = tmp; | |
164 | + rv = apr_bucket_read(copy, &s, &len, APR_BLOCK_READ); | |
165 | + if (rv != APR_SUCCESS) { | |
166 | + apr_brigade_cleanup(bbout); | |
167 | + return rv; | |
168 | + } | |
169 | + } | |
170 | + if (start64 > off_first) { | |
171 | + rv = apr_bucket_split(copy, (apr_size_t)(start64 - off_first)); | |
172 | + if (rv != APR_SUCCESS) { | |
173 | + apr_brigade_cleanup(bbout); | |
174 | + return rv; | |
175 | + } | |
176 | + } | |
177 | + else { | |
178 | + copy = APR_BUCKET_PREV(copy); | |
179 | + } | |
180 | + } | |
181 | + else if (rv != APR_SUCCESS) { | |
182 | + apr_brigade_cleanup(bbout); | |
183 | + return rv; | |
184 | + } | |
185 | + out_first = APR_BUCKET_NEXT(copy); | |
186 | + APR_BUCKET_REMOVE(copy); | |
187 | + apr_bucket_destroy(copy); | |
188 | + } | |
189 | + else { | |
190 | + out_first = copy; | |
191 | + } | |
192 | + } | |
193 | + if (e == last) { | |
194 | + if (e == first) { | |
195 | + off_last += start64 - off_first; | |
196 | + copy = out_first; | |
197 | + } | |
198 | + if (end64 - off_last != (apr_uint64_t)e->length) { | |
199 | + rv = apr_bucket_split(copy, (apr_size_t)(end64 + 1 - off_last)); | |
200 | + if (rv == APR_ENOTIMPL) { | |
201 | + rv = apr_bucket_read(copy, &s, &len, APR_BLOCK_READ); | |
202 | + if (rv != APR_SUCCESS) { | |
203 | + apr_brigade_cleanup(bbout); | |
204 | + return rv; | |
205 | + } | |
206 | + /* | |
207 | + * The read above might have morphed copy in a bucket | |
208 | + * of shorter length. So read until we reached | |
209 | + * the correct bucket for splitting. | |
210 | + */ | |
211 | + while (end64 + 1 - off_last > (apr_uint64_t)copy->length) { | |
212 | + off_last += (apr_uint64_t)copy->length; | |
213 | + copy = APR_BUCKET_NEXT(copy); | |
214 | + rv = apr_bucket_read(copy, &s, &len, APR_BLOCK_READ); | |
215 | + if (rv != APR_SUCCESS) { | |
216 | + apr_brigade_cleanup(bbout); | |
217 | + return rv; | |
218 | + } | |
219 | + } | |
220 | + if (end64 < off_last + (apr_uint64_t)copy->length - 1) { | |
221 | + rv = apr_bucket_split(copy, end64 + 1 - off_last); | |
222 | + if (rv != APR_SUCCESS) { | |
223 | + apr_brigade_cleanup(bbout); | |
224 | + return rv; | |
225 | + } | |
226 | + } | |
227 | + } | |
228 | + else if (rv != APR_SUCCESS) { | |
229 | + apr_brigade_cleanup(bbout); | |
230 | + return rv; | |
231 | + } | |
232 | + copy = APR_BUCKET_NEXT(copy); | |
233 | + if (copy != APR_BRIGADE_SENTINEL(bbout)) { | |
234 | + APR_BUCKET_REMOVE(copy); | |
235 | + apr_bucket_destroy(copy); | |
236 | + } | |
237 | + } | |
238 | + break; | |
239 | + } | |
240 | + e = APR_BUCKET_NEXT(e); | |
241 | + } | |
242 | + | |
243 | + AP_DEBUG_ASSERT(APR_SUCCESS == apr_brigade_length(bbout, 1, &pofft)); | |
244 | + pos = (apr_uint64_t)pofft; | |
245 | + AP_DEBUG_ASSERT(pos == end64 - start64 + 1); | |
246 | + return APR_SUCCESS; | |
247 | +} | |
248 | + | |
249 | +typedef struct indexes_t { | |
250 | + apr_off_t start; | |
251 | + apr_off_t end; | |
252 | +} indexes_t; | |
253 | + | |
254 | AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f, | |
255 | apr_bucket_brigade *bb) | |
256 | { | |
257 | -#define MIN_LENGTH(len1, len2) ((len1 > len2) ? len2 : len1) | |
258 | request_rec *r = f->r; | |
259 | conn_rec *c = r->connection; | |
260 | - byterange_ctx *ctx; | |
261 | apr_bucket *e; | |
262 | apr_bucket_brigade *bsend; | |
263 | + apr_bucket_brigade *tmpbb; | |
264 | apr_off_t range_start; | |
265 | apr_off_t range_end; | |
266 | - char *current; | |
267 | apr_off_t clength = 0; | |
268 | apr_status_t rv; | |
269 | int found = 0; | |
270 | int num_ranges; | |
271 | + char *boundary = NULL; | |
272 | + char *bound_head = NULL; | |
273 | + apr_array_header_t *indexes; | |
274 | + indexes_t *idx; | |
275 | + int original_status; | |
276 | + int i; | |
277 | ||
278 | - /* Iterate through the brigade until reaching EOS or a bucket with | |
279 | - * unknown length. */ | |
280 | + /* | |
281 | + * Iterate through the brigade until reaching EOS or a bucket with | |
282 | + * unknown length. | |
283 | + */ | |
284 | for (e = APR_BRIGADE_FIRST(bb); | |
285 | (e != APR_BRIGADE_SENTINEL(bb) && !APR_BUCKET_IS_EOS(e) | |
286 | && e->length != (apr_size_t)-1); | |
287 | @@ -160,90 +280,80 @@ | |
288 | clength += e->length; | |
289 | } | |
290 | ||
291 | - /* Don't attempt to do byte range work if this brigade doesn't | |
292 | + /* | |
293 | + * Don't attempt to do byte range work if this brigade doesn't | |
294 | * contain an EOS, or if any of the buckets has an unknown length; | |
295 | * this avoids the cases where it is expensive to perform | |
296 | - * byteranging (i.e. may require arbitrary amounts of memory). */ | |
297 | + * byteranging (i.e. may require arbitrary amounts of memory). | |
298 | + */ | |
299 | if (!APR_BUCKET_IS_EOS(e) || clength <= 0) { | |
300 | ap_remove_output_filter(f); | |
301 | return ap_pass_brigade(f->next, bb); | |
302 | } | |
303 | ||
304 | - num_ranges = ap_set_byterange(r); | |
305 | + original_status = r->status; | |
306 | + num_ranges = ap_set_byterange(r, clength, &indexes); | |
307 | ||
308 | /* We have nothing to do, get out of the way. */ | |
309 | if (num_ranges == 0) { | |
310 | + r->status = original_status; | |
311 | ap_remove_output_filter(f); | |
312 | return ap_pass_brigade(f->next, bb); | |
313 | } | |
314 | ||
315 | - ctx = apr_pcalloc(r->pool, sizeof(*ctx)); | |
316 | - ctx->num_ranges = num_ranges; | |
317 | - /* create a brigade in case we never call ap_save_brigade() */ | |
318 | - ctx->bb = apr_brigade_create(r->pool, c->bucket_alloc); | |
319 | - | |
320 | - if (ctx->num_ranges > 1) { | |
321 | + if (num_ranges > 1) { | |
322 | /* Is ap_make_content_type required here? */ | |
323 | const char *orig_ct = ap_make_content_type(r, r->content_type); | |
324 | - ctx->boundary = apr_psprintf(r->pool, "%" APR_UINT64_T_HEX_FMT "%lx", | |
325 | - (apr_uint64_t)r->request_time, (long) getpid()); | |
326 | + boundary = apr_psprintf(r->pool, "%" APR_UINT64_T_HEX_FMT "%lx", | |
327 | + (apr_uint64_t)r->request_time, (long) getpid()); | |
328 | ||
329 | ap_set_content_type(r, apr_pstrcat(r->pool, "multipart", | |
330 | use_range_x(r) ? "/x-" : "/", | |
331 | "byteranges; boundary=", | |
332 | - ctx->boundary, NULL)); | |
333 | + boundary, NULL)); | |
334 | ||
335 | if (strcasecmp(orig_ct, NO_CONTENT_TYPE)) { | |
336 | - ctx->bound_head = apr_pstrcat(r->pool, | |
337 | - CRLF "--", ctx->boundary, | |
338 | - CRLF "Content-type: ", | |
339 | - orig_ct, | |
340 | - CRLF "Content-range: bytes ", | |
341 | - NULL); | |
342 | + bound_head = apr_pstrcat(r->pool, | |
343 | + CRLF "--", boundary, | |
344 | + CRLF "Content-type: ", | |
345 | + orig_ct, | |
346 | + CRLF "Content-range: bytes ", | |
347 | + NULL); | |
348 | } | |
349 | else { | |
350 | /* if we have no type for the content, do our best */ | |
351 | - ctx->bound_head = apr_pstrcat(r->pool, | |
352 | - CRLF "--", ctx->boundary, | |
353 | - CRLF "Content-range: bytes ", | |
354 | - NULL); | |
355 | + bound_head = apr_pstrcat(r->pool, | |
356 | + CRLF "--", boundary, | |
357 | + CRLF "Content-range: bytes ", | |
358 | + NULL); | |
359 | } | |
360 | - ap_xlate_proto_to_ascii(ctx->bound_head, strlen(ctx->bound_head)); | |
361 | + ap_xlate_proto_to_ascii(bound_head, strlen(bound_head)); | |
362 | } | |
363 | ||
364 | /* this brigade holds what we will be sending */ | |
365 | bsend = apr_brigade_create(r->pool, c->bucket_alloc); | |
366 | + tmpbb = apr_brigade_create(r->pool, c->bucket_alloc); | |
367 | ||
368 | - while ((current = ap_getword(r->pool, &r->range, ',')) | |
369 | - && (rv = parse_byterange(current, clength, &range_start, | |
370 | - &range_end))) { | |
371 | - apr_bucket *e2; | |
372 | - apr_bucket *ec; | |
373 | + idx = (indexes_t *)indexes->elts; | |
374 | + for (i = 0; i < indexes->nelts; i++, idx++) { | |
375 | + range_start = idx->start; | |
376 | + range_end = idx->end; | |
377 | ||
378 | - if (rv == -1) { | |
379 | - continue; | |
380 | - } | |
381 | - | |
382 | - /* These calls to apr_brigage_partition should only fail in | |
383 | - * pathological cases, e.g. a file being truncated whilst | |
384 | - * being served. */ | |
385 | - if ((rv = apr_brigade_partition(bb, range_start, &ec)) != APR_SUCCESS) { | |
386 | + rv = copy_brigade_range(bb, tmpbb, range_start, range_end); | |
387 | + if (rv != APR_SUCCESS ) { | |
388 | ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, | |
389 | - PARTITION_ERR_FMT, range_start, clength); | |
390 | + "copy_brigade_range() failed [%" APR_OFF_T_FMT | |
391 | + "-%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT "]", | |
392 | + range_start, range_end, clength); | |
393 | continue; | |
394 | } | |
395 | - if ((rv = apr_brigade_partition(bb, range_end+1, &e2)) != APR_SUCCESS) { | |
396 | - ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, | |
397 | - PARTITION_ERR_FMT, range_end+1, clength); | |
398 | - continue; | |
399 | - } | |
400 | - | |
401 | found = 1; | |
402 | ||
403 | - /* For single range requests, we must produce Content-Range header. | |
404 | + /* | |
405 | + * For single range requests, we must produce Content-Range header. | |
406 | * Otherwise, we need to produce the multipart boundaries. | |
407 | */ | |
408 | - if (ctx->num_ranges == 1) { | |
409 | + if (num_ranges == 1) { | |
410 | apr_table_setn(r->headers_out, "Content-Range", | |
411 | apr_psprintf(r->pool, "bytes " BYTERANGE_FMT, | |
412 | range_start, range_end, clength)); | |
413 | @@ -251,7 +361,7 @@ | |
414 | else { | |
415 | char *ts; | |
416 | ||
417 | - e = apr_bucket_pool_create(ctx->bound_head, strlen(ctx->bound_head), | |
418 | + e = apr_bucket_pool_create(bound_head, strlen(bound_head), | |
419 | r->pool, c->bucket_alloc); | |
420 | APR_BRIGADE_INSERT_TAIL(bsend, e); | |
421 | ||
422 | @@ -263,23 +373,19 @@ | |
423 | APR_BRIGADE_INSERT_TAIL(bsend, e); | |
424 | } | |
425 | ||
426 | - do { | |
427 | - apr_bucket *foo; | |
428 | - const char *str; | |
429 | - apr_size_t len; | |
430 | - | |
431 | - if (apr_bucket_copy(ec, &foo) != APR_SUCCESS) { | |
432 | - /* As above; this should not fail since the bucket has | |
433 | - * a known length, but just to be sure, this takes | |
434 | - * care of uncopyable buckets that do somehow manage | |
435 | - * to slip through. */ | |
436 | - /* XXX: check for failure? */ | |
437 | - apr_bucket_read(ec, &str, &len, APR_BLOCK_READ); | |
438 | - apr_bucket_copy(ec, &foo); | |
439 | - } | |
440 | - APR_BRIGADE_INSERT_TAIL(bsend, foo); | |
441 | - ec = APR_BUCKET_NEXT(ec); | |
442 | - } while (ec != e2); | |
443 | + APR_BRIGADE_CONCAT(bsend, tmpbb); | |
444 | + if (i && i % 32 == 0) { | |
445 | + /* | |
446 | + * Every now and then, pass what we have down the filter chain. | |
447 | + * In this case, the content-length filter cannot calculate and | |
448 | + * set the content length and we must remove any Content-Length | |
449 | + * header already present. | |
450 | + */ | |
451 | + apr_table_unset(r->headers_out, "Content-Length"); | |
452 | + if ((rv = ap_pass_brigade(f->next, bsend)) != APR_SUCCESS) | |
453 | + return rv; | |
454 | + apr_brigade_cleanup(bsend); | |
455 | + } | |
456 | } | |
457 | ||
458 | if (found == 0) { | |
459 | @@ -294,11 +400,11 @@ | |
460 | return ap_pass_brigade(f->next, bsend); | |
461 | } | |
462 | ||
463 | - if (ctx->num_ranges > 1) { | |
464 | + if (num_ranges > 1) { | |
465 | char *end; | |
466 | ||
467 | /* add the final boundary */ | |
468 | - end = apr_pstrcat(r->pool, CRLF "--", ctx->boundary, "--" CRLF, NULL); | |
469 | + end = apr_pstrcat(r->pool, CRLF "--", boundary, "--" CRLF, NULL); | |
470 | ap_xlate_proto_to_ascii(end, strlen(end)); | |
471 | e = apr_bucket_pool_create(end, strlen(end), r->pool, c->bucket_alloc); | |
472 | APR_BRIGADE_INSERT_TAIL(bsend, e); | |
473 | @@ -309,24 +415,32 @@ | |
474 | ||
475 | /* we're done with the original content - all of our data is in bsend. */ | |
476 | apr_brigade_cleanup(bb); | |
477 | + apr_brigade_destroy(tmpbb); | |
478 | ||
479 | /* send our multipart output */ | |
480 | return ap_pass_brigade(f->next, bsend); | |
481 | } | |
482 | ||
483 | -static int ap_set_byterange(request_rec *r) | |
484 | +static int ap_set_byterange(request_rec *r, apr_off_t clength, | |
485 | + apr_array_header_t **indexes) | |
486 | { | |
487 | const char *range; | |
488 | const char *if_range; | |
489 | const char *match; | |
490 | const char *ct; | |
491 | - int num_ranges; | |
492 | + char *cur; | |
493 | + int num_ranges = 0; | |
494 | + apr_off_t sum_lengths = 0; | |
495 | + indexes_t *idx; | |
496 | + int ranges = 1; | |
497 | + const char *it; | |
498 | ||
499 | if (r->assbackwards) { | |
500 | return 0; | |
501 | } | |
502 | ||
503 | - /* Check for Range request-header (HTTP/1.1) or Request-Range for | |
504 | + /* | |
505 | + * Check for Range request-header (HTTP/1.1) or Request-Range for | |
506 | * backwards-compatibility with second-draft Luotonen/Franks | |
507 | * byte-ranges (e.g. Netscape Navigator 2-3). | |
508 | * | |
509 | @@ -356,7 +470,8 @@ | |
510 | return 0; | |
511 | } | |
512 | ||
513 | - /* Check the If-Range header for Etag or Date. | |
514 | + /* | |
515 | + * Check the If-Range header for Etag or Date. | |
516 | * Note that this check will return false (as required) if either | |
517 | * of the two etags are weak. | |
518 | */ | |
519 | @@ -373,17 +488,77 @@ | |
520 | } | |
521 | } | |
522 | ||
523 | - if (!ap_strchr_c(range, ',')) { | |
524 | - /* a single range */ | |
525 | - num_ranges = 1; | |
526 | + range += 6; | |
527 | + it = range; | |
528 | + while (*it) { | |
529 | + if (*it++ == ',') { | |
530 | + ranges++; | |
531 | + } | |
532 | } | |
533 | - else { | |
534 | - /* a multiple range */ | |
535 | - num_ranges = 2; | |
536 | + it = range; | |
537 | + *indexes = apr_array_make(r->pool, ranges, sizeof(indexes_t)); | |
538 | + while ((cur = ap_getword(r->pool, &range, ','))) { | |
539 | + char *dash; | |
540 | + char *errp; | |
541 | + apr_off_t number, start, end; | |
542 | + | |
543 | + if (!(dash = strchr(cur, '-'))) { | |
544 | + break; | |
545 | + } | |
546 | + | |
547 | + if (dash == range) { | |
548 | + /* In the form "-5" */ | |
549 | + if (apr_strtoff(&number, dash+1, &errp, 10) || *errp) { | |
550 | + break; | |
551 | + } | |
552 | + start = clength - number; | |
553 | + end = clength - 1; | |
554 | + } | |
555 | + else { | |
556 | + *dash++ = '\0'; | |
557 | + if (apr_strtoff(&number, cur, &errp, 10) || *errp) { | |
558 | + break; | |
559 | + } | |
560 | + start = number; | |
561 | + if (*dash) { | |
562 | + if (apr_strtoff(&number, dash, &errp, 10) || *errp) { | |
563 | + break; | |
564 | + } | |
565 | + end = number; | |
566 | + } | |
567 | + else { /* "5-" */ | |
568 | + end = clength - 1; | |
569 | + } | |
570 | + } | |
571 | + | |
572 | + if (start < 0) { | |
573 | + start = 0; | |
574 | + } | |
575 | + if (end >= clength) { | |
576 | + end = clength - 1; | |
577 | + } | |
578 | + | |
579 | + if (start > end) { | |
580 | + /* ignore? count? */ | |
581 | + break; | |
582 | + } | |
583 | + | |
584 | + idx = (indexes_t *)apr_array_push(*indexes); | |
585 | + idx->start = start; | |
586 | + idx->end = end; | |
587 | + sum_lengths += end - start + 1; | |
588 | + /* new set again */ | |
589 | + num_ranges++; | |
590 | } | |
591 | ||
592 | + if (sum_lengths >= clength) { | |
593 | + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, | |
594 | + "Sum of ranges not smaller than file, ignoring."); | |
595 | + return 0; | |
596 | + } | |
597 | + | |
598 | r->status = HTTP_PARTIAL_CONTENT; | |
599 | - r->range = range + 6; | |
600 | + r->range = it; | |
601 | ||
602 | return num_ranges; | |
603 | } |