now ngx.print/ngx.say allow (nested) array-like table arguments. the array elements in them will be sent piece by piece. this will avoid string concatenation for templating engines like ltp.
This commit is contained in:
Родитель
d9f68ec6aa
Коммит
9880409151
|
@ -1217,6 +1217,21 @@ Emit args concatenated to the HTTP client (as response body).
|
|||
|
||||
Lua nil value will result in outputing "nil", and Lua boolean values will emit "true" or "false".
|
||||
|
||||
Also, nested arrays of strings are also allowed. The elements in the arrays will be sent one by one. For example
|
||||
|
||||
local table = {
|
||||
"hello, ",
|
||||
{"world: ", true, " or ", false,
|
||||
{": ", nil}}
|
||||
}
|
||||
ngx.print(table)
|
||||
|
||||
will yield the output
|
||||
|
||||
hello, world: true or false: nil
|
||||
|
||||
Non-array table arguments will cause a Lua exception to be thrown.
|
||||
|
||||
ngx.say(a, b, ...)
|
||||
------------------
|
||||
* **Context:** `rewrite_by_lua*`, `access_by_lua*`, `content_by_lua*`
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "ngx_http_lua_contentby.h"
|
||||
#include "ngx_http_lua_headers_out.h"
|
||||
#include "ngx_http_lua_headers_in.h"
|
||||
#include <math.h>
|
||||
|
||||
#define NGX_UNESCAPE_URI_COMPONENT 0
|
||||
|
||||
|
@ -46,6 +47,8 @@ static uintptr_t ngx_http_lua_escape_uri(u_char *dst, u_char *src,
|
|||
static int ngx_http_lua_ngx_req_header_set_helper(lua_State *L);
|
||||
static int ngx_http_lua_parse_args(ngx_http_request_t *r, lua_State *L,
|
||||
u_char *buf, u_char *last);
|
||||
static size_t ngx_http_lua_calc_strlen_in_table(lua_State *L, int arg_i);
|
||||
static u_char * ngx_http_lua_copy_str_in_table(lua_State *L, u_char *dst);
|
||||
|
||||
|
||||
#if defined(NDK) && NDK
|
||||
|
@ -182,10 +185,7 @@ log_wrapper(ngx_http_request_t *r, const char *ident, int level, lua_State *L)
|
|||
break;
|
||||
|
||||
default:
|
||||
/* impossible to reach here */
|
||||
msg = lua_pushfstring(L, "string, number, boolean, or nil "
|
||||
"expected, got %s", lua_typename(L, type));
|
||||
return luaL_argerror(L, i, msg);
|
||||
return luaL_error(L, "impossible to reach here");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -359,9 +359,15 @@ ngx_http_lua_ngx_echo(lua_State *L, ngx_flag_t newline)
|
|||
|
||||
break;
|
||||
|
||||
case LUA_TTABLE:
|
||||
size += ngx_http_lua_calc_strlen_in_table(L, i);
|
||||
break;
|
||||
|
||||
default:
|
||||
msg = lua_pushfstring(L, "string, number, boolean, or nil "
|
||||
"expected, got %s", lua_typename(L, type));
|
||||
msg = lua_pushfstring(L, "string, number, boolean, nil, "
|
||||
"or array table expected, got %s",
|
||||
lua_typename(L, type));
|
||||
|
||||
return luaL_argerror(L, i, msg);
|
||||
}
|
||||
}
|
||||
|
@ -412,11 +418,12 @@ ngx_http_lua_ngx_echo(lua_State *L, ngx_flag_t newline)
|
|||
|
||||
break;
|
||||
|
||||
case LUA_TTABLE:
|
||||
b->last = ngx_http_lua_copy_str_in_table(L, b->last);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* impossible to reach here */
|
||||
msg = lua_pushfstring(L, "string, number, boolean, or nil "
|
||||
"expected, got %s", lua_typename(L, type));
|
||||
return luaL_argerror(L, i, msg);
|
||||
return luaL_error(L, "impossible to reach here");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -424,6 +431,10 @@ ngx_http_lua_ngx_echo(lua_State *L, ngx_flag_t newline)
|
|||
*b->last++ = '\n';
|
||||
}
|
||||
|
||||
if (b->last != b->end) {
|
||||
return luaL_error(L, "buffer error: %p != %p", b->last, b->end);
|
||||
}
|
||||
|
||||
cl = ngx_alloc_chain_link(r->pool);
|
||||
if (cl == NULL) {
|
||||
return luaL_error(L, "out of memory");
|
||||
|
@ -3214,3 +3225,152 @@ ngx_http_lua_parse_args(ngx_http_request_t *r, lua_State *L, u_char *buf,
|
|||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static size_t
|
||||
ngx_http_lua_calc_strlen_in_table(lua_State *L, int arg_i)
|
||||
{
|
||||
double key;
|
||||
int max;
|
||||
int i;
|
||||
int type;
|
||||
size_t size;
|
||||
size_t len;
|
||||
const char *msg;
|
||||
|
||||
max = 0;
|
||||
|
||||
lua_pushnil(L); /* stack: table key */
|
||||
while (lua_next(L, -2) != 0) { /* stack: table key value */
|
||||
if (lua_type(L, -2) == LUA_TNUMBER && (key = lua_tonumber(L, -2))) {
|
||||
if (floor(key) == key && key >= 1) {
|
||||
if (key > max) {
|
||||
max = key;
|
||||
}
|
||||
|
||||
lua_pop(L, 1); /* stack: table key */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* not an array (non positive integer key) */
|
||||
lua_pop(L, 2); /* stack: table */
|
||||
|
||||
msg = lua_pushfstring(L, "on-array table found");
|
||||
luaL_argerror(L, arg_i, msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size = 0;
|
||||
|
||||
for (i = 1; i <= max; i++) {
|
||||
lua_rawgeti(L, -1, i); /* stack: table value */
|
||||
type = lua_type(L, -1);
|
||||
switch (type) {
|
||||
case LUA_TNUMBER:
|
||||
case LUA_TSTRING:
|
||||
lua_tolstring(L, -1, &len);
|
||||
size += len;
|
||||
break;
|
||||
|
||||
case LUA_TNIL:
|
||||
size += sizeof("nil") - 1;
|
||||
break;
|
||||
|
||||
case LUA_TBOOLEAN:
|
||||
if (lua_toboolean(L, -1)) {
|
||||
size += sizeof("true") - 1;
|
||||
|
||||
} else {
|
||||
size += sizeof("false") - 1;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case LUA_TTABLE:
|
||||
size += ngx_http_lua_calc_strlen_in_table(L, arg_i);
|
||||
break;
|
||||
|
||||
default:
|
||||
msg = lua_pushfstring(L, "bad data type %s found",
|
||||
lua_typename(L, type));
|
||||
luaL_argerror(L, arg_i, msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
lua_pop(L, 1); /* stack: table */
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
static u_char *
|
||||
ngx_http_lua_copy_str_in_table(lua_State *L, u_char *dst)
|
||||
{
|
||||
double key;
|
||||
int max;
|
||||
int i;
|
||||
int type;
|
||||
size_t len;
|
||||
u_char *p;
|
||||
|
||||
max = 0;
|
||||
|
||||
lua_pushnil(L); /* stack: table key */
|
||||
while (lua_next(L, -2) != 0) { /* stack: table key value */
|
||||
key = lua_tonumber(L, -2);
|
||||
if (key > max) {
|
||||
max = key;
|
||||
}
|
||||
|
||||
lua_pop(L, 1); /* stack: table key */
|
||||
}
|
||||
|
||||
for (i = 1; i <= max; i++) {
|
||||
lua_rawgeti(L, -1, i); /* stack: table value */
|
||||
type = lua_type(L, -1);
|
||||
switch (type) {
|
||||
case LUA_TNUMBER:
|
||||
case LUA_TSTRING:
|
||||
p = (u_char *) lua_tolstring(L, -1, &len);
|
||||
dst = ngx_copy(dst, p, len);
|
||||
break;
|
||||
|
||||
case LUA_TNIL:
|
||||
*dst++ = 'n';
|
||||
*dst++ = 'i';
|
||||
*dst++ = 'l';
|
||||
break;
|
||||
|
||||
case LUA_TBOOLEAN:
|
||||
if (lua_toboolean(L, -1)) {
|
||||
*dst++ = 't';
|
||||
*dst++ = 'r';
|
||||
*dst++ = 'u';
|
||||
*dst++ = 'e';
|
||||
|
||||
} else {
|
||||
*dst++ = 'f';
|
||||
*dst++ = 'a';
|
||||
*dst++ = 'l';
|
||||
*dst++ = 's';
|
||||
*dst++ = 'e';
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case LUA_TTABLE:
|
||||
dst = ngx_http_lua_copy_str_in_table(L, dst);
|
||||
break;
|
||||
|
||||
default:
|
||||
luaL_error(L, "impossible to reach here");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lua_pop(L, 1); /* stack: table */
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ run_tests();
|
|||
__DATA__
|
||||
|
||||
=== TEST 1: sanity
|
||||
|
||||
--- config
|
||||
location /lua {
|
||||
content_by_lua '
|
||||
|
|
|
@ -7,8 +7,8 @@ use Test::Nginx::Socket;
|
|||
#workers(2);
|
||||
#log_level('warn');
|
||||
|
||||
#repeat_each(2);
|
||||
repeat_each(1);
|
||||
repeat_each(2);
|
||||
#repeat_each(1);
|
||||
|
||||
plan tests => repeat_each() * (blocks() * 2);
|
||||
|
||||
|
@ -69,7 +69,8 @@ a=3&b=4&c
|
|||
--- error_code: 500
|
||||
|
||||
|
||||
=== TEST 2: empty request body
|
||||
|
||||
=== TEST 3: empty request body
|
||||
--- config
|
||||
location /lua {
|
||||
lua_need_request_body on;
|
||||
|
|
Загрузка…
Ссылка в новой задаче