Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ngx_http_sub_filter_module.c
Go to the documentation of this file.
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 {
16 
18 
20 
23 
24 
25 typedef enum {
29 
30 
31 typedef struct {
35 
36  ngx_uint_t once; /* unsigned once:1 */
37 
39 
40  u_char *pos;
41  u_char *copy_start;
42  u_char *copy_end;
43 
49 
51 
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"),
73  ngx_http_sub_filter,
75  0,
76  NULL },
77 
78  { ngx_string("sub_filter_types"),
82  offsetof(ngx_http_sub_loc_conf_t, types_keys),
84 
85  { ngx_string("sub_filter_once"),
89  offsetof(ngx_http_sub_loc_conf_t, once),
90  NULL },
91 
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 
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 */
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 {
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) {
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;
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 
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 
241  "saved: \"%V\" state: %d", &ctx->saved, ctx->state);
242 
243  rc = ngx_http_sub_parse(r, ctx);
244 
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 
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) {
448  "sub out: %p %p", cl->buf, cl->buf->pos);
449  if (cl->buf == b) {
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) {
497 
498  } else {
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 
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;
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 
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 {
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,
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 }