1 
2 /*
3  * Copyright (C) Roman Arutyunyan
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_array_t  *mirror;
15     ngx_flag_t    request_body;
16 } ngx_http_mirror_loc_conf_t;
17 
18 
19 typedef struct {
20     ngx_int_t     status;
21 } ngx_http_mirror_ctx_t;
22 
23 
24 static ngx_int_t ngx_http_mirror_handler(ngx_http_request_t *r);
25 static void ngx_http_mirror_body_handler(ngx_http_request_t *r);
26 static ngx_int_t ngx_http_mirror_handler_internal(ngx_http_request_t *r);
27 static void *ngx_http_mirror_create_loc_conf(ngx_conf_t *cf);
28 static char *ngx_http_mirror_merge_loc_conf(ngx_conf_t *cf, void *parent,
29     void *child);
30 static char *ngx_http_mirror(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
31 static ngx_int_t ngx_http_mirror_init(ngx_conf_t *cf);
32 
33 
34 static ngx_command_t  ngx_http_mirror_commands[] = {
35 
36     { ngx_string("mirror"),
37       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
38       ngx_http_mirror,
39       NGX_HTTP_LOC_CONF_OFFSET,
40       0,
41       NULL },
42 
43     { ngx_string("mirror_request_body"),
44       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
45       ngx_conf_set_flag_slot,
46       NGX_HTTP_LOC_CONF_OFFSET,
47       offsetof(ngx_http_mirror_loc_conf_t, request_body),
48       NULL },
49 
50       ngx_null_command
51 };
52 
53 
54 static ngx_http_module_t  ngx_http_mirror_module_ctx = {
55     NULL,                                  /* preconfiguration */
56     ngx_http_mirror_init,                  /* postconfiguration */
57 
58     NULL,                                  /* create main configuration */
59     NULL,                                  /* init main configuration */
60 
61     NULL,                                  /* create server configuration */
62     NULL,                                  /* merge server configuration */
63 
64     ngx_http_mirror_create_loc_conf,       /* create location configuration */
65     ngx_http_mirror_merge_loc_conf         /* merge location configuration */
66 };
67 
68 
69 ngx_module_t  ngx_http_mirror_module = {
70     NGX_MODULE_V1,
71     &ngx_http_mirror_module_ctx,           /* module context */
72     ngx_http_mirror_commands,              /* module directives */
73     NGX_HTTP_MODULE,                       /* module type */
74     NULL,                                  /* init master */
75     NULL,                                  /* init module */
76     NULL,                                  /* init process */
77     NULL,                                  /* init thread */
78     NULL,                                  /* exit thread */
79     NULL,                                  /* exit process */
80     NULL,                                  /* exit master */
81     NGX_MODULE_V1_PADDING
82 };
83 
84 
85 static ngx_int_t
ngx_http_mirror_handler(ngx_http_request_t * r)86 ngx_http_mirror_handler(ngx_http_request_t *r)
87 {
88     ngx_int_t                    rc;
89     ngx_http_mirror_ctx_t       *ctx;
90     ngx_http_mirror_loc_conf_t  *mlcf;
91 
92     if (r != r->main) {
93         return NGX_DECLINED;
94     }
95 
96     mlcf = ngx_http_get_module_loc_conf(r, ngx_http_mirror_module);
97 
98     if (mlcf->mirror == NULL) {
99         return NGX_DECLINED;
100     }
101 
102     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mirror handler");
103 
104     if (mlcf->request_body) {
105         ctx = ngx_http_get_module_ctx(r, ngx_http_mirror_module);
106 
107         if (ctx) {
108             return ctx->status;
109         }
110 
111         ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_mirror_ctx_t));
112         if (ctx == NULL) {
113             return NGX_ERROR;
114         }
115 
116         ctx->status = NGX_DONE;
117 
118         ngx_http_set_ctx(r, ctx, ngx_http_mirror_module);
119 
120         rc = ngx_http_read_client_request_body(r, ngx_http_mirror_body_handler);
121         if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
122             return rc;
123         }
124 
125         ngx_http_finalize_request(r, NGX_DONE);
126         return NGX_DONE;
127     }
128 
129     return ngx_http_mirror_handler_internal(r);
130 }
131 
132 
133 static void
ngx_http_mirror_body_handler(ngx_http_request_t * r)134 ngx_http_mirror_body_handler(ngx_http_request_t *r)
135 {
136     ngx_http_mirror_ctx_t  *ctx;
137 
138     ctx = ngx_http_get_module_ctx(r, ngx_http_mirror_module);
139 
140     ctx->status = ngx_http_mirror_handler_internal(r);
141 
142     r->preserve_body = 1;
143 
144     r->write_event_handler = ngx_http_core_run_phases;
145     ngx_http_core_run_phases(r);
146 }
147 
148 
149 static ngx_int_t
ngx_http_mirror_handler_internal(ngx_http_request_t * r)150 ngx_http_mirror_handler_internal(ngx_http_request_t *r)
151 {
152     ngx_str_t                   *name;
153     ngx_uint_t                   i;
154     ngx_http_request_t          *sr;
155     ngx_http_mirror_loc_conf_t  *mlcf;
156 
157     mlcf = ngx_http_get_module_loc_conf(r, ngx_http_mirror_module);
158 
159     name = mlcf->mirror->elts;
160 
161     for (i = 0; i < mlcf->mirror->nelts; i++) {
162         if (ngx_http_subrequest(r, &name[i], &r->args, &sr, NULL,
163                                 NGX_HTTP_SUBREQUEST_BACKGROUND)
164             != NGX_OK)
165         {
166             return NGX_HTTP_INTERNAL_SERVER_ERROR;
167         }
168 
169         sr->header_only = 1;
170         sr->method = r->method;
171         sr->method_name = r->method_name;
172     }
173 
174     return NGX_DECLINED;
175 }
176 
177 
178 static void *
ngx_http_mirror_create_loc_conf(ngx_conf_t * cf)179 ngx_http_mirror_create_loc_conf(ngx_conf_t *cf)
180 {
181     ngx_http_mirror_loc_conf_t  *mlcf;
182 
183     mlcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_mirror_loc_conf_t));
184     if (mlcf == NULL) {
185         return NULL;
186     }
187 
188     mlcf->mirror = NGX_CONF_UNSET_PTR;
189     mlcf->request_body = NGX_CONF_UNSET;
190 
191     return mlcf;
192 }
193 
194 
195 static char *
ngx_http_mirror_merge_loc_conf(ngx_conf_t * cf,void * parent,void * child)196 ngx_http_mirror_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
197 {
198     ngx_http_mirror_loc_conf_t *prev = parent;
199     ngx_http_mirror_loc_conf_t *conf = child;
200 
201     ngx_conf_merge_ptr_value(conf->mirror, prev->mirror, NULL);
202     ngx_conf_merge_value(conf->request_body, prev->request_body, 1);
203 
204     return NGX_CONF_OK;
205 }
206 
207 
208 static char *
ngx_http_mirror(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)209 ngx_http_mirror(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
210 {
211     ngx_http_mirror_loc_conf_t *mlcf = conf;
212 
213     ngx_str_t  *value, *s;
214 
215     value = cf->args->elts;
216 
217     if (ngx_strcmp(value[1].data, "off") == 0) {
218         if (mlcf->mirror != NGX_CONF_UNSET_PTR) {
219             return "is duplicate";
220         }
221 
222         mlcf->mirror = NULL;
223         return NGX_CONF_OK;
224     }
225 
226     if (mlcf->mirror == NULL) {
227         return "is duplicate";
228     }
229 
230     if (mlcf->mirror == NGX_CONF_UNSET_PTR) {
231         mlcf->mirror = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t));
232         if (mlcf->mirror == NULL) {
233             return NGX_CONF_ERROR;
234         }
235     }
236 
237     s = ngx_array_push(mlcf->mirror);
238     if (s == NULL) {
239         return NGX_CONF_ERROR;
240     }
241 
242     *s = value[1];
243 
244     return NGX_CONF_OK;
245 }
246 
247 
248 static ngx_int_t
ngx_http_mirror_init(ngx_conf_t * cf)249 ngx_http_mirror_init(ngx_conf_t *cf)
250 {
251     ngx_http_handler_pt        *h;
252     ngx_http_core_main_conf_t  *cmcf;
253 
254     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
255 
256     h = ngx_array_push(&cmcf->phases[NGX_HTTP_PRECONTENT_PHASE].handlers);
257     if (h == NULL) {
258         return NGX_ERROR;
259     }
260 
261     *h = ngx_http_mirror_handler;
262 
263     return NGX_OK;
264 }
265