Mercurial > hg > nginx
comparison src/http/modules/ngx_http_sub_filter_module.c @ 6228:b9447fc457b4
Sub filter: support of multiple strings to replace.
author | Dmitry Volyntsev <xeioex@nginx.com> |
---|---|
date | Mon, 17 Aug 2015 17:42:02 +0300 |
parents | 5322be87fc02 |
children | 2c045e5b8291 |
comparison
equal
deleted
inserted
replaced
6227:bd55d75a1410 | 6228:b9447fc457b4 |
---|---|
11 | 11 |
12 | 12 |
13 typedef struct { | 13 typedef struct { |
14 ngx_str_t match; | 14 ngx_str_t match; |
15 ngx_http_complex_value_t value; | 15 ngx_http_complex_value_t value; |
16 } ngx_http_sub_match_t; | |
17 | |
18 | |
19 typedef struct { | |
20 ngx_uint_t min_match_len; | |
21 ngx_uint_t max_match_len; | |
22 | |
23 u_char index[257]; | |
24 u_char shift[256]; | |
25 } ngx_http_sub_tables_t; | |
26 | |
27 | |
28 typedef struct { | |
29 ngx_http_sub_tables_t *tables; | |
16 | 30 |
17 ngx_hash_t types; | 31 ngx_hash_t types; |
18 | 32 |
19 ngx_flag_t once; | 33 ngx_flag_t once; |
20 ngx_flag_t last_modified; | 34 ngx_flag_t last_modified; |
21 | 35 |
22 ngx_array_t *types_keys; | 36 ngx_array_t *types_keys; |
37 ngx_array_t *matches; | |
23 } ngx_http_sub_loc_conf_t; | 38 } ngx_http_sub_loc_conf_t; |
24 | 39 |
25 | 40 |
26 typedef enum { | |
27 sub_start_state = 0, | |
28 sub_match_state, | |
29 } ngx_http_sub_state_e; | |
30 | |
31 | |
32 typedef struct { | 41 typedef struct { |
33 ngx_str_t match; | |
34 ngx_str_t saved; | 42 ngx_str_t saved; |
35 ngx_str_t looked; | 43 ngx_str_t looked; |
36 | 44 |
37 ngx_uint_t once; /* unsigned once:1 */ | 45 ngx_uint_t once; /* unsigned once:1 */ |
38 | 46 |
46 ngx_chain_t *out; | 54 ngx_chain_t *out; |
47 ngx_chain_t **last_out; | 55 ngx_chain_t **last_out; |
48 ngx_chain_t *busy; | 56 ngx_chain_t *busy; |
49 ngx_chain_t *free; | 57 ngx_chain_t *free; |
50 | 58 |
51 ngx_str_t sub; | 59 ngx_str_t *sub; |
52 | 60 ngx_uint_t applied; |
53 ngx_uint_t state; | 61 |
62 ngx_int_t offset; | |
63 ngx_uint_t index; | |
54 } ngx_http_sub_ctx_t; | 64 } ngx_http_sub_ctx_t; |
65 | |
66 | |
67 static ngx_uint_t ngx_http_sub_cmp_index; | |
55 | 68 |
56 | 69 |
57 static ngx_int_t ngx_http_sub_output(ngx_http_request_t *r, | 70 static ngx_int_t ngx_http_sub_output(ngx_http_request_t *r, |
58 ngx_http_sub_ctx_t *ctx); | 71 ngx_http_sub_ctx_t *ctx); |
59 static ngx_int_t ngx_http_sub_parse(ngx_http_request_t *r, | 72 static ngx_int_t ngx_http_sub_parse(ngx_http_request_t *r, |
62 static char * ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd, | 75 static char * ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd, |
63 void *conf); | 76 void *conf); |
64 static void *ngx_http_sub_create_conf(ngx_conf_t *cf); | 77 static void *ngx_http_sub_create_conf(ngx_conf_t *cf); |
65 static char *ngx_http_sub_merge_conf(ngx_conf_t *cf, | 78 static char *ngx_http_sub_merge_conf(ngx_conf_t *cf, |
66 void *parent, void *child); | 79 void *parent, void *child); |
80 static void ngx_http_sub_init_tables(ngx_http_sub_tables_t *tables, | |
81 ngx_http_sub_match_t *match, ngx_uint_t n); | |
82 static ngx_int_t ngx_http_sub_cmp_matches(const void *one, const void *two); | |
67 static ngx_int_t ngx_http_sub_filter_init(ngx_conf_t *cf); | 83 static ngx_int_t ngx_http_sub_filter_init(ngx_conf_t *cf); |
68 | 84 |
69 | 85 |
70 static ngx_command_t ngx_http_sub_filter_commands[] = { | 86 static ngx_command_t ngx_http_sub_filter_commands[] = { |
71 | 87 |
142 ngx_http_sub_ctx_t *ctx; | 158 ngx_http_sub_ctx_t *ctx; |
143 ngx_http_sub_loc_conf_t *slcf; | 159 ngx_http_sub_loc_conf_t *slcf; |
144 | 160 |
145 slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module); | 161 slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module); |
146 | 162 |
147 if (slcf->match.len == 0 | 163 if (slcf->matches == NULL |
148 || r->headers_out.content_length_n == 0 | 164 || r->headers_out.content_length_n == 0 |
149 || ngx_http_test_content_type(r, &slcf->types) == NULL) | 165 || ngx_http_test_content_type(r, &slcf->types) == NULL) |
150 { | 166 { |
151 return ngx_http_next_header_filter(r); | 167 return ngx_http_next_header_filter(r); |
152 } | 168 } |
154 ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_sub_ctx_t)); | 170 ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_sub_ctx_t)); |
155 if (ctx == NULL) { | 171 if (ctx == NULL) { |
156 return NGX_ERROR; | 172 return NGX_ERROR; |
157 } | 173 } |
158 | 174 |
159 ctx->saved.data = ngx_pnalloc(r->pool, slcf->match.len); | 175 ctx->saved.data = ngx_pnalloc(r->pool, slcf->tables->max_match_len - 1); |
160 if (ctx->saved.data == NULL) { | 176 if (ctx->saved.data == NULL) { |
161 return NGX_ERROR; | 177 return NGX_ERROR; |
162 } | 178 } |
163 | 179 |
164 ctx->looked.data = ngx_pnalloc(r->pool, slcf->match.len); | 180 ctx->looked.data = ngx_pnalloc(r->pool, slcf->tables->max_match_len - 1); |
165 if (ctx->looked.data == NULL) { | 181 if (ctx->looked.data == NULL) { |
166 return NGX_ERROR; | 182 return NGX_ERROR; |
167 } | 183 } |
168 | 184 |
169 ngx_http_set_ctx(r, ctx, ngx_http_sub_filter_module); | 185 ngx_http_set_ctx(r, ctx, ngx_http_sub_filter_module); |
170 | 186 |
171 ctx->match = slcf->match; | 187 ctx->offset = slcf->tables->min_match_len - 1; |
172 ctx->last_out = &ctx->out; | 188 ctx->last_out = &ctx->out; |
173 | 189 |
174 r->filter_need_in_memory = 1; | 190 r->filter_need_in_memory = 1; |
175 | 191 |
176 if (r == r->main) { | 192 if (r == r->main) { |
192 static ngx_int_t | 208 static ngx_int_t |
193 ngx_http_sub_body_filter(ngx_http_request_t *r, ngx_chain_t *in) | 209 ngx_http_sub_body_filter(ngx_http_request_t *r, ngx_chain_t *in) |
194 { | 210 { |
195 ngx_int_t rc; | 211 ngx_int_t rc; |
196 ngx_buf_t *b; | 212 ngx_buf_t *b; |
213 ngx_str_t *sub; | |
197 ngx_chain_t *cl; | 214 ngx_chain_t *cl; |
198 ngx_http_sub_ctx_t *ctx; | 215 ngx_http_sub_ctx_t *ctx; |
216 ngx_http_sub_match_t *match; | |
199 ngx_http_sub_loc_conf_t *slcf; | 217 ngx_http_sub_loc_conf_t *slcf; |
200 | 218 |
201 ctx = ngx_http_get_module_ctx(r, ngx_http_sub_filter_module); | 219 ctx = ngx_http_get_module_ctx(r, ngx_http_sub_filter_module); |
202 | 220 |
203 if (ctx == NULL) { | 221 if (ctx == NULL) { |
240 ctx->buf = ctx->in->buf; | 258 ctx->buf = ctx->in->buf; |
241 ctx->in = ctx->in->next; | 259 ctx->in = ctx->in->next; |
242 ctx->pos = ctx->buf->pos; | 260 ctx->pos = ctx->buf->pos; |
243 } | 261 } |
244 | 262 |
245 if (ctx->state == sub_start_state) { | |
246 ctx->copy_start = ctx->pos; | |
247 ctx->copy_end = ctx->pos; | |
248 } | |
249 | |
250 b = NULL; | 263 b = NULL; |
251 | 264 |
252 while (ctx->pos < ctx->buf->last) { | 265 while (ctx->pos < ctx->buf->last) { |
253 | |
254 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
255 "saved: \"%V\" state: %d", &ctx->saved, ctx->state); | |
256 | 266 |
257 rc = ngx_http_sub_parse(r, ctx); | 267 rc = ngx_http_sub_parse(r, ctx); |
258 | 268 |
259 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | 269 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
260 "parse: %d, looked: \"%V\" %p-%p", | 270 "parse: %d, looked: \"%V\" %p-%p", |
318 | 328 |
319 *ctx->last_out = cl; | 329 *ctx->last_out = cl; |
320 ctx->last_out = &cl->next; | 330 ctx->last_out = &cl->next; |
321 } | 331 } |
322 | 332 |
323 if (ctx->state == sub_start_state) { | |
324 ctx->copy_start = ctx->pos; | |
325 ctx->copy_end = ctx->pos; | |
326 | |
327 } else { | |
328 ctx->copy_start = NULL; | |
329 ctx->copy_end = NULL; | |
330 } | |
331 | |
332 if (ctx->looked.len > (size_t) (ctx->pos - ctx->buf->pos)) { | |
333 ctx->saved.len = ctx->looked.len - (ctx->pos - ctx->buf->pos); | |
334 ngx_memcpy(ctx->saved.data, ctx->looked.data, ctx->saved.len); | |
335 } | |
336 | |
337 if (rc == NGX_AGAIN) { | 333 if (rc == NGX_AGAIN) { |
338 continue; | 334 continue; |
339 } | 335 } |
340 | 336 |
341 | 337 |
350 | 346 |
351 ngx_memzero(b, sizeof(ngx_buf_t)); | 347 ngx_memzero(b, sizeof(ngx_buf_t)); |
352 | 348 |
353 slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module); | 349 slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module); |
354 | 350 |
355 if (ctx->sub.data == NULL) { | 351 if (ctx->sub == NULL) { |
356 | 352 ctx->sub = ngx_pcalloc(r->pool, sizeof(ngx_str_t) |
357 if (ngx_http_complex_value(r, &slcf->value, &ctx->sub) | 353 * slcf->matches->nelts); |
354 if (ctx->sub == NULL) { | |
355 return NGX_ERROR; | |
356 } | |
357 } | |
358 | |
359 sub = &ctx->sub[ctx->index]; | |
360 | |
361 if (sub->data == NULL) { | |
362 match = slcf->matches->elts; | |
363 | |
364 if (ngx_http_complex_value(r, &match[ctx->index].value, sub) | |
358 != NGX_OK) | 365 != NGX_OK) |
359 { | 366 { |
360 return NGX_ERROR; | 367 return NGX_ERROR; |
361 } | 368 } |
362 } | 369 } |
363 | 370 |
364 if (ctx->sub.len) { | 371 if (sub->len) { |
365 b->memory = 1; | 372 b->memory = 1; |
366 b->pos = ctx->sub.data; | 373 b->pos = sub->data; |
367 b->last = ctx->sub.data + ctx->sub.len; | 374 b->last = sub->data + sub->len; |
368 | 375 |
369 } else { | 376 } else { |
370 b->sync = 1; | 377 b->sync = 1; |
371 } | 378 } |
372 | 379 |
373 *ctx->last_out = cl; | 380 *ctx->last_out = cl; |
374 ctx->last_out = &cl->next; | 381 ctx->last_out = &cl->next; |
375 | 382 |
376 ctx->once = slcf->once; | 383 ctx->index = 0; |
384 ctx->once = slcf->once && (++ctx->applied == slcf->matches->nelts); | |
377 | 385 |
378 continue; | 386 continue; |
379 } | 387 } |
380 | 388 |
381 if (ctx->looked.len | 389 if (ctx->looked.len |
426 | 434 |
427 b->recycled = ctx->buf->recycled; | 435 b->recycled = ctx->buf->recycled; |
428 } | 436 } |
429 | 437 |
430 ctx->buf = NULL; | 438 ctx->buf = NULL; |
431 | |
432 ctx->saved.len = ctx->looked.len; | |
433 ngx_memcpy(ctx->saved.data, ctx->looked.data, ctx->looked.len); | |
434 } | 439 } |
435 | 440 |
436 if (ctx->out == NULL && ctx->busy == NULL) { | 441 if (ctx->out == NULL && ctx->busy == NULL) { |
437 return NGX_OK; | 442 return NGX_OK; |
438 } | 443 } |
511 | 516 |
512 | 517 |
513 static ngx_int_t | 518 static ngx_int_t |
514 ngx_http_sub_parse(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx) | 519 ngx_http_sub_parse(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx) |
515 { | 520 { |
516 u_char *p, *last, *copy_end, ch, match; | 521 u_char *p, *last, *pat, *pat_end, c; |
517 size_t looked, i; | 522 ngx_str_t *m; |
518 ngx_http_sub_state_e state; | 523 ngx_int_t offset, start, next, end, len, rc; |
524 ngx_uint_t shift, i, j; | |
525 ngx_http_sub_match_t *match; | |
526 ngx_http_sub_tables_t *tables; | |
527 ngx_http_sub_loc_conf_t *slcf; | |
528 | |
529 slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module); | |
530 tables = slcf->tables; | |
531 | |
532 offset = ctx->offset; | |
533 end = ctx->buf->last - ctx->pos; | |
519 | 534 |
520 if (ctx->once) { | 535 if (ctx->once) { |
521 ctx->copy_start = ctx->pos; | 536 /* sets start and next to end */ |
522 ctx->copy_end = ctx->buf->last; | 537 offset = end + (ngx_int_t) tables->min_match_len - 1; |
523 ctx->pos = ctx->buf->last; | 538 goto again; |
524 ctx->looked.len = 0; | 539 } |
525 | 540 |
526 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "once"); | 541 while (offset < end) { |
527 | 542 |
528 return NGX_AGAIN; | 543 c = offset < 0 ? ctx->looked.data[ctx->looked.len + offset] |
529 } | 544 : ctx->pos[offset]; |
530 | 545 |
531 state = ctx->state; | 546 c = ngx_tolower(c); |
532 looked = ctx->looked.len; | 547 |
533 last = ctx->buf->last; | 548 shift = tables->shift[c]; |
534 copy_end = ctx->copy_end; | 549 if (shift > 0) { |
535 | 550 offset += shift; |
536 for (p = ctx->pos; p < last; p++) { | 551 continue; |
537 | 552 } |
538 ch = *p; | 553 |
539 ch = ngx_tolower(ch); | 554 /* a potential match */ |
540 | 555 |
541 if (state == sub_start_state) { | 556 start = offset - (ngx_int_t) tables->min_match_len + 1; |
542 | 557 match = slcf->matches->elts; |
543 /* the tight loop */ | 558 |
544 | 559 i = ngx_max(tables->index[c], ctx->index); |
545 match = ctx->match.data[0]; | 560 j = tables->index[c + 1]; |
546 | 561 |
547 for ( ;; ) { | 562 while (i != j) { |
548 if (ch == match) { | 563 |
549 | 564 if (slcf->once && ctx->sub && ctx->sub[i].data) { |
550 if (ctx->match.len == 1) { | 565 goto next; |
551 ctx->pos = p + 1; | 566 } |
552 ctx->copy_end = p; | 567 |
553 | 568 m = &match[i].match; |
554 return NGX_OK; | 569 |
570 pat = m->data; | |
571 pat_end = m->data + m->len; | |
572 | |
573 if (start >= 0) { | |
574 p = ctx->pos + start; | |
575 | |
576 } else { | |
577 last = ctx->looked.data + ctx->looked.len; | |
578 p = last + start; | |
579 | |
580 while (p < last && pat < pat_end) { | |
581 if (ngx_tolower(*p) != *pat) { | |
582 goto next; | |
555 } | 583 } |
556 | 584 |
557 copy_end = p; | 585 p++; |
558 ctx->looked.data[0] = *p; | 586 pat++; |
559 looked = 1; | |
560 state = sub_match_state; | |
561 | |
562 goto match_started; | |
563 } | 587 } |
564 | 588 |
565 if (++p == last) { | 589 p = ctx->pos; |
566 break; | 590 } |
591 | |
592 while (p < ctx->buf->last && pat < pat_end) { | |
593 if (ngx_tolower(*p) != *pat) { | |
594 goto next; | |
567 } | 595 } |
568 | 596 |
569 ch = *p; | |
570 ch = ngx_tolower(ch); | |
571 } | |
572 | |
573 ctx->state = state; | |
574 ctx->pos = p; | |
575 ctx->looked.len = looked; | |
576 ctx->copy_end = p; | |
577 | |
578 if (ctx->copy_start == NULL) { | |
579 ctx->copy_start = ctx->buf->pos; | |
580 } | |
581 | |
582 return NGX_AGAIN; | |
583 | |
584 match_started: | |
585 | |
586 continue; | |
587 } | |
588 | |
589 /* state == sub_match_state */ | |
590 | |
591 if (ch == ctx->match.data[looked]) { | |
592 ctx->looked.data[looked] = *p; | |
593 looked++; | |
594 | |
595 if (looked == ctx->match.len) { | |
596 | |
597 ctx->state = sub_start_state; | |
598 ctx->pos = p + 1; | |
599 ctx->looked.len = 0; | |
600 ctx->saved.len = 0; | |
601 ctx->copy_end = copy_end; | |
602 | |
603 if (ctx->copy_start == NULL && copy_end) { | |
604 ctx->copy_start = ctx->buf->pos; | |
605 } | |
606 | |
607 return NGX_OK; | |
608 } | |
609 | |
610 } else { | |
611 /* | |
612 * check if there is another partial match in previously | |
613 * matched substring to catch cases like "aab" in "aaab" | |
614 */ | |
615 | |
616 ctx->looked.data[looked] = *p; | |
617 looked++; | |
618 | |
619 for (i = 1; i < looked; i++) { | |
620 if (ngx_strncasecmp(ctx->looked.data + i, | |
621 ctx->match.data, looked - i) | |
622 == 0) | |
623 { | |
624 break; | |
625 } | |
626 } | |
627 | |
628 if (i < looked) { | |
629 if (ctx->saved.len > i) { | |
630 ctx->saved.len = i; | |
631 } | |
632 | |
633 if ((size_t) (p + 1 - ctx->buf->pos) >= looked - i) { | |
634 copy_end = p + 1 - (looked - i); | |
635 } | |
636 | |
637 ngx_memmove(ctx->looked.data, ctx->looked.data + i, looked - i); | |
638 looked = looked - i; | |
639 | |
640 } else { | |
641 copy_end = p; | |
642 looked = 0; | |
643 state = sub_start_state; | |
644 } | |
645 | |
646 if (ctx->saved.len) { | |
647 p++; | 597 p++; |
648 goto out; | 598 pat++; |
649 } | 599 } |
650 } | 600 |
651 } | 601 ctx->index = i; |
652 | 602 |
653 ctx->saved.len = 0; | 603 if (pat != pat_end) { |
654 | 604 /* partial match */ |
655 out: | 605 goto again; |
656 | 606 } |
657 ctx->state = state; | 607 |
658 ctx->pos = p; | 608 ctx->offset = offset + (ngx_int_t) m->len; |
659 ctx->looked.len = looked; | 609 next = start + (ngx_int_t) m->len; |
660 | 610 end = ngx_max(next, 0); |
661 ctx->copy_end = (state == sub_start_state) ? p : copy_end; | 611 rc = NGX_OK; |
662 | 612 |
663 if (ctx->copy_start == NULL && ctx->copy_end) { | 613 goto done; |
664 ctx->copy_start = ctx->buf->pos; | 614 |
665 } | 615 next: |
666 | 616 |
667 return NGX_AGAIN; | 617 i++; |
618 } | |
619 | |
620 offset++; | |
621 ctx->index = 0; | |
622 } | |
623 | |
624 again: | |
625 | |
626 ctx->offset = offset; | |
627 start = offset - (ngx_int_t) tables->min_match_len + 1; | |
628 next = start; | |
629 rc = NGX_AGAIN; | |
630 | |
631 done: | |
632 | |
633 /* send [ - looked.len, start ] to client */ | |
634 | |
635 ctx->saved.len = ctx->looked.len + ngx_min(start, 0); | |
636 ngx_memcpy(ctx->saved.data, ctx->looked.data, ctx->saved.len); | |
637 | |
638 ctx->copy_start = ctx->pos; | |
639 ctx->copy_end = ctx->pos + ngx_max(start, 0); | |
640 | |
641 /* save [ next, end ] in looked */ | |
642 | |
643 len = ngx_min(next, 0); | |
644 p = ctx->looked.data; | |
645 p = ngx_movemem(p, p + ctx->looked.len + len, - len); | |
646 | |
647 len = ngx_max(next, 0); | |
648 p = ngx_cpymem(p, ctx->pos + len, end - len); | |
649 ctx->looked.len = p - ctx->looked.data; | |
650 | |
651 /* update position */ | |
652 | |
653 ctx->pos += end; | |
654 ctx->offset -= end; | |
655 | |
656 return rc; | |
668 } | 657 } |
669 | 658 |
670 | 659 |
671 static char * | 660 static char * |
672 ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | 661 ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) |
673 { | 662 { |
674 ngx_http_sub_loc_conf_t *slcf = conf; | 663 ngx_http_sub_loc_conf_t *slcf = conf; |
675 | 664 |
676 ngx_str_t *value; | 665 ngx_str_t *value; |
666 ngx_http_sub_match_t *match; | |
677 ngx_http_compile_complex_value_t ccv; | 667 ngx_http_compile_complex_value_t ccv; |
678 | 668 |
679 if (slcf->match.data) { | |
680 return "is duplicate"; | |
681 } | |
682 | |
683 value = cf->args->elts; | 669 value = cf->args->elts; |
684 | 670 |
671 if (value[1].len == 0) { | |
672 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty search pattern"); | |
673 return NGX_CONF_ERROR; | |
674 } | |
675 | |
676 if (slcf->matches == NULL) { | |
677 slcf->matches = ngx_array_create(cf->pool, 1, | |
678 sizeof(ngx_http_sub_match_t)); | |
679 if (slcf->matches == NULL) { | |
680 return NGX_CONF_ERROR; | |
681 } | |
682 } | |
683 | |
684 if (slcf->matches->nelts == 255) { | |
685 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
686 "number of search patterns exceeds 255"); | |
687 return NGX_CONF_ERROR; | |
688 } | |
689 | |
685 ngx_strlow(value[1].data, value[1].data, value[1].len); | 690 ngx_strlow(value[1].data, value[1].data, value[1].len); |
686 | 691 |
687 slcf->match = value[1]; | 692 match = ngx_array_push(slcf->matches); |
693 if (match == NULL) { | |
694 return NGX_CONF_ERROR; | |
695 } | |
696 | |
697 match->match = value[1]; | |
688 | 698 |
689 ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); | 699 ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); |
690 | 700 |
691 ccv.cf = cf; | 701 ccv.cf = cf; |
692 ccv.value = &value[2]; | 702 ccv.value = &value[2]; |
693 ccv.complex_value = &slcf->value; | 703 ccv.complex_value = &match->value; |
694 | 704 |
695 if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { | 705 if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { |
696 return NGX_CONF_ERROR; | 706 return NGX_CONF_ERROR; |
697 } | 707 } |
698 | 708 |
711 } | 721 } |
712 | 722 |
713 /* | 723 /* |
714 * set by ngx_pcalloc(): | 724 * set by ngx_pcalloc(): |
715 * | 725 * |
716 * conf->match = { 0, NULL }; | 726 * conf->tables = NULL; |
717 * conf->types = { NULL }; | 727 * conf->types = { NULL }; |
718 * conf->types_keys = NULL; | 728 * conf->types_keys = NULL; |
729 * conf->matches = NULL; | |
719 */ | 730 */ |
720 | 731 |
721 slcf->once = NGX_CONF_UNSET; | 732 slcf->once = NGX_CONF_UNSET; |
722 slcf->last_modified = NGX_CONF_UNSET; | 733 slcf->last_modified = NGX_CONF_UNSET; |
723 | 734 |
730 { | 741 { |
731 ngx_http_sub_loc_conf_t *prev = parent; | 742 ngx_http_sub_loc_conf_t *prev = parent; |
732 ngx_http_sub_loc_conf_t *conf = child; | 743 ngx_http_sub_loc_conf_t *conf = child; |
733 | 744 |
734 ngx_conf_merge_value(conf->once, prev->once, 1); | 745 ngx_conf_merge_value(conf->once, prev->once, 1); |
735 ngx_conf_merge_str_value(conf->match, prev->match, ""); | |
736 ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0); | 746 ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0); |
737 | |
738 if (conf->value.value.data == NULL) { | |
739 conf->value = prev->value; | |
740 } | |
741 | 747 |
742 if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types, | 748 if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types, |
743 &prev->types_keys, &prev->types, | 749 &prev->types_keys, &prev->types, |
744 ngx_http_html_default_types) | 750 ngx_http_html_default_types) |
745 != NGX_OK) | 751 != NGX_OK) |
746 { | 752 { |
747 return NGX_CONF_ERROR; | 753 return NGX_CONF_ERROR; |
748 } | 754 } |
749 | 755 |
756 if (conf->matches == NULL) { | |
757 conf->matches = prev->matches; | |
758 conf->tables = prev->tables; | |
759 | |
760 } else { | |
761 conf->tables = ngx_palloc(cf->pool, sizeof(ngx_http_sub_tables_t)); | |
762 if (conf->tables == NULL) { | |
763 return NGX_CONF_ERROR; | |
764 } | |
765 | |
766 ngx_http_sub_init_tables(conf->tables, conf->matches->elts, | |
767 conf->matches->nelts); | |
768 } | |
769 | |
750 return NGX_CONF_OK; | 770 return NGX_CONF_OK; |
771 } | |
772 | |
773 | |
774 static void | |
775 ngx_http_sub_init_tables(ngx_http_sub_tables_t *tables, | |
776 ngx_http_sub_match_t *match, ngx_uint_t n) | |
777 { | |
778 u_char c; | |
779 ngx_uint_t i, j, min, max, ch; | |
780 | |
781 min = match[0].match.len; | |
782 max = match[0].match.len; | |
783 | |
784 for (i = 1; i < n; i++) { | |
785 min = ngx_min(min, match[i].match.len); | |
786 max = ngx_max(max, match[i].match.len); | |
787 } | |
788 | |
789 tables->min_match_len = min; | |
790 tables->max_match_len = max; | |
791 | |
792 ngx_http_sub_cmp_index = tables->min_match_len - 1; | |
793 ngx_sort(match, n, sizeof(ngx_http_sub_match_t), ngx_http_sub_cmp_matches); | |
794 | |
795 min = ngx_min(min, 255); | |
796 ngx_memset(tables->shift, min, 256); | |
797 | |
798 ch = 0; | |
799 | |
800 for (i = 0; i < n; i++) { | |
801 | |
802 for (j = 0; j < min; j++) { | |
803 c = match[i].match.data[tables->min_match_len - 1 - j]; | |
804 tables->shift[c] = ngx_min(tables->shift[c], (u_char) j); | |
805 } | |
806 | |
807 c = match[i].match.data[tables->min_match_len - 1]; | |
808 while (ch <= c) { | |
809 tables->index[ch++] = (u_char) i; | |
810 } | |
811 } | |
812 | |
813 while (ch < 257) { | |
814 tables->index[ch++] = (u_char) n; | |
815 } | |
816 } | |
817 | |
818 | |
819 static ngx_int_t | |
820 ngx_http_sub_cmp_matches(const void *one, const void *two) | |
821 { | |
822 ngx_int_t c1, c2; | |
823 ngx_http_sub_match_t *first, *second; | |
824 | |
825 first = (ngx_http_sub_match_t *) one; | |
826 second = (ngx_http_sub_match_t *) two; | |
827 | |
828 c1 = first->match.data[ngx_http_sub_cmp_index]; | |
829 c2 = second->match.data[ngx_http_sub_cmp_index]; | |
830 | |
831 return c1 - c2; | |
751 } | 832 } |
752 | 833 |
753 | 834 |
754 static ngx_int_t | 835 static ngx_int_t |
755 ngx_http_sub_filter_init(ngx_conf_t *cf) | 836 ngx_http_sub_filter_init(ngx_conf_t *cf) |