Line data Source code
1 :
2 : /*
3 : * Copyright (C) Yichun Zhang (agentzh)
4 : */
5 :
6 :
7 : #ifndef DDEBUG
8 : #define DDEBUG 0
9 : #endif
10 : #include "ddebug.h"
11 :
12 : #include "ngx_http_lua_headerfilterby.h"
13 : #include "ngx_http_lua_exception.h"
14 : #include "ngx_http_lua_util.h"
15 : #include "ngx_http_lua_pcrefix.h"
16 : #include "ngx_http_lua_time.h"
17 : #include "ngx_http_lua_log.h"
18 : #include "ngx_http_lua_regex.h"
19 : #include "ngx_http_lua_cache.h"
20 : #include "ngx_http_lua_headers.h"
21 : #include "ngx_http_lua_variable.h"
22 : #include "ngx_http_lua_string.h"
23 : #include "ngx_http_lua_misc.h"
24 : #include "ngx_http_lua_consts.h"
25 : #include "ngx_http_lua_shdict.h"
26 :
27 :
28 : static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
29 :
30 :
31 : /**
32 : * Set environment table for the given code closure.
33 : *
34 : * Before:
35 : * | code closure | <- top
36 : * | ... |
37 : *
38 : * After:
39 : * | code closure | <- top
40 : * | ... |
41 : * */
42 : static void
43 0 : ngx_http_lua_header_filter_by_lua_env(lua_State *L, ngx_http_request_t *r)
44 : {
45 : /* set nginx request pointer to current lua thread's globals table */
46 0 : ngx_http_lua_set_req(L, r);
47 :
48 : /**
49 : * we want to create empty environment for current script
50 : *
51 : * newt = {}
52 : * newt["_G"] = newt
53 : * setmetatable(newt, {__index = _G})
54 : *
55 : * if a function or symbol is not defined in our env, __index will lookup
56 : * in the global env.
57 : *
58 : * all variables created in the script-env will be thrown away at the end
59 : * of the script run.
60 : * */
61 0 : ngx_http_lua_create_new_globals_table(L, 0 /* narr */, 1 /* nrec */);
62 :
63 : /* {{{ make new env inheriting main thread's globals table */
64 0 : lua_createtable(L, 0, 1 /* nrec */); /* the metatable for the new env */
65 0 : ngx_http_lua_get_globals_table(L);
66 0 : lua_setfield(L, -2, "__index");
67 0 : lua_setmetatable(L, -2); /* setmetatable({}, {__index = _G}) */
68 : /* }}} */
69 :
70 0 : lua_setfenv(L, -2); /* set new running env for the code closure */
71 0 : }
72 :
73 :
74 : ngx_int_t
75 0 : ngx_http_lua_header_filter_by_chunk(lua_State *L, ngx_http_request_t *r)
76 : {
77 0 : int old_exit_code = 0;
78 : ngx_int_t rc;
79 : u_char *err_msg;
80 : size_t len;
81 : #if (NGX_PCRE)
82 : ngx_pool_t *old_pool;
83 : #endif
84 : ngx_http_lua_ctx_t *ctx;
85 :
86 0 : ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
87 0 : if (ctx->exited) {
88 0 : old_exit_code = ctx->exit_code;
89 : }
90 :
91 : /* initialize nginx context in Lua VM, code chunk at stack top sp = 1 */
92 0 : ngx_http_lua_header_filter_by_lua_env(L, r);
93 :
94 : #if (NGX_PCRE)
95 : /* XXX: work-around to nginx regex subsystem */
96 0 : old_pool = ngx_http_lua_pcre_malloc_init(r->pool);
97 : #endif
98 :
99 0 : lua_pushcfunction(L, ngx_http_lua_traceback);
100 0 : lua_insert(L, 1); /* put it under chunk and args */
101 :
102 : /* protected call user code */
103 0 : rc = lua_pcall(L, 0, 1, 1);
104 :
105 0 : lua_remove(L, 1); /* remove traceback function */
106 :
107 : #if (NGX_PCRE)
108 : /* XXX: work-around to nginx regex subsystem */
109 0 : ngx_http_lua_pcre_malloc_done(old_pool);
110 : #endif
111 :
112 : dd("rc == %d", (int) rc);
113 :
114 0 : if (rc != 0) {
115 : /* error occurred when running loaded code */
116 0 : err_msg = (u_char *) lua_tolstring(L, -1, &len);
117 :
118 0 : if (err_msg == NULL) {
119 0 : err_msg = (u_char *) "unknown reason";
120 0 : len = sizeof("unknown reason") - 1;
121 : }
122 :
123 0 : ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
124 : "failed to run header_filter_by_lua*: %*s", len, err_msg);
125 :
126 0 : lua_settop(L, 0); /* clear remaining elems on stack */
127 :
128 0 : return NGX_ERROR;
129 : }
130 :
131 : dd("exited: %d, exit code: %d, old exit code: %d",
132 : (int) ctx->exited, (int) ctx->exit_code, (int) old_exit_code);
133 :
134 : #if 1
135 : /* clear Lua stack */
136 0 : lua_settop(L, 0);
137 : #endif
138 :
139 0 : if (ctx->exited && ctx->exit_code != old_exit_code) {
140 0 : if (ctx->exit_code == NGX_ERROR) {
141 0 : return NGX_ERROR;
142 : }
143 :
144 : dd("finalize request with %d", (int) ctx->exit_code);
145 :
146 0 : rc = ngx_http_filter_finalize_request(r, &ngx_http_lua_module,
147 : ctx->exit_code);
148 0 : if (rc == NGX_ERROR || rc == NGX_AGAIN) {
149 0 : return rc;
150 : }
151 :
152 0 : return NGX_DECLINED;
153 : }
154 :
155 0 : return NGX_OK;
156 : }
157 :
158 :
159 : ngx_int_t
160 0 : ngx_http_lua_header_filter_inline(ngx_http_request_t *r)
161 : {
162 : lua_State *L;
163 : ngx_int_t rc;
164 : ngx_http_lua_loc_conf_t *llcf;
165 :
166 0 : llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
167 :
168 0 : L = ngx_http_lua_get_lua_vm(r, NULL);
169 :
170 : /* load Lua inline script (w/ cache) sp = 1 */
171 0 : rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,
172 0 : llcf->header_filter_src.value.data,
173 : llcf->header_filter_src.value.len,
174 0 : llcf->header_filter_src_key,
175 : "=header_filter_by_lua");
176 0 : if (rc != NGX_OK) {
177 0 : return NGX_ERROR;
178 : }
179 :
180 : dd("calling header filter by chunk");
181 :
182 0 : return ngx_http_lua_header_filter_by_chunk(L, r);
183 : }
184 :
185 :
186 : ngx_int_t
187 0 : ngx_http_lua_header_filter_file(ngx_http_request_t *r)
188 : {
189 : lua_State *L;
190 : ngx_int_t rc;
191 : u_char *script_path;
192 : ngx_http_lua_loc_conf_t *llcf;
193 : ngx_str_t eval_src;
194 :
195 0 : llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
196 :
197 : /* Eval nginx variables in code path string first */
198 0 : if (ngx_http_complex_value(r, &llcf->header_filter_src, &eval_src)
199 : != NGX_OK)
200 : {
201 0 : return NGX_ERROR;
202 : }
203 :
204 0 : script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data,
205 : eval_src.len);
206 :
207 0 : if (script_path == NULL) {
208 0 : return NGX_ERROR;
209 : }
210 :
211 0 : L = ngx_http_lua_get_lua_vm(r, NULL);
212 :
213 : /* load Lua script file (w/ cache) sp = 1 */
214 0 : rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,
215 0 : llcf->header_filter_src_key);
216 0 : if (rc != NGX_OK) {
217 0 : return NGX_ERROR;
218 : }
219 :
220 : /* make sure we have a valid code chunk */
221 0 : ngx_http_lua_assert(lua_isfunction(L, -1));
222 :
223 0 : return ngx_http_lua_header_filter_by_chunk(L, r);
224 : }
225 :
226 :
227 : static ngx_int_t
228 0 : ngx_http_lua_header_filter(ngx_http_request_t *r)
229 : {
230 : ngx_http_lua_loc_conf_t *llcf;
231 : ngx_http_lua_ctx_t *ctx;
232 : ngx_int_t rc;
233 : ngx_http_cleanup_t *cln;
234 : uint16_t old_context;
235 :
236 0 : ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
237 : "lua header filter for user lua code, uri \"%V\"", &r->uri);
238 :
239 0 : llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
240 :
241 0 : if (llcf->body_filter_handler) {
242 0 : r->filter_need_in_memory = 1;
243 : }
244 :
245 0 : if (llcf->header_filter_handler == NULL) {
246 : dd("no header filter handler found");
247 0 : return ngx_http_next_header_filter(r);
248 : }
249 :
250 0 : ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
251 :
252 : dd("ctx = %p", ctx);
253 :
254 0 : if (ctx == NULL) {
255 0 : ctx = ngx_http_lua_create_ctx(r);
256 0 : if (ctx == NULL) {
257 0 : return NGX_ERROR;
258 : }
259 : }
260 :
261 0 : if (ctx->cleanup == NULL) {
262 0 : cln = ngx_http_cleanup_add(r, 0);
263 0 : if (cln == NULL) {
264 0 : return NGX_ERROR;
265 : }
266 :
267 0 : cln->handler = ngx_http_lua_request_cleanup_handler;
268 0 : cln->data = ctx;
269 0 : ctx->cleanup = &cln->handler;
270 : }
271 :
272 0 : old_context = ctx->context;
273 0 : ctx->context = NGX_HTTP_LUA_CONTEXT_HEADER_FILTER;
274 :
275 : dd("calling header filter handler");
276 0 : rc = llcf->header_filter_handler(r);
277 :
278 0 : ctx->context = old_context;
279 :
280 0 : if (rc == NGX_DECLINED) {
281 0 : return NGX_OK;
282 : }
283 :
284 0 : if (rc == NGX_AGAIN || rc == NGX_ERROR) {
285 0 : return rc;
286 : }
287 :
288 0 : return ngx_http_next_header_filter(r);
289 : }
290 :
291 :
292 : ngx_int_t
293 0 : ngx_http_lua_header_filter_init(void)
294 : {
295 : dd("calling header filter init");
296 0 : ngx_http_next_header_filter = ngx_http_top_header_filter;
297 0 : ngx_http_top_header_filter = ngx_http_lua_header_filter;
298 :
299 0 : return NGX_OK;
300 : }
301 :
302 : /* vi:set ft=c ts=4 sw=4 et fdm=marker: */
|