blob: 35e3d861678947d2645c81b8012806b32ed0dc36 [file] [log] [blame] [raw]
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_args.h"
#include "ngx_http_lua_util.h"
static int ngx_http_lua_ngx_req_set_uri_args(lua_State *L);
static int ngx_http_lua_ngx_req_get_uri_args(lua_State *L);
static int ngx_http_lua_ngx_req_get_post_args(lua_State *L);
static int
ngx_http_lua_ngx_req_set_uri_args(lua_State *L)
{
ngx_http_request_t *r;
ngx_str_t args;
const char *msg;
size_t len;
u_char *p;
if (lua_gettop(L) != 1) {
return luaL_error(L, "expecting 1 argument but seen %d",
lua_gettop(L));
}
r = ngx_http_lua_get_req(L);
if (r == NULL) {
return luaL_error(L, "no request object found");
}
ngx_http_lua_check_fake_request(L, r);
switch (lua_type(L, 1)) {
case LUA_TNUMBER:
case LUA_TSTRING:
p = (u_char *) lua_tolstring(L, 1, &len);
args.data = ngx_palloc(r->pool, len);
if (args.data == NULL) {
return luaL_error(L, "out of memory");
}
ngx_memcpy(args.data, p, len);
args.len = len;
break;
case LUA_TTABLE:
ngx_http_lua_process_args_option(r, L, 1, &args);
dd("args: %.*s", (int) args.len, args.data);
break;
default:
msg = lua_pushfstring(L, "string, number, or table expected, "
"but got %s", luaL_typename(L, 2));
return luaL_argerror(L, 1, msg);
}
dd("args: %.*s", (int) args.len, args.data);
r->args.data = args.data;
r->args.len = args.len;
r->valid_unparsed_uri = 0;
return 0;
}
static int
ngx_http_lua_ngx_req_get_uri_args(lua_State *L)
{
ngx_http_request_t *r;
u_char *buf;
u_char *last;
int retval;
int n;
int max;
n = lua_gettop(L);
if (n != 0 && n != 1) {
return luaL_error(L, "expecting 0 or 1 arguments but seen %d", n);
}
if (n == 1) {
max = luaL_checkinteger(L, 1);
lua_pop(L, 1);
} else {
max = NGX_HTTP_LUA_MAX_ARGS;
}
r = ngx_http_lua_get_req(L);
if (r == NULL) {
return luaL_error(L, "no request object found");
}
ngx_http_lua_check_fake_request(L, r);
lua_createtable(L, 0, 4);
/* we copy r->args over to buf to simplify
* unescaping query arg keys and values */
buf = ngx_palloc(r->pool, r->args.len);
if (buf == NULL) {
return luaL_error(L, "out of memory");
}
ngx_memcpy(buf, r->args.data, r->args.len);
last = buf + r->args.len;
retval = ngx_http_lua_parse_args(L, buf, last, max);
ngx_pfree(r->pool, buf);
return retval;
}
static int
ngx_http_lua_ngx_req_get_post_args(lua_State *L)
{
ngx_http_request_t *r;
u_char *buf;
int retval;
size_t len;
ngx_chain_t *cl;
u_char *p;
u_char *last;
int n;
int max;
n = lua_gettop(L);
if (n != 0 && n != 1) {
return luaL_error(L, "expecting 0 or 1 arguments but seen %d", n);
}
if (n == 1) {
max = luaL_checkinteger(L, 1);
lua_pop(L, 1);
} else {
max = NGX_HTTP_LUA_MAX_ARGS;
}
r = ngx_http_lua_get_req(L);
if (r == NULL) {
return luaL_error(L, "no request object found");
}
ngx_http_lua_check_fake_request(L, r);
if (r->discard_body) {
lua_createtable(L, 0, 0);
return 1;
}
if (r->request_body == NULL) {
return luaL_error(L, "no request body found; "
"maybe you should turn on lua_need_request_body?");
}
if (r->request_body->temp_file) {
return luaL_error(L, "requesty body in temp file not supported");
}
lua_createtable(L, 0, 4);
if (r->request_body->bufs == NULL) {
return 1;
}
/* we copy r->request_body->bufs over to buf to simplify
* unescaping query arg keys and values */
len = 0;
for (cl = r->request_body->bufs; cl; cl = cl->next) {
len += cl->buf->last - cl->buf->pos;
}
dd("post body length: %d", (int) len);
buf = ngx_palloc(r->pool, len);
if (buf == NULL) {
return luaL_error(L, "out of memory");
}
p = buf;
for (cl = r->request_body->bufs; cl; cl = cl->next) {
p = ngx_copy(p, cl->buf->pos, cl->buf->last - cl->buf->pos);
}
dd("post body: %.*s", (int) len, buf);
last = buf + len;
retval = ngx_http_lua_parse_args(L, buf, last, max);
ngx_pfree(r->pool, buf);
return retval;
}
int
ngx_http_lua_parse_args(lua_State *L, u_char *buf, u_char *last, int max)
{
u_char *p, *q;
u_char *src, *dst;
unsigned parsing_value;
size_t len;
int count = 0;
int top;
top = lua_gettop(L);
p = buf;
parsing_value = 0;
q = p;
while (p != last) {
if (*p == '=' && ! parsing_value) {
/* key data is between p and q */
src = q; dst = q;
ngx_http_lua_unescape_uri(&dst, &src, p - q,
NGX_UNESCAPE_URI_COMPONENT);
dd("pushing key %.*s", (int) (dst - q), q);
/* push the key */
lua_pushlstring(L, (char *) q, dst - q);
/* skip the current '=' char */
p++;
q = p;
parsing_value = 1;
} else if (*p == '&') {
/* reached the end of a key or a value, just save it */
src = q; dst = q;
ngx_http_lua_unescape_uri(&dst, &src, p - q,
NGX_UNESCAPE_URI_COMPONENT);
dd("pushing key or value %.*s", (int) (dst - q), q);
/* push the value or key */
lua_pushlstring(L, (char *) q, dst - q);
/* skip the current '&' char */
p++;
q = p;
if (parsing_value) {
/* end of the current pair's value */
parsing_value = 0;
} else {
/* the current parsing pair takes no value,
* just push the value "true" */
dd("pushing boolean true");
lua_pushboolean(L, 1);
}
(void) lua_tolstring(L, -2, &len);
if (len == 0) {
/* ignore empty string key pairs */
dd("popping key and value...");
lua_pop(L, 2);
} else {
dd("setting table...");
ngx_http_lua_set_multi_value_table(L, top);
}
if (max > 0 && ++count == max) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
"lua hit query args limit %d", max);
return 1;
}
} else {
p++;
}
}
if (p != q || parsing_value) {
src = q; dst = q;
ngx_http_lua_unescape_uri(&dst, &src, p - q,
NGX_UNESCAPE_URI_COMPONENT);
dd("pushing key or value %.*s", (int) (dst - q), q);
lua_pushlstring(L, (char *) q, dst - q);
if (!parsing_value) {
dd("pushing boolean true...");
lua_pushboolean(L, 1);
}
(void) lua_tolstring(L, -2, &len);
if (len == 0) {
/* ignore empty string key pairs */
dd("popping key and value...");
lua_pop(L, 2);
} else {
dd("setting table...");
ngx_http_lua_set_multi_value_table(L, top);
}
}
dd("gettop: %d", lua_gettop(L));
dd("type: %s", lua_typename(L, lua_type(L, 1)));
if (lua_gettop(L) != top) {
return luaL_error(L, "internal error: stack in bad state");
}
return 1;
}
void
ngx_http_lua_inject_req_args_api(lua_State *L)
{
lua_pushcfunction(L, ngx_http_lua_ngx_req_set_uri_args);
lua_setfield(L, -2, "set_uri_args");
lua_pushcfunction(L, ngx_http_lua_ngx_req_get_uri_args);
lua_setfield(L, -2, "get_uri_args");
lua_pushcfunction(L, ngx_http_lua_ngx_req_get_uri_args);
lua_setfield(L, -2, "get_query_args"); /* deprecated */
lua_pushcfunction(L, ngx_http_lua_ngx_req_get_post_args);
lua_setfield(L, -2, "get_post_args");
}
#ifndef NGX_HTTP_LUA_NO_FFI_API
size_t
ngx_http_lua_ffi_req_get_querystring_len(ngx_http_request_t *r)
{
return r->args.len;
}
int
ngx_http_lua_ffi_req_get_uri_args_count(ngx_http_request_t *r, int max)
{
int count;
u_char *p, *last;
if (r->connection->fd == -1) {
return NGX_HTTP_LUA_FFI_BAD_CONTEXT;
}
if (max < 0) {
max = NGX_HTTP_LUA_MAX_ARGS;
}
last = r->args.data + r->args.len;
count = 0;
for (p = r->args.data; p != last; p++) {
if (*p == '&') {
if (count == 0) {
count += 2;
} else {
count++;
}
}
}
if (count) {
if (max > 0 && count > max) {
count = max;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua hit query args limit %d", max);
}
return count;
}
if (r->args.len) {
return 1;
}
return 0;
}
int
ngx_http_lua_ffi_req_get_uri_args(ngx_http_request_t *r, u_char *buf,
ngx_http_lua_ffi_table_elt_t *out, int count)
{
int i, parsing_value = 0;
u_char *last, *p, *q;
u_char *src, *dst;
if (count <= 0) {
return NGX_OK;
}
ngx_memcpy(buf, r->args.data, r->args.len);
i = 0;
last = buf + r->args.len;
p = buf;
q = p;
while (p != last) {
if (*p == '=' && !parsing_value) {
/* key data is between p and q */
src = q; dst = q;
ngx_http_lua_unescape_uri(&dst, &src, p - q,
NGX_UNESCAPE_URI_COMPONENT);
dd("saving key %.*s", (int) (dst - q), q);
out[i].key.data = q;
out[i].key.len = (int) (dst - q);
/* skip the current '=' char */
p++;
q = p;
parsing_value = 1;
} else if (*p == '&') {
/* reached the end of a key or a value, just save it */
src = q; dst = q;
ngx_http_lua_unescape_uri(&dst, &src, p - q,
NGX_UNESCAPE_URI_COMPONENT);
dd("pushing key or value %.*s", (int) (dst - q), q);
if (parsing_value) {
/* end of the current pair's value */
parsing_value = 0;
if (out[i].key.len) {
out[i].value.data = q;
out[i].value.len = (int) (dst - q);
i++;
}
} else {
/* the current parsing pair takes no value,
* just push the value "true" */
dd("pushing boolean true");
if (dst - q) {
out[i].key.data = q;
out[i].key.len = (int) (dst - q);
out[i].value.len = -1;
i++;
}
}
if (i == count) {
return i;
}
/* skip the current '&' char */
p++;
q = p;
} else {
p++;
}
}
if (p != q || parsing_value) {
src = q; dst = q;
ngx_http_lua_unescape_uri(&dst, &src, p - q,
NGX_UNESCAPE_URI_COMPONENT);
dd("pushing key or value %.*s", (int) (dst - q), q);
if (parsing_value) {
if (out[i].key.len) {
out[i].value.data = q;
out[i].value.len = (int) (dst - q);
i++;
}
} else {
if (dst - q) {
out[i].key.data = q;
out[i].key.len = (int) (dst - q);
out[i].value.len = (int) -1;
i++;
}
}
}
return i;
}
#endif /* NGX_HTTP_LUA_NO_FFI_API */
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */