[PATCH] Allow Partial Content responses to satisfy Range requests
Steven Hartland
steven.hartland at multiplay.co.uk
Tue Nov 25 14:22:13 UTC 2014
# HG changeset patch
# User Steven Hartland <steven.hartland at 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);
}
More information about the nginx-devel
mailing list