Line data Source code
1 :
2 : /*
3 : * Copyright (C) Xiaozhe Wang (chaoslawful)
4 : * Copyright (C) Yichun Zhang (agentzh)
5 : */
6 :
7 :
8 : #ifndef DDEBUG
9 : #define DDEBUG 0
10 : #endif
11 : #include "ddebug.h"
12 :
13 :
14 : #include "ngx_http_lua_bodyfilterby.h"
15 : #include "ngx_http_lua_exception.h"
16 : #include "ngx_http_lua_util.h"
17 : #include "ngx_http_lua_pcrefix.h"
18 : #include "ngx_http_lua_time.h"
19 : #include "ngx_http_lua_log.h"
20 : #include "ngx_http_lua_regex.h"
21 : #include "ngx_http_lua_cache.h"
22 : #include "ngx_http_lua_headers.h"
23 : #include "ngx_http_lua_variable.h"
24 : #include "ngx_http_lua_string.h"
25 : #include "ngx_http_lua_misc.h"
26 : #include "ngx_http_lua_consts.h"
27 : #include "ngx_http_lua_output.h"
28 :
29 :
30 : static void ngx_http_lua_body_filter_by_lua_env(lua_State *L,
31 : ngx_http_request_t *r, ngx_chain_t *in);
32 : static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
33 :
34 :
35 : /* key for the ngx_chain_t *in pointer in the Lua thread */
36 : #define ngx_http_lua_chain_key "__ngx_cl"
37 :
38 :
39 : /**
40 : * Set environment table for the given code closure.
41 : *
42 : * Before:
43 : * | code closure | <- top
44 : * | ... |
45 : *
46 : * After:
47 : * | code closure | <- top
48 : * | ... |
49 : * */
50 : static void
51 0 : ngx_http_lua_body_filter_by_lua_env(lua_State *L, ngx_http_request_t *r,
52 : ngx_chain_t *in)
53 : {
54 : /* set nginx request pointer to current lua thread's globals table */
55 0 : ngx_http_lua_set_req(L, r);
56 :
57 0 : lua_pushlightuserdata(L, in);
58 0 : lua_setglobal(L, ngx_http_lua_chain_key);
59 :
60 : /**
61 : * we want to create empty environment for current script
62 : *
63 : * setmetatable({}, {__index = _G})
64 : *
65 : * if a function or symbol is not defined in our env, __index will lookup
66 : * in the global env.
67 : *
68 : * all variables created in the script-env will be thrown away at the end
69 : * of the script run.
70 : * */
71 0 : ngx_http_lua_create_new_globals_table(L, 0 /* narr */, 1 /* nrec */);
72 :
73 : /* {{{ make new env inheriting main thread's globals table */
74 0 : lua_createtable(L, 0, 1 /* nrec */); /* the metatable for the new
75 : env */
76 0 : ngx_http_lua_get_globals_table(L);
77 0 : lua_setfield(L, -2, "__index");
78 0 : lua_setmetatable(L, -2); /* setmetatable({}, {__index = _G}) */
79 : /* }}} */
80 :
81 0 : lua_setfenv(L, -2); /* set new running env for the code closure */
82 0 : }
83 :
84 :
85 : ngx_int_t
86 0 : ngx_http_lua_body_filter_by_chunk(lua_State *L, ngx_http_request_t *r,
87 : ngx_chain_t *in)
88 : {
89 : ngx_int_t rc;
90 : u_char *err_msg;
91 : size_t len;
92 : #if (NGX_PCRE)
93 : ngx_pool_t *old_pool;
94 : #endif
95 :
96 : dd("initialize nginx context in Lua VM, code chunk at stack top sp = 1");
97 0 : ngx_http_lua_body_filter_by_lua_env(L, r, in);
98 :
99 : #if (NGX_PCRE)
100 : /* XXX: work-around to nginx regex subsystem */
101 0 : old_pool = ngx_http_lua_pcre_malloc_init(r->pool);
102 : #endif
103 :
104 0 : lua_pushcfunction(L, ngx_http_lua_traceback);
105 0 : lua_insert(L, 1); /* put it under chunk and args */
106 :
107 : dd("protected call user code");
108 0 : rc = lua_pcall(L, 0, 1, 1);
109 :
110 0 : lua_remove(L, 1); /* remove traceback function */
111 :
112 : #if (NGX_PCRE)
113 : /* XXX: work-around to nginx regex subsystem */
114 0 : ngx_http_lua_pcre_malloc_done(old_pool);
115 : #endif
116 :
117 0 : if (rc != 0) {
118 :
119 : /* error occurred */
120 0 : err_msg = (u_char *) lua_tolstring(L, -1, &len);
121 :
122 0 : if (err_msg == NULL) {
123 0 : err_msg = (u_char *) "unknown reason";
124 0 : len = sizeof("unknown reason") - 1;
125 : }
126 :
127 0 : ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
128 : "failed to run body_filter_by_lua*: %*s", len, err_msg);
129 :
130 0 : lua_settop(L, 0); /* clear remaining elems on stack */
131 :
132 0 : return NGX_ERROR;
133 : }
134 :
135 : /* rc == 0 */
136 :
137 0 : rc = (ngx_int_t) lua_tointeger(L, -1);
138 :
139 : dd("got return value: %d", (int) rc);
140 :
141 0 : lua_settop(L, 0);
142 :
143 0 : if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
144 0 : return NGX_ERROR;
145 : }
146 :
147 0 : return NGX_OK;
148 : }
149 :
150 :
151 : ngx_int_t
152 0 : ngx_http_lua_body_filter_inline(ngx_http_request_t *r, ngx_chain_t *in)
153 : {
154 : lua_State *L;
155 : ngx_int_t rc;
156 : ngx_http_lua_loc_conf_t *llcf;
157 :
158 0 : llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
159 :
160 0 : L = ngx_http_lua_get_lua_vm(r, NULL);
161 :
162 : /* load Lua inline script (w/ cache) sp = 1 */
163 0 : rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,
164 0 : llcf->body_filter_src.value.data,
165 : llcf->body_filter_src.value.len,
166 0 : llcf->body_filter_src_key,
167 : "=body_filter_by_lua");
168 0 : if (rc != NGX_OK) {
169 0 : return NGX_ERROR;
170 : }
171 :
172 0 : rc = ngx_http_lua_body_filter_by_chunk(L, r, in);
173 :
174 : dd("body filter by chunk returns %d", (int) rc);
175 :
176 0 : if (rc != NGX_OK) {
177 0 : return NGX_ERROR;
178 : }
179 :
180 0 : return NGX_OK;
181 : }
182 :
183 :
184 : ngx_int_t
185 0 : ngx_http_lua_body_filter_file(ngx_http_request_t *r, ngx_chain_t *in)
186 : {
187 : lua_State *L;
188 : ngx_int_t rc;
189 : u_char *script_path;
190 : ngx_http_lua_loc_conf_t *llcf;
191 : ngx_str_t eval_src;
192 :
193 0 : llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
194 :
195 : /* Eval nginx variables in code path string first */
196 0 : if (ngx_http_complex_value(r, &llcf->body_filter_src, &eval_src)
197 : != NGX_OK)
198 : {
199 0 : return NGX_ERROR;
200 : }
201 :
202 0 : script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data,
203 : eval_src.len);
204 :
205 0 : if (script_path == NULL) {
206 0 : return NGX_ERROR;
207 : }
208 :
209 0 : L = ngx_http_lua_get_lua_vm(r, NULL);
210 :
211 : /* load Lua script file (w/ cache) sp = 1 */
212 0 : rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,
213 0 : llcf->body_filter_src_key);
214 0 : if (rc != NGX_OK) {
215 0 : return NGX_ERROR;
216 : }
217 :
218 : /* make sure we have a valid code chunk */
219 0 : ngx_http_lua_assert(lua_isfunction(L, -1));
220 :
221 0 : rc = ngx_http_lua_body_filter_by_chunk(L, r, in);
222 :
223 0 : if (rc != NGX_OK) {
224 0 : return NGX_ERROR;
225 : }
226 :
227 0 : return NGX_OK;
228 : }
229 :
230 :
231 : static ngx_int_t
232 0 : ngx_http_lua_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
233 : {
234 : ngx_http_lua_loc_conf_t *llcf;
235 : ngx_http_lua_ctx_t *ctx;
236 : ngx_int_t rc;
237 : uint16_t old_context;
238 : ngx_http_cleanup_t *cln;
239 : lua_State *L;
240 : ngx_chain_t *out;
241 :
242 0 : ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
243 : "lua body filter for user lua code, uri \"%V\"", &r->uri);
244 :
245 0 : if (in == NULL) {
246 0 : return ngx_http_next_body_filter(r, in);
247 : }
248 :
249 0 : llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
250 :
251 0 : if (llcf->body_filter_handler == NULL) {
252 : dd("no body filter handler found");
253 0 : return ngx_http_next_body_filter(r, in);
254 : }
255 :
256 0 : ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
257 :
258 : dd("ctx = %p", ctx);
259 :
260 0 : if (ctx == NULL) {
261 0 : ctx = ngx_http_lua_create_ctx(r);
262 0 : if (ctx == NULL) {
263 0 : return NGX_ERROR;
264 : }
265 : }
266 :
267 0 : if (ctx->seen_last_in_filter) {
268 0 : for (/* void */; in; in = in->next) {
269 : dd("mark the buf as consumed: %d", (int) ngx_buf_size(in->buf));
270 0 : in->buf->pos = in->buf->last;
271 0 : in->buf->file_pos = in->buf->file_last;
272 : }
273 :
274 0 : return NGX_OK;
275 : }
276 :
277 0 : if (ctx->cleanup == NULL) {
278 0 : cln = ngx_http_cleanup_add(r, 0);
279 0 : if (cln == NULL) {
280 0 : return NGX_ERROR;
281 : }
282 :
283 0 : cln->handler = ngx_http_lua_request_cleanup_handler;
284 0 : cln->data = ctx;
285 0 : ctx->cleanup = &cln->handler;
286 : }
287 :
288 0 : old_context = ctx->context;
289 0 : ctx->context = NGX_HTTP_LUA_CONTEXT_BODY_FILTER;
290 :
291 : dd("calling body filter handler");
292 0 : rc = llcf->body_filter_handler(r, in);
293 :
294 : dd("calling body filter handler returned %d", (int) rc);
295 :
296 0 : ctx->context = old_context;
297 :
298 0 : if (rc != NGX_OK) {
299 0 : return NGX_ERROR;
300 : }
301 :
302 0 : L = ngx_http_lua_get_lua_vm(r, ctx);
303 :
304 0 : lua_getglobal(L, ngx_http_lua_chain_key);
305 0 : out = lua_touserdata(L, -1);
306 0 : lua_pop(L, 1);
307 :
308 0 : if (in == out) {
309 0 : return ngx_http_next_body_filter(r, in);
310 : }
311 :
312 0 : if (out == NULL) {
313 : /* do not forward NULL to the next filters because the input is
314 : * not NULL */
315 0 : return NGX_OK;
316 : }
317 :
318 : /* in != out */
319 0 : rc = ngx_http_next_body_filter(r, out);
320 0 : if (rc == NGX_ERROR) {
321 0 : return NGX_ERROR;
322 : }
323 :
324 : #if nginx_version >= 1001004
325 0 : ngx_chain_update_chains(r->pool,
326 : #else
327 : ngx_chain_update_chains(
328 : #endif
329 : &ctx->free_bufs, &ctx->busy_bufs, &out,
330 : (ngx_buf_tag_t) &ngx_http_lua_module);
331 :
332 0 : return rc;
333 : }
334 :
335 :
336 : ngx_int_t
337 0 : ngx_http_lua_body_filter_init(void)
338 : {
339 : dd("calling body filter init");
340 0 : ngx_http_next_body_filter = ngx_http_top_body_filter;
341 0 : ngx_http_top_body_filter = ngx_http_lua_body_filter;
342 :
343 0 : return NGX_OK;
344 : }
345 :
346 :
347 : int
348 0 : ngx_http_lua_body_filter_param_get(lua_State *L)
349 : {
350 : u_char *data, *p;
351 : size_t size;
352 : ngx_chain_t *cl;
353 : ngx_buf_t *b;
354 : int idx;
355 : ngx_chain_t *in;
356 :
357 0 : idx = luaL_checkint(L, 2);
358 :
359 : dd("index: %d", idx);
360 :
361 0 : if (idx != 1 && idx != 2) {
362 0 : lua_pushnil(L);
363 0 : return 1;
364 : }
365 :
366 0 : lua_getglobal(L, ngx_http_lua_chain_key);
367 0 : in = lua_touserdata(L, -1);
368 :
369 0 : if (idx == 2) {
370 : /* asking for the eof argument */
371 :
372 0 : for (cl = in; cl; cl = cl->next) {
373 0 : if (cl->buf->last_buf || cl->buf->last_in_chain) {
374 0 : lua_pushboolean(L, 1);
375 0 : return 1;
376 : }
377 : }
378 :
379 0 : lua_pushboolean(L, 0);
380 0 : return 1;
381 : }
382 :
383 : /* idx == 1 */
384 :
385 0 : size = 0;
386 :
387 0 : if (in == NULL) {
388 : /* being a cleared chain on the Lua land */
389 0 : lua_pushliteral(L, "");
390 0 : return 1;
391 : }
392 :
393 0 : if (in->next == NULL) {
394 :
395 : dd("seen only single buffer");
396 :
397 0 : b = in->buf;
398 0 : lua_pushlstring(L, (char *) b->pos, b->last - b->pos);
399 0 : return 1;
400 : }
401 :
402 : dd("seen multiple buffers");
403 :
404 0 : for (cl = in; cl; cl = cl->next) {
405 0 : b = cl->buf;
406 :
407 0 : size += b->last - b->pos;
408 :
409 0 : if (b->last_buf || b->last_in_chain) {
410 : break;
411 : }
412 : }
413 :
414 0 : data = (u_char *) lua_newuserdata(L, size);
415 :
416 0 : for (p = data, cl = in; cl; cl = cl->next) {
417 0 : b = cl->buf;
418 0 : p = ngx_copy(p, b->pos, b->last - b->pos);
419 :
420 0 : if (b->last_buf || b->last_in_chain) {
421 : break;
422 : }
423 : }
424 :
425 0 : lua_pushlstring(L, (char *) data, size);
426 0 : return 1;
427 : }
428 :
429 :
430 : int
431 0 : ngx_http_lua_body_filter_param_set(lua_State *L, ngx_http_request_t *r,
432 : ngx_http_lua_ctx_t *ctx)
433 : {
434 : int type;
435 : int idx;
436 : int found;
437 : u_char *data;
438 : size_t size;
439 : unsigned last;
440 0 : unsigned flush = 0;
441 : ngx_buf_t *b;
442 : ngx_chain_t *cl;
443 : ngx_chain_t *in;
444 :
445 0 : idx = luaL_checkint(L, 2);
446 :
447 : dd("index: %d", idx);
448 :
449 0 : if (idx != 1 && idx != 2) {
450 0 : return luaL_error(L, "bad index: %d", idx);
451 : }
452 :
453 0 : if (idx == 2) {
454 : /* overwriting the eof flag */
455 0 : last = lua_toboolean(L, 3);
456 :
457 0 : lua_getglobal(L, ngx_http_lua_chain_key);
458 0 : in = lua_touserdata(L, -1);
459 0 : lua_pop(L, 1);
460 :
461 0 : if (last) {
462 0 : ctx->seen_last_in_filter = 1;
463 :
464 : /* the "in" chain cannot be NULL and we set the "last_buf" or
465 : * "last_in_chain" flag in the last buf of "in" */
466 :
467 0 : for (cl = in; cl; cl = cl->next) {
468 0 : if (cl->next == NULL) {
469 0 : if (r == r->main) {
470 0 : cl->buf->last_buf = 1;
471 :
472 : } else {
473 0 : cl->buf->last_in_chain = 1;
474 : }
475 :
476 0 : break;
477 : }
478 : }
479 :
480 : } else {
481 : /* last == 0 */
482 :
483 0 : found = 0;
484 :
485 0 : for (cl = in; cl; cl = cl->next) {
486 0 : b = cl->buf;
487 :
488 0 : if (b->last_buf) {
489 0 : b->last_buf = 0;
490 0 : found = 1;
491 : }
492 :
493 0 : if (b->last_in_chain) {
494 0 : b->last_in_chain = 0;
495 0 : found = 1;
496 : }
497 :
498 0 : if (found && b->last == b->pos && !ngx_buf_in_memory(b)) {
499 : /* make it a special sync buf to make
500 : * ngx_http_write_filter_module happy. */
501 0 : b->sync = 1;
502 : }
503 : }
504 :
505 0 : ctx->seen_last_in_filter = 0;
506 : }
507 :
508 0 : return 0;
509 : }
510 :
511 : /* idx == 1, overwriting the chunk data */
512 :
513 0 : type = lua_type(L, 3);
514 :
515 0 : switch (type) {
516 0 : case LUA_TSTRING:
517 : case LUA_TNUMBER:
518 0 : data = (u_char *) lua_tolstring(L, 3, &size);
519 0 : break;
520 :
521 0 : case LUA_TNIL:
522 : /* discard the buffers */
523 :
524 0 : lua_getglobal(L, ngx_http_lua_chain_key); /* key val */
525 0 : in = lua_touserdata(L, -1);
526 0 : lua_pop(L, 1);
527 :
528 0 : last = 0;
529 :
530 0 : for (cl = in; cl; cl = cl->next) {
531 0 : b = cl->buf;
532 :
533 0 : if (b->flush) {
534 0 : flush = 1;
535 : }
536 :
537 0 : if (b->last_in_chain || b->last_buf) {
538 0 : last = 1;
539 : }
540 :
541 : dd("mark the buf as consumed: %d", (int) ngx_buf_size(b));
542 0 : b->pos = b->last;
543 : }
544 :
545 : /* cl == NULL */
546 :
547 0 : goto done;
548 :
549 0 : case LUA_TTABLE:
550 0 : size = ngx_http_lua_calc_strlen_in_table(L, 3 /* index */, 3 /* arg */,
551 : 1 /* strict */);
552 0 : data = NULL;
553 0 : break;
554 :
555 0 : default:
556 0 : return luaL_error(L, "bad chunk data type: %s",
557 : lua_typename(L, type));
558 : }
559 :
560 0 : lua_getglobal(L, ngx_http_lua_chain_key);
561 0 : in = lua_touserdata(L, -1);
562 0 : lua_pop(L, 1);
563 :
564 0 : last = 0;
565 :
566 0 : for (cl = in; cl; cl = cl->next) {
567 0 : b = cl->buf;
568 :
569 0 : if (b->flush) {
570 0 : flush = 1;
571 : }
572 :
573 0 : if (b->last_buf || b->last_in_chain) {
574 0 : last = 1;
575 : }
576 :
577 : dd("mark the buf as consumed: %d", (int) ngx_buf_size(cl->buf));
578 0 : cl->buf->pos = cl->buf->last;
579 : }
580 :
581 : /* cl == NULL */
582 :
583 0 : if (size == 0) {
584 0 : goto done;
585 : }
586 :
587 0 : cl = ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool,
588 : &ctx->free_bufs, size);
589 0 : if (cl == NULL) {
590 0 : return luaL_error(L, "no memory");
591 : }
592 :
593 0 : if (type == LUA_TTABLE) {
594 0 : cl->buf->last = ngx_http_lua_copy_str_in_table(L, 3, cl->buf->last);
595 :
596 : } else {
597 0 : cl->buf->last = ngx_copy(cl->buf->pos, data, size);
598 : }
599 :
600 0 : done:
601 :
602 0 : if (last || flush) {
603 0 : if (cl == NULL) {
604 0 : cl = ngx_http_lua_chain_get_free_buf(r->connection->log,
605 : r->pool,
606 : &ctx->free_bufs, 0);
607 0 : if (cl == NULL) {
608 0 : return luaL_error(L, "no memory");
609 : }
610 : }
611 :
612 0 : if (last) {
613 0 : ctx->seen_last_in_filter = 1;
614 :
615 0 : if (r == r->main) {
616 0 : cl->buf->last_buf = 1;
617 :
618 : } else {
619 0 : cl->buf->last_in_chain = 1;
620 : }
621 : }
622 :
623 0 : if (flush) {
624 0 : cl->buf->flush = 1;
625 : }
626 : }
627 :
628 0 : lua_pushlightuserdata(L, cl);
629 0 : lua_setglobal(L, ngx_http_lua_chain_key);
630 0 : return 0;
631 : }
632 :
633 : /* vi:set ft=c ts=4 sw=4 et fdm=marker: */
|