static int ngx_http_lua_ngx_echo(lua_State *L, unsigned newline) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; const char *p; size_t len; size_t size; ngx_buf_t *b; ngx_chain_t *cl; ngx_int_t rc; int i; int nargs; int type; const char *msg; ngx_buf_tag_t tag; lua_pushlightuserdata(L, &ngx_http_lua_request_key); lua_rawget(L, LUA_GLOBALSINDEX); r = lua_touserdata(L, -1); lua_pop(L, 1); if (r == NULL) { return luaL_error(L, "no request object found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); if (r->header_only) { return 0; } if (ctx->eof) { return luaL_error(L, "seen eof already"); } nargs = lua_gettop(L); size = 0; for (i = 1; i <= nargs; i++) { type = lua_type(L, i); switch (type) { case LUA_TNUMBER: case LUA_TSTRING: lua_tolstring(L, i, &len); size += len; break; case LUA_TNIL: size += sizeof("nil") - 1; break; case LUA_TBOOLEAN: if (lua_toboolean(L, i)) { size += sizeof("true") - 1; } else { size += sizeof("false") - 1; } break; case LUA_TTABLE: size += ngx_http_lua_calc_strlen_in_table(L, i, i, 0 /* strict */); break; case LUA_TLIGHTUSERDATA: dd("userdata: %p", lua_touserdata(L, i)); if (lua_touserdata(L, i) == NULL) { size += sizeof("null") - 1; break; } continue; default: msg = lua_pushfstring(L, "string, number, boolean, nil, " "ngx.null, or array table expected, " "but got %s", lua_typename(L, type)); return luaL_argerror(L, i, msg); } } if (newline) { size += sizeof("\n") - 1; } if (size == 0) { /* do nothing for empty strings */ return 0; } tag = (ngx_buf_tag_t) &ngx_http_lua_module; cl = ngx_http_lua_chains_get_free_buf(r->connection->log, r->pool, &ctx->free_bufs, size, tag); if (cl == NULL) { return luaL_error(L, "out of memory"); } b = cl->buf; for (i = 1; i <= nargs; i++) { type = lua_type(L, i); switch (type) { case LUA_TNUMBER: case LUA_TSTRING: p = lua_tolstring(L, i, &len); b->last = ngx_copy(b->last, (u_char *) p, len); break; case LUA_TNIL: *b->last++ = 'n'; *b->last++ = 'i'; *b->last++ = 'l'; break; case LUA_TBOOLEAN: if (lua_toboolean(L, i)) { *b->last++ = 't'; *b->last++ = 'r'; *b->last++ = 'u'; *b->last++ = 'e'; } else { *b->last++ = 'f'; *b->last++ = 'a'; *b->last++ = 'l'; *b->last++ = 's'; *b->last++ = 'e'; } break; case LUA_TTABLE: b->last = ngx_http_lua_copy_str_in_table(L, i, b->last); break; case LUA_TLIGHTUSERDATA: *b->last++ = 'n'; *b->last++ = 'u'; *b->last++ = 'l'; *b->last++ = 'l'; break; default: return luaL_error(L, "impossible to reach here"); } } if (newline) { *b->last++ = '\n'; } #if 0 if (b->last != b->end) { return luaL_error(L, "buffer error: %p != %p", b->last, b->end); } #endif ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, newline ? "lua say response" : "lua print response"); rc = ngx_http_lua_send_chain_link(r, ctx, cl); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return luaL_error(L, "failed to send data through the output filters"); } dd("downstream write: %d, buf len: %d", (int) rc, (int) (b->last - b->pos)); if (!ctx->out) { #if nginx_version >= 1001004 ngx_chain_update_chains(r->pool, #else ngx_chain_update_chains( #endif &ctx->free_bufs, &ctx->busy_bufs, &cl, tag); dd("out lua buf tag: %p, buffered: %x, busy bufs: %p", &ngx_http_lua_module, (int) r->connection->buffered, ctx->busy_bufs); }
int ngx_http_lua_body_filter_param_set(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx) { int type; int idx; u_char *data; size_t size; unsigned last; ngx_chain_t *cl; ngx_chain_t *in; ngx_buf_tag_t tag; idx = luaL_checkint(L, 2); dd("index: %d", idx); if (idx != 1 && idx != 2) { return luaL_error(L, "bad index: %d", idx); } if (idx == 2) { /* overwriting the eof flag */ last = lua_toboolean(L, 3); lua_pushlightuserdata(L, &ngx_http_lua_body_filter_chain_key); lua_rawget(L, LUA_GLOBALSINDEX); in = lua_touserdata(L, -1); if (last) { if (in) { for (cl = in; cl; cl = cl->next) { if (cl->next == NULL) { cl->buf->last_buf = 1; break; } } } else { tag = (ngx_buf_tag_t) &ngx_http_lua_module; cl = ngx_http_lua_chains_get_free_buf(r->connection->log, r->pool, &ctx->free_bufs, 0, tag); if (cl == NULL) { return luaL_error(L, "out of memory"); } cl->buf->last_buf = 1; lua_pushlightuserdata(L, &ngx_http_lua_body_filter_chain_key); lua_pushlightuserdata(L, cl); lua_rawset(L, LUA_GLOBALSINDEX); } } else { /* last == 0 */ if (in) { for (size = 0, cl = in; cl; cl = cl->next) { if (cl->buf->last_buf) { cl->buf->last_buf = 0; } size += cl->buf->last - cl->buf->pos; } if (size == 0) { lua_pushlightuserdata(L, &ngx_http_lua_body_filter_chain_key); lua_pushlightuserdata(L, NULL); lua_rawset(L, LUA_GLOBALSINDEX); } } } return 0; } /* idx == 1, overwriting the chunk data */ type = lua_type(L, 3); switch (type) { case LUA_TSTRING: case LUA_TNUMBER: data = (u_char *) lua_tolstring(L, 3, &size); break; case LUA_TNIL: /* discard the buffers */ lua_pushlightuserdata(L, &ngx_http_lua_body_filter_chain_key); /* key */ lua_pushvalue(L, -1); /* key key */ lua_rawget(L, LUA_GLOBALSINDEX); /* key val */ in = lua_touserdata(L, -1); lua_pop(L, 1); /* key */ for (cl = in; cl; cl = cl->next) { dd("mark the buf as consumed: %d", (int) ngx_buf_size(cl->buf)); cl->buf->pos = cl->buf->last; } lua_pushlightuserdata(L, NULL); /* key val */ lua_rawset(L, LUA_GLOBALSINDEX); return 0; case LUA_TTABLE: size = ngx_http_lua_calc_strlen_in_table(L, 3 /* index */, 3 /* arg */, 1 /* strict */); data = NULL; break; default: return luaL_error(L, "bad chunk data type: %s", lua_typename(L, type)); } lua_pushlightuserdata(L, &ngx_http_lua_body_filter_chain_key); lua_rawget(L, LUA_GLOBALSINDEX); in = lua_touserdata(L, -1); lua_pop(L, 1); last = 0; for (cl = in; cl; cl = cl->next) { if (cl->buf->last_buf) { last = 1; } dd("mark the buf as consumed: %d", (int) ngx_buf_size(cl->buf)); cl->buf->pos = cl->buf->last; } if (size == 0) { if (last) { if (in) { in->buf->last_buf = 1; } else { tag = (ngx_buf_tag_t) &ngx_http_lua_module; cl = ngx_http_lua_chains_get_free_buf(r->connection->log, r->pool, &ctx->free_bufs, 0, tag); if (cl == NULL) { return luaL_error(L, "out of memory"); } cl->buf->last_buf = 1; lua_pushlightuserdata(L, &ngx_http_lua_body_filter_chain_key); lua_pushlightuserdata(L, cl); lua_rawset(L, LUA_GLOBALSINDEX); } } return 0; } tag = (ngx_buf_tag_t) &ngx_http_lua_module; cl = ngx_http_lua_chains_get_free_buf(r->connection->log, r->pool, &ctx->free_bufs, size, tag); if (cl == NULL) { return luaL_error(L, "out of memory"); } if (type == LUA_TTABLE) { cl->buf->last = ngx_http_lua_copy_str_in_table(L, 3, cl->buf->last); } else { cl->buf->last = ngx_copy(cl->buf->pos, data, size); } cl->buf->last_buf = last; lua_pushlightuserdata(L, &ngx_http_lua_body_filter_chain_key); lua_pushlightuserdata(L, cl); lua_rawset(L, LUA_GLOBALSINDEX); return 0; }
int ngx_http_lua_body_filter_param_set(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx) { int type; int idx; int found; u_char *data; size_t size; unsigned last; ngx_chain_t *cl; ngx_chain_t *in; ngx_buf_tag_t tag; idx = luaL_checkint(L, 2); dd("index: %d", idx); if (idx != 1 && idx != 2) { return luaL_error(L, "bad index: %d", idx); } if (idx == 2) { /* overwriting the eof flag */ last = lua_toboolean(L, 3); lua_getglobal(L, ngx_http_lua_chain_key); in = lua_touserdata(L, -1); if (last) { ctx->seen_last_in_filter = 1; for (cl = in; cl; cl = cl->next) { if (cl->next == NULL) { if (r == r->main) { cl->buf->last_buf = 1; } else { cl->buf->last_in_chain = 1; } break; } } } else { /* last == 0 */ found = 0; for (cl = in; cl; cl = cl->next) { if (cl->buf->last_buf) { cl->buf->last_buf = 0; found = 1; } if (cl->buf->last_in_chain) { cl->buf->last_in_chain = 0; found = 1; } if (found && cl->buf->last - cl->buf->pos == 0) { /* make it a special sync buf to make * ngx_http_write_filter_module happy. */ cl->buf->temporary = 0; cl->buf->memory = 0; cl->buf->mmap = 0; cl->buf->in_file = 0; cl->buf->sync = 1; } } } return 0; } /* idx == 1, overwriting the chunk data */ type = lua_type(L, 3); switch (type) { case LUA_TSTRING: case LUA_TNUMBER: data = (u_char *) lua_tolstring(L, 3, &size); break; case LUA_TNIL: /* discard the buffers */ lua_getglobal(L, ngx_http_lua_chain_key); /* key val */ in = lua_touserdata(L, -1); lua_pop(L, 1); lua_pushliteral(L, ngx_http_lua_chain_key); /* key */ for (cl = in; cl; cl = cl->next) { dd("mark the buf as consumed: %d", (int) ngx_buf_size(cl->buf)); cl->buf->pos = cl->buf->last; /* will later be skipped by ngx_http_lua_body_filter() * and never be forwarded to the next filters. */ } return 0; case LUA_TTABLE: size = ngx_http_lua_calc_strlen_in_table(L, 3 /* index */, 3 /* arg */, 1 /* strict */); data = NULL; break; default: return luaL_error(L, "bad chunk data type: %s", lua_typename(L, type)); } lua_getglobal(L, ngx_http_lua_chain_key); in = lua_touserdata(L, -1); lua_pop(L, 1); last = 0; for (cl = in; cl; cl = cl->next) { if (cl->buf->last_buf || cl->buf->last_in_chain) { last = 1; } dd("mark the buf as consumed: %d", (int) ngx_buf_size(cl->buf)); cl->buf->pos = cl->buf->last; /* will later be skipped by ngx_http_lua_body_filter() and * never be forwarded to the next filters */ } if (size == 0 && !last) { return 0; } tag = (ngx_buf_tag_t) &ngx_http_lua_module; cl = ngx_http_lua_chains_get_free_buf(r->connection->log, r->pool, &ctx->free_bufs, size, tag); if (cl == NULL) { return luaL_error(L, "out of memory"); } if (size == 0) { /* "last" must already be set */ if (r == r->main) { cl->buf->last_buf = 1; } else { cl->buf->last_in_chain = 1; } return 0; } if (type == LUA_TTABLE) { cl->buf->last = ngx_http_lua_copy_str_in_table(L, 3, cl->buf->last); } else { cl->buf->last = ngx_copy(cl->buf->pos, data, size); } if (last) { ctx->seen_last_in_filter = 1; if (r == r->main) { cl->buf->last_buf = 1; } else { cl->buf->last_in_chain = 1; } } lua_pushlightuserdata(L, cl); lua_setglobal(L, ngx_http_lua_chain_key); return 0; }