# HG changeset patch # User Maxim Dounin # Date 1214441362 -14400 # Node ID 8aec31ab4d5238c1f306fcfc48d61ebd5f68ba56 Bytes filter module skeleton, currently does nothing. Bytes module designed to return only specific range of file as requested in arguments. It's intended to replace flv module as more generic alternative. diff --git a/config b/config new file mode 100644 --- /dev/null +++ b/config @@ -0,0 +1,10 @@ +# (C) Maxim Dounin +# Configuration for ngx_http_bytes_filter_module. + +ngx_addon_name="ngx_http_bytes_filter_module" + +HTTP_RANGE_BODY_FILTER_MODULE="$HTTP_RANGE_BODY_FILTER_MODULE \ + ngx_http_bytes_filter_module" + +NGX_ADDON_SRCS="$NGX_ADDON_SRCS \ + $ngx_addon_dir/ngx_http_bytes_filter_module.c" diff --git a/ngx_http_bytes_filter_module.c b/ngx_http_bytes_filter_module.c new file mode 100644 --- /dev/null +++ b/ngx_http_bytes_filter_module.c @@ -0,0 +1,219 @@ + +/* + * Copyright (C) Maxim Dounin + */ + +#include +#include +#include + + +typedef struct { + ngx_flag_t enable; +} ngx_http_bytes_conf_t; + + +typedef struct { + off_t start; + off_t end; +} ngx_http_bytes_t; + + +typedef struct { + off_t offset; + ngx_array_t ranges; +} ngx_http_bytes_ctx_t; + + +static void *ngx_http_bytes_create_conf(ngx_conf_t *cf); +static char *ngx_http_bytes_merge_conf(ngx_conf_t *cf, void *parent, + void *child); +static ngx_int_t ngx_http_bytes_init(ngx_conf_t *cf); + + +static ngx_command_t ngx_http_bytes_commands[] = { + + { ngx_string("bytes"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_bytes_conf_t, enable), + NULL }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_bytes_module_ctx = { + NULL, /* preconfiguration */ + ngx_http_bytes_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_bytes_create_conf, /* create location configuration */ + ngx_http_bytes_merge_conf /* merge location configuration */ +}; + + +ngx_module_t ngx_http_bytes_filter_module = { + NGX_MODULE_V1, + &ngx_http_bytes_module_ctx, /* module context */ + ngx_http_bytes_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_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_bytes_header_filter(ngx_http_request_t *r) +{ + u_char *p; + ngx_http_bytes_conf_t *conf; + ngx_http_bytes_ctx_t *ctx; + + conf = ngx_http_get_module_loc_conf(r, ngx_http_bytes_filter_module); + + if (!conf->enable || r->args.len == 0) { + return ngx_http_next_header_filter(r); + } + + p = (u_char *) ngx_strnstr(r->args.data, "bytes=", r->args.len); + + if (p == NULL) { + return ngx_http_next_header_filter(r); + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "bytes header filter: r %p", r); + + p += sizeof("bytes=") - 1; + + /* create context */ + + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_bytes_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; + } + + if (ngx_array_init(&ctx->ranges, r->pool, 1, sizeof(ngx_http_bytes_t)) + == NGX_ERROR) + { + return NGX_ERROR; + } + + /* + * bytes= contain ranges compatible with RFC 2616, "14.35.1 Byte Ranges", + * but no whitespaces permitted + */ + +#if 0 + for ( ;; ) { + start = 0; + end = 0; + suffix = 0; + + if (*p != '-') { + if (*p < '0' || *p > '9') { + break; + } + + while (*p >= '0' && *p <= '9') { + start = start * 10 + *p++ - '0'; + } + + if (*p != '-') { + break; + } + + if (*p == ',' || *p == '\0') { + /* no last-byte-pos, assume end of file */ + end = len - 1; + } + + } else { + suffix = 1; + p++; + } + } +#endif + + /* ... */ + + ngx_http_set_ctx(r, ctx, ngx_http_bytes_filter_module); + + return ngx_http_next_header_filter(r); +} + + +static ngx_int_t +ngx_http_bytes_body_filter(ngx_http_request_t *r, ngx_chain_t *in) +{ + ngx_http_bytes_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_bytes_filter_module); + + if (ctx == NULL) { + return ngx_http_next_body_filter(r, in); + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "bytes body filter: r %p, in %p", r, in); + + return ngx_http_next_body_filter(r, in); +} + + +static void * +ngx_http_bytes_create_conf(ngx_conf_t *cf) +{ + ngx_http_bytes_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_bytes_conf_t)); + if (conf == NULL) { + return NGX_CONF_ERROR; + } + + conf->enable = NGX_CONF_UNSET; + + return conf; +} + + +static char * +ngx_http_bytes_merge_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_http_bytes_conf_t *prev = parent; + ngx_http_bytes_conf_t *conf = child; + + ngx_conf_merge_value(conf->enable, prev->enable, 0); + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_http_bytes_init(ngx_conf_t *cf) +{ + ngx_http_next_header_filter = ngx_http_top_header_filter; + ngx_http_top_header_filter = ngx_http_bytes_header_filter; + + ngx_http_next_body_filter = ngx_http_top_body_filter; + ngx_http_top_body_filter = ngx_http_bytes_body_filter; + + return NGX_OK; +}