/* * 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: */