src/http/modules/ngx_http_sub_filter_module.c - nginx-1.7.10

Global variables defined

Data types defined

Functions defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_http.h>


  8. typedef struct {
  9.     ngx_str_t                  match;
  10.     ngx_http_complex_value_t   value;

  11.     ngx_hash_t                 types;

  12.     ngx_flag_t                 once;
  13.     ngx_flag_t                 last_modified;

  14.     ngx_array_t               *types_keys;
  15. } ngx_http_sub_loc_conf_t;


  16. typedef enum {
  17.     sub_start_state = 0,
  18.     sub_match_state,
  19. } ngx_http_sub_state_e;


  20. typedef struct {
  21.     ngx_str_t                  match;
  22.     ngx_str_t                  saved;
  23.     ngx_str_t                  looked;

  24.     ngx_uint_t                 once;   /* unsigned  once:1 */

  25.     ngx_buf_t                 *buf;

  26.     u_char                    *pos;
  27.     u_char                    *copy_start;
  28.     u_char                    *copy_end;

  29.     ngx_chain_t               *in;
  30.     ngx_chain_t               *out;
  31.     ngx_chain_t              **last_out;
  32.     ngx_chain_t               *busy;
  33.     ngx_chain_t               *free;

  34.     ngx_str_t                  sub;

  35.     ngx_uint_t                 state;
  36. } ngx_http_sub_ctx_t;


  37. static ngx_int_t ngx_http_sub_output(ngx_http_request_t *r,
  38.     ngx_http_sub_ctx_t *ctx);
  39. static ngx_int_t ngx_http_sub_parse(ngx_http_request_t *r,
  40.     ngx_http_sub_ctx_t *ctx);

  41. static char * ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd,
  42.     void *conf);
  43. static void *ngx_http_sub_create_conf(ngx_conf_t *cf);
  44. static char *ngx_http_sub_merge_conf(ngx_conf_t *cf,
  45.     void *parent, void *child);
  46. static ngx_int_t ngx_http_sub_filter_init(ngx_conf_t *cf);


  47. static ngx_command_t  ngx_http_sub_filter_commands[] = {

  48.     { ngx_string("sub_filter"),
  49.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
  50.       ngx_http_sub_filter,
  51.       NGX_HTTP_LOC_CONF_OFFSET,
  52.       0,
  53.       NULL },

  54.     { ngx_string("sub_filter_types"),
  55.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
  56.       ngx_http_types_slot,
  57.       NGX_HTTP_LOC_CONF_OFFSET,
  58.       offsetof(ngx_http_sub_loc_conf_t, types_keys),
  59.       &ngx_http_html_default_types[0] },

  60.     { ngx_string("sub_filter_once"),
  61.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
  62.       ngx_conf_set_flag_slot,
  63.       NGX_HTTP_LOC_CONF_OFFSET,
  64.       offsetof(ngx_http_sub_loc_conf_t, once),
  65.       NULL },

  66.     { ngx_string("sub_filter_last_modified"),
  67.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
  68.       ngx_conf_set_flag_slot,
  69.       NGX_HTTP_LOC_CONF_OFFSET,
  70.       offsetof(ngx_http_sub_loc_conf_t, last_modified),
  71.       NULL },

  72.       ngx_null_command
  73. };


  74. static ngx_http_module_t  ngx_http_sub_filter_module_ctx = {
  75.     NULL,                                  /* preconfiguration */
  76.     ngx_http_sub_filter_init,              /* postconfiguration */

  77.     NULL,                                  /* create main configuration */
  78.     NULL,                                  /* init main configuration */

  79.     NULL,                                  /* create server configuration */
  80.     NULL,                                  /* merge server configuration */

  81.     ngx_http_sub_create_conf,              /* create location configuration */
  82.     ngx_http_sub_merge_conf                /* merge location configuration */
  83. };


  84. ngx_module_t  ngx_http_sub_filter_module = {
  85.     NGX_MODULE_V1,
  86.     &ngx_http_sub_filter_module_ctx,       /* module context */
  87.     ngx_http_sub_filter_commands,          /* module directives */
  88.     NGX_HTTP_MODULE,                       /* module type */
  89.     NULL,                                  /* init master */
  90.     NULL,                                  /* init module */
  91.     NULL,                                  /* init process */
  92.     NULL,                                  /* init thread */
  93.     NULL,                                  /* exit thread */
  94.     NULL,                                  /* exit process */
  95.     NULL,                                  /* exit master */
  96.     NGX_MODULE_V1_PADDING
  97. };


  98. static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
  99. static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;


  100. static ngx_int_t
  101. ngx_http_sub_header_filter(ngx_http_request_t *r)
  102. {
  103.     ngx_http_sub_ctx_t        *ctx;
  104.     ngx_http_sub_loc_conf_t  *slcf;

  105.     slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module);

  106.     if (slcf->match.len == 0
  107.         || r->headers_out.content_length_n == 0
  108.         || ngx_http_test_content_type(r, &slcf->types) == NULL)
  109.     {
  110.         return ngx_http_next_header_filter(r);
  111.     }

  112.     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_sub_ctx_t));
  113.     if (ctx == NULL) {
  114.         return NGX_ERROR;
  115.     }

  116.     ctx->saved.data = ngx_pnalloc(r->pool, slcf->match.len);
  117.     if (ctx->saved.data == NULL) {
  118.         return NGX_ERROR;
  119.     }

  120.     ctx->looked.data = ngx_pnalloc(r->pool, slcf->match.len);
  121.     if (ctx->looked.data == NULL) {
  122.         return NGX_ERROR;
  123.     }

  124.     ngx_http_set_ctx(r, ctx, ngx_http_sub_filter_module);

  125.     ctx->match = slcf->match;
  126.     ctx->last_out = &ctx->out;

  127.     r->filter_need_in_memory = 1;

  128.     if (r == r->main) {
  129.         ngx_http_clear_content_length(r);

  130.         if (!slcf->last_modified) {
  131.             ngx_http_clear_last_modified(r);
  132.             ngx_http_clear_etag(r);

  133.         } else {
  134.             ngx_http_weak_etag(r);
  135.         }
  136.     }

  137.     return ngx_http_next_header_filter(r);
  138. }


  139. static ngx_int_t
  140. ngx_http_sub_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
  141. {
  142.     ngx_int_t                  rc;
  143.     ngx_buf_t                 *b;
  144.     ngx_chain_t               *cl;
  145.     ngx_http_sub_ctx_t        *ctx;
  146.     ngx_http_sub_loc_conf_t   *slcf;

  147.     ctx = ngx_http_get_module_ctx(r, ngx_http_sub_filter_module);

  148.     if (ctx == NULL) {
  149.         return ngx_http_next_body_filter(r, in);
  150.     }

  151.     if ((in == NULL
  152.          && ctx->buf == NULL
  153.          && ctx->in == NULL
  154.          && ctx->busy == NULL))
  155.     {
  156.         return ngx_http_next_body_filter(r, in);
  157.     }

  158.     if (ctx->once && (ctx->buf == NULL || ctx->in == NULL)) {

  159.         if (ctx->busy) {
  160.             if (ngx_http_sub_output(r, ctx) == NGX_ERROR) {
  161.                 return NGX_ERROR;
  162.             }
  163.         }

  164.         return ngx_http_next_body_filter(r, in);
  165.     }

  166.     /* add the incoming chain to the chain ctx->in */

  167.     if (in) {
  168.         if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
  169.             return NGX_ERROR;
  170.         }
  171.     }

  172.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  173.                    "http sub filter \"%V\"", &r->uri);

  174.     while (ctx->in || ctx->buf) {

  175.         if (ctx->buf == NULL) {
  176.             ctx->buf = ctx->in->buf;
  177.             ctx->in = ctx->in->next;
  178.             ctx->pos = ctx->buf->pos;
  179.         }

  180.         if (ctx->state == sub_start_state) {
  181.             ctx->copy_start = ctx->pos;
  182.             ctx->copy_end = ctx->pos;
  183.         }

  184.         b = NULL;

  185.         while (ctx->pos < ctx->buf->last) {

  186.             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  187.                            "saved: \"%V\" state: %d", &ctx->saved, ctx->state);

  188.             rc = ngx_http_sub_parse(r, ctx);

  189.             ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  190.                            "parse: %d, looked: \"%V\" %p-%p",
  191.                            rc, &ctx->looked, ctx->copy_start, ctx->copy_end);

  192.             if (rc == NGX_ERROR) {
  193.                 return rc;
  194.             }

  195.             if (ctx->saved.len) {

  196.                 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  197.                                "saved: \"%V\"", &ctx->saved);

  198.                 cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
  199.                 if (cl == NULL) {
  200.                     return NGX_ERROR;
  201.                 }

  202.                 b = cl->buf;

  203.                 ngx_memzero(b, sizeof(ngx_buf_t));

  204.                 b->pos = ngx_pnalloc(r->pool, ctx->saved.len);
  205.                 if (b->pos == NULL) {
  206.                     return NGX_ERROR;
  207.                 }

  208.                 ngx_memcpy(b->pos, ctx->saved.data, ctx->saved.len);
  209.                 b->last = b->pos + ctx->saved.len;
  210.                 b->memory = 1;

  211.                 *ctx->last_out = cl;
  212.                 ctx->last_out = &cl->next;

  213.                 ctx->saved.len = 0;
  214.             }

  215.             if (ctx->copy_start != ctx->copy_end) {

  216.                 cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
  217.                 if (cl == NULL) {
  218.                     return NGX_ERROR;
  219.                 }

  220.                 b = cl->buf;

  221.                 ngx_memcpy(b, ctx->buf, sizeof(ngx_buf_t));

  222.                 b->pos = ctx->copy_start;
  223.                 b->last = ctx->copy_end;
  224.                 b->shadow = NULL;
  225.                 b->last_buf = 0;
  226.                 b->last_in_chain = 0;
  227.                 b->recycled = 0;

  228.                 if (b->in_file) {
  229.                     b->file_last = b->file_pos + (b->last - ctx->buf->pos);
  230.                     b->file_pos += b->pos - ctx->buf->pos;
  231.                 }

  232.                 *ctx->last_out = cl;
  233.                 ctx->last_out = &cl->next;
  234.             }

  235.             if (ctx->state == sub_start_state) {
  236.                 ctx->copy_start = ctx->pos;
  237.                 ctx->copy_end = ctx->pos;

  238.             } else {
  239.                 ctx->copy_start = NULL;
  240.                 ctx->copy_end = NULL;
  241.             }

  242.             if (ctx->looked.len > (size_t) (ctx->pos - ctx->buf->pos)) {
  243.                 ctx->saved.len = ctx->looked.len - (ctx->pos - ctx->buf->pos);
  244.                 ngx_memcpy(ctx->saved.data, ctx->looked.data, ctx->saved.len);
  245.             }

  246.             if (rc == NGX_AGAIN) {
  247.                 continue;
  248.             }


  249.             /* rc == NGX_OK */

  250.             cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
  251.             if (cl == NULL) {
  252.                 return NGX_ERROR;
  253.             }

  254.             b = cl->buf;

  255.             ngx_memzero(b, sizeof(ngx_buf_t));

  256.             slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module);

  257.             if (ctx->sub.data == NULL) {

  258.                 if (ngx_http_complex_value(r, &slcf->value, &ctx->sub)
  259.                     != NGX_OK)
  260.                 {
  261.                     return NGX_ERROR;
  262.                 }
  263.             }

  264.             if (ctx->sub.len) {
  265.                 b->memory = 1;
  266.                 b->pos = ctx->sub.data;
  267.                 b->last = ctx->sub.data + ctx->sub.len;

  268.             } else {
  269.                 b->sync = 1;
  270.             }

  271.             *ctx->last_out = cl;
  272.             ctx->last_out = &cl->next;

  273.             ctx->once = slcf->once;

  274.             continue;
  275.         }

  276.         if (ctx->looked.len
  277.             && (ctx->buf->last_buf || ctx->buf->last_in_chain))
  278.         {
  279.             cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
  280.             if (cl == NULL) {
  281.                 return NGX_ERROR;
  282.             }

  283.             b = cl->buf;

  284.             ngx_memzero(b, sizeof(ngx_buf_t));

  285.             b->pos = ctx->looked.data;
  286.             b->last = b->pos + ctx->looked.len;
  287.             b->memory = 1;

  288.             *ctx->last_out = cl;
  289.             ctx->last_out = &cl->next;

  290.             ctx->looked.len = 0;
  291.         }

  292.         if (ctx->buf->last_buf || ctx->buf->flush || ctx->buf->sync
  293.             || ngx_buf_in_memory(ctx->buf))
  294.         {
  295.             if (b == NULL) {
  296.                 cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
  297.                 if (cl == NULL) {
  298.                     return NGX_ERROR;
  299.                 }

  300.                 b = cl->buf;

  301.                 ngx_memzero(b, sizeof(ngx_buf_t));

  302.                 b->sync = 1;

  303.                 *ctx->last_out = cl;
  304.                 ctx->last_out = &cl->next;
  305.             }

  306.             b->last_buf = ctx->buf->last_buf;
  307.             b->last_in_chain = ctx->buf->last_in_chain;
  308.             b->flush = ctx->buf->flush;
  309.             b->shadow = ctx->buf;

  310.             b->recycled = ctx->buf->recycled;
  311.         }

  312.         ctx->buf = NULL;

  313.         ctx->saved.len = ctx->looked.len;
  314.         ngx_memcpy(ctx->saved.data, ctx->looked.data, ctx->looked.len);
  315.     }

  316.     if (ctx->out == NULL && ctx->busy == NULL) {
  317.         return NGX_OK;
  318.     }

  319.     return ngx_http_sub_output(r, ctx);
  320. }


  321. static ngx_int_t
  322. ngx_http_sub_output(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx)
  323. {
  324.     ngx_int_t     rc;
  325.     ngx_buf_t    *b;
  326.     ngx_chain_t  *cl;

  327. #if 1
  328.     b = NULL;
  329.     for (cl = ctx->out; cl; cl = cl->next) {
  330.         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  331.                        "sub out: %p %p", cl->buf, cl->buf->pos);
  332.         if (cl->buf == b) {
  333.             ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
  334.                           "the same buf was used in sub");
  335.             ngx_debug_point();
  336.             return NGX_ERROR;
  337.         }
  338.         b = cl->buf;
  339.     }
  340. #endif

  341.     rc = ngx_http_next_body_filter(r, ctx->out);

  342.     if (ctx->busy == NULL) {
  343.         ctx->busy = ctx->out;

  344.     } else {
  345.         for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ }
  346.         cl->next = ctx->out;
  347.     }

  348.     ctx->out = NULL;
  349.     ctx->last_out = &ctx->out;

  350.     while (ctx->busy) {

  351.         cl = ctx->busy;
  352.         b = cl->buf;

  353.         if (ngx_buf_size(b) != 0) {
  354.             break;
  355.         }

  356.         if (b->shadow) {
  357.             b->shadow->pos = b->shadow->last;
  358.         }

  359.         ctx->busy = cl->next;

  360.         if (ngx_buf_in_memory(b) || b->in_file) {
  361.             /* add data bufs only to the free buf chain */

  362.             cl->next = ctx->free;
  363.             ctx->free = cl;
  364.         }
  365.     }

  366.     if (ctx->in || ctx->buf) {
  367.         r->buffered |= NGX_HTTP_SUB_BUFFERED;

  368.     } else {
  369.         r->buffered &= ~NGX_HTTP_SUB_BUFFERED;
  370.     }

  371.     return rc;
  372. }


  373. static ngx_int_t
  374. ngx_http_sub_parse(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx)
  375. {
  376.     u_char                *p, *last, *copy_end, ch, match;
  377.     size_t                 looked, i;
  378.     ngx_http_sub_state_e   state;

  379.     if (ctx->once) {
  380.         ctx->copy_start = ctx->pos;
  381.         ctx->copy_end = ctx->buf->last;
  382.         ctx->pos = ctx->buf->last;
  383.         ctx->looked.len = 0;

  384.         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "once");

  385.         return NGX_AGAIN;
  386.     }

  387.     state = ctx->state;
  388.     looked = ctx->looked.len;
  389.     last = ctx->buf->last;
  390.     copy_end = ctx->copy_end;

  391.     for (p = ctx->pos; p < last; p++) {

  392.         ch = *p;
  393.         ch = ngx_tolower(ch);

  394.         if (state == sub_start_state) {

  395.             /* the tight loop */

  396.             match = ctx->match.data[0];

  397.             for ( ;; ) {
  398.                 if (ch == match) {

  399.                     if (ctx->match.len == 1) {
  400.                         ctx->pos = p + 1;
  401.                         ctx->copy_end = p;

  402.                         return NGX_OK;
  403.                     }

  404.                     copy_end = p;
  405.                     ctx->looked.data[0] = *p;
  406.                     looked = 1;
  407.                     state = sub_match_state;

  408.                     goto match_started;
  409.                 }

  410.                 if (++p == last) {
  411.                     break;
  412.                 }

  413.                 ch = *p;
  414.                 ch = ngx_tolower(ch);
  415.             }

  416.             ctx->state = state;
  417.             ctx->pos = p;
  418.             ctx->looked.len = looked;
  419.             ctx->copy_end = p;

  420.             if (ctx->copy_start == NULL) {
  421.                 ctx->copy_start = ctx->buf->pos;
  422.             }

  423.             return NGX_AGAIN;

  424.         match_started:

  425.             continue;
  426.         }

  427.         /* state == sub_match_state */

  428.         if (ch == ctx->match.data[looked]) {
  429.             ctx->looked.data[looked] = *p;
  430.             looked++;

  431.             if (looked == ctx->match.len) {

  432.                 ctx->state = sub_start_state;
  433.                 ctx->pos = p + 1;
  434.                 ctx->looked.len = 0;
  435.                 ctx->saved.len = 0;
  436.                 ctx->copy_end = copy_end;

  437.                 if (ctx->copy_start == NULL && copy_end) {
  438.                     ctx->copy_start = ctx->buf->pos;
  439.                 }

  440.                 return NGX_OK;
  441.             }

  442.         } else {
  443.             /*
  444.              * check if there is another partial match in previously
  445.              * matched substring to catch cases like "aab" in "aaab"
  446.              */

  447.             ctx->looked.data[looked] = *p;
  448.             looked++;

  449.             for (i = 1; i < looked; i++) {
  450.                 if (ngx_strncasecmp(ctx->looked.data + i,
  451.                                     ctx->match.data, looked - i)
  452.                     == 0)
  453.                 {
  454.                     break;
  455.                 }
  456.             }

  457.             if (i < looked) {
  458.                 if (ctx->saved.len > i) {
  459.                     ctx->saved.len = i;
  460.                 }

  461.                 if ((size_t) (p + 1 - ctx->buf->pos) >= looked - i) {
  462.                     copy_end = p + 1 - (looked - i);
  463.                 }

  464.                 ngx_memmove(ctx->looked.data, ctx->looked.data + i, looked - i);
  465.                 looked = looked - i;

  466.             } else {
  467.                 copy_end = p;
  468.                 looked = 0;
  469.                 state = sub_start_state;
  470.             }

  471.             if (ctx->saved.len) {
  472.                 p++;
  473.                 goto out;
  474.             }
  475.         }
  476.     }

  477.     ctx->saved.len = 0;

  478. out:

  479.     ctx->state = state;
  480.     ctx->pos = p;
  481.     ctx->looked.len = looked;

  482.     ctx->copy_end = (state == sub_start_state) ? p : copy_end;

  483.     if (ctx->copy_start == NULL && ctx->copy_end) {
  484.         ctx->copy_start = ctx->buf->pos;
  485.     }

  486.     return NGX_AGAIN;
  487. }


  488. static char *
  489. ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  490. {
  491.     ngx_http_sub_loc_conf_t *slcf = conf;

  492.     ngx_str_t                         *value;
  493.     ngx_http_compile_complex_value_t   ccv;

  494.     if (slcf->match.data) {
  495.         return "is duplicate";
  496.     }

  497.     value = cf->args->elts;

  498.     ngx_strlow(value[1].data, value[1].data, value[1].len);

  499.     slcf->match = value[1];

  500.     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

  501.     ccv.cf = cf;
  502.     ccv.value = &value[2];
  503.     ccv.complex_value = &slcf->value;

  504.     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
  505.         return NGX_CONF_ERROR;
  506.     }

  507.     return NGX_CONF_OK;
  508. }


  509. static void *
  510. ngx_http_sub_create_conf(ngx_conf_t *cf)
  511. {
  512.     ngx_http_sub_loc_conf_t  *slcf;

  513.     slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_sub_loc_conf_t));
  514.     if (slcf == NULL) {
  515.         return NULL;
  516.     }

  517.     /*
  518.      * set by ngx_pcalloc():
  519.      *
  520.      *     conf->match = { 0, NULL };
  521.      *     conf->types = { NULL };
  522.      *     conf->types_keys = NULL;
  523.      */

  524.     slcf->once = NGX_CONF_UNSET;
  525.     slcf->last_modified = NGX_CONF_UNSET;

  526.     return slcf;
  527. }


  528. static char *
  529. ngx_http_sub_merge_conf(ngx_conf_t *cf, void *parent, void *child)
  530. {
  531.     ngx_http_sub_loc_conf_t *prev = parent;
  532.     ngx_http_sub_loc_conf_t *conf = child;

  533.     ngx_conf_merge_value(conf->once, prev->once, 1);
  534.     ngx_conf_merge_str_value(conf->match, prev->match, "");
  535.     ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0);

  536.     if (conf->value.value.data == NULL) {
  537.         conf->value = prev->value;
  538.     }

  539.     if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
  540.                              &prev->types_keys, &prev->types,
  541.                              ngx_http_html_default_types)
  542.         != NGX_OK)
  543.     {
  544.         return NGX_CONF_ERROR;
  545.     }

  546.     return NGX_CONF_OK;
  547. }


  548. static ngx_int_t
  549. ngx_http_sub_filter_init(ngx_conf_t *cf)
  550. {
  551.     ngx_http_next_header_filter = ngx_http_top_header_filter;
  552.     ngx_http_top_header_filter = ngx_http_sub_header_filter;

  553.     ngx_http_next_body_filter = ngx_http_top_body_filter;
  554.     ngx_http_top_body_filter = ngx_http_sub_body_filter;

  555.     return NGX_OK;
  556. }