diff options
author | Konstantin Ananyev <konstantin.ananyev@intel.com> | 2017-10-31 12:40:17 +0000 |
---|---|---|
committer | Konstantin Ananyev <konstantin.ananyev@intel.com> | 2017-10-31 14:19:39 +0000 |
commit | e18a033b921d0d79fa8278f853548e6125b93e0c (patch) | |
tree | a6a55edf6ddceef824561818c9836914c326340d /app/nginx/src/http/modules/ngx_http_sub_filter_module.c | |
parent | 7e18fa1bf263822c46d7431a911b41d6377d5f69 (diff) |
Integrate TLDK with NGINX
Created a clone of nginx (from https://github.com/nginx/nginx)
to demonstrate and benchmark TLDK library integrated with real
world application.
A new nginx module is created and and BSD socket-like API is implemented
on top of native TLDK API.
Note, that right now only minimalistic subset of socket-like API is provided:
- accept
- close
- readv
- recv
- writev
so only limited nginx functionality is available for a moment.
Change-Id: Ie1efe9349a0538da4348a48fb8306cbf636b5a92
Signed-off-by: Mohammad Abdul Awal <mohammad.abdul.awal@intel.com>
Signed-off-by: Reshma Pattan <reshma.pattan@intel.com>
Signed-off-by: Remy Horton <remy.horton@intel.com>
Signed-off-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
Diffstat (limited to 'app/nginx/src/http/modules/ngx_http_sub_filter_module.c')
-rw-r--r-- | app/nginx/src/http/modules/ngx_http_sub_filter_module.c | 1018 |
1 files changed, 1018 insertions, 0 deletions
diff --git a/app/nginx/src/http/modules/ngx_http_sub_filter_module.c b/app/nginx/src/http/modules/ngx_http_sub_filter_module.c new file mode 100644 index 0000000..de58c6f --- /dev/null +++ b/app/nginx/src/http/modules/ngx_http_sub_filter_module.c @@ -0,0 +1,1018 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include <ngx_config.h> +#include <ngx_core.h> +#include <ngx_http.h> + + +typedef struct { + ngx_http_complex_value_t match; + ngx_http_complex_value_t value; +} ngx_http_sub_pair_t; + + +typedef struct { + ngx_str_t match; + ngx_http_complex_value_t *value; +} ngx_http_sub_match_t; + + +typedef struct { + ngx_uint_t min_match_len; + ngx_uint_t max_match_len; + + u_char index[257]; + u_char shift[256]; +} ngx_http_sub_tables_t; + + +typedef struct { + ngx_uint_t dynamic; /* unsigned dynamic:1; */ + + ngx_array_t *pairs; + + ngx_http_sub_tables_t *tables; + + ngx_hash_t types; + + ngx_flag_t once; + ngx_flag_t last_modified; + + ngx_array_t *types_keys; + ngx_array_t *matches; +} ngx_http_sub_loc_conf_t; + + +typedef struct { + ngx_str_t saved; + ngx_str_t looked; + + ngx_uint_t once; /* unsigned once:1 */ + + ngx_buf_t *buf; + + u_char *pos; + u_char *copy_start; + u_char *copy_end; + + ngx_chain_t *in; + ngx_chain_t *out; + ngx_chain_t **last_out; + ngx_chain_t *busy; + ngx_chain_t *free; + + ngx_str_t *sub; + ngx_uint_t applied; + + ngx_int_t offset; + ngx_uint_t index; + + ngx_http_sub_tables_t *tables; + ngx_array_t *matches; +} ngx_http_sub_ctx_t; + + +static ngx_uint_t ngx_http_sub_cmp_index; + + +static ngx_int_t ngx_http_sub_output(ngx_http_request_t *r, + ngx_http_sub_ctx_t *ctx); +static ngx_int_t ngx_http_sub_parse(ngx_http_request_t *r, + ngx_http_sub_ctx_t *ctx, ngx_uint_t flush); +static ngx_int_t ngx_http_sub_match(ngx_http_sub_ctx_t *ctx, ngx_int_t start, + ngx_str_t *m); + +static char * ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static void *ngx_http_sub_create_conf(ngx_conf_t *cf); +static char *ngx_http_sub_merge_conf(ngx_conf_t *cf, + void *parent, void *child); +static void ngx_http_sub_init_tables(ngx_http_sub_tables_t *tables, + ngx_http_sub_match_t *match, ngx_uint_t n); +static ngx_int_t ngx_http_sub_cmp_matches(const void *one, const void *two); +static ngx_int_t ngx_http_sub_filter_init(ngx_conf_t *cf); + + +static ngx_command_t ngx_http_sub_filter_commands[] = { + + { ngx_string("sub_filter"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, + ngx_http_sub_filter, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("sub_filter_types"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, + ngx_http_types_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_sub_loc_conf_t, types_keys), + &ngx_http_html_default_types[0] }, + + { ngx_string("sub_filter_once"), + 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_sub_loc_conf_t, once), + NULL }, + + { ngx_string("sub_filter_last_modified"), + 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_sub_loc_conf_t, last_modified), + NULL }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_sub_filter_module_ctx = { + NULL, /* preconfiguration */ + ngx_http_sub_filter_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_sub_create_conf, /* create location configuration */ + ngx_http_sub_merge_conf /* merge location configuration */ +}; + + +ngx_module_t ngx_http_sub_filter_module = { + NGX_MODULE_V1, + &ngx_http_sub_filter_module_ctx, /* module context */ + ngx_http_sub_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_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_sub_header_filter(ngx_http_request_t *r) +{ + ngx_str_t *m; + ngx_uint_t i, j, n; + ngx_http_sub_ctx_t *ctx; + ngx_http_sub_pair_t *pairs; + ngx_http_sub_match_t *matches; + ngx_http_sub_loc_conf_t *slcf; + + slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module); + + if (slcf->pairs == NULL + || r->headers_out.content_length_n == 0 + || ngx_http_test_content_type(r, &slcf->types) == NULL) + { + return ngx_http_next_header_filter(r); + } + + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_sub_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; + } + + if (slcf->dynamic == 0) { + ctx->tables = slcf->tables; + ctx->matches = slcf->matches; + + } else { + pairs = slcf->pairs->elts; + n = slcf->pairs->nelts; + + matches = ngx_pcalloc(r->pool, sizeof(ngx_http_sub_match_t) * n); + if (matches == NULL) { + return NGX_ERROR; + } + + j = 0; + for (i = 0; i < n; i++) { + matches[j].value = &pairs[i].value; + + if (pairs[i].match.lengths == NULL) { + matches[j].match = pairs[i].match.value; + j++; + continue; + } + + m = &matches[j].match; + if (ngx_http_complex_value(r, &pairs[i].match, m) != NGX_OK) { + return NGX_ERROR; + } + + if (m->len == 0) { + continue; + } + + ngx_strlow(m->data, m->data, m->len); + j++; + } + + if (j == 0) { + return ngx_http_next_header_filter(r); + } + + ctx->matches = ngx_palloc(r->pool, sizeof(ngx_array_t)); + if (ctx->matches == NULL) { + return NGX_ERROR; + } + + ctx->matches->elts = matches; + ctx->matches->nelts = j; + + ctx->tables = ngx_palloc(r->pool, sizeof(ngx_http_sub_tables_t)); + if (ctx->tables == NULL) { + return NGX_ERROR; + } + + ngx_http_sub_init_tables(ctx->tables, ctx->matches->elts, + ctx->matches->nelts); + } + + ngx_http_set_ctx(r, ctx, ngx_http_sub_filter_module); + + ctx->saved.data = ngx_pnalloc(r->pool, ctx->tables->max_match_len - 1); + if (ctx->saved.data == NULL) { + return NGX_ERROR; + } + + ctx->looked.data = ngx_pnalloc(r->pool, ctx->tables->max_match_len - 1); + if (ctx->looked.data == NULL) { + return NGX_ERROR; + } + + ctx->offset = ctx->tables->min_match_len - 1; + ctx->last_out = &ctx->out; + + r->filter_need_in_memory = 1; + + if (r == r->main) { + ngx_http_clear_content_length(r); + + if (!slcf->last_modified) { + ngx_http_clear_last_modified(r); + ngx_http_clear_etag(r); + + } else { + ngx_http_weak_etag(r); + } + } + + return ngx_http_next_header_filter(r); +} + + +static ngx_int_t +ngx_http_sub_body_filter(ngx_http_request_t *r, ngx_chain_t *in) +{ + ngx_int_t rc; + ngx_buf_t *b; + ngx_str_t *sub; + ngx_uint_t flush, last; + ngx_chain_t *cl; + ngx_http_sub_ctx_t *ctx; + ngx_http_sub_match_t *match; + ngx_http_sub_loc_conf_t *slcf; + + ctx = ngx_http_get_module_ctx(r, ngx_http_sub_filter_module); + + if (ctx == NULL) { + return ngx_http_next_body_filter(r, in); + } + + if ((in == NULL + && ctx->buf == NULL + && ctx->in == NULL + && ctx->busy == NULL)) + { + return ngx_http_next_body_filter(r, in); + } + + if (ctx->once && (ctx->buf == NULL || ctx->in == NULL)) { + + if (ctx->busy) { + if (ngx_http_sub_output(r, ctx) == NGX_ERROR) { + return NGX_ERROR; + } + } + + return ngx_http_next_body_filter(r, in); + } + + /* add the incoming chain to the chain ctx->in */ + + if (in) { + if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) { + return NGX_ERROR; + } + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http sub filter \"%V\"", &r->uri); + + flush = 0; + last = 0; + + while (ctx->in || ctx->buf) { + + if (ctx->buf == NULL) { + ctx->buf = ctx->in->buf; + ctx->in = ctx->in->next; + ctx->pos = ctx->buf->pos; + } + + if (ctx->buf->flush || ctx->buf->recycled) { + flush = 1; + } + + if (ctx->in == NULL) { + last = flush; + } + + b = NULL; + + while (ctx->pos < ctx->buf->last) { + + rc = ngx_http_sub_parse(r, ctx, last); + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "parse: %i, looked: \"%V\" %p-%p", + rc, &ctx->looked, ctx->copy_start, ctx->copy_end); + + if (rc == NGX_ERROR) { + return rc; + } + + if (ctx->saved.len) { + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "saved: \"%V\"", &ctx->saved); + + cl = ngx_chain_get_free_buf(r->pool, &ctx->free); + if (cl == NULL) { + return NGX_ERROR; + } + + b = cl->buf; + + ngx_memzero(b, sizeof(ngx_buf_t)); + + b->pos = ngx_pnalloc(r->pool, ctx->saved.len); + if (b->pos == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(b->pos, ctx->saved.data, ctx->saved.len); + b->last = b->pos + ctx->saved.len; + b->memory = 1; + + *ctx->last_out = cl; + ctx->last_out = &cl->next; + + ctx->saved.len = 0; + } + + if (ctx->copy_start != ctx->copy_end) { + + cl = ngx_chain_get_free_buf(r->pool, &ctx->free); + if (cl == NULL) { + return NGX_ERROR; + } + + b = cl->buf; + + ngx_memcpy(b, ctx->buf, sizeof(ngx_buf_t)); + + b->pos = ctx->copy_start; + b->last = ctx->copy_end; + b->shadow = NULL; + b->last_buf = 0; + b->last_in_chain = 0; + b->recycled = 0; + + if (b->in_file) { + b->file_last = b->file_pos + (b->last - ctx->buf->pos); + b->file_pos += b->pos - ctx->buf->pos; + } + + *ctx->last_out = cl; + ctx->last_out = &cl->next; + } + + if (rc == NGX_AGAIN) { + continue; + } + + + /* rc == NGX_OK */ + + cl = ngx_chain_get_free_buf(r->pool, &ctx->free); + if (cl == NULL) { + return NGX_ERROR; + } + + b = cl->buf; + + ngx_memzero(b, sizeof(ngx_buf_t)); + + slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module); + + if (ctx->sub == NULL) { + ctx->sub = ngx_pcalloc(r->pool, sizeof(ngx_str_t) + * ctx->matches->nelts); + if (ctx->sub == NULL) { + return NGX_ERROR; + } + } + + sub = &ctx->sub[ctx->index]; + + if (sub->data == NULL) { + match = ctx->matches->elts; + + if (ngx_http_complex_value(r, match[ctx->index].value, sub) + != NGX_OK) + { + return NGX_ERROR; + } + } + + if (sub->len) { + b->memory = 1; + b->pos = sub->data; + b->last = sub->data + sub->len; + + } else { + b->sync = 1; + } + + *ctx->last_out = cl; + ctx->last_out = &cl->next; + + ctx->index = 0; + ctx->once = slcf->once && (++ctx->applied == ctx->matches->nelts); + + continue; + } + + if (ctx->looked.len + && (ctx->buf->last_buf || ctx->buf->last_in_chain)) + { + cl = ngx_chain_get_free_buf(r->pool, &ctx->free); + if (cl == NULL) { + return NGX_ERROR; + } + + b = cl->buf; + + ngx_memzero(b, sizeof(ngx_buf_t)); + + b->pos = ctx->looked.data; + b->last = b->pos + ctx->looked.len; + b->memory = 1; + + *ctx->last_out = cl; + ctx->last_out = &cl->next; + + ctx->looked.len = 0; + } + + if (ctx->buf->last_buf || ctx->buf->flush || ctx->buf->sync + || ngx_buf_in_memory(ctx->buf)) + { + if (b == NULL) { + cl = ngx_chain_get_free_buf(r->pool, &ctx->free); + if (cl == NULL) { + return NGX_ERROR; + } + + b = cl->buf; + + ngx_memzero(b, sizeof(ngx_buf_t)); + + b->sync = 1; + + *ctx->last_out = cl; + ctx->last_out = &cl->next; + } + + b->last_buf = ctx->buf->last_buf; + b->last_in_chain = ctx->buf->last_in_chain; + b->flush = ctx->buf->flush; + b->shadow = ctx->buf; + + b->recycled = ctx->buf->recycled; + } + + ctx->buf = NULL; + } + + if (ctx->out == NULL && ctx->busy == NULL) { + return NGX_OK; + } + + return ngx_http_sub_output(r, ctx); +} + + +static ngx_int_t +ngx_http_sub_output(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx) +{ + ngx_int_t rc; + ngx_buf_t *b; + ngx_chain_t *cl; + +#if 1 + b = NULL; + for (cl = ctx->out; cl; cl = cl->next) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "sub out: %p %p", cl->buf, cl->buf->pos); + if (cl->buf == b) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "the same buf was used in sub"); + ngx_debug_point(); + return NGX_ERROR; + } + b = cl->buf; + } +#endif + + rc = ngx_http_next_body_filter(r, ctx->out); + + if (ctx->busy == NULL) { + ctx->busy = ctx->out; + + } else { + for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ } + cl->next = ctx->out; + } + + ctx->out = NULL; + ctx->last_out = &ctx->out; + + while (ctx->busy) { + + cl = ctx->busy; + b = cl->buf; + + if (ngx_buf_size(b) != 0) { + break; + } + + if (b->shadow) { + b->shadow->pos = b->shadow->last; + } + + ctx->busy = cl->next; + + if (ngx_buf_in_memory(b) || b->in_file) { + /* add data bufs only to the free buf chain */ + + cl->next = ctx->free; + ctx->free = cl; + } + } + + if (ctx->in || ctx->buf) { + r->buffered |= NGX_HTTP_SUB_BUFFERED; + + } else { + r->buffered &= ~NGX_HTTP_SUB_BUFFERED; + } + + return rc; +} + + +static ngx_int_t +ngx_http_sub_parse(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx, + ngx_uint_t flush) +{ + u_char *p, c; + ngx_str_t *m; + ngx_int_t offset, start, next, end, len, rc; + ngx_uint_t shift, i, j; + ngx_http_sub_match_t *match; + ngx_http_sub_tables_t *tables; + ngx_http_sub_loc_conf_t *slcf; + + slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module); + tables = ctx->tables; + match = ctx->matches->elts; + + offset = ctx->offset; + end = ctx->buf->last - ctx->pos; + + if (ctx->once) { + /* sets start and next to end */ + offset = end + (ngx_int_t) tables->min_match_len - 1; + goto again; + } + + while (offset < end) { + + c = offset < 0 ? ctx->looked.data[ctx->looked.len + offset] + : ctx->pos[offset]; + + c = ngx_tolower(c); + + shift = tables->shift[c]; + if (shift > 0) { + offset += shift; + continue; + } + + /* a potential match */ + + start = offset - (ngx_int_t) tables->min_match_len + 1; + + i = ngx_max((ngx_uint_t) tables->index[c], ctx->index); + j = tables->index[c + 1]; + + while (i != j) { + + if (slcf->once && ctx->sub && ctx->sub[i].data) { + goto next; + } + + m = &match[i].match; + + rc = ngx_http_sub_match(ctx, start, m); + + if (rc == NGX_DECLINED) { + goto next; + } + + ctx->index = i; + + if (rc == NGX_AGAIN) { + goto again; + } + + ctx->offset = offset + (ngx_int_t) m->len; + next = start + (ngx_int_t) m->len; + end = ngx_max(next, 0); + rc = NGX_OK; + + goto done; + + next: + + i++; + } + + offset++; + ctx->index = 0; + } + + if (flush) { + for ( ;; ) { + start = offset - (ngx_int_t) tables->min_match_len + 1; + + if (start >= end) { + break; + } + + for (i = 0; i < ctx->matches->nelts; i++) { + m = &match[i].match; + + if (ngx_http_sub_match(ctx, start, m) == NGX_AGAIN) { + goto again; + } + } + + offset++; + } + } + +again: + + ctx->offset = offset; + start = offset - (ngx_int_t) tables->min_match_len + 1; + next = start; + rc = NGX_AGAIN; + +done: + + /* send [ - looked.len, start ] to client */ + + ctx->saved.len = ctx->looked.len + ngx_min(start, 0); + ngx_memcpy(ctx->saved.data, ctx->looked.data, ctx->saved.len); + + ctx->copy_start = ctx->pos; + ctx->copy_end = ctx->pos + ngx_max(start, 0); + + /* save [ next, end ] in looked */ + + len = ngx_min(next, 0); + p = ctx->looked.data; + p = ngx_movemem(p, p + ctx->looked.len + len, - len); + + len = ngx_max(next, 0); + p = ngx_cpymem(p, ctx->pos + len, end - len); + ctx->looked.len = p - ctx->looked.data; + + /* update position */ + + ctx->pos += end; + ctx->offset -= end; + + return rc; +} + + +static ngx_int_t +ngx_http_sub_match(ngx_http_sub_ctx_t *ctx, ngx_int_t start, ngx_str_t *m) +{ + u_char *p, *last, *pat, *pat_end; + + pat = m->data; + pat_end = m->data + m->len; + + if (start >= 0) { + p = ctx->pos + start; + + } else { + last = ctx->looked.data + ctx->looked.len; + p = last + start; + + while (p < last && pat < pat_end) { + if (ngx_tolower(*p) != *pat) { + return NGX_DECLINED; + } + + p++; + pat++; + } + + p = ctx->pos; + } + + while (p < ctx->buf->last && pat < pat_end) { + if (ngx_tolower(*p) != *pat) { + return NGX_DECLINED; + } + + p++; + pat++; + } + + if (pat != pat_end) { + /* partial match */ + return NGX_AGAIN; + } + + return NGX_OK; +} + + +static char * +ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_sub_loc_conf_t *slcf = conf; + + ngx_str_t *value; + ngx_http_sub_pair_t *pair; + ngx_http_compile_complex_value_t ccv; + + value = cf->args->elts; + + if (value[1].len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty search pattern"); + return NGX_CONF_ERROR; + } + + if (slcf->pairs == NULL) { + slcf->pairs = ngx_array_create(cf->pool, 1, + sizeof(ngx_http_sub_pair_t)); + if (slcf->pairs == NULL) { + return NGX_CONF_ERROR; + } + } + + if (slcf->pairs->nelts == 255) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "number of search patterns exceeds 255"); + return NGX_CONF_ERROR; + } + + ngx_strlow(value[1].data, value[1].data, value[1].len); + + pair = ngx_array_push(slcf->pairs); + if (pair == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &pair->match; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (ccv.complex_value->lengths != NULL) { + slcf->dynamic = 1; + + } else { + ngx_strlow(pair->match.value.data, pair->match.value.data, + pair->match.value.len); + } + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[2]; + ccv.complex_value = &pair->value; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static void * +ngx_http_sub_create_conf(ngx_conf_t *cf) +{ + ngx_http_sub_loc_conf_t *slcf; + + slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_sub_loc_conf_t)); + if (slcf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * conf->dynamic = 0; + * conf->pairs = NULL; + * conf->tables = NULL; + * conf->types = { NULL }; + * conf->types_keys = NULL; + * conf->matches = NULL; + */ + + slcf->once = NGX_CONF_UNSET; + slcf->last_modified = NGX_CONF_UNSET; + + return slcf; +} + + +static char * +ngx_http_sub_merge_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_uint_t i, n; + ngx_http_sub_pair_t *pairs; + ngx_http_sub_match_t *matches; + ngx_http_sub_loc_conf_t *prev = parent; + ngx_http_sub_loc_conf_t *conf = child; + + ngx_conf_merge_value(conf->once, prev->once, 1); + ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0); + + if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types, + &prev->types_keys, &prev->types, + ngx_http_html_default_types) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + if (conf->pairs == NULL) { + conf->dynamic = prev->dynamic; + conf->pairs = prev->pairs; + conf->matches = prev->matches; + conf->tables = prev->tables; + } + + if (conf->pairs && conf->dynamic == 0 && conf->tables == NULL) { + pairs = conf->pairs->elts; + n = conf->pairs->nelts; + + matches = ngx_palloc(cf->pool, sizeof(ngx_http_sub_match_t) * n); + if (matches == NULL) { + return NGX_CONF_ERROR; + } + + for (i = 0; i < n; i++) { + matches[i].match = pairs[i].match.value; + matches[i].value = &pairs[i].value; + } + + conf->matches = ngx_palloc(cf->pool, sizeof(ngx_array_t)); + if (conf->matches == NULL) { + return NGX_CONF_ERROR; + } + + conf->matches->elts = matches; + conf->matches->nelts = n; + + conf->tables = ngx_palloc(cf->pool, sizeof(ngx_http_sub_tables_t)); + if (conf->tables == NULL) { + return NGX_CONF_ERROR; + } + + ngx_http_sub_init_tables(conf->tables, conf->matches->elts, + conf->matches->nelts); + } + + return NGX_CONF_OK; +} + + +static void +ngx_http_sub_init_tables(ngx_http_sub_tables_t *tables, + ngx_http_sub_match_t *match, ngx_uint_t n) +{ + u_char c; + ngx_uint_t i, j, min, max, ch; + + min = match[0].match.len; + max = match[0].match.len; + + for (i = 1; i < n; i++) { + min = ngx_min(min, match[i].match.len); + max = ngx_max(max, match[i].match.len); + } + + tables->min_match_len = min; + tables->max_match_len = max; + + ngx_http_sub_cmp_index = tables->min_match_len - 1; + ngx_sort(match, n, sizeof(ngx_http_sub_match_t), ngx_http_sub_cmp_matches); + + min = ngx_min(min, 255); + ngx_memset(tables->shift, min, 256); + + ch = 0; + + for (i = 0; i < n; i++) { + + for (j = 0; j < min; j++) { + c = match[i].match.data[tables->min_match_len - 1 - j]; + tables->shift[c] = ngx_min(tables->shift[c], (u_char) j); + } + + c = match[i].match.data[tables->min_match_len - 1]; + while (ch <= (ngx_uint_t) c) { + tables->index[ch++] = (u_char) i; + } + } + + while (ch < 257) { + tables->index[ch++] = (u_char) n; + } +} + + +static ngx_int_t +ngx_http_sub_cmp_matches(const void *one, const void *two) +{ + ngx_int_t c1, c2; + ngx_http_sub_match_t *first, *second; + + first = (ngx_http_sub_match_t *) one; + second = (ngx_http_sub_match_t *) two; + + c1 = first->match.data[ngx_http_sub_cmp_index]; + c2 = second->match.data[ngx_http_sub_cmp_index]; + + return c1 - c2; +} + + +static ngx_int_t +ngx_http_sub_filter_init(ngx_conf_t *cf) +{ + ngx_http_next_header_filter = ngx_http_top_header_filter; + ngx_http_top_header_filter = ngx_http_sub_header_filter; + + ngx_http_next_body_filter = ngx_http_top_body_filter; + ngx_http_top_body_filter = ngx_http_sub_body_filter; + + return NGX_OK; +} |