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:
agentzh (章亦春) 2011-08-12 18:21:22 +08:00
Родитель d9f68ec6aa
Коммит 9880409151
4 изменённых файлов: 189 добавлений и 14 удалений

Просмотреть файл

@ -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;