]> granicus.if.org Git - apache/blobdiff - modules/http/byterange_filter.c
Turn some APR_BUCKET_REMOVE(e)+apr_bucket_destroy(e) into the equivalent apr_bucket_d...
[apache] / modules / http / byterange_filter.c
index 184b8de2f7c54346380f8dc299080e9b884d3d49..cc11140ada6d4947c88628b03d2a48b05c72f10c 100644 (file)
 #ifndef AP_DEFAULT_MAX_RANGES
 #define AP_DEFAULT_MAX_RANGES 200
 #endif
+#ifndef AP_DEFAULT_MAX_OVERLAPS
+#define AP_DEFAULT_MAX_OVERLAPS 20
+#endif
+#ifndef AP_DEFAULT_MAX_REVERSALS
+#define AP_DEFAULT_MAX_REVERSALS 20
+#endif
 
 #define MAX_PREALLOC_RANGES 100
 
@@ -76,16 +82,15 @@ static int ap_set_byterange(request_rec *r, apr_off_t clength,
                             int *overlaps, int *reversals)
 {
     const char *range;
-    const char *if_range;
-    const char *match;
     const char *ct;
-    char *cur, **new;
+    char *cur;
     apr_array_header_t *merged;
     int num_ranges = 0, unsatisfiable = 0;
     apr_off_t ostart = 0, oend = 0, sum_lengths = 0;
     int in_merge = 0;
     indexes_t *idx;
     int ranges = 1;
+    int i;
     const char *it;
 
     *overlaps = 0;
@@ -94,56 +99,30 @@ static int ap_set_byterange(request_rec *r, apr_off_t clength,
     if (r->assbackwards) {
         return 0;
     }
-    
-    /*
-     * Check for Range request-header (HTTP/1.1) or Request-Range for
-     * backwards-compatibility with second-draft Luotonen/Franks
-     * byte-ranges (e.g. Netscape Navigator 2-3).
-     *
-     * We support this form, with Request-Range, and (farther down) we
-     * send multipart/x-byteranges instead of multipart/byteranges for
-     * Request-Range based requests to work around a bug in Netscape
-     * Navigator 2-3 and MSIE 3.
-     */
-    
-    if (!(range = apr_table_get(r->headers_in, "Range"))) {
-        range = apr_table_get(r->headers_in, "Request-Range");
-    }
-    
+
+    range = apr_table_get(r->headers_in, "Range");
     if (!range || strncasecmp(range, "bytes=", 6) || r->status != HTTP_OK) {
         return 0;
     }
-    
+
     /* is content already a single range? */
     if (apr_table_get(r->headers_out, "Content-Range")) {
         return 0;
     }
-    
+
     /* is content already a multiple range? */
     if ((ct = apr_table_get(r->headers_out, "Content-Type"))
-        && (!strncasecmp(ct, "multipart/byteranges", 20)
-            || !strncasecmp(ct, "multipart/x-byteranges", 22))) {
+        && strncasecmp(ct, "multipart/byteranges", 20) == 0) {
             return 0;
-        }
-    
+    }
+
     /*
      * Check the If-Range header for Etag or Date.
-     * Note that this check will return false (as required) if either
-     * of the two etags are weak.
      */
-    if ((if_range = apr_table_get(r->headers_in, "If-Range"))) {
-        if (if_range[0] == '"') {
-            if (!(match = apr_table_get(r->headers_out, "Etag"))
-                || (strcmp(if_range, match) != 0)) {
-                return 0;
-            }
-        }
-        else if (!(match = apr_table_get(r->headers_out, "Last-Modified"))
-                 || (strcmp(if_range, match) != 0)) {
-            return 0;
-        }
+    if (AP_CONDITION_NOMATCH == ap_condition_if_range(r, r->headers_out)) {
+        return 0;
     }
-    
+
     range += 6;
     it = range;
     while (*it) {
@@ -156,24 +135,23 @@ static int ap_set_byterange(request_rec *r, apr_off_t clength,
         ranges = MAX_PREALLOC_RANGES;
     }
     *indexes = apr_array_make(r->pool, ranges, sizeof(indexes_t));
-    merged = apr_array_make(r->pool, ranges, sizeof(char *));
     while ((cur = ap_getword(r->pool, &range, ','))) {
         char *dash;
         char *errp;
         apr_off_t number, start, end;
-        
+
         if (!*cur)
             break;
-        
+
         /*
          * Per RFC 2616 14.35.1: If there is at least one syntactically invalid
          * byte-range-spec, we must ignore the whole header.
          */
-        
+
         if (!(dash = strchr(cur, '-'))) {
             return 0;
         }
-        
+
         if (dash == cur) {
             /* In the form "-5" */
             if (apr_strtoff(&number, dash+1, &errp, 10) || *errp) {
@@ -202,9 +180,23 @@ static int ap_set_byterange(request_rec *r, apr_off_t clength,
             }
             else {                  /* "5-" */
                 end = clength - 1;
+                /*
+                 * special case: 0-
+                 *   ignore all other ranges provided
+                 *   return as a single range: 0-
+                 */
+                if (start == 0) {
+                    num_ranges = 0;
+                    sum_lengths = 0;
+                    in_merge = 1;
+                    oend = end;
+                    ostart = start;
+                    apr_array_clear(*indexes);
+                    break;
+                }
             }
         }
-        
+
         if (start < 0) {
             start = 0;
         }
@@ -215,7 +207,7 @@ static int ap_set_byterange(request_rec *r, apr_off_t clength,
         if (end >= clength) {
             end = clength - 1;
         }
-        
+
         if (!in_merge) {
             /* new set */
             ostart = start;
@@ -224,11 +216,11 @@ static int ap_set_byterange(request_rec *r, apr_off_t clength,
             continue;
         }
         in_merge = 0;
-        
+
         if (start >= ostart && end <= oend) {
             in_merge = 1;
         }
-        
+
         if (start < ostart && end >= ostart-1) {
             ostart = start;
             ++*reversals;
@@ -238,14 +230,11 @@ static int ap_set_byterange(request_rec *r, apr_off_t clength,
             oend = end;
             in_merge = 1;
         }
-        
+
         if (in_merge) {
             ++*overlaps;
             continue;
         } else {
-            new = (char **)apr_array_push(merged);
-            *new = apr_psprintf(r->pool, "%" APR_OFF_T_FMT "-%" APR_OFF_T_FMT,
-                                ostart, oend);
             idx = (indexes_t *)apr_array_push(*indexes);
             idx->start = ostart;
             idx->end = oend;
@@ -257,11 +246,8 @@ static int ap_set_byterange(request_rec *r, apr_off_t clength,
             num_ranges++;
         }
     }
-    
+
     if (in_merge) {
-        new = (char **)apr_array_push(merged);
-        *new = apr_psprintf(r->pool, "%" APR_OFF_T_FMT "-%" APR_OFF_T_FMT,
-                            ostart, oend);
         idx = (indexes_t *)apr_array_push(*indexes);
         idx->start = ostart;
         idx->end = oend;
@@ -272,34 +258,30 @@ static int ap_set_byterange(request_rec *r, apr_off_t clength,
         /* If all ranges are unsatisfiable, we should return 416 */
         return -1;
     }
-    if (sum_lengths >= clength) {
+    if (sum_lengths > clength) {
         ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
-                      "Sum of ranges not smaller than file, ignoring.");
+                      "Sum of ranges larger than file, ignoring.");
         return 0;
     }
-    
+
+    /*
+     * create the merged table now, now that we know we need it
+     */
+    merged = apr_array_make(r->pool, num_ranges, sizeof(char *));
+    idx = (indexes_t *)(*indexes)->elts;
+    for (i = 0; i < (*indexes)->nelts; i++, idx++) {
+        char **new = (char **)apr_array_push(merged);
+        *new = apr_psprintf(r->pool, "%" APR_OFF_T_FMT "-%" APR_OFF_T_FMT,
+                            idx->start, idx->end);
+    }
+
     r->status = HTTP_PARTIAL_CONTENT;
     r->range = apr_array_pstrcat(r->pool, merged, ',');
-    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01583)
                   "Range: %s | %s (%d : %d : %"APR_OFF_T_FMT")",
                   it, r->range, *overlaps, *reversals, clength);
-    
-    return num_ranges;
-}
 
-/*
- * Here we try to be compatible with clients that want multipart/x-byteranges
- * instead of multipart/byteranges (also see above), as per HTTP/1.1. We
- * look for the Request-Range header (e.g. Netscape 2 and 3) as an indication
- * that the browser supports an older protocol. We also check User-Agent
- * for Microsoft Internet Explorer 3, which needs this as well.
- */
-static int use_range_x(request_rec *r)
-{
-    const char *ua;
-    return (apr_table_get(r->headers_in, "Request-Range")
-            || ((ua = apr_table_get(r->headers_in, "User-Agent"))
-                && ap_strstr_c(ua, "MSIE 3")));
+    return num_ranges;
 }
 
 #define BYTERANGE_FMT "%" APR_OFF_T_FMT "-%" APR_OFF_T_FMT "/%" APR_OFF_T_FMT
@@ -368,8 +350,7 @@ static apr_status_t copy_brigade_range(apr_bucket_brigade *bb,
                     return rv;
                 }
                 out_first = APR_BUCKET_NEXT(copy);
-                APR_BUCKET_REMOVE(copy);
-                apr_bucket_destroy(copy);
+                apr_bucket_delete(copy);
             }
             else {
                 out_first = copy;
@@ -388,8 +369,7 @@ static apr_status_t copy_brigade_range(apr_bucket_brigade *bb,
                 }
                 copy = APR_BUCKET_NEXT(copy);
                 if (copy != APR_BRIGADE_SENTINEL(bbout)) {
-                    APR_BUCKET_REMOVE(copy);
-                    apr_bucket_destroy(copy);
+                    apr_bucket_delete(copy);
                 }
             }
             break;
@@ -431,19 +411,24 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f,
     apr_status_t rv;
     int found = 0;
     int num_ranges;
-    char *boundary = NULL;
     char *bound_head = NULL;
     apr_array_header_t *indexes;
     indexes_t *idx;
     int i;
     int original_status;
-    int max_ranges;
+    int max_ranges, max_overlaps, max_reversals;
     int overlaps = 0, reversals = 0;
     core_dir_config *core_conf = ap_get_core_module_config(r->per_dir_config);
 
     max_ranges = ( (core_conf->max_ranges >= 0 || core_conf->max_ranges == AP_MAXRANGES_UNLIMITED)
                    ? core_conf->max_ranges
                    : AP_DEFAULT_MAX_RANGES );
+    max_overlaps = ( (core_conf->max_overlaps >= 0 || core_conf->max_overlaps == AP_MAXRANGES_UNLIMITED)
+                  ? core_conf->max_overlaps
+                  : AP_DEFAULT_MAX_OVERLAPS );
+    max_reversals = ( (core_conf->max_reversals >= 0 || core_conf->max_reversals == AP_MAXRANGES_UNLIMITED)
+                  ? core_conf->max_reversals
+                  : AP_DEFAULT_MAX_REVERSALS );
     /*
      * Iterate through the brigade until reaching EOS or a bucket with
      * unknown length.
@@ -469,8 +454,11 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f,
     original_status = r->status;
     num_ranges = ap_set_byterange(r, clength, &indexes, &overlaps, &reversals);
 
-    /* We have nothing to do, get out of the way. */
-    if (num_ranges == 0 || (max_ranges >= 0 && num_ranges > max_ranges)) {
+    /* No Ranges or we hit a limit? We have nothing to do, get out of the way. */
+    if (num_ranges == 0 ||
+        (max_ranges >= 0 && num_ranges > max_ranges) ||
+        (max_overlaps >= 0 && overlaps > max_overlaps) ||
+        (max_reversals >= 0 && reversals > max_reversals)) {
         r->status = original_status;
         ap_remove_output_filter(f);
         return ap_pass_brigade(f->next, bb);
@@ -485,17 +473,14 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f,
     if (num_ranges > 1) {
         /* Is ap_make_content_type required here? */
         const char *orig_ct = ap_make_content_type(r, r->content_type);
-        boundary = apr_psprintf(r->pool, "%" APR_UINT64_T_HEX_FMT "%lx",
-                                (apr_uint64_t)r->request_time, c->id);
 
-        ap_set_content_type(r, apr_pstrcat(r->pool, "multipart",
-                                           use_range_x(r) ? "/x-" : "/",
-                                           "byteranges; boundary=",
-                                           boundary, NULL));
+        ap_set_content_type(r, apr_pstrcat(r->pool,
+                                           "multipart/byteranges; boundary=",
+                                           ap_multipart_boundary, NULL));
 
         if (orig_ct) {
             bound_head = apr_pstrcat(r->pool,
-                                     CRLF "--", boundary,
+                                     CRLF "--", ap_multipart_boundary,
                                      CRLF "Content-type: ",
                                      orig_ct,
                                      CRLF "Content-range: bytes ",
@@ -504,7 +489,7 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f,
         else {
             /* if we have no type for the content, do our best */
             bound_head = apr_pstrcat(r->pool,
-                                     CRLF "--", boundary,
+                                     CRLF "--", ap_multipart_boundary,
                                      CRLF "Content-range: bytes ",
                                      NULL);
         }
@@ -520,7 +505,7 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f,
 
         rv = copy_brigade_range(bb, tmpbb, range_start, range_end);
         if (rv != APR_SUCCESS ) {
-            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01584)
                           "copy_brigade_range() failed [%" APR_OFF_T_FMT
                           "-%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT "]",
                           range_start, range_end, clength);
@@ -576,7 +561,8 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f,
         char *end;
 
         /* add the final boundary */
-        end = apr_pstrcat(r->pool, CRLF "--", boundary, "--" CRLF, NULL);
+        end = apr_pstrcat(r->pool, CRLF "--", ap_multipart_boundary, "--" CRLF,
+                          NULL);
         ap_xlate_proto_to_ascii(end, strlen(end));
         e = apr_bucket_pool_create(end, strlen(end), r->pool, c->bucket_alloc);
         APR_BRIGADE_INSERT_TAIL(bsend, e);