~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Nginx/http/modules/ngx_http_sub_filter_module.c

Version: ~ [ nginx-1.4.1 ] ~ [ nginx-1.5.0 ] ~

  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_str_t                  match;
 15     ngx_http_complex_value_t   value;
 16 
 17     ngx_hash_t                 types;
 18 
 19     ngx_flag_t                 once;
 20 
 21     ngx_array_t               *types_keys;
 22 } ngx_http_sub_loc_conf_t;
 23 
 24 
 25 typedef enum {
 26     sub_start_state = 0,
 27     sub_match_state,
 28 } ngx_http_sub_state_e;
 29 
 30 
 31 typedef struct {
 32     ngx_str_t                  match;
 33     ngx_str_t                  saved;
 34     ngx_str_t                  looked;
 35 
 36     ngx_uint_t                 once;   /* unsigned  once:1 */
 37 
 38     ngx_buf_t                 *buf;
 39 
 40     u_char                    *pos;
 41     u_char                    *copy_start;
 42     u_char                    *copy_end;
 43 
 44     ngx_chain_t               *in;
 45     ngx_chain_t               *out;
 46     ngx_chain_t              **last_out;
 47     ngx_chain_t               *busy;
 48     ngx_chain_t               *free;
 49 
 50     ngx_str_t                  sub;
 51 
 52     ngx_uint_t                 state;
 53 } ngx_http_sub_ctx_t;
 54 
 55 
 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);
 60 
 61 static char * ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd,
 62     void *conf);
 63 static void *ngx_http_sub_create_conf(ngx_conf_t *cf);
 64 static char *ngx_http_sub_merge_conf(ngx_conf_t *cf,
 65     void *parent, void *child);
 66 static ngx_int_t ngx_http_sub_filter_init(ngx_conf_t *cf);
 67 
 68 
 69 static ngx_command_t  ngx_http_sub_filter_commands[] = {
 70 
 71     { ngx_string("sub_filter"),
 72       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
 73       ngx_http_sub_filter,
 74       NGX_HTTP_LOC_CONF_OFFSET,
 75       0,
 76       NULL },
 77 
 78     { ngx_string("sub_filter_types"),
 79       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
 80       ngx_http_types_slot,
 81       NGX_HTTP_LOC_CONF_OFFSET,
 82       offsetof(ngx_http_sub_loc_conf_t, types_keys),
 83       &ngx_http_html_default_types[0] },
 84 
 85     { ngx_string("sub_filter_once"),
 86       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
 87       ngx_conf_set_flag_slot,
 88       NGX_HTTP_LOC_CONF_OFFSET,
 89       offsetof(ngx_http_sub_loc_conf_t, once),
 90       NULL },
 91 
 92       ngx_null_command
 93 };
 94 
 95 
 96 static ngx_http_module_t  ngx_http_sub_filter_module_ctx = {
 97     NULL,                                  /* preconfiguration */
 98     ngx_http_sub_filter_init,              /* postconfiguration */
 99 
100     NULL,                                  /* create main configuration */
101     NULL,                                  /* init main configuration */
102 
103     NULL,                                  /* create server configuration */
104     NULL,                                  /* merge server configuration */
105 
106     ngx_http_sub_create_conf,              /* create location configuration */
107     ngx_http_sub_merge_conf                /* merge location configuration */
108 };
109 
110 
111 ngx_module_t  ngx_http_sub_filter_module = {
112     NGX_MODULE_V1,
113     &ngx_http_sub_filter_module_ctx,       /* module context */
114     ngx_http_sub_filter_commands,          /* module directives */
115     NGX_HTTP_MODULE,                       /* module type */
116     NULL,                                  /* init master */
117     NULL,                                  /* init module */
118     NULL,                                  /* init process */
119     NULL,                                  /* init thread */
120     NULL,                                  /* exit thread */
121     NULL,                                  /* exit process */
122     NULL,                                  /* exit master */
123     NGX_MODULE_V1_PADDING
124 };
125 
126 
127 static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
128 static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
129 
130 
131 static ngx_int_t
132 ngx_http_sub_header_filter(ngx_http_request_t *r)
133 {
134     ngx_http_sub_ctx_t        *ctx;
135     ngx_http_sub_loc_conf_t  *slcf;
136 
137     slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module);
138 
139     if (slcf->match.len == 0
140         || r->headers_out.content_length_n == 0
141         || ngx_http_test_content_type(r, &slcf->types) == NULL)
142     {
143         return ngx_http_next_header_filter(r);
144     }
145 
146     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_sub_ctx_t));
147     if (ctx == NULL) {
148         return NGX_ERROR;
149     }
150 
151     ctx->saved.data = ngx_pnalloc(r->pool, slcf->match.len);
152     if (ctx->saved.data == NULL) {
153         return NGX_ERROR;
154     }
155 
156     ctx->looked.data = ngx_pnalloc(r->pool, slcf->match.len);
157     if (ctx->looked.data == NULL) {
158         return NGX_ERROR;
159     }
160 
161     ngx_http_set_ctx(r, ctx, ngx_http_sub_filter_module);
162 
163     ctx->match = slcf->match;
164     ctx->last_out = &ctx->out;
165 
166     r->filter_need_in_memory = 1;
167 
168     if (r == r->main) {
169         ngx_http_clear_content_length(r);
170         ngx_http_clear_last_modified(r);
171         ngx_http_clear_etag(r);
172     }
173 
174     return ngx_http_next_header_filter(r);
175 }
176 
177 
178 static ngx_int_t
179 ngx_http_sub_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
180 {
181     ngx_int_t                  rc;
182     ngx_buf_t                 *b;
183     ngx_chain_t               *cl;
184     ngx_http_sub_ctx_t        *ctx;
185     ngx_http_sub_loc_conf_t   *slcf;
186 
187     ctx = ngx_http_get_module_ctx(r, ngx_http_sub_filter_module);
188 
189     if (ctx == NULL) {
190         return ngx_http_next_body_filter(r, in);
191     }
192 
193     if ((in == NULL
194          && ctx->buf == NULL
195          && ctx->in == NULL
196          && ctx->busy == NULL))
197     {
198         return ngx_http_next_body_filter(r, in);
199     }
200 
201     if (ctx->once && (ctx->buf == NULL || ctx->in == NULL)) {
202 
203         if (ctx->busy) {
204             if (ngx_http_sub_output(r, ctx) == NGX_ERROR) {
205                 return NGX_ERROR;
206             }
207         }
208 
209         return ngx_http_next_body_filter(r, in);
210     }
211 
212     /* add the incoming chain to the chain ctx->in */
213 
214     if (in) {
215         if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
216             return NGX_ERROR;
217         }
218     }
219 
220     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
221                    "http sub filter \"%V\"", &r->uri);
222 
223     while (ctx->in || ctx->buf) {
224 
225         if (ctx->buf == NULL) {
226             ctx->buf = ctx->in->buf;
227             ctx->in = ctx->in->next;
228             ctx->pos = ctx->buf->pos;
229         }
230 
231         if (ctx->state == sub_start_state) {
232             ctx->copy_start = ctx->pos;
233             ctx->copy_end = ctx->pos;
234         }
235 
236         b = NULL;
237 
238         while (ctx->pos < ctx->buf->last) {
239 
240             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
241                            "saved: \"%V\" state: %d", &ctx->saved, ctx->state);
242 
243             rc = ngx_http_sub_parse(r, ctx);
244 
245             ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
246                            "parse: %d, looked: \"%V\" %p-%p",
247                            rc, &ctx->looked, ctx->copy_start, ctx->copy_end);
248 
249             if (rc == NGX_ERROR) {
250                 return rc;
251             }
252 
253             if (ctx->copy_start != ctx->copy_end) {
254 
255                 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
256                                "saved: \"%V\"", &ctx->saved);
257 
258                 if (ctx->saved.len) {
259 
260                     if (ctx->free) {
261                         cl = ctx->free;
262                         ctx->free = ctx->free->next;
263                         b = cl->buf;
264                         ngx_memzero(b, sizeof(ngx_buf_t));
265 
266                     } else {
267                         b = ngx_calloc_buf(r->pool);
268                         if (b == NULL) {
269                             return NGX_ERROR;
270                         }
271 
272                         cl = ngx_alloc_chain_link(r->pool);
273                         if (cl == NULL) {
274                             return NGX_ERROR;
275                         }
276 
277                         cl->buf = b;
278                     }
279 
280                     b->pos = ngx_pnalloc(r->pool, ctx->saved.len);
281                     if (b->pos == NULL) {
282                         return NGX_ERROR;
283                     }
284 
285                     ngx_memcpy(b->pos, ctx->saved.data, ctx->saved.len);
286                     b->last = b->pos + ctx->saved.len;
287                     b->memory = 1;
288 
289                     *ctx->last_out = cl;
290                     ctx->last_out = &cl->next;
291 
292                     ctx->saved.len = 0;
293                 }
294 
295                 if (ctx->free) {
296                     cl = ctx->free;
297                     ctx->free = ctx->free->next;
298                     b = cl->buf;
299 
300                 } else {
301                     b = ngx_alloc_buf(r->pool);
302                     if (b == NULL) {
303                         return NGX_ERROR;
304                     }
305 
306                     cl = ngx_alloc_chain_link(r->pool);
307                     if (cl == NULL) {
308                         return NGX_ERROR;
309                     }
310 
311                     cl->buf = b;
312                 }
313 
314                 ngx_memcpy(b, ctx->buf, sizeof(ngx_buf_t));
315 
316                 b->pos = ctx->copy_start;
317                 b->last = ctx->copy_end;
318                 b->shadow = NULL;
319                 b->last_buf = 0;
320                 b->recycled = 0;
321 
322                 if (b->in_file) {
323                     b->file_last = b->file_pos + (b->last - ctx->buf->pos);
324                     b->file_pos += b->pos - ctx->buf->pos;
325                 }
326 
327                 cl->next = NULL;
328                 *ctx->last_out = cl;
329                 ctx->last_out = &cl->next;
330             }
331 
332             if (ctx->state == sub_start_state) {
333                 ctx->copy_start = ctx->pos;
334                 ctx->copy_end = ctx->pos;
335 
336             } else {
337                 ctx->copy_start = NULL;
338                 ctx->copy_end = NULL;
339             }
340 
341             if (rc == NGX_AGAIN) {
342                 continue;
343             }
344 
345 
346             /* rc == NGX_OK */
347 
348             b = ngx_calloc_buf(r->pool);
349             if (b == NULL) {
350                 return NGX_ERROR;
351             }
352 
353             cl = ngx_alloc_chain_link(r->pool);
354             if (cl == NULL) {
355                 return NGX_ERROR;
356             }
357 
358             slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module);
359 
360             if (ctx->sub.data == NULL) {
361 
362                 if (ngx_http_complex_value(r, &slcf->value, &ctx->sub)
363                     != NGX_OK)
364                 {
365                     return NGX_ERROR;
366                 }
367             }
368 
369             if (ctx->sub.len) {
370                 b->memory = 1;
371                 b->pos = ctx->sub.data;
372                 b->last = ctx->sub.data + ctx->sub.len;
373 
374             } else {
375                 b->sync = 1;
376             }
377 
378             cl->buf = b;
379             cl->next = NULL;
380             *ctx->last_out = cl;
381             ctx->last_out = &cl->next;
382 
383             ctx->once = slcf->once;
384 
385             continue;
386         }
387 
388         if (ctx->buf->last_buf || ngx_buf_in_memory(ctx->buf)) {
389             if (b == NULL) {
390                 if (ctx->free) {
391                     cl = ctx->free;
392                     ctx->free = ctx->free->next;
393                     b = cl->buf;
394                     ngx_memzero(b, sizeof(ngx_buf_t));
395 
396                 } else {
397                     b = ngx_calloc_buf(r->pool);
398                     if (b == NULL) {
399                         return NGX_ERROR;
400                     }
401 
402                     cl = ngx_alloc_chain_link(r->pool);
403                     if (cl == NULL) {
404                         return NGX_ERROR;
405                     }
406 
407                     cl->buf = b;
408                 }
409 
410                 b->sync = 1;
411 
412                 cl->next = NULL;
413                 *ctx->last_out = cl;
414                 ctx->last_out = &cl->next;
415             }
416 
417             b->last_buf = ctx->buf->last_buf;
418             b->shadow = ctx->buf;
419 
420             b->recycled = ctx->buf->recycled;
421         }
422 
423         ctx->buf = NULL;
424 
425         ctx->saved.len = ctx->looked.len;
426         ngx_memcpy(ctx->saved.data, ctx->looked.data, ctx->looked.len);
427     }
428 
429     if (ctx->out == NULL && ctx->busy == NULL) {
430         return NGX_OK;
431     }
432 
433     return ngx_http_sub_output(r, ctx);
434 }
435 
436 
437 static ngx_int_t
438 ngx_http_sub_output(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx)
439 {
440     ngx_int_t     rc;
441     ngx_buf_t    *b;
442     ngx_chain_t  *cl;
443 
444 #if 1
445     b = NULL;
446     for (cl = ctx->out; cl; cl = cl->next) {
447         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
448                        "sub out: %p %p", cl->buf, cl->buf->pos);
449         if (cl->buf == b) {
450             ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
451                           "the same buf was used in sub");
452             ngx_debug_point();
453             return NGX_ERROR;
454         }
455         b = cl->buf;
456     }
457 #endif
458 
459     rc = ngx_http_next_body_filter(r, ctx->out);
460 
461     if (ctx->busy == NULL) {
462         ctx->busy = ctx->out;
463 
464     } else {
465         for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ }
466         cl->next = ctx->out;
467     }
468 
469     ctx->out = NULL;
470     ctx->last_out = &ctx->out;
471 
472     while (ctx->busy) {
473 
474         cl = ctx->busy;
475         b = cl->buf;
476 
477         if (ngx_buf_size(b) != 0) {
478             break;
479         }
480 
481         if (b->shadow) {
482             b->shadow->pos = b->shadow->last;
483         }
484 
485         ctx->busy = cl->next;
486 
487         if (ngx_buf_in_memory(b) || b->in_file) {
488             /* add data bufs only to the free buf chain */
489 
490             cl->next = ctx->free;
491             ctx->free = cl;
492         }
493     }
494 
495     if (ctx->in || ctx->buf) {
496         r->buffered |= NGX_HTTP_SUB_BUFFERED;
497 
498     } else {
499         r->buffered &= ~NGX_HTTP_SUB_BUFFERED;
500     }
501 
502     return rc;
503 }
504 
505 
506 static ngx_int_t
507 ngx_http_sub_parse(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx)
508 {
509     u_char                *p, *last, *copy_end, ch, match;
510     size_t                 looked;
511     ngx_http_sub_state_e   state;
512 
513     if (ctx->once) {
514         ctx->copy_start = ctx->pos;
515         ctx->copy_end = ctx->buf->last;
516         ctx->pos = ctx->buf->last;
517         ctx->looked.len = 0;
518 
519         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "once");
520 
521         return NGX_AGAIN;
522     }
523 
524     state = ctx->state;
525     looked = ctx->looked.len;
526     last = ctx->buf->last;
527     copy_end = ctx->copy_end;
528 
529     for (p = ctx->pos; p < last; p++) {
530 
531         ch = *p;
532         ch = ngx_tolower(ch);
533 
534         if (state == sub_start_state) {
535 
536             /* the tight loop */
537 
538             match = ctx->match.data[0];
539 
540             for ( ;; ) {
541                 if (ch == match) {
542                     copy_end = p;
543                     ctx->looked.data[0] = *p;
544                     looked = 1;
545                     state = sub_match_state;
546 
547                     goto match_started;
548                 }
549 
550                 if (++p == last) {
551                     break;
552                 }
553 
554                 ch = *p;
555                 ch = ngx_tolower(ch);
556             }
557 
558             ctx->state = state;
559             ctx->pos = p;
560             ctx->looked.len = looked;
561             ctx->copy_end = p;
562 
563             if (ctx->copy_start == NULL) {
564                 ctx->copy_start = ctx->buf->pos;
565             }
566 
567             return NGX_AGAIN;
568 
569         match_started:
570 
571             continue;
572         }
573 
574         /* state == sub_match_state */
575 
576         if (ch == ctx->match.data[looked]) {
577             ctx->looked.data[looked] = *p;
578             looked++;
579 
580             if (looked == ctx->match.len) {
581                 if ((size_t) (p - ctx->pos) < looked) {
582                     ctx->saved.len = 0;
583                 }
584 
585                 ctx->state = sub_start_state;
586                 ctx->pos = p + 1;
587                 ctx->looked.len = 0;
588                 ctx->copy_end = copy_end;
589 
590                 if (ctx->copy_start == NULL && copy_end) {
591                     ctx->copy_start = ctx->buf->pos;
592                 }
593 
594                 return NGX_OK;
595             }
596 
597         } else if (ch == ctx->match.data[0]) {
598             copy_end = p;
599             ctx->looked.data[0] = *p;
600             looked = 1;
601 
602         } else {
603             copy_end = p;
604             looked = 0;
605             state = sub_start_state;
606         }
607     }
608 
609     ctx->state = state;
610     ctx->pos = p;
611     ctx->looked.len = looked;
612 
613     ctx->copy_end = (state == sub_start_state) ? p : copy_end;
614 
615     if (ctx->copy_start == NULL && ctx->copy_end) {
616         ctx->copy_start = ctx->buf->pos;
617     }
618 
619     return NGX_AGAIN;
620 }
621 
622 
623 static char *
624 ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
625 {
626     ngx_http_sub_loc_conf_t *slcf = conf;
627 
628     ngx_str_t                         *value;
629     ngx_http_compile_complex_value_t   ccv;
630 
631     if (slcf->match.data) {
632         return "is duplicate";
633     }
634 
635     value = cf->args->elts;
636 
637     ngx_strlow(value[1].data, value[1].data, value[1].len);
638 
639     slcf->match = value[1];
640 
641     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
642 
643     ccv.cf = cf;
644     ccv.value = &value[2];
645     ccv.complex_value = &slcf->value;
646 
647     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
648         return NGX_CONF_ERROR;
649     }
650 
651     return NGX_CONF_OK;
652 }
653 
654 
655 static void *
656 ngx_http_sub_create_conf(ngx_conf_t *cf)
657 {
658     ngx_http_sub_loc_conf_t  *slcf;
659 
660     slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_sub_loc_conf_t));
661     if (slcf == NULL) {
662         return NULL;
663     }
664 
665     /*
666      * set by ngx_pcalloc():
667      *
668      *     conf->match = { 0, NULL };
669      *     conf->sub = { 0, NULL };
670      *     conf->sub_lengths = NULL;
671      *     conf->sub_values = NULL;
672      *     conf->types = { NULL };
673      *     conf->types_keys = NULL;
674      */
675 
676     slcf->once = NGX_CONF_UNSET;
677 
678     return slcf;
679 }
680 
681 
682 static char *
683 ngx_http_sub_merge_conf(ngx_conf_t *cf, void *parent, void *child)
684 {
685     ngx_http_sub_loc_conf_t *prev = parent;
686     ngx_http_sub_loc_conf_t *conf = child;
687 
688     ngx_conf_merge_value(conf->once, prev->once, 1);
689     ngx_conf_merge_str_value(conf->match, prev->match, "");
690 
691     if (conf->value.value.data == NULL) {
692         conf->value = prev->value;
693     }
694 
695     if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
696                              &prev->types_keys, &prev->types,
697                              ngx_http_html_default_types)
698         != NGX_OK)
699     {
700         return NGX_CONF_ERROR;
701     }
702 
703     return NGX_CONF_OK;
704 }
705 
706 
707 static ngx_int_t
708 ngx_http_sub_filter_init(ngx_conf_t *cf)
709 {
710     ngx_http_next_header_filter = ngx_http_top_header_filter;
711     ngx_http_top_header_filter = ngx_http_sub_header_filter;
712 
713     ngx_http_next_body_filter = ngx_http_top_body_filter;
714     ngx_http_top_body_filter = ngx_http_sub_body_filter;
715 
716     return NGX_OK;
717 }
718 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.