Apache HTTPD
byterange_filter.c
Go to the documentation of this file.
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * byterange_filter.c --- HTTP byterange filter and friends.
19 */
20
21#include "apr.h"
22
23#include "apr_strings.h"
24#include "apr_buckets.h"
25#include "apr_lib.h"
26#include "apr_signal.h"
27
28#define APR_WANT_STDIO /* for sscanf */
29#define APR_WANT_STRFUNC
30#define APR_WANT_MEMFUNC
31#include "apr_want.h"
32
33#include "util_filter.h"
34#include "ap_config.h"
35#include "httpd.h"
36#include "http_config.h"
37#include "http_core.h"
38#include "http_protocol.h"
39#include "http_main.h"
40#include "http_request.h"
41#include "http_vhost.h"
42#include "http_log.h" /* For errors detected in basic auth common
43 * support code... */
44#include "apr_date.h" /* For apr_date_parse_http and APR_DATE_BAD */
45#include "util_charset.h"
46#include "util_ebcdic.h"
47#include "util_time.h"
48
49#include "mod_core.h"
50
51#if APR_HAVE_STDARG_H
52#include <stdarg.h>
53#endif
54#if APR_HAVE_UNISTD_H
55#include <unistd.h>
56#endif
57
58#ifndef AP_DEFAULT_MAX_RANGES
59#define AP_DEFAULT_MAX_RANGES 200
60#endif
61#ifndef AP_DEFAULT_MAX_OVERLAPS
62#define AP_DEFAULT_MAX_OVERLAPS 20
63#endif
64#ifndef AP_DEFAULT_MAX_REVERSALS
65#define AP_DEFAULT_MAX_REVERSALS 20
66#endif
67
68#define MAX_PREALLOC_RANGES 100
69
71
76
77/*
78 * Returns: number of ranges (merged) or -1 for no-good
79 */
82 int *overlaps, int *reversals)
83{
84 const char *range;
85 const char *ct;
86 char *cur;
87 apr_array_header_t *merged;
88 int num_ranges = 0, unsatisfiable = 0;
89 apr_off_t ostart = 0, oend = 0, sum_lengths = 0;
90 int in_merge = 0;
92 int ranges = 1;
93 int i;
94 const char *it;
95
96 *overlaps = 0;
97 *reversals = 0;
98
99 if (r->assbackwards) {
100 return 0;
101 }
102
103 /*
104 * Check for Range request-header (HTTP/1.1) or Request-Range for
105 * backwards-compatibility with second-draft Luotonen/Franks
106 * byte-ranges (e.g. Netscape Navigator 2-3).
107 *
108 * We support this form, with Request-Range, and (farther down) we
109 * send multipart/x-byteranges instead of multipart/byteranges for
110 * Request-Range based requests to work around a bug in Netscape
111 * Navigator 2-3 and MSIE 3.
112 */
113
114 if (!(range = apr_table_get(r->headers_in, "Range"))) {
115 range = apr_table_get(r->headers_in, "Request-Range");
116 }
117
118 if (!range || strncasecmp(range, "bytes=", 6) || r->status != HTTP_OK) {
119 return 0;
120 }
121
122 /* is content already a single range? */
123 if (apr_table_get(r->headers_out, "Content-Range")) {
124 return 0;
125 }
126
127 /* is content already a multiple range? */
128 if ((ct = apr_table_get(r->headers_out, "Content-Type"))
129 && (!strncasecmp(ct, "multipart/byteranges", 20)
130 || !strncasecmp(ct, "multipart/x-byteranges", 22))) {
131 return 0;
132 }
133
134 /*
135 * Check the If-Range header for Etag or Date.
136 */
138 return 0;
139 }
140
141 range += 6;
142 it = range;
143 while (*it) {
144 if (*it++ == ',') {
145 ranges++;
146 }
147 }
148 it = range;
151 }
153 while ((cur = ap_getword(r->pool, &range, ','))) {
154 char *dash;
155 apr_off_t number, start, end;
156
157 if (!*cur)
158 break;
159
160 /*
161 * Per RFC 2616 14.35.1: If there is at least one syntactically invalid
162 * byte-range-spec, we must ignore the whole header.
163 */
164
165 if (!(dash = strchr(cur, '-'))) {
166 return 0;
167 }
168
169 if (dash == cur) {
170 /* In the form "-5" */
171 if (!ap_parse_strict_length(&number, dash+1)) {
172 return 0;
173 }
174 if (number < 1) {
175 return 0;
176 }
177 start = clength - number;
178 end = clength - 1;
179 }
180 else {
181 *dash++ = '\0';
182 if (!ap_parse_strict_length(&number, cur)) {
183 return 0;
184 }
185 start = number;
186 if (*dash) {
187 if (!ap_parse_strict_length(&number, dash)) {
188 return 0;
189 }
190 end = number;
191 if (start > end) {
192 return 0;
193 }
194 }
195 else { /* "5-" */
196 end = clength - 1;
197 /*
198 * special case: 0-
199 * ignore all other ranges provided
200 * return as a single range: 0-
201 */
202 if (start == 0) {
203 num_ranges = 0;
204 sum_lengths = 0;
205 in_merge = 1;
206 oend = end;
207 ostart = start;
209 break;
210 }
211 }
212 }
213
214 if (start < 0) {
215 start = 0;
216 }
217 if (start >= clength) {
218 unsatisfiable = 1;
219 continue;
220 }
221 if (end >= clength) {
222 end = clength - 1;
223 }
224
225 if (!in_merge) {
226 /* new set */
227 ostart = start;
228 oend = end;
229 in_merge = 1;
230 continue;
231 }
232 in_merge = 0;
233
234 if (start >= ostart && end <= oend) {
235 in_merge = 1;
236 }
237
239 ostart = start;
240 ++*reversals;
241 in_merge = 1;
242 }
243 if (end >= oend && start <= oend+1 ) {
244 oend = end;
245 in_merge = 1;
246 }
247
248 if (in_merge) {
249 ++*overlaps;
250 continue;
251 } else {
253 idx->start = ostart;
254 idx->end = oend;
255 sum_lengths += oend - ostart + 1;
256 /* new set again */
257 in_merge = 1;
258 ostart = start;
259 oend = end;
260 num_ranges++;
261 }
262 }
263
264 if (in_merge) {
266 idx->start = ostart;
267 idx->end = oend;
268 sum_lengths += oend - ostart + 1;
269 num_ranges++;
270 }
271 else if (num_ranges == 0 && unsatisfiable) {
272 /* If all ranges are unsatisfiable, we should return 416 */
273 return -1;
274 }
275 if (sum_lengths > clength) {
277 "Sum of ranges larger than file, ignoring.");
278 return 0;
279 }
280
281 /*
282 * create the merged table now, now that we know we need it
283 */
284 merged = apr_array_make(r->pool, num_ranges, sizeof(char *));
285 idx = (indexes_t *)(*indexes)->elts;
286 for (i = 0; i < (*indexes)->nelts; i++, idx++) {
287 char **new = (char **)apr_array_push(merged);
289 idx->start, idx->end);
290 }
291
293 r->range = apr_array_pstrcat(r->pool, merged, ',');
295 "Range: %s | %s (%d : %d : %"APR_OFF_T_FMT")",
296 it, r->range, *overlaps, *reversals, clength);
297
298 return num_ranges;
299}
300
301/*
302 * Here we try to be compatible with clients that want multipart/x-byteranges
303 * instead of multipart/byteranges (also see above), as per HTTP/1.1. We
304 * look for the Request-Range header (e.g. Netscape 2 and 3) as an indication
305 * that the browser supports an older protocol. We also check User-Agent
306 * for Microsoft Internet Explorer 3, which needs this as well.
307 */
309{
310 const char *ua;
311 return (apr_table_get(r->headers_in, "Request-Range")
312 || ((ua = apr_table_get(r->headers_in, "User-Agent"))
313 && ap_strstr_c(ua, "MSIE 3")));
314}
315
316#define BYTERANGE_FMT "%" APR_OFF_T_FMT "-%" APR_OFF_T_FMT "/%" APR_OFF_T_FMT
317
322{
324 apr_uint64_t pos = 0, off_first = 0, off_last = 0;
325 apr_status_t rv;
327 apr_off_t pofft = 0;
328
329 /*
330 * Once we know that start and end are >= 0 convert everything to apr_uint64_t.
331 * See the comments in apr_brigade_partition why.
332 * In short apr_off_t (for values >= 0)and apr_size_t fit into apr_uint64_t.
333 */
336
338 return APR_EINVAL;
339
340 for (e = APR_BRIGADE_FIRST(bb);
341 e != APR_BRIGADE_SENTINEL(bb);
343 {
345 /* we know that no bucket has undefined length (-1) */
348 if (!first && (elen64 + pos > start64)) {
349 first = e;
350 off_first = pos;
351 }
352 if (elen64 + pos > end64) {
353 last = e;
354 off_last = pos;
355 break;
356 }
357 pos += elen64;
358 }
359 if (!first || !last)
360 return APR_EINVAL;
361
362 e = first;
363 while (1)
364 {
365 apr_bucket *copy;
367 rv = apr_bucket_copy(e, &copy);
368 if (rv != APR_SUCCESS) {
370 return rv;
371 }
372
374 if (e == first) {
375 if (off_first != start64) {
377 if (rv != APR_SUCCESS) {
379 return rv;
380 }
382 apr_bucket_delete(copy);
383 }
384 else {
385 out_first = copy;
386 }
387 }
388 if (e == last) {
389 if (e == first) {
391 copy = out_first;
392 }
393 if (end64 - off_last != (apr_uint64_t)e->length) {
394 rv = apr_bucket_split(copy, (apr_size_t)(end64 + 1 - off_last));
395 if (rv != APR_SUCCESS) {
397 return rv;
398 }
399 copy = APR_BUCKET_NEXT(copy);
400 if (copy != APR_BRIGADE_SENTINEL(bbout)) {
401 apr_bucket_delete(copy);
402 }
403 }
404 break;
405 }
407 }
408
410 pos = (apr_uint64_t)pofft;
411 AP_DEBUG_ASSERT(pos == end64 - start64 + 1);
412 return APR_SUCCESS;
413}
414
416{
417 apr_bucket *e;
418 conn_rec *c = f->r->connection;
420 f->r->status = HTTP_OK;
422 f->r->pool, c->bucket_alloc);
424 e = apr_bucket_eos_create(c->bucket_alloc);
426 return ap_pass_brigade(f->next, tmpbb);
427}
428
431{
432 request_rec *r = f->r;
434 apr_bucket *e;
436 apr_bucket_brigade *tmpbb;
439 apr_off_t clength = 0;
440 apr_status_t rv;
441 int found = 0;
442 int num_ranges;
443 char *bound_head = NULL;
445 indexes_t *idx;
446 int i;
447 int original_status;
448 int max_ranges, max_overlaps, max_reversals;
449 int overlaps = 0, reversals = 0;
451
452 max_ranges = ( (core_conf->max_ranges >= 0 || core_conf->max_ranges == AP_MAXRANGES_UNLIMITED)
453 ? core_conf->max_ranges
455 max_overlaps = ( (core_conf->max_overlaps >= 0 || core_conf->max_overlaps == AP_MAXRANGES_UNLIMITED)
456 ? core_conf->max_overlaps
458 max_reversals = ( (core_conf->max_reversals >= 0 || core_conf->max_reversals == AP_MAXRANGES_UNLIMITED)
459 ? core_conf->max_reversals
461 /*
462 * Iterate through the brigade until reaching EOS or a bucket with
463 * unknown length.
464 */
465 for (e = APR_BRIGADE_FIRST(bb);
467 && e->length != (apr_size_t)-1);
468 e = APR_BUCKET_NEXT(e)) {
469 clength += e->length;
470 }
471
472 /*
473 * Don't attempt to do byte range work if this brigade doesn't
474 * contain an EOS, or if any of the buckets has an unknown length;
475 * this avoids the cases where it is expensive to perform
476 * byteranging (i.e. may require arbitrary amounts of memory).
477 */
478 if (!APR_BUCKET_IS_EOS(e) || clength <= 0) {
480 return ap_pass_brigade(f->next, bb);
481 }
482
485
486 /* No Ranges or we hit a limit? We have nothing to do, get out of the way. */
487 if (num_ranges == 0 ||
488 (max_ranges >= 0 && num_ranges > max_ranges) ||
489 (max_overlaps >= 0 && overlaps > max_overlaps) ||
490 (max_reversals >= 0 && reversals > max_reversals)) {
493 return ap_pass_brigade(f->next, bb);
494 }
495
496 /* this brigade holds what we will be sending */
497 bsend = apr_brigade_create(r->pool, c->bucket_alloc);
498
499 if (num_ranges < 0)
500 return send_416(f, bsend);
501
502 if (num_ranges > 1) {
503 /* Is ap_make_content_type required here? */
505
506 ap_set_content_type(r, apr_pstrcat(r->pool, "multipart",
507 use_range_x(r) ? "/x-" : "/",
508 "byteranges; boundary=",
510
511 if (orig_ct) {
514 CRLF "Content-type: ",
515 orig_ct,
516 CRLF "Content-range: bytes ",
517 NULL);
518 }
519 else {
520 /* if we have no type for the content, do our best */
523 CRLF "Content-range: bytes ",
524 NULL);
525 }
527 }
528
529 tmpbb = apr_brigade_create(r->pool, c->bucket_alloc);
530
531 idx = (indexes_t *)indexes->elts;
532 for (i = 0; i < indexes->nelts; i++, idx++) {
533 range_start = idx->start;
534 range_end = idx->end;
535
536 rv = copy_brigade_range(bb, tmpbb, range_start, range_end);
537 if (rv != APR_SUCCESS ) {
539 "copy_brigade_range() failed [%" APR_OFF_T_FMT
540 "-%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT "]",
541 range_start, range_end, clength);
542 continue;
543 }
544 found = 1;
545
546 /*
547 * For single range requests, we must produce Content-Range header.
548 * Otherwise, we need to produce the multipart boundaries.
549 */
550 if (num_ranges == 1) {
551 apr_table_setn(r->headers_out, "Content-Range",
552 apr_psprintf(r->pool, "bytes " BYTERANGE_FMT,
553 range_start, range_end, clength));
554 }
555 else {
556 char *ts;
557
559 r->pool, c->bucket_alloc);
561
563 range_start, range_end, clength);
564 ap_xlate_proto_to_ascii(ts, strlen(ts));
565 e = apr_bucket_pool_create(ts, strlen(ts), r->pool,
566 c->bucket_alloc);
568 }
569
571 if (i && !(i & 0x1F)) {
572 /*
573 * Every now and then, pass what we have down the filter chain.
574 * In this case, the content-length filter cannot calculate and
575 * set the content length and we must remove any Content-Length
576 * header already present.
577 */
578 apr_table_unset(r->headers_out, "Content-Length");
579 if ((rv = ap_pass_brigade(f->next, bsend)) != APR_SUCCESS)
580 return rv;
582 }
583 }
584
585 if (found == 0) {
586 /* bsend is assumed to be empty if we get here. */
587 return send_416(f, bsend);
588 }
589
590 if (num_ranges > 1) {
591 char *end;
592
593 /* add the final boundary */
595 NULL);
597 e = apr_bucket_pool_create(end, strlen(end), r->pool, c->bucket_alloc);
599 }
600
601 e = apr_bucket_eos_create(c->bucket_alloc);
603
604 /* we're done with the original content - all of our data is in bsend. */
606 apr_brigade_destroy(tmpbb);
607
608 /* send our multipart output */
609 return ap_pass_brigade(f->next, bsend);
610}
Symbol export macros and hook functions.
APR-UTIL Buckets/Bucket Brigades.
APR-UTIL date routines.
APR general purpose library routines.
APR Signal Handling.
APR Strings library.
APR Standard Headers Support.
static apr_status_t send_416(ap_filter_t *f, apr_bucket_brigade *tmpbb)
#define MAX_PREALLOC_RANGES
#define BYTERANGE_FMT
static int ap_set_byterange(request_rec *r, apr_off_t clength, apr_array_header_t **indexes, int *overlaps, int *reversals)
#define AP_DEFAULT_MAX_OVERLAPS
static apr_status_t copy_brigade_range(apr_bucket_brigade *bb, apr_bucket_brigade *bbout, apr_off_t start, apr_off_t end)
#define AP_DEFAULT_MAX_RANGES
static int use_range_x(request_rec *r)
#define AP_DEFAULT_MAX_REVERSALS
return found
Definition core.c:2840
#define APLOG_USE_MODULE(foo)
request_rec * r
#define AP_CORE_DECLARE_NONSTD
Definition httpd.h:390
#define ap_xlate_proto_to_ascii(x, y)
Definition util_ebcdic.h:80
apr_status_t ap_pass_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket)
void ap_remove_output_filter(ap_filter_t *f)
#define ap_get_core_module_config(v)
Definition http_core.h:383
#define APLOGNO(n)
Definition http_log.h:117
#define ap_log_rerror
Definition http_log.h:454
#define APLOG_ERR
Definition http_log.h:67
#define APLOG_MARK
Definition http_log.h:283
#define APLOG_TRACE1
Definition http_log.h:72
#define APLOG_DEBUG
Definition http_log.h:71
ap_condition_e ap_condition_if_range(request_rec *r, apr_table_t *headers)
const char * ap_make_content_type(request_rec *r, const char *type)
Definition protocol.c:110
void ap_set_content_type(request_rec *r, const char *ct)
apr_status_t ap_byterange_filter(ap_filter_t *f, apr_bucket_brigade *bb)
apr_bucket * ap_bucket_error_create(int error, const char *buf, apr_pool_t *p, apr_bucket_alloc_t *list)
@ AP_CONDITION_NOMATCH
#define CRLF
Definition httpd.h:724
#define APR_EINVAL
Definition apr_errno.h:711
apr_file_t * f
#define APR_BRIGADE_INSERT_TAIL(b, e)
#define apr_bucket_split(e, point)
apr_file_t apr_off_t start
#define APR_BUCKET_NEXT(e)
apr_bucket * e
#define APR_BRIGADE_CONCAT(a, b)
#define APR_BRIGADE_SENTINEL(b)
#define apr_bucket_delete(e)
#define APR_BUCKET_IS_EOS(e)
#define apr_bucket_copy(e, c)
#define APR_BRIGADE_FIRST(b)
#define HTTP_OK
Definition httpd.h:490
#define HTTP_RANGE_NOT_SATISFIABLE
Definition httpd.h:524
#define HTTP_PARTIAL_CONTENT
Definition httpd.h:496
const char * ap_multipart_boundary
Definition http_core.c:44
char * ap_getword(apr_pool_t *p, const char **line, char stop)
Definition util.c:723
#define ap_strstr_c(s, c)
Definition httpd.h:2361
#define AP_DEBUG_ASSERT(exp)
Definition httpd.h:2283
int ap_parse_strict_length(apr_off_t *len, const char *str)
Definition util.c:2683
apr_size_t size
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
int strncasecmp(const char *a, const char *b, size_t n)
apr_vformatter_buff_t * c
Definition apr_lib.h:175
const char char ** end
const char char ** last
const apr_array_header_t * first
Definition apr_tables.h:207
Apache Configuration.
CORE HTTP Daemon.
#define AP_MAXRANGES_UNLIMITED
Definition http_core.h:652
Apache Logging library.
Command line options.
HTTP protocol handling.
Apache Request library.
Virtual Host package.
HTTP Daemon routines.
mod_core private header file
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
The representation of a filter chain.
apr_size_t length
apr_pool_t * pool
Structure to store things which are per connection.
Definition httpd.h:1152
Per-directory configuration.
Definition http_core.h:527
apr_off_t start
apr_off_t end
A structure that represents the current request.
Definition httpd.h:845
int status
Definition httpd.h:891
const char * content_type
Definition httpd.h:992
int assbackwards
Definition httpd.h:868
apr_pool_t * pool
Definition httpd.h:847
conn_rec * connection
Definition httpd.h:849
apr_table_t * headers_in
Definition httpd.h:976
const char * range
Definition httpd.h:938
struct ap_conf_vector_t * per_dir_config
Definition httpd.h:1047
apr_table_t * headers_out
Definition httpd.h:978
charset conversion
Utilities for EBCDIC conversion.
Apache filter library.
Apache date-time handling functions.