# HG changeset patch # User Steven Hartland <steven.hartl...@multiplay.co.uk> # Date 1416925134 0 # Tue Nov 25 14:18:54 2014 +0000 # Node ID 0c3c06fabfc3b1c57710c0cced4837c10e3e9bbb # Parent 7d7eac6e31df1d962a644f8093c1fbb8f91620ce Allow Partial Content responses to satisfy Range requests.
diff -r 7d7eac6e31df -r 0c3c06fabfc3 src/http/modules/ngx_http_range_filter_module.c --- a/src/http/modules/ngx_http_range_filter_module.c Tue Nov 04 19:56:23 2014 +0900 +++ b/src/http/modules/ngx_http_range_filter_module.c Tue Nov 25 14:18:54 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); + + 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 7d7eac6e31df -r 0c3c06fabfc3 src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c Tue Nov 04 19:56:23 2014 +0900 +++ b/src/http/ngx_http_upstream.c Tue Nov 25 14:18:54 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 } }; @@ -4499,37 +4504,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@nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel