3 * Copyright (C) Igor Sysoev
7 #include <ngx_config.h>
16 ngx_array_t
*types
; /* array of ngx_str_t */
18 ngx_array_t
*sub_lengths
;
19 ngx_array_t
*sub_values
;
22 } ngx_http_sub_loc_conf_t
;
28 } ngx_http_sub_state_e
;
34 ngx_uint_t once
; /* unsigned once:1 */
44 ngx_chain_t
**last_out
;
56 static ngx_int_t
ngx_http_sub_output(ngx_http_request_t
*r
,
57 ngx_http_sub_ctx_t
*ctx
);
58 static ngx_int_t
ngx_http_sub_parse(ngx_http_request_t
*r
,
59 ngx_http_sub_ctx_t
*ctx
);
61 static char * ngx_http_sub_filter(ngx_conf_t
*cf
, ngx_command_t
*cmd
,
63 static char *ngx_http_sub_types(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
);
64 static void *ngx_http_sub_create_conf(ngx_conf_t
*cf
);
65 static char *ngx_http_sub_merge_conf(ngx_conf_t
*cf
,
66 void *parent
, void *child
);
67 static ngx_int_t
ngx_http_sub_filter_init(ngx_conf_t
*cf
);
70 static ngx_command_t ngx_http_sub_filter_commands
[] = {
72 { ngx_string("sub_filter"),
73 NGX_HTTP_MAIN_CONF
|NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_CONF_TAKE2
,
75 NGX_HTTP_LOC_CONF_OFFSET
,
79 { ngx_string("sub_filter_types"),
80 NGX_HTTP_MAIN_CONF
|NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_CONF_1MORE
,
82 NGX_HTTP_LOC_CONF_OFFSET
,
86 { ngx_string("sub_filter_once"),
87 NGX_HTTP_MAIN_CONF
|NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_CONF_FLAG
,
88 ngx_conf_set_flag_slot
,
89 NGX_HTTP_LOC_CONF_OFFSET
,
90 offsetof(ngx_http_sub_loc_conf_t
, once
),
97 static ngx_http_module_t ngx_http_sub_filter_module_ctx
= {
98 NULL
, /* preconfiguration */
99 ngx_http_sub_filter_init
, /* postconfiguration */
101 NULL
, /* create main configuration */
102 NULL
, /* init main configuration */
104 NULL
, /* create server configuration */
105 NULL
, /* merge server configuration */
107 ngx_http_sub_create_conf
, /* create location configuration */
108 ngx_http_sub_merge_conf
/* merge location configuration */
112 ngx_module_t ngx_http_sub_filter_module
= {
114 &ngx_http_sub_filter_module_ctx
, /* module context */
115 ngx_http_sub_filter_commands
, /* module directives */
116 NGX_HTTP_MODULE
, /* module type */
117 NULL
, /* init master */
118 NULL
, /* init module */
119 NULL
, /* init process */
120 NULL
, /* init thread */
121 NULL
, /* exit thread */
122 NULL
, /* exit process */
123 NULL
, /* exit master */
124 NGX_MODULE_V1_PADDING
128 static ngx_http_output_header_filter_pt ngx_http_next_header_filter
;
129 static ngx_http_output_body_filter_pt ngx_http_next_body_filter
;
133 ngx_http_sub_header_filter(ngx_http_request_t
*r
)
137 ngx_http_sub_ctx_t
*ctx
;
138 ngx_http_sub_loc_conf_t
*slcf
;
140 slcf
= ngx_http_get_module_loc_conf(r
, ngx_http_sub_filter_module
);
142 if (slcf
->match
.len
== 0
143 || r
->headers_out
.content_type
.len
== 0
144 || r
->headers_out
.content_length_n
== 0)
146 return ngx_http_next_header_filter(r
);
149 type
= slcf
->types
->elts
;
150 for (i
= 0; i
< slcf
->types
->nelts
; i
++) {
151 if (r
->headers_out
.content_type
.len
>= type
[i
].len
152 && ngx_strncasecmp(r
->headers_out
.content_type
.data
,
153 type
[i
].data
, type
[i
].len
) == 0)
159 return ngx_http_next_header_filter(r
);
163 ctx
= ngx_pcalloc(r
->pool
, sizeof(ngx_http_sub_ctx_t
));
168 ngx_http_set_ctx(r
, ctx
, ngx_http_sub_filter_module
);
170 ctx
->match
= slcf
->match
;
171 ctx
->last_out
= &ctx
->out
;
172 ctx
->sub
= slcf
->sub
;
174 r
->filter_need_in_memory
= 1;
177 ngx_http_clear_content_length(r
);
178 ngx_http_clear_last_modified(r
);
181 return ngx_http_next_header_filter(r
);
186 ngx_http_sub_body_filter(ngx_http_request_t
*r
, ngx_chain_t
*in
)
191 ngx_http_sub_ctx_t
*ctx
;
192 ngx_http_sub_loc_conf_t
*slcf
;
194 ctx
= ngx_http_get_module_ctx(r
, ngx_http_sub_filter_module
);
197 return ngx_http_next_body_filter(r
, in
);
203 && ctx
->busy
== NULL
))
205 return ngx_http_next_body_filter(r
, in
);
208 if (ctx
->once
&& (ctx
->buf
== NULL
|| ctx
->in
== NULL
)) {
211 if (ngx_http_sub_output(r
, ctx
) == NGX_ERROR
) {
216 return ngx_http_next_body_filter(r
, in
);
219 /* add the incoming chain to the chain ctx->in */
222 if (ngx_chain_add_copy(r
->pool
, &ctx
->in
, in
) == NGX_ERROR
) {
227 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
228 "http sub filter \"%V\"", &r
->uri
);
230 while (ctx
->in
|| ctx
->buf
) {
232 if (ctx
->buf
== NULL
){
233 ctx
->buf
= ctx
->in
->buf
;
234 ctx
->in
= ctx
->in
->next
;
235 ctx
->pos
= ctx
->buf
->pos
;
238 if (ctx
->state
== sub_start_state
) {
239 ctx
->copy_start
= ctx
->pos
;
240 ctx
->copy_end
= ctx
->pos
;
245 while (ctx
->pos
< ctx
->buf
->last
) {
247 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
248 "saved: %d state: %d", ctx
->saved
, ctx
->state
);
250 rc
= ngx_http_sub_parse(r
, ctx
);
252 ngx_log_debug4(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
253 "parse: %d, looked: %d %p-%p",
254 rc
, ctx
->looked
, ctx
->copy_start
, ctx
->copy_end
);
256 if (rc
== NGX_ERROR
) {
260 if (ctx
->copy_start
!= ctx
->copy_end
) {
262 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
263 "saved: %d", ctx
->saved
);
269 ctx
->free
= ctx
->free
->next
;
271 ngx_memzero(b
, sizeof(ngx_buf_t
));
274 b
= ngx_calloc_buf(r
->pool
);
279 cl
= ngx_alloc_chain_link(r
->pool
);
288 b
->pos
= ctx
->match
.data
;
289 b
->last
= ctx
->match
.data
+ ctx
->saved
;
292 ctx
->last_out
= &cl
->next
;
299 ctx
->free
= ctx
->free
->next
;
303 b
= ngx_alloc_buf(r
->pool
);
308 cl
= ngx_alloc_chain_link(r
->pool
);
316 ngx_memcpy(b
, ctx
->buf
, sizeof(ngx_buf_t
));
318 b
->pos
= ctx
->copy_start
;
319 b
->last
= ctx
->copy_end
;
325 b
->file_last
= b
->file_pos
+ (b
->last
- ctx
->buf
->pos
);
326 b
->file_pos
+= b
->pos
- ctx
->buf
->pos
;
331 ctx
->last_out
= &cl
->next
;
334 if (ctx
->state
== sub_start_state
) {
335 ctx
->copy_start
= ctx
->pos
;
336 ctx
->copy_end
= ctx
->pos
;
339 ctx
->copy_start
= NULL
;
340 ctx
->copy_end
= NULL
;
343 if (rc
== NGX_AGAIN
) {
350 b
= ngx_calloc_buf(r
->pool
);
355 cl
= ngx_alloc_chain_link(r
->pool
);
360 slcf
= ngx_http_get_module_loc_conf(r
, ngx_http_sub_filter_module
);
362 if (ctx
->sub
.data
== NULL
) {
364 if (ngx_http_script_run(r
, &ctx
->sub
, slcf
->sub_lengths
->elts
,
365 0, slcf
->sub_values
->elts
)
374 b
->pos
= ctx
->sub
.data
;
375 b
->last
= ctx
->sub
.data
+ ctx
->sub
.len
;
384 ctx
->last_out
= &cl
->next
;
386 ctx
->once
= slcf
->once
;
391 if (ctx
->buf
->last_buf
|| ngx_buf_in_memory(ctx
->buf
)) {
395 ctx
->free
= ctx
->free
->next
;
397 ngx_memzero(b
, sizeof(ngx_buf_t
));
400 b
= ngx_calloc_buf(r
->pool
);
405 cl
= ngx_alloc_chain_link(r
->pool
);
417 ctx
->last_out
= &cl
->next
;
420 b
->last_buf
= ctx
->buf
->last_buf
;
421 b
->shadow
= ctx
->buf
;
423 b
->recycled
= ctx
->buf
->recycled
;
428 ctx
->saved
= ctx
->looked
;
431 if (ctx
->out
== NULL
&& ctx
->busy
== NULL
) {
435 return ngx_http_sub_output(r
, ctx
);
440 ngx_http_sub_output(ngx_http_request_t
*r
, ngx_http_sub_ctx_t
*ctx
)
448 for (cl
= ctx
->out
; cl
; cl
= cl
->next
) {
449 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
450 "sub out: %p %p", cl
->buf
, cl
->buf
->pos
);
452 ngx_log_error(NGX_LOG_ALERT
, r
->connection
->log
, 0,
453 "the same buf was used in sub");
461 rc
= ngx_http_next_body_filter(r
, ctx
->out
);
463 if (ctx
->busy
== NULL
) {
464 ctx
->busy
= ctx
->out
;
467 for (cl
= ctx
->busy
; cl
->next
; cl
= cl
->next
) { /* void */ }
472 ctx
->last_out
= &ctx
->out
;
479 if (ngx_buf_size(b
) != 0) {
483 #if (NGX_HAVE_WRITE_ZEROCOPY)
484 if (b
->zerocopy_busy
) {
490 b
->shadow
->pos
= b
->shadow
->last
;
493 ctx
->busy
= cl
->next
;
495 if (ngx_buf_in_memory(b
) || b
->in_file
) {
496 /* add data bufs only to the free buf chain */
498 cl
->next
= ctx
->free
;
503 if (ctx
->in
|| ctx
->buf
) {
504 r
->buffered
|= NGX_HTTP_SUB_BUFFERED
;
507 r
->buffered
&= ~NGX_HTTP_SUB_BUFFERED
;
515 ngx_http_sub_parse(ngx_http_request_t
*r
, ngx_http_sub_ctx_t
*ctx
)
517 u_char
*p
, *last
, *copy_end
, ch
, match
;
519 ngx_http_sub_state_e state
;
522 ctx
->copy_start
= ctx
->pos
;
523 ctx
->copy_end
= ctx
->buf
->last
;
524 ctx
->pos
= ctx
->buf
->last
;
527 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0, "once");
533 looked
= ctx
->looked
;
534 last
= ctx
->buf
->last
;
535 copy_end
= ctx
->copy_end
;
537 for (p
= ctx
->pos
; p
< last
; p
++) {
540 ch
= ngx_tolower(ch
);
542 if (state
== sub_start_state
) {
546 match
= ctx
->match
.data
[0];
552 state
= sub_match_state
;
562 ch
= ngx_tolower(ch
);
567 ctx
->looked
= looked
;
570 if (ctx
->copy_start
== NULL
) {
571 ctx
->copy_start
= ctx
->buf
->pos
;
581 /* state == sub_match_state */
583 if (ch
== ctx
->match
.data
[looked
]) {
586 if (looked
== ctx
->match
.len
) {
587 if ((size_t) (p
- ctx
->pos
) < looked
) {
591 ctx
->state
= sub_start_state
;
594 ctx
->copy_end
= copy_end
;
596 if (ctx
->copy_start
== NULL
&& copy_end
) {
597 ctx
->copy_start
= ctx
->buf
->pos
;
603 } else if (ch
== ctx
->match
.data
[0]) {
610 state
= sub_start_state
;
616 ctx
->looked
= looked
;
618 ctx
->copy_end
= (state
== sub_start_state
) ? p
: copy_end
;
620 if (ctx
->copy_start
== NULL
&& ctx
->copy_end
) {
621 ctx
->copy_start
= ctx
->buf
->pos
;
629 ngx_http_sub_filter(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
)
631 ngx_http_sub_loc_conf_t
*slcf
= conf
;
635 ngx_http_script_compile_t sc
;
637 if (slcf
->match
.len
) {
638 return "is duplicate";
641 value
= cf
->args
->elts
;
643 ngx_strlow(value
[1].data
, value
[1].data
, value
[1].len
);
645 slcf
->match
= value
[1];
647 n
= ngx_http_script_variables_count(&value
[2]);
650 slcf
->sub
= value
[2];
654 ngx_memzero(&sc
, sizeof(ngx_http_script_compile_t
));
657 sc
.source
= &value
[2];
658 sc
.lengths
= &slcf
->sub_lengths
;
659 sc
.values
= &slcf
->sub_values
;
661 sc
.complete_lengths
= 1;
662 sc
.complete_values
= 1;
664 if (ngx_http_script_compile(&sc
) != NGX_OK
) {
665 return NGX_CONF_ERROR
;
673 ngx_http_sub_types(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
)
675 ngx_http_sub_loc_conf_t
*slcf
= conf
;
677 ngx_str_t
*value
, *type
;
680 if (slcf
->types
== NULL
) {
681 slcf
->types
= ngx_array_create(cf
->pool
, 4, sizeof(ngx_str_t
));
682 if (slcf
->types
== NULL
) {
683 return NGX_CONF_ERROR
;
686 type
= ngx_array_push(slcf
->types
);
688 return NGX_CONF_ERROR
;
691 type
->len
= sizeof("text/html") - 1;
692 type
->data
= (u_char
*) "text/html";
695 value
= cf
->args
->elts
;
697 for (i
= 1; i
< cf
->args
->nelts
; i
++) {
699 if (ngx_strcmp(value
[i
].data
, "text/html") == 0) {
703 type
= ngx_array_push(slcf
->types
);
705 return NGX_CONF_ERROR
;
708 type
->len
= value
[i
].len
;
710 type
->data
= ngx_pnalloc(cf
->pool
, type
->len
+ 1);
711 if (type
->data
== NULL
) {
712 return NGX_CONF_ERROR
;
715 ngx_cpystrn(type
->data
, value
[i
].data
, type
->len
+ 1);
723 ngx_http_sub_create_conf(ngx_conf_t
*cf
)
725 ngx_http_sub_loc_conf_t
*slcf
;
727 slcf
= ngx_pcalloc(cf
->pool
, sizeof(ngx_http_sub_loc_conf_t
));
729 return NGX_CONF_ERROR
;
733 * set by ngx_pcalloc():
735 * conf->match.len = 0;
736 * conf->match.data = NULL;
738 * conf->sub.data = NULL;
739 * conf->sub_lengths = NULL;
740 * conf->sub_values = NULL;
741 * conf->types = NULL;
744 slcf
->once
= NGX_CONF_UNSET
;
751 ngx_http_sub_merge_conf(ngx_conf_t
*cf
, void *parent
, void *child
)
753 ngx_http_sub_loc_conf_t
*prev
= parent
;
754 ngx_http_sub_loc_conf_t
*conf
= child
;
758 ngx_conf_merge_value(conf
->once
, prev
->once
, 1);
759 ngx_conf_merge_str_value(conf
->match
, prev
->match
, "");
761 if (conf
->sub
.data
== NULL
&& conf
->sub_lengths
== NULL
) {
762 conf
->sub
= prev
->sub
;
763 conf
->sub_lengths
= prev
->sub_lengths
;
764 conf
->sub_values
= prev
->sub_values
;
767 if (conf
->types
== NULL
) {
768 if (prev
->types
== NULL
) {
769 conf
->types
= ngx_array_create(cf
->pool
, 1, sizeof(ngx_str_t
));
770 if (conf
->types
== NULL
) {
771 return NGX_CONF_ERROR
;
774 type
= ngx_array_push(conf
->types
);
776 return NGX_CONF_ERROR
;
779 type
->len
= sizeof("text/html") - 1;
780 type
->data
= (u_char
*) "text/html";
783 conf
->types
= prev
->types
;
792 ngx_http_sub_filter_init(ngx_conf_t
*cf
)
794 ngx_http_next_header_filter
= ngx_http_top_header_filter
;
795 ngx_http_top_header_filter
= ngx_http_sub_header_filter
;
797 ngx_http_next_body_filter
= ngx_http_top_body_filter
;
798 ngx_http_top_body_filter
= ngx_http_sub_body_filter
;