[PATCH] Allow Partial Content responses to satisfy Range requests

Steven Hartland steven.hartland at multiplay.co.uk
Thu Dec 4 21:20:15 UTC 2014


This time test suite passes on default compile:
All tests successful.
Files=141, Tests=964, 86 wallclock secs ( 0.49 usr  0.22 sys + 10.05 
cusr  2.55 csys = 13.31 CPU)
Result: PASS

On 04/12/2014 21:07, Steven Hartland wrote:
> # HG changeset patch
> # User Steven Hartland <steven.hartland at multiplay.co.uk>
> # Date 1417727204 0
> #      Thu Dec 04 21:06:44 2014 +0000
> # Node ID 05d3973ece9af030d0312932938fc3d1f2f139dd
> # Parent  1573fc7875fa09ee55763ce7ddc4e98d61e1deaf
> Allow Partial Content responses to satisfy Range requests
>
> diff -r 1573fc7875fa -r 05d3973ece9a src/http/modules/ngx_http_range_filter_module.c
> --- a/src/http/modules/ngx_http_range_filter_module.c	Wed Nov 26 18:35:37 2014 -0800
> +++ b/src/http/modules/ngx_http_range_filter_module.c	Thu Dec 04 21:06:44 2014 +0000
> @@ -54,6 +54,7 @@
>   
>   typedef struct {
>       off_t        offset;
> +    off_t        content_length;
>       ngx_str_t    boundary_header;
>       ngx_array_t  ranges;
>   } ngx_http_range_filter_ctx_t;
> @@ -65,7 +66,8 @@
>       ngx_http_range_filter_ctx_t *ctx);
>   static ngx_int_t ngx_http_range_multipart_header(ngx_http_request_t *r,
>       ngx_http_range_filter_ctx_t *ctx);
> -static ngx_int_t ngx_http_range_not_satisfiable(ngx_http_request_t *r);
> +static ngx_int_t ngx_http_range_not_satisfiable(ngx_http_request_t *r,
> +    ngx_http_range_filter_ctx_t *ctx);
>   static ngx_int_t ngx_http_range_test_overlapped(ngx_http_request_t *r,
>       ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);
>   static ngx_int_t ngx_http_range_singlepart_body(ngx_http_request_t *r,
> @@ -76,6 +78,9 @@
>   static ngx_int_t ngx_http_range_header_filter_init(ngx_conf_t *cf);
>   static ngx_int_t ngx_http_range_body_filter_init(ngx_conf_t *cf);
>   
> +static ngx_int_t ngx_http_content_range_parse(ngx_http_request_t *r,
> +    ngx_http_range_filter_ctx_t *ctx);
> +
>   
>   static ngx_http_module_t  ngx_http_range_header_filter_module_ctx = {
>       NULL,                                  /* preconfiguration */
> @@ -153,8 +158,8 @@
>       ngx_http_range_filter_ctx_t  *ctx;
>   
>       if (r->http_version < NGX_HTTP_VERSION_10
> -        || r->headers_out.status != NGX_HTTP_OK
> -        || r != r->main
> +        || (r->headers_out.status != NGX_HTTP_OK
> +        && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT)
>           || r->headers_out.content_length_n == -1
>           || !r->allow_ranges)
>       {
> @@ -230,26 +235,31 @@
>   
>       ranges = r->single_range ? 1 : clcf->max_ranges;
>   
> -    switch (ngx_http_range_parse(r, ctx, ranges)) {
> +    switch (ngx_http_content_range_parse(r, ctx)) {
> +    case NGX_OK:
> +        switch (ngx_http_range_parse(r, ctx, ranges)) {
> +        case NGX_OK:
> +            ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);
>   
> -    case NGX_OK:
> -        ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);
> +            r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
> +            r->headers_out.status_line.len = 0;
>   
> -        r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
> -        r->headers_out.status_line.len = 0;
> +            if (ctx->ranges.nelts == 1) {
> +                return ngx_http_range_singlepart_header(r, ctx);
> +            }
>   
> -        if (ctx->ranges.nelts == 1) {
> -            return ngx_http_range_singlepart_header(r, ctx);
> +            return ngx_http_range_multipart_header(r, ctx);
> +
> +        case NGX_HTTP_RANGE_NOT_SATISFIABLE:
> +            return ngx_http_range_not_satisfiable(r, ctx);
> +
> +        case NGX_ERROR:
> +            return NGX_ERROR;
> +
> +        default: /* NGX_DECLINED */
> +            break;
>           }
> -
> -        return ngx_http_range_multipart_header(r, ctx);
> -
> -    case NGX_HTTP_RANGE_NOT_SATISFIABLE:
> -        return ngx_http_range_not_satisfiable(r);
> -
> -    case NGX_ERROR:
> -        return NGX_ERROR;
> -
> +        break;
>       default: /* NGX_DECLINED */
>           break;
>       }
> @@ -274,13 +284,12 @@
>       ngx_uint_t ranges)
>   {
>       u_char            *p;
> -    off_t              start, end, size, content_length;
> +    off_t              start, end, size;
>       ngx_uint_t         suffix;
>       ngx_http_range_t  *range;
>   
>       p = r->headers_in.range->value.data + 6;
>       size = 0;
> -    content_length = r->headers_out.content_length_n;
>   
>       for ( ;; ) {
>           start = 0;
> @@ -298,6 +307,10 @@
>                   start = start * 10 + *p++ - '0';
>               }
>   
> +            if (start < ctx->offset) {
> +                return NGX_HTTP_RANGE_NOT_SATISFIABLE;
> +            }
> +
>               while (*p == ' ') { p++; }
>   
>               if (*p++ != '-') {
> @@ -307,7 +320,7 @@
>               while (*p == ' ') { p++; }
>   
>               if (*p == ',' || *p == '\0') {
> -                end = content_length;
> +                end = ctx->content_length;
>                   goto found;
>               }
>   
> @@ -331,12 +344,12 @@
>           }
>   
>           if (suffix) {
> -            start = content_length - end;
> -            end = content_length - 1;
> +            start = ctx->content_length - end;
> +            end = ctx->content_length - 1;
>           }
>   
> -        if (end >= content_length) {
> -            end = content_length;
> +        if (end >= ctx->content_length) {
> +            end = ctx->content_length;
>   
>           } else {
>               end++;
> @@ -369,7 +382,7 @@
>           return NGX_HTTP_RANGE_NOT_SATISFIABLE;
>       }
>   
> -    if (size > content_length) {
> +    if (size > ctx->content_length) {
>           return NGX_DECLINED;
>       }
>   
> @@ -384,16 +397,18 @@
>       ngx_table_elt_t   *content_range;
>       ngx_http_range_t  *range;
>   
> -    content_range = ngx_list_push(&r->headers_out.headers);
> -    if (content_range == NULL) {
> -        return NGX_ERROR;
> +    if (r->headers_out.content_range == NULL) {
> +        content_range = ngx_list_push(&r->headers_out.headers);
> +        if (content_range == NULL) {
> +            return NGX_ERROR;
> +        }
> +        r->headers_out.content_range = content_range;
> +        content_range->hash = 1;
> +        ngx_str_set(&content_range->key, "Content-Range");
> +    } else {
> +        content_range = r->headers_out.content_range;
>       }
>   
> -    r->headers_out.content_range = content_range;
> -
> -    content_range->hash = 1;
> -    ngx_str_set(&content_range->key, "Content-Range");
> -
>       content_range->value.data = ngx_pnalloc(r->pool,
>                                       sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN);
>       if (content_range->value.data == NULL) {
> @@ -407,7 +422,7 @@
>       content_range->value.len = ngx_sprintf(content_range->value.data,
>                                              "bytes %O-%O/%O",
>                                              range->start, range->end - 1,
> -                                           r->headers_out.content_length_n)
> +                                           ctx->content_length)
>                                  - content_range->value.data;
>   
>       r->headers_out.content_length_n = range->end - range->start;
> @@ -546,22 +561,25 @@
>   
>   
>   static ngx_int_t
> -ngx_http_range_not_satisfiable(ngx_http_request_t *r)
> +ngx_http_range_not_satisfiable(ngx_http_request_t *r,
> +    ngx_http_range_filter_ctx_t *ctx)
>   {
>       ngx_table_elt_t  *content_range;
>   
>       r->headers_out.status = NGX_HTTP_RANGE_NOT_SATISFIABLE;
>   
> -    content_range = ngx_list_push(&r->headers_out.headers);
> -    if (content_range == NULL) {
> -        return NGX_ERROR;
> +    if (r->headers_out.content_range == NULL) {
> +        content_range = ngx_list_push(&r->headers_out.headers);
> +        if (content_range == NULL) {
> +            return NGX_ERROR;
> +        }
> +        r->headers_out.content_range = content_range;
> +        content_range->hash = 1;
> +        ngx_str_set(&content_range->key, "Content-Range");
> +    } else {
> +        content_range = r->headers_out.content_range;
>       }
>   
> -    r->headers_out.content_range = content_range;
> -
> -    content_range->hash = 1;
> -    ngx_str_set(&content_range->key, "Content-Range");
> -
>       content_range->value.data = ngx_pnalloc(r->pool,
>                                          sizeof("bytes */") - 1 + NGX_OFF_T_LEN);
>       if (content_range->value.data == NULL) {
> @@ -570,7 +588,7 @@
>   
>       content_range->value.len = ngx_sprintf(content_range->value.data,
>                                              "bytes */%O",
> -                                           r->headers_out.content_length_n)
> +                                           ctx->content_length)
>                                  - content_range->value.data;
>   
>       ngx_http_clear_content_length(r);
> @@ -888,3 +906,76 @@
>   
>       return NGX_OK;
>   }
> +
> +
> +static ngx_int_t
> +ngx_http_content_range_parse(ngx_http_request_t *r,
> +    ngx_http_range_filter_ctx_t *ctx)
> +{
> +    u_char     *p;
> +    off_t      start, end, len;
> +
> +    ctx->offset = 0;
> +    ctx->content_length = r->headers_out.content_length_n;
> +
> +    if (r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT) {
> +       return NGX_OK;
> +    }
> +
> +    if (r->headers_out.content_range == NULL
> +        || r->headers_out.content_range->value.len == 0) {
> +       return NGX_HTTP_RANGE_NOT_SATISFIABLE;
> +    }
> +
> +    if (r->headers_out.content_range->value.len < 7
> +        || ngx_strncasecmp(r->headers_out.content_range->value.data,
> +                           (u_char *) "bytes ", 6) != 0) {
> +       return NGX_DECLINED;
> +    }
> +
> +    start = 0;
> +    end = 0;
> +    len = 0;
> +
> +    p = r->headers_out.content_range->value.data + 6;
> +
> +    while (*p == ' ') { p++; }
> +
> +    if (*p < '0' || *p > '9') {
> +        return NGX_HTTP_RANGE_NOT_SATISFIABLE;
> +    }
> +
> +    while (*p >= '0' && *p <= '9') {
> +        start = start * 10 + *p++ - '0';
> +    }
> +
> +    if (*p++ != '-') {
> +        return NGX_HTTP_RANGE_NOT_SATISFIABLE;
> +    }
> +
> +    while (*p >= '0' && *p <= '9') {
> +        end = end * 10 + *p++ - '0';
> +    }
> +
> +    if (*p++ != '/') {
> +        return NGX_HTTP_RANGE_NOT_SATISFIABLE;
> +    }
> +
> +    if (*p < '0' || *p > '9') {
> +        return NGX_HTTP_RANGE_NOT_SATISFIABLE;
> +    }
> +
> +    while (*p >= '0' && *p <= '9') {
> +        len = len * 10 + *p++ - '0';
> +    }
> +
> +    if (*p != '\0') {
> +        return NGX_HTTP_RANGE_NOT_SATISFIABLE;
> +    }
> +
> +    ctx->offset = start;
> +    ctx->content_length = len;
> +
> +    return NGX_OK;
> +}
> +
> diff -r 1573fc7875fa -r 05d3973ece9a src/http/ngx_http_upstream.c
> --- a/src/http/ngx_http_upstream.c	Wed Nov 26 18:35:37 2014 -0800
> +++ b/src/http/ngx_http_upstream.c	Thu Dec 04 21:06:44 2014 +0000
> @@ -292,6 +292,11 @@
>                    ngx_http_upstream_copy_content_encoding, 0, 0 },
>   #endif
>   
> +    { ngx_string("Content-Range"),
> +                 ngx_http_upstream_ignore_header_line, 0,
> +                 ngx_http_upstream_copy_allow_ranges,
> +                 offsetof(ngx_http_headers_out_t, content_range), 1 },
> +
>       { ngx_null_string, NULL, 0, NULL, 0, 0 }
>   };
>   
> @@ -4516,37 +4521,26 @@
>   ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r,
>       ngx_table_elt_t *h, ngx_uint_t offset)
>   {
> -    ngx_table_elt_t  *ho;
> -
>       if (r->upstream->conf->force_ranges) {
>           return NGX_OK;
>       }
> -
>   #if (NGX_HTTP_CACHE)
> -
>       if (r->cached) {
>           r->allow_ranges = 1;
> -        return NGX_OK;
> +        if (offsetof(ngx_http_headers_out_t, accept_ranges) == offset) {
> +            return NGX_OK;
> +       }
>       }
>   
>       if (r->upstream->cacheable) {
>           r->allow_ranges = 1;
>           r->single_range = 1;
> -        return NGX_OK;
> -    }
> -
> +        if (offsetof(ngx_http_headers_out_t, accept_ranges) == offset) {
> +            return NGX_OK;
> +        }
> +    }
>   #endif
> -
> -    ho = ngx_list_push(&r->headers_out.headers);
> -    if (ho == NULL) {
> -        return NGX_ERROR;
> -    }
> -
> -    *ho = *h;
> -
> -    r->headers_out.accept_ranges = ho;
> -
> -    return NGX_OK;
> +    return ngx_http_upstream_copy_header_line(r, h, offset);
>   }
>   
>   
>
> _______________________________________________
> nginx-devel mailing list
> nginx-devel at nginx.org
> http://mailman.nginx.org/mailman/listinfo/nginx-devel



More information about the nginx-devel mailing list