blob: 228e3a80d85160d6060a407b5ec79ab6d2c3bf74 [file] [log] [blame] [raw]
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_script.h"
static void * ngx_http_lua_script_add_code(ngx_array_t *codes, size_t size);
static size_t ngx_http_lua_script_copy_len_code(
ngx_http_lua_script_engine_t *e);
static void ngx_http_lua_script_copy_code(ngx_http_lua_script_engine_t *e);
static ngx_int_t ngx_http_lua_script_add_copy_code(
ngx_http_lua_script_compile_t *sc, ngx_str_t *value, ngx_uint_t last);
static ngx_int_t ngx_http_lua_script_compile(ngx_http_lua_script_compile_t *sc);
static ngx_int_t ngx_http_lua_script_add_capture_code(
ngx_http_lua_script_compile_t *sc, ngx_uint_t n);
static size_t ngx_http_lua_script_copy_capture_len_code(
ngx_http_lua_script_engine_t *e);
static void ngx_http_lua_script_copy_capture_code(
ngx_http_lua_script_engine_t *e);
static ngx_int_t ngx_http_lua_script_done(ngx_http_lua_script_compile_t *sc);
static ngx_int_t ngx_http_lua_script_init_arrays(
ngx_http_lua_script_compile_t *sc);
ngx_int_t
ngx_http_lua_compile_complex_value(ngx_http_lua_compile_complex_value_t *ccv)
{
ngx_str_t *v;
ngx_uint_t i, n, nv;
ngx_array_t lengths, values, *pl, *pv;
ngx_http_lua_script_compile_t sc;
v = ccv->value;
nv = 0;
for (i = 0; i < v->len; i++) {
if (v->data[i] == '$') {
nv++;
}
}
ccv->complex_value->value = *v;
ccv->complex_value->lengths = NULL;
ccv->complex_value->values = NULL;
if (nv == 0) {
return NGX_OK;
}
n = nv * (2 * sizeof(ngx_http_lua_script_copy_code_t)
+ sizeof(ngx_http_lua_script_capture_code_t))
+ sizeof(uintptr_t);
if (ngx_array_init(&lengths, ccv->pool, n, 1) != NGX_OK) {
return NGX_ERROR;
}
n = (nv * (2 * sizeof(ngx_http_lua_script_copy_code_t)
+ sizeof(ngx_http_lua_script_capture_code_t))
+ sizeof(uintptr_t)
+ sizeof(uintptr_t) - 1)
& ~(sizeof(uintptr_t) - 1);
if (ngx_array_init(&values, ccv->pool, n, 1) != NGX_OK) {
return NGX_ERROR;
}
pl = &lengths;
pv = &values;
ngx_memzero(&sc, sizeof(ngx_http_lua_script_compile_t));
sc.pool = ccv->pool;
sc.log = ccv->log;
sc.source = v;
sc.lengths = &pl;
sc.values = &pv;
sc.complete_lengths = 1;
sc.complete_values = 1;
if (ngx_http_lua_script_compile(&sc) != NGX_OK) {
ngx_array_destroy(&lengths);
ngx_array_destroy(&values);
return NGX_ERROR;
}
ccv->complex_value->lengths = lengths.elts;
ccv->complex_value->values = values.elts;
return NGX_OK;
}
ngx_int_t
ngx_http_lua_complex_value(ngx_http_request_t *r, ngx_str_t *subj,
size_t offset, ngx_int_t count, int *cap,
ngx_http_lua_complex_value_t *val, luaL_Buffer *luabuf)
{
size_t len;
u_char *p;
ngx_http_lua_script_code_pt code;
ngx_http_lua_script_len_code_pt lcode;
ngx_http_lua_script_engine_t e;
if (val->lengths == NULL) {
luaL_addlstring(luabuf, (char *) &subj->data[offset], cap[0] - offset);
luaL_addlstring(luabuf, (char *) val->value.data, val->value.len);
return NGX_OK;
}
ngx_memzero(&e, sizeof(ngx_http_lua_script_engine_t));
e.log = r->connection->log;
e.ncaptures = count * 2;
e.captures = cap;
e.captures_data = subj->data;
e.ip = val->lengths;
len = 0;
while (*(uintptr_t *) e.ip) {
lcode = *(ngx_http_lua_script_len_code_pt *) e.ip;
len += lcode(&e);
}
p = ngx_pnalloc(r->pool, len);
if (p == NULL) {
return NGX_ERROR;
}
e.ip = val->values;
e.pos = p;
while (*(uintptr_t *) e.ip) {
code = *(ngx_http_lua_script_code_pt *) e.ip;
code((ngx_http_lua_script_engine_t *) &e);
}
luaL_addlstring(luabuf, (char *) &subj->data[offset], cap[0] - offset);
luaL_addlstring(luabuf, (char *) p, len);
ngx_pfree(r->pool, p);
return NGX_OK;
}
static ngx_int_t
ngx_http_lua_script_compile(ngx_http_lua_script_compile_t *sc)
{
u_char ch;
ngx_str_t name;
ngx_uint_t i, bracket;
unsigned num_var;
ngx_uint_t n = 0;
if (ngx_http_lua_script_init_arrays(sc) != NGX_OK) {
return NGX_ERROR;
}
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] == '$') {
name.data = &sc->source->data[i];
i++;
name.len++;
if (ngx_http_lua_script_add_copy_code(sc, &name,
(i == sc->source->len))
!= NGX_OK)
{
return NGX_ERROR;
}
continue;
}
if (sc->source->data[i] >= '0' && sc->source->data[i] <= '9') {
num_var = 1;
n = 0;
} else {
num_var = 0;
}
if (sc->source->data[i] == '{') {
bracket = 1;
if (++i == sc->source->len) {
goto invalid_variable;
}
if (sc->source->data[i] >= '0' && sc->source->data[i] <= '9') {
num_var = 1;
n = 0;
}
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 (num_var) {
if (ch >= '0' && ch <= '9') {
n = n * 10 + (ch - '0');
continue;
}
break;
}
/* not a number variable like $1, $2, etc */
if ((ch >= 'A' && ch <= 'Z')
|| (ch >= 'a' && ch <= 'z')
|| (ch >= '0' && ch <= '9')
|| ch == '_')
{
continue;
}
break;
}
if (bracket) {
ngx_log_error(NGX_LOG_ERR, sc->log, 0,
"the closing bracket in \"%V\" "
"variable is missing", &name);
return NGX_ERROR;
}
if (name.len == 0) {
goto invalid_variable;
}
if (!num_var) {
ngx_log_error(NGX_LOG_ERR, sc->log, 0,
"attempt to use named capturing variable "
"\"%V\" (named captures not supported yet)",
&name);
return NGX_ERROR;
}
sc->variables++;
if (ngx_http_lua_script_add_capture_code(sc, n) != NGX_OK) {
return NGX_ERROR;
}
continue;
}
name.data = &sc->source->data[i];
while (i < sc->source->len) {
if (sc->source->data[i] == '$') {
break;
}
i++;
name.len++;
}
if (ngx_http_lua_script_add_copy_code(sc, &name, (i == sc->source->len))
!= NGX_OK)
{
return NGX_ERROR;
}
}
return ngx_http_lua_script_done(sc);
invalid_variable:
ngx_log_error(NGX_LOG_ERR, sc->log, 0,
"lua script: invalid capturing variable name found in \"%V\"",
sc->source);
return NGX_ERROR;
}
static ngx_int_t
ngx_http_lua_script_add_copy_code(ngx_http_lua_script_compile_t *sc,
ngx_str_t *value, ngx_uint_t last)
{
size_t size, len;
ngx_http_lua_script_copy_code_t *code;
len = value->len;
code = ngx_http_lua_script_add_code(*sc->lengths,
sizeof(ngx_http_lua_script_copy_code_t));
if (code == NULL) {
return NGX_ERROR;
}
code->code = (ngx_http_lua_script_code_pt)
ngx_http_lua_script_copy_len_code;
code->len = len;
size = (sizeof(ngx_http_lua_script_copy_code_t) + len +
sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1);
code = ngx_http_lua_script_add_code(*sc->values, size);
if (code == NULL) {
return NGX_ERROR;
}
code->code = ngx_http_lua_script_copy_code;
code->len = len;
ngx_memcpy((u_char *) code + sizeof(ngx_http_lua_script_copy_code_t),
value->data, value->len);
return NGX_OK;
}
static size_t
ngx_http_lua_script_copy_len_code(ngx_http_lua_script_engine_t *e)
{
ngx_http_lua_script_copy_code_t *code;
code = (ngx_http_lua_script_copy_code_t *) e->ip;
e->ip += sizeof(ngx_http_lua_script_copy_code_t);
return code->len;
}
static void
ngx_http_lua_script_copy_code(ngx_http_lua_script_engine_t *e)
{
u_char *p;
ngx_http_lua_script_copy_code_t *code;
code = (ngx_http_lua_script_copy_code_t *) e->ip;
p = e->pos;
if (!e->skip) {
e->pos = ngx_copy(p, e->ip + sizeof(ngx_http_lua_script_copy_code_t),
code->len);
}
e->ip += sizeof(ngx_http_lua_script_copy_code_t)
+ ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1));
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->log, 0,
"lua script copy: \"%*s\"", e->pos - p, p);
}
static ngx_int_t
ngx_http_lua_script_add_capture_code(ngx_http_lua_script_compile_t *sc,
ngx_uint_t n)
{
ngx_http_lua_script_capture_code_t *code;
code = ngx_http_lua_script_add_code(*sc->lengths,
sizeof(ngx_http_lua_script_capture_code_t));
if (code == NULL) {
return NGX_ERROR;
}
code->code = (ngx_http_lua_script_code_pt)
ngx_http_lua_script_copy_capture_len_code;
code->n = 2 * n;
code = ngx_http_lua_script_add_code(*sc->values,
sizeof(ngx_http_lua_script_capture_code_t));
if (code == NULL) {
return NGX_ERROR;
}
code->code = ngx_http_lua_script_copy_capture_code;
code->n = 2 * n;
return NGX_OK;
}
static size_t
ngx_http_lua_script_copy_capture_len_code(ngx_http_lua_script_engine_t *e)
{
int *cap;
ngx_uint_t n;
ngx_http_lua_script_capture_code_t *code;
code = (ngx_http_lua_script_capture_code_t *) e->ip;
e->ip += sizeof(ngx_http_lua_script_capture_code_t);
n = code->n;
if (n < e->ncaptures) {
cap = e->captures;
return cap[n + 1] - cap[n];
}
return 0;
}
static void
ngx_http_lua_script_copy_capture_code(ngx_http_lua_script_engine_t *e)
{
int *cap;
u_char *p, *pos;
ngx_uint_t n;
ngx_http_lua_script_capture_code_t *code;
code = (ngx_http_lua_script_capture_code_t *) e->ip;
e->ip += sizeof(ngx_http_lua_script_capture_code_t);
n = code->n;
pos = e->pos;
if (n < e->ncaptures) {
cap = e->captures;
p = e->captures_data;
e->pos = ngx_copy(pos, &p[cap[n]], cap[n + 1] - cap[n]);
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->log, 0,
"lua script capture: \"%*s\"", e->pos - pos, pos);
}
static ngx_int_t
ngx_http_lua_script_init_arrays(ngx_http_lua_script_compile_t *sc)
{
ngx_uint_t n;
if (*sc->lengths == NULL) {
n = sc->variables * (2 * sizeof(ngx_http_lua_script_copy_code_t)
+ sizeof(ngx_http_lua_script_capture_code_t))
+ sizeof(uintptr_t);
*sc->lengths = ngx_array_create(sc->pool, n, 1);
if (*sc->lengths == NULL) {
return NGX_ERROR;
}
}
if (*sc->values == NULL) {
n = (sc->variables * (2 * sizeof(ngx_http_lua_script_copy_code_t)
+ sizeof(ngx_http_lua_script_capture_code_t))
+ sizeof(uintptr_t)
+ sizeof(uintptr_t) - 1)
& ~(sizeof(uintptr_t) - 1);
*sc->values = ngx_array_create(sc->pool, n, 1);
if (*sc->values == NULL) {
return NGX_ERROR;
}
}
sc->variables = 0;
return NGX_OK;
}
static ngx_int_t
ngx_http_lua_script_done(ngx_http_lua_script_compile_t *sc)
{
uintptr_t *code;
if (sc->complete_lengths) {
code = ngx_http_lua_script_add_code(*sc->lengths, sizeof(uintptr_t));
if (code == NULL) {
return NGX_ERROR;
}
*code = (uintptr_t) NULL;
}
if (sc->complete_values) {
code = ngx_http_lua_script_add_code(*sc->values, sizeof(uintptr_t));
if (code == NULL) {
return NGX_ERROR;
}
*code = (uintptr_t) NULL;
}
return NGX_OK;
}
static void *
ngx_http_lua_script_add_code(ngx_array_t *codes, size_t size)
{
return ngx_array_push_n(codes, size);
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */