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 "nginx.h"
15 #include "ngx_http_lua_directive.h"
16 #include "ngx_http_lua_util.h"
17 #include "ngx_http_lua_exception.h"
18 #include "ngx_http_lua_pcrefix.h"
19 #include "ngx_http_lua_args.h"
20 #include "ngx_http_lua_uri.h"
21 #include "ngx_http_lua_req_body.h"
22 #include "ngx_http_lua_headers.h"
23 #include "ngx_http_lua_output.h"
24 #include "ngx_http_lua_control.h"
25 #include "ngx_http_lua_ndk.h"
26 #include "ngx_http_lua_subrequest.h"
27 #include "ngx_http_lua_log.h"
28 #include "ngx_http_lua_string.h"
29 #include "ngx_http_lua_misc.h"
30 #include "ngx_http_lua_consts.h"
31 #include "ngx_http_lua_shdict.h"
32 #include "ngx_http_lua_coroutine.h"
33 #include "ngx_http_lua_socket_tcp.h"
34 #include "ngx_http_lua_socket_udp.h"
35 #include "ngx_http_lua_sleep.h"
36 #include "ngx_http_lua_setby.h"
37 #include "ngx_http_lua_headerfilterby.h"
38 #include "ngx_http_lua_bodyfilterby.h"
39 #include "ngx_http_lua_logby.h"
40 #include "ngx_http_lua_probe.h"
41 #include "ngx_http_lua_uthread.h"
42 #include "ngx_http_lua_contentby.h"
43 #include "ngx_http_lua_timer.h"
44 #include "ngx_http_lua_config.h"
45 #include "ngx_http_lua_socket_tcp.h"
46 #include "ngx_http_lua_ssl_certby.h"
47 #include "ngx_http_lua_ssl.h"
48 #include "ngx_http_lua_log_ringbuf.h"
49 
50 
51 #if 1
52 #undef ngx_http_lua_probe_info
53 #define ngx_http_lua_probe_info(msg)
54 #endif
55 
56 
57 #ifndef NGX_HTTP_LUA_BT_DEPTH
58 #define NGX_HTTP_LUA_BT_DEPTH  22
59 #endif
60 
61 
62 #ifndef NGX_HTTP_LUA_BT_MAX_COROS
63 #define NGX_HTTP_LUA_BT_MAX_COROS  5
64 #endif
65 
66 
67 #if (NGX_HTTP_LUA_HAVE_SA_RESTART)
68 #define NGX_HTTP_LUA_SA_RESTART_SIGS {                                       \
69     ngx_signal_value(NGX_RECONFIGURE_SIGNAL),                                \
70     ngx_signal_value(NGX_REOPEN_SIGNAL),                                     \
71     ngx_signal_value(NGX_NOACCEPT_SIGNAL),                                   \
72     ngx_signal_value(NGX_TERMINATE_SIGNAL),                                  \
73     ngx_signal_value(NGX_SHUTDOWN_SIGNAL),                                   \
74     ngx_signal_value(NGX_CHANGEBIN_SIGNAL),                                  \
75     SIGALRM,                                                                 \
76     SIGINT,                                                                  \
77     SIGIO,                                                                   \
78     SIGCHLD,                                                                 \
79     SIGSYS,                                                                  \
80     SIGPIPE,                                                                 \
81     0                                                                        \
82 };
83 #endif
84 
85 
86 char ngx_http_lua_code_cache_key;
87 char ngx_http_lua_socket_pool_key;
88 char ngx_http_lua_coroutines_key;
89 char ngx_http_lua_headers_metatable_key;
90 
91 
92 ngx_uint_t  ngx_http_lua_location_hash = 0;
93 ngx_uint_t  ngx_http_lua_content_length_hash = 0;
94 
95 
96 static ngx_int_t ngx_http_lua_send_http10_headers(ngx_http_request_t *r,
97     ngx_http_lua_ctx_t *ctx);
98 static void ngx_http_lua_init_registry(lua_State *L, ngx_log_t *log);
99 static void ngx_http_lua_init_globals(lua_State *L, ngx_cycle_t *cycle,
100     ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log);
101 #ifdef OPENRESTY_LUAJIT
102 static void ngx_http_lua_inject_global_write_guard(lua_State *L,
103     ngx_log_t *log);
104 #endif
105 static void ngx_http_lua_set_path(ngx_cycle_t *cycle, lua_State *L, int tab_idx,
106     const char *fieldname, const char *path, const char *default_path,
107     ngx_log_t *log);
108 static ngx_int_t ngx_http_lua_handle_exec(lua_State *L, ngx_http_request_t *r,
109     ngx_http_lua_ctx_t *ctx);
110 static ngx_int_t ngx_http_lua_handle_exit(lua_State *L, ngx_http_request_t *r,
111     ngx_http_lua_ctx_t *ctx);
112 static ngx_int_t ngx_http_lua_handle_rewrite_jump(lua_State *L,
113     ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx);
114 static int ngx_http_lua_thread_traceback(lua_State *L, lua_State *co,
115     ngx_http_lua_co_ctx_t *coctx);
116 static void ngx_http_lua_inject_ngx_api(lua_State *L,
117     ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log);
118 static void ngx_http_lua_inject_arg_api(lua_State *L);
119 static int ngx_http_lua_param_get(lua_State *L);
120 static int ngx_http_lua_param_set(lua_State *L);
121 static ngx_int_t ngx_http_lua_output_filter(ngx_http_request_t *r,
122     ngx_chain_t *in);
123 static ngx_int_t ngx_http_lua_send_special(ngx_http_request_t *r,
124     ngx_uint_t flags);
125 static void ngx_http_lua_finalize_threads(ngx_http_request_t *r,
126     ngx_http_lua_ctx_t *ctx, lua_State *L);
127 static ngx_int_t ngx_http_lua_post_zombie_thread(ngx_http_request_t *r,
128     ngx_http_lua_co_ctx_t *parent, ngx_http_lua_co_ctx_t *thread);
129 static void ngx_http_lua_cleanup_zombie_child_uthreads(ngx_http_request_t *r,
130     lua_State *L, ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t *coctx);
131 static ngx_int_t ngx_http_lua_on_abort_resume(ngx_http_request_t *r);
132 static void ngx_http_lua_close_fake_request(ngx_http_request_t *r);
133 static ngx_int_t ngx_http_lua_flush_pending_output(ngx_http_request_t *r,
134     ngx_http_lua_ctx_t *ctx);
135 static ngx_int_t
136     ngx_http_lua_process_flushing_coroutines(ngx_http_request_t *r,
137     ngx_http_lua_ctx_t *ctx);
138 static lua_State *ngx_http_lua_new_state(lua_State *parent_vm,
139     ngx_cycle_t *cycle, ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log);
140 static int ngx_http_lua_get_raw_phase_context(lua_State *L);
141 
142 
143 #ifndef LUA_PATH_SEP
144 #define LUA_PATH_SEP ";"
145 #endif
146 
147 
148 #if !defined(LUA_DEFAULT_PATH) && (NGX_DEBUG)
149 #define LUA_DEFAULT_PATH "../lua-resty-core/lib/?.lua;"                      \
150                          "../lua-resty-lrucache/lib/?.lua"
151 #endif
152 
153 
154 #define AUX_MARK "\1"
155 
156 
157 static void
ngx_http_lua_set_path(ngx_cycle_t * cycle,lua_State * L,int tab_idx,const char * fieldname,const char * path,const char * default_path,ngx_log_t * log)158 ngx_http_lua_set_path(ngx_cycle_t *cycle, lua_State *L, int tab_idx,
159     const char *fieldname, const char *path, const char *default_path,
160     ngx_log_t *log)
161 {
162     const char          *tmp_path;
163     const char          *prefix;
164 
165     /* XXX here we use some hack to simplify string manipulation */
166     tmp_path = luaL_gsub(L, path, LUA_PATH_SEP LUA_PATH_SEP,
167                          LUA_PATH_SEP AUX_MARK LUA_PATH_SEP);
168 
169     lua_pushlstring(L, (char *) cycle->prefix.data, cycle->prefix.len);
170     prefix = lua_tostring(L, -1);
171     tmp_path = luaL_gsub(L, tmp_path, "$prefix", prefix);
172     tmp_path = luaL_gsub(L, tmp_path, "${prefix}", prefix);
173     lua_pop(L, 3);
174 
175     dd("tmp_path path: %s", tmp_path);
176 
177 #if (NGX_DEBUG)
178     tmp_path =
179 #else
180     (void)
181 #endif
182         luaL_gsub(L, tmp_path, AUX_MARK, default_path);
183 
184 #if (NGX_DEBUG)
185     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,
186                    "lua setting lua package.%s to \"%s\"", fieldname, tmp_path);
187 #endif
188 
189     lua_remove(L, -2);
190 
191     /* fix negative index as there's new data on stack */
192     tab_idx = (tab_idx < 0) ? (tab_idx - 1) : tab_idx;
193     lua_setfield(L, tab_idx, fieldname);
194 }
195 
196 
197 #ifndef OPENRESTY_LUAJIT
198 /**
199  * Create new table and set _G field to itself.
200  *
201  * After:
202  *         | new table | <- top
203  *         |    ...    |
204  * */
205 void
ngx_http_lua_create_new_globals_table(lua_State * L,int narr,int nrec)206 ngx_http_lua_create_new_globals_table(lua_State *L, int narr, int nrec)
207 {
208     lua_createtable(L, narr, nrec + 1);
209     lua_pushvalue(L, -1);
210     lua_setfield(L, -2, "_G");
211 }
212 #endif /* OPENRESTY_LUAJIT */
213 
214 
215 static lua_State *
ngx_http_lua_new_state(lua_State * parent_vm,ngx_cycle_t * cycle,ngx_http_lua_main_conf_t * lmcf,ngx_log_t * log)216 ngx_http_lua_new_state(lua_State *parent_vm, ngx_cycle_t *cycle,
217     ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log)
218 {
219     lua_State       *L;
220     const char      *old_path;
221     const char      *new_path;
222     size_t           old_path_len;
223     const char      *old_cpath;
224     const char      *new_cpath;
225     size_t           old_cpath_len;
226 
227     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "lua creating new vm state");
228 
229     L = luaL_newstate();
230     if (L == NULL) {
231         return NULL;
232     }
233 
234     luaL_openlibs(L);
235 
236     lua_getglobal(L, "package");
237 
238     if (!lua_istable(L, -1)) {
239         ngx_log_error(NGX_LOG_EMERG, log, 0,
240                       "the \"package\" table does not exist");
241         return NULL;
242     }
243 
244     if (parent_vm) {
245         lua_getglobal(parent_vm, "package");
246         lua_getfield(parent_vm, -1, "path");
247         old_path = lua_tolstring(parent_vm, -1, &old_path_len);
248         lua_pop(parent_vm, 1);
249 
250         lua_pushlstring(L, old_path, old_path_len);
251         lua_setfield(L, -2, "path");
252 
253         lua_getfield(parent_vm, -1, "cpath");
254         old_path = lua_tolstring(parent_vm, -1, &old_path_len);
255         lua_pop(parent_vm, 2);
256 
257         lua_pushlstring(L, old_path, old_path_len);
258         lua_setfield(L, -2, "cpath");
259 
260     } else {
261 #ifdef LUA_DEFAULT_PATH
262 #   define LUA_DEFAULT_PATH_LEN (sizeof(LUA_DEFAULT_PATH) - 1)
263         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
264                        "lua prepending default package.path with %s",
265                        LUA_DEFAULT_PATH);
266 
267         lua_pushliteral(L, LUA_DEFAULT_PATH ";"); /* package default */
268         lua_getfield(L, -2, "path"); /* package default old */
269         lua_concat(L, 2); /* package new */
270         lua_setfield(L, -2, "path"); /* package */
271 #endif
272 
273 #ifdef LUA_DEFAULT_CPATH
274 #   define LUA_DEFAULT_CPATH_LEN (sizeof(LUA_DEFAULT_CPATH) - 1)
275         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
276                        "lua prepending default package.cpath with %s",
277                        LUA_DEFAULT_CPATH);
278 
279         lua_pushliteral(L, LUA_DEFAULT_CPATH ";"); /* package default */
280         lua_getfield(L, -2, "cpath"); /* package default old */
281         old_cpath = lua_tolstring(L, -1, &old_cpath_len);
282         lua_concat(L, 2); /* package new */
283         lua_setfield(L, -2, "cpath"); /* package */
284 #endif
285 
286         if (lmcf->lua_path.len != 0) {
287             lua_getfield(L, -1, "path"); /* get original package.path */
288             old_path = lua_tolstring(L, -1, &old_path_len);
289 
290             dd("old path: %s", old_path);
291 
292             lua_pushlstring(L, (char *) lmcf->lua_path.data,
293                             lmcf->lua_path.len);
294             new_path = lua_tostring(L, -1);
295 
296             ngx_http_lua_set_path(cycle, L, -3, "path", new_path, old_path,
297                                   log);
298 
299             lua_pop(L, 2);
300         }
301 
302         if (lmcf->lua_cpath.len != 0) {
303             lua_getfield(L, -1, "cpath"); /* get original package.cpath */
304             old_cpath = lua_tolstring(L, -1, &old_cpath_len);
305 
306             dd("old cpath: %s", old_cpath);
307 
308             lua_pushlstring(L, (char *) lmcf->lua_cpath.data,
309                             lmcf->lua_cpath.len);
310             new_cpath = lua_tostring(L, -1);
311 
312             ngx_http_lua_set_path(cycle, L, -3, "cpath", new_cpath, old_cpath,
313                                   log);
314 
315 
316             lua_pop(L, 2);
317         }
318     }
319 
320     lua_pop(L, 1); /* remove the "package" table */
321 
322     ngx_http_lua_init_registry(L, log);
323     ngx_http_lua_init_globals(L, cycle, lmcf, log);
324 
325     return L;
326 }
327 
328 
329 lua_State *
ngx_http_lua_new_thread(ngx_http_request_t * r,lua_State * L,int * ref)330 ngx_http_lua_new_thread(ngx_http_request_t *r, lua_State *L, int *ref)
331 {
332     int              base;
333     lua_State       *co;
334 
335 #ifdef HAVE_LUA_RESETTHREAD
336     ngx_queue_t     *q;
337 
338     ngx_http_lua_main_conf_t    *lmcf;
339     ngx_http_lua_thread_ref_t   *tref;
340 
341     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
342                    "lua creating new thread");
343 
344     lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
345 
346     if (L == lmcf->lua && !ngx_queue_empty(&lmcf->cached_lua_threads)) {
347         q = ngx_queue_head(&lmcf->cached_lua_threads);
348         tref = ngx_queue_data(q, ngx_http_lua_thread_ref_t, queue);
349 
350         ngx_http_lua_assert(tref->ref != LUA_NOREF);
351         ngx_http_lua_assert(tref->co != NULL);
352 
353         co = tref->co;
354         *ref = tref->ref;
355 
356         tref->co = NULL;
357         tref->ref = LUA_NOREF;
358 
359         ngx_queue_remove(q);
360         ngx_queue_insert_head(&lmcf->free_lua_threads, q);
361 
362         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
363                        "lua reusing cached lua thread %p (ref %d)", co, *ref);
364 
365 #if 0
366         {
367             int n = 0;
368             lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(
369                                   coroutines_key));
370             lua_rawget(L, LUA_REGISTRYINDEX);
371             lua_pushnil(L);  /* first key */
372             while (lua_next(L, -2) != 0) {
373                 if (!lua_isnil(L, -1) && !lua_isnil(L, -2)) {
374                     n++;
375                 }
376                 lua_pop(L, 1);
377             }
378             lua_pop(L, 1);
379 
380             ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
381                            "! lua reusing cached lua thread %p (ref %d, n %d)",
382                            co, *ref, n);
383         }
384 #endif
385 
386     } else {
387 #else
388     {
389 #endif
390         base = lua_gettop(L);
391 
392         lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(
393                               coroutines_key));
394         lua_rawget(L, LUA_REGISTRYINDEX);
395 
396         co = lua_newthread(L);
397 
398 #ifndef OPENRESTY_LUAJIT
399         /*  {{{ inherit coroutine's globals to main thread's globals table
400          *  for print() function will try to find tostring() in current
401          *  globals table.
402          */
403         /*  new globals table for coroutine */
404         ngx_http_lua_create_new_globals_table(co, 0, 0);
405 
406         lua_createtable(co, 0, 1);
407         ngx_http_lua_get_globals_table(co);
408         lua_setfield(co, -2, "__index");
409         lua_setmetatable(co, -2);
410 
411         ngx_http_lua_set_globals_table(co);
412         /*  }}} */
413 #endif /* OPENRESTY_LUAJIT */
414 
415         *ref = luaL_ref(L, -2);
416 
417         ngx_log_debug2(NGX_LOG_DEBUG_HTTP,
418                        ngx_cycle->log, 0, "lua ref lua thread %p (ref %d)", co,
419                        *ref);
420 
421         if (*ref == LUA_NOREF) {
422             lua_settop(L, base);  /* restore main thread stack */
423             return NULL;
424         }
425 
426         lua_settop(L, base);
427     }
428 
429     return co;
430 }
431 
432 
433 void
434 ngx_http_lua_del_thread(ngx_http_request_t *r, lua_State *L,
435     ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t *coctx)
436 {
437 #ifdef HAVE_LUA_RESETTHREAD
438     ngx_queue_t                 *q;
439     ngx_http_lua_main_conf_t    *lmcf;
440     ngx_http_lua_thread_ref_t   *tref;
441 #endif
442 
443     if (coctx->co_ref == LUA_NOREF) {
444         return;
445     }
446 
447     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
448                    "lua deleting light thread %p (ref %d)", coctx->co,
449                    coctx->co_ref);
450 
451     ngx_http_lua_probe_thread_delete(r, coctx->co, ctx);
452 
453 #ifdef HAVE_LUA_RESETTHREAD
454     lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
455 
456     if (ctx != NULL
457         && coctx->co == ctx->entry_co_ctx.co
458         && L == lmcf->lua && !ngx_queue_empty(&lmcf->free_lua_threads))
459     {
460         lua_resetthread(L, coctx->co);
461         q = ngx_queue_head(&lmcf->free_lua_threads);
462         tref = ngx_queue_data(q, ngx_http_lua_thread_ref_t, queue);
463 
464         ngx_http_lua_assert(tref->ref == LUA_NOREF);
465         ngx_http_lua_assert(tref->co == NULL);
466 
467         tref->ref = coctx->co_ref;
468         tref->co = coctx->co;
469 
470         ngx_queue_remove(q);
471         ngx_queue_insert_head(&lmcf->cached_lua_threads, q);
472 
473         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
474                        "lua caching unused lua thread %p (ref %d)", coctx->co,
475                        coctx->co_ref);
476 
477     } else {
478 #endif
479         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
480                        "http lua unref thread %p: %d", coctx->co,
481                        coctx->co_ref);
482 
483         lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(
484                               coroutines_key));
485         lua_rawget(L, LUA_REGISTRYINDEX);
486         luaL_unref(L, -1, coctx->co_ref);
487         lua_pop(L, 1);
488 #ifdef HAVE_LUA_RESETTHREAD
489     }
490 #endif
491 
492     coctx->co_ref = LUA_NOREF;
493     coctx->co_status = NGX_HTTP_LUA_CO_DEAD;
494 }
495 
496 
497 u_char *
498 ngx_http_lua_rebase_path(ngx_pool_t *pool, u_char *src, size_t len)
499 {
500     u_char     *p;
501     ngx_str_t   dst;
502 
503     dst.data = ngx_palloc(pool, len + 1);
504     if (dst.data == NULL) {
505         return NULL;
506     }
507 
508     dst.len = len;
509 
510     p = ngx_copy(dst.data, src, len);
511     *p = '\0';
512 
513     if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->prefix, &dst)
514         != NGX_OK)
515     {
516         return NULL;
517     }
518 
519     return dst.data;
520 }
521 
522 
523 ngx_int_t
524 ngx_http_lua_send_header_if_needed(ngx_http_request_t *r,
525     ngx_http_lua_ctx_t *ctx)
526 {
527     ngx_int_t            rc;
528 
529     dd("send header if needed: %d", r->header_sent || ctx->header_sent);
530 
531     if (!r->header_sent && !ctx->header_sent) {
532         if (r->headers_out.status == 0) {
533             r->headers_out.status = NGX_HTTP_OK;
534         }
535 
536         if (!ctx->mime_set
537             && ngx_http_lua_set_content_type(r, ctx) != NGX_OK)
538         {
539             return NGX_ERROR;
540         }
541 
542         if (!ctx->headers_set) {
543             ngx_http_clear_content_length(r);
544             ngx_http_clear_accept_ranges(r);
545         }
546 
547         if (!ctx->buffering) {
548             dd("sending headers");
549             rc = ngx_http_send_header(r);
550             ctx->header_sent = 1;
551             return rc;
552         }
553     }
554 
555     return NGX_OK;
556 }
557 
558 
559 ngx_int_t
560 ngx_http_lua_send_chain_link(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx,
561     ngx_chain_t *in)
562 {
563     ngx_int_t                     rc;
564     ngx_chain_t                  *cl;
565     ngx_chain_t                 **ll;
566     ngx_http_lua_loc_conf_t      *llcf;
567 
568 #if 1
569     if (ctx->acquired_raw_req_socket || ctx->eof) {
570         dd("ctx->eof already set or raw req socket already acquired");
571         return NGX_OK;
572     }
573 #endif
574 
575     if ((r->method & NGX_HTTP_HEAD) && !r->header_only) {
576         r->header_only = 1;
577     }
578 
579     llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
580 
581     if (llcf->http10_buffering
582         && !ctx->buffering
583         && !r->header_sent
584         && !ctx->header_sent
585         && r->http_version < NGX_HTTP_VERSION_11
586         && r->headers_out.content_length_n < 0)
587     {
588         ctx->buffering = 1;
589     }
590 
591     rc = ngx_http_lua_send_header_if_needed(r, ctx);
592 
593     if (rc == NGX_ERROR || rc > NGX_OK) {
594         return rc;
595     }
596 
597     if (r->header_only) {
598         ctx->eof = 1;
599 
600         if (ctx->buffering) {
601             return ngx_http_lua_send_http10_headers(r, ctx);
602         }
603 
604         return rc;
605     }
606 
607     if (in == NULL) {
608         dd("last buf to be sent");
609 
610 #if 1
611         if (!r->request_body && r == r->main) {
612             if (ngx_http_discard_request_body(r) != NGX_OK) {
613                 return NGX_ERROR;
614             }
615         }
616 #endif
617 
618         if (ctx->buffering) {
619             rc = ngx_http_lua_send_http10_headers(r, ctx);
620             if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
621                 return rc;
622             }
623 
624             if (ctx->out) {
625 
626                 rc = ngx_http_lua_output_filter(r, ctx->out);
627 
628                 if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
629                     return rc;
630                 }
631 
632                 ctx->out = NULL;
633             }
634         }
635 
636         ctx->eof = 1;
637 
638         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
639                        "lua sending last buf of the response body");
640 
641         rc = ngx_http_lua_send_special(r, NGX_HTTP_LAST);
642 
643         if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
644             return rc;
645         }
646 
647         return NGX_OK;
648     }
649 
650     /* in != NULL */
651 
652     if (ctx->buffering) {
653         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
654                        "lua buffering output bufs for the HTTP 1.0 request");
655 
656         for (cl = ctx->out, ll = &ctx->out; cl; cl = cl->next) {
657             ll = &cl->next;
658         }
659 
660         *ll = in;
661 
662         return NGX_OK;
663     }
664 
665     return ngx_http_lua_output_filter(r, in);
666 }
667 
668 
669 static ngx_int_t
670 ngx_http_lua_send_special(ngx_http_request_t *r, ngx_uint_t flags)
671 {
672     ngx_int_t            rc;
673     ngx_http_request_t  *ar; /* active request */
674 
675     ar = r->connection->data;
676 
677     if (ar != r) {
678 
679         /* bypass ngx_http_postpone_filter_module */
680 
681         r->connection->data = r;
682         rc = ngx_http_send_special(r, flags);
683         r->connection->data = ar;
684         return rc;
685     }
686 
687     return ngx_http_send_special(r, flags);
688 }
689 
690 
691 static ngx_int_t
692 ngx_http_lua_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
693 {
694     ngx_int_t            rc;
695     ngx_http_lua_ctx_t  *ctx;
696     ngx_http_request_t  *ar; /* active request */
697 
698     ar = r->connection->data;
699 
700     if (ar != r) {
701 
702         /* bypass ngx_http_postpone_filter_module */
703 
704         r->connection->data = r;
705         rc = ngx_http_output_filter(r, in);
706         r->connection->data = ar;
707         return rc;
708     }
709 
710     rc = ngx_http_output_filter(r, in);
711 
712     if (rc == NGX_ERROR) {
713         return NGX_ERROR;
714     }
715 
716     ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
717 
718     ngx_chain_update_chains(r->pool,
719                             &ctx->free_bufs, &ctx->busy_bufs, &in,
720                             (ngx_buf_tag_t) &ngx_http_lua_module);
721 
722     return rc;
723 }
724 
725 
726 static ngx_int_t
727 ngx_http_lua_send_http10_headers(ngx_http_request_t *r,
728     ngx_http_lua_ctx_t *ctx)
729 {
730     off_t                size;
731     ngx_chain_t         *cl;
732     ngx_int_t            rc;
733 
734     if (r->header_sent || ctx->header_sent) {
735         return NGX_OK;
736     }
737 
738     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
739                    "lua sending HTTP 1.0 response headers");
740 
741     if (r->header_only) {
742         goto send;
743     }
744 
745     if (r->headers_out.content_length == NULL) {
746         for (size = 0, cl = ctx->out; cl; cl = cl->next) {
747             size += ngx_buf_size(cl->buf);
748         }
749 
750         r->headers_out.content_length_n = size;
751 
752         if (r->headers_out.content_length) {
753             r->headers_out.content_length->hash = 0;
754         }
755     }
756 
757 send:
758 
759     rc = ngx_http_send_header(r);
760     ctx->header_sent = 1;
761     return rc;
762 }
763 
764 
765 static void
766 ngx_http_lua_init_registry(lua_State *L, ngx_log_t *log)
767 {
768     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0,
769                    "lua initializing lua registry");
770 
771     /* {{{ register a table to anchor lua coroutines reliably:
772      * {([int]ref) = [cort]} */
773     lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(
774                           coroutines_key));
775     lua_createtable(L, 0, 32 /* nrec */);
776     lua_rawset(L, LUA_REGISTRYINDEX);
777     /* }}} */
778 
779     /* create the registry entry for the Lua request ctx data table */
780     lua_pushliteral(L, ngx_http_lua_ctx_tables_key);
781     lua_createtable(L, 0, 32 /* nrec */);
782     lua_rawset(L, LUA_REGISTRYINDEX);
783 
784     /* create the registry entry for the Lua socket connection pool table */
785     lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(
786                           socket_pool_key));
787     lua_createtable(L, 0, 8 /* nrec */);
788     lua_rawset(L, LUA_REGISTRYINDEX);
789 
790     /* {{{ register table to cache user code:
791      * { [(string)cache_key] = <code closure> } */
792     lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(
793                           code_cache_key));
794     lua_createtable(L, 0, 8 /* nrec */);
795     lua_rawset(L, LUA_REGISTRYINDEX);
796     /* }}} */
797 }
798 
799 
800 static void
801 ngx_http_lua_init_globals(lua_State *L, ngx_cycle_t *cycle,
802     ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log)
803 {
804     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0,
805                    "lua initializing lua globals");
806 
807 #if defined(NDK) && NDK
808     ngx_http_lua_inject_ndk_api(L);
809 #endif /* defined(NDK) && NDK */
810 
811     ngx_http_lua_inject_ngx_api(L, lmcf, log);
812 }
813 
814 
815 static void
816 ngx_http_lua_inject_ngx_api(lua_State *L, ngx_http_lua_main_conf_t *lmcf,
817     ngx_log_t *log)
818 {
819     lua_createtable(L, 0 /* narr */, 113 /* nrec */);    /* ngx.* */
820 
821     lua_pushcfunction(L, ngx_http_lua_get_raw_phase_context);
822     lua_setfield(L, -2, "_phase_ctx");
823 
824     ngx_http_lua_inject_arg_api(L);
825 
826     ngx_http_lua_inject_http_consts(L);
827     ngx_http_lua_inject_core_consts(L);
828 
829     ngx_http_lua_inject_log_api(L);
830     ngx_http_lua_inject_output_api(L);
831     ngx_http_lua_inject_string_api(L);
832     ngx_http_lua_inject_control_api(log, L);
833     ngx_http_lua_inject_subrequest_api(L);
834     ngx_http_lua_inject_sleep_api(L);
835 
836     ngx_http_lua_inject_req_api(log, L);
837     ngx_http_lua_inject_resp_header_api(L);
838     ngx_http_lua_create_headers_metatable(log, L);
839     ngx_http_lua_inject_shdict_api(lmcf, L);
840     ngx_http_lua_inject_socket_tcp_api(log, L);
841     ngx_http_lua_inject_socket_udp_api(log, L);
842     ngx_http_lua_inject_uthread_api(log, L);
843     ngx_http_lua_inject_timer_api(L);
844     ngx_http_lua_inject_config_api(L);
845 
846     lua_getglobal(L, "package"); /* ngx package */
847     lua_getfield(L, -1, "loaded"); /* ngx package loaded */
848     lua_pushvalue(L, -3); /* ngx package loaded ngx */
849     lua_setfield(L, -2, "ngx"); /* ngx package loaded */
850     lua_pop(L, 2);
851 
852     lua_setglobal(L, "ngx");
853 
854     ngx_http_lua_inject_coroutine_api(log, L);
855 }
856 
857 
858 #ifdef OPENRESTY_LUAJIT
859 static void
860 ngx_http_lua_inject_global_write_guard(lua_State *L, ngx_log_t *log)
861 {
862     int         rc;
863 
864     const char buf[] =
865         "local ngx_log = ngx.log\n"
866         "local ngx_WARN = ngx.WARN\n"
867         "local tostring = tostring\n"
868         "local ngx_get_phase = ngx.get_phase\n"
869         "local traceback = require 'debug'.traceback\n"
870         "local function newindex(table, key, value)\n"
871             "rawset(table, key, value)\n"
872             "local phase = ngx_get_phase()\n"
873             "if phase == 'init_worker' or phase == 'init' then\n"
874                 "return\n"
875             "end\n"
876             "ngx_log(ngx_WARN, 'writing a global Lua variable "
877                      "(\\'', tostring(key), '\\') which may lead to "
878                      "race conditions between concurrent requests, so "
879                      "prefer the use of \\'local\\' variables', "
880                      "traceback('', 2))\n"
881         "end\n"
882         "setmetatable(_G, { __newindex = newindex })\n"
883         ;
884 
885     rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, "=_G write guard");
886 
887     if (rc != 0) {
888         ngx_log_error(NGX_LOG_ERR, log, 0,
889                       "failed to load Lua code (%i): %s",
890                       rc, lua_tostring(L, -1));
891 
892         lua_pop(L, 1);
893         return;
894     }
895 
896     rc = lua_pcall(L, 0, 0, 0);
897     if (rc != 0) {
898         ngx_log_error(NGX_LOG_ERR, log, 0,
899                       "failed to run Lua code (%i): %s",
900                       rc, lua_tostring(L, -1));
901         lua_pop(L, 1);
902     }
903 }
904 #endif
905 
906 
907 void
908 ngx_http_lua_discard_bufs(ngx_pool_t *pool, ngx_chain_t *in)
909 {
910     ngx_chain_t         *cl;
911 
912     for (cl = in; cl; cl = cl->next) {
913         cl->buf->pos = cl->buf->last;
914         cl->buf->file_pos = cl->buf->file_last;
915     }
916 }
917 
918 
919 ngx_int_t
920 ngx_http_lua_add_copy_chain(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx,
921     ngx_chain_t ***plast, ngx_chain_t *in, ngx_int_t *eof)
922 {
923     ngx_chain_t     *cl;
924     size_t           len;
925     ngx_buf_t       *b;
926 
927     len = 0;
928     *eof = 0;
929 
930     for (cl = in; cl; cl = cl->next) {
931         if (ngx_buf_in_memory(cl->buf)) {
932             len += cl->buf->last - cl->buf->pos;
933         }
934 
935         if (cl->buf->last_in_chain || cl->buf->last_buf) {
936             *eof = 1;
937         }
938     }
939 
940     if (len == 0) {
941         return NGX_OK;
942     }
943 
944     cl = ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool,
945                                          &ctx->free_bufs, len);
946     if (cl == NULL) {
947         return NGX_ERROR;
948     }
949 
950     dd("chains get free buf: %d == %d", (int) (cl->buf->end - cl->buf->start),
951        (int) len);
952 
953     b = cl->buf;
954 
955     while (in) {
956         if (ngx_buf_in_memory(in->buf)) {
957             b->last = ngx_copy(b->last, in->buf->pos,
958                                in->buf->last - in->buf->pos);
959         }
960 
961         in = in->next;
962     }
963 
964     **plast = cl;
965     *plast = &cl->next;
966 
967     return NGX_OK;
968 }
969 
970 
971 void
972 ngx_http_lua_reset_ctx(ngx_http_request_t *r, lua_State *L,
973     ngx_http_lua_ctx_t *ctx)
974 {
975     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
976                    "lua reset ctx");
977 
978     ngx_http_lua_finalize_threads(r, ctx, L);
979 
980 #if 0
981     if (ctx->user_co_ctx) {
982         /* no way to destroy a list but clean up the whole pool */
983         ctx->user_co_ctx = NULL;
984     }
985 #endif
986 
987     ngx_memzero(&ctx->entry_co_ctx, sizeof(ngx_http_lua_co_ctx_t));
988 
989     ctx->entry_co_ctx.next_zombie_child_thread =
990         &ctx->entry_co_ctx.zombie_child_threads;
991 
992     ctx->entry_co_ctx.co_ref = LUA_NOREF;
993 
994     ctx->entered_rewrite_phase = 0;
995     ctx->entered_access_phase = 0;
996     ctx->entered_content_phase = 0;
997 
998     ctx->exit_code = 0;
999     ctx->exited = 0;
1000     ctx->resume_handler = ngx_http_lua_wev_handler;
1001 
1002     ngx_str_null(&ctx->exec_uri);
1003     ngx_str_null(&ctx->exec_args);
1004 
1005     ctx->co_op = 0;
1006 }
1007 
1008 
1009 /* post read callback for rewrite and access phases */
1010 void
1011 ngx_http_lua_generic_phase_post_read(ngx_http_request_t *r)
1012 {
1013     ngx_http_lua_ctx_t  *ctx;
1014 
1015     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1016                    "lua post read for rewrite/access phases");
1017 
1018     ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
1019 
1020     ctx->read_body_done = 1;
1021 
1022     r->main->count--;
1023 
1024     if (ctx->waiting_more_body) {
1025         ctx->waiting_more_body = 0;
1026         ngx_http_core_run_phases(r);
1027     }
1028 }
1029 
1030 
1031 void
1032 ngx_http_lua_request_cleanup_handler(void *data)
1033 {
1034     ngx_http_lua_ctx_t          *ctx = data;
1035 
1036     ngx_http_lua_request_cleanup(ctx, 0 /* forcible */);
1037 }
1038 
1039 
1040 void
1041 ngx_http_lua_request_cleanup(ngx_http_lua_ctx_t *ctx, int forcible)
1042 {
1043     lua_State                   *L;
1044     ngx_http_request_t          *r;
1045     ngx_http_lua_main_conf_t    *lmcf;
1046 
1047     /*  force coroutine handling the request quit */
1048     if (ctx == NULL) {
1049         dd("ctx is NULL");
1050         return;
1051     }
1052 
1053     r = ctx->request;
1054 
1055     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1056                    "lua request cleanup: forcible=%d", forcible);
1057 
1058     if (ctx->cleanup) {
1059         *ctx->cleanup = NULL;
1060         ctx->cleanup = NULL;
1061     }
1062 
1063     lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
1064 
1065 #if 1
1066     if (r->connection->fd == (ngx_socket_t) -1) {
1067         /* being a fake request */
1068 
1069         if (ctx->context == NGX_HTTP_LUA_CONTEXT_TIMER) {
1070             /* being a timer handler */
1071             lmcf->running_timers--;
1072         }
1073     }
1074 #endif
1075 
1076     L = ngx_http_lua_get_lua_vm(r, ctx);
1077 
1078     ngx_http_lua_finalize_threads(r, ctx, L);
1079 }
1080 
1081 
1082 /*
1083  * description:
1084  *  run a Lua coroutine specified by ctx->cur_co_ctx->co
1085  * return value:
1086  *  NGX_AGAIN:      I/O interruption: r->main->count intact
1087  *  NGX_DONE:       I/O interruption: r->main->count already incremented by 1
1088  *  NGX_ERROR:      error
1089  *  >= 200          HTTP status code
1090  */
1091 ngx_int_t
1092 ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r,
1093     ngx_http_lua_ctx_t *ctx, volatile int nrets)
1094 {
1095     ngx_http_lua_co_ctx_t   *next_coctx, *parent_coctx, *orig_coctx;
1096     int                      rv, success = 1;
1097     lua_State               *next_co;
1098     lua_State               *old_co;
1099     const char              *err, *msg, *trace;
1100     ngx_int_t                rc;
1101 #if (NGX_PCRE)
1102     ngx_pool_t              *old_pool = NULL;
1103 #endif
1104 
1105     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1106                    "lua run thread, top:%d c:%ud", lua_gettop(L),
1107                    r->main->count);
1108 
1109     /* set Lua VM panic handler */
1110     lua_atpanic(L, ngx_http_lua_atpanic);
1111 
1112     NGX_LUA_EXCEPTION_TRY {
1113 
1114         /*
1115          * silence a -Werror=clobbered warning with gcc 5.4
1116          * due to above setjmp
1117          */
1118         err = NULL;
1119         msg = NULL;
1120         trace = NULL;
1121 
1122         if (ctx->cur_co_ctx->thread_spawn_yielded) {
1123             ngx_http_lua_probe_info("thread spawn yielded");
1124 
1125             ctx->cur_co_ctx->thread_spawn_yielded = 0;
1126             nrets = 1;
1127         }
1128 
1129         for ( ;; ) {
1130 
1131             dd("ctx: %p, co: %p, co status: %d, co is_wrap: %d",
1132                ctx, ctx->cur_co_ctx->co, ctx->cur_co_ctx->co_status,
1133                ctx->cur_co_ctx->is_wrap);
1134 
1135 #if (NGX_PCRE)
1136             /* XXX: work-around to nginx regex subsystem */
1137             old_pool = ngx_http_lua_pcre_malloc_init(r->pool);
1138 #endif
1139 
1140             orig_coctx = ctx->cur_co_ctx;
1141 
1142 #ifdef NGX_LUA_USE_ASSERT
1143             dd("%p: saved co top: %d, nrets: %d, true top: %d",
1144                orig_coctx->co,
1145                (int) orig_coctx->co_top, (int) nrets,
1146                (int) lua_gettop(orig_coctx->co));
1147 #endif
1148 
1149 #if DDEBUG
1150             if (lua_gettop(orig_coctx->co) > 0) {
1151                 dd("co top elem: %s", luaL_typename(orig_coctx->co, -1));
1152             }
1153 
1154             if (orig_coctx->propagate_error) {
1155                 dd("co propagate_error: %d", orig_coctx->propagate_error);
1156             }
1157 #endif
1158 
1159             if (orig_coctx->propagate_error) {
1160                 orig_coctx->propagate_error = 0;
1161                 goto propagate_error;
1162             }
1163 
1164             ngx_http_lua_assert(orig_coctx->co_top + nrets
1165                                 == lua_gettop(orig_coctx->co));
1166 
1167             rv = lua_resume(orig_coctx->co, nrets);
1168 
1169 #if (NGX_PCRE)
1170             /* XXX: work-around to nginx regex subsystem */
1171             ngx_http_lua_pcre_malloc_done(old_pool);
1172 #endif
1173 
1174 #if 0
1175             /* test the longjmp thing */
1176             if (rand() % 2 == 0) {
1177                 NGX_LUA_EXCEPTION_THROW(1);
1178             }
1179 #endif
1180 
1181             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1182                            "lua resume returned %d", rv);
1183 
1184             switch (rv) {
1185             case LUA_YIELD:
1186                 /*  yielded, let event handler do the rest job */
1187                 /*  FIXME: add io cmd dispatcher here */
1188 
1189                 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1190                                "lua thread yielded");
1191 
1192 #ifdef NGX_LUA_USE_ASSERT
1193                 dd("%p: saving curr top after yield: %d (co-op: %d)",
1194                    orig_coctx->co,
1195                    (int) lua_gettop(orig_coctx->co), (int) ctx->co_op);
1196                 orig_coctx->co_top = lua_gettop(orig_coctx->co);
1197 #endif
1198 
1199                 if (r->uri_changed) {
1200                     return ngx_http_lua_handle_rewrite_jump(L, r, ctx);
1201                 }
1202 
1203                 if (ctx->exited) {
1204                     return ngx_http_lua_handle_exit(L, r, ctx);
1205                 }
1206 
1207                 if (ctx->exec_uri.len) {
1208                     return ngx_http_lua_handle_exec(L, r, ctx);
1209                 }
1210 
1211                 /*
1212                  * check if coroutine.resume or coroutine.yield called
1213                  * lua_yield()
1214                  */
1215                 switch (ctx->co_op) {
1216 
1217                 case NGX_HTTP_LUA_USER_CORO_NOP:
1218                     dd("hit! it is the API yield");
1219 
1220                     ngx_http_lua_assert(lua_gettop(ctx->cur_co_ctx->co) == 0);
1221 
1222                     ctx->cur_co_ctx = NULL;
1223 
1224                     return NGX_AGAIN;
1225 
1226                 case NGX_HTTP_LUA_USER_THREAD_RESUME:
1227 
1228                     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1229                                    "lua user thread resume");
1230 
1231                     ctx->co_op = NGX_HTTP_LUA_USER_CORO_NOP;
1232                     nrets = lua_gettop(ctx->cur_co_ctx->co) - 1;
1233                     dd("nrets = %d", nrets);
1234 
1235 #ifdef NGX_LUA_USE_ASSERT
1236                     /* ignore the return value (the thread) already pushed */
1237                     orig_coctx->co_top--;
1238 #endif
1239 
1240                     break;
1241 
1242                 case NGX_HTTP_LUA_USER_CORO_RESUME:
1243                     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1244                                    "lua coroutine: resume");
1245 
1246                     /*
1247                      * the target coroutine lies at the base of the
1248                      * parent's stack
1249                      */
1250                     ctx->co_op = NGX_HTTP_LUA_USER_CORO_NOP;
1251 
1252                     old_co = ctx->cur_co_ctx->parent_co_ctx->co;
1253 
1254                     nrets = lua_gettop(old_co);
1255                     if (nrets) {
1256                         dd("moving %d return values to parent", nrets);
1257                         lua_xmove(old_co, ctx->cur_co_ctx->co, nrets);
1258 
1259 #ifdef NGX_LUA_USE_ASSERT
1260                         ctx->cur_co_ctx->parent_co_ctx->co_top -= nrets;
1261 #endif
1262                     }
1263 
1264                     break;
1265 
1266                 default:
1267                     /* ctx->co_op == NGX_HTTP_LUA_USER_CORO_YIELD */
1268 
1269                     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1270                                    "lua coroutine: yield");
1271 
1272                     ctx->co_op = NGX_HTTP_LUA_USER_CORO_NOP;
1273 
1274                     if (ngx_http_lua_is_thread(ctx)) {
1275                         ngx_http_lua_probe_thread_yield(r, ctx->cur_co_ctx->co);
1276 
1277                         /* discard any return values from user
1278                          * coroutine.yield()'s arguments */
1279                         lua_settop(ctx->cur_co_ctx->co, 0);
1280 
1281 #ifdef NGX_LUA_USE_ASSERT
1282                         ctx->cur_co_ctx->co_top = 0;
1283 #endif
1284 
1285                         ngx_http_lua_probe_info("set co running");
1286                         ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_RUNNING;
1287 
1288                         if (ctx->posted_threads) {
1289                             ngx_http_lua_post_thread(r, ctx, ctx->cur_co_ctx);
1290                             ctx->cur_co_ctx = NULL;
1291                             return NGX_AGAIN;
1292                         }
1293 
1294                         /* no pending threads, so resume the thread
1295                          * immediately */
1296 
1297                         nrets = 0;
1298                         continue;
1299                     }
1300 
1301                     /* being a user coroutine that has a parent */
1302 
1303                     nrets = lua_gettop(ctx->cur_co_ctx->co);
1304 
1305                     next_coctx = ctx->cur_co_ctx->parent_co_ctx;
1306                     next_co = next_coctx->co;
1307 
1308                     if (nrets) {
1309                         dd("moving %d return values to next co", nrets);
1310                         lua_xmove(ctx->cur_co_ctx->co, next_co, nrets);
1311 #ifdef NGX_LUA_USE_ASSERT
1312                         ctx->cur_co_ctx->co_top -= nrets;
1313 #endif
1314                     }
1315 
1316                     if (!ctx->cur_co_ctx->is_wrap) {
1317                         /*
1318                          * prepare return values for coroutine.resume
1319                          * (true plus any retvals)
1320                          */
1321                         lua_pushboolean(next_co, 1);
1322                         lua_insert(next_co, 1);
1323                         nrets++;  /* add the true boolean value */
1324                     }
1325 
1326                     ctx->cur_co_ctx = next_coctx;
1327 
1328                     break;
1329                 }
1330 
1331                 /* try resuming on the new coroutine again */
1332                 continue;
1333 
1334             case 0:
1335 
1336                 ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx);
1337 
1338                 ngx_http_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 1);
1339 
1340                 ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_DEAD;
1341 
1342                 if (ctx->cur_co_ctx->zombie_child_threads) {
1343                     ngx_http_lua_cleanup_zombie_child_uthreads(r, L, ctx,
1344                                                                ctx->cur_co_ctx);
1345                 }
1346 
1347                 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1348                                "lua light thread ended normally");
1349 
1350                 if (ngx_http_lua_is_entry_thread(ctx)) {
1351 
1352                     lua_settop(L, 0);
1353 
1354                     ngx_http_lua_del_thread(r, L, ctx, ctx->cur_co_ctx);
1355 
1356                     dd("uthreads: %d", (int) ctx->uthreads);
1357 
1358                     if (ctx->uthreads) {
1359 
1360                         ctx->cur_co_ctx = NULL;
1361                         return NGX_AGAIN;
1362                     }
1363 
1364                     /* all user threads terminated already */
1365                     goto done;
1366                 }
1367 
1368                 if (ctx->cur_co_ctx->is_uthread) {
1369                     /* being a user thread */
1370 
1371                     lua_settop(L, 0);
1372 
1373                     parent_coctx = ctx->cur_co_ctx->parent_co_ctx;
1374 
1375                     if (ngx_http_lua_coroutine_alive(parent_coctx)) {
1376                         if (ctx->cur_co_ctx->waited_by_parent) {
1377                             ngx_http_lua_probe_info("parent already waiting");
1378                             ctx->cur_co_ctx->waited_by_parent = 0;
1379                             success = 1;
1380                             goto user_co_done;
1381                         }
1382 
1383                         ngx_http_lua_probe_info("parent still alive");
1384 
1385                         if (ngx_http_lua_post_zombie_thread(r, parent_coctx,
1386                                                             ctx->cur_co_ctx)
1387                             != NGX_OK)
1388                         {
1389                             return NGX_ERROR;
1390                         }
1391 
1392                         lua_pushboolean(ctx->cur_co_ctx->co, 1);
1393                         lua_insert(ctx->cur_co_ctx->co, 1);
1394 
1395                         ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_ZOMBIE;
1396                         ctx->cur_co_ctx = NULL;
1397                         return NGX_AGAIN;
1398                     }
1399 
1400                     ngx_http_lua_del_thread(r, L, ctx, ctx->cur_co_ctx);
1401                     ctx->uthreads--;
1402 
1403                     if (ctx->uthreads == 0) {
1404                         if (ngx_http_lua_entry_thread_alive(ctx)) {
1405                             ctx->cur_co_ctx = NULL;
1406                             return NGX_AGAIN;
1407                         }
1408 
1409                         /* all threads terminated already */
1410                         goto done;
1411                     }
1412 
1413                     /* some other user threads still running */
1414                     ctx->cur_co_ctx = NULL;
1415                     return NGX_AGAIN;
1416                 }
1417 
1418                 /* being a user coroutine that has a parent */
1419 
1420                 success = 1;
1421 
1422 user_co_done:
1423 
1424                 nrets = lua_gettop(ctx->cur_co_ctx->co);
1425 
1426                 next_coctx = ctx->cur_co_ctx->parent_co_ctx;
1427 
1428                 if (next_coctx == NULL) {
1429                     /* being a light thread */
1430                     goto no_parent;
1431                 }
1432 
1433                 next_co = next_coctx->co;
1434 
1435                 if (nrets) {
1436                     lua_xmove(ctx->cur_co_ctx->co, next_co, nrets);
1437                 }
1438 
1439                 if (ctx->cur_co_ctx->is_uthread) {
1440                     ngx_http_lua_del_thread(r, L, ctx, ctx->cur_co_ctx);
1441                     ctx->uthreads--;
1442                 }
1443 
1444                 if (!ctx->cur_co_ctx->is_wrap) {
1445                     /*
1446                      * ended successfully, coroutine.resume returns true plus
1447                      * any return values
1448                      */
1449                     lua_pushboolean(next_co, success);
1450                     lua_insert(next_co, 1);
1451                     nrets++;
1452                 }
1453 
1454                 ctx->cur_co_ctx = next_coctx;
1455 
1456                 ngx_http_lua_probe_info("set parent running");
1457 
1458                 next_coctx->co_status = NGX_HTTP_LUA_CO_RUNNING;
1459 
1460                 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1461                                "lua coroutine: lua user thread ended normally");
1462 
1463                 continue;
1464 
1465             case LUA_ERRRUN:
1466                 err = "runtime error";
1467                 break;
1468 
1469             case LUA_ERRSYNTAX:
1470                 err = "syntax error";
1471                 break;
1472 
1473             case LUA_ERRMEM:
1474                 err = "memory allocation error";
1475                 ngx_quit = 1;
1476                 break;
1477 
1478             case LUA_ERRERR:
1479                 err = "error handler error";
1480                 break;
1481 
1482             default:
1483                 err = "unknown error";
1484                 break;
1485             }
1486 
1487             if (ctx->cur_co_ctx != orig_coctx) {
1488                 ctx->cur_co_ctx = orig_coctx;
1489             }
1490 
1491             ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx);
1492 
1493             ngx_http_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 0);
1494 
1495             ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_DEAD;
1496 
1497             if (orig_coctx->is_uthread
1498                 || orig_coctx->is_wrap
1499                 || ngx_http_lua_is_entry_thread(ctx))
1500             {
1501                 ngx_http_lua_thread_traceback(L, orig_coctx->co, orig_coctx);
1502                 trace = lua_tostring(L, -1);
1503 
1504                 if (lua_isstring(orig_coctx->co, -1)) {
1505                     msg = lua_tostring(orig_coctx->co, -1);
1506                     dd("user custom error msg: %s", msg);
1507 
1508                 } else {
1509                     msg = "unknown reason";
1510                 }
1511             }
1512 
1513 propagate_error:
1514 
1515             if (ctx->cur_co_ctx->is_uthread) {
1516                 ngx_http_lua_assert(err != NULL && msg != NULL
1517                                     && trace != NULL);
1518 
1519                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1520                               "lua user thread aborted: %s: %s\n%s",
1521                               err, msg, trace);
1522 
1523                 lua_settop(L, 0);
1524 
1525                 parent_coctx = ctx->cur_co_ctx->parent_co_ctx;
1526 
1527                 if (ngx_http_lua_coroutine_alive(parent_coctx)) {
1528                     if (ctx->cur_co_ctx->waited_by_parent) {
1529                         ctx->cur_co_ctx->waited_by_parent = 0;
1530                         success = 0;
1531                         goto user_co_done;
1532                     }
1533 
1534                     if (ngx_http_lua_post_zombie_thread(r, parent_coctx,
1535                                                         ctx->cur_co_ctx)
1536                         != NGX_OK)
1537                     {
1538                         return NGX_ERROR;
1539                     }
1540 
1541                     lua_pushboolean(ctx->cur_co_ctx->co, 0);
1542                     lua_insert(ctx->cur_co_ctx->co, 1);
1543 
1544                     ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_ZOMBIE;
1545                     ctx->cur_co_ctx = NULL;
1546                     return NGX_AGAIN;
1547                 }
1548 
1549                 ngx_http_lua_del_thread(r, L, ctx, ctx->cur_co_ctx);
1550                 ctx->uthreads--;
1551 
1552                 if (ctx->uthreads == 0) {
1553                     if (ngx_http_lua_entry_thread_alive(ctx)) {
1554                         ctx->cur_co_ctx = NULL;
1555                         return NGX_AGAIN;
1556                     }
1557 
1558                     /* all threads terminated already */
1559                     goto done;
1560                 }
1561 
1562                 /* some other user threads still running */
1563                 ctx->cur_co_ctx = NULL;
1564                 return NGX_AGAIN;
1565             }
1566 
1567             if (ngx_http_lua_is_entry_thread(ctx)) {
1568                 ngx_http_lua_assert(err != NULL && msg != NULL
1569                                     && trace != NULL);
1570 
1571                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1572                               "lua entry thread aborted: %s: %s\n%s",
1573                               err, msg, trace);
1574 
1575                 lua_settop(L, 0);
1576 
1577                 /* being the entry thread aborted */
1578 
1579                 if (r->filter_finalize) {
1580                     ngx_http_set_ctx(r, ctx, ngx_http_lua_module);
1581                 }
1582 
1583                 ngx_http_lua_request_cleanup(ctx, 0);
1584 
1585                 dd("headers sent? %d", r->header_sent || ctx->header_sent);
1586 
1587                 if (ctx->no_abort) {
1588                     ctx->no_abort = 0;
1589                     return NGX_ERROR;
1590                 }
1591 
1592                 return (r->header_sent || ctx->header_sent) ? NGX_ERROR :
1593                        NGX_HTTP_INTERNAL_SERVER_ERROR;
1594             }
1595 
1596             /* being a user coroutine that has a parent */
1597 
1598             next_coctx = ctx->cur_co_ctx->parent_co_ctx;
1599             if (next_coctx == NULL) {
1600                 goto no_parent;
1601             }
1602 
1603             next_co = next_coctx->co;
1604 
1605             ngx_http_lua_probe_info("set parent running");
1606 
1607             next_coctx->co_status = NGX_HTTP_LUA_CO_RUNNING;
1608 
1609             ctx->cur_co_ctx = next_coctx;
1610 
1611             if (orig_coctx->is_wrap) {
1612                 /*
1613                  * coroutine.wrap propagates errors
1614                  * to its parent coroutine
1615                  */
1616                 next_coctx->propagate_error = 1;
1617                 continue;
1618             }
1619 
1620             /*
1621              * ended with error, coroutine.resume returns false plus
1622              * err msg
1623              */
1624             lua_pushboolean(next_co, 0);
1625             lua_xmove(orig_coctx->co, next_co, 1);
1626             nrets = 2;
1627 
1628             /* try resuming on the new coroutine again */
1629             continue;
1630         }
1631 
1632     } NGX_LUA_EXCEPTION_CATCH {
1633         dd("nginx execution restored");
1634     }
1635 
1636     return NGX_ERROR;
1637 
1638 no_parent:
1639 
1640     lua_settop(L, 0);
1641 
1642     ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_DEAD;
1643 
1644     if (r->filter_finalize) {
1645         ngx_http_set_ctx(r, ctx, ngx_http_lua_module);
1646     }
1647 
1648     ngx_http_lua_request_cleanup(ctx, 0);
1649 
1650     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "lua handler aborted: "
1651                   "user coroutine has no parent");
1652 
1653     return (r->header_sent || ctx->header_sent) ?
1654                 NGX_ERROR : NGX_HTTP_INTERNAL_SERVER_ERROR;
1655 
1656 done:
1657 
1658     if (ctx->entered_content_phase
1659         && r->connection->fd != (ngx_socket_t) -1)
1660     {
1661         rc = ngx_http_lua_send_chain_link(r, ctx,
1662                                           NULL /* last_buf */);
1663 
1664         if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
1665             return rc;
1666         }
1667     }
1668 
1669     return NGX_OK;
1670 }
1671 
1672 
1673 ngx_int_t
1674 ngx_http_lua_wev_handler(ngx_http_request_t *r)
1675 {
1676     ngx_int_t                    rc;
1677     ngx_event_t                 *wev;
1678     ngx_connection_t            *c;
1679     ngx_http_lua_ctx_t          *ctx;
1680     ngx_http_core_loc_conf_t    *clcf;
1681 
1682     ngx_http_lua_socket_tcp_upstream_t *u;
1683 
1684     c = r->connection;
1685     wev = c->write;
1686 
1687     ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
1688     if (ctx == NULL) {
1689         return NGX_ERROR;
1690     }
1691 
1692     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
1693                    "lua run write event handler: timedout:%ud, ready:%ud, "
1694                    "writing_raw_req_socket:%ud",
1695                    wev->timedout, wev->ready, ctx->writing_raw_req_socket);
1696 
1697     clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);
1698 
1699     if (wev->timedout && !ctx->writing_raw_req_socket) {
1700         if (!wev->delayed) {
1701             ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
1702                           "client timed out");
1703             c->timedout = 1;
1704 
1705             goto flush_coros;
1706         }
1707 
1708         wev->timedout = 0;
1709         wev->delayed = 0;
1710 
1711         if (!wev->ready) {
1712             ngx_add_timer(wev, clcf->send_timeout);
1713 
1714             if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {
1715                 if (ctx->entered_content_phase) {
1716                     ngx_http_lua_finalize_request(r, NGX_ERROR);
1717                 }
1718                 return NGX_ERROR;
1719             }
1720         }
1721     }
1722 
1723     if (!wev->ready && !wev->timedout) {
1724         goto useless;
1725     }
1726 
1727     if (ctx->writing_raw_req_socket) {
1728         ctx->writing_raw_req_socket = 0;
1729 
1730         u = ctx->downstream;
1731         if (u == NULL) {
1732             return NGX_ERROR;
1733         }
1734 
1735         u->write_event_handler(r, u);
1736         return NGX_DONE;
1737     }
1738 
1739     if (c->buffered & (NGX_HTTP_LOWLEVEL_BUFFERED | NGX_LOWLEVEL_BUFFERED)) {
1740         rc = ngx_http_lua_flush_pending_output(r, ctx);
1741 
1742         dd("flush pending output returned %d, c->error: %d", (int) rc,
1743            c->error);
1744 
1745         if (rc != NGX_ERROR && rc != NGX_OK) {
1746             goto useless;
1747         }
1748 
1749         /* when rc == NGX_ERROR, c->error must be set */
1750     }
1751 
1752 flush_coros:
1753 
1754     dd("ctx->flushing_coros: %d", (int) ctx->flushing_coros);
1755 
1756     if (ctx->flushing_coros) {
1757         return ngx_http_lua_process_flushing_coroutines(r, ctx);
1758     }
1759 
1760     /* ctx->flushing_coros == 0 */
1761 
1762 useless:
1763 
1764     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
1765                    "useless lua write event handler");
1766 
1767     if (ctx->entered_content_phase) {
1768         return NGX_OK;
1769     }
1770 
1771     return NGX_DONE;
1772 }
1773 
1774 
1775 static ngx_int_t
1776 ngx_http_lua_process_flushing_coroutines(ngx_http_request_t *r,
1777     ngx_http_lua_ctx_t *ctx)
1778 {
1779     ngx_int_t                    rc, n;
1780     ngx_uint_t                   i;
1781     ngx_list_part_t             *part;
1782     ngx_http_lua_co_ctx_t       *coctx;
1783 
1784     dd("processing flushing coroutines");
1785 
1786     coctx = &ctx->entry_co_ctx;
1787     n = ctx->flushing_coros;
1788 
1789     if (coctx->flushing) {
1790         coctx->flushing = 0;
1791 
1792         ctx->flushing_coros--;
1793         n--;
1794         ctx->cur_co_ctx = coctx;
1795 
1796         rc = ngx_http_lua_flush_resume_helper(r, ctx);
1797         if (rc == NGX_ERROR || rc >= NGX_OK) {
1798             return rc;
1799         }
1800 
1801         /* rc == NGX_DONE */
1802     }
1803 
1804     if (n) {
1805 
1806         if (ctx->user_co_ctx == NULL) {
1807             return NGX_ERROR;
1808         }
1809 
1810         part = &ctx->user_co_ctx->part;
1811         coctx = part->elts;
1812 
1813         for (i = 0; /* void */; i++) {
1814 
1815             if (i >= part->nelts) {
1816                 if (part->next == NULL) {
1817                     break;
1818                 }
1819 
1820                 part = part->next;
1821                 coctx = part->elts;
1822                 i = 0;
1823             }
1824 
1825             if (coctx[i].flushing) {
1826                 coctx[i].flushing = 0;
1827                 ctx->flushing_coros--;
1828                 n--;
1829                 ctx->cur_co_ctx = &coctx[i];
1830 
1831                 rc = ngx_http_lua_flush_resume_helper(r, ctx);
1832                 if (rc == NGX_ERROR || rc >= NGX_OK) {
1833                     return rc;
1834                 }
1835 
1836                 /* rc == NGX_DONE */
1837 
1838                 if (n == 0) {
1839                     return NGX_DONE;
1840                 }
1841             }
1842         }
1843     }
1844 
1845     if (n) {
1846         return NGX_ERROR;
1847     }
1848 
1849     return NGX_DONE;
1850 }
1851 
1852 
1853 static ngx_int_t
1854 ngx_http_lua_flush_pending_output(ngx_http_request_t *r,
1855     ngx_http_lua_ctx_t *ctx)
1856 {
1857     ngx_int_t           rc;
1858     ngx_chain_t        *cl;
1859     ngx_event_t        *wev;
1860     ngx_connection_t   *c;
1861 
1862     ngx_http_core_loc_conf_t    *clcf;
1863 
1864     c = r->connection;
1865     wev = c->write;
1866 
1867     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
1868                    "lua flushing output: buffered 0x%uxd",
1869                    c->buffered);
1870 
1871     if (ctx->busy_bufs) {
1872         /* FIXME since cosockets also share this busy_bufs chain, this condition
1873          * might not be strong enough. better use separate busy_bufs chains. */
1874         rc = ngx_http_lua_output_filter(r, NULL);
1875 
1876     } else {
1877         cl = ngx_http_lua_get_flush_chain(r, ctx);
1878         if (cl == NULL) {
1879             return NGX_ERROR;
1880         }
1881 
1882         rc = ngx_http_lua_output_filter(r, cl);
1883     }
1884 
1885     dd("output filter returned %d", (int) rc);
1886 
1887     if (rc == NGX_ERROR || rc > NGX_OK) {
1888         return rc;
1889     }
1890 
1891     if (c->buffered & (NGX_HTTP_LOWLEVEL_BUFFERED | NGX_LOWLEVEL_BUFFERED)) {
1892 
1893         clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);
1894 
1895         if (!wev->delayed) {
1896             ngx_add_timer(wev, clcf->send_timeout);
1897         }
1898 
1899         if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {
1900             if (ctx->entered_content_phase) {
1901                 ngx_http_lua_finalize_request(r, NGX_ERROR);
1902             }
1903 
1904             return NGX_ERROR;
1905         }
1906 
1907         if (ctx->flushing_coros) {
1908             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
1909                            "lua flush still waiting: buffered 0x%uxd",
1910                            c->buffered);
1911 
1912             return NGX_DONE;
1913         }
1914 
1915     } else {
1916 #if 1
1917         if (wev->timer_set && !wev->delayed) {
1918             ngx_del_timer(wev);
1919         }
1920 #endif
1921     }
1922 
1923     return NGX_OK;
1924 }
1925 
1926 
1927 u_char *
1928 ngx_http_lua_digest_hex(u_char *dest, const u_char *buf, int buf_len)
1929 {
1930     ngx_md5_t                     md5;
1931     u_char                        md5_buf[MD5_DIGEST_LENGTH];
1932 
1933     ngx_md5_init(&md5);
1934     ngx_md5_update(&md5, buf, buf_len);
1935     ngx_md5_final(md5_buf, &md5);
1936 
1937     return ngx_hex_dump(dest, md5_buf, sizeof(md5_buf));
1938 }
1939 
1940 
1941 void
1942 ngx_http_lua_set_multi_value_table(lua_State *L, int index)
1943 {
1944     if (index < 0) {
1945         index = lua_gettop(L) + index + 1;
1946     }
1947 
1948     lua_pushvalue(L, -2); /* stack: table key value key */
1949     lua_rawget(L, index);
1950     if (lua_isnil(L, -1)) {
1951         lua_pop(L, 1); /* stack: table key value */
1952         lua_rawset(L, index); /* stack: table */
1953 
1954     } else {
1955         if (!lua_istable(L, -1)) {
1956             /* just inserted one value */
1957             lua_createtable(L, 4, 0);
1958                 /* stack: table key value value table */
1959             lua_insert(L, -2);
1960                 /* stack: table key value table value */
1961             lua_rawseti(L, -2, 1);
1962                 /* stack: table key value table */
1963             lua_insert(L, -2);
1964                 /* stack: table key table value */
1965 
1966             lua_rawseti(L, -2, 2); /* stack: table key table */
1967 
1968             lua_rawset(L, index); /* stack: table */
1969 
1970         } else {
1971             /* stack: table key value table */
1972             lua_insert(L, -2); /* stack: table key table value */
1973 
1974             lua_rawseti(L, -2, lua_objlen(L, -2) + 1);
1975                 /* stack: table key table  */
1976             lua_pop(L, 2); /* stack: table */
1977         }
1978     }
1979 }
1980 
1981 
1982 uintptr_t
1983 ngx_http_lua_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type)
1984 {
1985     ngx_uint_t      n;
1986     uint32_t       *escape;
1987     static u_char   hex[] = "0123456789ABCDEF";
1988 
1989                     /* " ", "#", "%", "?", %00-%1F, %7F-%FF */
1990 
1991     static uint32_t   uri[] = {
1992         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
1993 
1994                     /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
1995         0x80000029, /* 1000 0000 0000 0000  0000 0000 0010 1001 */
1996 
1997                     /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
1998         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
1999 
2000                     /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
2001         0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */
2002 
2003         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2004         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2005         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2006         0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2007     };
2008 
2009                     /* " ", "#", "%", "+", "?", %00-%1F, %7F-%FF */
2010 
2011     static uint32_t   args[] = {
2012         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2013 
2014                     /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
2015         0x80000829, /* 1000 0000 0000 0000  0000 1000 0010 1001 */
2016 
2017                     /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
2018         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
2019 
2020                     /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
2021         0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */
2022 
2023         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2024         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2025         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2026         0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2027     };
2028 
2029                     /* not ALPHA, DIGIT, "-", ".", "_", "~" */
2030 
2031     static uint32_t   uri_component[] = {
2032         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2033 
2034                     /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
2035         0xfc00987d, /* 1111 1100 0000 0000  1001 1000 0111 1101 */
2036 
2037                     /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
2038         0x78000001, /* 0111 1000 0000 0000  0000 0000 0000 0001 */
2039 
2040                     /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
2041         0xb8000001, /* 1011 1000 0000 0000  0000 0000 0000 0001 */
2042 
2043         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2044         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2045         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2046         0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2047     };
2048 
2049                     /* " ", "#", """, "%", "'", %00-%1F, %7F-%FF */
2050 
2051     static uint32_t   html[] = {
2052         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2053 
2054                     /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
2055         0x000000ad, /* 0000 0000 0000 0000  0000 0000 1010 1101 */
2056 
2057                     /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
2058         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
2059 
2060                     /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
2061         0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */
2062 
2063         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2064         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2065         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2066         0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2067     };
2068 
2069                     /* " ", """, "%", "'", %00-%1F, %7F-%FF */
2070 
2071     static uint32_t   refresh[] = {
2072         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2073 
2074                     /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
2075         0x00000085, /* 0000 0000 0000 0000  0000 0000 1000 0101 */
2076 
2077                     /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
2078         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
2079 
2080                     /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
2081         0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */
2082 
2083         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2084         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2085         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2086         0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2087     };
2088 
2089                     /* " ", "%", %00-%1F */
2090 
2091     static uint32_t   memcached[] = {
2092         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2093 
2094                     /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
2095         0x00000021, /* 0000 0000 0000 0000  0000 0000 0010 0001 */
2096 
2097                     /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
2098         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
2099 
2100                     /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
2101         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
2102 
2103         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
2104         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
2105         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
2106         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
2107     };
2108 
2109                     /* mail_auth is the same as memcached */
2110 
2111                     /* " ", """, "(", ")", ",", "/", ":", ";", "?",
2112                      * "<", "=", ">", "?", "@", "[", "]", "\", "{",
2113                      * "}", %00-%1F, %7F-%FF
2114                      */
2115 
2116     static uint32_t   header_name[] = {
2117         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2118 
2119                     /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
2120         0xfc009305, /* 1111 1100 0000 0000  1001 0011 0000 0101 */
2121 
2122                     /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
2123         0x38000001, /* 0011 1000 0000 0000  0000 0000 0000 0001 */
2124 
2125                     /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
2126         0xa8000000, /* 1010 1000 0000 0000  0000 0000 0000 0000 */
2127 
2128         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2129         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2130         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2131         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
2132     };
2133 
2134                     /* "%00-%08, %0A-%0F, %7F */
2135 
2136     static uint32_t   header_value[] = {
2137         0xfffffdff, /* 1111 1111 1111 1111  1111 1101 1111 1111 */
2138 
2139                     /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
2140         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
2141 
2142                     /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
2143         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
2144 
2145                     /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
2146         0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */
2147 
2148         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
2149         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
2150         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
2151         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
2152     };
2153 
2154     static uint32_t  *map[] =
2155         { uri, args, uri_component, html, refresh, memcached, memcached,
2156           header_name, header_value };
2157 
2158     escape = map[type];
2159 
2160     if (dst == NULL) {
2161 
2162         /* find the number of the characters to be escaped */
2163 
2164         n = 0;
2165 
2166         while (size) {
2167             if (escape[*src >> 5] & (1 << (*src & 0x1f))) {
2168                 n++;
2169             }
2170 
2171             src++;
2172             size--;
2173         }
2174 
2175         return (uintptr_t) n;
2176     }
2177 
2178     while (size) {
2179         if (escape[*src >> 5] & (1 << (*src & 0x1f))) {
2180             *dst++ = '%';
2181             *dst++ = hex[*src >> 4];
2182             *dst++ = hex[*src & 0xf];
2183             src++;
2184 
2185         } else {
2186             *dst++ = *src++;
2187         }
2188 
2189         size--;
2190     }
2191 
2192     return (uintptr_t) dst;
2193 }
2194 
2195 
2196 /* XXX we also decode '+' to ' ' */
2197 void
2198 ngx_http_lua_unescape_uri(u_char **dst, u_char **src, size_t size,
2199     ngx_uint_t type)
2200 {
2201     u_char  *d, *s, ch, c, decoded;
2202     enum {
2203         sw_usual = 0,
2204         sw_quoted,
2205         sw_quoted_second,
2206     } state;
2207 
2208     d = *dst;
2209     s = *src;
2210 
2211     state = 0;
2212     decoded = 0;
2213 
2214     while (size--) {
2215 
2216         ch = *s++;
2217 
2218         switch (state) {
2219         case sw_usual:
2220             if (ch == '?'
2221                 && (type & (NGX_UNESCAPE_URI|NGX_UNESCAPE_REDIRECT)))
2222             {
2223                 *d++ = ch;
2224                 goto done;
2225             }
2226 
2227             if (ch == '%') {
2228                 state = sw_quoted;
2229                 break;
2230             }
2231 
2232             if (ch == '+') {
2233                 *d++ = ' ';
2234                 break;
2235             }
2236 
2237             *d++ = ch;
2238             break;
2239 
2240         case sw_quoted:
2241 
2242             if (ch >= '0' && ch <= '9') {
2243                 decoded = (u_char) (ch - '0');
2244                 state = sw_quoted_second;
2245                 break;
2246             }
2247 
2248             c = (u_char) (ch | 0x20);
2249             if (c >= 'a' && c <= 'f') {
2250                 decoded = (u_char) (c - 'a' + 10);
2251                 state = sw_quoted_second;
2252                 break;
2253             }
2254 
2255             /* the invalid quoted character */
2256 
2257             state = sw_usual;
2258 
2259             *d++ = ch;
2260 
2261             break;
2262 
2263         case sw_quoted_second:
2264 
2265             state = sw_usual;
2266 
2267             if (ch >= '0' && ch <= '9') {
2268                 ch = (u_char) ((decoded << 4) + ch - '0');
2269 
2270                 if (type & NGX_UNESCAPE_REDIRECT) {
2271                     if (ch > '%' && ch < 0x7f) {
2272                         *d++ = ch;
2273                         break;
2274                     }
2275 
2276                     *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1);
2277                     break;
2278                 }
2279 
2280                 *d++ = ch;
2281 
2282                 break;
2283             }
2284 
2285             c = (u_char) (ch | 0x20);
2286             if (c >= 'a' && c <= 'f') {
2287                 ch = (u_char) ((decoded << 4) + c - 'a' + 10);
2288 
2289                 if (type & NGX_UNESCAPE_URI) {
2290                     if (ch == '?') {
2291                         *d++ = ch;
2292                         goto done;
2293                     }
2294 
2295                     *d++ = ch;
2296                     break;
2297                 }
2298 
2299                 if (type & NGX_UNESCAPE_REDIRECT) {
2300                     if (ch == '?') {
2301                         *d++ = ch;
2302                         goto done;
2303                     }
2304 
2305                     if (ch > '%' && ch < 0x7f) {
2306                         *d++ = ch;
2307                         break;
2308                     }
2309 
2310                     *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1);
2311                     break;
2312                 }
2313 
2314                 *d++ = ch;
2315 
2316                 break;
2317             }
2318 
2319             /* the invalid quoted character */
2320 
2321             break;
2322         }
2323     }
2324 
2325 done:
2326 
2327     *dst = d;
2328     *src = s;
2329 }
2330 
2331 
2332 void
2333 ngx_http_lua_inject_req_api(ngx_log_t *log, lua_State *L)
2334 {
2335     /* ngx.req table */
2336 
2337     lua_createtable(L, 0 /* narr */, 23 /* nrec */);    /* .req */
2338 
2339     ngx_http_lua_inject_req_header_api(L);
2340     ngx_http_lua_inject_req_uri_api(log, L);
2341     ngx_http_lua_inject_req_args_api(L);
2342     ngx_http_lua_inject_req_body_api(L);
2343     ngx_http_lua_inject_req_socket_api(L);
2344     ngx_http_lua_inject_req_misc_api(L);
2345 
2346     lua_setfield(L, -2, "req");
2347 }
2348 
2349 
2350 static ngx_int_t
2351 ngx_http_lua_handle_exec(lua_State *L, ngx_http_request_t *r,
2352     ngx_http_lua_ctx_t *ctx)
2353 {
2354     ngx_int_t               rc;
2355 
2356     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2357                    "lua thread initiated internal redirect to %V",
2358                    &ctx->exec_uri);
2359 
2360     ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx);
2361 
2362     ngx_http_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 1);
2363 
2364     ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_DEAD;
2365 
2366     if (r->filter_finalize) {
2367         ngx_http_set_ctx(r, ctx, ngx_http_lua_module);
2368     }
2369 
2370     ngx_http_lua_request_cleanup(ctx, 1 /* forcible */);
2371 
2372     if (ctx->exec_uri.data[0] == '@') {
2373         if (ctx->exec_args.len > 0) {
2374             ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
2375                           "query strings %V ignored when exec'ing "
2376                           "named location %V",
2377                           &ctx->exec_args, &ctx->exec_uri);
2378         }
2379 
2380         r->write_event_handler = ngx_http_request_empty_handler;
2381 
2382 #if 1
2383         if (r->read_event_handler == ngx_http_lua_rd_check_broken_connection) {
2384             /* resume the read event handler */
2385 
2386             r->read_event_handler = ngx_http_block_reading;
2387         }
2388 #endif
2389 
2390 #if 1
2391         /* clear the modules contexts */
2392         ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module);
2393 #endif
2394 
2395         rc = ngx_http_named_location(r, &ctx->exec_uri);
2396         if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
2397             return rc;
2398         }
2399 
2400 #if 0
2401         if (!ctx->entered_content_phase) {
2402             /* XXX ensure the main request ref count
2403              * is decreased because the current
2404              * request will be quit */
2405             r->main->count--;
2406             dd("XXX decrement main count: c:%d", (int) r->main->count);
2407         }
2408 #endif
2409 
2410         return NGX_DONE;
2411     }
2412 
2413     dd("internal redirect to %.*s", (int) ctx->exec_uri.len,
2414        ctx->exec_uri.data);
2415 
2416     r->write_event_handler = ngx_http_request_empty_handler;
2417 
2418     if (r->read_event_handler == ngx_http_lua_rd_check_broken_connection) {
2419         /* resume the read event handler */
2420 
2421         r->read_event_handler = ngx_http_block_reading;
2422     }
2423 
2424     rc = ngx_http_internal_redirect(r, &ctx->exec_uri, &ctx->exec_args);
2425 
2426     dd("internal redirect returned %d when in content phase? "
2427        "%d", (int) rc, ctx->entered_content_phase);
2428 
2429     if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
2430         return rc;
2431     }
2432 
2433 #if 0
2434     if (!ctx->entered_content_phase) {
2435         /* XXX ensure the main request ref count
2436          * is decreased because the current
2437          * request will be quit */
2438         dd("XXX decrement main count");
2439         r->main->count--;
2440     }
2441 #endif
2442 
2443     return NGX_DONE;
2444 }
2445 
2446 
2447 static ngx_int_t
2448 ngx_http_lua_handle_exit(lua_State *L, ngx_http_request_t *r,
2449     ngx_http_lua_ctx_t *ctx)
2450 {
2451     ngx_int_t           rc;
2452 
2453     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2454                    "lua thread aborting request with status %d",
2455                    ctx->exit_code);
2456 
2457     ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx);
2458 
2459     ngx_http_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 1);
2460 
2461     ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_DEAD;
2462 
2463     if (r->filter_finalize) {
2464         ngx_http_set_ctx(r, ctx, ngx_http_lua_module);
2465     }
2466 
2467     ngx_http_lua_request_cleanup(ctx, 0);
2468 
2469     if (r->connection->fd == (ngx_socket_t) -1) {  /* fake request */
2470         return ctx->exit_code;
2471     }
2472 
2473 #if 1
2474     if (!r->header_sent
2475         && !ctx->header_sent
2476         && r->headers_out.status == 0
2477         && ctx->exit_code >= NGX_HTTP_OK)
2478     {
2479         r->headers_out.status = ctx->exit_code;
2480     }
2481 #endif
2482 
2483     if (ctx->buffering
2484         && r->headers_out.status
2485         && ctx->exit_code != NGX_ERROR
2486         && ctx->exit_code != NGX_HTTP_REQUEST_TIME_OUT
2487         && ctx->exit_code != NGX_HTTP_CLIENT_CLOSED_REQUEST
2488         && ctx->exit_code != NGX_HTTP_CLOSE)
2489     {
2490         rc = ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */);
2491 
2492         if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
2493             return rc;
2494         }
2495 
2496         if (ctx->exit_code >= NGX_HTTP_OK) {
2497             return NGX_HTTP_OK;
2498         }
2499 
2500         return ctx->exit_code;
2501     }
2502 
2503     if ((ctx->exit_code == NGX_OK
2504          && ctx->entered_content_phase)
2505         || (ctx->exit_code >= NGX_HTTP_OK
2506             && ctx->exit_code < NGX_HTTP_SPECIAL_RESPONSE
2507             && ctx->exit_code != NGX_HTTP_NO_CONTENT))
2508     {
2509         rc = ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */);
2510 
2511         if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
2512             return rc;
2513         }
2514     }
2515 
2516 #if 1
2517     if ((r->header_sent || ctx->header_sent)
2518         && ctx->exit_code > NGX_OK
2519         && ctx->exit_code != NGX_HTTP_REQUEST_TIME_OUT
2520         && ctx->exit_code != NGX_HTTP_CLIENT_CLOSED_REQUEST
2521         && ctx->exit_code != NGX_HTTP_CLOSE)
2522     {
2523         if (ctx->entered_content_phase) {
2524             return NGX_OK;
2525         }
2526 
2527         return NGX_HTTP_OK;
2528     }
2529 #endif
2530 
2531     return ctx->exit_code;
2532 }
2533 
2534 
2535 void
2536 ngx_http_lua_process_args_option(ngx_http_request_t *r, lua_State *L,
2537     int table, ngx_str_t *args)
2538 {
2539     u_char              *key;
2540     size_t               key_len;
2541     u_char              *value;
2542     size_t               value_len;
2543     size_t               len = 0;
2544     size_t               key_escape = 0;
2545     uintptr_t            total_escape = 0;
2546     int                  n;
2547     int                  i;
2548     u_char              *p;
2549 
2550     if (table < 0) {
2551         table = lua_gettop(L) + table + 1;
2552     }
2553 
2554     n = 0;
2555     lua_pushnil(L);
2556     while (lua_next(L, table) != 0) {
2557         if (lua_type(L, -2) != LUA_TSTRING) {
2558             luaL_error(L, "attempt to use a non-string key in the "
2559                        "\"args\" option table");
2560             return;
2561         }
2562 
2563         key = (u_char *) lua_tolstring(L, -2, &key_len);
2564 
2565         key_escape = 2 * ngx_http_lua_escape_uri(NULL, key, key_len,
2566                                                  NGX_ESCAPE_URI_COMPONENT);
2567         total_escape += key_escape;
2568 
2569         switch (lua_type(L, -1)) {
2570         case LUA_TNUMBER:
2571         case LUA_TSTRING:
2572             value = (u_char *) lua_tolstring(L, -1, &value_len);
2573 
2574             total_escape += 2 * ngx_http_lua_escape_uri(NULL, value, value_len,
2575                                                       NGX_ESCAPE_URI_COMPONENT);
2576 
2577             len += key_len + value_len + (sizeof("=") - 1);
2578             n++;
2579 
2580             break;
2581 
2582         case LUA_TBOOLEAN:
2583             if (lua_toboolean(L, -1)) {
2584                 len += key_len;
2585                 n++;
2586             }
2587 
2588             break;
2589 
2590         case LUA_TTABLE:
2591 
2592             i = 0;
2593             lua_pushnil(L);
2594             while (lua_next(L, -2) != 0) {
2595                 if (lua_isboolean(L, -1)) {
2596                     if (lua_toboolean(L, -1)) {
2597                         len += key_len;
2598 
2599                     } else {
2600                         lua_pop(L, 1);
2601                         continue;
2602                     }
2603 
2604                 } else {
2605                     value = (u_char *) lua_tolstring(L, -1, &value_len);
2606 
2607                     if (value == NULL) {
2608                         luaL_error(L, "attempt to use %s as query arg value",
2609                                    luaL_typename(L, -1));
2610                         return;
2611                     }
2612 
2613                     total_escape +=
2614                         2 * ngx_http_lua_escape_uri(NULL, value,
2615                                                     value_len,
2616                                                     NGX_ESCAPE_URI_COMPONENT);
2617 
2618                     len += key_len + value_len + (sizeof("=") - 1);
2619                 }
2620 
2621                 if (i++ > 0) {
2622                     total_escape += key_escape;
2623                 }
2624 
2625                 n++;
2626                 lua_pop(L, 1);
2627             }
2628 
2629             break;
2630 
2631         default:
2632             luaL_error(L, "attempt to use %s as query arg value",
2633                        luaL_typename(L, -1));
2634             return;
2635         }
2636 
2637         lua_pop(L, 1);
2638     }
2639 
2640     len += (size_t) total_escape;
2641 
2642     if (n > 1) {
2643         len += (n - 1) * (sizeof("&") - 1);
2644     }
2645 
2646     dd("len 1: %d", (int) len);
2647 
2648     if (r) {
2649         p = ngx_palloc(r->pool, len);
2650         if (p == NULL) {
2651             luaL_error(L, "no memory");
2652             return;
2653         }
2654 
2655     } else {
2656         p = lua_newuserdata(L, len);
2657     }
2658 
2659     args->data = p;
2660     args->len = len;
2661 
2662     i = 0;
2663     lua_pushnil(L);
2664     while (lua_next(L, table) != 0) {
2665         key = (u_char *) lua_tolstring(L, -2, &key_len);
2666 
2667         switch (lua_type(L, -1)) {
2668         case LUA_TNUMBER:
2669         case LUA_TSTRING:
2670 
2671             if (total_escape) {
2672                 p = (u_char *) ngx_http_lua_escape_uri(p, key, key_len,
2673                                                        NGX_ESCAPE_URI_COMPONENT
2674                                                        );
2675 
2676             } else {
2677                 dd("shortcut: no escape required");
2678 
2679                 p = ngx_copy(p, key, key_len);
2680             }
2681 
2682             *p++ = '=';
2683 
2684             value = (u_char *) lua_tolstring(L, -1, &value_len);
2685 
2686             if (total_escape) {
2687                 p = (u_char *) ngx_http_lua_escape_uri(p, value, value_len,
2688                                                        NGX_ESCAPE_URI_COMPONENT
2689                                                        );
2690 
2691             } else {
2692                 p = ngx_copy(p, value, value_len);
2693             }
2694 
2695             if (i != n - 1) {
2696                 /* not the last pair */
2697                 *p++ = '&';
2698             }
2699 
2700             i++;
2701 
2702             break;
2703 
2704         case LUA_TBOOLEAN:
2705             if (lua_toboolean(L, -1)) {
2706                 if (total_escape) {
2707                     p = (u_char *) ngx_http_lua_escape_uri(p, key, key_len,
2708                                                 NGX_ESCAPE_URI_COMPONENT);
2709 
2710                 } else {
2711                     dd("shortcut: no escape required");
2712 
2713                     p = ngx_copy(p, key, key_len);
2714                 }
2715 
2716                 if (i != n - 1) {
2717                     /* not the last pair */
2718                     *p++ = '&';
2719                 }
2720 
2721                 i++;
2722             }
2723 
2724             break;
2725 
2726         case LUA_TTABLE:
2727 
2728             lua_pushnil(L);
2729             while (lua_next(L, -2) != 0) {
2730 
2731                 if (lua_isboolean(L, -1)) {
2732                     if (lua_toboolean(L, -1)) {
2733                         if (total_escape) {
2734                             p = (u_char *)
2735                                     ngx_http_lua_escape_uri(p, key, key_len,
2736                                                       NGX_ESCAPE_URI_COMPONENT);
2737 
2738                         } else {
2739                             dd("shortcut: no escape required");
2740 
2741                             p = ngx_copy(p, key, key_len);
2742                         }
2743 
2744                     } else {
2745                         lua_pop(L, 1);
2746                         continue;
2747                     }
2748 
2749                 } else {
2750 
2751                     if (total_escape) {
2752                         p = (u_char *)
2753                                 ngx_http_lua_escape_uri(p, key,
2754                                                         key_len,
2755                                                         NGX_ESCAPE_URI_COMPONENT
2756                                                         );
2757 
2758                     } else {
2759                         dd("shortcut: no escape required");
2760 
2761                         p = ngx_copy(p, key, key_len);
2762                     }
2763 
2764                     *p++ = '=';
2765 
2766                     value = (u_char *) lua_tolstring(L, -1, &value_len);
2767 
2768                     if (total_escape) {
2769                         p = (u_char *)
2770                                 ngx_http_lua_escape_uri(p, value,
2771                                                         value_len,
2772                                                         NGX_ESCAPE_URI_COMPONENT
2773                                                         );
2774 
2775                     } else {
2776                         p = ngx_copy(p, value, value_len);
2777                     }
2778                 }
2779 
2780                 if (i != n - 1) {
2781                     /* not the last pair */
2782                     *p++ = '&';
2783                 }
2784 
2785                 i++;
2786                 lua_pop(L, 1);
2787             }
2788 
2789             break;
2790 
2791         default:
2792             luaL_error(L, "should not reach here");
2793             return;
2794         }
2795 
2796         lua_pop(L, 1);
2797     }
2798 
2799     if (p - args->data != (ssize_t) len) {
2800         luaL_error(L, "buffer error: %d != %d",
2801                    (int) (p - args->data), (int) len);
2802         return;
2803     }
2804 }
2805 
2806 
2807 static ngx_int_t
2808 ngx_http_lua_handle_rewrite_jump(lua_State *L, ngx_http_request_t *r,
2809     ngx_http_lua_ctx_t *ctx)
2810 {
2811     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2812                    "lua thread aborting request with URI rewrite jump: "
2813                    "\"%V?%V\"", &r->uri, &r->args);
2814 
2815     ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx);
2816 
2817     ngx_http_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 1);
2818 
2819     ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_DEAD;
2820 
2821     if (r->filter_finalize) {
2822         ngx_http_set_ctx(r, ctx, ngx_http_lua_module);
2823     }
2824 
2825     ngx_http_lua_request_cleanup(ctx, 1 /* forcible */);
2826     ngx_http_lua_init_ctx(r, ctx);
2827 
2828     return NGX_OK;
2829 }
2830 
2831 
2832 /* XXX ngx_open_and_stat_file is static in the core. sigh. */
2833 ngx_int_t
2834 ngx_http_lua_open_and_stat_file(u_char *name, ngx_open_file_info_t *of,
2835     ngx_log_t *log)
2836 {
2837     ngx_fd_t         fd;
2838     ngx_file_info_t  fi;
2839 
2840     if (of->fd != NGX_INVALID_FILE) {
2841 
2842         if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) {
2843             of->failed = ngx_file_info_n;
2844             goto failed;
2845         }
2846 
2847         if (of->uniq == ngx_file_uniq(&fi)) {
2848             goto done;
2849         }
2850 
2851     } else if (of->test_dir) {
2852 
2853         if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) {
2854             of->failed = ngx_file_info_n;
2855             goto failed;
2856         }
2857 
2858         if (ngx_is_dir(&fi)) {
2859             goto done;
2860         }
2861     }
2862 
2863     if (!of->log) {
2864 
2865         /*
2866          * Use non-blocking open() not to hang on FIFO files, etc.
2867          * This flag has no effect on a regular files.
2868          */
2869 
2870         fd = ngx_open_file(name, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,
2871                            NGX_FILE_OPEN, 0);
2872 
2873     } else {
2874         fd = ngx_open_file(name, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN,
2875                            NGX_FILE_DEFAULT_ACCESS);
2876     }
2877 
2878     if (fd == NGX_INVALID_FILE) {
2879         of->failed = ngx_open_file_n;
2880         goto failed;
2881     }
2882 
2883     if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
2884         ngx_log_error(NGX_LOG_CRIT, log, ngx_errno,
2885                       ngx_fd_info_n " \"%s\" failed", name);
2886 
2887         if (ngx_close_file(fd) == NGX_FILE_ERROR) {
2888             ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
2889                           ngx_close_file_n " \"%s\" failed", name);
2890         }
2891 
2892         of->fd = NGX_INVALID_FILE;
2893 
2894         return NGX_ERROR;
2895     }
2896 
2897     if (ngx_is_dir(&fi)) {
2898         if (ngx_close_file(fd) == NGX_FILE_ERROR) {
2899             ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
2900                           ngx_close_file_n " \"%s\" failed", name);
2901         }
2902 
2903         of->fd = NGX_INVALID_FILE;
2904 
2905     } else {
2906         of->fd = fd;
2907 
2908         if (of->directio <= ngx_file_size(&fi)) {
2909             if (ngx_directio_on(fd) == NGX_FILE_ERROR) {
2910                 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
2911                               ngx_directio_on_n " \"%s\" failed", name);
2912 
2913             } else {
2914                 of->is_directio = 1;
2915             }
2916         }
2917     }
2918 
2919 done:
2920 
2921     of->uniq = ngx_file_uniq(&fi);
2922     of->mtime = ngx_file_mtime(&fi);
2923     of->size = ngx_file_size(&fi);
2924     of->fs_size = ngx_file_fs_size(&fi);
2925     of->is_dir = ngx_is_dir(&fi);
2926     of->is_file = ngx_is_file(&fi);
2927     of->is_link = ngx_is_link(&fi);
2928     of->is_exec = ngx_is_exec(&fi);
2929 
2930     return NGX_OK;
2931 
2932 failed:
2933 
2934     of->fd = NGX_INVALID_FILE;
2935     of->err = ngx_errno;
2936 
2937     return NGX_ERROR;
2938 }
2939 
2940 
2941 ngx_chain_t *
2942 ngx_http_lua_chain_get_free_buf(ngx_log_t *log, ngx_pool_t *p,
2943     ngx_chain_t **free, size_t len)
2944 {
2945     ngx_buf_t    *b;
2946     ngx_chain_t  *cl;
2947     u_char       *start, *end;
2948 
2949     const ngx_buf_tag_t  tag = (ngx_buf_tag_t) &ngx_http_lua_module;
2950 
2951     if (*free) {
2952         cl = *free;
2953         *free = cl->next;
2954         cl->next = NULL;
2955 
2956         b = cl->buf;
2957         start = b->start;
2958         end = b->end;
2959         if (start && (size_t) (end - start) >= len) {
2960             ngx_log_debug4(NGX_LOG_DEBUG_HTTP, log, 0,
2961                            "lua reuse free buf memory %O >= %uz, cl:%p, p:%p",
2962                            (off_t) (end - start), len, cl, start);
2963 
2964             ngx_memzero(b, sizeof(ngx_buf_t));
2965 
2966             b->start = start;
2967             b->pos = start;
2968             b->last = start;
2969             b->end = end;
2970             b->tag = tag;
2971 
2972             if (len) {
2973                 b->temporary = 1;
2974             }
2975 
2976             return cl;
2977         }
2978 
2979         ngx_log_debug4(NGX_LOG_DEBUG_HTTP, log, 0,
2980                        "lua reuse free buf chain, but reallocate memory "
2981                        "because %uz >= %O, cl:%p, p:%p", len,
2982                        (off_t) (b->end - b->start), cl, b->start);
2983 
2984         if (ngx_buf_in_memory(b) && b->start) {
2985             ngx_pfree(p, b->start);
2986         }
2987 
2988         ngx_memzero(b, sizeof(ngx_buf_t));
2989 
2990         if (len == 0) {
2991             return cl;
2992         }
2993 
2994         b->start = ngx_palloc(p, len);
2995         if (b->start == NULL) {
2996             return NULL;
2997         }
2998 
2999         b->end = b->start + len;
3000 
3001         dd("buf start: %p", cl->buf->start);
3002 
3003         b->pos = b->start;
3004         b->last = b->start;
3005         b->tag = tag;
3006         b->temporary = 1;
3007 
3008         return cl;
3009     }
3010 
3011     cl = ngx_alloc_chain_link(p);
3012     if (cl == NULL) {
3013         return NULL;
3014     }
3015 
3016     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,
3017                    "lua allocate new chainlink and new buf of size %uz, cl:%p",
3018                    len, cl);
3019 
3020     cl->buf = len ? ngx_create_temp_buf(p, len) : ngx_calloc_buf(p);
3021     if (cl->buf == NULL) {
3022         return NULL;
3023     }
3024 
3025     dd("buf start: %p", cl->buf->start);
3026 
3027     cl->buf->tag = tag;
3028     cl->next = NULL;
3029 
3030     return cl;
3031 }
3032 
3033 
3034 static int
3035 ngx_http_lua_thread_traceback(lua_State *L, lua_State *co,
3036     ngx_http_lua_co_ctx_t *coctx)
3037 {
3038     int         base;
3039     int         level, coid;
3040     lua_Debug   ar;
3041 
3042     base = lua_gettop(L);
3043     lua_checkstack(L, 3);
3044     lua_pushliteral(L, "stack traceback:");
3045     coid = 0;
3046 
3047     while (co) {
3048 
3049         if (coid >= NGX_HTTP_LUA_BT_MAX_COROS) {
3050             break;
3051         }
3052 
3053         lua_checkstack(L, 2);
3054         lua_pushfstring(L, "\ncoroutine %d:", coid++);
3055 
3056         level = 0;
3057 
3058         while (lua_getstack(co, level++, &ar)) {
3059 
3060             lua_checkstack(L, 5);
3061 
3062             if (level > NGX_HTTP_LUA_BT_DEPTH) {
3063                 lua_pushliteral(L, "\n\t...");
3064                 break;
3065             }
3066 
3067             lua_pushliteral(L, "\n\t");
3068             lua_getinfo(co, "Snl", &ar);
3069             lua_pushfstring(L, "%s:", ar.short_src);
3070 
3071             if (ar.currentline > 0) {
3072                 lua_pushfstring(L, "%d:", ar.currentline);
3073             }
3074 
3075             if (*ar.namewhat != '\0') {  /* is there a name? */
3076                 lua_pushfstring(L, " in function " LUA_QS, ar.name);
3077 
3078             } else {
3079                 if (*ar.what == 'm') {  /* main? */
3080                     lua_pushliteral(L, " in main chunk");
3081 
3082                 } else if (*ar.what == 'C' || *ar.what == 't') {
3083                     lua_pushliteral(L, " ?");  /* C function or tail call */
3084 
3085                 } else {
3086                     lua_pushfstring(L, " in function <%s:%d>",
3087                                     ar.short_src, ar.linedefined);
3088                 }
3089             }
3090         }
3091 
3092         if (lua_gettop(L) - base >= 15) {
3093             lua_concat(L, lua_gettop(L) - base);
3094         }
3095 
3096         /* check if the coroutine has a parent coroutine*/
3097         coctx = coctx->parent_co_ctx;
3098         if (!coctx || coctx->co_status == NGX_HTTP_LUA_CO_DEAD) {
3099             break;
3100         }
3101 
3102         co = coctx->co;
3103     }
3104 
3105     lua_concat(L, lua_gettop(L) - base);
3106     return 1;
3107 }
3108 
3109 
3110 int
3111 ngx_http_lua_traceback(lua_State *L)
3112 {
3113     if (!lua_isstring(L, 1)) { /* 'message' not a string? */
3114         return 1;  /* keep it intact */
3115     }
3116 
3117     lua_getglobal(L, "debug");
3118     if (!lua_istable(L, -1)) {
3119         lua_pop(L, 1);
3120         return 1;
3121     }
3122 
3123     lua_getfield(L, -1, "traceback");
3124     if (!lua_isfunction(L, -1)) {
3125         lua_pop(L, 2);
3126         return 1;
3127     }
3128 
3129     lua_pushvalue(L, 1);  /* pass error message */
3130     lua_pushinteger(L, 2);  /* skip this function and traceback */
3131     lua_call(L, 2, 1);  /* call debug.traceback */
3132     return 1;
3133 }
3134 
3135 
3136 static void
3137 ngx_http_lua_inject_arg_api(lua_State *L)
3138 {
3139     lua_pushliteral(L, "arg");
3140     lua_newtable(L);    /*  .arg table aka {} */
3141 
3142     lua_createtable(L, 0 /* narr */, 2 /* nrec */);    /*  the metatable */
3143 
3144     lua_pushcfunction(L, ngx_http_lua_param_get);
3145     lua_setfield(L, -2, "__index");
3146 
3147     lua_pushcfunction(L, ngx_http_lua_param_set);
3148     lua_setfield(L, -2, "__newindex");
3149 
3150     lua_setmetatable(L, -2);    /*  tie the metatable to param table */
3151 
3152     dd("top: %d, type -1: %s", lua_gettop(L), luaL_typename(L, -1));
3153 
3154     lua_rawset(L, -3);    /*  set ngx.arg table */
3155 }
3156 
3157 
3158 static int
3159 ngx_http_lua_param_get(lua_State *L)
3160 {
3161     ngx_http_lua_ctx_t          *ctx;
3162     ngx_http_request_t          *r;
3163 
3164     r = ngx_http_lua_get_req(L);
3165     if (r == NULL) {
3166         return 0;
3167     }
3168 
3169     ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
3170     if (ctx == NULL) {
3171         return luaL_error(L, "ctx not found");
3172     }
3173 
3174     ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_SET
3175                                | NGX_HTTP_LUA_CONTEXT_BODY_FILTER);
3176 
3177     if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SET)) {
3178         return ngx_http_lua_setby_param_get(L, r);
3179     }
3180 
3181     /* ctx->context & (NGX_HTTP_LUA_CONTEXT_BODY_FILTER) */
3182 
3183     return ngx_http_lua_body_filter_param_get(L, r);
3184 }
3185 
3186 
3187 static int
3188 ngx_http_lua_param_set(lua_State *L)
3189 {
3190     ngx_http_lua_ctx_t          *ctx;
3191     ngx_http_request_t          *r;
3192 
3193     r = ngx_http_lua_get_req(L);
3194     if (r == NULL) {
3195         return 0;
3196     }
3197 
3198     ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
3199     if (ctx == NULL) {
3200         return luaL_error(L, "ctx not found");
3201     }
3202 
3203     ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_BODY_FILTER);
3204 
3205     return ngx_http_lua_body_filter_param_set(L, r, ctx);
3206 }
3207 
3208 
3209 ngx_http_lua_co_ctx_t *
3210 ngx_http_lua_get_co_ctx(lua_State *L, ngx_http_lua_ctx_t *ctx)
3211 {
3212 #ifdef HAVE_LUA_EXDATA2
3213     return (ngx_http_lua_co_ctx_t *) lua_getexdata2(L);
3214 #else
3215     ngx_uint_t                   i;
3216     ngx_list_part_t             *part;
3217     ngx_http_lua_co_ctx_t       *coctx;
3218 
3219     if (L == ctx->entry_co_ctx.co) {
3220         return &ctx->entry_co_ctx;
3221     }
3222 
3223     if (ctx->user_co_ctx == NULL) {
3224         return NULL;
3225     }
3226 
3227     part = &ctx->user_co_ctx->part;
3228     coctx = part->elts;
3229 
3230     /* FIXME: we should use rbtree here to prevent O(n) lookup overhead */
3231 
3232     for (i = 0; /* void */; i++) {
3233 
3234         if (i >= part->nelts) {
3235             if (part->next == NULL) {
3236                 break;
3237             }
3238 
3239             part = part->next;
3240             coctx = part->elts;
3241             i = 0;
3242         }
3243 
3244         if (coctx[i].co == L) {
3245             return &coctx[i];
3246         }
3247     }
3248 
3249     return NULL;
3250 #endif
3251 }
3252 
3253 
3254 ngx_http_lua_co_ctx_t *
3255 ngx_http_lua_create_co_ctx(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx)
3256 {
3257     ngx_http_lua_co_ctx_t       *coctx;
3258 
3259     if (ctx->user_co_ctx == NULL) {
3260         ctx->user_co_ctx = ngx_list_create(r->pool, 4,
3261                                            sizeof(ngx_http_lua_co_ctx_t));
3262         if (ctx->user_co_ctx == NULL) {
3263             return NULL;
3264         }
3265     }
3266 
3267     coctx = ngx_list_push(ctx->user_co_ctx);
3268     if (coctx == NULL) {
3269         return NULL;
3270     }
3271 
3272     ngx_memzero(coctx, sizeof(ngx_http_lua_co_ctx_t));
3273 
3274     coctx->next_zombie_child_thread = &coctx->zombie_child_threads;
3275     coctx->co_ref = LUA_NOREF;
3276 
3277     return coctx;
3278 }
3279 
3280 
3281 /* this is for callers other than the content handler */
3282 ngx_int_t
3283 ngx_http_lua_run_posted_threads(ngx_connection_t *c, lua_State *L,
3284     ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, ngx_uint_t nreqs)
3285 {
3286     ngx_int_t                        rc;
3287     ngx_http_lua_posted_thread_t    *pt;
3288 
3289     for ( ;; ) {
3290         if (c->destroyed || c->requests != nreqs) {
3291             return NGX_DONE;
3292         }
3293 
3294         pt = ctx->posted_threads;
3295         if (pt == NULL) {
3296             return NGX_DONE;
3297         }
3298 
3299         ctx->posted_threads = pt->next;
3300 
3301         ngx_http_lua_probe_run_posted_thread(r, pt->co_ctx->co,
3302                                              (int) pt->co_ctx->co_status);
3303 
3304         if (pt->co_ctx->co_status != NGX_HTTP_LUA_CO_RUNNING) {
3305             continue;
3306         }
3307 
3308         ctx->cur_co_ctx = pt->co_ctx;
3309 
3310         rc = ngx_http_lua_run_thread(L, r, ctx, 0);
3311 
3312         if (rc == NGX_AGAIN) {
3313             continue;
3314         }
3315 
3316         if (rc == NGX_DONE) {
3317             ngx_http_lua_finalize_request(r, NGX_DONE);
3318             continue;
3319         }
3320 
3321         /* rc == NGX_ERROR || rc >= NGX_OK */
3322 
3323         if (ctx->entered_content_phase) {
3324             ngx_http_lua_finalize_request(r, rc);
3325         }
3326 
3327         return rc;
3328     }
3329 
3330     /* impossible to reach here */
3331 }
3332 
3333 
3334 ngx_int_t
3335 ngx_http_lua_post_thread(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx,
3336     ngx_http_lua_co_ctx_t *coctx)
3337 {
3338     ngx_http_lua_posted_thread_t  **p;
3339     ngx_http_lua_posted_thread_t   *pt;
3340 
3341     pt = ngx_palloc(r->pool, sizeof(ngx_http_lua_posted_thread_t));
3342     if (pt == NULL) {
3343         return NGX_ERROR;
3344     }
3345 
3346     pt->co_ctx = coctx;
3347     pt->next = NULL;
3348 
3349     for (p = &ctx->posted_threads; *p; p = &(*p)->next) { /* void */ }
3350 
3351     *p = pt;
3352 
3353     return NGX_OK;
3354 }
3355 
3356 
3357 static void
3358 ngx_http_lua_finalize_threads(ngx_http_request_t *r,
3359     ngx_http_lua_ctx_t *ctx, lua_State *L)
3360 {
3361     int                              ref;
3362     ngx_uint_t                       i;
3363     ngx_list_part_t                 *part;
3364     ngx_http_lua_co_ctx_t           *cc, *coctx;
3365 
3366     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
3367                    "http lua finalize threads");
3368 
3369 #if 1
3370     coctx = ctx->on_abort_co_ctx;
3371     if (coctx && coctx->co_ref != LUA_NOREF) {
3372         if (coctx->co_status != NGX_HTTP_LUA_CO_SUSPENDED) {
3373             /* the on_abort thread contributes to the coctx->uthreads
3374              * counter only when it actually starts running */
3375             ngx_http_lua_cleanup_pending_operation(coctx);
3376             ctx->uthreads--;
3377         }
3378 
3379         ngx_http_lua_del_thread(r, L, ctx, coctx);
3380         ctx->on_abort_co_ctx = NULL;
3381     }
3382 #endif
3383 
3384     if (ctx->user_co_ctx) {
3385         part = &ctx->user_co_ctx->part;
3386         cc = part->elts;
3387 
3388         for (i = 0; /* void */; i++) {
3389 
3390             if (i >= part->nelts) {
3391                 if (part->next == NULL) {
3392                     break;
3393                 }
3394 
3395                 part = part->next;
3396                 cc = part->elts;
3397                 i = 0;
3398             }
3399 
3400             coctx = &cc[i];
3401 
3402             ref = coctx->co_ref;
3403 
3404             if (ref != LUA_NOREF) {
3405                 ngx_http_lua_cleanup_pending_operation(coctx);
3406 
3407                 ngx_http_lua_del_thread(r, L, ctx, coctx);
3408 
3409                 ctx->uthreads--;
3410             }
3411         }
3412 
3413         ctx->user_co_ctx = NULL;
3414     }
3415 
3416     ngx_http_lua_assert(ctx->uthreads == 0);
3417 
3418     coctx = &ctx->entry_co_ctx;
3419 
3420     ref = coctx->co_ref;
3421     if (ref != LUA_NOREF) {
3422         ngx_http_lua_cleanup_pending_operation(coctx);
3423         ngx_http_lua_del_thread(r, L, ctx, coctx);
3424     }
3425 }
3426 
3427 
3428 static ngx_int_t
3429 ngx_http_lua_post_zombie_thread(ngx_http_request_t *r,
3430     ngx_http_lua_co_ctx_t *parent, ngx_http_lua_co_ctx_t *thread)
3431 {
3432     ngx_http_lua_posted_thread_t   *pt;
3433 
3434     pt = ngx_palloc(r->pool, sizeof(ngx_http_lua_posted_thread_t));
3435     if (pt == NULL) {
3436         return NGX_ERROR;
3437     }
3438 
3439     pt->co_ctx = thread;
3440     pt->next = NULL;
3441 
3442     ngx_http_lua_assert(parent->next_zombie_child_thread != NULL);
3443 
3444     *parent->next_zombie_child_thread = pt;
3445     parent->next_zombie_child_thread = &pt->next;
3446 
3447     return NGX_OK;
3448 }
3449 
3450 
3451 static void
3452 ngx_http_lua_cleanup_zombie_child_uthreads(ngx_http_request_t *r,
3453     lua_State *L, ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t *coctx)
3454 {
3455     ngx_http_lua_posted_thread_t   *pt;
3456 
3457     for (pt = coctx->zombie_child_threads; pt; pt = pt->next) {
3458         if (pt->co_ctx->co_ref != LUA_NOREF) {
3459             ngx_http_lua_del_thread(r, L, ctx, pt->co_ctx);
3460             ctx->uthreads--;
3461         }
3462     }
3463 
3464     coctx->zombie_child_threads = NULL;
3465     coctx->next_zombie_child_thread = &coctx->zombie_child_threads;
3466 }
3467 
3468 
3469 ngx_int_t
3470 ngx_http_lua_check_broken_connection(ngx_http_request_t *r, ngx_event_t *ev)
3471 {
3472     int                  n;
3473     char                 buf[1];
3474     ngx_err_t            err;
3475     ngx_int_t            event;
3476     ngx_connection_t    *c;
3477 
3478     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0,
3479                    "http lua check client, write event:%d, \"%V\"",
3480                    ev->write, &r->uri);
3481 
3482     c = r->connection;
3483 
3484     if (c->error) {
3485         if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) {
3486 
3487             event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT;
3488 
3489             if (ngx_del_event(ev, event, 0) != NGX_OK) {
3490                 return NGX_HTTP_INTERNAL_SERVER_ERROR;
3491             }
3492         }
3493 
3494         return NGX_HTTP_CLIENT_CLOSED_REQUEST;
3495     }
3496 
3497 #if (NGX_HTTP_V2)
3498     if (r->stream) {
3499         return NGX_OK;
3500     }
3501 #endif
3502 
3503 #if (NGX_HAVE_KQUEUE)
3504 
3505     if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
3506 
3507         if (!ev->pending_eof) {
3508             return NGX_OK;
3509         }
3510 
3511         ev->eof = 1;
3512 
3513         if (ev->kq_errno) {
3514             ev->error = 1;
3515         }
3516 
3517         ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno,
3518                       "kevent() reported that client prematurely closed "
3519                       "connection");
3520 
3521         return NGX_HTTP_CLIENT_CLOSED_REQUEST;
3522     }
3523 
3524 #endif
3525 
3526     n = recv(c->fd, buf, 1, MSG_PEEK);
3527 
3528     err = ngx_socket_errno;
3529 
3530     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, err,
3531                    "http lua recv(): %d", n);
3532 
3533     if (ev->write && (n >= 0 || err == NGX_EAGAIN)) {
3534         return NGX_OK;
3535     }
3536 
3537     if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) {
3538         dd("event is active");
3539 
3540         event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT;
3541 
3542 #if 1
3543         if (ngx_del_event(ev, event, 0) != NGX_OK) {
3544             return NGX_HTTP_INTERNAL_SERVER_ERROR;
3545         }
3546 #endif
3547     }
3548 
3549     dd("HERE %d", (int) n);
3550 
3551     if (n > 0) {
3552         return NGX_OK;
3553     }
3554 
3555     if (n == -1) {
3556         if (err == NGX_EAGAIN) {
3557             dd("HERE");
3558             return NGX_OK;
3559         }
3560 
3561         ev->error = 1;
3562 
3563     } else { /* n == 0 */
3564         err = 0;
3565     }
3566 
3567     ev->eof = 1;
3568 
3569     ngx_log_error(NGX_LOG_INFO, ev->log, err,
3570                   "client prematurely closed connection");
3571 
3572     return NGX_HTTP_CLIENT_CLOSED_REQUEST;
3573 }
3574 
3575 
3576 void
3577 ngx_http_lua_rd_check_broken_connection(ngx_http_request_t *r)
3578 {
3579     ngx_int_t                   rc;
3580     ngx_event_t                *rev;
3581     ngx_http_lua_ctx_t         *ctx;
3582 
3583     if (r->done) {
3584         return;
3585     }
3586 
3587     rc = ngx_http_lua_check_broken_connection(r, r->connection->read);
3588 
3589     if (rc == NGX_OK) {
3590         return;
3591     }
3592 
3593     /* rc == NGX_ERROR || rc > NGX_OK */
3594 
3595     ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
3596     if (ctx == NULL) {
3597         return;
3598     }
3599 
3600     if (ctx->on_abort_co_ctx == NULL) {
3601         r->connection->error = 1;
3602         ngx_http_lua_request_cleanup(ctx, 0);
3603         ngx_http_lua_finalize_request(r, rc);
3604         return;
3605     }
3606 
3607     if (ctx->on_abort_co_ctx->co_status != NGX_HTTP_LUA_CO_SUSPENDED) {
3608 
3609         /* on_abort already run for the current request handler */
3610 
3611         rev = r->connection->read;
3612 
3613         if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && rev->active) {
3614             if (ngx_del_event(rev, NGX_READ_EVENT, 0) != NGX_OK) {
3615                 ngx_http_lua_request_cleanup(ctx, 0);
3616                 ngx_http_lua_finalize_request(r,
3617                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
3618                 return;
3619             }
3620         }
3621 
3622         return;
3623     }
3624 
3625     ctx->uthreads++;
3626     ctx->resume_handler = ngx_http_lua_on_abort_resume;
3627     ctx->on_abort_co_ctx->co_status = NGX_HTTP_LUA_CO_RUNNING;
3628     ctx->cur_co_ctx = ctx->on_abort_co_ctx;
3629 
3630     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
3631                    "lua waking up the on_abort callback thread");
3632 
3633     if (ctx->entered_content_phase) {
3634         r->write_event_handler = ngx_http_lua_content_wev_handler;
3635 
3636     } else {
3637         r->write_event_handler = ngx_http_core_run_phases;
3638     }
3639 
3640     r->write_event_handler(r);
3641 }
3642 
3643 
3644 static ngx_int_t
3645 ngx_http_lua_on_abort_resume(ngx_http_request_t *r)
3646 {
3647     lua_State                   *vm;
3648     ngx_int_t                    rc;
3649     ngx_uint_t                   nreqs;
3650     ngx_connection_t            *c;
3651     ngx_http_lua_ctx_t          *ctx;
3652 
3653     ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
3654     if (ctx == NULL) {
3655         return NGX_ERROR;
3656     }
3657 
3658     ctx->resume_handler = ngx_http_lua_wev_handler;
3659 
3660     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
3661                    "lua resuming the on_abort callback thread");
3662 
3663 #if 0
3664     ngx_http_lua_probe_info("tcp resume");
3665 #endif
3666 
3667     c = r->connection;
3668     vm = ngx_http_lua_get_lua_vm(r, ctx);
3669     nreqs = c->requests;
3670 
3671     rc = ngx_http_lua_run_thread(vm, r, ctx, 0);
3672 
3673     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
3674                    "lua run thread returned %d", rc);
3675 
3676     if (rc == NGX_AGAIN) {
3677         return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);
3678     }
3679 
3680     if (rc == NGX_DONE) {
3681         ngx_http_lua_finalize_request(r, NGX_DONE);
3682         return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);
3683     }
3684 
3685     if (ctx->entered_content_phase) {
3686         ngx_http_lua_finalize_request(r, rc);
3687         return NGX_DONE;
3688     }
3689 
3690     return rc;
3691 }
3692 
3693 
3694 ngx_int_t
3695 ngx_http_lua_test_expect(ngx_http_request_t *r)
3696 {
3697     ngx_int_t   n;
3698     ngx_str_t  *expect;
3699 
3700     if (r->expect_tested
3701         || r->headers_in.expect == NULL
3702         || r->http_version < NGX_HTTP_VERSION_11)
3703     {
3704         return NGX_OK;
3705     }
3706 
3707     r->expect_tested = 1;
3708 
3709     expect = &r->headers_in.expect->value;
3710 
3711     if (expect->len != sizeof("100-continue") - 1
3712         || ngx_strncasecmp(expect->data, (u_char *) "100-continue",
3713                            sizeof("100-continue") - 1)
3714            != 0)
3715     {
3716         return NGX_OK;
3717     }
3718 
3719     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
3720                    "send 100 Continue");
3721 
3722     n = r->connection->send(r->connection,
3723                             (u_char *) "HTTP/1.1 100 Continue" CRLF CRLF,
3724                             sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1);
3725 
3726     if (n == sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1) {
3727         return NGX_OK;
3728     }
3729 
3730     /* we assume that such small packet should be send successfully */
3731 
3732     return NGX_ERROR;
3733 }
3734 
3735 
3736 void
3737 ngx_http_lua_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
3738 {
3739     ngx_http_lua_ctx_t              *ctx;
3740 
3741     ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
3742     if (ctx && ctx->cur_co_ctx) {
3743         ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx);
3744     }
3745 
3746     if (r->connection->fd != (ngx_socket_t) -1) {
3747         ngx_http_finalize_request(r, rc);
3748         return;
3749     }
3750 
3751     ngx_http_lua_finalize_fake_request(r, rc);
3752 }
3753 
3754 
3755 void
3756 ngx_http_lua_finalize_fake_request(ngx_http_request_t *r, ngx_int_t rc)
3757 {
3758     ngx_connection_t          *c;
3759 #if (NGX_HTTP_SSL)
3760     ngx_ssl_conn_t            *ssl_conn;
3761     ngx_http_lua_ssl_ctx_t    *cctx;
3762 #endif
3763 
3764     c = r->connection;
3765 
3766     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
3767                    "http lua finalize fake request: %d, a:%d, c:%d",
3768                    rc, r == c->data, r->main->count);
3769 
3770     if (rc == NGX_DONE) {
3771         ngx_http_lua_close_fake_request(r);
3772         return;
3773     }
3774 
3775     if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
3776 
3777 #if (NGX_HTTP_SSL)
3778 
3779         if (r->connection->ssl) {
3780             ssl_conn = r->connection->ssl->connection;
3781             if (ssl_conn) {
3782                 c = ngx_ssl_get_connection(ssl_conn);
3783 
3784                 if (c && c->ssl) {
3785                     cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);
3786                     if (cctx != NULL) {
3787                         cctx->exit_code = 0;
3788                     }
3789                 }
3790             }
3791         }
3792 
3793 #endif
3794 
3795         ngx_http_lua_close_fake_request(r);
3796         return;
3797     }
3798 
3799     if (c->read->timer_set) {
3800         ngx_del_timer(c->read);
3801     }
3802 
3803     if (c->write->timer_set) {
3804         c->write->delayed = 0;
3805         ngx_del_timer(c->write);
3806     }
3807 
3808     ngx_http_lua_close_fake_request(r);
3809 }
3810 
3811 
3812 static void
3813 ngx_http_lua_close_fake_request(ngx_http_request_t *r)
3814 {
3815     ngx_connection_t  *c;
3816 
3817     r = r->main;
3818     c = r->connection;
3819 
3820     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
3821                    "http lua fake request count:%d", r->count);
3822 
3823     if (r->count == 0) {
3824         ngx_log_error(NGX_LOG_ALERT, c->log, 0, "http lua fake request "
3825                       "count is zero");
3826     }
3827 
3828     r->count--;
3829 
3830     if (r->count) {
3831         return;
3832     }
3833 
3834     ngx_http_lua_free_fake_request(r);
3835     ngx_http_lua_close_fake_connection(c);
3836 }
3837 
3838 
3839 void
3840 ngx_http_lua_free_fake_request(ngx_http_request_t *r)
3841 {
3842     ngx_log_t                 *log;
3843     ngx_http_cleanup_t        *cln;
3844 
3845     log = r->connection->log;
3846 
3847     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http lua close fake "
3848                    "request");
3849 
3850     if (r->pool == NULL) {
3851         ngx_log_error(NGX_LOG_ALERT, log, 0, "http lua fake request "
3852                       "already closed");
3853         return;
3854     }
3855 
3856     cln = r->cleanup;
3857     r->cleanup = NULL;
3858 
3859     while (cln) {
3860         if (cln->handler) {
3861             cln->handler(cln->data);
3862         }
3863 
3864         cln = cln->next;
3865     }
3866 
3867     r->request_line.len = 0;
3868 
3869     r->connection->destroyed = 1;
3870 }
3871 
3872 
3873 void
3874 ngx_http_lua_close_fake_connection(ngx_connection_t *c)
3875 {
3876     ngx_pool_t          *pool;
3877     ngx_connection_t    *saved_c = NULL;
3878 
3879     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
3880                    "http lua close fake http connection %p", c);
3881 
3882     c->destroyed = 1;
3883 
3884     pool = c->pool;
3885 
3886     if (c->read->timer_set) {
3887         ngx_del_timer(c->read);
3888     }
3889 
3890     if (c->write->timer_set) {
3891         ngx_del_timer(c->write);
3892     }
3893 
3894     c->read->closed = 1;
3895     c->write->closed = 1;
3896 
3897     /* we temporarily use a valid fd (0) to make ngx_free_connection happy */
3898 
3899     c->fd = 0;
3900 
3901     if (ngx_cycle->files) {
3902         saved_c = ngx_cycle->files[0];
3903     }
3904 
3905     ngx_free_connection(c);
3906 
3907     c->fd = (ngx_socket_t) -1;
3908 
3909     if (ngx_cycle->files) {
3910         ngx_cycle->files[0] = saved_c;
3911     }
3912 
3913     if (pool) {
3914         ngx_destroy_pool(pool);
3915     }
3916 }
3917 
3918 
3919 ngx_int_t
3920 ngx_http_lua_init_vm(lua_State **new_vm, lua_State *parent_vm,
3921     ngx_cycle_t *cycle, ngx_pool_t *pool, ngx_http_lua_main_conf_t *lmcf,
3922     ngx_log_t *log, ngx_pool_cleanup_t **pcln)
3923 {
3924     int                              rc;
3925     lua_State                       *L;
3926     ngx_uint_t                       i;
3927     ngx_pool_cleanup_t              *cln;
3928     ngx_http_lua_preload_hook_t     *hook;
3929     ngx_http_lua_vm_state_t         *state;
3930 
3931     cln = ngx_pool_cleanup_add(pool, 0);
3932     if (cln == NULL) {
3933         return NGX_ERROR;
3934     }
3935 
3936     /* create new Lua VM instance */
3937     L = ngx_http_lua_new_state(parent_vm, cycle, lmcf, log);
3938     if (L == NULL) {
3939         return NGX_ERROR;
3940     }
3941 
3942     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "lua initialize the "
3943                    "global Lua VM %p", L);
3944 
3945     /* register cleanup handler for Lua VM */
3946     cln->handler = ngx_http_lua_cleanup_vm;
3947 
3948     state = ngx_alloc(sizeof(ngx_http_lua_vm_state_t), log);
3949     if (state == NULL) {
3950         return NGX_ERROR;
3951     }
3952 
3953     state->vm = L;
3954     state->count = 1;
3955 
3956     cln->data = state;
3957 
3958     if (lmcf->vm_cleanup == NULL) {
3959         /* this assignment will happen only once,
3960          * and also only for the main Lua VM */
3961         lmcf->vm_cleanup = cln;
3962     }
3963 
3964     if (pcln) {
3965         *pcln = cln;
3966     }
3967 
3968 #ifdef OPENRESTY_LUAJIT
3969     /* load FFI library first since cdata needs it */
3970     luaopen_ffi(L);
3971 #endif
3972 
3973     if (lmcf->preload_hooks) {
3974 
3975         /* register the 3rd-party module's preload hooks */
3976 
3977         lua_getglobal(L, "package");
3978         lua_getfield(L, -1, "preload");
3979 
3980         hook = lmcf->preload_hooks->elts;
3981 
3982         for (i = 0; i < lmcf->preload_hooks->nelts; i++) {
3983 
3984             ngx_http_lua_probe_register_preload_package(L,
3985                                                         hook[i].package);
3986 
3987             lua_pushcfunction(L, hook[i].loader);
3988             lua_setfield(L, -2, (char *) hook[i].package);
3989         }
3990 
3991         lua_pop(L, 2);
3992     }
3993 
3994     *new_vm = L;
3995 
3996     lua_getglobal(L, "require");
3997     lua_pushstring(L, "resty.core");
3998 
3999     rc = lua_pcall(L, 1, 1, 0);
4000     if (rc != 0) {
4001         return NGX_DECLINED;
4002     }
4003 
4004 #ifdef OPENRESTY_LUAJIT
4005     ngx_http_lua_inject_global_write_guard(L, log);
4006 #endif
4007 
4008     return NGX_OK;
4009 }
4010 
4011 
4012 void
4013 ngx_http_lua_cleanup_vm(void *data)
4014 {
4015     lua_State                       *L;
4016     ngx_http_lua_vm_state_t         *state = data;
4017 
4018 #if (DDEBUG)
4019     if (state) {
4020         dd("cleanup VM: c:%d, s:%p", (int) state->count, state->vm);
4021     }
4022 #endif
4023 
4024     if (state) {
4025         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
4026                        "lua decrementing the reference count for Lua VM: %i",
4027                        state->count);
4028 
4029         if (--state->count == 0) {
4030             L = state->vm;
4031             ngx_http_lua_cleanup_conn_pools(L);
4032             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
4033                            "lua close the global Lua VM %p", L);
4034             lua_close(L);
4035             ngx_free(state);
4036         }
4037     }
4038 }
4039 
4040 
4041 ngx_connection_t *
4042 ngx_http_lua_create_fake_connection(ngx_pool_t *pool)
4043 {
4044     ngx_log_t               *log;
4045     ngx_connection_t        *c;
4046     ngx_connection_t        *saved_c = NULL;
4047 
4048     /* (we temporarily use a valid fd (0) to make ngx_get_connection happy) */
4049     if (ngx_cycle->files) {
4050         saved_c = ngx_cycle->files[0];
4051     }
4052 
4053     c = ngx_get_connection(0, ngx_cycle->log);
4054 
4055     if (ngx_cycle->files) {
4056         ngx_cycle->files[0] = saved_c;
4057     }
4058 
4059     if (c == NULL) {
4060         return NULL;
4061     }
4062 
4063     c->fd = (ngx_socket_t) -1;
4064     c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
4065 
4066     if (pool) {
4067         c->pool = pool;
4068 
4069     } else {
4070         c->pool = ngx_create_pool(128, c->log);
4071         if (c->pool == NULL) {
4072             goto failed;
4073         }
4074     }
4075 
4076     log = ngx_pcalloc(c->pool, sizeof(ngx_log_t));
4077     if (log == NULL) {
4078         goto failed;
4079     }
4080 
4081     c->log = log;
4082     c->log->connection = c->number;
4083     c->log->action = NULL;
4084     c->log->data = NULL;
4085 
4086     c->log_error = NGX_ERROR_INFO;
4087 
4088 #if 0
4089     c->buffer = ngx_create_temp_buf(c->pool, 2);
4090     if (c->buffer == NULL) {
4091         goto failed;
4092     }
4093 
4094     c->buffer->start[0] = CR;
4095     c->buffer->start[1] = LF;
4096 #endif
4097 
4098     c->error = 1;
4099 
4100     dd("created fake connection: %p", c);
4101 
4102     return c;
4103 
4104 failed:
4105 
4106     ngx_http_lua_close_fake_connection(c);
4107     return NULL;
4108 }
4109 
4110 
4111 ngx_http_request_t *
4112 ngx_http_lua_create_fake_request(ngx_connection_t *c)
4113 {
4114     ngx_http_request_t      *r;
4115 
4116     r = ngx_pcalloc(c->pool, sizeof(ngx_http_request_t));
4117     if (r == NULL) {
4118         return NULL;
4119     }
4120 
4121     c->requests++;
4122 
4123     r->pool = c->pool;
4124 
4125     dd("r pool allocated: %d", (int) (sizeof(ngx_http_lua_ctx_t)
4126        + sizeof(void *) * ngx_http_max_module + sizeof(ngx_http_cleanup_t)));
4127 
4128 #if 0
4129     hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t));
4130     if (hc == NULL) {
4131         goto failed;
4132     }
4133 
4134     r->header_in = c->buffer;
4135     r->header_end = c->buffer->start;
4136 
4137     if (ngx_list_init(&r->headers_out.headers, r->pool, 0,
4138                       sizeof(ngx_table_elt_t))
4139         != NGX_OK)
4140     {
4141         goto failed;
4142     }
4143 
4144     if (ngx_list_init(&r->headers_in.headers, r->pool, 0,
4145                       sizeof(ngx_table_elt_t))
4146         != NGX_OK)
4147     {
4148         goto failed;
4149     }
4150 #endif
4151 
4152     r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module);
4153     if (r->ctx == NULL) {
4154         return NULL;
4155     }
4156 
4157 #if 0
4158     cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
4159 
4160     r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts
4161                                * sizeof(ngx_http_variable_value_t));
4162     if (r->variables == NULL) {
4163         goto failed;
4164     }
4165 #endif
4166 
4167     r->connection = c;
4168 
4169     r->headers_in.content_length_n = 0;
4170     c->data = r;
4171 #if 0
4172     hc->request = r;
4173     r->http_connection = hc;
4174 #endif
4175     r->signature = NGX_HTTP_MODULE;
4176     r->main = r;
4177     r->count = 1;
4178 
4179     r->method = NGX_HTTP_UNKNOWN;
4180 
4181     r->headers_in.keep_alive_n = -1;
4182     r->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1;
4183     r->subrequests = NGX_HTTP_MAX_SUBREQUESTS + 1;
4184 
4185     r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;
4186     r->discard_body = 1;
4187 
4188     dd("created fake request %p", r);
4189 
4190     return r;
4191 }
4192 
4193 
4194 ngx_int_t
4195 ngx_http_lua_report(ngx_log_t *log, lua_State *L, int status,
4196     const char *prefix)
4197 {
4198     const char      *msg;
4199 
4200     if (status && !lua_isnil(L, -1)) {
4201         msg = lua_tostring(L, -1);
4202         if (msg == NULL) {
4203             msg = "unknown error";
4204         }
4205 
4206         ngx_log_error(NGX_LOG_ERR, log, 0, "%s error: %s", prefix, msg);
4207         lua_pop(L, 1);
4208     }
4209 
4210     /* force a full garbage-collection cycle */
4211     lua_gc(L, LUA_GCCOLLECT, 0);
4212 
4213     return status == 0 ? NGX_OK : NGX_ERROR;
4214 }
4215 
4216 
4217 int
4218 ngx_http_lua_do_call(ngx_log_t *log, lua_State *L)
4219 {
4220     int                 status, base;
4221 #if (NGX_PCRE)
4222     ngx_pool_t         *old_pool;
4223 #endif
4224 
4225     base = lua_gettop(L);  /* function index */
4226     lua_pushcfunction(L, ngx_http_lua_traceback);  /* push traceback function */
4227     lua_insert(L, base);  /* put it under chunk and args */
4228 
4229 #if (NGX_PCRE)
4230     old_pool = ngx_http_lua_pcre_malloc_init(ngx_cycle->pool);
4231 #endif
4232 
4233     status = lua_pcall(L, 0, 0, base);
4234 
4235 #if (NGX_PCRE)
4236     ngx_http_lua_pcre_malloc_done(old_pool);
4237 #endif
4238 
4239     lua_remove(L, base);
4240 
4241     return status;
4242 }
4243 
4244 
4245 static int
4246 ngx_http_lua_get_raw_phase_context(lua_State *L)
4247 {
4248     ngx_http_request_t      *r;
4249     ngx_http_lua_ctx_t      *ctx;
4250 
4251 #ifdef OPENRESTY_LUAJIT
4252     r = lua_getexdata(L);
4253 #else
4254     r = lua_touserdata(L, 1);
4255 #endif
4256 
4257     if (r == NULL) {
4258         return 0;
4259     }
4260 
4261     ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
4262     if (ctx == NULL) {
4263         return 0;
4264     }
4265 
4266     lua_pushinteger(L, (int) ctx->context);
4267     return 1;
4268 }
4269 
4270 
4271 ngx_http_cleanup_t *
4272 ngx_http_lua_cleanup_add(ngx_http_request_t *r, size_t size)
4273 {
4274     ngx_http_cleanup_t  *cln;
4275     ngx_http_lua_ctx_t  *ctx;
4276 
4277     if (size == 0) {
4278         ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
4279 
4280         r = r->main;
4281 
4282         if (ctx != NULL && ctx->free_cleanup) {
4283             cln = ctx->free_cleanup;
4284             ctx->free_cleanup = cln->next;
4285 
4286             dd("reuse cleanup: %p", cln);
4287 
4288             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
4289                            "lua http cleanup reuse: %p", cln);
4290 
4291             cln->handler = NULL;
4292             cln->next = r->cleanup;
4293 
4294             r->cleanup = cln;
4295 
4296             return cln;
4297         }
4298     }
4299 
4300     return ngx_http_cleanup_add(r, size);
4301 }
4302 
4303 
4304 void
4305 ngx_http_lua_cleanup_free(ngx_http_request_t *r, ngx_http_cleanup_pt *cleanup)
4306 {
4307     ngx_http_cleanup_t  **last;
4308     ngx_http_cleanup_t   *cln;
4309     ngx_http_lua_ctx_t   *ctx;
4310 
4311     ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
4312     if (ctx == NULL) {
4313         return;
4314     }
4315 
4316     r = r->main;
4317 
4318     cln = (ngx_http_cleanup_t *)
4319               ((u_char *) cleanup - offsetof(ngx_http_cleanup_t, handler));
4320 
4321     dd("cln: %p, cln->handler: %p, &cln->handler: %p",
4322        cln, cln->handler, &cln->handler);
4323 
4324     last = &r->cleanup;
4325 
4326     while (*last) {
4327         if (*last == cln) {
4328             *last = cln->next;
4329 
4330             cln->next = ctx->free_cleanup;
4331             ctx->free_cleanup = cln;
4332 
4333             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
4334                            "lua http cleanup free: %p", cln);
4335 
4336             return;
4337         }
4338 
4339         last = &(*last)->next;
4340     }
4341 }
4342 
4343 
4344 #if (NGX_HTTP_LUA_HAVE_SA_RESTART)
4345 void
4346 ngx_http_lua_set_sa_restart(ngx_log_t *log)
4347 {
4348     int                    *signo;
4349     int                     sigs[] = NGX_HTTP_LUA_SA_RESTART_SIGS;
4350     struct sigaction        act;
4351 
4352     for (signo = sigs; *signo != 0; signo++) {
4353         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
4354                        "setting SA_RESTART for signal %d", *signo);
4355 
4356         if (sigaction(*signo, NULL, &act) != 0) {
4357             ngx_log_error(NGX_LOG_WARN, log, ngx_errno, "failed to get "
4358                           "sigaction for signal %d", *signo);
4359         }
4360 
4361         act.sa_flags |= SA_RESTART;
4362 
4363         if (sigaction(*signo, &act, NULL) != 0) {
4364             ngx_log_error(NGX_LOG_WARN, log, ngx_errno, "failed to set "
4365                           "sigaction for signal %d", *signo);
4366         }
4367     }
4368 }
4369 #endif
4370 
4371 
4372 size_t
4373 ngx_http_lua_escape_log(u_char *dst, u_char *src, size_t size)
4374 {
4375     size_t          n;
4376     u_char          c;
4377     static u_char   hex[] = "0123456789ABCDEF";
4378 
4379     static uint32_t escape[] = {
4380         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
4381 
4382                     /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
4383         0x00000004, /* 0000 0000 0000 0000  0000 0000 0000 0100 */
4384 
4385                     /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
4386         0x10000000, /* 0001 0000 0000 0000  0000 0000 0000 0000 */
4387 
4388                     /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
4389         0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */
4390 
4391         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
4392         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
4393         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
4394         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
4395     };
4396 
4397     if (dst == NULL) {
4398 
4399         /* find the number of characters to be escaped */
4400 
4401         n = 0;
4402 
4403         while (size) {
4404             c = *src;
4405             if (escape[c >> 5] & (1 << (c & 0x1f))) {
4406                 n += 4;
4407 
4408             } else {
4409                 n++;
4410             }
4411 
4412             src++;
4413             size--;
4414         }
4415 
4416         return n;
4417     }
4418 
4419     while (size) {
4420         c = *src;
4421         if (escape[c >> 5] & (1 << (c & 0x1f))) {
4422             *dst++ = '\\';
4423             *dst++ = 'x';
4424             *dst++ = hex[*src >> 4];
4425             *dst++ = hex[*src & 0xf];
4426             src++;
4427 
4428         } else {
4429             *dst++ = *src++;
4430         }
4431 
4432         size--;
4433     }
4434 
4435     return 0;
4436 }
4437 
4438 
4439 ngx_int_t
4440 ngx_http_lua_copy_escaped_header(ngx_http_request_t *r,
4441     ngx_str_t *dst, int is_name)
4442 {
4443     size_t       escape;
4444     size_t       len;
4445     u_char      *data;
4446     int          type;
4447 
4448     type = is_name
4449         ? NGX_HTTP_LUA_ESCAPE_HEADER_NAME : NGX_HTTP_LUA_ESCAPE_HEADER_VALUE;
4450 
4451     data = dst->data;
4452     len = dst->len;
4453 
4454     escape = ngx_http_lua_escape_uri(NULL, data, len, type);
4455     if (escape > 0) {
4456         /*
4457          * we allocate space for the trailling '\0' char here because nginx
4458          * header values must be null-terminated
4459          */
4460         dst->data = ngx_palloc(r->pool, len + 2 * escape + 1);
4461         if (dst->data == NULL) {
4462             return NGX_ERROR;
4463         }
4464 
4465         ngx_http_lua_escape_uri(dst->data, data, len, type);
4466         dst->len = len + 2 * escape;
4467         dst->data[dst->len] = '\0';
4468     }
4469 
4470     return NGX_OK;
4471 }
4472 
4473 /* vi:set ft=c ts=4 sw=4 et fdm=marker: */
4474