Slice module
Roman Arutyunyan
arut at nginx.com
Tue Sep 29 16:00:25 UTC 2015
Hello,
I'm happy to publish the experimental Slice module. The module makes it
possible to split a big upstream response into smaller parts and cache them
independently.
The module supports range requests. When a part of a file is requested,
only the required slice upstream requests are made. If caching is enabled,
future requests will only go to upstream for missing slices.
The module adds
- "slice" directive setting the slice size.
- "$slice_range" variable, which must be added to the cache key
expression and passed to upstream as the Range header value.
The variable holds current slice range in the HTTP Range field format.
Build
-----
Use the --with-http_slice_module configure script option.
Example
-------
location / {
slice 1m;
proxy_cache cache;
proxy_cache_key $uri$is_args$args$slice_range;
proxy_set_header Range $slice_range;
proxy_cache_valid 200 206 1h;
proxy_pass http://127.0.0.1:9000;
}
Known issues
------------
The module can lead to excessive memory and file handle usage.
Thanks for testing.
--
Best wishes,
Roman Arutyunyan
-------------- next part --------------
diff -r 0f313cf0a1ee auto/modules
--- a/auto/modules Tue Sep 22 17:36:22 2015 +0300
+++ b/auto/modules Mon Sep 28 17:07:15 2015 +0300
@@ -73,6 +73,11 @@ if [ $HTTP_SSI = YES ]; then
fi
+if [ $HTTP_SLICE = YES ]; then
+ HTTP_POSTPONE=YES
+fi
+
+
if [ $HTTP_ADDITION = YES ]; then
HTTP_POSTPONE=YES
fi
@@ -140,6 +145,11 @@ if [ $HTTP_SSI = YES ]; then
HTTP_SRCS="$HTTP_SRCS $HTTP_SSI_SRCS"
fi
+if [ $HTTP_SLICE = YES ]; then
+ HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_SLICE_FILTER_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_SLICE_SRCS"
+fi
+
if [ $HTTP_CHARSET = YES ]; then
HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_CHARSET_FILTER_MODULE"
HTTP_SRCS="$HTTP_SRCS $HTTP_CHARSET_SRCS"
diff -r 0f313cf0a1ee auto/options
--- a/auto/options Tue Sep 22 17:36:22 2015 +0300
+++ b/auto/options Mon Sep 28 17:07:15 2015 +0300
@@ -60,6 +60,7 @@ HTTP_GZIP=YES
HTTP_SSL=NO
HTTP_V2=NO
HTTP_SSI=YES
+HTTP_SLICE=NO
HTTP_POSTPONE=NO
HTTP_REALIP=NO
HTTP_XSLT=NO
@@ -226,6 +227,7 @@ do
--with-http_random_index_module) HTTP_RANDOM_INDEX=YES ;;
--with-http_secure_link_module) HTTP_SECURE_LINK=YES ;;
--with-http_degradation_module) HTTP_DEGRADATION=YES ;;
+ --with-http_slice_module) HTTP_SLICE=YES ;;
--without-http_charset_module) HTTP_CHARSET=NO ;;
--without-http_gzip_module) HTTP_GZIP=NO ;;
@@ -395,6 +397,7 @@ cat << END
--with-http_secure_link_module enable ngx_http_secure_link_module
--with-http_degradation_module enable ngx_http_degradation_module
--with-http_stub_status_module enable ngx_http_stub_status_module
+ --with-http_slice_module enable ngx_http_slice_module
--without-http_charset_module disable ngx_http_charset_module
--without-http_gzip_module disable ngx_http_gzip_module
diff -r 0f313cf0a1ee auto/sources
--- a/auto/sources Tue Sep 22 17:36:22 2015 +0300
+++ b/auto/sources Mon Sep 28 17:07:15 2015 +0300
@@ -347,6 +347,10 @@ HTTP_SSI_DEPS=src/http/modules/ngx_http_
HTTP_SSI_SRCS=src/http/modules/ngx_http_ssi_filter_module.c
+HTTP_SLICE_FILTER_MODULE=ngx_http_slice_filter_module
+HTTP_SLICE_SRCS=src/http/modules/ngx_http_slice_filter_module.c
+
+
HTTP_XSLT_FILTER_MODULE=ngx_http_xslt_filter_module
HTTP_XSLT_SRCS=src/http/modules/ngx_http_xslt_filter_module.c
diff -r 0f313cf0a1ee src/http/modules/ngx_http_slice_filter_module.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/http/modules/ngx_http_slice_filter_module.c Mon Sep 28 17:07:15 2015 +0300
@@ -0,0 +1,777 @@
+
+/*
+ * Copyright (C) Roman Arutyunyan
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ size_t size;
+} ngx_http_slice_loc_conf_t;
+
+
+typedef struct {
+ off_t start;
+ off_t end;
+ off_t content_length;
+ off_t offset;
+ ngx_str_t range;
+ ngx_str_t etag;
+ unsigned last:1;
+ unsigned bad_range:1;
+} ngx_http_slice_ctx_t;
+
+
+static ngx_int_t ngx_http_slice_handler(ngx_http_request_t *r);
+static ngx_int_t ngx_http_slice_parse_request_range(ngx_http_request_t *r,
+ ngx_http_slice_ctx_t *ctx);
+static ngx_int_t ngx_http_slice_header_filter(ngx_http_request_t *r);
+static ngx_int_t ngx_http_slice_parse_response_range(ngx_http_request_t *r,
+ ngx_http_slice_ctx_t *ctx);
+static ngx_int_t ngx_http_slice_body_filter(ngx_http_request_t *r,
+ ngx_chain_t *in);
+static ngx_int_t ngx_http_slice_range_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static void *ngx_http_slice_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_slice_merge_loc_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static ngx_int_t ngx_http_slice_add_variables(ngx_conf_t *cf);
+static ngx_int_t ngx_http_slice_init(ngx_conf_t *cf);
+
+
+static ngx_command_t ngx_http_slice_filter_commands[] = {
+
+ { ngx_string("slice"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_slice_loc_conf_t, size),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_slice_filter_module_ctx = {
+ ngx_http_slice_add_variables, /* preconfiguration */
+ ngx_http_slice_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_slice_create_loc_conf, /* create location configuration */
+ ngx_http_slice_merge_loc_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_slice_filter_module = {
+ NGX_MODULE_V1,
+ &ngx_http_slice_filter_module_ctx, /* module context */
+ ngx_http_slice_filter_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_str_t ngx_http_slice_range_name = ngx_string("slice_range");
+
+static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
+static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
+
+
+static ngx_int_t
+ngx_http_slice_handler(ngx_http_request_t *r)
+{
+ off_t start, end;
+ u_char *p;
+ ngx_http_slice_ctx_t *ctx;
+ ngx_http_slice_loc_conf_t *slcf;
+
+ slcf = ngx_http_get_module_loc_conf(r, ngx_http_slice_filter_module);
+ if (slcf->size == 0) {
+ return NGX_DECLINED;
+ }
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_slice_filter_module);
+ if (ctx) {
+ return NGX_DECLINED;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http slice handler");
+
+ ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_slice_ctx_t));
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_set_ctx(r, ctx, ngx_http_slice_filter_module);
+
+ if (ngx_http_slice_parse_request_range(r, ctx) != NGX_OK) {
+ ctx->bad_range = 1;
+ }
+
+ if (ctx->start > 0) {
+ start = slcf->size * (ctx->start / slcf->size);
+
+ } else {
+ start = 0;
+ }
+
+ end = start + slcf->size - 1;
+
+ p = ngx_pnalloc(r->pool, sizeof("bytes=-") - 1 + 2 * NGX_OFF_T_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ ctx->range.data = p;
+ ctx->range.len = ngx_sprintf(p, "bytes=%O-%O", start, end) - p;
+
+ return NGX_DECLINED;
+}
+
+
+static ngx_int_t
+ngx_http_slice_parse_request_range(ngx_http_request_t *r,
+ ngx_http_slice_ctx_t *ctx)
+{
+ off_t start, end, cutoff, cutlim;
+ u_char *p;
+ ngx_uint_t suffix;
+
+ if (r->method == NGX_HTTP_HEAD) {
+ return NGX_OK;
+ }
+
+ if (r->headers_in.range == NULL
+ || r->headers_in.range->value.len < 7
+ || ngx_strncasecmp(r->headers_in.range->value.data,
+ (u_char *) "bytes=", 6)
+ != 0)
+ {
+ ctx->end = -1;
+ return NGX_OK;
+ }
+
+ p = r->headers_in.range->value.data + 6;
+
+ cutoff = NGX_MAX_OFF_T_VALUE / 10;
+ cutlim = NGX_MAX_OFF_T_VALUE % 10;
+
+ start = 0;
+ end = 0;
+ suffix = 0;
+
+ while (*p == ' ') { p++; }
+
+ if (*p != '-') {
+ if (*p < '0' || *p > '9') {
+ return NGX_ERROR;
+ }
+
+ while (*p >= '0' && *p <= '9') {
+ if (start >= cutoff && (start > cutoff || *p - '0' > cutlim)) {
+ return NGX_ERROR;
+ }
+
+ start = start * 10 + *p++ - '0';
+ }
+
+ while (*p == ' ') { p++; }
+
+ if (*p++ != '-') {
+ return NGX_ERROR;
+ }
+
+ while (*p == ' ') { p++; }
+
+ if (*p == ',') {
+ ctx->end = -1;
+ return NGX_OK;
+ }
+
+ if (*p == '\0') {
+ ctx->start = start;
+ ctx->end = -1;
+ return NGX_OK;
+ }
+
+ } else {
+ suffix = 1;
+ p++;
+ }
+
+ if (*p < '0' || *p > '9') {
+ return NGX_ERROR;
+ }
+
+ while (*p >= '0' && *p <= '9') {
+ if (end >= cutoff && (end > cutoff || *p - '0' > cutlim)) {
+ return NGX_ERROR;
+ }
+
+ end = end * 10 + *p++ - '0';
+ }
+
+ while (*p == ' ') { p++; }
+
+ if (*p != ',' && *p != '\0') {
+ return NGX_ERROR;
+ }
+
+ if (suffix) {
+ ctx->start = -end;
+ ctx->end = -1;
+ return NGX_OK;
+ }
+
+ if (*p == ',') {
+ ctx->end = -1;
+ return NGX_OK;
+ }
+
+ ctx->start = start;
+ ctx->end = end + 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_slice_header_filter(ngx_http_request_t *r)
+{
+ ngx_table_elt_t *h;
+ ngx_http_slice_ctx_t *ctx;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_slice_filter_module);
+ if (ctx == NULL) {
+ return ngx_http_next_header_filter(r);
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http slice header filter");
+
+ if (r->headers_out.status != NGX_HTTP_OK
+ && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT)
+ {
+ if (r != r->main) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "bad status code %ui in slice response",
+ r->headers_out.status);
+ return NGX_ERROR;
+ }
+
+ return ngx_http_next_header_filter(r);
+ }
+
+ if (ngx_http_slice_parse_response_range(r, ctx) != NGX_OK) {
+ if (r != r->main) {
+ return NGX_ERROR;
+ }
+
+ return ngx_http_filter_finalize_request(r, NULL, NGX_HTTP_BAD_GATEWAY);
+ }
+
+ if (r != r->main) {
+ if (ctx->etag.len) {
+ h = r->headers_out.etag;
+
+ if (h == NULL
+ || h->value.len != ctx->etag.len
+ || ngx_strncmp(h->value.data, ctx->etag.data, ctx->etag.len)
+ != 0)
+ {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "bad etag in slice response");
+ return NGX_ERROR;
+ }
+ }
+
+ return ngx_http_next_header_filter(r);
+ }
+
+ if (ctx->start < 0) {
+ ctx->start += ctx->content_length;
+ if (ctx->start < 0) {
+ ctx->start = 0;
+ }
+ }
+
+ if (ctx->end == -1 || ctx->end > ctx->content_length) {
+ ctx->end = ctx->content_length;
+ }
+
+ if (ctx->start >= ctx->end) {
+ ctx->bad_range = 1;
+ }
+
+ if (ctx->bad_range) {
+ h = ngx_list_push(&r->headers_out.headers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ h->hash = 1;
+ ngx_str_set(&h->key, "Content-Range");
+
+ h->value.data = ngx_pnalloc(r->pool,
+ sizeof("bytes */") - 1 + NGX_OFF_T_LEN);
+ if (h->value.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ h->value.len = ngx_sprintf(h->value.data, "bytes */%O",
+ ctx->content_length)
+ - h->value.data;
+
+ ngx_http_clear_content_length(r);
+
+ r->headers_out.content_range = h;
+ r->headers_out.status = NGX_HTTP_RANGE_NOT_SATISFIABLE;
+
+ return NGX_HTTP_RANGE_NOT_SATISFIABLE;
+ }
+
+ if (r->headers_in.range == NULL) {
+ ngx_http_clear_content_length(r);
+
+ r->headers_out.content_length_n = ctx->content_length;
+ r->headers_out.status = NGX_HTTP_OK;
+ r->headers_out.status_line.len = 0;
+
+ } else {
+ h = ngx_list_push(&r->headers_out.headers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ h->hash = 1;
+ ngx_str_set(&h->key, "Content-Range");
+
+ h->value.data = ngx_pnalloc(r->pool,
+ sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN);
+ if (h->value.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ h->value.len = ngx_sprintf(h->value.data, "bytes %O-%O/%O",
+ ctx->start, ctx->end - 1,
+ ctx->content_length)
+ - h->value.data;
+
+ ngx_http_clear_content_length(r);
+
+ r->headers_out.content_range = h;
+ r->headers_out.content_length_n = ctx->end - ctx->start;
+ r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
+ r->headers_out.status_line.len = 0;
+ }
+
+ return ngx_http_next_header_filter(r);
+}
+
+
+static ngx_int_t
+ngx_http_slice_parse_response_range(ngx_http_request_t *r,
+ ngx_http_slice_ctx_t *ctx)
+{
+ off_t start, end, content_length, cutoff, cutlim;
+ u_char *p;
+ ngx_table_elt_t *h;
+
+ if (r->headers_out.status == NGX_HTTP_PARTIAL_CONTENT) {
+ h = r->headers_out.content_range;
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ h->hash = 0;
+ r->headers_out.content_range = NULL;
+
+ cutoff = NGX_MAX_OFF_T_VALUE / 10;
+ cutlim = NGX_MAX_OFF_T_VALUE % 10;
+
+ start = 0;
+ end = 0;
+ content_length = 0;
+
+ if (h->value.len < 7
+ || ngx_strncmp(h->value.data, "bytes ", 6) != 0)
+ {
+ return NGX_ERROR;
+ }
+
+ p = h->value.data + 6;
+
+ while (*p == ' ') { p++; }
+
+ if (*p < '0' || *p > '9') {
+ return NGX_ERROR;
+ }
+
+ while (*p >= '0' && *p <= '9') {
+ if (start >= cutoff && (start > cutoff || *p - '0' > cutlim)) {
+ return NGX_ERROR;
+ }
+
+ start = start * 10 + *p++ - '0';
+ }
+
+ while (*p == ' ') { p++; }
+
+ if (*p++ != '-') {
+ return NGX_ERROR;
+ }
+
+ while (*p == ' ') { p++; }
+
+ if (*p < '0' || *p > '9') {
+ return NGX_ERROR;
+ }
+
+ while (*p >= '0' && *p <= '9') {
+ if (end >= cutoff && (end > cutoff || *p - '0' > cutlim)) {
+ return NGX_ERROR;
+ }
+
+ end = end * 10 + *p++ - '0';
+ }
+
+ end++;
+
+ while (*p == ' ') { p++; }
+
+ if (*p++ != '/') {
+ return NGX_ERROR;
+ }
+
+ while (*p == ' ') { p++; }
+
+ if (*p == '*') {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "no complete length in slice response");
+ return NGX_ERROR;
+ }
+
+ if (*p < '0' || *p > '9') {
+ return NGX_ERROR;
+ }
+
+ while (*p >= '0' && *p <= '9') {
+ if (content_length >= cutoff
+ && (content_length > cutoff || *p - '0' > cutlim))
+ {
+ return NGX_ERROR;
+ }
+
+ content_length = content_length * 10 + *p++ - '0';
+ }
+
+ while (*p == ' ') { p++; }
+
+ if (*p != '\0') {
+ return NGX_ERROR;
+ }
+
+ } else { /* r->headers_out.status == NGX_HTTP_OK */
+
+ content_length = r->headers_out.content_length_n;
+
+ if (content_length == -1) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "no content length in slice response");
+ return NGX_ERROR;
+ }
+
+ start = 0;
+ end = content_length;
+ }
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http slice range: %O-%O/%O",
+ start, end, content_length);
+
+ /* make sure we received at least one byte from the range */
+
+ if (ctx->start >= 0
+ && !(ctx->start >= start && ctx->start < end))
+ {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "bad range in slice response: %O-%O", start, end);
+ return NGX_ERROR;
+ }
+
+ ctx->offset = start;
+ ctx->content_length = content_length;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_slice_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ off_t start, end;
+ u_char *p;
+ ngx_buf_t *b;
+ ngx_int_t rc;
+ ngx_chain_t *out, *cl, **ll;
+ ngx_http_request_t *sr;
+ ngx_http_slice_ctx_t *ctx, *sctx, *pctx;
+ ngx_http_slice_loc_conf_t *slcf;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_slice_filter_module);
+ if (ctx == NULL) {
+ return ngx_http_next_body_filter(r, in);
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http slice body filter");
+
+ if (r->headers_out.status != NGX_HTTP_OK
+ && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT)
+ {
+ return ngx_http_next_body_filter(r, in);
+ }
+
+ out = NULL;
+ ll = &out;
+
+ for (cl = in; cl; cl = cl->next) {
+
+ b = cl->buf;
+
+ start = ctx->offset;
+ end = ctx->offset + ngx_buf_size(b);
+
+ ctx->offset = end;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http slice body buf: %O-%O, l:%d",
+ start, end, (int) b->last_buf);
+
+ if (b->last_buf) {
+ b->last_buf = 0;
+ b->sync = 1;
+ ctx->last = 1;
+ }
+
+ if (ngx_buf_special(b)) {
+ *ll = cl;
+ ll = &cl->next;
+ continue;
+ }
+
+ if (ctx->end <= start || ctx->start >= end) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http slice body skip");
+
+ if (b->in_file) {
+ b->file_pos = b->file_last;
+ }
+
+ b->pos = b->last;
+ b->sync = 1;
+
+ continue;
+ }
+
+ if (ctx->start > start) {
+
+ if (b->in_file) {
+ b->file_pos += ctx->start - start;
+ }
+
+ if (ngx_buf_in_memory(b)) {
+ b->pos += (size_t) (ctx->start - start);
+ }
+ }
+
+ if (ctx->end <= end) {
+
+ if (b->in_file) {
+ b->file_last -= end - ctx->end;
+ }
+
+ if (ngx_buf_in_memory(b)) {
+ b->last -= (size_t) (end - ctx->end);
+ }
+
+ b->last_buf = 1;
+ *ll = cl;
+ cl->next = NULL;
+
+ break;
+ }
+
+ *ll = cl;
+ ll = &cl->next;
+ }
+
+ rc = ngx_http_next_body_filter(r, out);
+ if (rc == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (r != r->main && out) {
+ pctx = ngx_http_get_module_ctx(r->main, ngx_http_slice_filter_module);
+ if (pctx) {
+ pctx->offset = ctx->offset;
+ }
+ }
+
+ if (r != r->main
+ || !ctx->last
+ || ctx->offset >= ctx->end)
+ {
+ return rc;
+ }
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http slice next offset:%O, start:%O, end:%O",
+ ctx->offset, ctx->start, ctx->end);
+
+ if (ngx_http_subrequest(r, &r->uri, &r->args, &sr, NULL, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ sctx = ngx_pcalloc(r->pool, sizeof(ngx_http_slice_ctx_t));
+ if (sctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_set_ctx(sr, sctx, ngx_http_slice_filter_module);
+
+ sctx->start = ngx_max(ctx->start, ctx->offset);
+ sctx->end = ctx->end;
+
+ if (r->headers_out.etag) {
+ sctx->etag = r->headers_out.etag->value;
+ }
+
+ slcf = ngx_http_get_module_loc_conf(r, ngx_http_slice_filter_module);
+
+ start = slcf->size * (sctx->start / slcf->size);
+ end = start + slcf->size - 1;
+
+ p = ngx_pnalloc(r->pool, sizeof("bytes=-") - 1 + 2 * NGX_OFF_T_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ sctx->range.data = p;
+ sctx->range.len = ngx_sprintf(p, "bytes=%O-%O", start, end) - p;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http slice subrequest range: \"%V\"", &sctx->range);
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_http_slice_range_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_http_slice_ctx_t *ctx;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_slice_filter_module);
+ if (ctx == NULL) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ v->data= ctx->range.data;
+ v->valid = 1;
+ v->not_found = 0;
+ v->no_cacheable = 1;
+ v->len = ctx->range.len;
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_slice_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_slice_loc_conf_t *slcf;
+
+ slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_slice_loc_conf_t));
+ if (slcf == NULL) {
+ return NULL;
+ }
+
+ slcf->size = NGX_CONF_UNSET_SIZE;
+
+ return slcf;
+}
+
+
+static char *
+ngx_http_slice_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_slice_loc_conf_t *prev = parent;
+ ngx_http_slice_loc_conf_t *conf = child;
+
+ ngx_conf_merge_size_value(conf->size, prev->size, 0);
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_slice_add_variables(ngx_conf_t *cf)
+{
+ ngx_http_variable_t *var;
+
+ var = ngx_http_add_variable(cf, &ngx_http_slice_range_name, 0);
+ if (var == NULL) {
+ return NGX_ERROR;
+ }
+
+ var->get_handler = ngx_http_slice_range_variable;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_slice_init(ngx_conf_t *cf)
+{
+ ngx_http_handler_pt *h;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ *h = ngx_http_slice_handler;
+
+ ngx_http_next_header_filter = ngx_http_top_header_filter;
+ ngx_http_top_header_filter = ngx_http_slice_header_filter;
+
+ ngx_http_next_body_filter = ngx_http_top_body_filter;
+ ngx_http_top_body_filter = ngx_http_slice_body_filter;
+
+ return NGX_OK;
+}
diff -r 0f313cf0a1ee src/http/ngx_http_upstream.c
--- a/src/http/ngx_http_upstream.c Tue Sep 22 17:36:22 2015 +0300
+++ b/src/http/ngx_http_upstream.c Mon Sep 28 17:07:15 2015 +0300
@@ -291,6 +291,11 @@ ngx_http_upstream_header_t ngx_http_ups
ngx_http_upstream_process_transfer_encoding, 0,
ngx_http_upstream_ignore_header_line, 0, 0 },
+ { ngx_string("Content-Range"),
+ ngx_http_upstream_ignore_header_line, 0,
+ ngx_http_upstream_copy_header_line,
+ offsetof(ngx_http_headers_out_t, content_range), 0 },
+
#if (NGX_HTTP_GZIP)
{ ngx_string("Content-Encoding"),
ngx_http_upstream_process_header_line,
More information about the nginx-devel
mailing list