1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) Nginx, Inc.
5  */
6 
7 
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_http.h>
11 
12 
13 typedef struct {
14     ngx_http_complex_value_t   match;
15     ngx_http_complex_value_t   value;
16 } ngx_http_sub_pair_t;
17 
18 
19 typedef struct {
20     ngx_str_t                  match;
21     ngx_http_complex_value_t  *value;
22 } ngx_http_sub_match_t;
23 
24 
25 typedef struct {
26     ngx_uint_t                 min_match_len;
27     ngx_uint_t                 max_match_len;
28 
29     u_char                     index[257];
30     u_char                     shift[256];
31 } ngx_http_sub_tables_t;
32 
33 
34 typedef struct {
35     ngx_uint_t                 dynamic; /* unsigned dynamic:1; */
36 
37     ngx_array_t               *pairs;
38 
39     ngx_http_sub_tables_t     *tables;
40 
41     ngx_hash_t                 types;
42 
43     ngx_flag_t                 once;
44     ngx_flag_t                 last_modified;
45 
46     ngx_array_t               *types_keys;
47     ngx_array_t               *matches;
48 } ngx_http_sub_loc_conf_t;
49 
50 
51 typedef struct {
52     ngx_str_t                  saved;
53     ngx_str_t                  looked;
54 
55     ngx_uint_t                 once;   /* unsigned  once:1 */
56 
57     ngx_buf_t                 *buf;
58 
59     u_char                    *pos;
60     u_char                    *copy_start;
61     u_char                    *copy_end;
62 
63     ngx_chain_t               *in;
64     ngx_chain_t               *out;
65     ngx_chain_t              **last_out;
66     ngx_chain_t               *busy;
67     ngx_chain_t               *free;
68 
69     ngx_str_t                 *sub;
70     ngx_uint_t                 applied;
71 
72     ngx_int_t                  offset;
73     ngx_uint_t                 index;
74 
75     ngx_http_sub_tables_t     *tables;
76     ngx_array_t               *matches;
77 } ngx_http_sub_ctx_t;
78 
79 
80 static ngx_uint_t ngx_http_sub_cmp_index;
81 
82 
83 static ngx_int_t ngx_http_sub_output(ngx_http_request_t *r,
84     ngx_http_sub_ctx_t *ctx);
85 static ngx_int_t ngx_http_sub_parse(ngx_http_request_t *r,
86     ngx_http_sub_ctx_t *ctx, ngx_uint_t flush);
87 static ngx_int_t ngx_http_sub_match(ngx_http_sub_ctx_t *ctx, ngx_int_t start,
88     ngx_str_t *m);
89 
90 static char * ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd,
91     void *conf);
92 static void *ngx_http_sub_create_conf(ngx_conf_t *cf);
93 static char *ngx_http_sub_merge_conf(ngx_conf_t *cf,
94     void *parent, void *child);
95 static void ngx_http_sub_init_tables(ngx_http_sub_tables_t *tables,
96     ngx_http_sub_match_t *match, ngx_uint_t n);
97 static ngx_int_t ngx_http_sub_cmp_matches(const void *one, const void *two);
98 static ngx_int_t ngx_http_sub_filter_init(ngx_conf_t *cf);
99 
100 
101 static ngx_command_t  ngx_http_sub_filter_commands[] = {
102 
103     { ngx_string("sub_filter"),
104       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
105       ngx_http_sub_filter,
106       NGX_HTTP_LOC_CONF_OFFSET,
107       0,
108       NULL },
109 
110     { ngx_string("sub_filter_types"),
111       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
112       ngx_http_types_slot,
113       NGX_HTTP_LOC_CONF_OFFSET,
114       offsetof(ngx_http_sub_loc_conf_t, types_keys),
115       &ngx_http_html_default_types[0] },
116 
117     { ngx_string("sub_filter_once"),
118       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
119       ngx_conf_set_flag_slot,
120       NGX_HTTP_LOC_CONF_OFFSET,
121       offsetof(ngx_http_sub_loc_conf_t, once),
122       NULL },
123 
124     { ngx_string("sub_filter_last_modified"),
125       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
126       ngx_conf_set_flag_slot,
127       NGX_HTTP_LOC_CONF_OFFSET,
128       offsetof(ngx_http_sub_loc_conf_t, last_modified),
129       NULL },
130 
131       ngx_null_command
132 };
133 
134 
135 static ngx_http_module_t  ngx_http_sub_filter_module_ctx = {
136     NULL,                                  /* preconfiguration */
137     ngx_http_sub_filter_init,              /* postconfiguration */
138 
139     NULL,                                  /* create main configuration */
140     NULL,                                  /* init main configuration */
141 
142     NULL,                                  /* create server configuration */
143     NULL,                                  /* merge server configuration */
144 
145     ngx_http_sub_create_conf,              /* create location configuration */
146     ngx_http_sub_merge_conf                /* merge location configuration */
147 };
148 
149 
150 ngx_module_t  ngx_http_sub_filter_module = {
151     NGX_MODULE_V1,
152     &ngx_http_sub_filter_module_ctx,       /* module context */
153     ngx_http_sub_filter_commands,          /* module directives */
154     NGX_HTTP_MODULE,                       /* module type */
155     NULL,                                  /* init master */
156     NULL,                                  /* init module */
157     NULL,                                  /* init process */
158     NULL,                                  /* init thread */
159     NULL,                                  /* exit thread */
160     NULL,                                  /* exit process */
161     NULL,                                  /* exit master */
162     NGX_MODULE_V1_PADDING
163 };
164 
165 
166 static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
167 static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
168 
169 
170 static ngx_int_t
ngx_http_sub_header_filter(ngx_http_request_t * r)171 ngx_http_sub_header_filter(ngx_http_request_t *r)
172 {
173     ngx_str_t                *m;
174     ngx_uint_t                i, j, n;
175     ngx_http_sub_ctx_t       *ctx;
176     ngx_http_sub_pair_t      *pairs;
177     ngx_http_sub_match_t     *matches;
178     ngx_http_sub_loc_conf_t  *slcf;
179 
180     slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module);
181 
182     if (slcf->pairs == NULL
183         || r->headers_out.content_length_n == 0
184         || ngx_http_test_content_type(r, &slcf->types) == NULL)
185     {
186         return ngx_http_next_header_filter(r);
187     }
188 
189     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_sub_ctx_t));
190     if (ctx == NULL) {
191         return NGX_ERROR;
192     }
193 
194     if (slcf->dynamic == 0) {
195         ctx->tables = slcf->tables;
196         ctx->matches = slcf->matches;
197 
198     } else {
199         pairs = slcf->pairs->elts;
200         n = slcf->pairs->nelts;
201 
202         matches = ngx_pcalloc(r->pool, sizeof(ngx_http_sub_match_t) * n);
203         if (matches == NULL) {
204             return NGX_ERROR;
205         }
206 
207         j = 0;
208         for (i = 0; i < n; i++) {
209             matches[j].value = &pairs[i].value;
210 
211             if (pairs[i].match.lengths == NULL) {
212                 matches[j].match = pairs[i].match.value;
213                 j++;
214                 continue;
215             }
216 
217             m = &matches[j].match;
218             if (ngx_http_complex_value(r, &pairs[i].match, m) != NGX_OK) {
219                 return NGX_ERROR;
220             }
221 
222             if (m->len == 0) {
223                 continue;
224             }
225 
226             ngx_strlow(m->data, m->data, m->len);
227             j++;
228         }
229 
230         if (j == 0) {
231             return ngx_http_next_header_filter(r);
232         }
233 
234         ctx->matches = ngx_palloc(r->pool, sizeof(ngx_array_t));
235         if (ctx->matches == NULL) {
236             return NGX_ERROR;
237         }
238 
239         ctx->matches->elts = matches;
240         ctx->matches->nelts = j;
241 
242         ctx->tables = ngx_palloc(r->pool, sizeof(ngx_http_sub_tables_t));
243         if (ctx->tables == NULL) {
244             return NGX_ERROR;
245         }
246 
247         ngx_http_sub_init_tables(ctx->tables, ctx->matches->elts,
248                                  ctx->matches->nelts);
249     }
250 
251     ctx->saved.data = ngx_pnalloc(r->pool, ctx->tables->max_match_len - 1);
252     if (ctx->saved.data == NULL) {
253         return NGX_ERROR;
254     }
255 
256     ctx->looked.data = ngx_pnalloc(r->pool, ctx->tables->max_match_len - 1);
257     if (ctx->looked.data == NULL) {
258         return NGX_ERROR;
259     }
260 
261     ngx_http_set_ctx(r, ctx, ngx_http_sub_filter_module);
262 
263     ctx->offset = ctx->tables->min_match_len - 1;
264     ctx->last_out = &ctx->out;
265 
266     r->filter_need_in_memory = 1;
267 
268     if (r == r->main) {
269         ngx_http_clear_content_length(r);
270 
271         if (!slcf->last_modified) {
272             ngx_http_clear_last_modified(r);
273             ngx_http_clear_etag(r);
274 
275         } else {
276             ngx_http_weak_etag(r);
277         }
278     }
279 
280     return ngx_http_next_header_filter(r);
281 }
282 
283 
284 static ngx_int_t
ngx_http_sub_body_filter(ngx_http_request_t * r,ngx_chain_t * in)285 ngx_http_sub_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
286 {
287     ngx_int_t                  rc;
288     ngx_buf_t                 *b;
289     ngx_str_t                 *sub;
290     ngx_uint_t                 flush, last;
291     ngx_chain_t               *cl;
292     ngx_http_sub_ctx_t        *ctx;
293     ngx_http_sub_match_t      *match;
294     ngx_http_sub_loc_conf_t   *slcf;
295 
296     ctx = ngx_http_get_module_ctx(r, ngx_http_sub_filter_module);
297 
298     if (ctx == NULL) {
299         return ngx_http_next_body_filter(r, in);
300     }
301 
302     if ((in == NULL
303          && ctx->buf == NULL
304          && ctx->in == NULL
305          && ctx->busy == NULL))
306     {
307         return ngx_http_next_body_filter(r, in);
308     }
309 
310     if (ctx->once && (ctx->buf == NULL || ctx->in == NULL)) {
311 
312         if (ctx->busy) {
313             if (ngx_http_sub_output(r, ctx) == NGX_ERROR) {
314                 return NGX_ERROR;
315             }
316         }
317 
318         return ngx_http_next_body_filter(r, in);
319     }
320 
321     /* add the incoming chain to the chain ctx->in */
322 
323     if (in) {
324         if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
325             return NGX_ERROR;
326         }
327     }
328 
329     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
330                    "http sub filter \"%V\"", &r->uri);
331 
332     flush = 0;
333     last = 0;
334 
335     while (ctx->in || ctx->buf) {
336 
337         if (ctx->buf == NULL) {
338             ctx->buf = ctx->in->buf;
339             ctx->in = ctx->in->next;
340             ctx->pos = ctx->buf->pos;
341         }
342 
343         if (ctx->buf->flush || ctx->buf->recycled) {
344             flush = 1;
345         }
346 
347         if (ctx->in == NULL) {
348             last = flush;
349         }
350 
351         b = NULL;
352 
353         while (ctx->pos < ctx->buf->last) {
354 
355             rc = ngx_http_sub_parse(r, ctx, last);
356 
357             ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
358                            "parse: %i, looked: \"%V\" %p-%p",
359                            rc, &ctx->looked, ctx->copy_start, ctx->copy_end);
360 
361             if (rc == NGX_ERROR) {
362                 return rc;
363             }
364 
365             if (ctx->saved.len) {
366 
367                 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
368                                "saved: \"%V\"", &ctx->saved);
369 
370                 cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
371                 if (cl == NULL) {
372                     return NGX_ERROR;
373                 }
374 
375                 b = cl->buf;
376 
377                 ngx_memzero(b, sizeof(ngx_buf_t));
378 
379                 b->pos = ngx_pnalloc(r->pool, ctx->saved.len);
380                 if (b->pos == NULL) {
381                     return NGX_ERROR;
382                 }
383 
384                 ngx_memcpy(b->pos, ctx->saved.data, ctx->saved.len);
385                 b->last = b->pos + ctx->saved.len;
386                 b->memory = 1;
387 
388                 *ctx->last_out = cl;
389                 ctx->last_out = &cl->next;
390 
391                 ctx->saved.len = 0;
392             }
393 
394             if (ctx->copy_start != ctx->copy_end) {
395 
396                 cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
397                 if (cl == NULL) {
398                     return NGX_ERROR;
399                 }
400 
401                 b = cl->buf;
402 
403                 ngx_memcpy(b, ctx->buf, sizeof(ngx_buf_t));
404 
405                 b->pos = ctx->copy_start;
406                 b->last = ctx->copy_end;
407                 b->shadow = NULL;
408                 b->last_buf = 0;
409                 b->last_in_chain = 0;
410                 b->recycled = 0;
411 
412                 if (b->in_file) {
413                     b->file_last = b->file_pos + (b->last - ctx->buf->pos);
414                     b->file_pos += b->pos - ctx->buf->pos;
415                 }
416 
417                 *ctx->last_out = cl;
418                 ctx->last_out = &cl->next;
419             }
420 
421             if (rc == NGX_AGAIN) {
422                 continue;
423             }
424 
425 
426             /* rc == NGX_OK */
427 
428             cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
429             if (cl == NULL) {
430                 return NGX_ERROR;
431             }
432 
433             b = cl->buf;
434 
435             ngx_memzero(b, sizeof(ngx_buf_t));
436 
437             slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module);
438 
439             if (ctx->sub == NULL) {
440                 ctx->sub = ngx_pcalloc(r->pool, sizeof(ngx_str_t)
441                                                 * ctx->matches->nelts);
442                 if (ctx->sub == NULL) {
443                     return NGX_ERROR;
444                 }
445             }
446 
447             sub = &ctx->sub[ctx->index];
448 
449             if (sub->data == NULL) {
450                 match = ctx->matches->elts;
451 
452                 if (ngx_http_complex_value(r, match[ctx->index].value, sub)
453                     != NGX_OK)
454                 {
455                     return NGX_ERROR;
456                 }
457             }
458 
459             if (sub->len) {
460                 b->memory = 1;
461                 b->pos = sub->data;
462                 b->last = sub->data + sub->len;
463 
464             } else {
465                 b->sync = 1;
466             }
467 
468             *ctx->last_out = cl;
469             ctx->last_out = &cl->next;
470 
471             ctx->index = 0;
472             ctx->once = slcf->once && (++ctx->applied == ctx->matches->nelts);
473 
474             continue;
475         }
476 
477         if (ctx->looked.len
478             && (ctx->buf->last_buf || ctx->buf->last_in_chain))
479         {
480             cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
481             if (cl == NULL) {
482                 return NGX_ERROR;
483             }
484 
485             b = cl->buf;
486 
487             ngx_memzero(b, sizeof(ngx_buf_t));
488 
489             b->pos = ctx->looked.data;
490             b->last = b->pos + ctx->looked.len;
491             b->memory = 1;
492 
493             *ctx->last_out = cl;
494             ctx->last_out = &cl->next;
495 
496             ctx->looked.len = 0;
497         }
498 
499         if (ctx->buf->last_buf || ctx->buf->flush || ctx->buf->sync
500             || ngx_buf_in_memory(ctx->buf))
501         {
502             if (b == NULL) {
503                 cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
504                 if (cl == NULL) {
505                     return NGX_ERROR;
506                 }
507 
508                 b = cl->buf;
509 
510                 ngx_memzero(b, sizeof(ngx_buf_t));
511 
512                 b->sync = 1;
513 
514                 *ctx->last_out = cl;
515                 ctx->last_out = &cl->next;
516             }
517 
518             b->last_buf = ctx->buf->last_buf;
519             b->last_in_chain = ctx->buf->last_in_chain;
520             b->flush = ctx->buf->flush;
521             b->shadow = ctx->buf;
522 
523             b->recycled = ctx->buf->recycled;
524         }
525 
526         ctx->buf = NULL;
527     }
528 
529     if (ctx->out == NULL && ctx->busy == NULL) {
530         return NGX_OK;
531     }
532 
533     return ngx_http_sub_output(r, ctx);
534 }
535 
536 
537 static ngx_int_t
ngx_http_sub_output(ngx_http_request_t * r,ngx_http_sub_ctx_t * ctx)538 ngx_http_sub_output(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx)
539 {
540     ngx_int_t     rc;
541     ngx_buf_t    *b;
542     ngx_chain_t  *cl;
543 
544 #if 1
545     b = NULL;
546     for (cl = ctx->out; cl; cl = cl->next) {
547         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
548                        "sub out: %p %p", cl->buf, cl->buf->pos);
549         if (cl->buf == b) {
550             ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
551                           "the same buf was used in sub");
552             ngx_debug_point();
553             return NGX_ERROR;
554         }
555         b = cl->buf;
556     }
557 #endif
558 
559     rc = ngx_http_next_body_filter(r, ctx->out);
560 
561     if (ctx->busy == NULL) {
562         ctx->busy = ctx->out;
563 
564     } else {
565         for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ }
566         cl->next = ctx->out;
567     }
568 
569     ctx->out = NULL;
570     ctx->last_out = &ctx->out;
571 
572     while (ctx->busy) {
573 
574         cl = ctx->busy;
575         b = cl->buf;
576 
577         if (ngx_buf_size(b) != 0) {
578             break;
579         }
580 
581         if (b->shadow) {
582             b->shadow->pos = b->shadow->last;
583         }
584 
585         ctx->busy = cl->next;
586 
587         if (ngx_buf_in_memory(b) || b->in_file) {
588             /* add data bufs only to the free buf chain */
589 
590             cl->next = ctx->free;
591             ctx->free = cl;
592         }
593     }
594 
595     if (ctx->in || ctx->buf) {
596         r->buffered |= NGX_HTTP_SUB_BUFFERED;
597 
598     } else {
599         r->buffered &= ~NGX_HTTP_SUB_BUFFERED;
600     }
601 
602     return rc;
603 }
604 
605 
606 static ngx_int_t
ngx_http_sub_parse(ngx_http_request_t * r,ngx_http_sub_ctx_t * ctx,ngx_uint_t flush)607 ngx_http_sub_parse(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx,
608     ngx_uint_t flush)
609 {
610     u_char                   *p, c;
611     ngx_str_t                *m;
612     ngx_int_t                 offset, start, next, end, len, rc;
613     ngx_uint_t                shift, i, j;
614     ngx_http_sub_match_t     *match;
615     ngx_http_sub_tables_t    *tables;
616     ngx_http_sub_loc_conf_t  *slcf;
617 
618     slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module);
619     tables = ctx->tables;
620     match = ctx->matches->elts;
621 
622     offset = ctx->offset;
623     end = ctx->buf->last - ctx->pos;
624 
625     if (ctx->once) {
626         /* sets start and next to end */
627         offset = end + (ngx_int_t) tables->min_match_len - 1;
628         goto again;
629     }
630 
631     while (offset < end) {
632 
633         c = offset < 0 ? ctx->looked.data[ctx->looked.len + offset]
634                        : ctx->pos[offset];
635 
636         c = ngx_tolower(c);
637 
638         shift = tables->shift[c];
639         if (shift > 0) {
640             offset += shift;
641             continue;
642         }
643 
644         /* a potential match */
645 
646         start = offset - (ngx_int_t) tables->min_match_len + 1;
647 
648         i = ngx_max((ngx_uint_t) tables->index[c], ctx->index);
649         j = tables->index[c + 1];
650 
651         while (i != j) {
652 
653             if (slcf->once && ctx->sub && ctx->sub[i].data) {
654                 goto next;
655             }
656 
657             m = &match[i].match;
658 
659             rc = ngx_http_sub_match(ctx, start, m);
660 
661             if (rc == NGX_DECLINED) {
662                 goto next;
663             }
664 
665             ctx->index = i;
666 
667             if (rc == NGX_AGAIN) {
668                 goto again;
669             }
670 
671             ctx->offset = offset + (ngx_int_t) m->len;
672             next = start + (ngx_int_t) m->len;
673             end = ngx_max(next, 0);
674             rc = NGX_OK;
675 
676             goto done;
677 
678         next:
679 
680             i++;
681         }
682 
683         offset++;
684         ctx->index = 0;
685     }
686 
687     if (flush) {
688         for ( ;; ) {
689             start = offset - (ngx_int_t) tables->min_match_len + 1;
690 
691             if (start >= end) {
692                 break;
693             }
694 
695             for (i = 0; i < ctx->matches->nelts; i++) {
696                 m = &match[i].match;
697 
698                 if (ngx_http_sub_match(ctx, start, m) == NGX_AGAIN) {
699                     goto again;
700                 }
701             }
702 
703             offset++;
704         }
705     }
706 
707 again:
708 
709     ctx->offset = offset;
710     start = offset - (ngx_int_t) tables->min_match_len + 1;
711     next = start;
712     rc = NGX_AGAIN;
713 
714 done:
715 
716     /* send [ - looked.len, start ] to client */
717 
718     ctx->saved.len = ctx->looked.len + ngx_min(start, 0);
719     ngx_memcpy(ctx->saved.data, ctx->looked.data, ctx->saved.len);
720 
721     ctx->copy_start = ctx->pos;
722     ctx->copy_end = ctx->pos + ngx_max(start, 0);
723 
724     /* save [ next, end ] in looked */
725 
726     len = ngx_min(next, 0);
727     p = ctx->looked.data;
728     p = ngx_movemem(p, p + ctx->looked.len + len, - len);
729 
730     len = ngx_max(next, 0);
731     p = ngx_cpymem(p, ctx->pos + len, end - len);
732     ctx->looked.len = p - ctx->looked.data;
733 
734     /* update position */
735 
736     ctx->pos += end;
737     ctx->offset -= end;
738 
739     return rc;
740 }
741 
742 
743 static ngx_int_t
ngx_http_sub_match(ngx_http_sub_ctx_t * ctx,ngx_int_t start,ngx_str_t * m)744 ngx_http_sub_match(ngx_http_sub_ctx_t *ctx, ngx_int_t start, ngx_str_t *m)
745 {
746     u_char  *p, *last, *pat, *pat_end;
747 
748     pat = m->data;
749     pat_end = m->data + m->len;
750 
751     if (start >= 0) {
752         p = ctx->pos + start;
753 
754     } else {
755         last = ctx->looked.data + ctx->looked.len;
756         p = last + start;
757 
758         while (p < last && pat < pat_end) {
759             if (ngx_tolower(*p) != *pat) {
760                 return NGX_DECLINED;
761             }
762 
763             p++;
764             pat++;
765         }
766 
767         p = ctx->pos;
768     }
769 
770     while (p < ctx->buf->last && pat < pat_end) {
771         if (ngx_tolower(*p) != *pat) {
772             return NGX_DECLINED;
773         }
774 
775         p++;
776         pat++;
777     }
778 
779     if (pat != pat_end) {
780         /* partial match */
781         return NGX_AGAIN;
782     }
783 
784     return NGX_OK;
785 }
786 
787 
788 static char *
ngx_http_sub_filter(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)789 ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
790 {
791     ngx_http_sub_loc_conf_t *slcf = conf;
792 
793     ngx_str_t                         *value;
794     ngx_http_sub_pair_t               *pair;
795     ngx_http_compile_complex_value_t   ccv;
796 
797     value = cf->args->elts;
798 
799     if (value[1].len == 0) {
800         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty search pattern");
801         return NGX_CONF_ERROR;
802     }
803 
804     if (slcf->pairs == NULL) {
805         slcf->pairs = ngx_array_create(cf->pool, 1,
806                                        sizeof(ngx_http_sub_pair_t));
807         if (slcf->pairs == NULL) {
808             return NGX_CONF_ERROR;
809         }
810     }
811 
812     if (slcf->pairs->nelts == 255) {
813         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
814                            "number of search patterns exceeds 255");
815         return NGX_CONF_ERROR;
816     }
817 
818     ngx_strlow(value[1].data, value[1].data, value[1].len);
819 
820     pair = ngx_array_push(slcf->pairs);
821     if (pair == NULL) {
822         return NGX_CONF_ERROR;
823     }
824 
825     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
826 
827     ccv.cf = cf;
828     ccv.value = &value[1];
829     ccv.complex_value = &pair->match;
830 
831     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
832         return NGX_CONF_ERROR;
833     }
834 
835     if (ccv.complex_value->lengths != NULL) {
836         slcf->dynamic = 1;
837 
838     } else {
839         ngx_strlow(pair->match.value.data, pair->match.value.data,
840                    pair->match.value.len);
841     }
842 
843     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
844 
845     ccv.cf = cf;
846     ccv.value = &value[2];
847     ccv.complex_value = &pair->value;
848 
849     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
850         return NGX_CONF_ERROR;
851     }
852 
853     return NGX_CONF_OK;
854 }
855 
856 
857 static void *
ngx_http_sub_create_conf(ngx_conf_t * cf)858 ngx_http_sub_create_conf(ngx_conf_t *cf)
859 {
860     ngx_http_sub_loc_conf_t  *slcf;
861 
862     slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_sub_loc_conf_t));
863     if (slcf == NULL) {
864         return NULL;
865     }
866 
867     /*
868      * set by ngx_pcalloc():
869      *
870      *     conf->dynamic = 0;
871      *     conf->pairs = NULL;
872      *     conf->tables = NULL;
873      *     conf->types = { NULL };
874      *     conf->types_keys = NULL;
875      *     conf->matches = NULL;
876      */
877 
878     slcf->once = NGX_CONF_UNSET;
879     slcf->last_modified = NGX_CONF_UNSET;
880 
881     return slcf;
882 }
883 
884 
885 static char *
ngx_http_sub_merge_conf(ngx_conf_t * cf,void * parent,void * child)886 ngx_http_sub_merge_conf(ngx_conf_t *cf, void *parent, void *child)
887 {
888     ngx_uint_t                i, n;
889     ngx_http_sub_pair_t      *pairs;
890     ngx_http_sub_match_t     *matches;
891     ngx_http_sub_loc_conf_t  *prev = parent;
892     ngx_http_sub_loc_conf_t  *conf = child;
893 
894     ngx_conf_merge_value(conf->once, prev->once, 1);
895     ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0);
896 
897     if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
898                              &prev->types_keys, &prev->types,
899                              ngx_http_html_default_types)
900         != NGX_OK)
901     {
902         return NGX_CONF_ERROR;
903     }
904 
905     if (conf->pairs == NULL) {
906         conf->dynamic = prev->dynamic;
907         conf->pairs = prev->pairs;
908         conf->matches = prev->matches;
909         conf->tables = prev->tables;
910     }
911 
912     if (conf->pairs && conf->dynamic == 0 && conf->tables == NULL) {
913         pairs = conf->pairs->elts;
914         n = conf->pairs->nelts;
915 
916         matches = ngx_palloc(cf->pool, sizeof(ngx_http_sub_match_t) * n);
917         if (matches == NULL) {
918             return NGX_CONF_ERROR;
919         }
920 
921         for (i = 0; i < n; i++) {
922             matches[i].match = pairs[i].match.value;
923             matches[i].value = &pairs[i].value;
924         }
925 
926         conf->matches = ngx_palloc(cf->pool, sizeof(ngx_array_t));
927         if (conf->matches == NULL) {
928             return NGX_CONF_ERROR;
929         }
930 
931         conf->matches->elts = matches;
932         conf->matches->nelts = n;
933 
934         conf->tables = ngx_palloc(cf->pool, sizeof(ngx_http_sub_tables_t));
935         if (conf->tables == NULL) {
936             return NGX_CONF_ERROR;
937         }
938 
939         ngx_http_sub_init_tables(conf->tables, conf->matches->elts,
940                                  conf->matches->nelts);
941     }
942 
943     return NGX_CONF_OK;
944 }
945 
946 
947 static void
ngx_http_sub_init_tables(ngx_http_sub_tables_t * tables,ngx_http_sub_match_t * match,ngx_uint_t n)948 ngx_http_sub_init_tables(ngx_http_sub_tables_t *tables,
949     ngx_http_sub_match_t *match, ngx_uint_t n)
950 {
951     u_char      c;
952     ngx_uint_t  i, j, min, max, ch;
953 
954     min = match[0].match.len;
955     max = match[0].match.len;
956 
957     for (i = 1; i < n; i++) {
958         min = ngx_min(min, match[i].match.len);
959         max = ngx_max(max, match[i].match.len);
960     }
961 
962     tables->min_match_len = min;
963     tables->max_match_len = max;
964 
965     ngx_http_sub_cmp_index = tables->min_match_len - 1;
966     ngx_sort(match, n, sizeof(ngx_http_sub_match_t), ngx_http_sub_cmp_matches);
967 
968     min = ngx_min(min, 255);
969     ngx_memset(tables->shift, min, 256);
970 
971     ch = 0;
972 
973     for (i = 0; i < n; i++) {
974 
975         for (j = 0; j < min; j++) {
976             c = match[i].match.data[tables->min_match_len - 1 - j];
977             tables->shift[c] = ngx_min(tables->shift[c], (u_char) j);
978         }
979 
980         c = match[i].match.data[tables->min_match_len - 1];
981         while (ch <= (ngx_uint_t) c) {
982             tables->index[ch++] = (u_char) i;
983         }
984     }
985 
986     while (ch < 257) {
987         tables->index[ch++] = (u_char) n;
988     }
989 }
990 
991 
992 static ngx_int_t
ngx_http_sub_cmp_matches(const void * one,const void * two)993 ngx_http_sub_cmp_matches(const void *one, const void *two)
994 {
995     ngx_int_t              c1, c2;
996     ngx_http_sub_match_t  *first, *second;
997 
998     first = (ngx_http_sub_match_t *) one;
999     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
ngx_http_sub_filter_init(ngx_conf_t * cf)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 }
1019