├── README.md ├── config └── ngx_http_internal_redirect_module.c /README.md: -------------------------------------------------------------------------------- 1 | # NGINX HTTP Internal Redirect Module 2 | 3 | ## Introduction 4 | 5 | The `ngx_http_internal_redirect_module` is used to make an internal redirect to the uri specified according to the condition specified. 6 | 7 | ## Synopsis 8 | 9 | ```nginx 10 | 11 | location / { 12 | internal_redirect_if ($request_method = 'FOO') skip; 13 | internal_redirect_if ($request_method = 'FOO') @foo; 14 | internal_redirect_if ($request_method = 'BAR') /foo; 15 | internal_redirect_if ($request_method = 'BAZ') =200; 16 | internal_redirect_if ($request_method = 'QUZ') "$foo$bar"; 17 | root html; 18 | } 19 | 20 | location @foo { 21 | return 200; 22 | } 23 | 24 | location /bar { 25 | return 200; 26 | } 27 | ``` 28 | 29 | ## Directives 30 | 31 | * **syntax**: *internal_redirect_if (condition) skip* 32 | * **syntax**: *internal_redirect_if (condition) uri* 33 | * **syntax**: *internal_redirect_if (condition) =code* 34 | * **default**: -- 35 | * **context**: http, server, location 36 | 37 | The specified `condition` is evaluated. If true, an internal redirect would be made to the `uri` specified in this directive. The syntax of condition is the same as it in the `if` directive in `rewrite` module. The syntax of `uri` is the same as it in the `try_files` directive. If the last argument is `skip`, this handler would be skipped. 38 | 39 | * **syntax**: *internal_redirect_if_no_postponed on | off* 40 | * **default**: off 41 | * **context**: http 42 | 43 | Control whether or not to disable postpone the `internal_redirect_if` directives to run at the end of the request-processing phase. By default, this directive is turned off. 44 | 45 | * **syntax**: *internal_redirect_if_phase post_read | rewrite* 46 | * **default**: rewrite 47 | * **context**: http 48 | 49 | The phase to which this handler would register to. Note, if set to `post_read`, the conditions set in `location {}` would not come to force. 50 | 51 | 52 | ## Installation 53 | 54 | ```shell 55 | cd nginx-**version** 56 | ./configure --add-module=/path/to/this/directory 57 | make 58 | make install 59 | ``` 60 | 61 | ## Status 62 | 63 | This module is tested with following nginx releases: 64 | - 1.2.6 65 | - 1.2.7 66 | - 1.23.1 67 | 68 | Others are not tested. 69 | 70 | ## Author 71 | 72 | FengGu 73 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | ngx_addon_name=ngx_http_internal_redirect_module 2 | HTTP_MODULES="$HTTP_MODULES ngx_http_internal_redirect_module" 3 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_internal_redirect_module.c" 4 | -------------------------------------------------------------------------------- /ngx_http_internal_redirect_module.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2022, FengGu 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | * 27 | */ 28 | 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | 35 | typedef struct { 36 | ngx_flag_t postponed; 37 | ngx_uint_t phase; 38 | unsigned required:1; 39 | } ngx_http_internal_redirect_main_conf_t; 40 | 41 | 42 | typedef struct { 43 | ngx_array_t *codes; 44 | ngx_array_t *lengths; 45 | ngx_array_t *values; 46 | ngx_str_t name; 47 | unsigned code:10; 48 | } ngx_http_internal_redirect_entry_t; 49 | 50 | 51 | typedef struct { 52 | ngx_array_t *redirects; /* ngx_http_internal_redirect_entry_t */ 53 | } ngx_http_internal_redirect_loc_conf_t; 54 | 55 | 56 | static ngx_conf_enum_t ngx_http_internal_redirect_if_phase[] = { 57 | { ngx_string("post_read"), NGX_HTTP_POST_READ_PHASE }, 58 | { ngx_string("rewrite"), NGX_HTTP_REWRITE_PHASE }, 59 | { ngx_null_string, 0 } 60 | }; 61 | 62 | 63 | static char *ngx_http_internal_redirect_if(ngx_conf_t *cf, ngx_command_t *cmd, 64 | void *conf); 65 | static ngx_int_t ngx_http_internal_redirect_init(ngx_conf_t *cf); 66 | static ngx_int_t ngx_http_internal_redirect_handler(ngx_http_request_t *r); 67 | static char *ngx_http_internal_redirect_if_condition(ngx_conf_t *cf, 68 | ngx_http_internal_redirect_entry_t *redirect); 69 | static char *ngx_http_internal_redirect_if_condition_value(ngx_conf_t *cf, 70 | ngx_http_internal_redirect_entry_t *redirect, ngx_str_t *value); 71 | static void *ngx_http_internal_redirect_create_main_conf(ngx_conf_t *cf); 72 | static char *ngx_http_internal_redirect_init_main_conf(ngx_conf_t *cf, 73 | void *conf); 74 | static void *ngx_http_internal_redirect_create_loc_conf(ngx_conf_t *cf); 75 | static char *ngx_http_internal_redirect_merge_loc_conf(ngx_conf_t *cf, 76 | void *parent, void *child); 77 | 78 | 79 | static ngx_command_t ngx_http_internal_redirect_commands[] = { 80 | 81 | { ngx_string("internal_redirect_if"), 82 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_2MORE, 83 | ngx_http_internal_redirect_if, 84 | NGX_HTTP_LOC_CONF_OFFSET, 85 | 0, 86 | NULL }, 87 | 88 | { ngx_string("internal_redirect_if_no_postpone"), 89 | NGX_HTTP_MAIN_CONF|NGX_CONF_FLAG, 90 | ngx_conf_set_flag_slot, 91 | NGX_HTTP_MAIN_CONF_OFFSET, 92 | offsetof(ngx_http_internal_redirect_main_conf_t, postponed), 93 | NULL }, 94 | 95 | { ngx_string("internal_redirect_if_phase"), 96 | NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, 97 | ngx_conf_set_enum_slot, 98 | NGX_HTTP_MAIN_CONF_OFFSET, 99 | offsetof(ngx_http_internal_redirect_main_conf_t, phase), 100 | ngx_http_internal_redirect_if_phase }, 101 | 102 | ngx_null_command 103 | }; 104 | 105 | 106 | static ngx_http_module_t ngx_http_internal_redirect_module_ctx = { 107 | NULL, /* preconfiguration */ 108 | ngx_http_internal_redirect_init, /* postconfiguration */ 109 | 110 | ngx_http_internal_redirect_create_main_conf, 111 | /* create main configuration */ 112 | ngx_http_internal_redirect_init_main_conf, 113 | /* init main configuration */ 114 | 115 | NULL, /* create server configuration */ 116 | NULL, /* merge server configuration */ 117 | 118 | ngx_http_internal_redirect_create_loc_conf, 119 | /* create location configuration */ 120 | ngx_http_internal_redirect_merge_loc_conf, 121 | /* merge location configuration */ 122 | }; 123 | 124 | 125 | ngx_module_t ngx_http_internal_redirect_module = { 126 | NGX_MODULE_V1, 127 | &ngx_http_internal_redirect_module_ctx, /* module context */ 128 | ngx_http_internal_redirect_commands, /* module directives */ 129 | NGX_HTTP_MODULE, /* module type */ 130 | NULL, /* init master */ 131 | NULL, /* init module */ 132 | NULL, /* init process */ 133 | NULL, /* init thread */ 134 | NULL, /* exit thread */ 135 | NULL, /* exit process */ 136 | NULL, /* exit master */ 137 | NGX_MODULE_V1_PADDING 138 | }; 139 | 140 | 141 | static char * 142 | ngx_http_internal_redirect_if(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 143 | { 144 | ngx_str_t *value; 145 | ngx_int_t code; 146 | ngx_uint_t n; 147 | ngx_http_script_compile_t sc; 148 | ngx_http_internal_redirect_entry_t *redirect; 149 | ngx_http_internal_redirect_main_conf_t *imcf; 150 | ngx_http_internal_redirect_loc_conf_t *ilcf = conf; 151 | 152 | if (ilcf->redirects == NULL) { 153 | ilcf->redirects = ngx_array_create(cf->pool, 4, 154 | sizeof(ngx_http_internal_redirect_entry_t)); 155 | if (ilcf->redirects == NULL) { 156 | return NGX_CONF_ERROR; 157 | } 158 | } 159 | 160 | redirect = ngx_array_push(ilcf->redirects); 161 | if (redirect == NULL) { 162 | return NGX_CONF_ERROR; 163 | } 164 | ngx_memzero(redirect, sizeof(ngx_http_internal_redirect_entry_t)); 165 | 166 | value = cf->args->elts; 167 | redirect->name = value[cf->args->nelts - 1]; 168 | 169 | n = ngx_http_script_variables_count(&redirect->name); 170 | 171 | if (n) { 172 | ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); 173 | 174 | sc.cf = cf; 175 | sc.source = &redirect->name; 176 | sc.lengths = &redirect->lengths; 177 | sc.values = &redirect->values; 178 | sc.variables = n; 179 | sc.complete_lengths = 1; 180 | sc.complete_values = 1; 181 | 182 | if (ngx_http_script_compile(&sc) != NGX_OK) { 183 | return NGX_CONF_ERROR; 184 | } 185 | } 186 | 187 | if (redirect->name.data[0] == '=') { 188 | code = ngx_atoi(redirect->name.data + 1, redirect->name.len - 1); 189 | if (code == NGX_ERROR || code > 999) { 190 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 191 | "invalid code \"%V\"", 192 | &redirect->name); 193 | return NGX_CONF_ERROR; 194 | } 195 | 196 | redirect->code = code; 197 | } 198 | 199 | cf->args->nelts--; 200 | 201 | if (ngx_http_internal_redirect_if_condition(cf, redirect) != NGX_CONF_OK) { 202 | return NGX_CONF_ERROR; 203 | } 204 | 205 | imcf = ngx_http_conf_get_module_main_conf(cf, 206 | ngx_http_internal_redirect_module); 207 | imcf->required = 1; 208 | 209 | return NGX_CONF_OK; 210 | } 211 | 212 | 213 | static char * 214 | ngx_http_internal_redirect_if_condition(ngx_conf_t *cf, 215 | ngx_http_internal_redirect_entry_t *redirect) 216 | { 217 | u_char *p; 218 | size_t len; 219 | ngx_str_t *value; 220 | ngx_uint_t cur, last; 221 | ngx_http_script_code_pt *code; 222 | ngx_http_script_file_code_t *fop; 223 | #if (NGX_PCRE) 224 | ngx_regex_compile_t rc; 225 | ngx_http_script_regex_code_t *regex; 226 | u_char errstr[NGX_MAX_CONF_ERRSTR]; 227 | #endif 228 | 229 | value = cf->args->elts; 230 | last = cf->args->nelts - 1; 231 | 232 | if (value[1].len < 1 || value[1].data[0] != '(') { 233 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 234 | "invalid condition \"%V\"", &value[1]); 235 | return NGX_CONF_ERROR; 236 | } 237 | 238 | if (value[1].len == 1) { 239 | cur = 2; 240 | 241 | } else { 242 | cur = 1; 243 | value[1].len--; 244 | value[1].data++; 245 | } 246 | 247 | if (value[last].len < 1 || value[last].data[value[last].len - 1] != ')') { 248 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 249 | "invalid condition \"%V\"", &value[last]); 250 | return NGX_CONF_ERROR; 251 | } 252 | 253 | if (value[last].len == 1) { 254 | last--; 255 | 256 | } else { 257 | value[last].len--; 258 | value[last].data[value[last].len] = '\0'; 259 | } 260 | 261 | len = value[cur].len; 262 | p = value[cur].data; 263 | 264 | if (len > 1 && p[0] == '$') { 265 | 266 | if (cur != last && cur + 2 != last) { 267 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 268 | "invalid condition \"%V\"", &value[cur]); 269 | return NGX_CONF_ERROR; 270 | } 271 | 272 | if (ngx_http_internal_redirect_if_condition_value(cf, redirect, 273 | &value[cur]) 274 | != NGX_CONF_OK) 275 | { 276 | return NGX_CONF_ERROR; 277 | } 278 | 279 | if (cur == last) { 280 | goto end; 281 | } 282 | 283 | cur++; 284 | 285 | len = value[cur].len; 286 | p = value[cur].data; 287 | 288 | if (len == 1 && p[0] == '=') { 289 | if (ngx_http_internal_redirect_if_condition_value(cf, redirect, 290 | &value[last]) 291 | != NGX_CONF_OK) 292 | { 293 | return NGX_CONF_ERROR; 294 | } 295 | 296 | code = ngx_http_script_start_code(cf->pool, &redirect->codes, 297 | sizeof(uintptr_t)); 298 | if (code == NULL) { 299 | return NGX_CONF_ERROR; 300 | } 301 | 302 | *code = ngx_http_script_equal_code; 303 | 304 | goto end; 305 | } 306 | 307 | if (len == 2 && p[0] == '!' && p[1] == '=') { 308 | 309 | if (ngx_http_internal_redirect_if_condition_value(cf, redirect, 310 | &value[last]) 311 | != NGX_CONF_OK) 312 | { 313 | return NGX_CONF_ERROR; 314 | } 315 | 316 | code = ngx_http_script_start_code(cf->pool, &redirect->codes, 317 | sizeof(uintptr_t)); 318 | if (code == NULL) { 319 | return NGX_CONF_ERROR; 320 | } 321 | 322 | *code = ngx_http_script_not_equal_code; 323 | goto end; 324 | } 325 | 326 | if ((len == 1 && p[0] == '~') 327 | || (len == 2 && p[0] == '~' && p[1] == '*') 328 | || (len == 2 && p[0] == '!' && p[1] == '~') 329 | || (len == 3 && p[0] == '!' && p[1] == '~' && p[2] == '*')) 330 | { 331 | #if (NGX_PCRE) 332 | regex = ngx_http_script_start_code(cf->pool, &redirect->codes, 333 | sizeof(ngx_http_script_regex_code_t)); 334 | if (regex == NULL) { 335 | return NGX_CONF_ERROR; 336 | } 337 | 338 | ngx_memzero(regex, sizeof(ngx_http_script_regex_code_t)); 339 | 340 | ngx_memzero(&rc, sizeof(ngx_regex_compile_t)); 341 | 342 | rc.pattern = value[last]; 343 | rc.options = (p[len - 1] == '*') ? NGX_REGEX_CASELESS : 0; 344 | rc.err.len = NGX_MAX_CONF_ERRSTR; 345 | rc.err.data = errstr; 346 | 347 | regex->regex = ngx_http_regex_compile(cf, &rc); 348 | if (regex->regex == NULL) { 349 | return NGX_CONF_ERROR; 350 | } 351 | 352 | regex->code = ngx_http_script_regex_start_code; 353 | regex->next = sizeof(ngx_http_script_regex_code_t); 354 | regex->test = 1; 355 | if (p[0] == '!') { 356 | regex->negative_test = 1; 357 | } 358 | regex->name = value[last]; 359 | 360 | goto end; 361 | #else 362 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 363 | "using regex \"%V\" requires PCRE library", 364 | &value[last]); 365 | return NGX_CONF_ERROR; 366 | #endif 367 | } 368 | 369 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 370 | "unexpected \"%V\" in condition", &value[cur]); 371 | return NGX_CONF_ERROR; 372 | 373 | } else if ((len == 2 && p[0] == '-') 374 | || (len == 3 && p[0] == '!' && p[1] == '-')) 375 | { 376 | if (cur + 1 != last) { 377 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 378 | "invalid condition \"%V\"", &value[cur]); 379 | return NGX_CONF_ERROR; 380 | } 381 | 382 | value[last].data[value[last].len] = '\0'; 383 | value[last].len++; 384 | 385 | if (ngx_http_internal_redirect_if_condition_value(cf, redirect, 386 | &value[last]) 387 | != NGX_CONF_OK) 388 | { 389 | return NGX_CONF_ERROR; 390 | } 391 | 392 | fop = ngx_http_script_start_code(cf->pool, &redirect->codes, 393 | sizeof(ngx_http_script_file_code_t)); 394 | if (fop == NULL) { 395 | return NGX_CONF_ERROR; 396 | } 397 | 398 | fop->code = ngx_http_script_file_code; 399 | 400 | if (p[1] == 'f') { 401 | fop->op = ngx_http_script_file_plain; 402 | goto end; 403 | } 404 | 405 | if (p[1] == 'd') { 406 | fop->op = ngx_http_script_file_dir; 407 | goto end; 408 | } 409 | 410 | if (p[1] == 'e') { 411 | fop->op = ngx_http_script_file_exists; 412 | goto end; 413 | } 414 | 415 | if (p[1] == 'x') { 416 | fop->op = ngx_http_script_file_exec; 417 | goto end; 418 | } 419 | 420 | if (p[0] == '!') { 421 | if (p[2] == 'f') { 422 | fop->op = ngx_http_script_file_not_plain; 423 | goto end; 424 | } 425 | 426 | if (p[2] == 'd') { 427 | fop->op = ngx_http_script_file_not_dir; 428 | goto end; 429 | } 430 | 431 | if (p[2] == 'e') { 432 | fop->op = ngx_http_script_file_not_exists; 433 | goto end; 434 | } 435 | 436 | if (p[2] == 'x') { 437 | fop->op = ngx_http_script_file_not_exec; 438 | goto end; 439 | } 440 | } 441 | 442 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 443 | "invalid condition \"%V\"", &value[cur]); 444 | return NGX_CONF_ERROR; 445 | } 446 | 447 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 448 | "invalid condition \"%V\"", &value[cur]); 449 | 450 | return NGX_CONF_ERROR; 451 | 452 | end: 453 | 454 | code = ngx_array_push_n(redirect->codes, sizeof(uintptr_t)); 455 | if (code == NULL) { 456 | return NGX_CONF_ERROR; 457 | } 458 | 459 | *(uintptr_t *) code = (uintptr_t) NULL; 460 | 461 | return NGX_CONF_OK; 462 | } 463 | 464 | 465 | static char * 466 | ngx_http_internal_redirect_if_condition_value(ngx_conf_t *cf, 467 | ngx_http_internal_redirect_entry_t *redirect, ngx_str_t *value) 468 | { 469 | ngx_int_t n; 470 | ngx_http_script_compile_t sc; 471 | ngx_http_script_value_code_t *val; 472 | ngx_http_script_complex_value_code_t *complex; 473 | 474 | n = ngx_http_script_variables_count(value); 475 | 476 | if (n == 0) { 477 | val = ngx_http_script_start_code(cf->pool, &redirect->codes, 478 | sizeof(ngx_http_script_value_code_t)); 479 | if (val == NULL) { 480 | return NGX_CONF_ERROR; 481 | } 482 | 483 | n = ngx_atoi(value->data, value->len); 484 | 485 | if (n == NGX_ERROR) { 486 | n = 0; 487 | } 488 | 489 | val->code = ngx_http_script_value_code; 490 | val->value = (uintptr_t) n; 491 | val->text_len = (uintptr_t) value->len; 492 | val->text_data = (uintptr_t) value->data; 493 | 494 | return NGX_CONF_OK; 495 | } 496 | 497 | complex = ngx_http_script_start_code(cf->pool, &redirect->codes, 498 | sizeof(ngx_http_script_complex_value_code_t)); 499 | if (complex == NULL) { 500 | return NGX_CONF_ERROR; 501 | } 502 | 503 | complex->code = ngx_http_script_complex_value_code; 504 | complex->lengths = NULL; 505 | 506 | ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); 507 | 508 | sc.cf = cf; 509 | sc.source = value; 510 | sc.lengths = &complex->lengths; 511 | sc.values = &redirect->codes; 512 | sc.variables = n; 513 | sc.complete_lengths = 1; 514 | 515 | if (ngx_http_script_compile(&sc) != NGX_OK) { 516 | return NGX_CONF_ERROR; 517 | } 518 | 519 | return NGX_CONF_OK; 520 | } 521 | 522 | 523 | static ngx_int_t 524 | ngx_http_internal_redirect_init(ngx_conf_t *cf) 525 | { 526 | ngx_http_handler_pt *h; 527 | ngx_http_core_main_conf_t *cmcf; 528 | ngx_http_internal_redirect_main_conf_t *imcf; 529 | 530 | cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); 531 | imcf = ngx_http_conf_get_module_main_conf(cf, 532 | ngx_http_internal_redirect_module); 533 | if (imcf->required) { 534 | h = ngx_array_push(&cmcf->phases[imcf->phase].handlers); 535 | if (h == NULL) { 536 | return NGX_ERROR; 537 | } 538 | 539 | *h = ngx_http_internal_redirect_handler; 540 | } 541 | 542 | return NGX_OK; 543 | } 544 | 545 | 546 | static ngx_int_t 547 | ngx_http_internal_redirect_handler(ngx_http_request_t *r) 548 | { 549 | u_char *p; 550 | ngx_uint_t i; 551 | ngx_str_t uri, args; 552 | ngx_http_script_code_pt code; 553 | ngx_http_script_engine_t e; 554 | ngx_http_variable_value_t stack[10]; 555 | ngx_http_internal_redirect_entry_t *redirects; 556 | ngx_http_internal_redirect_main_conf_t *imcf; 557 | ngx_http_internal_redirect_loc_conf_t *ilcf; 558 | ngx_http_core_main_conf_t *cmcf; 559 | ngx_http_phase_handler_t *ph, *cur_ph, *last_ph, tmp; 560 | 561 | imcf = ngx_http_get_module_main_conf(r, ngx_http_internal_redirect_module); 562 | 563 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 564 | "internal_redirect handler: %ui, postponed: %i", 565 | r->phase_handler, 566 | imcf->postponed); 567 | 568 | if (!imcf->postponed) { 569 | 570 | imcf->postponed = 1; 571 | 572 | cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); 573 | 574 | ph = cmcf->phase_engine.handlers; 575 | cur_ph = &ph[r->phase_handler]; 576 | last_ph = &ph[cur_ph->next - 1]; 577 | 578 | ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 579 | "internal_redirect lh: %ui, cur_ph: %p, last_ph: %p", 580 | cur_ph->next - 1, 581 | cur_ph, 582 | last_ph); 583 | 584 | if (cur_ph < last_ph) { 585 | tmp = *cur_ph; 586 | 587 | ngx_memmove(cur_ph, cur_ph + 1, 588 | (last_ph - cur_ph) * sizeof(ngx_http_phase_handler_t)); 589 | 590 | *last_ph = tmp; 591 | r->phase_handler--; /* redo the current ph */ 592 | 593 | return NGX_DECLINED; 594 | } 595 | } 596 | 597 | ilcf = ngx_http_get_module_loc_conf(r, ngx_http_internal_redirect_module); 598 | 599 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 600 | "internal_redirect redirects: %p, n: %i", 601 | ilcf->redirects, 602 | ilcf->redirects ? ilcf->redirects->nelts : 0); 603 | 604 | if (ilcf->redirects == NULL) { 605 | return NGX_DECLINED; 606 | } 607 | 608 | redirects = ilcf->redirects->elts; 609 | for (i = 0; i < ilcf->redirects->nelts; i++) { 610 | ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); 611 | ngx_memzero(&stack, sizeof(stack)); 612 | 613 | e.sp = stack; 614 | e.ip = redirects[i].codes->elts; 615 | e.request = r; 616 | e.quote = 1; 617 | e.log = 1; 618 | e.status = NGX_DECLINED; 619 | 620 | while (*(uintptr_t *) e.ip) { 621 | code = *(ngx_http_script_code_pt *) e.ip; 622 | code(&e); 623 | } 624 | 625 | e.sp--; 626 | 627 | if (e.sp->len && (e.sp->len != 1 || e.sp->data[0] != '0')) { 628 | break; 629 | } 630 | } 631 | 632 | if (i == ilcf->redirects->nelts) { 633 | return NGX_DECLINED; 634 | } 635 | 636 | if (redirects[i].code) { 637 | return redirects[i].code; 638 | } 639 | 640 | if (redirects[i].lengths) { 641 | 642 | if (ngx_http_script_run(r, &uri, redirects[i].lengths->elts, 0, 643 | redirects->values->elts) 644 | == NULL) 645 | { 646 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 647 | } 648 | 649 | } else { 650 | uri = redirects[i].name; 651 | } 652 | 653 | // skip this handler 654 | if (uri.len == 4 && uri.data[0] == 's' && uri.data[1] == 'k' 655 | && uri.data[2] == 'i' && uri.data[3] == 'p') 656 | { 657 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 658 | "internal_redirect handler skip"); 659 | 660 | return NGX_DECLINED; 661 | } 662 | 663 | if (uri.data[0] == '@') { 664 | 665 | (void) ngx_http_named_location(r, &uri); 666 | 667 | } else { 668 | 669 | if (uri.data[0] != '/') { 670 | p = ngx_pcalloc(r->pool, uri.len + 1); 671 | if (p == NULL) { 672 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 673 | } 674 | 675 | uri.len++; 676 | *p = '/'; 677 | ngx_memcpy(p + 1, uri.data, uri.len); 678 | uri.data = p; 679 | } 680 | 681 | ngx_http_split_args(r, &uri, &args); 682 | 683 | (void) ngx_http_internal_redirect(r, &uri, &args); 684 | } 685 | 686 | ngx_http_finalize_request(r, NGX_DONE); 687 | 688 | return NGX_DONE; 689 | } 690 | 691 | 692 | static void * 693 | ngx_http_internal_redirect_create_main_conf(ngx_conf_t *cf) 694 | { 695 | ngx_http_internal_redirect_main_conf_t *imcf; 696 | 697 | imcf = ngx_pcalloc(cf->pool, 698 | sizeof(ngx_http_internal_redirect_main_conf_t)); 699 | if (imcf == NULL) { 700 | return NULL; 701 | } 702 | 703 | /* 704 | * set by ngx_pcalloc(): 705 | * imcf->required = 0; 706 | */ 707 | 708 | imcf->postponed = NGX_CONF_UNSET; 709 | imcf->phase = NGX_CONF_UNSET_UINT; 710 | 711 | return imcf; 712 | } 713 | 714 | 715 | static char * 716 | ngx_http_internal_redirect_init_main_conf(ngx_conf_t *cf, void *conf) 717 | { 718 | ngx_http_internal_redirect_main_conf_t *imcf = conf; 719 | 720 | if (imcf->postponed == NGX_CONF_UNSET) { 721 | imcf->postponed = 0; 722 | } 723 | 724 | if (imcf->phase == NGX_CONF_UNSET_UINT) { 725 | imcf->phase = NGX_HTTP_REWRITE_PHASE; 726 | } 727 | 728 | return NGX_CONF_OK; 729 | } 730 | 731 | 732 | static void * 733 | ngx_http_internal_redirect_create_loc_conf(ngx_conf_t *cf) 734 | { 735 | ngx_http_internal_redirect_loc_conf_t *conf; 736 | 737 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_internal_redirect_loc_conf_t)); 738 | if (conf == NULL) { 739 | return NULL; 740 | } 741 | 742 | /* 743 | * set by ngx_pcalloc(): 744 | * conf->redirects = NULL; 745 | */ 746 | 747 | return conf; 748 | } 749 | 750 | 751 | static char * 752 | ngx_http_internal_redirect_merge_loc_conf(ngx_conf_t *cf, void *parent, 753 | void *child) 754 | { 755 | ngx_http_internal_redirect_loc_conf_t *prev = parent; 756 | ngx_http_internal_redirect_loc_conf_t *conf = child; 757 | 758 | if (conf->redirects == NULL && prev->redirects) { 759 | conf->redirects = prev->redirects; 760 | } 761 | 762 | return NGX_CONF_OK; 763 | } 764 | --------------------------------------------------------------------------------