upstream nginx-0.7.36
[nginx.git] / nginx / src / http / modules / ngx_http_addition_filter_module.c
1
2 /*
3  * Copyright (C) Igor Sysoev
4  */
5
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_http.h>
10
11
12 typedef struct {
13     ngx_str_t     before_body;
14     ngx_str_t     after_body;
15
16     ngx_hash_t    types;
17     ngx_array_t  *types_keys;
18 } ngx_http_addition_conf_t;
19
20
21 typedef struct {
22     ngx_uint_t    before_body_sent;
23 } ngx_http_addition_ctx_t;
24
25
26 static void *ngx_http_addition_create_conf(ngx_conf_t *cf);
27 static char *ngx_http_addition_merge_conf(ngx_conf_t *cf, void *parent,
28     void *child);
29 static ngx_int_t ngx_http_addition_filter_init(ngx_conf_t *cf);
30
31
32 static ngx_command_t  ngx_http_addition_commands[] = {
33
34     { ngx_string("add_before_body"),
35       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
36       ngx_conf_set_str_slot,
37       NGX_HTTP_LOC_CONF_OFFSET,
38       offsetof(ngx_http_addition_conf_t, before_body),
39       NULL },
40
41     { ngx_string("add_after_body"),
42       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
43       ngx_conf_set_str_slot,
44       NGX_HTTP_LOC_CONF_OFFSET,
45       offsetof(ngx_http_addition_conf_t, after_body),
46       NULL },
47
48     { ngx_string("addtion_types"),
49       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
50       ngx_http_types_slot,
51       NGX_HTTP_LOC_CONF_OFFSET,
52       offsetof(ngx_http_addition_conf_t, types_keys),
53       &ngx_http_html_default_types[0] },
54
55       ngx_null_command
56 };
57
58
59 static ngx_http_module_t  ngx_http_addition_filter_module_ctx = {
60     NULL,                                  /* preconfiguration */
61     ngx_http_addition_filter_init,         /* postconfiguration */
62
63     NULL,                                  /* create main configuration */
64     NULL,                                  /* init main configuration */
65
66     NULL,                                  /* create server configuration */
67     NULL,                                  /* merge server configuration */
68
69     ngx_http_addition_create_conf,         /* create location configuration */
70     ngx_http_addition_merge_conf           /* merge location configuration */
71 };
72
73
74 ngx_module_t  ngx_http_addition_filter_module = {
75     NGX_MODULE_V1,
76     &ngx_http_addition_filter_module_ctx,  /* module context */
77     ngx_http_addition_commands,            /* module directives */
78     NGX_HTTP_MODULE,                       /* module type */
79     NULL,                                  /* init master */
80     NULL,                                  /* init module */
81     NULL,                                  /* init process */
82     NULL,                                  /* init thread */
83     NULL,                                  /* exit thread */
84     NULL,                                  /* exit process */
85     NULL,                                  /* exit master */
86     NGX_MODULE_V1_PADDING
87 };
88
89
90 static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
91 static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
92
93
94 static ngx_int_t
95 ngx_http_addition_header_filter(ngx_http_request_t *r)
96 {
97     ngx_http_addition_ctx_t   *ctx;
98     ngx_http_addition_conf_t  *conf;
99
100     if (r->headers_out.status != NGX_HTTP_OK || r != r->main) {
101         return ngx_http_next_header_filter(r);
102     }
103
104     conf = ngx_http_get_module_loc_conf(r, ngx_http_addition_filter_module);
105
106     if (conf->before_body.len == 0 && conf->after_body.len == 0) {
107         return ngx_http_next_header_filter(r);
108     }
109
110     if (ngx_http_test_content_type(r, &conf->types) == NULL) {
111         return ngx_http_next_header_filter(r);
112     }
113
114     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_addition_ctx_t));
115     if (ctx == NULL) {
116         return NGX_ERROR;
117     }
118
119     ngx_http_set_ctx(r, ctx, ngx_http_addition_filter_module);
120
121     ngx_http_clear_content_length(r);
122     ngx_http_clear_accept_ranges(r);
123
124     return ngx_http_next_header_filter(r);
125 }
126
127
128 static ngx_int_t
129 ngx_http_addition_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
130 {
131     ngx_int_t                  rc;
132     ngx_uint_t                 last;
133     ngx_chain_t               *cl;
134     ngx_http_request_t        *sr;
135     ngx_http_addition_ctx_t   *ctx;
136     ngx_http_addition_conf_t  *conf;
137
138     if (in == NULL || r->header_only) {
139         return ngx_http_next_body_filter(r, in);
140     }
141
142     ctx = ngx_http_get_module_ctx(r, ngx_http_addition_filter_module);
143
144     if (ctx == NULL) {
145         return ngx_http_next_body_filter(r, in);
146     }
147
148     conf = ngx_http_get_module_loc_conf(r, ngx_http_addition_filter_module);
149
150     if (!ctx->before_body_sent) {
151         ctx->before_body_sent = 1;
152
153         if (conf->before_body.len) {
154             if (ngx_http_subrequest(r, &conf->before_body, NULL, &sr, NULL, 0)
155                 != NGX_OK)
156             {
157                 return NGX_ERROR;
158             }
159         }
160     }
161
162     if (conf->after_body.len == 0) {
163         ngx_http_set_ctx(r, NULL, ngx_http_addition_filter_module);
164         return ngx_http_next_body_filter(r, in);
165     }
166
167     last = 0;
168
169     for (cl = in; cl; cl = cl->next) {
170         if (cl->buf->last_buf) {
171             cl->buf->last_buf = 0;
172             cl->buf->sync = 1;
173             last = 1;
174         }
175     }
176
177     rc = ngx_http_next_body_filter(r, in);
178
179     if (rc == NGX_ERROR || !last || conf->after_body.len == 0) {
180         return rc;
181     }
182
183     if (ngx_http_subrequest(r, &conf->after_body, NULL, &sr, NULL, 0)
184         != NGX_OK)
185     {
186         return NGX_ERROR;
187     }
188
189     ngx_http_set_ctx(r, NULL, ngx_http_addition_filter_module);
190
191     return ngx_http_send_special(r, NGX_HTTP_LAST);
192 }
193
194
195 static ngx_int_t
196 ngx_http_addition_filter_init(ngx_conf_t *cf)
197 {
198     ngx_http_next_header_filter = ngx_http_top_header_filter;
199     ngx_http_top_header_filter = ngx_http_addition_header_filter;
200
201     ngx_http_next_body_filter = ngx_http_top_body_filter;
202     ngx_http_top_body_filter = ngx_http_addition_body_filter;
203
204     return NGX_OK;
205 }
206
207
208 static void *
209 ngx_http_addition_create_conf(ngx_conf_t *cf)
210 {
211     ngx_http_addition_conf_t  *conf;
212
213     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_addition_conf_t));
214     if (conf == NULL) {
215         return NGX_CONF_ERROR;
216     }
217
218     /*
219      * set by ngx_pcalloc():
220      *
221      *     conf->before_body = { 0, NULL };
222      *     conf->after_body = { 0, NULL };
223      *     conf->types = { NULL };
224      *     conf->types_keys = NULL;
225      */
226
227     return conf;
228 }
229
230
231 static char *
232 ngx_http_addition_merge_conf(ngx_conf_t *cf, void *parent, void *child)
233 {
234     ngx_http_addition_conf_t *prev = parent;
235     ngx_http_addition_conf_t *conf = child;
236
237     ngx_conf_merge_str_value(conf->before_body, prev->before_body, "");
238     ngx_conf_merge_str_value(conf->after_body, prev->after_body, "");
239
240     if (ngx_http_merge_types(cf, conf->types_keys, &conf->types,
241                              prev->types_keys, &prev->types,
242                              ngx_http_html_default_types)
243         != NGX_OK)
244     {
245         return NGX_CONF_ERROR;
246     }
247
248     return NGX_CONF_OK;
249 }