mirror of
https://github.com/nginx/nginx.git
synced 2024-12-15 13:28:59 +08:00
08e63d46de
*) Feature: the "error_page" directive supports the variables. *) Change: now the procfs interface instead of sysctl is used on Linux. *) Change: now the "Content-Type" header line is inherited from first response when the "X-Accel-Redirect" was used. *) Bugfix: the "error_page" directive did not redirect the 413 error. *) Bugfix: the trailing "?" did not remove old arguments if no new arguments were added to a rewritten URI. *) Bugfix: nginx could not run on 64-bit FreeBSD 7.0-CURRENT.
1202 lines
29 KiB
C
1202 lines
29 KiB
C
|
|
/*
|
|
* Copyright (C) Igor Sysoev
|
|
*/
|
|
|
|
|
|
#include <ngx_config.h>
|
|
#include <ngx_core.h>
|
|
#include <ngx_http.h>
|
|
|
|
|
|
#define ngx_http_script_exit (u_char *) &ngx_http_script_exit_code
|
|
|
|
static uintptr_t ngx_http_script_exit_code = (uintptr_t) NULL;
|
|
|
|
|
|
ngx_uint_t
|
|
ngx_http_script_variables_count(ngx_str_t *value)
|
|
{
|
|
ngx_uint_t i, n;
|
|
|
|
for (n = 0, i = 0; i < value->len; i++) {
|
|
if (value->data[i] == '$') {
|
|
n++;
|
|
}
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
|
|
ngx_int_t
|
|
ngx_http_script_compile(ngx_http_script_compile_t *sc)
|
|
{
|
|
u_char ch;
|
|
size_t size;
|
|
ngx_int_t index, *p;
|
|
ngx_str_t name;
|
|
uintptr_t *code;
|
|
ngx_uint_t i, n, bracket;
|
|
ngx_http_script_var_code_t *var_code;
|
|
ngx_http_script_copy_code_t *copy;
|
|
ngx_http_script_copy_capture_code_t *copy_capture;
|
|
|
|
if (sc->flushes && *sc->flushes == NULL) {
|
|
n = sc->variables ? sc->variables : 1;
|
|
*sc->flushes = ngx_array_create(sc->cf->pool, n, sizeof(ngx_uint_t));
|
|
if (*sc->flushes == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
}
|
|
|
|
|
|
if (*sc->lengths == NULL) {
|
|
n = sc->variables * (2 * sizeof(ngx_http_script_copy_code_t)
|
|
+ sizeof(ngx_http_script_var_code_t))
|
|
+ sizeof(uintptr_t);
|
|
|
|
*sc->lengths = ngx_array_create(sc->cf->pool, n, 1);
|
|
if (*sc->lengths == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
}
|
|
|
|
|
|
if (*sc->values == NULL) {
|
|
n = (sc->variables * (2 * sizeof(ngx_http_script_copy_code_t)
|
|
+ sizeof(ngx_http_script_var_code_t))
|
|
+ sizeof(uintptr_t)
|
|
+ sc->source->len
|
|
+ sizeof(uintptr_t) - 1)
|
|
& ~(sizeof(uintptr_t) - 1);
|
|
|
|
*sc->values = ngx_array_create(sc->cf->pool, n, 1);
|
|
if (*sc->values == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
}
|
|
|
|
sc->variables = 0;
|
|
|
|
for (i = 0; i < sc->source->len; /* void */ ) {
|
|
|
|
name.len = 0;
|
|
|
|
if (sc->source->data[i] == '$') {
|
|
|
|
if (++i == sc->source->len) {
|
|
goto invalid_variable;
|
|
}
|
|
|
|
if (sc->source->data[i] >= '1' && sc->source->data[i] <= '9') {
|
|
|
|
n = sc->source->data[i] - '0';
|
|
|
|
if (sc->captures_mask & (1 << n)) {
|
|
sc->dup_capture = 1;
|
|
}
|
|
|
|
sc->captures_mask |= 1 << n;
|
|
|
|
copy_capture = ngx_http_script_add_code(*sc->lengths,
|
|
sizeof(ngx_http_script_copy_capture_code_t),
|
|
NULL);
|
|
if (copy_capture == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
copy_capture->code = (ngx_http_script_code_pt)
|
|
ngx_http_script_copy_capture_len_code;
|
|
copy_capture->n = 2 * n;
|
|
|
|
|
|
copy_capture = ngx_http_script_add_code(*sc->values,
|
|
sizeof(ngx_http_script_copy_capture_code_t),
|
|
&sc->main);
|
|
if (copy_capture == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
copy_capture->code = ngx_http_script_copy_capture_code;
|
|
copy_capture->n = 2 * n;
|
|
|
|
if (sc->ncaptures < n) {
|
|
sc->ncaptures = n;
|
|
}
|
|
|
|
i++;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (sc->source->data[i] == '{') {
|
|
bracket = 1;
|
|
|
|
if (++i == sc->source->len) {
|
|
goto invalid_variable;
|
|
}
|
|
|
|
name.data = &sc->source->data[i];
|
|
|
|
} else {
|
|
bracket = 0;
|
|
name.data = &sc->source->data[i];
|
|
}
|
|
|
|
for ( /* void */ ; i < sc->source->len; i++, name.len++) {
|
|
ch = sc->source->data[i];
|
|
|
|
if (ch == '}' && bracket) {
|
|
i++;
|
|
bracket = 0;
|
|
break;
|
|
}
|
|
|
|
if ((ch >= 'A' && ch <= 'Z')
|
|
|| (ch >= 'a' && ch <= 'z')
|
|
|| (ch >= '0' && ch <= '9')
|
|
|| ch == '_')
|
|
{
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (bracket) {
|
|
ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0,
|
|
"the closing bracket in \"%V\" "
|
|
"variable is missing", &name);
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
if (name.len == 0) {
|
|
goto invalid_variable;
|
|
}
|
|
|
|
sc->variables++;
|
|
|
|
index = ngx_http_get_variable_index(sc->cf, &name);
|
|
|
|
if (index == NGX_ERROR) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
if (sc->flushes) {
|
|
p = ngx_array_push(*sc->flushes);
|
|
if (p == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
*p = index;
|
|
}
|
|
|
|
var_code = ngx_http_script_add_code(*sc->lengths,
|
|
sizeof(ngx_http_script_var_code_t),
|
|
NULL);
|
|
if (var_code == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
var_code->code = (ngx_http_script_code_pt)
|
|
ngx_http_script_copy_var_len_code;
|
|
var_code->index = (uintptr_t) index;
|
|
|
|
|
|
var_code = ngx_http_script_add_code(*sc->values,
|
|
sizeof(ngx_http_script_var_code_t),
|
|
&sc->main);
|
|
if (var_code == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
var_code->code = ngx_http_script_copy_var_code;
|
|
var_code->index = (uintptr_t) index;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (sc->source->data[i] == '?' && sc->compile_args) {
|
|
sc->args = 1;
|
|
sc->compile_args = 0;
|
|
|
|
code = ngx_http_script_add_code(*sc->values, sizeof(uintptr_t),
|
|
&sc->main);
|
|
if (code == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
*code = (uintptr_t) ngx_http_script_start_args_code;
|
|
|
|
i++;
|
|
|
|
continue;
|
|
}
|
|
|
|
name.data = &sc->source->data[i];
|
|
|
|
while (i < sc->source->len
|
|
&& sc->source->data[i] != '$'
|
|
&& !(sc->source->data[i] == '?' && sc->compile_args))
|
|
{
|
|
i++;
|
|
name.len++;
|
|
}
|
|
|
|
sc->size += name.len;
|
|
|
|
copy = ngx_http_script_add_code(*sc->lengths,
|
|
sizeof(ngx_http_script_copy_code_t),
|
|
NULL);
|
|
if (copy == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
|
|
copy->len = name.len;
|
|
|
|
size = (sizeof(ngx_http_script_copy_code_t) + name.len
|
|
+ sizeof(uintptr_t) - 1)
|
|
& ~(sizeof(uintptr_t) - 1);
|
|
|
|
copy = ngx_http_script_add_code(*sc->values, size, &sc->main);
|
|
if (copy == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
copy->code = ngx_http_script_copy_code;
|
|
copy->len = name.len;
|
|
|
|
ngx_memcpy((u_char *) copy + sizeof(ngx_http_script_copy_code_t),
|
|
name.data, name.len);
|
|
}
|
|
|
|
if (sc->complete_lengths) {
|
|
code = ngx_http_script_add_code(*sc->lengths, sizeof(uintptr_t), NULL);
|
|
if (code == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
*code = (uintptr_t) NULL;
|
|
}
|
|
|
|
if (sc->complete_values) {
|
|
code = ngx_http_script_add_code(*sc->values, sizeof(uintptr_t),
|
|
&sc->main);
|
|
if (code == NULL) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
*code = (uintptr_t) NULL;
|
|
}
|
|
|
|
return NGX_OK;
|
|
|
|
invalid_variable:
|
|
|
|
ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0, "invalid variable name");
|
|
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
|
|
u_char *
|
|
ngx_http_script_run(ngx_http_request_t *r, ngx_str_t *value,
|
|
void *code_lengths, size_t len, void *code_values)
|
|
{
|
|
ngx_http_script_code_pt code;
|
|
ngx_http_script_len_code_pt lcode;
|
|
ngx_http_script_engine_t e;
|
|
|
|
ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
|
|
|
|
e.ip = code_lengths;
|
|
e.request = r;
|
|
e.flushed = 1;
|
|
|
|
while (*(uintptr_t *) e.ip) {
|
|
lcode = *(ngx_http_script_len_code_pt *) e.ip;
|
|
len += lcode(&e);
|
|
}
|
|
|
|
|
|
value->len = len;
|
|
value->data = ngx_palloc(r->pool, len);
|
|
if (value->data == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
e.ip = code_values;
|
|
e.pos = value->data;
|
|
|
|
while (*(uintptr_t *) e.ip) {
|
|
code = *(ngx_http_script_code_pt *) e.ip;
|
|
code((ngx_http_script_engine_t *) &e);
|
|
}
|
|
|
|
return e.pos;
|
|
}
|
|
|
|
|
|
void
|
|
ngx_http_script_flush_no_cachable_variables(ngx_http_request_t *r,
|
|
ngx_array_t *indices)
|
|
{
|
|
ngx_uint_t n, *index;
|
|
|
|
if (indices) {
|
|
index = indices->elts;
|
|
for (n = 0; n < indices->nelts; n++) {
|
|
if (r->variables[index[n]].no_cachable) {
|
|
r->variables[index[n]].valid = 0;
|
|
r->variables[index[n]].not_found = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void *
|
|
ngx_http_script_start_code(ngx_pool_t *pool, ngx_array_t **codes, size_t size)
|
|
{
|
|
if (*codes == NULL) {
|
|
*codes = ngx_array_create(pool, 256, 1);
|
|
if (*codes == NULL) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return ngx_array_push_n(*codes, size);
|
|
}
|
|
|
|
|
|
void *
|
|
ngx_http_script_add_code(ngx_array_t *codes, size_t size, void *code)
|
|
{
|
|
u_char *elts, **p;
|
|
void *new;
|
|
|
|
elts = codes->elts;
|
|
|
|
new = ngx_array_push_n(codes, size);
|
|
if (new == NULL) {
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
if (code) {
|
|
if (elts != codes->elts) {
|
|
p = code;
|
|
*p += (u_char *) codes->elts - elts;
|
|
}
|
|
}
|
|
|
|
return new;
|
|
}
|
|
|
|
|
|
size_t
|
|
ngx_http_script_copy_len_code(ngx_http_script_engine_t *e)
|
|
{
|
|
ngx_http_script_copy_code_t *code;
|
|
|
|
code = (ngx_http_script_copy_code_t *) e->ip;
|
|
|
|
e->ip += sizeof(ngx_http_script_copy_code_t);
|
|
|
|
return code->len;
|
|
}
|
|
|
|
|
|
void
|
|
ngx_http_script_copy_code(ngx_http_script_engine_t *e)
|
|
{
|
|
ngx_http_script_copy_code_t *code;
|
|
|
|
code = (ngx_http_script_copy_code_t *) e->ip;
|
|
|
|
if (!e->skip) {
|
|
e->pos = ngx_copy(e->pos, e->ip + sizeof(ngx_http_script_copy_code_t),
|
|
code->len);
|
|
}
|
|
|
|
e->ip += sizeof(ngx_http_script_copy_code_t)
|
|
+ ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1));
|
|
|
|
if (e->log) {
|
|
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
|
|
"http script copy: \"%V\"", &e->buf);
|
|
}
|
|
}
|
|
|
|
|
|
size_t
|
|
ngx_http_script_copy_var_len_code(ngx_http_script_engine_t *e)
|
|
{
|
|
ngx_http_variable_value_t *value;
|
|
ngx_http_script_var_code_t *code;
|
|
|
|
code = (ngx_http_script_var_code_t *) e->ip;
|
|
|
|
e->ip += sizeof(ngx_http_script_var_code_t);
|
|
|
|
if (e->flushed) {
|
|
value = ngx_http_get_indexed_variable(e->request, code->index);
|
|
|
|
} else {
|
|
value = ngx_http_get_flushed_variable(e->request, code->index);
|
|
}
|
|
|
|
if (value && !value->not_found) {
|
|
return value->len;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void
|
|
ngx_http_script_copy_var_code(ngx_http_script_engine_t *e)
|
|
{
|
|
ngx_http_variable_value_t *value;
|
|
ngx_http_script_var_code_t *code;
|
|
|
|
code = (ngx_http_script_var_code_t *) e->ip;
|
|
|
|
e->ip += sizeof(ngx_http_script_var_code_t);
|
|
|
|
if (!e->skip) {
|
|
|
|
if (e->flushed) {
|
|
value = ngx_http_get_indexed_variable(e->request, code->index);
|
|
|
|
} else {
|
|
value = ngx_http_get_flushed_variable(e->request, code->index);
|
|
}
|
|
|
|
if (value && !value->not_found) {
|
|
e->pos = ngx_copy(e->pos, value->data, value->len);
|
|
|
|
if (e->log) {
|
|
ngx_log_debug1(NGX_LOG_DEBUG_HTTP,
|
|
e->request->connection->log, 0,
|
|
"http script var: \"%V\"", &e->buf);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
size_t
|
|
ngx_http_script_copy_capture_len_code(ngx_http_script_engine_t *e)
|
|
{
|
|
ngx_http_script_copy_capture_code_t *code;
|
|
|
|
code = (ngx_http_script_copy_capture_code_t *) e->ip;
|
|
|
|
e->ip += sizeof(ngx_http_script_copy_capture_code_t);
|
|
|
|
if (code->n < e->ncaptures) {
|
|
if ((e->args || e->quote)
|
|
&& (e->request->quoted_uri || e->request->plus_in_uri))
|
|
{
|
|
return e->captures[code->n + 1] - e->captures[code->n]
|
|
+ 2 * ngx_escape_uri(NULL,
|
|
&e->line.data[e->captures[code->n]],
|
|
e->captures[code->n + 1] - e->captures[code->n],
|
|
NGX_ESCAPE_ARGS);
|
|
} else {
|
|
return e->captures[code->n + 1] - e->captures[code->n];
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void
|
|
ngx_http_script_copy_capture_code(ngx_http_script_engine_t *e)
|
|
{
|
|
ngx_http_script_copy_capture_code_t *code;
|
|
|
|
code = (ngx_http_script_copy_capture_code_t *) e->ip;
|
|
|
|
e->ip += sizeof(ngx_http_script_copy_capture_code_t);
|
|
|
|
if (code->n < e->ncaptures) {
|
|
if ((e->args || e->quote)
|
|
&& (e->request->quoted_uri || e->request->plus_in_uri))
|
|
{
|
|
e->pos = (u_char *) ngx_escape_uri(e->pos,
|
|
&e->line.data[e->captures[code->n]],
|
|
e->captures[code->n + 1] - e->captures[code->n],
|
|
NGX_ESCAPE_ARGS);
|
|
} else {
|
|
e->pos = ngx_copy(e->pos,
|
|
&e->line.data[e->captures[code->n]],
|
|
e->captures[code->n + 1] - e->captures[code->n]);
|
|
}
|
|
}
|
|
|
|
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
|
|
"http script capture: \"%V\"", &e->buf);
|
|
}
|
|
|
|
|
|
void
|
|
ngx_http_script_start_args_code(ngx_http_script_engine_t *e)
|
|
{
|
|
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
|
|
"http script args");
|
|
|
|
e->args = e->pos;
|
|
e->ip += sizeof(uintptr_t);
|
|
}
|
|
|
|
|
|
|
|
#if (NGX_PCRE)
|
|
|
|
void
|
|
ngx_http_script_regex_start_code(ngx_http_script_engine_t *e)
|
|
{
|
|
size_t len;
|
|
ngx_int_t rc;
|
|
ngx_uint_t n;
|
|
ngx_http_request_t *r;
|
|
ngx_http_script_engine_t le;
|
|
ngx_http_script_len_code_pt lcode;
|
|
ngx_http_script_regex_code_t *code;
|
|
|
|
code = (ngx_http_script_regex_code_t *) e->ip;
|
|
|
|
r = e->request;
|
|
|
|
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
|
"http script regex: \"%V\"", &code->name);
|
|
|
|
if (code->uri) {
|
|
e->line = r->uri;
|
|
} else {
|
|
e->sp--;
|
|
e->line.len = e->sp->len;
|
|
e->line.data = e->sp->data;
|
|
}
|
|
|
|
rc = ngx_regex_exec(code->regex, &e->line, e->captures, code->ncaptures);
|
|
|
|
if (rc == NGX_REGEX_NO_MATCHED) {
|
|
if (e->log) {
|
|
ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
|
|
"\"%V\" does not match \"%V\"",
|
|
&code->name, &e->line);
|
|
}
|
|
|
|
e->ncaptures = 0;
|
|
|
|
if (code->test) {
|
|
if (code->negative_test) {
|
|
e->sp->len = 1;
|
|
e->sp->data = (u_char *) "1";
|
|
|
|
} else {
|
|
e->sp->len = 0;
|
|
e->sp->data = (u_char *) "";
|
|
}
|
|
|
|
e->sp++;
|
|
|
|
e->ip += sizeof(ngx_http_script_regex_code_t);
|
|
return;
|
|
}
|
|
|
|
e->ip += code->next;
|
|
return;
|
|
}
|
|
|
|
if (rc < 0) {
|
|
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
|
|
ngx_regex_exec_n " failed: %d on \"%V\" using \"%V\"",
|
|
rc, &e->line, &code->name);
|
|
|
|
e->ip = ngx_http_script_exit;
|
|
e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
return;
|
|
}
|
|
|
|
if (e->log) {
|
|
ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
|
|
"\"%V\" matches \"%V\"", &code->name, &e->line);
|
|
}
|
|
|
|
e->ncaptures = code->ncaptures;
|
|
|
|
if (code->test) {
|
|
if (code->negative_test) {
|
|
e->sp->len = 0;
|
|
e->sp->data = (u_char *) "";
|
|
|
|
} else {
|
|
e->sp->len = 1;
|
|
e->sp->data = (u_char *) "1";
|
|
}
|
|
|
|
e->sp++;
|
|
|
|
e->ip += sizeof(ngx_http_script_regex_code_t);
|
|
return;
|
|
}
|
|
|
|
if (code->status) {
|
|
e->status = code->status;
|
|
|
|
if (!code->redirect) {
|
|
e->ip = ngx_http_script_exit;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (code->uri) {
|
|
r->internal = 1;
|
|
r->valid_unparsed_uri = 0;
|
|
|
|
if (code->break_cycle) {
|
|
r->valid_location = 0;
|
|
r->uri_changed = 0;
|
|
|
|
} else {
|
|
r->uri_changed = 1;
|
|
}
|
|
}
|
|
|
|
if (code->lengths == NULL) {
|
|
e->buf.len = code->size;
|
|
|
|
if (code->uri) {
|
|
if (rc && (r->quoted_uri || r->plus_in_uri)) {
|
|
e->buf.len += 2 * ngx_escape_uri(NULL, r->uri.data, r->uri.len,
|
|
NGX_ESCAPE_ARGS);
|
|
}
|
|
}
|
|
|
|
for (n = 1; n < (ngx_uint_t) rc; n++) {
|
|
e->buf.len += e->captures[2 * n + 1] - e->captures[2 * n];
|
|
}
|
|
|
|
} else {
|
|
ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
|
|
|
|
le.ip = code->lengths->elts;
|
|
le.request = r;
|
|
le.captures = e->captures;
|
|
le.ncaptures = e->ncaptures;
|
|
|
|
len = 1; /* reserve 1 byte for possible "?" */
|
|
|
|
while (*(uintptr_t *) le.ip) {
|
|
lcode = *(ngx_http_script_len_code_pt *) le.ip;
|
|
len += lcode(&le);
|
|
}
|
|
|
|
e->buf.len = len;
|
|
}
|
|
|
|
if (code->add_args && r->args.len) {
|
|
e->buf.len += r->args.len + 1;
|
|
}
|
|
|
|
e->buf.data = ngx_palloc(r->pool, e->buf.len);
|
|
if (e->buf.data == NULL) {
|
|
e->ip = ngx_http_script_exit;
|
|
e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
return;
|
|
}
|
|
|
|
e->quote = code->redirect;
|
|
|
|
e->pos = e->buf.data;
|
|
|
|
e->ip += sizeof(ngx_http_script_regex_code_t);
|
|
}
|
|
|
|
|
|
void
|
|
ngx_http_script_regex_end_code(ngx_http_script_engine_t *e)
|
|
{
|
|
u_char *dst, *src;
|
|
ngx_http_request_t *r;
|
|
ngx_http_script_regex_end_code_t *code;
|
|
|
|
code = (ngx_http_script_regex_end_code_t *) e->ip;
|
|
|
|
r = e->request;
|
|
|
|
e->quote = 0;
|
|
|
|
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
|
"http script regex end");
|
|
|
|
if (code->redirect) {
|
|
|
|
dst = e->buf.data;
|
|
src = e->buf.data;
|
|
|
|
ngx_unescape_uri(&dst, &src, e->pos - e->buf.data, NGX_UNESCAPE_URI);
|
|
|
|
if (src < e->pos) {
|
|
dst = ngx_copy(dst, src, e->pos - src);
|
|
}
|
|
|
|
e->pos = dst;
|
|
|
|
if (code->add_args && r->args.len) {
|
|
*e->pos++ = (u_char) (code->args ? '&' : '?');
|
|
e->pos = ngx_copy(e->pos, r->args.data, r->args.len);
|
|
}
|
|
|
|
e->buf.len = e->pos - e->buf.data;
|
|
|
|
if (e->log) {
|
|
ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
|
|
"rewritten redirect: \"%V\"", &e->buf);
|
|
}
|
|
|
|
r->headers_out.location = ngx_list_push(&r->headers_out.headers);
|
|
if (r->headers_out.location == NULL) {
|
|
e->ip = ngx_http_script_exit;
|
|
e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
return;
|
|
}
|
|
|
|
r->headers_out.location->hash = 1;
|
|
r->headers_out.location->key.len = sizeof("Location") - 1;
|
|
r->headers_out.location->key.data = (u_char *) "Location";
|
|
r->headers_out.location->value = e->buf;
|
|
|
|
e->ip += sizeof(ngx_http_script_regex_end_code_t);
|
|
return;
|
|
}
|
|
|
|
if (e->args) {
|
|
e->buf.len = e->args - e->buf.data;
|
|
|
|
if (code->add_args && r->args.len) {
|
|
*e->pos++ = '&';
|
|
e->pos = ngx_copy(e->pos, r->args.data, r->args.len);
|
|
}
|
|
|
|
r->args.len = e->pos - e->args;
|
|
r->args.data = e->args;
|
|
|
|
e->args = NULL;
|
|
|
|
} else {
|
|
e->buf.len = e->pos - e->buf.data;
|
|
|
|
if (!code->add_args) {
|
|
r->args.len = 0;
|
|
}
|
|
}
|
|
|
|
if (e->log) {
|
|
ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
|
|
"rewritten data: \"%V\", args: \"%V\"",
|
|
&e->buf, &r->args);
|
|
}
|
|
|
|
if (code->uri) {
|
|
r->uri = e->buf;
|
|
|
|
if (r->uri.len == 0) {
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"the rewritten URI has a zero length");
|
|
e->ip = ngx_http_script_exit;
|
|
e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
return;
|
|
}
|
|
|
|
if (ngx_http_set_exten(r) != NGX_OK) {
|
|
e->ip = ngx_http_script_exit;
|
|
e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
return;
|
|
}
|
|
}
|
|
|
|
e->ip += sizeof(ngx_http_script_regex_end_code_t);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
void
|
|
ngx_http_script_return_code(ngx_http_script_engine_t *e)
|
|
{
|
|
ngx_http_script_return_code_t *code;
|
|
|
|
code = (ngx_http_script_return_code_t *) e->ip;
|
|
|
|
e->status = code->status;
|
|
|
|
if (code->status == NGX_HTTP_NO_CONTENT) {
|
|
e->request->header_only = 1;
|
|
}
|
|
|
|
e->ip += sizeof(ngx_http_script_return_code_t) - sizeof(uintptr_t);
|
|
}
|
|
|
|
|
|
void
|
|
ngx_http_script_break_code(ngx_http_script_engine_t *e)
|
|
{
|
|
e->request->uri_changed = 0;
|
|
|
|
e->ip = ngx_http_script_exit;
|
|
}
|
|
|
|
|
|
void
|
|
ngx_http_script_if_code(ngx_http_script_engine_t *e)
|
|
{
|
|
ngx_http_script_if_code_t *code;
|
|
|
|
code = (ngx_http_script_if_code_t *) e->ip;
|
|
|
|
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
|
|
"http script if");
|
|
|
|
e->sp--;
|
|
|
|
if (e->sp->len && e->sp->data[0] != '0') {
|
|
if (code->loc_conf) {
|
|
e->request->loc_conf = code->loc_conf;
|
|
ngx_http_update_location_config(e->request);
|
|
}
|
|
|
|
e->ip += sizeof(ngx_http_script_if_code_t);
|
|
return;
|
|
}
|
|
|
|
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
|
|
"http script if: false");
|
|
|
|
e->ip += code->next;
|
|
}
|
|
|
|
|
|
void
|
|
ngx_http_script_equal_code(ngx_http_script_engine_t *e)
|
|
{
|
|
ngx_http_variable_value_t *val, *res;
|
|
|
|
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
|
|
"http script equal");
|
|
|
|
e->sp--;
|
|
val = e->sp;
|
|
res = e->sp - 1;
|
|
|
|
e->ip += sizeof(uintptr_t);
|
|
|
|
if (val->len == res->len && ngx_strncmp(val->data, res->data, res->len)
|
|
== 0)
|
|
{
|
|
*res = ngx_http_variable_true_value;
|
|
return;
|
|
}
|
|
|
|
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
|
|
"http script equal: no");
|
|
|
|
*res = ngx_http_variable_null_value;
|
|
}
|
|
|
|
|
|
void
|
|
ngx_http_script_not_equal_code(ngx_http_script_engine_t *e)
|
|
{
|
|
ngx_http_variable_value_t *val, *res;
|
|
|
|
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
|
|
"http script not equal");
|
|
|
|
e->sp--;
|
|
val = e->sp;
|
|
res = e->sp - 1;
|
|
|
|
e->ip += sizeof(uintptr_t);
|
|
|
|
if (val->len == res->len && ngx_strncmp(val->data, res->data, res->len)
|
|
== 0)
|
|
{
|
|
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
|
|
"http script not equal: no");
|
|
|
|
*res = ngx_http_variable_null_value;
|
|
return;
|
|
}
|
|
|
|
*res = ngx_http_variable_true_value;
|
|
}
|
|
|
|
|
|
void
|
|
ngx_http_script_file_code(ngx_http_script_engine_t *e)
|
|
{
|
|
ngx_err_t err;
|
|
ngx_file_info_t fi;
|
|
ngx_http_variable_value_t *value;
|
|
ngx_http_script_file_code_t *code;
|
|
|
|
value = e->sp - 1;
|
|
|
|
code = (ngx_http_script_file_code_t *) e->ip;
|
|
e->ip += sizeof(ngx_http_script_file_code_t);
|
|
|
|
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
|
|
"http script file op %p", code->op);
|
|
|
|
if (ngx_file_info(value->data, &fi) == -1) {
|
|
err = ngx_errno;
|
|
|
|
if (err != NGX_ENOENT && err != NGX_ENOTDIR) {
|
|
ngx_log_error(NGX_LOG_CRIT, e->request->connection->log, err,
|
|
ngx_file_info_n " \"%s\" failed", value->data);
|
|
}
|
|
|
|
switch (code->op) {
|
|
|
|
case ngx_http_script_file_plain:
|
|
case ngx_http_script_file_dir:
|
|
case ngx_http_script_file_exists:
|
|
case ngx_http_script_file_exec:
|
|
goto false;
|
|
|
|
case ngx_http_script_file_not_plain:
|
|
case ngx_http_script_file_not_dir:
|
|
case ngx_http_script_file_not_exists:
|
|
case ngx_http_script_file_not_exec:
|
|
goto true;
|
|
}
|
|
|
|
goto false;
|
|
}
|
|
|
|
switch (code->op) {
|
|
case ngx_http_script_file_plain:
|
|
if (ngx_is_file(&fi)) {
|
|
goto true;
|
|
}
|
|
goto false;
|
|
|
|
case ngx_http_script_file_not_plain:
|
|
if (ngx_is_file(&fi)) {
|
|
goto false;
|
|
}
|
|
goto true;
|
|
|
|
case ngx_http_script_file_dir:
|
|
if (ngx_is_dir(&fi)) {
|
|
goto true;
|
|
}
|
|
goto false;
|
|
|
|
case ngx_http_script_file_not_dir:
|
|
if (ngx_is_dir(&fi)) {
|
|
goto false;
|
|
}
|
|
goto true;
|
|
|
|
case ngx_http_script_file_exists:
|
|
if (ngx_is_file(&fi) || ngx_is_dir(&fi) || ngx_is_link(&fi)) {
|
|
goto true;
|
|
}
|
|
goto false;
|
|
|
|
case ngx_http_script_file_not_exists:
|
|
if (ngx_is_file(&fi) || ngx_is_dir(&fi) || ngx_is_link(&fi)) {
|
|
goto false;
|
|
}
|
|
goto true;
|
|
|
|
#if (NGX_WIN32)
|
|
|
|
case ngx_http_script_file_exec:
|
|
goto false;
|
|
|
|
case ngx_http_script_file_not_exec:
|
|
goto true;
|
|
|
|
#else
|
|
|
|
case ngx_http_script_file_exec:
|
|
if (ngx_is_exec(&fi)) {
|
|
goto true;
|
|
}
|
|
goto false;
|
|
|
|
case ngx_http_script_file_not_exec:
|
|
if (ngx_is_exec(&fi)) {
|
|
goto false;
|
|
}
|
|
goto true;
|
|
|
|
#endif
|
|
}
|
|
|
|
false:
|
|
|
|
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
|
|
"http script file op false");
|
|
|
|
*value = ngx_http_variable_null_value;
|
|
return;
|
|
|
|
true:
|
|
|
|
*value = ngx_http_variable_true_value;
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
ngx_http_script_complex_value_code(ngx_http_script_engine_t *e)
|
|
{
|
|
size_t len;
|
|
ngx_http_script_engine_t le;
|
|
ngx_http_script_len_code_pt lcode;
|
|
ngx_http_script_complex_value_code_t *code;
|
|
|
|
code = (ngx_http_script_complex_value_code_t *) e->ip;
|
|
|
|
e->ip += sizeof(ngx_http_script_complex_value_code_t);
|
|
|
|
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
|
|
"http script complex value");
|
|
|
|
ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
|
|
|
|
le.ip = code->lengths->elts;
|
|
le.line = e->line;
|
|
le.request = e->request;
|
|
le.captures = e->captures;
|
|
le.ncaptures = e->ncaptures;
|
|
le.quote = e->quote;
|
|
|
|
for (len = 0; *(uintptr_t *) le.ip; len += lcode(&le)) {
|
|
lcode = *(ngx_http_script_len_code_pt *) le.ip;
|
|
}
|
|
|
|
e->buf.len = len;
|
|
e->buf.data = ngx_palloc(e->request->pool, len);
|
|
if (e->buf.data == NULL) {
|
|
e->ip = ngx_http_script_exit;
|
|
e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
return;
|
|
}
|
|
|
|
e->pos = e->buf.data;
|
|
|
|
e->sp->len = e->buf.len;
|
|
e->sp->data = e->buf.data;
|
|
e->sp++;
|
|
}
|
|
|
|
|
|
void
|
|
ngx_http_script_value_code(ngx_http_script_engine_t *e)
|
|
{
|
|
ngx_http_script_value_code_t *code;
|
|
|
|
code = (ngx_http_script_value_code_t *) e->ip;
|
|
|
|
e->ip += sizeof(ngx_http_script_value_code_t);
|
|
|
|
e->sp->len = code->text_len;
|
|
e->sp->data = (u_char *) code->text_data;
|
|
|
|
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
|
|
"http script value: \"%V\"", e->sp);
|
|
|
|
e->sp++;
|
|
}
|
|
|
|
|
|
void
|
|
ngx_http_script_set_var_code(ngx_http_script_engine_t *e)
|
|
{
|
|
ngx_http_request_t *r;
|
|
ngx_http_script_var_code_t *code;
|
|
|
|
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
|
|
"http script set var");
|
|
|
|
code = (ngx_http_script_var_code_t *) e->ip;
|
|
|
|
e->ip += sizeof(ngx_http_script_var_code_t);
|
|
|
|
r = e->request;
|
|
|
|
e->sp--;
|
|
|
|
r->variables[code->index].len = e->sp->len;
|
|
r->variables[code->index].valid = 1;
|
|
r->variables[code->index].no_cachable = 0;
|
|
r->variables[code->index].not_found = 0;
|
|
r->variables[code->index].data = e->sp->data;
|
|
}
|
|
|
|
|
|
void
|
|
ngx_http_script_var_set_handler_code(ngx_http_script_engine_t *e)
|
|
{
|
|
ngx_http_script_var_handler_code_t *code;
|
|
|
|
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
|
|
"http script set var handler");
|
|
|
|
code = (ngx_http_script_var_handler_code_t *) e->ip;
|
|
|
|
e->ip += sizeof(ngx_http_script_var_handler_code_t);
|
|
|
|
e->sp--;
|
|
|
|
code->handler(e->request, e->sp, code->data);
|
|
}
|
|
|
|
|
|
void
|
|
ngx_http_script_var_code(ngx_http_script_engine_t *e)
|
|
{
|
|
ngx_http_variable_value_t *value;
|
|
ngx_http_script_var_code_t *code;
|
|
|
|
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
|
|
"http script var");
|
|
|
|
code = (ngx_http_script_var_code_t *) e->ip;
|
|
|
|
e->ip += sizeof(ngx_http_script_var_code_t);
|
|
|
|
value = ngx_http_get_flushed_variable(e->request, code->index);
|
|
|
|
if (value && !value->not_found) {
|
|
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
|
|
"http script var: \"%V\"", value);
|
|
|
|
*e->sp = *value;
|
|
e->sp++;
|
|
|
|
return;
|
|
}
|
|
|
|
*e->sp = ngx_http_variable_null_value;
|
|
e->sp++;
|
|
}
|
|
|
|
|
|
void
|
|
ngx_http_script_nop_code(ngx_http_script_engine_t *e)
|
|
{
|
|
e->ip += sizeof(uintptr_t);
|
|
}
|