Welcome! Log In Create A New Profile

Advanced

[PATCH] Allow Partial Content responses to satisfy Range requests

September 01, 2014 06:58PM
# HG changeset patch
# User Steven Hartland <steven.hartland@multiplay.co.uk>
# Date 1409611936 0
# Mon Sep 01 22:52:16 2014 +0000
# Node ID 0dc608b347e24b914ee193214857de15aad2ac0b
# Parent 3f5f0ab59b359064db16e1aa52dfca335720dff6
Allow Partial Content responses to satisfy Range requests.

Previously on 200 responsed could statisfy Range requests, this adds support
for 206 responses (Partial Content) to also satisfy Range requests as long
as the Range fits within the Partial Content.

This can be used directly as well as by other modules where is can be used
to build more complex responses, such as the response to a Range request
from a predicable set of sub Range requests.

diff -r 3f5f0ab59b35 -r 0dc608b347e2 src/http/modules/ngx_http_range_filter_module.c
--- a/src/http/modules/ngx_http_range_filter_module.c Mon Sep 01 18:20:18 2014 +0400
+++ b/src/http/modules/ngx_http_range_filter_module.c Mon Sep 01 22:52:16 2014 +0000
@@ -54,6 +54,8 @@

typedef struct {
off_t offset;
+ off_t content_end;
+ off_t content_length;
ngx_str_t boundary_header;
ngx_array_t ranges;
} ngx_http_range_filter_ctx_t;
@@ -65,7 +67,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 +79,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 +159,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 +236,35 @@

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:
- ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);
+ switch (ngx_http_range_parse(r, ctx, ranges)) {

- r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
- r->headers_out.status_line.len = 0;
+ case NGX_OK:
+ ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);

- if (ctx->ranges.nelts == 1) {
- return ngx_http_range_singlepart_header(r, ctx);
+ 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);
+ }
+
+ 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);
-
+ break;
case NGX_HTTP_RANGE_NOT_SATISFIABLE:
- return ngx_http_range_not_satisfiable(r);
-
- case NGX_ERROR:
- return NGX_ERROR;
-
+ return ngx_http_range_not_satisfiable(r, ctx);
default: /* NGX_DECLINED */
break;
}
@@ -274,13 +289,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 +312,10 @@
start = start * 10 + *p++ - '0';
}

+ if (start < ctx->offset) {
+ return NGX_HTTP_RANGE_NOT_SATISFIABLE;
+ }
+
while (*p == ' ') { p++; }

if (*p++ != '-') {
@@ -307,7 +325,7 @@
while (*p == ' ') { p++; }

if (*p == ',' || *p == '\0') {
- end = content_length;
+ end = ctx->content_length;
goto found;
}

@@ -324,6 +342,10 @@
end = end * 10 + *p++ - '0';
}

+ if (end > ctx->content_end) {
+ return NGX_HTTP_RANGE_NOT_SATISFIABLE;
+ }
+
while (*p == ' ') { p++; }

if (*p != ',' && *p != '\0') {
@@ -331,12 +353,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 +391,7 @@
return NGX_HTTP_RANGE_NOT_SATISFIABLE;
}

- if (size > content_length) {
+ if (size > ctx->content_length) {
return NGX_DECLINED;
}

@@ -384,16 +406,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 +431,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 +570,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 +597,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 +915,77 @@

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_end = r->headers_out.content_length_n - 1;
+ 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_end = end;
+ ctx->content_length = len;
+
+ return NGX_OK;
+}
+
diff -r 3f5f0ab59b35 -r 0dc608b347e2 src/http/ngx_http_upstream.c
--- a/src/http/ngx_http_upstream.c Mon Sep 01 18:20:18 2014 +0400
+++ b/src/http/ngx_http_upstream.c Mon Sep 01 22:52:16 2014 +0000
@@ -286,6 +286,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 }
};

@@ -4362,33 +4367,23 @@
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 (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
Subject Author Views Posted

[PATCH] Allow Partial Content responses to satisfy Range requests

steveh 1519 September 01, 2014 06:58PM



Sorry, you do not have permission to post/reply in this forum.

Online Users

Guests: 122
Record Number of Users: 8 on April 13, 2023
Record Number of Guests: 421 on December 02, 2018
Powered by nginx      Powered by FreeBSD      PHP Powered      Powered by MariaDB      ipv6 ready