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