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