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_control.h"
15 : #include "ngx_http_lua_util.h"
16 : #include "ngx_http_lua_coroutine.h"
17 :
18 :
19 : static int ngx_http_lua_ngx_exec(lua_State *L);
20 : static int ngx_http_lua_ngx_redirect(lua_State *L);
21 : static int ngx_http_lua_ngx_exit(lua_State *L);
22 : static int ngx_http_lua_on_abort(lua_State *L);
23 :
24 :
25 : void
26 18 : ngx_http_lua_inject_control_api(ngx_log_t *log, lua_State *L)
27 : {
28 : /* ngx.redirect */
29 :
30 18 : lua_pushcfunction(L, ngx_http_lua_ngx_redirect);
31 18 : lua_setfield(L, -2, "redirect");
32 :
33 : /* ngx.exec */
34 :
35 18 : lua_pushcfunction(L, ngx_http_lua_ngx_exec);
36 18 : lua_setfield(L, -2, "exec");
37 :
38 18 : lua_pushcfunction(L, ngx_http_lua_ngx_exit);
39 18 : lua_setfield(L, -2, "throw_error"); /* deprecated */
40 :
41 : /* ngx.exit */
42 :
43 18 : lua_pushcfunction(L, ngx_http_lua_ngx_exit);
44 18 : lua_setfield(L, -2, "exit");
45 :
46 : /* ngx.on_abort */
47 :
48 18 : lua_pushcfunction(L, ngx_http_lua_on_abort);
49 18 : lua_setfield(L, -2, "on_abort");
50 18 : }
51 :
52 :
53 : static int
54 0 : ngx_http_lua_ngx_exec(lua_State *L)
55 : {
56 : int n;
57 : ngx_http_request_t *r;
58 : ngx_http_lua_ctx_t *ctx;
59 : ngx_str_t uri;
60 : ngx_str_t args, user_args;
61 : ngx_uint_t flags;
62 : u_char *p;
63 : u_char *q;
64 : size_t len;
65 : const char *msg;
66 :
67 0 : n = lua_gettop(L);
68 0 : if (n != 1 && n != 2) {
69 0 : return luaL_error(L, "expecting one or two arguments, but got %d",
70 : n);
71 : }
72 :
73 0 : r = ngx_http_lua_get_req(L);
74 0 : if (r == NULL) {
75 0 : return luaL_error(L, "no request object found");
76 : }
77 :
78 0 : ngx_str_null(&args);
79 :
80 : /* read the 1st argument (uri) */
81 :
82 0 : p = (u_char *) luaL_checklstring(L, 1, &len);
83 :
84 0 : if (len == 0) {
85 0 : return luaL_error(L, "The uri argument is empty");
86 : }
87 :
88 0 : uri.data = ngx_palloc(r->pool, len);
89 0 : if (uri.data == NULL) {
90 0 : return luaL_error(L, "no memory");
91 : }
92 :
93 0 : ngx_memcpy(uri.data, p, len);
94 :
95 0 : uri.len = len;
96 :
97 0 : ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
98 0 : if (ctx == NULL) {
99 0 : return luaL_error(L, "no ctx found");
100 : }
101 :
102 0 : ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE
103 : | NGX_HTTP_LUA_CONTEXT_ACCESS
104 : | NGX_HTTP_LUA_CONTEXT_CONTENT);
105 :
106 0 : ngx_http_lua_check_if_abortable(L, ctx);
107 :
108 0 : flags = NGX_HTTP_LOG_UNSAFE;
109 :
110 0 : if (ngx_http_parse_unsafe_uri(r, &uri, &args, &flags) != NGX_OK) {
111 0 : return luaL_error(L, "unsafe uri");
112 : }
113 :
114 0 : if (n == 2) {
115 : /* read the 2nd argument (args) */
116 : dd("args type: %s", luaL_typename(L, 2));
117 :
118 0 : switch (lua_type(L, 2)) {
119 0 : case LUA_TNUMBER:
120 : case LUA_TSTRING:
121 0 : p = (u_char *) lua_tolstring(L, 2, &len);
122 :
123 0 : user_args.data = ngx_palloc(r->pool, len);
124 0 : if (user_args.data == NULL) {
125 0 : return luaL_error(L, "no memory");
126 : }
127 :
128 0 : ngx_memcpy(user_args.data, p, len);
129 :
130 0 : user_args.len = len;
131 0 : break;
132 :
133 0 : case LUA_TTABLE:
134 0 : ngx_http_lua_process_args_option(r, L, 2, &user_args);
135 :
136 : dd("user_args: %.*s", (int) user_args.len, user_args.data);
137 :
138 0 : break;
139 :
140 0 : case LUA_TNIL:
141 0 : ngx_str_null(&user_args);
142 0 : break;
143 :
144 0 : default:
145 0 : msg = lua_pushfstring(L, "string, number, or table expected, "
146 : "but got %s", luaL_typename(L, 2));
147 0 : return luaL_argerror(L, 2, msg);
148 : }
149 :
150 : } else {
151 0 : user_args.data = NULL;
152 0 : user_args.len = 0;
153 : }
154 :
155 0 : if (user_args.len) {
156 0 : if (args.len == 0) {
157 0 : args = user_args;
158 :
159 : } else {
160 0 : p = ngx_palloc(r->pool, args.len + user_args.len + 1);
161 0 : if (p == NULL) {
162 0 : return luaL_error(L, "no memory");
163 : }
164 :
165 0 : q = ngx_copy(p, args.data, args.len);
166 0 : *q++ = '&';
167 0 : ngx_memcpy(q, user_args.data, user_args.len);
168 :
169 0 : args.data = p;
170 0 : args.len += user_args.len + 1;
171 : }
172 : }
173 :
174 0 : if (r->header_sent || ctx->header_sent) {
175 0 : return luaL_error(L, "attempt to call ngx.exec after "
176 : "sending out response headers");
177 : }
178 :
179 0 : ctx->exec_uri = uri;
180 0 : ctx->exec_args = args;
181 :
182 0 : ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
183 : "lua exec \"%V?%V\"",
184 : &ctx->exec_uri, &ctx->exec_args);
185 :
186 0 : return lua_yield(L, 0);
187 : }
188 :
189 :
190 : static int
191 0 : ngx_http_lua_ngx_redirect(lua_State *L)
192 : {
193 : ngx_http_lua_ctx_t *ctx;
194 : ngx_int_t rc;
195 : int n;
196 : u_char *p;
197 : u_char *uri;
198 : size_t len;
199 : ngx_table_elt_t *h;
200 : ngx_http_request_t *r;
201 :
202 0 : n = lua_gettop(L);
203 :
204 0 : if (n != 1 && n != 2) {
205 0 : return luaL_error(L, "expecting one or two arguments");
206 : }
207 :
208 0 : p = (u_char *) luaL_checklstring(L, 1, &len);
209 :
210 0 : if (n == 2) {
211 0 : rc = (ngx_int_t) luaL_checknumber(L, 2);
212 :
213 0 : if (rc != NGX_HTTP_MOVED_TEMPORARILY
214 0 : && rc != NGX_HTTP_MOVED_PERMANENTLY
215 0 : && rc != NGX_HTTP_SEE_OTHER
216 0 : && rc != NGX_HTTP_TEMPORARY_REDIRECT)
217 : {
218 0 : return luaL_error(L, "only ngx.HTTP_MOVED_TEMPORARILY, "
219 : "ngx.HTTP_MOVED_PERMANENTLY, "
220 : "ngx.HTTP_SEE_OTHER, and "
221 : "ngx.HTTP_TEMPORARY_REDIRECT are allowed");
222 : }
223 :
224 : } else {
225 0 : rc = NGX_HTTP_MOVED_TEMPORARILY;
226 : }
227 :
228 0 : r = ngx_http_lua_get_req(L);
229 0 : if (r == NULL) {
230 0 : return luaL_error(L, "no request object found");
231 : }
232 :
233 0 : ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
234 0 : if (ctx == NULL) {
235 0 : return luaL_error(L, "no request ctx found");
236 : }
237 :
238 0 : ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE
239 : | NGX_HTTP_LUA_CONTEXT_ACCESS
240 : | NGX_HTTP_LUA_CONTEXT_CONTENT);
241 :
242 0 : ngx_http_lua_check_if_abortable(L, ctx);
243 :
244 0 : if (r->header_sent || ctx->header_sent) {
245 0 : return luaL_error(L, "attempt to call ngx.redirect after sending out "
246 : "the headers");
247 : }
248 :
249 0 : uri = ngx_palloc(r->pool, len);
250 0 : if (uri == NULL) {
251 0 : return luaL_error(L, "no memory");
252 : }
253 :
254 0 : ngx_memcpy(uri, p, len);
255 :
256 0 : h = ngx_list_push(&r->headers_out.headers);
257 0 : if (h == NULL) {
258 0 : return luaL_error(L, "no memory");
259 : }
260 :
261 0 : h->hash = ngx_http_lua_location_hash;
262 :
263 : #if 0
264 : dd("location hash: %lu == %lu",
265 : (unsigned long) h->hash,
266 : (unsigned long) ngx_hash_key_lc((u_char *) "Location",
267 : sizeof("Location") - 1));
268 : #endif
269 :
270 0 : h->value.len = len;
271 0 : h->value.data = uri;
272 0 : ngx_str_set(&h->key, "Location");
273 :
274 0 : r->headers_out.status = rc;
275 :
276 0 : ctx->exit_code = rc;
277 0 : ctx->exited = 1;
278 :
279 0 : ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
280 : "lua redirect to \"%V\" with code %i",
281 : &h->value, ctx->exit_code);
282 :
283 0 : if (len && uri[0] != '/') {
284 0 : r->headers_out.location = h;
285 : }
286 :
287 : /*
288 : * we do not set r->headers_out.location here to avoid the handling
289 : * the local redirects without a host name by ngx_http_header_filter()
290 : */
291 :
292 0 : return lua_yield(L, 0);
293 : }
294 :
295 :
296 : static int
297 0 : ngx_http_lua_ngx_exit(lua_State *L)
298 : {
299 : ngx_int_t rc;
300 : ngx_http_request_t *r;
301 : ngx_http_lua_ctx_t *ctx;
302 :
303 0 : if (lua_gettop(L) != 1) {
304 0 : return luaL_error(L, "expecting one argument");
305 : }
306 :
307 0 : r = ngx_http_lua_get_req(L);
308 0 : if (r == NULL) {
309 0 : return luaL_error(L, "no request object found");
310 : }
311 :
312 0 : ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
313 0 : if (ctx == NULL) {
314 0 : return luaL_error(L, "no request ctx found");
315 : }
316 :
317 0 : ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE
318 : | NGX_HTTP_LUA_CONTEXT_ACCESS
319 : | NGX_HTTP_LUA_CONTEXT_CONTENT
320 : | NGX_HTTP_LUA_CONTEXT_TIMER
321 : | NGX_HTTP_LUA_CONTEXT_HEADER_FILTER
322 : | NGX_HTTP_LUA_CONTEXT_BALANCER
323 : | NGX_HTTP_LUA_CONTEXT_SSL_CERT
324 : | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE
325 : | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH);
326 :
327 0 : rc = (ngx_int_t) luaL_checkinteger(L, 1);
328 :
329 0 : if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT
330 : | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE
331 : | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH))
332 : {
333 :
334 : #if (NGX_HTTP_SSL)
335 :
336 0 : ctx->exit_code = rc;
337 0 : ctx->exited = 1;
338 :
339 0 : ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
340 : "lua exit with code %i", rc);
341 :
342 0 : if (ctx->context == NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE) {
343 0 : return 0;
344 : }
345 :
346 0 : return lua_yield(L, 0);
347 :
348 : #else
349 :
350 : return luaL_error(L, "no SSL support");
351 :
352 : #endif
353 : }
354 :
355 0 : if (ctx->no_abort
356 0 : && rc != NGX_ERROR
357 0 : && rc != NGX_HTTP_CLOSE
358 0 : && rc != NGX_HTTP_REQUEST_TIME_OUT
359 0 : && rc != NGX_HTTP_CLIENT_CLOSED_REQUEST)
360 : {
361 0 : return luaL_error(L, "attempt to abort with pending subrequests");
362 : }
363 :
364 0 : if ((r->header_sent || ctx->header_sent)
365 0 : && rc >= NGX_HTTP_SPECIAL_RESPONSE
366 0 : && rc != NGX_HTTP_REQUEST_TIME_OUT
367 0 : && rc != NGX_HTTP_CLIENT_CLOSED_REQUEST
368 0 : && rc != NGX_HTTP_CLOSE)
369 : {
370 0 : if (rc != (ngx_int_t) r->headers_out.status) {
371 0 : ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "attempt to "
372 : "set status %i via ngx.exit after sending out the "
373 : "response status %ui", rc, r->headers_out.status);
374 : }
375 :
376 0 : rc = NGX_HTTP_OK;
377 : }
378 :
379 : dd("setting exit code: %d", (int) rc);
380 :
381 0 : ctx->exit_code = rc;
382 0 : ctx->exited = 1;
383 :
384 0 : ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
385 : "lua exit with code %i", ctx->exit_code);
386 :
387 0 : if (ctx->context & (NGX_HTTP_LUA_CONTEXT_HEADER_FILTER
388 : | NGX_HTTP_LUA_CONTEXT_BALANCER))
389 : {
390 0 : return 0;
391 : }
392 :
393 : dd("calling yield");
394 0 : return lua_yield(L, 0);
395 : }
396 :
397 :
398 : static int
399 0 : ngx_http_lua_on_abort(lua_State *L)
400 : {
401 : ngx_http_request_t *r;
402 : ngx_http_lua_ctx_t *ctx;
403 0 : ngx_http_lua_co_ctx_t *coctx = NULL;
404 : ngx_http_lua_loc_conf_t *llcf;
405 :
406 0 : r = ngx_http_lua_get_req(L);
407 0 : if (r == NULL) {
408 0 : return luaL_error(L, "no request found");
409 : }
410 :
411 0 : ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
412 0 : if (ctx == NULL) {
413 0 : return luaL_error(L, "no request ctx found");
414 : }
415 :
416 0 : ngx_http_lua_check_fake_request2(L, r, ctx);
417 :
418 0 : if (ctx->on_abort_co_ctx) {
419 0 : lua_pushnil(L);
420 0 : lua_pushliteral(L, "duplicate call");
421 0 : return 2;
422 : }
423 :
424 0 : llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
425 0 : if (!llcf->check_client_abort) {
426 0 : lua_pushnil(L);
427 0 : lua_pushliteral(L, "lua_check_client_abort is off");
428 0 : return 2;
429 : }
430 :
431 0 : ngx_http_lua_coroutine_create_helper(L, r, ctx, &coctx);
432 :
433 0 : lua_pushlightuserdata(L, &ngx_http_lua_coroutines_key);
434 0 : lua_rawget(L, LUA_REGISTRYINDEX);
435 0 : lua_pushvalue(L, -2);
436 :
437 : dd("on_wait thread 1: %p", lua_tothread(L, -1));
438 :
439 0 : coctx->co_ref = luaL_ref(L, -2);
440 0 : lua_pop(L, 1);
441 :
442 0 : coctx->is_uthread = 1;
443 0 : ctx->on_abort_co_ctx = coctx;
444 :
445 : dd("on_wait thread 2: %p", coctx->co);
446 :
447 0 : coctx->co_status = NGX_HTTP_LUA_CO_SUSPENDED;
448 0 : coctx->parent_co_ctx = ctx->cur_co_ctx;
449 :
450 0 : lua_pushinteger(L, 1);
451 0 : return 1;
452 : }
453 :
454 :
455 : #ifndef NGX_LUA_NO_FFI_API
456 : int
457 0 : ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status, u_char *err,
458 : size_t *errlen)
459 : {
460 : ngx_http_lua_ctx_t *ctx;
461 :
462 0 : ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
463 0 : if (ctx == NULL) {
464 0 : *errlen = ngx_snprintf(err, *errlen, "no request ctx found") - err;
465 0 : return NGX_ERROR;
466 : }
467 :
468 0 : if (ngx_http_lua_ffi_check_context(ctx, NGX_HTTP_LUA_CONTEXT_REWRITE
469 : | NGX_HTTP_LUA_CONTEXT_ACCESS
470 : | NGX_HTTP_LUA_CONTEXT_CONTENT
471 : | NGX_HTTP_LUA_CONTEXT_TIMER
472 : | NGX_HTTP_LUA_CONTEXT_HEADER_FILTER
473 : | NGX_HTTP_LUA_CONTEXT_BALANCER
474 : | NGX_HTTP_LUA_CONTEXT_SSL_CERT
475 : | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE
476 : | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH,
477 : err, errlen)
478 : != NGX_OK)
479 : {
480 0 : return NGX_ERROR;
481 : }
482 :
483 0 : if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT
484 : | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE
485 : | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH))
486 : {
487 :
488 : #if (NGX_HTTP_SSL)
489 :
490 0 : ctx->exit_code = status;
491 0 : ctx->exited = 1;
492 :
493 0 : ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
494 : "lua exit with code %d", status);
495 :
496 0 : if (ctx->context == NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE) {
497 0 : return NGX_DONE;
498 : }
499 :
500 0 : return NGX_OK;
501 :
502 : #else
503 :
504 : return NGX_ERROR;
505 :
506 : #endif
507 : }
508 :
509 0 : if (ctx->no_abort
510 0 : && status != NGX_ERROR
511 0 : && status != NGX_HTTP_CLOSE
512 0 : && status != NGX_HTTP_REQUEST_TIME_OUT
513 0 : && status != NGX_HTTP_CLIENT_CLOSED_REQUEST)
514 : {
515 0 : *errlen = ngx_snprintf(err, *errlen,
516 : "attempt to abort with pending subrequests")
517 0 : - err;
518 0 : return NGX_ERROR;
519 : }
520 :
521 0 : if ((r->header_sent || ctx->header_sent)
522 0 : && status >= NGX_HTTP_SPECIAL_RESPONSE
523 0 : && status != NGX_HTTP_REQUEST_TIME_OUT
524 0 : && status != NGX_HTTP_CLIENT_CLOSED_REQUEST
525 0 : && status != NGX_HTTP_CLOSE)
526 : {
527 0 : if (status != (ngx_int_t) r->headers_out.status) {
528 0 : ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "attempt to "
529 : "set status %d via ngx.exit after sending out the "
530 : "response status %ui", status,
531 : r->headers_out.status);
532 : }
533 :
534 0 : status = NGX_HTTP_OK;
535 : }
536 :
537 0 : ctx->exit_code = status;
538 0 : ctx->exited = 1;
539 :
540 0 : ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
541 : "lua exit with code %i", ctx->exit_code);
542 :
543 0 : if (ctx->context & (NGX_HTTP_LUA_CONTEXT_HEADER_FILTER
544 : | NGX_HTTP_LUA_CONTEXT_BALANCER))
545 : {
546 0 : return NGX_DONE;
547 : }
548 :
549 0 : return NGX_OK;
550 : }
551 : #endif /* NGX_LUA_NO_FFI_API */
552 :
553 : /* vi:set ft=c ts=4 sw=4 et fdm=marker: */
|