Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.13.12 ]​[ nginx-1.12.2 ]​

0001 
0002 /*
0003  * Copyright (C) Igor Sysoev
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_http.h>
0011 
0012 
0013 typedef struct {
0014     ngx_http_complex_value_t   match;
0015     ngx_http_complex_value_t   value;
0016 } ngx_http_sub_pair_t;
0017 
0018 
0019 typedef struct {
0020     ngx_str_t                  match;
0021     ngx_http_complex_value_t  *value;
0022 } ngx_http_sub_match_t;
0023 
0024 
0025 typedef struct {
0026     ngx_uint_t                 min_match_len;
0027     ngx_uint_t                 max_match_len;
0028 
0029     u_char                     index[257];
0030     u_char                     shift[256];
0031 } ngx_http_sub_tables_t;
0032 
0033 
0034 typedef struct {
0035     ngx_uint_t                 dynamic; /* unsigned dynamic:1; */
0036 
0037     ngx_array_t               *pairs;
0038 
0039     ngx_http_sub_tables_t     *tables;
0040 
0041     ngx_hash_t                 types;
0042 
0043     ngx_flag_t                 once;
0044     ngx_flag_t                 last_modified;
0045 
0046     ngx_array_t               *types_keys;
0047     ngx_array_t               *matches;
0048 } ngx_http_sub_loc_conf_t;
0049 
0050 
0051 typedef struct {
0052     ngx_str_t                  saved;
0053     ngx_str_t                  looked;
0054 
0055     ngx_uint_t                 once;   /* unsigned  once:1 */
0056 
0057     ngx_buf_t                 *buf;
0058 
0059     u_char                    *pos;
0060     u_char                    *copy_start;
0061     u_char                    *copy_end;
0062 
0063     ngx_chain_t               *in;
0064     ngx_chain_t               *out;
0065     ngx_chain_t              **last_out;
0066     ngx_chain_t               *busy;
0067     ngx_chain_t               *free;
0068 
0069     ngx_str_t                 *sub;
0070     ngx_uint_t                 applied;
0071 
0072     ngx_int_t                  offset;
0073     ngx_uint_t                 index;
0074 
0075     ngx_http_sub_tables_t     *tables;
0076     ngx_array_t               *matches;
0077 } ngx_http_sub_ctx_t;
0078 
0079 
0080 static ngx_uint_t ngx_http_sub_cmp_index;
0081 
0082 
0083 static ngx_int_t ngx_http_sub_output(ngx_http_request_t *r,
0084     ngx_http_sub_ctx_t *ctx);
0085 static ngx_int_t ngx_http_sub_parse(ngx_http_request_t *r,
0086     ngx_http_sub_ctx_t *ctx, ngx_uint_t flush);
0087 static ngx_int_t ngx_http_sub_match(ngx_http_sub_ctx_t *ctx, ngx_int_t start,
0088     ngx_str_t *m);
0089 
0090 static char * ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd,
0091     void *conf);
0092 static void *ngx_http_sub_create_conf(ngx_conf_t *cf);
0093 static char *ngx_http_sub_merge_conf(ngx_conf_t *cf,
0094     void *parent, void *child);
0095 static void ngx_http_sub_init_tables(ngx_http_sub_tables_t *tables,
0096     ngx_http_sub_match_t *match, ngx_uint_t n);
0097 static ngx_int_t ngx_http_sub_cmp_matches(const void *one, const void *two);
0098 static ngx_int_t ngx_http_sub_filter_init(ngx_conf_t *cf);
0099 
0100 
0101 static ngx_command_t  ngx_http_sub_filter_commands[] = {
0102 
0103     { ngx_string("sub_filter"),
0104       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
0105       ngx_http_sub_filter,
0106       NGX_HTTP_LOC_CONF_OFFSET,
0107       0,
0108       NULL },
0109 
0110     { ngx_string("sub_filter_types"),
0111       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
0112       ngx_http_types_slot,
0113       NGX_HTTP_LOC_CONF_OFFSET,
0114       offsetof(ngx_http_sub_loc_conf_t, types_keys),
0115       &ngx_http_html_default_types[0] },
0116 
0117     { ngx_string("sub_filter_once"),
0118       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
0119       ngx_conf_set_flag_slot,
0120       NGX_HTTP_LOC_CONF_OFFSET,
0121       offsetof(ngx_http_sub_loc_conf_t, once),
0122       NULL },
0123 
0124     { ngx_string("sub_filter_last_modified"),
0125       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
0126       ngx_conf_set_flag_slot,
0127       NGX_HTTP_LOC_CONF_OFFSET,
0128       offsetof(ngx_http_sub_loc_conf_t, last_modified),
0129       NULL },
0130 
0131       ngx_null_command
0132 };
0133 
0134 
0135 static ngx_http_module_t  ngx_http_sub_filter_module_ctx = {
0136     NULL,                                  /* preconfiguration */
0137     ngx_http_sub_filter_init,              /* postconfiguration */
0138 
0139     NULL,                                  /* create main configuration */
0140     NULL,                                  /* init main configuration */
0141 
0142     NULL,                                  /* create server configuration */
0143     NULL,                                  /* merge server configuration */
0144 
0145     ngx_http_sub_create_conf,              /* create location configuration */
0146     ngx_http_sub_merge_conf                /* merge location configuration */
0147 };
0148 
0149 
0150 ngx_module_t  ngx_http_sub_filter_module = {
0151     NGX_MODULE_V1,
0152     &ngx_http_sub_filter_module_ctx,       /* module context */
0153     ngx_http_sub_filter_commands,          /* module directives */
0154     NGX_HTTP_MODULE,                       /* module type */
0155     NULL,                                  /* init master */
0156     NULL,                                  /* init module */
0157     NULL,                                  /* init process */
0158     NULL,                                  /* init thread */
0159     NULL,                                  /* exit thread */
0160     NULL,                                  /* exit process */
0161     NULL,                                  /* exit master */
0162     NGX_MODULE_V1_PADDING
0163 };
0164 
0165 
0166 static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
0167 static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
0168 
0169 
0170 static ngx_int_t
0171 ngx_http_sub_header_filter(ngx_http_request_t *r)
0172 {
0173     ngx_str_t                *m;
0174     ngx_uint_t                i, j, n;
0175     ngx_http_sub_ctx_t       *ctx;
0176     ngx_http_sub_pair_t      *pairs;
0177     ngx_http_sub_match_t     *matches;
0178     ngx_http_sub_loc_conf_t  *slcf;
0179 
0180     slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module);
0181 
0182     if (slcf->pairs == NULL
0183         || r->headers_out.content_length_n == 0
0184         || ngx_http_test_content_type(r, &slcf->types) == NULL)
0185     {
0186         return ngx_http_next_header_filter(r);
0187     }
0188 
0189     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_sub_ctx_t));
0190     if (ctx == NULL) {
0191         return NGX_ERROR;
0192     }
0193 
0194     if (slcf->dynamic == 0) {
0195         ctx->tables = slcf->tables;
0196         ctx->matches = slcf->matches;
0197 
0198     } else {
0199         pairs = slcf->pairs->elts;
0200         n = slcf->pairs->nelts;
0201 
0202         matches = ngx_pcalloc(r->pool, sizeof(ngx_http_sub_match_t) * n);
0203         if (matches == NULL) {
0204             return NGX_ERROR;
0205         }
0206 
0207         j = 0;
0208         for (i = 0; i < n; i++) {
0209             matches[j].value = &pairs[i].value;
0210 
0211             if (pairs[i].match.lengths == NULL) {
0212                 matches[j].match = pairs[i].match.value;
0213                 j++;
0214                 continue;
0215             }
0216 
0217             m = &matches[j].match;
0218             if (ngx_http_complex_value(r, &pairs[i].match, m) != NGX_OK) {
0219                 return NGX_ERROR;
0220             }
0221 
0222             if (m->len == 0) {
0223                 continue;
0224             }
0225 
0226             ngx_strlow(m->data, m->data, m->len);
0227             j++;
0228         }
0229 
0230         if (j == 0) {
0231             return ngx_http_next_header_filter(r);
0232         }
0233 
0234         ctx->matches = ngx_palloc(r->pool, sizeof(ngx_array_t));
0235         if (ctx->matches == NULL) {
0236             return NGX_ERROR;
0237         }
0238 
0239         ctx->matches->elts = matches;
0240         ctx->matches->nelts = j;
0241 
0242         ctx->tables = ngx_palloc(r->pool, sizeof(ngx_http_sub_tables_t));
0243         if (ctx->tables == NULL) {
0244             return NGX_ERROR;
0245         }
0246 
0247         ngx_http_sub_init_tables(ctx->tables, ctx->matches->elts,
0248                                  ctx->matches->nelts);
0249     }
0250 
0251     ctx->saved.data = ngx_pnalloc(r->pool, ctx->tables->max_match_len - 1);
0252     if (ctx->saved.data == NULL) {
0253         return NGX_ERROR;
0254     }
0255 
0256     ctx->looked.data = ngx_pnalloc(r->pool, ctx->tables->max_match_len - 1);
0257     if (ctx->looked.data == NULL) {
0258         return NGX_ERROR;
0259     }
0260 
0261     ngx_http_set_ctx(r, ctx, ngx_http_sub_filter_module);
0262 
0263     ctx->offset = ctx->tables->min_match_len - 1;
0264     ctx->last_out = &ctx->out;
0265 
0266     r->filter_need_in_memory = 1;
0267 
0268     if (r == r->main) {
0269         ngx_http_clear_content_length(r);
0270 
0271         if (!slcf->last_modified) {
0272             ngx_http_clear_last_modified(r);
0273             ngx_http_clear_etag(r);
0274 
0275         } else {
0276             ngx_http_weak_etag(r);
0277         }
0278     }
0279 
0280     return ngx_http_next_header_filter(r);
0281 }
0282 
0283 
0284 static ngx_int_t
0285 ngx_http_sub_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
0286 {
0287     ngx_int_t                  rc;
0288     ngx_buf_t                 *b;
0289     ngx_str_t                 *sub;
0290     ngx_uint_t                 flush, last;
0291     ngx_chain_t               *cl;
0292     ngx_http_sub_ctx_t        *ctx;
0293     ngx_http_sub_match_t      *match;
0294     ngx_http_sub_loc_conf_t   *slcf;
0295 
0296     ctx = ngx_http_get_module_ctx(r, ngx_http_sub_filter_module);
0297 
0298     if (ctx == NULL) {
0299         return ngx_http_next_body_filter(r, in);
0300     }
0301 
0302     if ((in == NULL
0303          && ctx->buf == NULL
0304          && ctx->in == NULL
0305          && ctx->busy == NULL))
0306     {
0307         return ngx_http_next_body_filter(r, in);
0308     }
0309 
0310     if (ctx->once && (ctx->buf == NULL || ctx->in == NULL)) {
0311 
0312         if (ctx->busy) {
0313             if (ngx_http_sub_output(r, ctx) == NGX_ERROR) {
0314                 return NGX_ERROR;
0315             }
0316         }
0317 
0318         return ngx_http_next_body_filter(r, in);
0319     }
0320 
0321     /* add the incoming chain to the chain ctx->in */
0322 
0323     if (in) {
0324         if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
0325             return NGX_ERROR;
0326         }
0327     }
0328 
0329     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0330                    "http sub filter \"%V\"", &r->uri);
0331 
0332     flush = 0;
0333     last = 0;
0334 
0335     while (ctx->in || ctx->buf) {
0336 
0337         if (ctx->buf == NULL) {
0338             ctx->buf = ctx->in->buf;
0339             ctx->in = ctx->in->next;
0340             ctx->pos = ctx->buf->pos;
0341         }
0342 
0343         if (ctx->buf->flush || ctx->buf->recycled) {
0344             flush = 1;
0345         }
0346 
0347         if (ctx->in == NULL) {
0348             last = flush;
0349         }
0350 
0351         b = NULL;
0352 
0353         while (ctx->pos < ctx->buf->last) {
0354 
0355             rc = ngx_http_sub_parse(r, ctx, last);
0356 
0357             ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0358                            "parse: %i, looked: \"%V\" %p-%p",
0359                            rc, &ctx->looked, ctx->copy_start, ctx->copy_end);
0360 
0361             if (rc == NGX_ERROR) {
0362                 return rc;
0363             }
0364 
0365             if (ctx->saved.len) {
0366 
0367                 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0368                                "saved: \"%V\"", &ctx->saved);
0369 
0370                 cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
0371                 if (cl == NULL) {
0372                     return NGX_ERROR;
0373                 }
0374 
0375                 b = cl->buf;
0376 
0377                 ngx_memzero(b, sizeof(ngx_buf_t));
0378 
0379                 b->pos = ngx_pnalloc(r->pool, ctx->saved.len);
0380                 if (b->pos == NULL) {
0381                     return NGX_ERROR;
0382                 }
0383 
0384                 ngx_memcpy(b->pos, ctx->saved.data, ctx->saved.len);
0385                 b->last = b->pos + ctx->saved.len;
0386                 b->memory = 1;
0387 
0388                 *ctx->last_out = cl;
0389                 ctx->last_out = &cl->next;
0390 
0391                 ctx->saved.len = 0;
0392             }
0393 
0394             if (ctx->copy_start != ctx->copy_end) {
0395 
0396                 cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
0397                 if (cl == NULL) {
0398                     return NGX_ERROR;
0399                 }
0400 
0401                 b = cl->buf;
0402 
0403                 ngx_memcpy(b, ctx->buf, sizeof(ngx_buf_t));
0404 
0405                 b->pos = ctx->copy_start;
0406                 b->last = ctx->copy_end;
0407                 b->shadow = NULL;
0408                 b->last_buf = 0;
0409                 b->last_in_chain = 0;
0410                 b->recycled = 0;
0411 
0412                 if (b->in_file) {
0413                     b->file_last = b->file_pos + (b->last - ctx->buf->pos);
0414                     b->file_pos += b->pos - ctx->buf->pos;
0415                 }
0416 
0417                 *ctx->last_out = cl;
0418                 ctx->last_out = &cl->next;
0419             }
0420 
0421             if (rc == NGX_AGAIN) {
0422                 continue;
0423             }
0424 
0425 
0426             /* rc == NGX_OK */
0427 
0428             cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
0429             if (cl == NULL) {
0430                 return NGX_ERROR;
0431             }
0432 
0433             b = cl->buf;
0434 
0435             ngx_memzero(b, sizeof(ngx_buf_t));
0436 
0437             slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module);
0438 
0439             if (ctx->sub == NULL) {
0440                 ctx->sub = ngx_pcalloc(r->pool, sizeof(ngx_str_t)
0441                                                 * ctx->matches->nelts);
0442                 if (ctx->sub == NULL) {
0443                     return NGX_ERROR;
0444                 }
0445             }
0446 
0447             sub = &ctx->sub[ctx->index];
0448 
0449             if (sub->data == NULL) {
0450                 match = ctx->matches->elts;
0451 
0452                 if (ngx_http_complex_value(r, match[ctx->index].value, sub)
0453                     != NGX_OK)
0454                 {
0455                     return NGX_ERROR;
0456                 }
0457             }
0458 
0459             if (sub->len) {
0460                 b->memory = 1;
0461                 b->pos = sub->data;
0462                 b->last = sub->data + sub->len;
0463 
0464             } else {
0465                 b->sync = 1;
0466             }
0467 
0468             *ctx->last_out = cl;
0469             ctx->last_out = &cl->next;
0470 
0471             ctx->index = 0;
0472             ctx->once = slcf->once && (++ctx->applied == ctx->matches->nelts);
0473 
0474             continue;
0475         }
0476 
0477         if (ctx->looked.len
0478             && (ctx->buf->last_buf || ctx->buf->last_in_chain))
0479         {
0480             cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
0481             if (cl == NULL) {
0482                 return NGX_ERROR;
0483             }
0484 
0485             b = cl->buf;
0486 
0487             ngx_memzero(b, sizeof(ngx_buf_t));
0488 
0489             b->pos = ctx->looked.data;
0490             b->last = b->pos + ctx->looked.len;
0491             b->memory = 1;
0492 
0493             *ctx->last_out = cl;
0494             ctx->last_out = &cl->next;
0495 
0496             ctx->looked.len = 0;
0497         }
0498 
0499         if (ctx->buf->last_buf || ctx->buf->flush || ctx->buf->sync
0500             || ngx_buf_in_memory(ctx->buf))
0501         {
0502             if (b == NULL) {
0503                 cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
0504                 if (cl == NULL) {
0505                     return NGX_ERROR;
0506                 }
0507 
0508                 b = cl->buf;
0509 
0510                 ngx_memzero(b, sizeof(ngx_buf_t));
0511 
0512                 b->sync = 1;
0513 
0514                 *ctx->last_out = cl;
0515                 ctx->last_out = &cl->next;
0516             }
0517 
0518             b->last_buf = ctx->buf->last_buf;
0519             b->last_in_chain = ctx->buf->last_in_chain;
0520             b->flush = ctx->buf->flush;
0521             b->shadow = ctx->buf;
0522 
0523             b->recycled = ctx->buf->recycled;
0524         }
0525 
0526         ctx->buf = NULL;
0527     }
0528 
0529     if (ctx->out == NULL && ctx->busy == NULL) {
0530         return NGX_OK;
0531     }
0532 
0533     return ngx_http_sub_output(r, ctx);
0534 }
0535 
0536 
0537 static ngx_int_t
0538 ngx_http_sub_output(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx)
0539 {
0540     ngx_int_t     rc;
0541     ngx_buf_t    *b;
0542     ngx_chain_t  *cl;
0543 
0544 #if 1
0545     b = NULL;
0546     for (cl = ctx->out; cl; cl = cl->next) {
0547         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0548                        "sub out: %p %p", cl->buf, cl->buf->pos);
0549         if (cl->buf == b) {
0550             ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
0551                           "the same buf was used in sub");
0552             ngx_debug_point();
0553             return NGX_ERROR;
0554         }
0555         b = cl->buf;
0556     }
0557 #endif
0558 
0559     rc = ngx_http_next_body_filter(r, ctx->out);
0560 
0561     if (ctx->busy == NULL) {
0562         ctx->busy = ctx->out;
0563 
0564     } else {
0565         for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ }
0566         cl->next = ctx->out;
0567     }
0568 
0569     ctx->out = NULL;
0570     ctx->last_out = &ctx->out;
0571 
0572     while (ctx->busy) {
0573 
0574         cl = ctx->busy;
0575         b = cl->buf;
0576 
0577         if (ngx_buf_size(b) != 0) {
0578             break;
0579         }
0580 
0581         if (b->shadow) {
0582             b->shadow->pos = b->shadow->last;
0583         }
0584 
0585         ctx->busy = cl->next;
0586 
0587         if (ngx_buf_in_memory(b) || b->in_file) {
0588             /* add data bufs only to the free buf chain */
0589 
0590             cl->next = ctx->free;
0591             ctx->free = cl;
0592         }
0593     }
0594 
0595     if (ctx->in || ctx->buf) {
0596         r->buffered |= NGX_HTTP_SUB_BUFFERED;
0597 
0598     } else {
0599         r->buffered &= ~NGX_HTTP_SUB_BUFFERED;
0600     }
0601 
0602     return rc;
0603 }
0604 
0605 
0606 static ngx_int_t
0607 ngx_http_sub_parse(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx,
0608     ngx_uint_t flush)
0609 {
0610     u_char                   *p, c;
0611     ngx_str_t                *m;
0612     ngx_int_t                 offset, start, next, end, len, rc;
0613     ngx_uint_t                shift, i, j;
0614     ngx_http_sub_match_t     *match;
0615     ngx_http_sub_tables_t    *tables;
0616     ngx_http_sub_loc_conf_t  *slcf;
0617 
0618     slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module);
0619     tables = ctx->tables;
0620     match = ctx->matches->elts;
0621 
0622     offset = ctx->offset;
0623     end = ctx->buf->last - ctx->pos;
0624 
0625     if (ctx->once) {
0626         /* sets start and next to end */
0627         offset = end + (ngx_int_t) tables->min_match_len - 1;
0628         goto again;
0629     }
0630 
0631     while (offset < end) {
0632 
0633         c = offset < 0 ? ctx->looked.data[ctx->looked.len + offset]
0634                        : ctx->pos[offset];
0635 
0636         c = ngx_tolower(c);
0637 
0638         shift = tables->shift[c];
0639         if (shift > 0) {
0640             offset += shift;
0641             continue;
0642         }
0643 
0644         /* a potential match */
0645 
0646         start = offset - (ngx_int_t) tables->min_match_len + 1;
0647 
0648         i = ngx_max((ngx_uint_t) tables->index[c], ctx->index);
0649         j = tables->index[c + 1];
0650 
0651         while (i != j) {
0652 
0653             if (slcf->once && ctx->sub && ctx->sub[i].data) {
0654                 goto next;
0655             }
0656 
0657             m = &match[i].match;
0658 
0659             rc = ngx_http_sub_match(ctx, start, m);
0660 
0661             if (rc == NGX_DECLINED) {
0662                 goto next;
0663             }
0664 
0665             ctx->index = i;
0666 
0667             if (rc == NGX_AGAIN) {
0668                 goto again;
0669             }
0670 
0671             ctx->offset = offset + (ngx_int_t) m->len;
0672             next = start + (ngx_int_t) m->len;
0673             end = ngx_max(next, 0);
0674             rc = NGX_OK;
0675 
0676             goto done;
0677 
0678         next:
0679 
0680             i++;
0681         }
0682 
0683         offset++;
0684         ctx->index = 0;
0685     }
0686 
0687     if (flush) {
0688         for ( ;; ) {
0689             start = offset - (ngx_int_t) tables->min_match_len + 1;
0690 
0691             if (start >= end) {
0692                 break;
0693             }
0694 
0695             for (i = 0; i < ctx->matches->nelts; i++) {
0696                 m = &match[i].match;
0697 
0698                 if (ngx_http_sub_match(ctx, start, m) == NGX_AGAIN) {
0699                     goto again;
0700                 }
0701             }
0702 
0703             offset++;
0704         }
0705     }
0706 
0707 again:
0708 
0709     ctx->offset = offset;
0710     start = offset - (ngx_int_t) tables->min_match_len + 1;
0711     next = start;
0712     rc = NGX_AGAIN;
0713 
0714 done:
0715 
0716     /* send [ - looked.len, start ] to client */
0717 
0718     ctx->saved.len = ctx->looked.len + ngx_min(start, 0);
0719     ngx_memcpy(ctx->saved.data, ctx->looked.data, ctx->saved.len);
0720 
0721     ctx->copy_start = ctx->pos;
0722     ctx->copy_end = ctx->pos + ngx_max(start, 0);
0723 
0724     /* save [ next, end ] in looked */
0725 
0726     len = ngx_min(next, 0);
0727     p = ctx->looked.data;
0728     p = ngx_movemem(p, p + ctx->looked.len + len, - len);
0729 
0730     len = ngx_max(next, 0);
0731     p = ngx_cpymem(p, ctx->pos + len, end - len);
0732     ctx->looked.len = p - ctx->looked.data;
0733 
0734     /* update position */
0735 
0736     ctx->pos += end;
0737     ctx->offset -= end;
0738 
0739     return rc;
0740 }
0741 
0742 
0743 static ngx_int_t
0744 ngx_http_sub_match(ngx_http_sub_ctx_t *ctx, ngx_int_t start, ngx_str_t *m)
0745 {
0746     u_char  *p, *last, *pat, *pat_end;
0747 
0748     pat = m->data;
0749     pat_end = m->data + m->len;
0750 
0751     if (start >= 0) {
0752         p = ctx->pos + start;
0753 
0754     } else {
0755         last = ctx->looked.data + ctx->looked.len;
0756         p = last + start;
0757 
0758         while (p < last && pat < pat_end) {
0759             if (ngx_tolower(*p) != *pat) {
0760                 return NGX_DECLINED;
0761             }
0762 
0763             p++;
0764             pat++;
0765         }
0766 
0767         p = ctx->pos;
0768     }
0769 
0770     while (p < ctx->buf->last && pat < pat_end) {
0771         if (ngx_tolower(*p) != *pat) {
0772             return NGX_DECLINED;
0773         }
0774 
0775         p++;
0776         pat++;
0777     }
0778 
0779     if (pat != pat_end) {
0780         /* partial match */
0781         return NGX_AGAIN;
0782     }
0783 
0784     return NGX_OK;
0785 }
0786 
0787 
0788 static char *
0789 ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0790 {
0791     ngx_http_sub_loc_conf_t *slcf = conf;
0792 
0793     ngx_str_t                         *value;
0794     ngx_http_sub_pair_t               *pair;
0795     ngx_http_compile_complex_value_t   ccv;
0796 
0797     value = cf->args->elts;
0798 
0799     if (value[1].len == 0) {
0800         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty search pattern");
0801         return NGX_CONF_ERROR;
0802     }
0803 
0804     if (slcf->pairs == NULL) {
0805         slcf->pairs = ngx_array_create(cf->pool, 1,
0806                                        sizeof(ngx_http_sub_pair_t));
0807         if (slcf->pairs == NULL) {
0808             return NGX_CONF_ERROR;
0809         }
0810     }
0811 
0812     if (slcf->pairs->nelts == 255) {
0813         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0814                            "number of search patterns exceeds 255");
0815         return NGX_CONF_ERROR;
0816     }
0817 
0818     ngx_strlow(value[1].data, value[1].data, value[1].len);
0819 
0820     pair = ngx_array_push(slcf->pairs);
0821     if (pair == NULL) {
0822         return NGX_CONF_ERROR;
0823     }
0824 
0825     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
0826 
0827     ccv.cf = cf;
0828     ccv.value = &value[1];
0829     ccv.complex_value = &pair->match;
0830 
0831     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
0832         return NGX_CONF_ERROR;
0833     }
0834 
0835     if (ccv.complex_value->lengths != NULL) {
0836         slcf->dynamic = 1;
0837 
0838     } else {
0839         ngx_strlow(pair->match.value.data, pair->match.value.data,
0840                    pair->match.value.len);
0841     }
0842 
0843     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
0844 
0845     ccv.cf = cf;
0846     ccv.value = &value[2];
0847     ccv.complex_value = &pair->value;
0848 
0849     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
0850         return NGX_CONF_ERROR;
0851     }
0852 
0853     return NGX_CONF_OK;
0854 }
0855 
0856 
0857 static void *
0858 ngx_http_sub_create_conf(ngx_conf_t *cf)
0859 {
0860     ngx_http_sub_loc_conf_t  *slcf;
0861 
0862     slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_sub_loc_conf_t));
0863     if (slcf == NULL) {
0864         return NULL;
0865     }
0866 
0867     /*
0868      * set by ngx_pcalloc():
0869      *
0870      *     conf->dynamic = 0;
0871      *     conf->pairs = NULL;
0872      *     conf->tables = NULL;
0873      *     conf->types = { NULL };
0874      *     conf->types_keys = NULL;
0875      *     conf->matches = NULL;
0876      */
0877 
0878     slcf->once = NGX_CONF_UNSET;
0879     slcf->last_modified = NGX_CONF_UNSET;
0880 
0881     return slcf;
0882 }
0883 
0884 
0885 static char *
0886 ngx_http_sub_merge_conf(ngx_conf_t *cf, void *parent, void *child)
0887 {
0888     ngx_uint_t                i, n;
0889     ngx_http_sub_pair_t      *pairs;
0890     ngx_http_sub_match_t     *matches;
0891     ngx_http_sub_loc_conf_t  *prev = parent;
0892     ngx_http_sub_loc_conf_t  *conf = child;
0893 
0894     ngx_conf_merge_value(conf->once, prev->once, 1);
0895     ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0);
0896 
0897     if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
0898                              &prev->types_keys, &prev->types,
0899                              ngx_http_html_default_types)
0900         != NGX_OK)
0901     {
0902         return NGX_CONF_ERROR;
0903     }
0904 
0905     if (conf->pairs == NULL) {
0906         conf->dynamic = prev->dynamic;
0907         conf->pairs = prev->pairs;
0908         conf->matches = prev->matches;
0909         conf->tables = prev->tables;
0910     }
0911 
0912     if (conf->pairs && conf->dynamic == 0 && conf->tables == NULL) {
0913         pairs = conf->pairs->elts;
0914         n = conf->pairs->nelts;
0915 
0916         matches = ngx_palloc(cf->pool, sizeof(ngx_http_sub_match_t) * n);
0917         if (matches == NULL) {
0918             return NGX_CONF_ERROR;
0919         }
0920 
0921         for (i = 0; i < n; i++) {
0922             matches[i].match = pairs[i].match.value;
0923             matches[i].value = &pairs[i].value;
0924         }
0925 
0926         conf->matches = ngx_palloc(cf->pool, sizeof(ngx_array_t));
0927         if (conf->matches == NULL) {
0928             return NGX_CONF_ERROR;
0929         }
0930 
0931         conf->matches->elts = matches;
0932         conf->matches->nelts = n;
0933 
0934         conf->tables = ngx_palloc(cf->pool, sizeof(ngx_http_sub_tables_t));
0935         if (conf->tables == NULL) {
0936             return NGX_CONF_ERROR;
0937         }
0938 
0939         ngx_http_sub_init_tables(conf->tables, conf->matches->elts,
0940                                  conf->matches->nelts);
0941     }
0942 
0943     return NGX_CONF_OK;
0944 }
0945 
0946 
0947 static void
0948 ngx_http_sub_init_tables(ngx_http_sub_tables_t *tables,
0949     ngx_http_sub_match_t *match, ngx_uint_t n)
0950 {
0951     u_char      c;
0952     ngx_uint_t  i, j, min, max, ch;
0953 
0954     min = match[0].match.len;
0955     max = match[0].match.len;
0956 
0957     for (i = 1; i < n; i++) {
0958         min = ngx_min(min, match[i].match.len);
0959         max = ngx_max(max, match[i].match.len);
0960     }
0961 
0962     tables->min_match_len = min;
0963     tables->max_match_len = max;
0964 
0965     ngx_http_sub_cmp_index = tables->min_match_len - 1;
0966     ngx_sort(match, n, sizeof(ngx_http_sub_match_t), ngx_http_sub_cmp_matches);
0967 
0968     min = ngx_min(min, 255);
0969     ngx_memset(tables->shift, min, 256);
0970 
0971     ch = 0;
0972 
0973     for (i = 0; i < n; i++) {
0974 
0975         for (j = 0; j < min; j++) {
0976             c = match[i].match.data[tables->min_match_len - 1 - j];
0977             tables->shift[c] = ngx_min(tables->shift[c], (u_char) j);
0978         }
0979 
0980         c = match[i].match.data[tables->min_match_len - 1];
0981         while (ch <= (ngx_uint_t) c) {
0982             tables->index[ch++] = (u_char) i;
0983         }
0984     }
0985 
0986     while (ch < 257) {
0987         tables->index[ch++] = (u_char) n;
0988     }
0989 }
0990 
0991 
0992 static ngx_int_t
0993 ngx_http_sub_cmp_matches(const void *one, const void *two)
0994 {
0995     ngx_int_t              c1, c2;
0996     ngx_http_sub_match_t  *first, *second;
0997 
0998     first = (ngx_http_sub_match_t *) one;
0999     second = (ngx_http_sub_match_t *) two;
1000 
1001     c1 = first->match.data[ngx_http_sub_cmp_index];
1002     c2 = second->match.data[ngx_http_sub_cmp_index];
1003 
1004     return c1 - c2;
1005 }
1006 
1007 
1008 static ngx_int_t
1009 ngx_http_sub_filter_init(ngx_conf_t *cf)
1010 {
1011     ngx_http_next_header_filter = ngx_http_top_header_filter;
1012     ngx_http_top_header_filter = ngx_http_sub_header_filter;
1013 
1014     ngx_http_next_body_filter = ngx_http_top_body_filter;
1015     ngx_http_top_body_filter = ngx_http_sub_body_filter;
1016 
1017     return NGX_OK;
1018 }