Mercurial > hg > nginx-vendor-current
comparison src/http/modules/ngx_http_rewrite_handler.c @ 34:aab2ea7c0458 NGINX_0_1_17
nginx 0.1.17
*) Change: the ngx_http_rewrite_module was rewritten from the scratch.
Now it is possible to redirect, to return the error codes, to check
the variables and referrers. The directives can be used inside
locations. The redirect directive was canceled.
*) Feature: the ngx_http_geo_module.
*) Feature: the proxy_set_x_var and fastcgi_set_var directives.
*) Bugfix: the location configuration with "=" modifier may be used in
another location.
*) Bugfix: the correct content type was set only for requests that use
small caps letters in extension.
*) Bugfix: if the proxy_pass or fastcgi_pass directives were set in the
location, and access was denied, and the error was redirected to a
static page, then the segmentation fault occurred.
*) Bugfix: if in a proxied "Location" header was a relative URL, then a
host name and a slash were added to them; bug appeared in 0.1.14.
*) Bugfix: the system error message was not logged on Linux.
author | Igor Sysoev <http://sysoev.ru> |
---|---|
date | Thu, 03 Feb 2005 00:00:00 +0300 |
parents | da8c190bdaba |
children | a39d1b793287 |
comparison
equal
deleted
inserted
replaced
33:27f09a550803 | 34:aab2ea7c0458 |
---|---|
7 #include <ngx_config.h> | 7 #include <ngx_config.h> |
8 #include <ngx_core.h> | 8 #include <ngx_core.h> |
9 #include <ngx_http.h> | 9 #include <ngx_http.h> |
10 | 10 |
11 | 11 |
12 #define NGX_HTTP_REWRITE_COPY_CAPTURE 0 | 12 typedef struct ngx_http_rewrite_engine_s ngx_http_rewrite_engine_t; |
13 #define NGX_HTTP_REWRITE_COPY_SHORT 1 | 13 |
14 #define NGX_HTTP_REWRITE_COPY_LONG 2 | 14 typedef void (*ngx_http_rewrite_code_pt) (ngx_http_rewrite_engine_t *e); |
15 #define NGX_HTTP_REWRITE_START_ARGS 3 | |
16 | 15 |
17 | 16 |
18 typedef struct { | 17 typedef struct { |
19 ngx_int_t op; | 18 ngx_str_t name; |
20 size_t len; | 19 ngx_uint_t wildcard; |
21 uintptr_t data; | 20 } ngx_http_rewrite_referer_t; |
22 } ngx_http_rewrite_op_t; | |
23 | 21 |
24 | 22 |
25 typedef struct { | 23 typedef struct { |
26 ngx_regex_t *regex; | 24 ngx_array_t *codes; /* uintptr_t */ |
27 ngx_uint_t ncaptures; | 25 ngx_array_t *referers; /* ngx_http_rewrite_referer_t */ |
28 | 26 |
29 ngx_array_t ops; | 27 ngx_uint_t max_captures; |
30 ngx_uint_t size; | 28 ngx_uint_t stack_size; |
31 | 29 |
32 ngx_str_t re_name; | 30 ngx_flag_t log; |
33 ngx_str_t s_name; | 31 |
34 | 32 ngx_flag_t no_referer; |
35 ngx_uint_t status; | 33 } ngx_http_rewrite_loc_conf_t; |
36 unsigned last:1; | |
37 } ngx_http_rewrite_rule_t; | |
38 | 34 |
39 | 35 |
40 typedef struct { | 36 typedef struct { |
41 ngx_array_t rules; | 37 ngx_http_rewrite_code_pt code; |
42 ngx_flag_t log; | 38 ngx_regex_t *regex; |
43 } ngx_http_rewrite_srv_conf_t; | 39 uintptr_t size; |
40 uintptr_t ncaptures; | |
41 uintptr_t status; | |
42 uintptr_t next; | |
43 | |
44 uintptr_t uri:1; | |
45 | |
46 /* add the r->args to the new arguments */ | |
47 uintptr_t args:1; | |
48 | |
49 uintptr_t redirect:1; | |
50 | |
51 ngx_str_t name; | |
52 } ngx_http_rewrite_regex_code_t; | |
44 | 53 |
45 | 54 |
46 typedef struct { | 55 typedef struct { |
47 ngx_str_t redirect; | 56 ngx_http_rewrite_code_pt code; |
48 } ngx_http_rewrite_loc_conf_t; | 57 |
49 | 58 uintptr_t uri:1; |
50 | 59 |
51 static void *ngx_http_rewrite_create_srv_conf(ngx_conf_t *cf); | 60 /* add the r->args to the new arguments */ |
52 static char *ngx_http_rewrite_merge_srv_conf(ngx_conf_t *cf, | 61 uintptr_t args:1; |
62 | |
63 uintptr_t redirect:1; | |
64 } ngx_http_rewrite_regex_end_code_t; | |
65 | |
66 typedef struct { | |
67 ngx_http_rewrite_code_pt code; | |
68 uintptr_t n; | |
69 } ngx_http_rewrite_copy_capture_code_t; | |
70 | |
71 | |
72 typedef struct { | |
73 ngx_http_rewrite_code_pt code; | |
74 uintptr_t len; | |
75 } ngx_http_rewrite_copy_code_t; | |
76 | |
77 | |
78 typedef struct { | |
79 ngx_http_rewrite_code_pt code; | |
80 uintptr_t status; | |
81 uintptr_t null; | |
82 } ngx_http_rewrite_return_code_t; | |
83 | |
84 | |
85 typedef struct { | |
86 ngx_http_rewrite_code_pt code; | |
87 uintptr_t next; | |
88 void **loc_conf; | |
89 } ngx_http_rewrite_if_code_t; | |
90 | |
91 | |
92 typedef struct { | |
93 ngx_http_rewrite_code_pt code; | |
94 uintptr_t index; | |
95 } ngx_http_rewrite_var_code_t; | |
96 | |
97 | |
98 struct ngx_http_rewrite_engine_s { | |
99 u_char *ip; | |
100 uintptr_t *sp; | |
101 | |
102 ngx_str_t buf; | |
103 ngx_str_t *line; | |
104 | |
105 u_char *pos; | |
106 | |
107 /* the start of the rewritten arguments */ | |
108 u_char *args; | |
109 | |
110 unsigned quote:1; | |
111 | |
112 ngx_int_t status; | |
113 | |
114 int *captures; | |
115 | |
116 ngx_http_request_t *request; | |
117 ngx_http_rewrite_loc_conf_t *conf; | |
118 }; | |
119 | |
120 | |
121 static ngx_int_t ngx_http_rewrite_init(ngx_cycle_t *cycle); | |
122 static void *ngx_http_rewrite_create_loc_conf(ngx_conf_t *cf); | |
123 static char *ngx_http_rewrite_merge_loc_conf(ngx_conf_t *cf, | |
53 void *parent, void *child); | 124 void *parent, void *child); |
54 static void *ngx_http_rewrite_create_loc_conf(ngx_conf_t *cf); | 125 static char *ngx_http_rewrite(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); |
55 static char *ngx_http_rewrite_rule(ngx_conf_t *cf, ngx_command_t *cmd, | 126 static char *ngx_http_rewrite_return(ngx_conf_t *cf, ngx_command_t *cmd, |
56 void *conf); | 127 void *conf); |
57 static char *ngx_http_redirect(ngx_conf_t *cf, void *post, void *data); | 128 static char *ngx_http_rewrite_if(ngx_conf_t *cf, ngx_command_t *cmd, |
58 static ngx_int_t ngx_http_rewrite_init(ngx_cycle_t *cycle); | 129 void *conf); |
59 | 130 static char *ngx_http_rewrite_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd, |
60 | 131 void *conf); |
61 static ngx_conf_post_handler_pt ngx_http_redirect_p = ngx_http_redirect; | 132 static void *ngx_http_rewrite_start_code(ngx_pool_t *pool, |
133 ngx_array_t **codes, size_t size); | |
134 static void *ngx_http_rewrite_add_code(ngx_array_t *codes, size_t size, | |
135 u_char **main); | |
62 | 136 |
63 | 137 |
64 static ngx_command_t ngx_http_rewrite_commands[] = { | 138 static ngx_command_t ngx_http_rewrite_commands[] = { |
65 | 139 |
66 { ngx_string("rewrite"), | 140 { ngx_string("rewrite"), |
67 NGX_HTTP_SRV_CONF|NGX_CONF_TAKE23, | 141 NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |
68 ngx_http_rewrite_rule, | 142 |NGX_CONF_TAKE23, |
69 NGX_HTTP_SRV_CONF_OFFSET, | 143 ngx_http_rewrite, |
144 NGX_HTTP_LOC_CONF_OFFSET, | |
70 0, | 145 0, |
71 NULL }, | 146 NULL }, |
72 | 147 |
73 { ngx_string("redirect"), | 148 { ngx_string("return"), |
74 NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | 149 NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |
75 ngx_conf_set_str_slot, | 150 |NGX_CONF_TAKE1, |
151 ngx_http_rewrite_return, | |
76 NGX_HTTP_LOC_CONF_OFFSET, | 152 NGX_HTTP_LOC_CONF_OFFSET, |
77 0, | 153 0, |
78 &ngx_http_redirect_p }, | 154 NULL }, |
155 | |
156 { ngx_string("if"), | |
157 NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_1MORE, | |
158 ngx_http_rewrite_if, | |
159 NGX_HTTP_LOC_CONF_OFFSET, | |
160 0, | |
161 NULL }, | |
162 | |
163 { ngx_string("valid_referers"), | |
164 NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, | |
165 ngx_http_rewrite_valid_referers, | |
166 NGX_HTTP_LOC_CONF_OFFSET, | |
167 0, | |
168 NULL }, | |
79 | 169 |
80 { ngx_string("rewrite_log"), | 170 { ngx_string("rewrite_log"), |
81 NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, | 171 NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |
172 |NGX_CONF_TAKE1, | |
82 ngx_conf_set_flag_slot, | 173 ngx_conf_set_flag_slot, |
83 NGX_HTTP_SRV_CONF_OFFSET, | 174 NGX_HTTP_LOC_CONF_OFFSET, |
84 offsetof(ngx_http_rewrite_srv_conf_t, log), | 175 offsetof(ngx_http_rewrite_loc_conf_t, log), |
85 NULL }, | 176 NULL }, |
86 | 177 |
87 ngx_null_command | 178 ngx_null_command |
88 }; | 179 }; |
89 | 180 |
92 NULL, /* pre conf */ | 183 NULL, /* pre conf */ |
93 | 184 |
94 NULL, /* create main configuration */ | 185 NULL, /* create main configuration */ |
95 NULL, /* init main configuration */ | 186 NULL, /* init main configuration */ |
96 | 187 |
97 ngx_http_rewrite_create_srv_conf, /* create server configuration */ | 188 NULL, /* create server configuration */ |
98 ngx_http_rewrite_merge_srv_conf, /* merge server configuration */ | 189 NULL, /* merge server configuration */ |
99 | 190 |
100 ngx_http_rewrite_create_loc_conf, /* create location configration */ | 191 ngx_http_rewrite_create_loc_conf, /* create location configration */ |
101 NULL, /* merge location configration */ | 192 ngx_http_rewrite_merge_loc_conf /* merge location configration */ |
102 }; | 193 }; |
103 | 194 |
104 | 195 |
105 ngx_module_t ngx_http_rewrite_module = { | 196 ngx_module_t ngx_http_rewrite_module = { |
106 NGX_MODULE, | 197 NGX_MODULE, |
107 &ngx_http_rewrite_module_ctx, /* module context */ | 198 &ngx_http_rewrite_module_ctx, /* module context */ |
108 ngx_http_rewrite_commands, /* module directives */ | 199 ngx_http_rewrite_commands, /* module directives */ |
109 NGX_HTTP_MODULE, /* module type */ | 200 NGX_HTTP_MODULE, /* module type */ |
110 ngx_http_rewrite_init, /* init module */ | 201 ngx_http_rewrite_init, /* init module */ |
111 NULL /* init child */ | 202 NULL /* init process */ |
112 }; | 203 }; |
113 | 204 |
114 | 205 |
206 #define ngx_http_rewrite_exit (u_char *) &ngx_http_rewrite_exit_code | |
207 | |
208 uintptr_t ngx_http_rewrite_exit_code = (uintptr_t) NULL; | |
209 | |
210 | |
115 static ngx_int_t ngx_http_rewrite_handler(ngx_http_request_t *r) | 211 static ngx_int_t ngx_http_rewrite_handler(ngx_http_request_t *r) |
116 { | 212 { |
117 int *captures; | 213 ngx_http_rewrite_code_pt code; |
118 u_char *p; | 214 ngx_http_rewrite_engine_t *e; |
119 size_t len; | 215 ngx_http_rewrite_loc_conf_t *cf; |
120 uintptr_t data; | 216 |
121 ngx_int_t rc; | 217 cf = ngx_http_get_module_loc_conf(r, ngx_http_rewrite_module); |
122 ngx_uint_t i, m, n; | 218 |
123 ngx_str_t uri, args; | 219 if (cf->codes == NULL) { |
124 ngx_http_rewrite_op_t *op; | 220 return NGX_DECLINED; |
125 ngx_http_rewrite_rule_t *rule; | 221 } |
126 ngx_http_rewrite_srv_conf_t *scf; | 222 |
223 if (!(e = ngx_palloc(r->pool, sizeof(ngx_http_rewrite_engine_t)))) { | |
224 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
225 } | |
226 | |
227 e->sp = ngx_palloc(r->pool, cf->stack_size * sizeof(ngx_int_t)); | |
228 if (e->sp == NULL) { | |
229 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
230 } | |
231 | |
232 if (cf->max_captures) { | |
233 e->captures = ngx_palloc(r->pool, cf->max_captures * sizeof(int)); | |
234 if (e->captures == NULL) { | |
235 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
236 } | |
237 | |
238 } else { | |
239 e->captures = NULL; | |
240 } | |
241 | |
242 e->ip = cf->codes->elts; | |
243 e->buf.len = 0; | |
244 e->buf.data = NULL; | |
245 e->line = NULL; | |
246 e->pos = NULL; | |
247 e->args = NULL; | |
248 e->quote = 1; | |
249 e->status = NGX_DECLINED; | |
250 e->request = r; | |
251 e->conf = cf; | |
252 | |
253 while (*(uintptr_t *) e->ip) { | |
254 code = *(ngx_http_rewrite_code_pt *) e->ip; | |
255 code(e); | |
256 } | |
257 | |
258 return e->status; | |
259 } | |
260 | |
261 | |
262 static void ngx_http_rewrite_regex_start_code(ngx_http_rewrite_engine_t *e) | |
263 { | |
264 ngx_int_t rc; | |
265 ngx_uint_t n; | |
266 ngx_http_request_t *r; | |
267 ngx_http_rewrite_regex_code_t *code; | |
268 | |
269 code = (ngx_http_rewrite_regex_code_t *) e->ip; | |
270 | |
271 r = e->request; | |
272 | |
273 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
274 "http rewrite start: \"%V\"", &code->name); | |
275 | |
276 if (code->uri) { | |
277 e->line = &r->uri; | |
278 } else { | |
279 e->line = *(ngx_str_t **) e->sp--; | |
280 } | |
281 | |
282 rc = ngx_regex_exec(code->regex, e->line, e->captures, code->ncaptures); | |
283 | |
284 if (rc == NGX_REGEX_NO_MATCHED) { | |
285 if (e->conf->log) { | |
286 ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, | |
287 "\"%V\" does not match \"%V\"", &code->name, e->line); | |
288 } | |
289 | |
290 e->ip += code->next; | |
291 return; | |
292 } | |
293 | |
294 if (rc < 0) { | |
295 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, | |
296 ngx_regex_exec_n " failed: %d on \"%V\" using \"%V\"", | |
297 rc, e->line, &code->name); | |
298 | |
299 e->ip = ngx_http_rewrite_exit; | |
300 e->status = NGX_HTTP_INTERNAL_SERVER_ERROR; | |
301 return; | |
302 } | |
303 | |
304 if (e->conf->log) { | |
305 ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, | |
306 "\"%V\" matches \"%V\"", &code->name, e->line); | |
307 } | |
308 | |
309 if (code->status) { | |
310 e->status = code->status; | |
311 | |
312 if (!code->redirect) { | |
313 e->ip = ngx_http_rewrite_exit; | |
314 return; | |
315 } | |
316 } | |
317 | |
318 e->buf.len = code->size; | |
319 | |
320 if (code->uri) { | |
321 r->uri_changed = 1; | |
322 | |
323 if (rc && (r->quoted_uri || r->plus_in_uri)) { | |
324 e->buf.len += 2 * ngx_escape_uri(NULL, r->uri.data, r->uri.len, | |
325 NGX_ESCAPE_ARGS); | |
326 } | |
327 } | |
328 | |
329 for (n = 1; n < (ngx_uint_t) rc; n++) { | |
330 e->buf.len += e->captures[2 * n + 1] - e->captures[2 * n]; | |
331 } | |
332 | |
333 if (code->args && r->args.len) { | |
334 e->buf.len += r->args.len + 1; | |
335 } | |
336 | |
337 if (!(e->buf.data = ngx_palloc(r->pool, e->buf.len))) { | |
338 e->ip = ngx_http_rewrite_exit; | |
339 e->status = NGX_HTTP_INTERNAL_SERVER_ERROR; | |
340 return; | |
341 } | |
342 | |
343 e->quote = code->redirect; | |
344 | |
345 e->pos = e->buf.data; | |
346 | |
347 e->ip += sizeof(ngx_http_rewrite_regex_code_t); | |
348 } | |
349 | |
350 | |
351 static void ngx_http_rewrite_regex_end_code(ngx_http_rewrite_engine_t *e) | |
352 { | |
353 ngx_http_request_t *r; | |
354 ngx_http_rewrite_regex_end_code_t *code; | |
355 | |
356 code = (ngx_http_rewrite_regex_end_code_t *) e->ip; | |
357 | |
358 r = e->request; | |
359 | |
360 e->quote = 0; | |
127 | 361 |
128 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | 362 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
129 "http rewrite handler"); | 363 "http rewrite end"); |
130 | 364 |
131 scf = ngx_http_get_module_srv_conf(r, ngx_http_rewrite_module); | 365 if (e->args) { |
132 | 366 e->buf.len = e->args - e->buf.data; |
133 rule = scf->rules.elts; | 367 |
134 for (i = 0; i < scf->rules.nelts; i++) { | 368 if (code->args && r->args.len) { |
135 | 369 *e->pos++ = '&'; |
136 if (rule[i].ncaptures) { | 370 e->pos = ngx_cpymem(e->pos, r->args.data, r->args.len); |
137 captures = ngx_palloc(r->pool, rule[i].ncaptures * sizeof(int)); | 371 } |
138 if (captures == NULL) { | 372 |
139 return NGX_HTTP_INTERNAL_SERVER_ERROR; | 373 r->args.len = e->pos - e->args; |
374 r->args.data = e->args; | |
375 | |
376 e->args = NULL; | |
377 | |
378 } else { | |
379 if (code->args && r->args.len) { | |
380 *e->pos++ = '&'; | |
381 e->pos = ngx_cpymem(e->pos, r->args.data, r->args.len); | |
382 } | |
383 | |
384 e->buf.len = e->pos - e->buf.data; | |
385 } | |
386 | |
387 if (!code->redirect) { | |
388 if (e->conf->log) { | |
389 ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, | |
390 "rewritten data: \"%V\", args: \"%V\"", | |
391 &e->buf, &r->args); | |
392 } | |
393 | |
394 if (code->uri) { | |
395 r->uri = e->buf; | |
396 | |
397 if (ngx_http_set_exten(r) != NGX_OK) { | |
398 e->ip = ngx_http_rewrite_exit; | |
399 e->status = NGX_HTTP_INTERNAL_SERVER_ERROR; | |
400 return; | |
140 } | 401 } |
141 | 402 } |
142 } else { | 403 |
143 captures = NULL; | 404 e->ip += sizeof(ngx_http_rewrite_regex_end_code_t); |
144 } | 405 return; |
145 | 406 } |
146 rc = ngx_regex_exec(rule[i].regex, &r->uri, | 407 |
147 captures, rule[i].ncaptures); | 408 ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, |
148 | 409 "rewritten redirect: \"%V\"", &e->buf); |
149 if (rc == NGX_REGEX_NO_MATCHED) { | 410 |
150 if (scf->log) { | 411 if (!(r->headers_out.location = ngx_list_push(&r->headers_out.headers))) { |
151 ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, | 412 e->ip = ngx_http_rewrite_exit; |
152 "\"%V\" does not match \"%V\"", | 413 e->status = NGX_HTTP_INTERNAL_SERVER_ERROR; |
153 &rule[i].re_name, &r->uri); | 414 return; |
154 } | 415 } |
155 | 416 |
156 continue; | 417 if (e->buf.data[0] != '/') { |
157 } | |
158 | |
159 if (rc < 0) { | |
160 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, | |
161 ngx_regex_exec_n | |
162 " failed: %d on \"%V\" using \"%V\"", | |
163 rc, &r->uri, &rule[i].re_name); | |
164 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
165 } | |
166 | |
167 if (scf->log) { | |
168 ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, | |
169 "\"%V\" matches \"%V\"", | |
170 &rule[i].re_name, &r->uri); | |
171 } | |
172 | |
173 if (rule[i].status) { | |
174 return rule[i].status; | |
175 } | |
176 | |
177 uri.len = rule[i].size; | |
178 | |
179 for (n = 1; n < (ngx_uint_t) rc; n++) { | |
180 uri.len += captures[2 * n + 1] - captures[2 * n]; | |
181 } | |
182 | |
183 if (!(uri.data = ngx_palloc(r->pool, uri.len))) { | |
184 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
185 } | |
186 | |
187 args.data = NULL; | |
188 p = uri.data; | |
189 | |
190 op = rule[i].ops.elts; | |
191 for (n = 0; n < rule[i].ops.nelts; n++) { | |
192 if (op[n].op == NGX_HTTP_REWRITE_COPY_SHORT) { | |
193 len = op[n].len; | |
194 data = op[n].data; | |
195 while (len--) { | |
196 *p++ = (char) (data & 0xff); | |
197 data >>= 8; | |
198 } | |
199 | |
200 } else if (op[n].op == NGX_HTTP_REWRITE_COPY_LONG) { | |
201 p = ngx_cpymem(p, (void *) op[n].data, op[n].len); | |
202 | |
203 } else if (op[n].op == NGX_HTTP_REWRITE_START_ARGS) { | |
204 args.data = p; | |
205 | |
206 } else { /* NGX_HTTP_REWRITE_COPY_CAPTURE */ | |
207 m = 2 * op[n].data; | |
208 p = ngx_cpymem(p, &r->uri.data[captures[m]], | |
209 captures[m + 1] - captures[m]); | |
210 } | |
211 } | |
212 | |
213 if (args.data) { | |
214 uri.len = args.data - uri.data; | |
215 args.len = p - args.data; | |
216 | |
217 r->args = args; | |
218 | |
219 } else { | |
220 uri.len = p - uri.data; | |
221 args.len = 0; | |
222 } | |
223 | |
224 r->uri = uri; | |
225 | |
226 if (scf->log) { | |
227 ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, | |
228 "rewritten uri: \"%V\", args: \"%V\"", &uri, &args); | |
229 } | |
230 | |
231 if (ngx_http_set_exten(r) != NGX_OK) { | |
232 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
233 } | |
234 | |
235 if (rule[i].last) { | |
236 return NGX_DECLINED; | |
237 } | |
238 } | |
239 | |
240 return NGX_DECLINED; | |
241 } | |
242 | |
243 | |
244 static ngx_int_t ngx_http_redirect_handler(ngx_http_request_t *r) | |
245 { | |
246 u_char *p; | |
247 ngx_http_rewrite_loc_conf_t *rlcf; | |
248 | |
249 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
250 "http redirect handler"); | |
251 | |
252 rlcf = ngx_http_get_module_loc_conf(r, ngx_http_rewrite_module); | |
253 | |
254 r->headers_out.location = ngx_list_push(&r->headers_out.headers); | |
255 if (r->headers_out.location == NULL) { | |
256 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
257 } | |
258 | |
259 if (rlcf->redirect.data[0] != '/') { | |
260 r->headers_out.location->key.len = sizeof("Location") - 1; | 418 r->headers_out.location->key.len = sizeof("Location") - 1; |
261 r->headers_out.location->key.data = (u_char *) "Location"; | 419 r->headers_out.location->key.data = (u_char *) "Location"; |
262 } | 420 } |
263 | 421 |
264 r->headers_out.location->value.len = rlcf->redirect.len | 422 r->headers_out.location->value = e->buf; |
265 + r->unparsed_uri.len; | 423 |
266 r->headers_out.location->value.data = ngx_palloc(r->pool, | 424 e->ip += sizeof(ngx_http_rewrite_regex_end_code_t); |
267 r->headers_out.location->value.len); | 425 } |
268 | 426 |
269 if (r->headers_out.location->value.data == NULL) { | 427 |
270 return NGX_HTTP_INTERNAL_SERVER_ERROR; | 428 static void ngx_http_rewrite_copy_capture_code(ngx_http_rewrite_engine_t *e) |
271 } | 429 { |
272 | 430 ngx_http_rewrite_copy_capture_code_t *code; |
273 p = ngx_cpymem(r->headers_out.location->value.data, rlcf->redirect.data, | 431 |
274 rlcf->redirect.len); | 432 code = (ngx_http_rewrite_copy_capture_code_t *) e->ip; |
275 p = ngx_cpystrn(p, r->unparsed_uri.data + 1, r->unparsed_uri.len); | 433 |
276 | 434 e->ip += sizeof(ngx_http_rewrite_copy_capture_code_t); |
277 return NGX_HTTP_MOVED_TEMPORARILY; | 435 |
278 } | 436 if ((e->args || e->quote) |
279 | 437 && (e->request->quoted_uri || e->request->plus_in_uri)) |
280 | 438 { |
281 static void *ngx_http_rewrite_create_srv_conf(ngx_conf_t *cf) | 439 e->pos = (u_char *) ngx_escape_uri(e->pos, |
282 { | 440 &e->line->data[e->captures[code->n]], |
283 ngx_http_rewrite_srv_conf_t *conf; | 441 e->captures[code->n + 1] - e->captures[code->n], |
284 | 442 NGX_ESCAPE_ARGS); |
285 if (!(conf = ngx_palloc(cf->pool, sizeof(ngx_http_rewrite_srv_conf_t)))) { | 443 } else { |
286 return NGX_CONF_ERROR; | 444 e->pos = ngx_cpymem(e->pos, &e->line->data[e->captures[code->n]], |
287 } | 445 e->captures[code->n + 1] - e->captures[code->n]); |
288 | 446 } |
289 ngx_init_array(conf->rules, cf->pool, 5, sizeof(ngx_http_rewrite_rule_t), | 447 |
290 NGX_CONF_ERROR); | 448 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, |
291 | 449 "http rewrite capture: \"%V\"", &e->buf); |
292 conf->log = NGX_CONF_UNSET; | 450 } |
293 | 451 |
294 return conf; | 452 |
295 } | 453 static void ngx_http_rewrite_copy_code(ngx_http_rewrite_engine_t *e) |
296 | 454 { |
297 | 455 ngx_http_rewrite_copy_code_t *code; |
298 static char *ngx_http_rewrite_merge_srv_conf(ngx_conf_t *cf, | 456 |
299 void *parent, void *child) | 457 code = (ngx_http_rewrite_copy_code_t *) e->ip; |
300 { | 458 |
301 ngx_http_rewrite_srv_conf_t *prev = parent; | 459 e->pos = ngx_cpymem(e->pos, e->ip + sizeof(ngx_http_rewrite_copy_code_t), |
302 ngx_http_rewrite_srv_conf_t *conf = child; | 460 code->len); |
303 | 461 |
304 ngx_conf_merge_value(conf->log, prev->log, 0); | 462 e->ip += sizeof(ngx_http_rewrite_copy_code_t) |
305 | 463 + ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1)); |
306 return NGX_CONF_OK; | 464 |
307 } | 465 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, |
466 "http rewrite copy: \"%V\"", &e->buf); | |
467 } | |
468 | |
469 | |
470 static void ngx_http_rewrite_start_args_code(ngx_http_rewrite_engine_t *e) | |
471 { | |
472 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, | |
473 "http rewrite args"); | |
474 | |
475 e->args = e->pos; | |
476 e->ip += sizeof(uintptr_t); | |
477 } | |
478 | |
479 | |
480 static void ngx_http_rewrite_return_code(ngx_http_rewrite_engine_t *e) | |
481 { | |
482 ngx_http_rewrite_return_code_t *code; | |
483 | |
484 code = (ngx_http_rewrite_return_code_t *) e->ip; | |
485 | |
486 e->status = code->status; | |
487 | |
488 e->ip += sizeof(ngx_http_rewrite_return_code_t) - sizeof(uintptr_t); | |
489 } | |
490 | |
491 | |
492 static void ngx_http_rewrite_if_code(ngx_http_rewrite_engine_t *e) | |
493 { | |
494 ngx_http_rewrite_if_code_t *code; | |
495 | |
496 code = (ngx_http_rewrite_if_code_t *) e->ip; | |
497 | |
498 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, | |
499 "http rewrite if"); | |
500 | |
501 if (*e->sp--) { | |
502 if (code->loc_conf) { | |
503 e->request->loc_conf = code->loc_conf; | |
504 } | |
505 | |
506 e->ip += sizeof(ngx_http_rewrite_if_code_t); | |
507 return; | |
508 } | |
509 | |
510 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, | |
511 "http rewrite if false"); | |
512 | |
513 e->ip += code->next; | |
514 } | |
515 | |
516 | |
517 static void ngx_http_rewrite_var_code(ngx_http_rewrite_engine_t *e) | |
518 { | |
519 ngx_http_variable_value_t *value; | |
520 ngx_http_rewrite_var_code_t *code; | |
521 | |
522 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, | |
523 "http rewrite var"); | |
524 | |
525 code = (ngx_http_rewrite_var_code_t *) e->ip; | |
526 | |
527 e->sp++; | |
528 | |
529 e->ip += sizeof(ngx_http_rewrite_var_code_t); | |
530 | |
531 if (!(value = ngx_http_get_variable(e->request, code->index))) { | |
532 *e->sp = (uintptr_t) 0; | |
533 return; | |
534 } | |
535 | |
536 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, | |
537 "http rewrite var: %p", value->value); | |
538 | |
539 *e->sp = value->value; | |
540 } | |
541 | |
542 | |
543 static void ngx_http_rewrite_invalid_referer_code(ngx_http_rewrite_engine_t *e) | |
544 { | |
545 u_char *ref; | |
546 size_t len; | |
547 ngx_uint_t i, n; | |
548 ngx_http_request_t *r; | |
549 ngx_http_rewrite_referer_t *refs; | |
550 ngx_http_rewrite_loc_conf_t *cf; | |
551 | |
552 r = e->request; | |
553 | |
554 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
555 "http rewrite invalid referer"); | |
556 | |
557 cf = ngx_http_get_module_loc_conf(r, ngx_http_rewrite_module); | |
558 | |
559 e->sp++; | |
560 e->ip += sizeof(uintptr_t); | |
561 | |
562 if (cf->referers == NULL) { | |
563 *e->sp = (uintptr_t) 0; | |
564 return; | |
565 } | |
566 | |
567 if (r->headers_in.referer == NULL) { | |
568 if (cf->no_referer) { | |
569 *e->sp = (uintptr_t) 0; | |
570 return; | |
571 } else { | |
572 *e->sp = (uintptr_t) 1; | |
573 return; | |
574 } | |
575 } | |
576 | |
577 len = r->headers_in.referer->value.len; | |
578 ref = r->headers_in.referer->value.data; | |
579 | |
580 if (len < sizeof("http://i.ru") - 1 | |
581 || (ngx_strncasecmp(ref, "http://", 7) != 0)) | |
582 { | |
583 *e->sp = (uintptr_t) 1; | |
584 return; | |
585 } | |
586 | |
587 len -= 7; | |
588 ref += 7; | |
589 | |
590 refs = cf->referers->elts; | |
591 for (i = 0; i < cf->referers->nelts; i++ ){ | |
592 | |
593 if (refs[i].name.len > len) { | |
594 continue; | |
595 } | |
596 | |
597 if (refs[i].wildcard) { | |
598 for (n = 0; n < len; n++) { | |
599 if (ref[n] == '/' || ref[n] == ':') { | |
600 break; | |
601 } | |
602 | |
603 if (ref[n] != '.') { | |
604 continue; | |
605 } | |
606 | |
607 if (ngx_strncmp(&ref[n], refs[i].name.data, | |
608 refs[i].name.len) == 0) | |
609 { | |
610 *e->sp = (uintptr_t) 0; | |
611 return; | |
612 } | |
613 } | |
614 | |
615 } else { | |
616 if (ngx_strncasecmp(refs[i].name.data, ref, refs[i].name.len) == 0) | |
617 { | |
618 *e->sp = (uintptr_t) 0; | |
619 return; | |
620 } | |
621 } | |
622 } | |
623 | |
624 *e->sp = (uintptr_t) 1; | |
625 } | |
626 | |
627 | |
628 static void ngx_http_rewrite_nop_code(ngx_http_rewrite_engine_t *e) | |
629 { | |
630 e->ip += sizeof(uintptr_t); | |
631 } | |
632 | |
633 | |
634 static ngx_int_t ngx_http_rewrite_init(ngx_cycle_t *cycle) | |
635 { | |
636 ngx_http_handler_pt *h; | |
637 ngx_http_core_main_conf_t *cmcf; | |
638 | |
639 cmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_core_module); | |
640 | |
641 h = ngx_push_array(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers); | |
642 if (h == NULL) { | |
643 return NGX_ERROR; | |
644 } | |
645 | |
646 *h = ngx_http_rewrite_handler; | |
647 | |
648 return NGX_OK; | |
649 } | |
308 | 650 |
309 | 651 |
310 static void *ngx_http_rewrite_create_loc_conf(ngx_conf_t *cf) | 652 static void *ngx_http_rewrite_create_loc_conf(ngx_conf_t *cf) |
311 { | 653 { |
312 ngx_http_rewrite_loc_conf_t *conf; | 654 ngx_http_rewrite_loc_conf_t *conf; |
313 | 655 |
314 if (!(conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_rewrite_loc_conf_t)))) { | 656 if (!(conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_rewrite_loc_conf_t)))) { |
315 return NGX_CONF_ERROR; | 657 return NGX_CONF_ERROR; |
316 } | 658 } |
317 | 659 |
660 conf->stack_size = NGX_CONF_UNSET_UINT; | |
661 conf->log = NGX_CONF_UNSET; | |
662 conf->no_referer = NGX_CONF_UNSET; | |
663 | |
318 return conf; | 664 return conf; |
319 } | 665 } |
320 | 666 |
321 | 667 |
322 static char *ngx_http_rewrite_rule(ngx_conf_t *cf, ngx_command_t *cmd, | 668 static char *ngx_http_rewrite_merge_loc_conf(ngx_conf_t *cf, |
323 void *conf) | 669 void *parent, void *child) |
324 { | 670 { |
325 ngx_http_rewrite_srv_conf_t *scf = conf; | 671 ngx_http_rewrite_loc_conf_t *prev = parent; |
326 | 672 ngx_http_rewrite_loc_conf_t *conf = child; |
327 u_char *data, *p; | 673 |
328 size_t len; | 674 uintptr_t *code, *last; |
329 ngx_str_t *value, err; | 675 ngx_http_rewrite_regex_code_t *regex; |
330 ngx_uint_t i, n; | 676 |
331 ngx_http_rewrite_op_t *op; | 677 ngx_conf_merge_value(conf->log, prev->log, 0); |
332 ngx_http_rewrite_rule_t *rule; | 678 ngx_conf_merge_unsigned_value(conf->stack_size, prev->stack_size, 10); |
333 u_char errstr[NGX_MAX_CONF_ERRSTR]; | 679 |
334 | 680 if (conf->referers == NULL) { |
335 if (!(rule = ngx_push_array(&scf->rules))) { | 681 conf->referers = prev->referers; |
682 ngx_conf_merge_value(conf->no_referer, prev->no_referer, 0); | |
683 } | |
684 | |
685 if (conf->codes == NULL) { | |
686 return NGX_CONF_OK; | |
687 } | |
688 | |
689 if (conf->codes == prev->codes) { | |
690 return NGX_CONF_OK; | |
691 } | |
692 | |
693 code = conf->codes->elts; | |
694 last = (uintptr_t *) ((u_char *) code + conf->codes->nelts); | |
695 | |
696 while (code < last) { | |
697 if (*code == (uintptr_t) NULL) { | |
698 return NGX_CONF_OK; | |
699 } | |
700 | |
701 if (*code == (uintptr_t) &ngx_http_rewrite_regex_start_code) { | |
702 regex = (ngx_http_rewrite_regex_code_t *) code; | |
703 if (conf->max_captures < regex->ncaptures) { | |
704 conf->max_captures = regex->ncaptures; | |
705 } | |
706 code = (uintptr_t *) ((u_char *) code + regex->next); | |
707 } | |
708 | |
709 if (*code == (uintptr_t) &ngx_http_rewrite_if_code) { | |
710 code += sizeof(ngx_http_rewrite_if_code_t) / sizeof(uintptr_t); | |
711 } | |
712 | |
713 if (*code == (uintptr_t) &ngx_http_rewrite_return_code) { | |
714 code += sizeof(ngx_http_rewrite_return_code_t) / sizeof(uintptr_t); | |
715 } | |
716 | |
717 if (*code == (uintptr_t) &ngx_http_rewrite_var_code) { | |
718 code += sizeof(ngx_http_rewrite_var_code_t) / sizeof(uintptr_t); | |
719 } | |
720 | |
721 if (*code == (uintptr_t) &ngx_http_rewrite_invalid_referer_code) { | |
722 code++; | |
723 } | |
724 | |
725 if (*code == (uintptr_t) &ngx_http_rewrite_nop_code) { | |
726 code++; | |
727 } | |
728 } | |
729 | |
730 if (!(code = ngx_array_push_n(conf->codes, sizeof(uintptr_t)))) { | |
336 return NGX_CONF_ERROR; | 731 return NGX_CONF_ERROR; |
337 } | 732 } |
338 | 733 |
339 ngx_init_array(rule->ops, cf->pool, 5, sizeof(ngx_http_rewrite_op_t), | 734 *code = (uintptr_t) NULL; |
340 NGX_CONF_ERROR); | 735 |
341 | 736 return NGX_CONF_OK; |
342 rule->ncaptures = 0; | 737 } |
343 rule->size = 0; | 738 |
344 rule->status = 0; | 739 |
345 rule->last = 0; | 740 static char *ngx_http_rewrite(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) |
741 { | |
742 ngx_http_rewrite_loc_conf_t *lcf = conf; | |
743 | |
744 u_char *data; | |
745 size_t len, size; | |
746 ngx_str_t *value, err; | |
747 ngx_uint_t i, n, last; | |
748 ngx_http_rewrite_code_pt *code; | |
749 ngx_http_rewrite_copy_code_t *copy; | |
750 ngx_http_rewrite_regex_code_t *regex; | |
751 ngx_http_rewrite_regex_end_code_t *regex_end; | |
752 ngx_http_rewrite_copy_capture_code_t *copy_capture; | |
753 u_char errstr[NGX_MAX_CONF_ERRSTR]; | |
754 | |
755 regex = ngx_http_rewrite_start_code(cf->pool, &lcf->codes, | |
756 sizeof(ngx_http_rewrite_regex_code_t)); | |
757 if (regex == NULL) { | |
758 return NGX_CONF_ERROR; | |
759 } | |
346 | 760 |
347 value = cf->args->elts; | 761 value = cf->args->elts; |
348 | 762 |
349 /* STUB */ { | 763 err.len = NGX_MAX_CONF_ERRSTR; |
350 err.len = NGX_MAX_CONF_ERRSTR; | 764 err.data = errstr; |
351 err.data = errstr; | 765 |
352 | 766 /* TODO: NGX_REGEX_CASELESS */ |
353 rule->regex = ngx_regex_compile(&value[1], 0, cf->pool, &err); | 767 |
354 | 768 regex->regex = ngx_regex_compile(&value[1], 0, cf->pool, &err); |
355 if (rule->regex == NULL) { | 769 |
356 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data); | 770 if (regex->regex == NULL) { |
357 return NGX_CONF_ERROR; | 771 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data); |
358 } | 772 return NGX_CONF_ERROR; |
359 | 773 } |
360 rule->re_name = value[1]; | 774 |
361 rule->s_name = value[2]; | 775 regex->code = ngx_http_rewrite_regex_start_code; |
362 | 776 regex->size = 0; |
363 if (ngx_strcasecmp(value[2].data, "forbidden:") == 0) { | 777 regex->ncaptures = 0; |
364 | 778 regex->status = 0; |
365 if (cf->args->nelts == 3) { | 779 regex->uri = 1; |
366 rule->status = NGX_HTTP_FORBIDDEN; | 780 regex->args = 1; |
367 rule->last = 1; | 781 regex->redirect = 0; |
368 return NGX_CONF_OK; | 782 regex->name = value[1]; |
369 } | 783 |
370 | 784 last = 0; |
785 | |
786 if (ngx_strncmp(value[2].data, "http://", sizeof("http://") - 1) == 0) { | |
787 regex->status = NGX_HTTP_MOVED_TEMPORARILY; | |
788 regex->redirect = 1; | |
789 last = 1; | |
790 } | |
791 | |
792 if (cf->args->nelts == 4) { | |
793 if (ngx_strcmp(value[3].data, "last") == 0) { | |
794 last = 1; | |
795 | |
796 } else if (ngx_strcmp(value[3].data, "redirect") == 0) { | |
797 regex->status = NGX_HTTP_MOVED_TEMPORARILY; | |
798 regex->redirect = 1; | |
799 last = 1; | |
800 | |
801 } else if (ngx_strcmp(value[3].data, "permanent") == 0) { | |
802 regex->status = NGX_HTTP_MOVED_PERMANENTLY; | |
803 regex->redirect = 1; | |
804 last = 1; | |
805 | |
806 } else { | |
371 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | 807 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, |
372 "invalid parameter \"%V\"", &value[3]); | 808 "invalid parameter \"%V\"", &value[3]); |
373 return NGX_CONF_ERROR; | 809 return NGX_CONF_ERROR; |
374 } | 810 } |
375 | 811 } |
376 i = 0; | 812 |
377 | 813 i = 0; |
378 while (i < value[2].len) { | 814 |
379 | 815 while (i < value[2].len) { |
380 if (!(op = ngx_push_array(&rule->ops))) { | 816 |
817 data = &value[2].data[i]; | |
818 | |
819 if (value[2].data[i] == '$' && i < value[2].len | |
820 && value[2].data[i + 1] >= '1' && value[2].data[i + 1] <= '9') | |
821 { | |
822 | |
823 /* the "$1" - "$9" captures */ | |
824 | |
825 copy_capture = ngx_http_rewrite_add_code(lcf->codes, | |
826 sizeof(ngx_http_rewrite_copy_capture_code_t), | |
827 (u_char **) ®ex); | |
828 if (copy_capture == NULL) { | |
381 return NGX_CONF_ERROR; | 829 return NGX_CONF_ERROR; |
382 } | 830 } |
383 | 831 |
384 data = &value[2].data[i]; | 832 i++; |
385 | 833 |
386 if (value[2].data[i] == '$' | 834 copy_capture->code = ngx_http_rewrite_copy_capture_code; |
387 && i < value[2].len | 835 copy_capture->n = value[2].data[i] - '0'; |
388 && value[2].data[i + 1] >= '1' | 836 |
389 && value[2].data[i + 1] <= '9') | 837 if (regex->ncaptures < copy_capture->n) { |
390 { | 838 regex->ncaptures = copy_capture->n; |
391 op->op = NGX_HTTP_REWRITE_COPY_CAPTURE; | 839 } |
392 op->len = 0; | 840 |
393 op->data = value[2].data[++i] - '0'; | 841 copy_capture->n *= 2; |
394 | 842 |
395 if (rule->ncaptures < op->data) { | 843 i++; |
396 rule->ncaptures = op->data; | 844 |
845 continue; | |
846 } | |
847 | |
848 if (value[2].data[i] == '?') { | |
849 | |
850 /* the arguments */ | |
851 | |
852 if (i == value[2].len - 1) { | |
853 /* the last "?" drops the original arguments */ | |
854 regex->args = 0; | |
855 break; | |
856 } | |
857 | |
858 if (!regex->redirect) { | |
859 code = ngx_http_rewrite_add_code(lcf->codes, sizeof(uintptr_t), | |
860 (u_char **) ®ex); | |
861 if (code == NULL) { | |
862 return NGX_CONF_ERROR; | |
397 } | 863 } |
864 | |
865 *code = ngx_http_rewrite_start_args_code; | |
398 | 866 |
399 i++; | 867 i++; |
400 | 868 |
401 continue; | 869 continue; |
402 } | 870 } |
871 } | |
872 | |
873 i++; | |
874 | |
875 /* the substituion strings */ | |
876 | |
877 while (i < value[2].len && value[2].data[i] != '$') { | |
403 | 878 |
404 if (value[2].data[i] == '?') { | 879 if (value[2].data[i] == '?') { |
405 op->op = NGX_HTTP_REWRITE_START_ARGS; | 880 |
406 op->len = 0; | 881 if (i == value[2].len - 1) { |
407 op->data = 0; | 882 /* |
408 | 883 * the last "?" drops the original arguments, |
409 i++; | 884 * and it should not be copied to a substituion |
410 | 885 */ |
886 regex->args = 0; | |
887 break; | |
888 } | |
889 | |
890 if (!regex->redirect) { | |
891 break; | |
892 } | |
893 } | |
894 | |
895 i++; | |
896 } | |
897 | |
898 len = &value[2].data[i] - data; | |
899 | |
900 if (len == 0) { | |
901 continue; | |
902 } | |
903 | |
904 regex->size += len; | |
905 | |
906 size = (len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1); | |
907 | |
908 copy = ngx_http_rewrite_add_code(lcf->codes, | |
909 sizeof(ngx_http_rewrite_copy_code_t) + size, | |
910 (u_char **) ®ex); | |
911 if (copy == NULL) { | |
912 return NGX_CONF_ERROR; | |
913 } | |
914 | |
915 copy->code = ngx_http_rewrite_copy_code; | |
916 copy->len = len; | |
917 | |
918 ngx_memcpy((u_char *) copy + sizeof(ngx_http_rewrite_copy_code_t), | |
919 data, len); | |
920 } | |
921 | |
922 n = ngx_regex_capture_count(regex->regex); | |
923 | |
924 if (regex->ncaptures > n) { | |
925 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
926 "pattern \"%V\" has less captures " | |
927 "than referrenced in substitution \"%V\"", | |
928 &value[1], &value[2]); | |
929 return NGX_CONF_ERROR; | |
930 } | |
931 | |
932 if (regex->ncaptures < n) { | |
933 regex->ncaptures = n; | |
934 } | |
935 | |
936 if (regex->ncaptures) { | |
937 regex->ncaptures = (regex->ncaptures + 1) * 3; | |
938 } | |
939 | |
940 regex_end = ngx_http_rewrite_add_code(lcf->codes, | |
941 sizeof(ngx_http_rewrite_regex_end_code_t), | |
942 (u_char **) ®ex); | |
943 if (regex_end == NULL) { | |
944 return NGX_CONF_ERROR; | |
945 } | |
946 | |
947 regex_end->code = ngx_http_rewrite_regex_end_code; | |
948 regex_end->uri = regex->uri; | |
949 regex_end->args = regex->args; | |
950 regex_end->redirect = regex->redirect; | |
951 | |
952 if (last) { | |
953 code = ngx_http_rewrite_add_code(lcf->codes, sizeof(uintptr_t), | |
954 (u_char **) ®ex); | |
955 if (code == NULL) { | |
956 return NGX_CONF_ERROR; | |
957 } | |
958 | |
959 *code = (uintptr_t) NULL; | |
960 } | |
961 | |
962 regex->next = (u_char *) lcf->codes->elts + lcf->codes->nelts | |
963 - (u_char *) regex; | |
964 | |
965 return NGX_CONF_OK; | |
966 } | |
967 | |
968 | |
969 | |
970 static char *ngx_http_rewrite_return(ngx_conf_t *cf, ngx_command_t *cmd, | |
971 void *conf) | |
972 { | |
973 ngx_http_rewrite_loc_conf_t *lcf = conf; | |
974 | |
975 ngx_str_t *value; | |
976 ngx_http_rewrite_return_code_t *ret; | |
977 | |
978 ret = ngx_http_rewrite_start_code(cf->pool, &lcf->codes, | |
979 sizeof(ngx_http_rewrite_return_code_t)); | |
980 if (ret == NULL) { | |
981 return NGX_CONF_ERROR; | |
982 } | |
983 | |
984 value = cf->args->elts; | |
985 | |
986 ret->code = ngx_http_rewrite_return_code; | |
987 ret->null = (uintptr_t) NULL; | |
988 | |
989 ret->status = ngx_atoi(value[1].data, value[1].len); | |
990 | |
991 if (ret->status == (uintptr_t) NGX_ERROR) { | |
992 return NGX_CONF_ERROR; | |
993 } | |
994 | |
995 return NGX_CONF_OK; | |
996 } | |
997 | |
998 | |
999 static char *ngx_http_rewrite_if(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
1000 { | |
1001 ngx_http_rewrite_loc_conf_t *lcf = conf; | |
1002 | |
1003 void *mconf; | |
1004 char *rv; | |
1005 u_char *elts; | |
1006 ngx_str_t *value; | |
1007 ngx_uint_t i; | |
1008 ngx_conf_t save; | |
1009 ngx_http_rewrite_code_pt *code; | |
1010 ngx_http_module_t *module; | |
1011 ngx_http_conf_ctx_t *ctx, *pctx; | |
1012 ngx_http_variable_t *var; | |
1013 ngx_http_core_loc_conf_t *clcf, *pclcf, **clcfp; | |
1014 ngx_http_core_main_conf_t *cmcf; | |
1015 ngx_http_rewrite_if_code_t *if_code; | |
1016 ngx_http_rewrite_var_code_t *var_code; | |
1017 ngx_http_rewrite_loc_conf_t *nlcf; | |
1018 | |
1019 if (!(ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)))) { | |
1020 return NGX_CONF_ERROR; | |
1021 } | |
1022 | |
1023 pctx = cf->ctx; | |
1024 ctx->main_conf = pctx->main_conf; | |
1025 ctx->srv_conf = pctx->srv_conf; | |
1026 | |
1027 ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); | |
1028 if (ctx->loc_conf == NULL) { | |
1029 return NGX_CONF_ERROR; | |
1030 } | |
1031 | |
1032 for (i = 0; ngx_modules[i]; i++) { | |
1033 if (ngx_modules[i]->type != NGX_HTTP_MODULE) { | |
1034 continue; | |
1035 } | |
1036 | |
1037 module = ngx_modules[i]->ctx; | |
1038 | |
1039 if (module->create_loc_conf) { | |
1040 | |
1041 if (!(mconf = module->create_loc_conf(cf))) { | |
1042 return NGX_CONF_ERROR; | |
1043 } | |
1044 | |
1045 ctx->loc_conf[ngx_modules[i]->ctx_index] = mconf; | |
1046 } | |
1047 } | |
1048 | |
1049 pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index]; | |
1050 | |
1051 clcf = ctx->loc_conf[ngx_http_core_module.ctx_index]; | |
1052 clcf->loc_conf = ctx->loc_conf; | |
1053 clcf->name = pclcf->name; | |
1054 clcf->noname = 1; | |
1055 | |
1056 if (pclcf->locations.elts == NULL) { | |
1057 if (ngx_array_init(&pclcf->locations, cf->pool, 4, sizeof(void *)) | |
1058 == NGX_ERROR) | |
1059 { | |
1060 return NGX_CONF_ERROR; | |
1061 } | |
1062 } | |
1063 | |
1064 if (!(clcfp = ngx_push_array(&pclcf->locations))) { | |
1065 return NGX_CONF_ERROR; | |
1066 } | |
1067 | |
1068 *clcfp = clcf; | |
1069 | |
1070 | |
1071 /* STUB: "if ($var)" */ | |
1072 | |
1073 value = cf->args->elts; | |
1074 | |
1075 if (value[1].len < 2 | |
1076 || value[1].data[0] != '(' | |
1077 || value[1].data[1] != '$' | |
1078 || value[1].data[value[1].len - 1] != ')') | |
1079 { | |
1080 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
1081 "invalid condition \"%V\"", &value[1]); | |
1082 return NGX_CONF_ERROR; | |
1083 } | |
1084 | |
1085 value[1].len -= 3; | |
1086 value[1].data += 2; | |
1087 | |
1088 if (value[1].len == sizeof("invalid_referer") - 1 | |
1089 && ngx_strncmp(value[1].data, "invalid_referer", | |
1090 sizeof("invalid_referer") - 1) == 0) | |
1091 { | |
1092 code = ngx_http_rewrite_start_code(cf->pool, &lcf->codes, | |
1093 sizeof(ngx_http_rewrite_code_pt)); | |
1094 if (code == NULL) { | |
1095 return NGX_CONF_ERROR; | |
1096 } | |
1097 | |
1098 *code = ngx_http_rewrite_invalid_referer_code; | |
1099 | |
1100 } else { | |
1101 | |
1102 cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); | |
1103 | |
1104 var = cmcf->variables.elts; | |
1105 for (i = 0; i < cmcf->variables.nelts; i++) { | |
1106 if (var[i].name.len != value[1].len) { | |
411 continue; | 1107 continue; |
412 } | 1108 } |
413 | 1109 |
414 i++; | 1110 if (ngx_strncasecmp(var[i].name.data, value[1].data, |
415 | 1111 var[i].name.len) == 0) |
416 while (i < value[2].len | |
417 && value[2].data[i] != '$' | |
418 && value[2].data[i] != '?') | |
419 { | 1112 { |
420 i++; | 1113 break; |
421 } | 1114 } |
422 | 1115 } |
423 len = &value[2].data[i] - data; | 1116 |
424 rule->size += len; | 1117 if (i == cmcf->variables.nelts) { |
425 | |
426 if (len) { | |
427 | |
428 op->len = len; | |
429 | |
430 if (len <= sizeof(uintptr_t)) { | |
431 op->op = NGX_HTTP_REWRITE_COPY_SHORT; | |
432 op->data = 0; | |
433 | |
434 while (len--) { | |
435 op->data <<= 8; | |
436 op->data |= data[len]; | |
437 } | |
438 | |
439 } else { | |
440 op->op = NGX_HTTP_REWRITE_COPY_LONG; | |
441 | |
442 if (!(p = ngx_palloc(cf->pool, len))) { | |
443 return NGX_CONF_ERROR; | |
444 } | |
445 | |
446 ngx_memcpy(p, data, len); | |
447 op->data = (uintptr_t) p; | |
448 } | |
449 } | |
450 } | |
451 | |
452 n = ngx_regex_capture_count(rule->regex); | |
453 | |
454 if (rule->ncaptures > n) { | |
455 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | 1118 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, |
456 "pattern \"%V\" has less captures " | 1119 "unknown variable name \"%V\"", &value[1]); |
457 "than referrenced in substitution \"%V\"", | |
458 &value[1], &value[2]); | |
459 return NGX_CONF_ERROR; | 1120 return NGX_CONF_ERROR; |
460 } | 1121 } |
461 | 1122 |
462 if (rule->ncaptures < n) { | 1123 var_code = ngx_http_rewrite_start_code(cf->pool, &lcf->codes, |
463 rule->ncaptures = n; | 1124 sizeof(ngx_http_rewrite_var_code_t)); |
464 } | 1125 if (var_code == NULL) { |
465 | 1126 return NGX_CONF_ERROR; |
466 if (rule->ncaptures) { | 1127 } |
467 rule->ncaptures = (rule->ncaptures + 1) * 3; | 1128 |
468 } | 1129 var_code->code = ngx_http_rewrite_var_code; |
469 | 1130 var_code->index = var[i].index; |
470 if (cf->args->nelts > 3) { | 1131 } |
471 if (ngx_strcmp(value[3].data, "last") == 0) { | 1132 |
472 rule->last = 1; | 1133 if_code = ngx_array_push_n(lcf->codes, sizeof(ngx_http_rewrite_if_code_t)); |
473 | 1134 if (if_code == NULL) { |
474 } else { | 1135 return NULL; |
475 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | 1136 } |
476 "invalid parameter \"%V\"", &value[3]); | 1137 |
477 return NGX_CONF_ERROR; | 1138 if_code->code = ngx_http_rewrite_if_code; |
478 } | 1139 |
479 } | 1140 elts = lcf->codes->elts; |
480 } | 1141 |
1142 | |
1143 /* the inside directives must compile to the same code array */ | |
1144 | |
1145 nlcf = ctx->loc_conf[ngx_http_rewrite_module.ctx_index]; | |
1146 nlcf->codes = lcf->codes; | |
1147 | |
1148 | |
1149 save = *cf; | |
1150 cf->ctx = ctx; | |
1151 | |
1152 if (pclcf->name.len == 0) { | |
1153 if_code->loc_conf = NULL; | |
1154 cf->cmd_type = NGX_HTTP_SIF_CONF; | |
1155 | |
1156 } else { | |
1157 if_code->loc_conf = ctx->loc_conf; | |
1158 cf->cmd_type = NGX_HTTP_LIF_CONF; | |
1159 } | |
1160 | |
1161 rv = ngx_conf_parse(cf, NULL); | |
1162 | |
1163 *cf = save; | |
1164 | |
1165 if (rv != NGX_CONF_OK) { | |
1166 return rv; | |
1167 } | |
1168 | |
1169 | |
1170 if (elts != lcf->codes->elts) { | |
1171 if_code = (ngx_http_rewrite_if_code_t *) | |
1172 ((u_char *) if_code + ((u_char *) lcf->codes->elts - elts)); | |
1173 } | |
1174 | |
1175 if_code->next = (u_char *) lcf->codes->elts + lcf->codes->nelts | |
1176 - (u_char *) if_code; | |
481 | 1177 |
482 return NGX_CONF_OK; | 1178 return NGX_CONF_OK; |
483 } | 1179 } |
484 | 1180 |
485 | 1181 |
486 static char *ngx_http_redirect(ngx_conf_t *cf, void *post, void *data) | 1182 static char *ngx_http_rewrite_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd, |
487 { | 1183 void *conf) |
488 ngx_http_core_loc_conf_t *clcf; | 1184 { |
489 | 1185 ngx_http_rewrite_loc_conf_t *lcf = conf; |
490 clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); | 1186 |
491 clcf->handler = ngx_http_redirect_handler; | 1187 ngx_uint_t i, server_names; |
1188 ngx_str_t *value; | |
1189 ngx_http_server_name_t *sn; | |
1190 ngx_http_core_srv_conf_t *cscf; | |
1191 ngx_http_rewrite_referer_t *ref; | |
1192 | |
1193 cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module); | |
1194 | |
1195 if (lcf->referers == NULL) { | |
1196 lcf->referers = ngx_array_create(cf->pool, | |
1197 cf->args->nelts + cscf->server_names.nelts, | |
1198 sizeof(ngx_http_rewrite_referer_t)); | |
1199 if (lcf->referers == NULL) { | |
1200 return NGX_CONF_ERROR; | |
1201 } | |
1202 } | |
1203 | |
1204 value = cf->args->elts; | |
1205 server_names = 0; | |
1206 | |
1207 for (i = 1; i < cf->args->nelts; i++) { | |
1208 if (value[i].len == 0) { | |
1209 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
1210 "invalid referer \"%V\"", &value[i]); | |
1211 return NGX_CONF_ERROR; | |
1212 } | |
1213 | |
1214 if (ngx_strcmp(value[i].data, "none") == 0) { | |
1215 lcf->no_referer = 1; | |
1216 continue; | |
1217 } | |
1218 | |
1219 if (ngx_strcmp(value[i].data, "server_names") == 0) { | |
1220 server_names = 1; | |
1221 continue; | |
1222 } | |
1223 | |
1224 if (!(ref = ngx_array_push(lcf->referers))) { | |
1225 return NGX_CONF_ERROR; | |
1226 } | |
1227 | |
1228 if (value[i].data[0] != '*') { | |
1229 ref->name = value[i]; | |
1230 ref->wildcard = 0; | |
1231 continue; | |
1232 } | |
1233 | |
1234 if (value[i].data[1] != '.') { | |
1235 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
1236 "invalid wildcard referer \"%V\"", &value[i]); | |
1237 return NGX_CONF_ERROR; | |
1238 } | |
1239 | |
1240 ref->name.len = value[i].len - 1; | |
1241 ref->name.data = value[i].data + 1; | |
1242 ref->wildcard = 1; | |
1243 } | |
1244 | |
1245 if (!server_names) { | |
1246 return NGX_CONF_OK; | |
1247 } | |
1248 | |
1249 sn = cscf->server_names.elts; | |
1250 for (i = 0; i < cscf->server_names.nelts; i++) { | |
1251 if (!(ref = ngx_array_push(lcf->referers))) { | |
1252 return NGX_CONF_ERROR; | |
1253 } | |
1254 | |
1255 ref->name.len = sn[i].name.len + 1; | |
1256 if (!(ref->name.data = ngx_palloc(cf->pool, ref->name.len))) { | |
1257 return NGX_CONF_ERROR; | |
1258 } | |
1259 | |
1260 ngx_memcpy(ref->name.data, sn[i].name.data, sn[i].name.len); | |
1261 ref->name.data[sn[i].name.len] = '/'; | |
1262 ref->wildcard = sn[i].wildcard; | |
1263 } | |
492 | 1264 |
493 return NGX_CONF_OK; | 1265 return NGX_CONF_OK; |
494 } | 1266 } |
495 | 1267 |
496 | 1268 |
497 static ngx_int_t ngx_http_rewrite_init(ngx_cycle_t *cycle) | 1269 static void *ngx_http_rewrite_start_code(ngx_pool_t *pool, |
498 { | 1270 ngx_array_t **codes, size_t size) |
499 ngx_http_handler_pt *h; | 1271 { |
500 ngx_http_core_main_conf_t *cmcf; | 1272 if (*codes == NULL) { |
501 | 1273 if (!(*codes = ngx_array_create(pool, 256, 1))) { |
502 cmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_core_module); | 1274 return NULL; |
503 | 1275 } |
504 h = ngx_push_array(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers); | 1276 } |
505 if (h == NULL) { | 1277 |
506 return NGX_ERROR; | 1278 return ngx_array_push_n(*codes, size); |
507 } | 1279 } |
508 | 1280 |
509 *h = ngx_http_rewrite_handler; | 1281 |
510 | 1282 static void *ngx_http_rewrite_add_code(ngx_array_t *codes, size_t size, |
511 return NGX_OK; | 1283 u_char **main) |
512 } | 1284 { |
1285 u_char *elts; | |
1286 void *new; | |
1287 | |
1288 elts = codes->elts; | |
1289 | |
1290 if (!(new = ngx_array_push_n(codes, size))) { | |
1291 return NGX_CONF_ERROR; | |
1292 } | |
1293 | |
1294 if (elts != codes->elts) { | |
1295 *main += (u_char *) codes->elts - elts; | |
1296 } | |
1297 | |
1298 return new; | |
1299 } |