summaryrefslogtreecommitdiff
path: root/ngx_stream_lua-0.0.16/src
diff options
context:
space:
mode:
Diffstat (limited to 'ngx_stream_lua-0.0.16/src')
-rw-r--r--ngx_stream_lua-0.0.16/src/api/ngx_stream_lua_api.h72
-rw-r--r--ngx_stream_lua-0.0.16/src/ddebug.h93
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_api.c221
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_args.c161
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_args.h28
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_balancer.c825
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_balancer.h35
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_cache.c319
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_cache.h32
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_clfactory.c937
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_clfactory.h30
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_common.h538
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_config.c78
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_config.h27
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_consts.c51
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_consts.h29
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_contentby.c355
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_contentby.h37
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_control.c164
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_control.h28
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_coroutine.c450
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_coroutine.h32
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_ctx.c210
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_ctx.h29
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_directive.c1338
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_directive.h70
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_exception.c66
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_exception.h41
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_initby.c50
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_initby.h31
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_initworkerby.c366
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_initworkerby.h33
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_input_filters.c146
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_input_filters.h37
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_lex.c8259
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_lex.h25
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_log.c463
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_log.h33
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_log_ringbuf.c236
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_log_ringbuf.h39
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_logby.c275
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_logby.h30
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_misc.c65
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_misc.h27
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_module.c1170
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_output.c739
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_output.h36
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_pcrefix.c195
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_pcrefix.h36
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_phase.c99
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_phase.h21
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_prereadby.c335
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_prereadby.h21
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_probe.h96
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_regex.c1007
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_request.c350
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_request.h67
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_script.c555
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_script.h96
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_semaphore.c583
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_semaphore.h59
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_shdict.c2068
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_shdict.h121
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_sleep.c223
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_sleep.h28
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_socket_tcp.c6242
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_socket_tcp.h189
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_socket_udp.c1954
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_socket_udp.h76
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_ssl.c47
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_ssl.h61
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_ssl_certby.c1655
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_ssl_certby.h45
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_ssl_client_helloby.c718
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_ssl_client_helloby.h30
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_string.c463
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_string.h28
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_time.c104
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_timer.c968
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_timer.h28
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_uthread.c290
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_uthread.h44
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_util.c3680
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_util.h538
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_variable.c226
-rw-r--r--ngx_stream_lua-0.0.16/src/ngx_stream_lua_worker.c177
86 files changed, 41879 insertions, 0 deletions
diff --git a/ngx_stream_lua-0.0.16/src/api/ngx_stream_lua_api.h b/ngx_stream_lua-0.0.16/src/api/ngx_stream_lua_api.h
new file mode 100644
index 0000000..515cfd3
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/api/ngx_stream_lua_api.h
@@ -0,0 +1,72 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/api/ngx_subsys_lua_api.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_API_H_INCLUDED_
+#define _NGX_STREAM_LUA_API_H_INCLUDED_
+
+
+#include <nginx.h>
+#include <ngx_core.h>
+
+
+
+
+#include <lua.h>
+#include <stdint.h>
+
+
+/* Public API for other Nginx modules */
+
+
+#define ngx_stream_lua_version 16
+
+
+typedef struct {
+ uint8_t type;
+
+ union {
+ int b; /* boolean */
+ lua_Number n; /* number */
+ ngx_str_t s; /* string */
+ } value;
+
+} ngx_stream_lua_value_t;
+
+
+typedef struct {
+ int len;
+ /* this padding hole on 64-bit systems is expected */
+ u_char *data;
+} ngx_stream_lua_ffi_str_t;
+
+
+lua_State *ngx_stream_lua_get_global_state(ngx_conf_t *cf);
+
+
+ngx_int_t ngx_stream_lua_add_package_preload(ngx_conf_t *cf,
+ const char *package, lua_CFunction func);
+
+ngx_int_t ngx_stream_lua_shared_dict_get(ngx_shm_zone_t *shm_zone,
+ u_char *key_data, size_t key_len, ngx_stream_lua_value_t *value);
+
+ngx_shm_zone_t *ngx_stream_lua_find_zone(u_char *name_data,
+ size_t name_len);
+
+ngx_shm_zone_t *ngx_stream_lua_shared_memory_add(ngx_conf_t *cf,
+ ngx_str_t *name, size_t size, void *tag);
+
+
+#endif /* _NGX_STREAM_LUA_API_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ddebug.h b/ngx_stream_lua-0.0.16/src/ddebug.h
new file mode 100644
index 0000000..6ee0cb5
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ddebug.h
@@ -0,0 +1,93 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ddebug.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _DDEBUG_H_INCLUDED_
+#define _DDEBUG_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <nginx.h>
+#include <ngx_core.h>
+
+
+#if defined(DDEBUG) && (DDEBUG)
+
+# if (NGX_HAVE_VARIADIC_MACROS)
+
+# define dd(...) fprintf(stderr, "lua *** %s: ", __func__); \
+ fprintf(stderr, __VA_ARGS__); \
+ fprintf(stderr, " at %s line %d.\n", __FILE__, __LINE__)
+
+# else
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <stdarg.h>
+
+static ngx_inline void
+dd(const char *fmt, ...) {
+}
+
+# endif
+
+#else
+
+# if (NGX_HAVE_VARIADIC_MACROS)
+
+# define dd(...)
+
+# else
+
+#include <stdarg.h>
+
+static ngx_inline void
+dd(const char *fmt, ...) {
+}
+
+# endif
+
+#endif
+
+#if defined(DDEBUG) && (DDEBUG)
+
+#define dd_check_read_event_handler(r) \
+ dd("r->read_event_handler = %s", \
+ r->read_event_handler == ngx_http_block_reading ? \
+ "ngx_http_block_reading" : \
+ r->read_event_handler == ngx_http_test_reading ? \
+ "ngx_http_test_reading" : \
+ r->read_event_handler == ngx_http_request_empty_handler ? \
+ "ngx_http_request_empty_handler" : "UNKNOWN")
+
+#define dd_check_write_event_handler(r) \
+ dd("r->write_event_handler = %s", \
+ r->write_event_handler == ngx_http_handler ? \
+ "ngx_http_handler" : \
+ r->write_event_handler == ngx_http_core_run_phases ? \
+ "ngx_http_core_run_phases" : \
+ r->write_event_handler == ngx_http_request_empty_handler ? \
+ "ngx_http_request_empty_handler" : "UNKNOWN")
+
+#else
+
+#define dd_check_read_event_handler(r)
+#define dd_check_write_event_handler(r)
+
+#endif
+
+
+#endif /* _DDEBUG_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_api.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_api.c
new file mode 100644
index 0000000..cf01b36
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_api.c
@@ -0,0 +1,221 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_api.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_common.h"
+#include "api/ngx_stream_lua_api.h"
+#include "ngx_stream_lua_shdict.h"
+#include "ngx_stream_lua_util.h"
+
+
+lua_State *
+ngx_stream_lua_get_global_state(ngx_conf_t *cf)
+{
+ ngx_stream_lua_main_conf_t *lmcf;
+
+ lmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_lua_module);
+
+ return lmcf->lua;
+}
+
+
+
+
+static ngx_int_t ngx_stream_lua_shared_memory_init(ngx_shm_zone_t *shm_zone,
+ void *data);
+
+
+ngx_int_t
+ngx_stream_lua_add_package_preload(ngx_conf_t *cf, const char *package,
+ lua_CFunction func)
+{
+ lua_State *L;
+
+ ngx_stream_lua_main_conf_t *lmcf;
+ ngx_stream_lua_preload_hook_t *hook;
+
+ lmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_lua_module);
+
+ L = lmcf->lua;
+
+ if (L) {
+ lua_getglobal(L, "package");
+ lua_getfield(L, -1, "preload");
+ lua_pushcfunction(L, func);
+ lua_setfield(L, -2, package);
+ lua_pop(L, 2);
+ }
+
+ /* we always register preload_hooks since we always create new Lua VMs
+ * when lua code cache is off. */
+
+ if (lmcf->preload_hooks == NULL) {
+ lmcf->preload_hooks =
+ ngx_array_create(cf->pool, 4,
+ sizeof(ngx_stream_lua_preload_hook_t));
+
+ if (lmcf->preload_hooks == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ hook = ngx_array_push(lmcf->preload_hooks);
+ if (hook == NULL) {
+ return NGX_ERROR;
+ }
+
+ hook->package = (u_char *) package;
+ hook->loader = func;
+
+ return NGX_OK;
+}
+
+
+ngx_shm_zone_t *
+ngx_stream_lua_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name,
+ size_t size, void *tag)
+{
+ ngx_stream_lua_main_conf_t *lmcf;
+ ngx_stream_lua_shm_zone_ctx_t *ctx;
+
+ ngx_shm_zone_t **zp;
+ ngx_shm_zone_t *zone;
+ ngx_int_t n;
+
+ lmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_lua_module);
+ if (lmcf == NULL) {
+ return NULL;
+ }
+
+ if (lmcf->shm_zones == NULL) {
+ lmcf->shm_zones = ngx_palloc(cf->pool, sizeof(ngx_array_t));
+ if (lmcf->shm_zones == NULL) {
+ return NULL;
+ }
+
+ if (ngx_array_init(lmcf->shm_zones, cf->pool, 2,
+ sizeof(ngx_shm_zone_t *))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+ }
+
+ zone = ngx_shared_memory_add(cf, name, (size_t) size, tag);
+ if (zone == NULL) {
+ return NULL;
+ }
+
+ if (zone->data) {
+ ctx = (ngx_stream_lua_shm_zone_ctx_t *) zone->data;
+ return &ctx->zone;
+ }
+
+ n = sizeof(ngx_stream_lua_shm_zone_ctx_t);
+
+ ctx = ngx_pcalloc(cf->pool, n);
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ ctx->lmcf = lmcf;
+ ctx->log = &cf->cycle->new_log;
+ ctx->cycle = cf->cycle;
+
+ ngx_memcpy(&ctx->zone, zone, sizeof(ngx_shm_zone_t));
+
+ zp = ngx_array_push(lmcf->shm_zones);
+ if (zp == NULL) {
+ return NULL;
+ }
+
+ *zp = zone;
+
+ /* set zone init */
+ zone->init = ngx_stream_lua_shared_memory_init;
+ zone->data = ctx;
+
+ lmcf->requires_shm = 1;
+
+ return &ctx->zone;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_shared_memory_init(ngx_shm_zone_t *shm_zone, void *data)
+{
+ ngx_stream_lua_shm_zone_ctx_t *octx = data;
+ ngx_stream_lua_main_conf_t *lmcf;
+ ngx_stream_lua_shm_zone_ctx_t *ctx;
+
+ ngx_shm_zone_t *ozone;
+ void *odata;
+ ngx_int_t rc;
+ volatile ngx_cycle_t *saved_cycle;
+ ngx_shm_zone_t *zone;
+
+ ctx = (ngx_stream_lua_shm_zone_ctx_t *) shm_zone->data;
+ zone = &ctx->zone;
+
+ odata = NULL;
+ if (octx) {
+ ozone = &octx->zone;
+ odata = ozone->data;
+ }
+
+ zone->shm = shm_zone->shm;
+#if defined(nginx_version) && nginx_version >= 1009000
+ zone->noreuse = shm_zone->noreuse;
+#endif
+
+ if (zone->init(zone, odata) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ dd("get lmcf");
+
+ lmcf = ctx->lmcf;
+ if (lmcf == NULL) {
+ return NGX_ERROR;
+ }
+
+ dd("lmcf->lua: %p", lmcf->lua);
+
+ lmcf->shm_zones_inited++;
+
+ if (lmcf->shm_zones_inited == lmcf->shm_zones->nelts
+ && lmcf->init_handler && !ngx_test_config)
+ {
+ saved_cycle = ngx_cycle;
+ ngx_cycle = ctx->cycle;
+
+ rc = lmcf->init_handler(ctx->log, lmcf, lmcf->lua);
+
+ ngx_cycle = saved_cycle;
+
+ if (rc != NGX_OK) {
+ /* an error happened */
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_args.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_args.c
new file mode 100644
index 0000000..0cdaef1
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_args.c
@@ -0,0 +1,161 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_args.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_args.h"
+#include "ngx_stream_lua_util.h"
+
+
+
+
+int
+ngx_stream_lua_parse_args(lua_State *L, u_char *buf, u_char *last, int max)
+{
+ u_char *p, *q;
+ u_char *src, *dst;
+ unsigned parsing_value;
+ size_t len;
+ int count = 0;
+ int top;
+
+ top = lua_gettop(L);
+
+ p = buf;
+
+ parsing_value = 0;
+ q = p;
+
+ while (p != last) {
+ if (*p == '=' && ! parsing_value) {
+ /* key data is between p and q */
+
+ src = q; dst = q;
+
+ ngx_stream_lua_unescape_uri(&dst, &src, p - q,
+ NGX_UNESCAPE_URI_COMPONENT);
+
+ dd("pushing key %.*s", (int) (dst - q), q);
+
+ /* push the key */
+ lua_pushlstring(L, (char *) q, dst - q);
+
+ /* skip the current '=' char */
+ p++;
+
+ q = p;
+ parsing_value = 1;
+
+ } else if (*p == '&') {
+ /* reached the end of a key or a value, just save it */
+ src = q; dst = q;
+
+ ngx_stream_lua_unescape_uri(&dst, &src, p - q,
+ NGX_UNESCAPE_URI_COMPONENT);
+
+ dd("pushing key or value %.*s", (int) (dst - q), q);
+
+ /* push the value or key */
+ lua_pushlstring(L, (char *) q, dst - q);
+
+ /* skip the current '&' char */
+ p++;
+
+ q = p;
+
+ if (parsing_value) {
+ /* end of the current pair's value */
+ parsing_value = 0;
+
+ } else {
+ /* the current parsing pair takes no value,
+ * just push the value "true" */
+ dd("pushing boolean true");
+
+ lua_pushboolean(L, 1);
+ }
+
+ (void) lua_tolstring(L, -2, &len);
+
+ if (len == 0) {
+ /* ignore empty string key pairs */
+ dd("popping key and value...");
+ lua_pop(L, 2);
+
+ } else {
+ dd("setting table...");
+ ngx_stream_lua_set_multi_value_table(L, top);
+ }
+
+ if (max > 0 && ++count == max) {
+ lua_pushliteral(L, "truncated");
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "stream lua hit query args limit %d",
+ max);
+ return 2;
+ }
+
+ } else {
+ p++;
+ }
+ }
+
+ if (p != q || parsing_value) {
+ src = q; dst = q;
+
+ ngx_stream_lua_unescape_uri(&dst, &src, p - q,
+ NGX_UNESCAPE_URI_COMPONENT);
+
+ dd("pushing key or value %.*s", (int) (dst - q), q);
+
+ lua_pushlstring(L, (char *) q, dst - q);
+
+ if (!parsing_value) {
+ dd("pushing boolean true...");
+ lua_pushboolean(L, 1);
+ }
+
+ (void) lua_tolstring(L, -2, &len);
+
+ if (len == 0) {
+ /* ignore empty string key pairs */
+ dd("popping key and value...");
+ lua_pop(L, 2);
+
+ } else {
+ dd("setting table...");
+ ngx_stream_lua_set_multi_value_table(L, top);
+ }
+ }
+
+ dd("gettop: %d", lua_gettop(L));
+ dd("type: %s", lua_typename(L, lua_type(L, 1)));
+
+ if (lua_gettop(L) != top) {
+ return luaL_error(L, "internal error: stack in bad state");
+ }
+
+ return 1;
+}
+
+
+
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_args.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_args.h
new file mode 100644
index 0000000..8620836
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_args.h
@@ -0,0 +1,28 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_args.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_ARGS_H_INCLUDED_
+#define _NGX_STREAM_LUA_ARGS_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+
+int ngx_stream_lua_parse_args(lua_State *L, u_char *buf, u_char *last, int max);
+
+
+#endif /* _NGX_STREAM_LUA_ARGS_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_balancer.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_balancer.c
new file mode 100644
index 0000000..6a4239e
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_balancer.c
@@ -0,0 +1,825 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_balancer.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_cache.h"
+#include "ngx_stream_lua_balancer.h"
+#include "ngx_stream_lua_util.h"
+#include "ngx_stream_lua_directive.h"
+
+
+struct ngx_stream_lua_balancer_peer_data_s {
+ /* the round robin data must be first */
+ ngx_stream_upstream_rr_peer_data_t rrp;
+
+ ngx_stream_lua_srv_conf_t *conf;
+ ngx_stream_lua_request_t *request;
+
+ ngx_uint_t more_tries;
+ ngx_uint_t total_tries;
+
+ struct sockaddr *sockaddr;
+ socklen_t socklen;
+
+ ngx_str_t *host;
+ in_port_t port;
+
+ int last_peer_state;
+
+};
+
+
+#if (NGX_STREAM_SSL && HAVE_NGX_STREAM_BALANCER_EXPORT_PATCH)
+static ngx_int_t ngx_stream_lua_balancer_set_session(ngx_peer_connection_t *pc,
+ void *data);
+static void ngx_stream_lua_balancer_save_session(ngx_peer_connection_t *pc,
+ void *data);
+#endif
+static ngx_int_t ngx_stream_lua_balancer_init(ngx_conf_t *cf,
+ ngx_stream_upstream_srv_conf_t *us);
+
+
+static ngx_int_t ngx_stream_lua_balancer_init_peer(ngx_stream_session_t *s,
+ ngx_stream_upstream_srv_conf_t *us);
+
+
+#if (HAS_NGX_STREAM_PROXY_GET_NEXT_UPSTREAM_TRIES_PATCH)
+ngx_uint_t
+ngx_stream_proxy_get_next_upstream_tries(ngx_stream_session_t *s);
+#endif
+
+
+static ngx_int_t ngx_stream_lua_balancer_get_peer(ngx_peer_connection_t *pc,
+ void *data);
+static ngx_int_t ngx_stream_lua_balancer_by_chunk(lua_State *L,
+ ngx_stream_lua_request_t *r);
+void ngx_stream_lua_balancer_free_peer(ngx_peer_connection_t *pc, void *data,
+ ngx_uint_t state);
+
+
+ngx_int_t
+ngx_stream_lua_balancer_handler_file(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_srv_conf_t *lscf, lua_State *L)
+{
+ ngx_int_t rc;
+
+ rc = ngx_stream_lua_cache_loadfile(r->connection->log, L,
+ lscf->balancer.src.data,
+ lscf->balancer.src_key);
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ /* make sure we have a valid code chunk */
+ ngx_stream_lua_assert(lua_isfunction(L, -1));
+
+ return ngx_stream_lua_balancer_by_chunk(L, r);
+}
+
+
+ngx_int_t
+ngx_stream_lua_balancer_handler_inline(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_srv_conf_t *lscf, lua_State *L)
+{
+ ngx_int_t rc;
+
+ rc = ngx_stream_lua_cache_loadbuffer(r->connection->log, L,
+ lscf->balancer.src.data,
+ lscf->balancer.src.len,
+ lscf->balancer.src_key,
+ "=balancer_by_lua");
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ /* make sure we have a valid code chunk */
+ ngx_stream_lua_assert(lua_isfunction(L, -1));
+
+ return ngx_stream_lua_balancer_by_chunk(L, r);
+}
+
+
+char *
+ngx_stream_lua_balancer_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ char *rv;
+ ngx_conf_t save;
+
+ save = *cf;
+ cf->handler = ngx_stream_lua_balancer_by_lua;
+ cf->handler_conf = conf;
+
+ rv = ngx_stream_lua_conf_lua_block_parse(cf, cmd);
+
+ *cf = save;
+
+ return rv;
+}
+
+
+char *
+ngx_stream_lua_balancer_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ u_char *p;
+ u_char *name;
+ ngx_str_t *value;
+
+ ngx_stream_lua_srv_conf_t *lscf = conf;
+ ngx_stream_upstream_srv_conf_t *uscf;
+
+ dd("enter");
+
+ /* must specify a content handler */
+ if (cmd->post == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (lscf->balancer.handler) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ lscf->balancer.handler = (ngx_stream_lua_srv_conf_handler_pt) cmd->post;
+
+ if (cmd->post == ngx_stream_lua_balancer_handler_file) {
+ /* Lua code in an external file */
+
+ name = ngx_stream_lua_rebase_path(cf->pool, value[1].data,
+ value[1].len);
+ if (name == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ lscf->balancer.src.data = name;
+ lscf->balancer.src.len = ngx_strlen(name);
+
+ p = ngx_palloc(cf->pool, NGX_STREAM_LUA_FILE_KEY_LEN + 1);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ lscf->balancer.src_key = p;
+
+ p = ngx_copy(p, NGX_STREAM_LUA_FILE_TAG,
+ NGX_STREAM_LUA_FILE_TAG_LEN);
+ p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len);
+ *p = '\0';
+
+ } else {
+ /* inlined Lua code */
+
+ lscf->balancer.src = value[1];
+
+ p = ngx_palloc(cf->pool,
+ sizeof("balancer_by_lua") + NGX_STREAM_LUA_INLINE_KEY_LEN);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ lscf->balancer.src_key = p;
+
+ p = ngx_copy(p, "balancer_by_lua", sizeof("balancer_by_lua") - 1);
+ p = ngx_copy(p, NGX_STREAM_LUA_INLINE_TAG,
+ NGX_STREAM_LUA_INLINE_TAG_LEN);
+ p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len);
+ *p = '\0';
+ }
+
+ uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module);
+
+ if (uscf->peer.init_upstream) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "load balancing method redefined");
+ }
+
+ uscf->peer.init_upstream = ngx_stream_lua_balancer_init;
+
+ uscf->flags = NGX_STREAM_UPSTREAM_CREATE
+ |NGX_STREAM_UPSTREAM_WEIGHT
+ |NGX_STREAM_UPSTREAM_MAX_FAILS
+ |NGX_STREAM_UPSTREAM_FAIL_TIMEOUT
+ |NGX_STREAM_UPSTREAM_DOWN;
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_balancer_init(ngx_conf_t *cf,
+ ngx_stream_upstream_srv_conf_t *us)
+{
+ if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ /* this callback is called upon individual requests */
+ us->peer.init = ngx_stream_lua_balancer_init_peer;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_balancer_init_peer(ngx_stream_session_t *s,
+ ngx_stream_upstream_srv_conf_t *us)
+{
+ ngx_stream_lua_srv_conf_t *bcf;
+ ngx_stream_lua_balancer_peer_data_t *bp;
+ ngx_stream_upstream_t *upstream;
+
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+
+ ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ ctx = ngx_stream_lua_create_ctx(s);
+
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ r = ctx->request;
+
+ upstream = s->upstream;
+
+
+ bp = ngx_pcalloc(r->pool, sizeof(ngx_stream_lua_balancer_peer_data_t));
+ if (bp == NULL) {
+ return NGX_ERROR;
+ }
+
+ upstream->peer.data = &bp->rrp;
+
+ if (ngx_stream_upstream_init_round_robin_peer(s, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ upstream->peer.get = ngx_stream_lua_balancer_get_peer;
+ upstream->peer.free = ngx_stream_lua_balancer_free_peer;
+
+ upstream->peer.notify = NULL;
+
+#if (NGX_STREAM_SSL && HAVE_NGX_STREAM_BALANCER_EXPORT_PATCH)
+ upstream->peer.set_session = ngx_stream_lua_balancer_set_session;
+ upstream->peer.save_session = ngx_stream_lua_balancer_save_session;
+#endif
+
+ bcf = ngx_stream_conf_upstream_srv_conf(us, ngx_stream_lua_module);
+
+ bp->conf = bcf;
+ bp->request = r;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_balancer_get_peer(ngx_peer_connection_t *pc, void *data)
+{
+ lua_State *L;
+ ngx_int_t rc;
+ ngx_stream_lua_request_t *r;
+
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_srv_conf_t *lscf;
+ ngx_stream_lua_main_conf_t *lmcf;
+ ngx_stream_lua_balancer_peer_data_t *bp = data;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "lua balancer peer, tries: %ui", pc->tries);
+
+ lscf = bp->conf;
+
+ r = bp->request;
+
+ ngx_stream_lua_assert(lscf->balancer.handler && r);
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+
+ if (ctx == NULL) {
+
+ ctx = ngx_stream_lua_create_ctx(r->session);
+
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ L = ngx_stream_lua_get_lua_vm(r, ctx);
+
+ } else {
+ L = ngx_stream_lua_get_lua_vm(r, ctx);
+
+ dd("reset ctx");
+ ngx_stream_lua_reset_ctx(r, L, ctx);
+ }
+
+ ctx->context = NGX_STREAM_LUA_CONTEXT_BALANCER;
+
+ bp->sockaddr = NULL;
+ bp->socklen = 0;
+ bp->more_tries = 0;
+ bp->total_tries++;
+
+ lmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_lua_module);
+
+ /* balancer_by_lua does not support yielding and
+ * there cannot be any conflicts among concurrent requests,
+ * thus it is safe to store the peer data in the main conf.
+ */
+ lmcf->balancer_peer_data = bp;
+
+ rc = lscf->balancer.handler(r, lscf, L);
+
+ if (rc == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (ctx->exited && ctx->exit_code != NGX_OK) {
+ rc = ctx->exit_code;
+ if (rc == NGX_ERROR
+ || rc == NGX_BUSY
+ || rc == NGX_DECLINED
+#ifdef HAVE_BALANCER_STATUS_CODE_PATCH
+ || rc >= NGX_STREAM_SPECIAL_RESPONSE
+#endif
+ ) {
+ return rc;
+ }
+
+ if (rc > NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ if (bp->sockaddr && bp->socklen) {
+ pc->sockaddr = bp->sockaddr;
+ pc->socklen = bp->socklen;
+ pc->cached = 0;
+ pc->connection = NULL;
+ pc->name = bp->host;
+
+ bp->rrp.peers->single = 0;
+
+ if (bp->more_tries) {
+ r->session->upstream->peer.tries += bp->more_tries;
+ }
+
+ dd("tries: %d", (int) r->session->upstream->peer.tries);
+
+ return NGX_OK;
+ }
+
+ return ngx_stream_upstream_get_round_robin_peer(pc, &bp->rrp);
+}
+
+
+static ngx_int_t
+ngx_stream_lua_balancer_by_chunk(lua_State *L, ngx_stream_lua_request_t *r)
+{
+ u_char *err_msg;
+ size_t len;
+ ngx_int_t rc;
+
+ /* init nginx context in Lua VM */
+ ngx_stream_lua_set_req(L, r);
+
+#ifndef OPENRESTY_LUAJIT
+ ngx_stream_lua_create_new_globals_table(L, 0 /* narr */, 1 /* nrec */);
+
+ /* {{{ make new env inheriting main thread's globals table */
+ lua_createtable(L, 0, 1 /* nrec */); /* the metatable for the new env */
+ ngx_stream_lua_get_globals_table(L);
+ lua_setfield(L, -2, "__index");
+ lua_setmetatable(L, -2); /* setmetatable({}, {__index = _G}) */
+ /* }}} */
+
+ lua_setfenv(L, -2); /* set new running env for the code closure */
+#endif /* OPENRESTY_LUAJIT */
+
+ lua_pushcfunction(L, ngx_stream_lua_traceback);
+ lua_insert(L, 1); /* put it under chunk and args */
+
+ /* protected call user code */
+ rc = lua_pcall(L, 0, 1, 1);
+
+ lua_remove(L, 1); /* remove traceback function */
+
+ dd("rc == %d", (int) rc);
+
+ if (rc != 0) {
+ /* error occurred when running loaded code */
+ err_msg = (u_char *) lua_tolstring(L, -1, &len);
+
+ if (err_msg == NULL) {
+ err_msg = (u_char *) "unknown reason";
+ len = sizeof("unknown reason") - 1;
+ }
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "failed to run balancer_by_lua*: %*s", len, err_msg);
+
+ lua_settop(L, 0); /* clear remaining elems on stack */
+
+ return NGX_ERROR;
+ }
+
+ lua_settop(L, 0); /* clear remaining elems on stack */
+ return rc;
+}
+
+
+void
+ngx_stream_lua_balancer_free_peer(ngx_peer_connection_t *pc, void *data,
+ ngx_uint_t state)
+{
+ ngx_stream_lua_balancer_peer_data_t *bp = data;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "lua balancer free peer, tries: %ui", pc->tries);
+
+ if (bp->sockaddr && bp->socklen) {
+ bp->last_peer_state = (int) state;
+
+ if (pc->tries) {
+ pc->tries--;
+ }
+
+ return;
+ }
+
+ /* fallback */
+
+ ngx_stream_upstream_free_round_robin_peer(pc, data, state);
+}
+
+
+#if (NGX_STREAM_SSL && HAVE_NGX_STREAM_BALANCER_EXPORT_PATCH)
+static ngx_int_t
+ngx_stream_lua_balancer_set_session(ngx_peer_connection_t *pc, void *data)
+{
+ ngx_stream_lua_balancer_peer_data_t *bp = data;
+
+ if (bp->sockaddr && bp->socklen) {
+ /* TODO */
+ return NGX_OK;
+ }
+
+ return ngx_stream_upstream_set_round_robin_peer_session(pc, &bp->rrp);
+}
+
+
+static void
+ngx_stream_lua_balancer_save_session(ngx_peer_connection_t *pc, void *data)
+{
+ ngx_stream_lua_balancer_peer_data_t *bp = data;
+
+ if (bp->sockaddr && bp->socklen) {
+ /* TODO */
+ return;
+ }
+
+ ngx_stream_upstream_save_round_robin_peer_session(pc, &bp->rrp);
+ return;
+}
+
+#endif
+
+
+int
+ngx_stream_lua_ffi_balancer_set_current_peer(ngx_stream_lua_request_t *r,
+ const u_char *addr, size_t addr_len, int port, char **err)
+{
+ ngx_url_t url;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_upstream_t *u;
+
+ ngx_stream_lua_main_conf_t *lmcf;
+ ngx_stream_lua_balancer_peer_data_t *bp;
+
+ if (r == NULL) {
+ *err = "no request found";
+ return NGX_ERROR;
+ }
+
+ u = r->session->upstream;
+
+ if (u == NULL) {
+ *err = "no upstream found";
+ return NGX_ERROR;
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ *err = "no ctx found";
+ return NGX_ERROR;
+ }
+
+ if ((ctx->context & NGX_STREAM_LUA_CONTEXT_BALANCER) == 0) {
+ *err = "API disabled in the current context";
+ return NGX_ERROR;
+ }
+
+ lmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_lua_module);
+
+ /* we cannot read r->upstream->peer.data here directly because
+ * it could be overridden by other modules like
+ * ngx_stream_upstream_keepalive_module.
+ */
+ bp = lmcf->balancer_peer_data;
+ if (bp == NULL) {
+ *err = "no upstream peer data found";
+ return NGX_ERROR;
+ }
+
+ ngx_memzero(&url, sizeof(ngx_url_t));
+
+ url.url.data = ngx_palloc(r->pool, addr_len);
+ if (url.url.data == NULL) {
+ *err = "no memory";
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(url.url.data, addr, addr_len);
+
+ url.url.len = addr_len;
+ url.default_port = (in_port_t) port;
+ url.uri_part = 0;
+ url.no_resolve = 1;
+
+ if (ngx_parse_url(r->pool, &url) != NGX_OK) {
+ if (url.err) {
+ *err = url.err;
+ }
+
+ return NGX_ERROR;
+ }
+
+ if (url.addrs && url.addrs[0].sockaddr) {
+ bp->sockaddr = url.addrs[0].sockaddr;
+ bp->socklen = url.addrs[0].socklen;
+ bp->host = &url.addrs[0].name;
+
+ } else {
+ *err = "no host allowed";
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+#if (NGX_STREAM_HAVE_PROXY_TIMEOUT_FIELDS_PATCH)
+int
+ngx_stream_lua_ffi_balancer_set_timeouts(ngx_stream_lua_request_t *r,
+ long connect_timeout, long timeout,
+ char **err)
+{
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_proxy_ctx_t *pctx;
+
+ if (r == NULL) {
+ *err = "no request found";
+ return NGX_ERROR;
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ *err = "no ctx found";
+ return NGX_ERROR;
+ }
+
+ if ((ctx->context & NGX_STREAM_LUA_CONTEXT_BALANCER) == 0) {
+ *err = "API disabled in the current context";
+ return NGX_ERROR;
+ }
+
+ pctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_proxy_module);
+ ngx_stream_lua_assert(pctx != NULL);
+ if (pctx == NULL) {
+ *err = "no proxy ctx found";
+ return NGX_ERROR;
+ }
+
+ if (connect_timeout > 0) {
+ pctx->connect_timeout = connect_timeout;
+ }
+
+ if (timeout > 0) {
+ pctx->timeout = timeout;
+ }
+
+ return NGX_OK;
+}
+#else
+int
+ngx_stream_lua_ffi_balancer_set_timeouts(ngx_stream_lua_request_t *r,
+ long connect_timeout, long timeout,
+ char **err)
+{
+ *err = "required Nginx patch not present, API disabled";
+ return NGX_ERROR;
+}
+#endif
+
+
+int
+ngx_stream_lua_ffi_balancer_set_more_tries(ngx_stream_lua_request_t *r,
+ int count, char **err)
+{
+#if (HAS_NGX_STREAM_PROXY_GET_NEXT_UPSTREAM_TRIES_PATCH)
+ ngx_uint_t max_tries, total;
+#endif
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_upstream_t *u;
+
+ ngx_stream_lua_main_conf_t *lmcf;
+ ngx_stream_lua_balancer_peer_data_t *bp;
+
+ if (r == NULL) {
+ *err = "no request found";
+ return NGX_ERROR;
+ }
+
+ u = r->session->upstream;
+
+ if (u == NULL) {
+ *err = "no upstream found";
+ return NGX_ERROR;
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ *err = "no ctx found";
+ return NGX_ERROR;
+ }
+
+ if ((ctx->context & NGX_STREAM_LUA_CONTEXT_BALANCER) == 0) {
+ *err = "API disabled in the current context";
+ return NGX_ERROR;
+ }
+
+ lmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_lua_module);
+
+ bp = lmcf->balancer_peer_data;
+ if (bp == NULL) {
+ *err = "no upstream peer data found";
+ return NGX_ERROR;
+ }
+
+#if (HAS_NGX_STREAM_PROXY_GET_NEXT_UPSTREAM_TRIES_PATCH)
+ max_tries = ngx_stream_proxy_get_next_upstream_tries(r->session);
+ total = bp->total_tries + u->peer.tries - 1;
+
+ if (max_tries && total + count > max_tries) {
+ count = max_tries - total;
+ *err = "reduced tries due to limit";
+
+ } else {
+ *err = NULL;
+ }
+#else
+ *err = NULL;
+#endif
+
+ bp->more_tries = count;
+ return NGX_OK;
+}
+
+
+int
+ngx_stream_lua_ffi_balancer_get_last_failure(ngx_stream_lua_request_t *r,
+ int *status, char **err)
+{
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_upstream_t *u;
+ ngx_stream_lua_balancer_peer_data_t *bp;
+ ngx_stream_lua_main_conf_t *lmcf;
+
+ if (r == NULL) {
+ *err = "no request found";
+ return NGX_ERROR;
+ }
+
+ u = r->session->upstream;
+
+ if (u == NULL) {
+ *err = "no upstream found";
+ return NGX_ERROR;
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ *err = "no ctx found";
+ return NGX_ERROR;
+ }
+
+ if ((ctx->context & NGX_STREAM_LUA_CONTEXT_BALANCER) == 0) {
+ *err = "API disabled in the current context";
+ return NGX_ERROR;
+ }
+
+ lmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_lua_module);
+
+ bp = lmcf->balancer_peer_data;
+ if (bp == NULL) {
+ *err = "no upstream peer data found";
+ return NGX_ERROR;
+ }
+
+ *status = 0;
+
+ return bp->last_peer_state;
+}
+
+
+int
+ngx_stream_lua_ffi_balancer_bind_to_local_addr(ngx_stream_lua_request_t *r,
+ const u_char *addr, size_t addr_len, u_char *errbuf, size_t *errbuf_size)
+{
+ u_char *p;
+ ngx_int_t rc;
+ ngx_str_t addr_str;
+ ngx_addr_t *addr_val;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_upstream_t *u;
+
+ if (r == NULL) {
+ p = ngx_snprintf(errbuf, *errbuf_size, "no request found");
+ *errbuf_size = p - errbuf;
+ return NGX_ERROR;
+ }
+
+ u = r->session->upstream;
+ if (u == NULL) {
+ p = ngx_snprintf(errbuf, *errbuf_size, "no upstream found");
+ *errbuf_size = p - errbuf;
+ return NGX_ERROR;
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ p = ngx_snprintf(errbuf, *errbuf_size, "no ctx found");
+ *errbuf_size = p - errbuf;
+ return NGX_ERROR;
+ }
+
+ if ((ctx->context & NGX_STREAM_LUA_CONTEXT_BALANCER) == 0) {
+ p = ngx_snprintf(errbuf, *errbuf_size,
+ "API disabled in the current context");
+ *errbuf_size = p - errbuf;
+ return NGX_ERROR;
+ }
+
+ addr_val = ngx_pcalloc(r->pool, sizeof(ngx_addr_t));
+ if (addr_val == NULL) {
+ p = ngx_snprintf(errbuf, *errbuf_size, "no memory");
+ *errbuf_size = p - errbuf;
+ return NGX_ERROR;
+ }
+
+ addr_str.len = addr_len;
+ addr_str.data = ngx_palloc(r->pool, addr_len);
+ if (addr_str.data == NULL) {
+ p = ngx_snprintf(errbuf, *errbuf_size, "no memory");
+ *errbuf_size = p - errbuf;
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(addr_str.data, addr, addr_len);
+
+ rc = ngx_parse_addr_port(r->pool, addr_val, addr_str.data, addr_str.len);
+ if (rc != NGX_OK) {
+ p = ngx_snprintf(errbuf, *errbuf_size, "parse addr port failed");
+ *errbuf_size = p - errbuf;
+ return NGX_ERROR;
+ }
+
+ addr_val->name = addr_str;
+
+ u->peer.local = addr_val;
+
+ return NGX_OK;
+}
+
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_balancer.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_balancer.h
new file mode 100644
index 0000000..fde8766
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_balancer.h
@@ -0,0 +1,35 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_balancer.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_BALANCER_H_INCLUDED_
+#define _NGX_STREAM_LUA_BALANCER_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+ngx_int_t ngx_stream_lua_balancer_handler_inline(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_srv_conf_t *lscf, lua_State *L);
+
+ngx_int_t ngx_stream_lua_balancer_handler_file(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_srv_conf_t *lscf, lua_State *L);
+
+char *ngx_stream_lua_balancer_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+char *ngx_stream_lua_balancer_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+#endif /* _NGX_STREAM_LUA_BALANCER_H_INCLUDED_ */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_cache.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_cache.c
new file mode 100644
index 0000000..4c57137
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_cache.c
@@ -0,0 +1,319 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_cache.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include <nginx.h>
+#include <ngx_md5.h>
+#include "ngx_stream_lua_common.h"
+#include "ngx_stream_lua_cache.h"
+#include "ngx_stream_lua_clfactory.h"
+#include "ngx_stream_lua_util.h"
+
+
+/**
+ * Find code chunk associated with the given key in code cache,
+ * and push it to the top of Lua stack if found.
+ *
+ * Stack layout before call:
+ * | ... | <- top
+ *
+ * Stack layout after call:
+ * | code chunk | <- top
+ * | ... |
+ *
+ * */
+static ngx_int_t
+ngx_stream_lua_cache_load_code(ngx_log_t *log, lua_State *L,
+ const char *key)
+{
+#ifndef OPENRESTY_LUAJIT
+ int rc;
+ u_char *err;
+#endif
+
+ /* get code cache table */
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ code_cache_key));
+ lua_rawget(L, LUA_REGISTRYINDEX); /* sp++ */
+
+ dd("Code cache table to load: %p", lua_topointer(L, -1));
+
+ if (!lua_istable(L, -1)) {
+ dd("Error: code cache table to load did not exist!!");
+ return NGX_ERROR;
+ }
+
+ lua_getfield(L, -1, key); /* sp++ */
+
+ if (lua_isfunction(L, -1)) {
+#ifdef OPENRESTY_LUAJIT
+ lua_remove(L, -2); /* sp-- */
+ return NGX_OK;
+#else
+ /* call closure factory to gen new closure */
+ rc = lua_pcall(L, 0, 1, 0);
+ if (rc == 0) {
+ /* remove cache table from stack, leave code chunk at
+ * top of stack */
+ lua_remove(L, -2); /* sp-- */
+ return NGX_OK;
+ }
+
+ if (lua_isstring(L, -1)) {
+ err = (u_char *) lua_tostring(L, -1);
+
+ } else {
+ err = (u_char *) "unknown error";
+ }
+
+ ngx_log_error(NGX_LOG_ERR, log, 0,
+ "lua: failed to run factory at key \"%s\": %s",
+ key, err);
+ lua_pop(L, 2);
+ return NGX_ERROR;
+#endif /* OPENRESTY_LUAJIT */
+ }
+
+ dd("Value associated with given key in code cache table is not code "
+ "chunk: stack top=%d, top value type=%s\n",
+ lua_gettop(L), luaL_typename(L, -1));
+
+ /* remove cache table and value from stack */
+ lua_pop(L, 2); /* sp-=2 */
+
+ return NGX_DECLINED;
+}
+
+
+/**
+ * Store the closure factory at the top of Lua stack to code cache, and
+ * associate it with the given key. Then generate new closure.
+ *
+ * Stack layout before call:
+ * | code factory | <- top
+ * | ... |
+ *
+ * Stack layout after call:
+ * | code chunk | <- top
+ * | ... |
+ *
+ * */
+static ngx_int_t
+ngx_stream_lua_cache_store_code(lua_State *L, const char *key)
+{
+#ifndef OPENRESTY_LUAJIT
+ int rc;
+#endif
+
+ /* get code cache table */
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ code_cache_key));
+ lua_rawget(L, LUA_REGISTRYINDEX);
+
+ dd("Code cache table to store: %p", lua_topointer(L, -1));
+
+ if (!lua_istable(L, -1)) {
+ dd("Error: code cache table to load did not exist!!");
+ return NGX_ERROR;
+ }
+
+ lua_pushvalue(L, -2); /* closure cache closure */
+ lua_setfield(L, -2, key); /* closure cache */
+
+ /* remove cache table, leave closure factory at top of stack */
+ lua_pop(L, 1); /* closure */
+
+#ifndef OPENRESTY_LUAJIT
+ /* call closure factory to generate new closure */
+ rc = lua_pcall(L, 0, 1, 0);
+ if (rc != 0) {
+ dd("Error: failed to call closure factory!!");
+ return NGX_ERROR;
+ }
+#endif
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_stream_lua_cache_loadbuffer(ngx_log_t *log, lua_State *L,
+ const u_char *src, size_t src_len, const u_char *cache_key,
+ const char *name)
+{
+ int n;
+ ngx_int_t rc;
+ const char *err = NULL;
+
+ n = lua_gettop(L);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, log, 0,
+ "looking up Lua code cache with key '%s'", cache_key);
+
+ rc = ngx_stream_lua_cache_load_code(log, L, (char *) cache_key);
+ if (rc == NGX_OK) {
+ /* code chunk loaded from cache, sp++ */
+ dd("Code cache hit! cache key='%s', stack top=%d, script='%.*s'",
+ cache_key, lua_gettop(L), (int) src_len, src);
+ return NGX_OK;
+ }
+
+ if (rc == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ /* rc == NGX_DECLINED */
+
+ dd("Code cache missed! cache key='%s', stack top=%d, script='%.*s'",
+ cache_key, lua_gettop(L), (int) src_len, src);
+
+ /* load closure factory of inline script to the top of lua stack, sp++ */
+ rc = ngx_stream_lua_clfactory_loadbuffer(L, (char *) src, src_len, name);
+
+ if (rc != 0) {
+ /* Oops! error occurred when loading Lua script */
+ if (rc == LUA_ERRMEM) {
+ err = "memory allocation error";
+
+ } else {
+ if (lua_isstring(L, -1)) {
+ err = lua_tostring(L, -1);
+
+ } else {
+ err = "unknown error";
+ }
+ }
+
+ goto error;
+ }
+
+ /* store closure factory and gen new closure at the top of lua stack to
+ * code cache */
+ rc = ngx_stream_lua_cache_store_code(L, (char *) cache_key);
+ if (rc != NGX_OK) {
+ err = "fail to generate new closure from the closure factory";
+ goto error;
+ }
+
+ return NGX_OK;
+
+error:
+
+ ngx_log_error(NGX_LOG_ERR, log, 0,
+ "failed to load inlined Lua code: %s", err);
+ lua_settop(L, n);
+ return NGX_ERROR;
+}
+
+
+ngx_int_t
+ngx_stream_lua_cache_loadfile(ngx_log_t *log, lua_State *L,
+ const u_char *script, const u_char *cache_key)
+{
+ int n;
+ ngx_int_t rc, errcode = NGX_ERROR;
+ u_char *p;
+ u_char buf[NGX_STREAM_LUA_FILE_KEY_LEN + 1];
+ const char *err = NULL;
+
+ n = lua_gettop(L);
+
+ /* calculate digest of script file path */
+ if (cache_key == NULL) {
+ dd("CACHE file key not pre-calculated...calculating");
+ p = ngx_copy(buf, NGX_STREAM_LUA_FILE_TAG, NGX_STREAM_LUA_FILE_TAG_LEN);
+
+ p = ngx_stream_lua_digest_hex(p, script, ngx_strlen(script));
+
+ *p = '\0';
+ cache_key = buf;
+
+ } else {
+ dd("CACHE file key already pre-calculated");
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, log, 0,
+ "looking up Lua code cache with key '%s'", cache_key);
+
+ rc = ngx_stream_lua_cache_load_code(log, L, (char *) cache_key);
+ if (rc == NGX_OK) {
+ /* code chunk loaded from cache, sp++ */
+ dd("Code cache hit! cache key='%s', stack top=%d, file path='%s'",
+ cache_key, lua_gettop(L), script);
+ return NGX_OK;
+ }
+
+ if (rc == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ /* rc == NGX_DECLINED */
+
+ dd("Code cache missed! cache key='%s', stack top=%d, file path='%s'",
+ cache_key, lua_gettop(L), script);
+
+ /* load closure factory of script file to the top of lua stack, sp++ */
+ rc = ngx_stream_lua_clfactory_loadfile(L, (char *) script);
+
+ dd("loadfile returns %d (%d)", (int) rc, LUA_ERRFILE);
+
+ if (rc != 0) {
+ /* Oops! error occurred when loading Lua script */
+ switch (rc) {
+ case LUA_ERRMEM:
+ err = "memory allocation error";
+ break;
+
+ case LUA_ERRFILE:
+ errcode = NGX_STREAM_INTERNAL_SERVER_ERROR;
+ /* fall through */
+
+ default:
+ if (lua_isstring(L, -1)) {
+ err = lua_tostring(L, -1);
+
+ } else {
+ err = "unknown error";
+ }
+ }
+
+ goto error;
+ }
+
+ /* store closure factory and gen new closure at the top of lua stack
+ * to code cache */
+ rc = ngx_stream_lua_cache_store_code(L, (char *) cache_key);
+ if (rc != NGX_OK) {
+ err = "fail to generate new closure from the closure factory";
+ goto error;
+ }
+
+ return NGX_OK;
+
+error:
+
+ ngx_log_error(NGX_LOG_ERR, log, 0,
+ "failed to load external Lua file \"%s\": %s", script, err);
+
+ lua_settop(L, n);
+ return errcode;
+}
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_cache.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_cache.h
new file mode 100644
index 0000000..5c3c73b
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_cache.h
@@ -0,0 +1,32 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_cache.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_CACHE_H_INCLUDED_
+#define _NGX_STREAM_LUA_CACHE_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+ngx_int_t ngx_stream_lua_cache_loadbuffer(ngx_log_t *log, lua_State *L,
+ const u_char *src, size_t src_len, const u_char *cache_key,
+ const char *name);
+ngx_int_t ngx_stream_lua_cache_loadfile(ngx_log_t *log, lua_State *L,
+ const u_char *script, const u_char *cache_key);
+
+
+#endif /* _NGX_STREAM_LUA_CACHE_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_clfactory.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_clfactory.c
new file mode 100644
index 0000000..acecc6f
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_clfactory.c
@@ -0,0 +1,937 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_clfactory.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include <nginx.h>
+#include "ngx_stream_lua_clfactory.h"
+
+
+#ifndef OPENRESTY_LUAJIT
+#define CLFACTORY_BEGIN_CODE "return function() "
+#define CLFACTORY_BEGIN_SIZE (sizeof(CLFACTORY_BEGIN_CODE) - 1)
+
+#define CLFACTORY_END_CODE "\nend"
+#define CLFACTORY_END_SIZE (sizeof(CLFACTORY_END_CODE) - 1)
+#endif
+
+
+/*
+ * taken from chaoslawful:
+ * Lua bytecode header Luajit bytecode header
+ * -------------- --------------
+ * | \033Lua | 0-3 | \033LJ | 0-2
+ * -------------- --------------
+ * | LuaC | 4 | bytecode | 3
+ * | Version | | version |
+ * -------------- --------------
+ * | LuaC | 5 | misc flag | 4 [F|S|B]
+ * | Format | --------------
+ * -------------- | chunkname | ULEB128 var-len
+ * | Endian | 6 | len | encoded uint32
+ * -------------- --------------
+ * | size of | 7 | chunkname |
+ * | int | | str no \0 |
+ * -------------- --------------
+ * | size of | 8
+ * | size_t |
+ * --------------
+ * | size of | 9
+ * | instruction|
+ * --------------
+ * | size of | 10
+ * | number |
+ * --------------
+ * | number | 11
+ * | is int? |
+ * --------------
+*/
+
+
+/*
+ * CLOSURE 0 0 RETURN 0 2 RETURN 0 1
+ * length(Instruction) = 4 or 8
+ * little endian or big endian
+*/
+#ifndef OPENRESTY_LUAJIT
+#define LUA_LITTLE_ENDIAN_4BYTES_CODE \
+ "\x24\x00\x00\x00\x1e\x00\x00\x01\x1e\x00\x80\x00"
+#define LUA_LITTLE_ENDIAN_8BYTES_CODE \
+ "\x24\x00\x00\x00\x00\x00\x00\x00\x1e\x00\x00\x01" \
+ "\x00\x00\x00\x00\x1e\x00\x80\x00\x00\x00\x00\x00"
+#define LUA_BIG_ENDIAN_4BYTES_CODE \
+ "\x00\x00\x00\x24\x01\x00\x00\x1e\x00\x08\x00\x1e"
+#define LUA_BIG_ENDIAN_8BYTES_CODE \
+ "\x00\x00\x00\x00\x00\x00\x00\x24\x00\x00\x00\x00" \
+ "\x01\x00\x00\x1e\x00\x00\x00\x00\x00\x08\x00\x1e"
+#define LUA_LITTLE_ENDIAN_4BYTES_CODE_LEN (4 + 4 + 4)
+#define LUA_LITTLE_ENDIAN_8BYTES_CODE_LEN (8 + 8 + 8)
+#define LUA_BIG_ENDIAN_4BYTES_CODE_LEN (4 + 4 + 4)
+#define LUA_BIG_ENDIAN_8BYTES_CODE_LEN (8 + 8 + 8)
+#define LUAC_HEADERSIZE 12
+#define LUAC_VERSION 0x51
+#endif /* OPENRESTY_LUAJIT */
+
+
+/*
+ * taken from chaoslawful:
+ * Lua Proto
+ * ---------------------
+ * | String | Can be empty string
+ * | [source] | (stripped or internal function)
+ * ---------------------
+ * | Int | At which line this function is defined
+ * | [linedefined] |
+ * ---------------------
+ * | Int | At while line this function definition ended
+ * | [lastlinedefined] |
+ * ---------------------
+ * | Char | Number of upvalues referenced by this function
+ * | [nups] |
+ * ---------------------
+ * | Char | Number of parameters of this function
+ * | [numparams] |
+ * ---------------------
+ * | Char | Does this function has variable number of arguments?
+ * | [is_var_arg] | main function always set to VARARG_ISVARARG (2)
+ * ---------------------
+ * | Char | Maximum stack size this function used
+ * | [maxstacksize] | Initially set to 2
+ * ---------------------
+ * | Vector(instr) | Code instructions of this function
+ * | [code] |
+ * ---------------------
+ * | Int | Number of constants referenced by this function
+ * | [sizek] |
+ * ---------------------
+ * | Char | ------------------------------------
+ * | type of [k[i]] | The type and content of constants |
+ * --------------------- |-> repeat for i in
+ * | Char if boolean | No content part if type is NIL | [1..sizek]
+ * | Number if number | ------------------------------------
+ * | String if string |
+ * ---------------------
+ * | Int | Number of internal functions
+ * | [sizep] |
+ * ---------------------
+ * | Function | -> repeat for i in [1..sizep]
+ * | at [p[i]] |
+ * ---------------------
+ * | Vector | Debug lineinfo vector
+ * | [lineinfo] | Empty vector here if dubug info is stripped
+ * ---------------------
+ * | Int | Number of local variable in this function
+ * | [sizelocvars] | 0 if debug info is stripped
+ * ---------------------
+ * | String | ------------------------------------
+ * | [locvars[i]] | Name of local var i |
+ * | .varname] | |
+ * --------------------- |
+ * | Int | instruction counter |
+ * | [locvars[i]] | where lcoal var i start to be |-> repeat for i in
+ * | .startpc] | referenced | [0..sizelocvars]
+ * --------------------- |
+ * | Int | instruction counter, where local |
+ * | [locvars[i]] | var i ceased to be referenced |
+ * | .endpc] | ------------------------------------
+ * ---------------------
+ * | Int | Number of upvalues referenced by this function,
+ * | [sizeupvalues] | 0 if stripped
+ * ---------------------
+ * | String | -> repeat for i in[0..sizeupvalues]
+ * | [upvalues[i]] |
+ * ---------------------
+*/
+
+#ifndef OPENRESTY_LUAJIT
+#define POS_SOURCE_STR_LEN LUAC_HEADERSIZE
+#define POS_START_LINE (POS_SOURCE_STR_LEN + sizeof(size_t))
+#define POS_LAST_LINE (POS_START_LINE + sizeof(int))
+#define POS_NUM_OF_UPVS (POS_LAST_LINE + sizeof(int))
+#define POS_NUM_OF_PARA (POS_NUM_OF_UPVS + sizeof(char))
+#define POS_IS_VAR_ARG (POS_NUM_OF_PARA + sizeof(char))
+#define POS_MAX_STACK_SIZE (POS_IS_VAR_ARG + sizeof(char))
+#define POS_NUM_OF_INST (POS_MAX_STACK_SIZE +sizeof(char))
+#define POS_BYTECODE (POS_NUM_OF_INST + sizeof(int))
+#define MAX_BEGIN_CODE_SIZE \
+ (POS_BYTECODE + LUA_LITTLE_ENDIAN_8BYTES_CODE_LEN \
+ + sizeof(int) + sizeof(int))
+#define MAX_END_CODE_SIZE (sizeof(int) + sizeof(int) + sizeof(int))
+#endif /* OPENRESTY_LUAJIT */
+
+/*
+ * taken from chaoslawful:
+ * Luajit bytecode format
+ * ---------------------
+ * | HEAD | Luajit bytecode head
+ * ---------------------
+ * | Internal | All internal functions
+ * | functions |
+ * ---------------------
+ * | ULEB128 | Rest data total length of this function
+ * | [Date len of | (not include itself)
+ * | this function] |
+ * ---------------------
+ * | Char | F(ffi) | V(vararg)| C(has internal funcs)
+ * | [func flag] |
+ * ---------------------
+ * | Char | Number of parameters of this function
+ * | [numparams] |
+ * ---------------------
+ * | Char |
+ * | [framesize] |
+ * ---------------------
+ * | Char | Number of upvalues referenced by this function
+ * | [sizeupvalues] |
+ * ---------------------
+ * | ULEB128 | Number of collectable constants referenced
+ * | [sizekgc] | by this function
+ * ---------------------
+ * | ULEB128 | Number of lua number constants referenced
+ * | [sizekn] | by this function
+ * ---------------------
+ * | ULEB128 | Number of bytecode instructions of this function
+ * | [sizebc]m1 | minus 1 to omit the BC_FUNCV/BC_FUNCF header bytecode
+ * ---------------------
+ * | ULEB128 |
+ * | [size of dbg | Size of debug lineinfo map, available when not stripped
+ * | lineinfo] |
+ * ---------------------
+ * | ULEB128 | Available when not stripped
+ * | [firstline] | The first line of this function's definition
+ * ---------------------
+ * | ULEB128 | Available when not stripped
+ * | [numline] | The number of lines of this function's definition
+ * ---------------------
+ * | [bytecode] | Bytecode instructions of this function
+ * ---------------------
+ * |[upvalue ref slots]| [sizeupvalues] * 2
+ * ---------------------
+ * | [collectable | [sizekgc] elems, variable length
+ * | constants] |
+ * ---------------------
+ * | [lua number | [sizekn] elems, variable length
+ * | constants] |
+ * ---------------------
+ * | [debug lineinfo | Length is the calculated size of debug lineinfo above
+ * | | Only available if not stripped
+ * ---------------------
+ * | Char |
+ * | [\x00] | Footer
+ * ---------------------
+*/
+
+/* bytecode for luajit 2.0 */
+
+#ifndef OPENRESTY_LUAJIT
+#define LJ20_LITTLE_ENDIAN_CODE_STRIPPED \
+ "\x14\x03\x00\x01\x00\x01\x00\x03" \
+ "\x31\x00\x00\x00\x30\x00\x00\x80\x48\x00\x02\x00" \
+ "\x00\x00"
+
+#define LJ20_BIG_ENDIAN_CODE_STRIPPED \
+ "\x14\x03\x00\x01\x00\x01\x00\x03" \
+ "\x00\x00\x00\x31\x80\x00\x00\x30\x00\x02\x00\x48" \
+ "\x00\x00"
+
+#define LJ20_LITTLE_ENDIAN_CODE \
+ "\x15\x03\x00\x01\x00\x01\x00\x03\x00" \
+ "\x31\x00\x00\x00\x30\x00\x00\x80\x48\x00\x02\x00" \
+ "\x00\x00"
+
+#define LJ20_BIG_ENDIAN_CODE \
+ "\x15\x03\x00\x01\x00\x01\x00\x03\x00" \
+ "\x00\x00\x00\x31\x80\x00\x00\x30\x00\x02\x00\x48" \
+ "\x00\x00"
+
+/* bytecode for luajit 2.1 */
+
+#define LJ21_LITTLE_ENDIAN_CODE_STRIPPED \
+ "\x14\x03\x00\x01\x00\x01\x00\x03" \
+ "\x33\x00\x00\x00\x32\x00\x00\x80\x4c\x00\x02\x00" \
+ "\x00\x00"
+
+#define LJ21_BIG_ENDIAN_CODE_STRIPPED \
+ "\x14\x03\x00\x01\x00\x01\x00\x03" \
+ "\x00\x00\x00\x33\x80\x00\x00\x32\x00\x02\x00\x4c" \
+ "\x00\x00"
+
+#define LJ21_LITTLE_ENDIAN_CODE \
+ "\x15\x03\x00\x01\x00\x01\x00\x03\x00" \
+ "\x33\x00\x00\x00\x32\x00\x00\x80\x4c\x00\x02\x00" \
+ "\x00\x00"
+
+#define LJ21_BIG_ENDIAN_CODE \
+ "\x15\x03\x00\x01\x00\x01\x00\x03\x00" \
+ "\x00\x00\x00\x33\x80\x00\x00\x32\x00\x02\x00\x4c" \
+ "\x00\x00"
+
+#define LJ_CODE_LEN 23
+#define LJ_CODE_LEN_STRIPPED 22
+#define LJ_HEADERSIZE 5
+#define LJ_BCDUMP_F_BE 0x01
+#define LJ_BCDUMP_F_STRIP 0x02
+#define LJ21_BCDUMP_VERSION 2
+#define LJ20_BCDUMP_VERSION 1
+#define LJ_SIGNATURE "\x1b\x4c\x4a"
+#endif /* OPENRESTY_LUAJIT */
+
+
+typedef enum {
+ NGX_LUA_TEXT_FILE,
+ NGX_LUA_BT_LUA,
+ NGX_LUA_BT_LJ
+} ngx_stream_lua_clfactory_file_type_e;
+
+
+enum {
+ NGX_LUA_READER_BUFSIZE = 4096
+};
+
+
+typedef struct {
+ ngx_stream_lua_clfactory_file_type_e file_type;
+
+ int extraline;
+ FILE *f;
+#ifndef OPENRESTY_LUAJIT
+ int sent_begin;
+ int sent_end;
+ size_t begin_code_len;
+ size_t end_code_len;
+ size_t rest_len;
+ union {
+ char *ptr;
+ char str[MAX_BEGIN_CODE_SIZE];
+ } begin_code;
+ union {
+ char *ptr;
+ char str[MAX_END_CODE_SIZE];
+ } end_code;
+#endif /* OPENRESTY_LUAJIT */
+ char buff[NGX_LUA_READER_BUFSIZE];
+} ngx_stream_lua_clfactory_file_ctx_t;
+
+
+typedef struct {
+#ifndef OPENRESTY_LUAJIT
+ int sent_begin;
+ int sent_end;
+#endif
+ const char *s;
+ size_t size;
+} ngx_stream_lua_clfactory_buffer_ctx_t;
+
+
+static const char *ngx_stream_lua_clfactory_getF(lua_State *L, void *ud,
+ size_t *size);
+static int ngx_stream_lua_clfactory_errfile(lua_State *L, const char *what,
+ int fname_index);
+static const char *ngx_stream_lua_clfactory_getS(lua_State *L, void *ud,
+ size_t *size);
+#ifndef OPENRESTY_LUAJIT
+static long ngx_stream_lua_clfactory_file_size(FILE *f);
+#endif
+
+
+#ifndef OPENRESTY_LUAJIT
+int
+ngx_stream_lua_clfactory_bytecode_prepare(lua_State *L,
+ ngx_stream_lua_clfactory_file_ctx_t *lf, int fname_index)
+{
+ int x = 1, size_of_int, size_of_size_t, little_endian,
+ size_of_inst, version, stripped;
+ static int num_of_inst = 3, num_of_inter_func = 1;
+ const char *emsg, *serr, *bytecode;
+ size_t size, bytecode_len;
+ long fsize;
+
+ serr = NULL;
+
+ *lf->begin_code.str = LUA_SIGNATURE[0];
+
+ if (lf->file_type == NGX_LUA_BT_LJ) {
+ size = fread(lf->begin_code.str + 1, 1, LJ_HEADERSIZE - 1, lf->f);
+
+ if (size != LJ_HEADERSIZE - 1) {
+ serr = strerror(errno);
+ emsg = "cannot read header";
+ goto error;
+ }
+
+ version = *(lf->begin_code.str + 3);
+
+ dd("version: %d", (int) version);
+
+ if (ngx_memcmp(lf->begin_code.str, LJ_SIGNATURE,
+ sizeof(LJ_SIGNATURE) - 1))
+ {
+ emsg = "bad byte-code header";
+ goto error;
+ }
+
+#if defined(DDEBUG) && (DDEBUG)
+ {
+ dd("==LJ_BT_HEADER==");
+ size_t i;
+ for (i = 0; i < LJ_HEADERSIZE; i++) {
+ dd("%ld: 0x%02X", i, (unsigned)(u_char) lf->begin_code.str[i]);
+ }
+ dd("==LJ_BT_HEADER_END==");
+ }
+#endif
+
+ lf->begin_code_len = LJ_HEADERSIZE;
+ little_endian = !((*(lf->begin_code.str + 4)) & LJ_BCDUMP_F_BE);
+ stripped = (*(lf->begin_code.str + 4)) & LJ_BCDUMP_F_STRIP;
+
+ dd("stripped: %d", (int) stripped);
+
+ if (version == LJ21_BCDUMP_VERSION) {
+ if (stripped) {
+ if (little_endian) {
+ lf->end_code.ptr = LJ21_LITTLE_ENDIAN_CODE_STRIPPED;
+
+ } else {
+ lf->end_code.ptr = LJ21_BIG_ENDIAN_CODE_STRIPPED;
+ }
+
+ lf->end_code_len = LJ_CODE_LEN_STRIPPED;
+
+ } else {
+ if (little_endian) {
+ lf->end_code.ptr = LJ21_LITTLE_ENDIAN_CODE;
+
+ } else {
+ lf->end_code.ptr = LJ21_BIG_ENDIAN_CODE;
+ }
+
+ lf->end_code_len = LJ_CODE_LEN;
+ }
+
+ } else if (version == LJ20_BCDUMP_VERSION) {
+ if (stripped) {
+ if (little_endian) {
+ lf->end_code.ptr = LJ20_LITTLE_ENDIAN_CODE_STRIPPED;
+
+ } else {
+ lf->end_code.ptr = LJ20_BIG_ENDIAN_CODE_STRIPPED;
+ }
+
+ lf->end_code_len = LJ_CODE_LEN_STRIPPED;
+
+ } else {
+ if (little_endian) {
+ lf->end_code.ptr = LJ20_LITTLE_ENDIAN_CODE;
+
+ } else {
+ lf->end_code.ptr = LJ20_BIG_ENDIAN_CODE;
+ }
+
+ lf->end_code_len = LJ_CODE_LEN;
+ }
+
+ } else {
+ emsg = "bytecode format version unsupported";
+ goto error;
+ }
+
+ fsize = ngx_stream_lua_clfactory_file_size(lf->f);
+ if (fsize < 0) {
+ serr = strerror(errno);
+ emsg = "cannot fseek/ftell";
+ goto error;
+ }
+
+ lf->rest_len = fsize - LJ_HEADERSIZE;
+
+#if defined(DDEBUG) && (DDEBUG)
+ {
+ size_t i = 0;
+ dd("==LJ_END_CODE: %ld rest_len: %ld==", lf->end_code_len,
+ lf->rest_len);
+
+ for (i = 0; i < lf->end_code_len; i++) {
+ dd("%ld: 0x%02X", i, (unsigned) ((u_char) lf->end_code.ptr[i]));
+ }
+ dd("==LJ_END_CODE_END==");
+ }
+#endif
+
+ } else {
+ size = fread(lf->begin_code.str + 1, 1, LUAC_HEADERSIZE - 1, lf->f);
+
+ if (size != LUAC_HEADERSIZE - 1) {
+ serr = strerror(errno);
+ emsg = "cannot read header";
+ goto error;
+ }
+
+ version = *(lf->begin_code.str + 4);
+ little_endian = *(lf->begin_code.str + 6);
+ size_of_int = *(lf->begin_code.str + 7);
+ size_of_size_t = *(lf->begin_code.str + 8);
+ size_of_inst = *(lf->begin_code.str + 9);
+
+#if defined(DDEBUG) && (DDEBUG)
+ {
+ dd("==LUA_BT_HEADER==");
+ size_t i;
+ for (i = 0; i < LUAC_HEADERSIZE; i++) {
+ dd("%ld, 0x%02X", i, (unsigned)(u_char) lf->begin_code.str[i]);
+ }
+ dd("==LUA_BT_HEADER_END==");
+ }
+#endif
+
+ if (ngx_memcmp(lf->begin_code.str, LUA_SIGNATURE,
+ sizeof(LUA_SIGNATURE) -1)
+ || version != LUAC_VERSION
+ || little_endian != (int) (*(char *) &x)
+ || size_of_int != sizeof(int)
+ || size_of_size_t != sizeof(size_t)
+ || (size_of_inst != 4 && size_of_inst != 8))
+ {
+ emsg = "bad byte-code header";
+ goto error;
+ }
+
+ /* clear the following fields to zero:
+ * - source string length
+ * - start line
+ * - last line
+ */
+ ngx_memzero(lf->begin_code.str + POS_SOURCE_STR_LEN,
+ sizeof(size_t) + sizeof(int) * 2);
+ /* number of upvalues */
+ *(lf->begin_code.str + POS_NUM_OF_UPVS) = 0;
+ /* number of parameters */
+ *(lf->begin_code.str + POS_NUM_OF_PARA) = 0;
+ /* is var-argument function? */
+ *(lf->begin_code.str + POS_IS_VAR_ARG) = 2;
+ /* max stack size */
+ *(lf->begin_code.str + POS_MAX_STACK_SIZE) = 2;
+ /* number of bytecode instructions */
+ ngx_memcpy(lf->begin_code.str + POS_NUM_OF_INST, &num_of_inst,
+ sizeof(int));
+
+ lf->begin_code_len = POS_BYTECODE;
+
+ if (little_endian) {
+ if (size_of_inst == 4) {
+ bytecode = LUA_LITTLE_ENDIAN_4BYTES_CODE;
+ bytecode_len = LUA_LITTLE_ENDIAN_4BYTES_CODE_LEN;
+
+ } else {
+ bytecode = LUA_LITTLE_ENDIAN_8BYTES_CODE;
+ bytecode_len = LUA_LITTLE_ENDIAN_8BYTES_CODE_LEN;
+ }
+
+ } else {
+ if (size_of_inst == 4) {
+ bytecode = LUA_BIG_ENDIAN_4BYTES_CODE;
+ bytecode_len = LUA_BIG_ENDIAN_4BYTES_CODE_LEN;
+
+ } else {
+ bytecode = LUA_BIG_ENDIAN_8BYTES_CODE;
+ bytecode_len = LUA_BIG_ENDIAN_8BYTES_CODE_LEN;
+ }
+ }
+
+ /* bytecode */
+ ngx_memcpy(lf->begin_code.str + POS_BYTECODE, bytecode, bytecode_len);
+
+ /* number of consts */
+ ngx_memzero(lf->begin_code.str + POS_BYTECODE + bytecode_len,
+ sizeof(int));
+ /* number of internal functions */
+ ngx_memcpy(lf->begin_code.str + POS_BYTECODE + bytecode_len
+ + sizeof(int), &num_of_inter_func, sizeof(int));
+
+ lf->begin_code_len += bytecode_len + sizeof(int) + sizeof(int);
+
+#if defined(DDEBUG) && (DDEBUG)
+ {
+ size_t i = 0;
+ dd("==LUA_BEGIN_CODE: %ld==", lf->begin_code_len);
+ for (i = 0; i < lf->begin_code_len; i++) {
+ dd("%ld: 0x%02X", i, (unsigned) ((u_char) lf->begin_code.str[i]));
+ }
+ dd("==LUA_BEGIN_CODE_END==");
+ }
+#endif
+
+ /* clear the following fields to zero:
+ * - lineinfo vector size
+ * - number of local vars
+ * - number of upvalues
+ */
+ ngx_memzero(lf->end_code.str, sizeof(int) * 3);
+
+ lf->end_code_len = sizeof(int) + sizeof(int) + sizeof(int);
+
+#if defined(DDEBUG) && (DDEBUG)
+ {
+ size_t i = 0;
+ dd("==LUA_END_CODE: %ld==", lf->end_code_len);
+ for (i = 0; i < lf->end_code_len; i++) {
+ dd("%ld: 0x%02X", i, (unsigned) ((u_char) lf->end_code.str[i]));
+ }
+ dd("==LUA_END_CODE_END==");
+ }
+#endif
+
+ }
+
+ return 0;
+
+error:
+
+ fclose(lf->f); /* close file (even in case of errors) */
+
+ if (serr) {
+ lua_pushfstring(L, "%s: %s", emsg, serr);
+
+ } else {
+ lua_pushstring(L, emsg);
+ }
+
+ lua_remove(L, fname_index);
+
+ return LUA_ERRFILE;
+}
+#endif /* OPENRESTY_LUAJIT */
+
+
+ngx_int_t
+ngx_stream_lua_clfactory_loadfile(lua_State *L, const char *filename)
+{
+ int c, status, readstatus;
+ ngx_flag_t sharp;
+
+ ngx_stream_lua_clfactory_file_ctx_t lf;
+
+ /* index of filename on the stack */
+ int fname_index;
+
+ sharp = 0;
+ fname_index = lua_gettop(L) + 1;
+
+ lf.extraline = 0;
+ lf.file_type = NGX_LUA_TEXT_FILE;
+
+#ifndef OPENRESTY_LUAJIT
+ lf.begin_code.ptr = CLFACTORY_BEGIN_CODE;
+ lf.begin_code_len = CLFACTORY_BEGIN_SIZE;
+ lf.end_code.ptr = CLFACTORY_END_CODE;
+ lf.end_code_len = CLFACTORY_END_SIZE;
+#endif
+
+ lua_pushfstring(L, "@%s", filename);
+
+ lf.f = fopen(filename, "r");
+ if (lf.f == NULL) {
+ return ngx_stream_lua_clfactory_errfile(L, "open", fname_index);
+ }
+
+ c = getc(lf.f);
+
+ if (c == '#') { /* Unix exec. file? */
+ lf.extraline = 1;
+
+ while ((c = getc(lf.f)) != EOF && c != '\n') {
+ /* skip first line */
+ }
+
+ if (c == '\n') {
+ c = getc(lf.f);
+ }
+
+ sharp = 1;
+ }
+
+ if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */
+ lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
+
+ if (lf.f == NULL) {
+ return ngx_stream_lua_clfactory_errfile(L, "reopen", fname_index);
+ }
+
+ /* check whether lib jit exists */
+ luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1);
+ lua_getfield(L, -1, "jit"); /* get _LOADED["jit"] */
+
+ if (lua_istable(L, -1)) {
+ lf.file_type = NGX_LUA_BT_LJ;
+
+ } else {
+ lf.file_type = NGX_LUA_BT_LUA;
+ }
+
+ lua_pop(L, 2);
+
+ /*
+ * Loading bytecode with an extra header is disabled for security
+ * reasons. This may circumvent the usual check for bytecode vs.
+ * Lua code by looking at the first char. Since this is a potential
+ * security violation no attempt is made to echo the chunkname either.
+ */
+ if (lf.file_type == NGX_LUA_BT_LJ && sharp) {
+
+ if (filename) {
+ fclose(lf.f); /* close file (even in case of errors) */
+ }
+
+ filename = lua_tostring(L, fname_index) + 1;
+ lua_pushfstring(L, "bad byte-code header in %s", filename);
+ lua_remove(L, fname_index);
+
+ return LUA_ERRFILE;
+ }
+
+ while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) {
+ /* skip eventual `#!...' */
+ }
+
+#ifndef OPENRESTY_LUAJIT
+ status = ngx_stream_lua_clfactory_bytecode_prepare(L, &lf, fname_index);
+
+ if (status != 0) {
+ return status;
+ }
+#endif
+
+ lf.extraline = 0;
+ }
+
+#ifndef OPENRESTY_LUAJIT
+ if (lf.file_type == NGX_LUA_TEXT_FILE) {
+ ungetc(c, lf.f);
+ }
+
+ lf.sent_begin = lf.sent_end = 0;
+
+#else
+ ungetc(c, lf.f);
+#endif
+ status = lua_load(L, ngx_stream_lua_clfactory_getF, &lf,
+ lua_tostring(L, -1));
+
+ readstatus = ferror(lf.f);
+
+ if (filename) {
+ fclose(lf.f); /* close file (even in case of errors) */
+ }
+
+ if (readstatus) {
+ lua_settop(L, fname_index); /* ignore results from `lua_load' */
+ return ngx_stream_lua_clfactory_errfile(L, "read", fname_index);
+ }
+
+ lua_remove(L, fname_index);
+
+ return status;
+}
+
+
+ngx_int_t
+ngx_stream_lua_clfactory_loadbuffer(lua_State *L, const char *buff,
+ size_t size, const char *name)
+{
+ ngx_stream_lua_clfactory_buffer_ctx_t ls;
+
+ ls.s = buff;
+ ls.size = size;
+#ifndef OPENRESTY_LUAJIT
+ ls.sent_begin = 0;
+ ls.sent_end = 0;
+#endif
+
+ return lua_load(L, ngx_stream_lua_clfactory_getS, &ls, name);
+}
+
+
+static const char *
+ngx_stream_lua_clfactory_getF(lua_State *L, void *ud, size_t *size)
+{
+#ifndef OPENRESTY_LUAJIT
+ char *buf;
+#endif
+ size_t num;
+
+ ngx_stream_lua_clfactory_file_ctx_t *lf;
+
+ lf = (ngx_stream_lua_clfactory_file_ctx_t *) ud;
+
+ if (lf->extraline) {
+ lf->extraline = 0;
+ *size = 1;
+ return "\n";
+ }
+
+#ifndef OPENRESTY_LUAJIT
+ if (lf->sent_begin == 0) {
+ lf->sent_begin = 1;
+ *size = lf->begin_code_len;
+
+ if (lf->file_type == NGX_LUA_TEXT_FILE) {
+ buf = lf->begin_code.ptr;
+
+ } else {
+ buf = lf->begin_code.str;
+ }
+
+ return buf;
+ }
+#endif /* OPENRESTY_LUAJIT */
+
+ num = fread(lf->buff, 1, sizeof(lf->buff), lf->f);
+
+ dd("fread returned %d", (int) num);
+
+ if (num == 0) {
+#ifndef OPENRESTY_LUAJIT
+ if (lf->sent_end == 0) {
+ lf->sent_end = 1;
+ *size = lf->end_code_len;
+
+ if (lf->file_type == NGX_LUA_BT_LUA) {
+ buf = lf->end_code.str;
+
+ } else {
+ buf = lf->end_code.ptr;
+ }
+
+ return buf;
+ }
+#endif /* OPENRESTY_LUAJIT */
+
+ *size = 0;
+ return NULL;
+ }
+
+#ifndef OPENRESTY_LUAJIT
+ if (lf->file_type == NGX_LUA_BT_LJ) {
+ /* skip the footer(\x00) in luajit */
+
+ lf->rest_len -= num;
+
+ if (lf->rest_len == 0) {
+ if (--num == 0 && lf->sent_end == 0) {
+ lf->sent_end = 1;
+ buf = lf->end_code.ptr;
+ *size = lf->end_code_len;
+
+ return buf;
+ }
+ }
+ }
+#endif /* OPENRESTY_LUAJIT */
+
+ *size = num;
+ return lf->buff;
+}
+
+
+static int
+ngx_stream_lua_clfactory_errfile(lua_State *L, const char *what,
+ int fname_index)
+{
+ const char *serr;
+ const char *filename;
+
+ filename = lua_tostring(L, fname_index) + 1;
+
+ if (errno) {
+ serr = strerror(errno);
+ lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr);
+
+ } else {
+ lua_pushfstring(L, "cannot %s %s", what, filename);
+ }
+
+ lua_remove(L, fname_index);
+
+ return LUA_ERRFILE;
+}
+
+
+static const char *
+ngx_stream_lua_clfactory_getS(lua_State *L, void *ud, size_t *size)
+{
+ ngx_stream_lua_clfactory_buffer_ctx_t *ls = ud;
+
+#ifndef OPENRESTY_LUAJIT
+ if (ls->sent_begin == 0) {
+ ls->sent_begin = 1;
+ *size = CLFACTORY_BEGIN_SIZE;
+
+ return CLFACTORY_BEGIN_CODE;
+ }
+#endif
+
+ if (ls->size == 0) {
+#ifndef OPENRESTY_LUAJIT
+ if (ls->sent_end == 0) {
+ ls->sent_end = 1;
+ *size = CLFACTORY_END_SIZE;
+ return CLFACTORY_END_CODE;
+ }
+#endif
+
+ return NULL;
+ }
+
+ *size = ls->size;
+ ls->size = 0;
+
+ return ls->s;
+}
+
+
+#ifndef OPENRESTY_LUAJIT
+static long
+ngx_stream_lua_clfactory_file_size(FILE *f)
+{
+ long cur_pos, len;
+
+ cur_pos = ftell(f);
+ if (cur_pos == -1) {
+ return -1;
+ }
+
+ if (fseek(f, 0, SEEK_END) != 0) {
+ return -1;
+ }
+
+ len = ftell(f);
+ if (len == -1) {
+ return -1;
+ }
+
+ if (fseek(f, cur_pos, SEEK_SET) != 0) {
+ return -1;
+ }
+
+ return len;
+}
+#endif /* OPENRESTY_LUAJIT */
+
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_clfactory.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_clfactory.h
new file mode 100644
index 0000000..8be7b17
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_clfactory.h
@@ -0,0 +1,30 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_clfactory.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_CLFACTORY_H_INCLUDED_
+#define _NGX_STREAM_LUA_CLFACTORY_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+ngx_int_t ngx_stream_lua_clfactory_loadfile(lua_State *L, const char *filename);
+ngx_int_t ngx_stream_lua_clfactory_loadbuffer(lua_State *L, const char *buff,
+ size_t size, const char *name);
+
+
+#endif /* _NGX_STREAM_LUA_CLFACTORY_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_common.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_common.h
new file mode 100644
index 0000000..f76229e
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_common.h
@@ -0,0 +1,538 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_common.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_COMMON_H_INCLUDED_
+#define _NGX_STREAM_LUA_COMMON_H_INCLUDED_
+
+
+#include "ngx_stream_lua_autoconf.h"
+
+#include <nginx.h>
+#include <ngx_core.h>
+#include <ngx_stream.h>
+#include <ngx_md5.h>
+
+#include <setjmp.h>
+#include <stdint.h>
+
+#include <luajit.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+
+#include "ngx_stream_lua_request.h"
+
+
+#if (NGX_PCRE)
+# if (NGX_PCRE2)
+# define LUA_HAVE_PCRE_JIT 1
+# else
+
+#include <pcre.h>
+
+# if (PCRE_MAJOR > 8) || (PCRE_MAJOR == 8 && PCRE_MINOR >= 21)
+# define LUA_HAVE_PCRE_JIT 1
+# else
+# define LUA_HAVE_PCRE_JIT 0
+# endif
+# endif
+#endif
+
+
+#if !defined(nginx_version) || nginx_version < 1013006
+#error at least nginx 1.13.6 is required but found an older version
+#endif
+
+
+
+
+#if LUA_VERSION_NUM != 501
+# error unsupported Lua language version
+#endif
+
+
+#if !defined(LUAJIT_VERSION_NUM) || (LUAJIT_VERSION_NUM < 20000)
+# error unsupported LuaJIT version
+#endif
+
+
+#if (!defined OPENSSL_NO_OCSP && defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB)
+# define NGX_STREAM_LUA_USE_OCSP 1
+#endif
+
+
+
+
+#ifndef NGX_HAVE_SHA1
+# if defined(nginx_version) && nginx_version >= 1011002
+# define NGX_HAVE_SHA1 1
+# endif
+#endif
+
+
+#ifndef MD5_DIGEST_LENGTH
+#define MD5_DIGEST_LENGTH 16
+#endif
+
+
+#ifdef NGX_LUA_USE_ASSERT
+# include <assert.h>
+# define ngx_stream_lua_assert(a) assert(a)
+#else
+# define ngx_stream_lua_assert(a)
+#endif
+
+
+/* Nginx HTTP Lua Inline tag prefix */
+
+#define NGX_STREAM_LUA_INLINE_TAG "nhli_"
+
+#define NGX_STREAM_LUA_INLINE_TAG_LEN \
+ (sizeof(NGX_STREAM_LUA_INLINE_TAG) - 1)
+
+#define NGX_STREAM_LUA_INLINE_KEY_LEN \
+ (NGX_STREAM_LUA_INLINE_TAG_LEN + 2 * MD5_DIGEST_LENGTH)
+
+/* Nginx HTTP Lua File tag prefix */
+
+#define NGX_STREAM_LUA_FILE_TAG "nhlf_"
+
+#define NGX_STREAM_LUA_FILE_TAG_LEN \
+ (sizeof(NGX_STREAM_LUA_FILE_TAG) - 1)
+
+#define NGX_STREAM_LUA_FILE_KEY_LEN \
+ (NGX_STREAM_LUA_FILE_TAG_LEN + 2 * MD5_DIGEST_LENGTH)
+
+
+#define NGX_STREAM_CLIENT_CLOSED_REQUEST 499
+
+
+
+
+#ifndef NGX_STREAM_LUA_MAX_ARGS
+#define NGX_STREAM_LUA_MAX_ARGS 100
+#endif
+
+
+/* must be within 16 bit */
+#define NGX_STREAM_LUA_CONTEXT_CONTENT 0x0001
+#define NGX_STREAM_LUA_CONTEXT_LOG 0x0002
+#define NGX_STREAM_LUA_CONTEXT_TIMER 0x0004
+#define NGX_STREAM_LUA_CONTEXT_INIT_WORKER 0x0008
+#define NGX_STREAM_LUA_CONTEXT_BALANCER 0x0010
+#define NGX_STREAM_LUA_CONTEXT_PREREAD 0x0020
+#define NGX_STREAM_LUA_CONTEXT_SSL_CERT 0x0040
+#define NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO 0x0080
+
+
+#define NGX_STREAM_LUA_FFI_NO_REQ_CTX -100
+#define NGX_STREAM_LUA_FFI_BAD_CONTEXT -101
+
+
+#if (NGX_PTR_SIZE >= 8 && !defined(_WIN64))
+#define ngx_stream_lua_lightudata_mask(ludata) \
+ ((void *) ((uintptr_t) (&ngx_stream_lua_##ludata) & ((1UL << 47) - 1)))
+
+#else
+#define ngx_stream_lua_lightudata_mask(ludata) \
+ (&ngx_stream_lua_##ludata)
+#endif
+
+
+typedef struct ngx_stream_lua_main_conf_s ngx_stream_lua_main_conf_t;
+typedef struct ngx_stream_lua_srv_conf_s ngx_stream_lua_srv_conf_t;
+
+
+typedef struct ngx_stream_lua_balancer_peer_data_s
+ ngx_stream_lua_balancer_peer_data_t;
+
+
+typedef struct ngx_stream_lua_sema_mm_s ngx_stream_lua_sema_mm_t;
+
+
+typedef ngx_int_t (*ngx_stream_lua_main_conf_handler_pt)(ngx_log_t *log,
+ ngx_stream_lua_main_conf_t *lmcf, lua_State *L);
+typedef ngx_int_t (*ngx_stream_lua_srv_conf_handler_pt)(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_srv_conf_t *lscf, lua_State *L);
+
+
+typedef struct {
+ u_char *package;
+ lua_CFunction loader;
+} ngx_stream_lua_preload_hook_t;
+
+
+struct ngx_stream_lua_main_conf_s {
+ lua_State *lua;
+ ngx_pool_cleanup_t *vm_cleanup;
+
+ ngx_str_t lua_path;
+ ngx_str_t lua_cpath;
+
+ ngx_cycle_t *cycle;
+ ngx_pool_t *pool;
+
+ ngx_int_t max_pending_timers;
+ ngx_int_t pending_timers;
+
+ ngx_int_t max_running_timers;
+ ngx_int_t running_timers;
+
+ ngx_connection_t *watcher; /* for watching the process exit event */
+
+#if (NGX_PCRE)
+ ngx_int_t regex_cache_entries;
+ ngx_int_t regex_cache_max_entries;
+ ngx_int_t regex_match_limit;
+#endif
+
+#if (LUA_HAVE_PCRE_JIT)
+#if (NGX_PCRE2)
+ pcre2_jit_stack *jit_stack;
+#else
+ pcre_jit_stack *jit_stack;
+#endif
+#endif
+
+ ngx_array_t *shm_zones; /* of ngx_shm_zone_t* */
+
+ ngx_array_t *shdict_zones; /* shm zones of "shdict" */
+
+ ngx_array_t *preload_hooks; /* of ngx_stream_lua_preload_hook_t */
+
+ ngx_flag_t postponed_to_preread_phase_end;
+
+ ngx_stream_lua_main_conf_handler_pt init_handler;
+ ngx_str_t init_src;
+
+ ngx_stream_lua_main_conf_handler_pt init_worker_handler;
+ ngx_str_t init_worker_src;
+
+ ngx_stream_lua_balancer_peer_data_t *balancer_peer_data;
+ /* neither yielding nor recursion is possible in
+ * balancer_by_lua*, so there cannot be any races among
+ * concurrent requests and it is safe to store the peer
+ * data pointer in the main conf.
+ */
+
+ ngx_uint_t shm_zones_inited;
+
+ ngx_stream_lua_sema_mm_t *sema_mm;
+
+ ngx_uint_t malloc_trim_cycle; /* a cycle is defined as the number
+ of reqeusts */
+ ngx_uint_t malloc_trim_req_count;
+
+
+ ngx_flag_t set_sa_restart;
+
+ unsigned requires_preread:1;
+
+ unsigned requires_log:1;
+ unsigned requires_shm:1;
+ unsigned requires_capture_log:1;
+};
+
+
+
+
+struct ngx_stream_lua_srv_conf_s {
+#if (NGX_STREAM_SSL)
+ ngx_ssl_t *ssl; /* shared by SSL cosockets */
+ ngx_array_t *ssl_certificates;
+ ngx_array_t *ssl_certificate_keys;
+ ngx_uint_t ssl_protocols;
+ ngx_str_t ssl_ciphers;
+ ngx_uint_t ssl_verify_depth;
+ ngx_str_t ssl_trusted_certificate;
+ ngx_str_t ssl_crl;
+#if (nginx_version >= 1019004)
+ ngx_array_t *ssl_conf_commands;
+#endif
+
+ struct {
+ ngx_stream_lua_srv_conf_handler_pt ssl_cert_handler;
+ ngx_str_t ssl_cert_src;
+ u_char *ssl_cert_src_key;
+
+ ngx_stream_lua_srv_conf_handler_pt ssl_client_hello_handler;
+ ngx_str_t ssl_client_hello_src;
+ u_char *ssl_client_hello_src_key;
+ } srv;
+#endif
+
+ ngx_flag_t enable_code_cache; /* whether to enable
+ code cache */
+
+ ngx_stream_lua_handler_pt preread_handler;
+
+ ngx_stream_lua_handler_pt content_handler;
+ ngx_stream_lua_handler_pt log_handler;
+
+ u_char *preread_chunkname;
+ ngx_stream_complex_value_t preread_src; /* access_by_lua
+ inline script/script
+ file path */
+
+ u_char *preread_src_key; /* cached key for access_src */
+
+ u_char *content_chunkname;
+
+ ngx_stream_complex_value_t content_src;
+ /* content_by_lua
+ * inline script/script
+ * file path */
+
+ u_char *content_src_key; /* cached key for content_src */
+
+ u_char *log_chunkname;
+ ngx_stream_complex_value_t log_src;
+ /* log_by_lua inline script/script
+ * file path */
+
+ u_char *log_src_key;
+ /* cached key for log_src */
+
+
+ ngx_msec_t keepalive_timeout;
+ ngx_msec_t connect_timeout;
+ ngx_msec_t send_timeout;
+ ngx_msec_t read_timeout;
+
+ size_t send_lowat;
+ size_t buffer_size;
+
+ ngx_uint_t pool_size;
+
+
+ ngx_flag_t log_socket_errors;
+ ngx_flag_t check_client_abort;
+
+
+ struct {
+ ngx_str_t src;
+ u_char *src_key;
+
+ ngx_stream_lua_srv_conf_handler_pt handler;
+ } balancer;
+
+};
+
+typedef ngx_stream_lua_srv_conf_t ngx_stream_lua_loc_conf_t;
+
+
+typedef enum {
+ NGX_STREAM_LUA_USER_CORO_NOP = 0,
+ NGX_STREAM_LUA_USER_CORO_RESUME = 1,
+ NGX_STREAM_LUA_USER_CORO_YIELD = 2,
+ NGX_STREAM_LUA_USER_THREAD_RESUME = 3
+} ngx_stream_lua_user_coro_op_t;
+
+
+typedef enum {
+ NGX_STREAM_LUA_CO_RUNNING = 0, /* coroutine running */
+ NGX_STREAM_LUA_CO_SUSPENDED = 1, /* coroutine suspended */
+ NGX_STREAM_LUA_CO_NORMAL = 2, /* coroutine normal */
+ NGX_STREAM_LUA_CO_DEAD = 3, /* coroutine dead */
+ NGX_STREAM_LUA_CO_ZOMBIE = 4, /* coroutine zombie */
+} ngx_stream_lua_co_status_t;
+
+
+typedef struct ngx_stream_lua_co_ctx_s ngx_stream_lua_co_ctx_t;
+
+typedef struct ngx_stream_lua_posted_thread_s ngx_stream_lua_posted_thread_t;
+
+struct ngx_stream_lua_posted_thread_s {
+ ngx_stream_lua_co_ctx_t *co_ctx;
+ ngx_stream_lua_posted_thread_t *next;
+};
+
+
+
+
+struct ngx_stream_lua_co_ctx_s {
+ void *data; /* user state for cosockets */
+
+ lua_State *co;
+ ngx_stream_lua_co_ctx_t *parent_co_ctx;
+
+ ngx_stream_lua_posted_thread_t *zombie_child_threads;
+
+ ngx_stream_lua_cleanup_pt cleanup;
+
+
+ ngx_event_t sleep; /* used for ngx.sleep */
+
+ ngx_queue_t sem_wait_queue;
+
+#ifdef NGX_LUA_USE_ASSERT
+ int co_top; /* stack top after yielding/creation,
+ only for sanity checks */
+#endif
+
+ int co_ref; /* reference to anchor the thread
+ coroutines (entry coroutine and user
+ threads) in the Lua registry,
+ preventing the thread coroutine
+ from beging collected by the
+ Lua GC */
+
+ unsigned waited_by_parent:1; /* whether being waited by
+ a parent coroutine */
+
+ unsigned co_status:3; /* the current coroutine's status */
+
+ unsigned flushing:1; /* indicates whether the current
+ coroutine is waiting for
+ ngx.flush(true) */
+
+ unsigned is_uthread:1; /* whether the current coroutine is
+ a user thread */
+
+ unsigned thread_spawn_yielded:1; /* yielded from
+ the ngx.thread.spawn()
+ call */
+ unsigned sem_resume_status:1;
+
+ unsigned is_wrap:1; /* set when creating coroutines via
+ coroutine.wrap */
+
+ unsigned propagate_error:1; /* set when propagating an error
+ from a coroutine to its
+ parent */
+};
+
+
+typedef struct {
+ lua_State *vm;
+ ngx_int_t count;
+} ngx_stream_lua_vm_state_t;
+
+
+typedef struct ngx_stream_lua_ctx_s {
+ /* for lua_coce_cache off: */
+ ngx_stream_lua_vm_state_t *vm_state;
+
+ ngx_stream_lua_request_t *request;
+ ngx_stream_lua_handler_pt resume_handler;
+
+ ngx_stream_lua_co_ctx_t *cur_co_ctx;
+ /* co ctx for the current coroutine */
+
+ /* FIXME: we should use rbtree here to prevent O(n) lookup overhead */
+ ngx_list_t *user_co_ctx; /* coroutine contexts for user
+ coroutines */
+
+ ngx_stream_lua_co_ctx_t entry_co_ctx; /* coroutine context for the
+ entry coroutine */
+
+ ngx_stream_lua_co_ctx_t *on_abort_co_ctx; /* coroutine context for the
+ on_abort thread */
+
+ int ctx_ref; /* reference to anchor
+ request ctx data in lua
+ registry */
+
+ unsigned flushing_coros; /* number of coroutines waiting on
+ ngx.flush(true) */
+
+ ngx_chain_t *out; /* buffered output chain for HTTP 1.0 */
+ ngx_chain_t *free_bufs;
+ ngx_chain_t *busy_bufs;
+ ngx_chain_t *free_recv_bufs;
+
+ ngx_stream_lua_cleanup_pt *cleanup;
+ ngx_stream_lua_cleanup_t *free_cleanup; /* free list of cleanup records */
+
+
+
+ ngx_int_t exit_code;
+
+ void *downstream;
+ /* can be either
+ * ngx_stream_lua_socket_tcp_upstream_t
+ * or ngx_stream_lua_co_ctx_t */
+
+
+ ngx_stream_lua_posted_thread_t *posted_threads;
+
+ int uthreads; /* number of active user threads */
+
+ uint16_t context; /* the current running directive context
+ (or running phase) for the current
+ Lua chunk */
+
+
+ unsigned waiting_more_body:1; /* 1: waiting for more
+ request body data;
+ 0: no need to wait */
+
+ unsigned co_op:2; /* coroutine API operation */
+
+ unsigned exited:1;
+
+ unsigned eof:1; /* 1: last_buf has been sent;
+ 0: last_buf not sent yet */
+
+ unsigned capture:1; /* 1: response body of current request
+ is to be captured by the lua
+ capture filter,
+ 0: not to be captured */
+
+
+ unsigned read_body_done:1; /* 1: request body has been all
+ read; 0: body has not been
+ all read */
+
+ unsigned headers_set:1; /* whether the user has set custom
+ response headers */
+
+ unsigned entered_preread_phase:1;
+
+ unsigned entered_content_phase:1;
+
+ unsigned buffering:1; /* HTTP 1.0 response body buffering flag */
+
+ unsigned no_abort:1; /* prohibit "world abortion" via ngx.exit()
+ and etc */
+
+ unsigned header_sent:1; /* r->header_sent is not sufficient for
+ * this because special header filters
+ * like ngx_image_filter may intercept
+ * the header. so we should always test
+ * both flags. see the test case in
+ * t/020-subrequest.t */
+
+ unsigned seen_last_in_filter:1; /* used by body_filter_by_lua* */
+ unsigned seen_last_for_subreq:1; /* used by body capture filter */
+ unsigned writing_raw_req_socket:1; /* used by raw downstream
+ socket */
+ unsigned acquired_raw_req_socket:1; /* whether a raw req socket
+ is acquired */
+ unsigned seen_body_data:1;
+ unsigned peek_needs_more_data:1; /* whether req socket is waiting
+ for more data in preread buf */
+} ngx_stream_lua_ctx_t;
+
+
+
+
+extern ngx_module_t ngx_stream_lua_module;
+
+
+
+#endif /* _NGX_STREAM_LUA_COMMON_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_config.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_config.c
new file mode 100644
index 0000000..36dbfbe
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_config.c
@@ -0,0 +1,78 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_config.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_config.h"
+#include "api/ngx_stream_lua_api.h"
+
+
+static int ngx_stream_lua_config_prefix(lua_State *L);
+static int ngx_stream_lua_config_configure(lua_State *L);
+
+
+void
+ngx_stream_lua_inject_config_api(lua_State *L)
+{
+ /* ngx.config */
+
+ lua_createtable(L, 0, 6 /* nrec */); /* .config */
+
+#if (NGX_DEBUG)
+ lua_pushboolean(L, 1);
+#else
+ lua_pushboolean(L, 0);
+#endif
+ lua_setfield(L, -2, "debug");
+
+ lua_pushcfunction(L, ngx_stream_lua_config_prefix);
+ lua_setfield(L, -2, "prefix");
+
+ lua_pushinteger(L, nginx_version);
+ lua_setfield(L, -2, "nginx_version");
+
+ lua_pushinteger(L, ngx_stream_lua_version);
+ lua_setfield(L, -2, "ngx_lua_version");
+
+ lua_pushcfunction(L, ngx_stream_lua_config_configure);
+ lua_setfield(L, -2, "nginx_configure");
+
+ lua_pushliteral(L, "stream");
+ lua_setfield(L, -2, "subsystem");
+
+ lua_setfield(L, -2, "config");
+}
+
+
+static int
+ngx_stream_lua_config_prefix(lua_State *L)
+{
+ lua_pushlstring(L, (char *) ngx_cycle->prefix.data,
+ ngx_cycle->prefix.len);
+ return 1;
+}
+
+
+static int
+ngx_stream_lua_config_configure(lua_State *L)
+{
+ lua_pushliteral(L, NGX_CONFIGURE);
+ return 1;
+}
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_config.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_config.h
new file mode 100644
index 0000000..025f4a6
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_config.h
@@ -0,0 +1,27 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_config.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_CONFIG_H_INCLUDED_
+#define _NGX_STREAM_LUA_CONFIG_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+void ngx_stream_lua_inject_config_api(lua_State *L);
+
+
+#endif /* _NGX_STREAM_LUA_CONFIG_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_consts.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_consts.c
new file mode 100644
index 0000000..ea19f90
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_consts.c
@@ -0,0 +1,51 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_consts.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_consts.h"
+
+
+void
+ngx_stream_lua_inject_core_consts(lua_State *L)
+{
+ /* {{{ core constants */
+ lua_pushinteger(L, NGX_OK);
+ lua_setfield(L, -2, "OK");
+
+ lua_pushinteger(L, NGX_AGAIN);
+ lua_setfield(L, -2, "AGAIN");
+
+ lua_pushinteger(L, NGX_DONE);
+ lua_setfield(L, -2, "DONE");
+
+ lua_pushinteger(L, NGX_DECLINED);
+ lua_setfield(L, -2, "DECLINED");
+
+ lua_pushinteger(L, NGX_ERROR);
+ lua_setfield(L, -2, "ERROR");
+
+ lua_pushlightuserdata(L, NULL);
+ lua_setfield(L, -2, "null");
+ /* }}} */
+}
+
+
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_consts.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_consts.h
new file mode 100644
index 0000000..355f8aa
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_consts.h
@@ -0,0 +1,29 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_consts.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_CONSTS_H_INCLUDED_
+#define _NGX_STREAM_LUA_CONSTS_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+
+
+void ngx_stream_lua_inject_core_consts(lua_State *L);
+
+
+#endif /* _NGX_STREAM_LUA_CONSTS_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_contentby.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_contentby.c
new file mode 100644
index 0000000..79370bf
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_contentby.c
@@ -0,0 +1,355 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_contentby.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_contentby.h"
+#include "ngx_stream_lua_util.h"
+#include "ngx_stream_lua_exception.h"
+#include "ngx_stream_lua_cache.h"
+#include "ngx_stream_lua_probe.h"
+
+
+
+
+ngx_int_t
+ngx_stream_lua_content_by_chunk(lua_State *L, ngx_stream_lua_request_t *r)
+{
+ int co_ref;
+ ngx_int_t rc;
+ lua_State *co;
+ ngx_event_t *rev;
+
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_cleanup_t *cln;
+ ngx_stream_lua_loc_conf_t *llcf;
+
+ dd("content by chunk");
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+
+ ngx_stream_lua_assert(ctx != NULL);
+
+ dd("reset ctx");
+ ngx_stream_lua_reset_ctx(r, L, ctx);
+
+ ctx->entered_content_phase = 1;
+
+ /* {{{ new coroutine to handle request */
+ co = ngx_stream_lua_new_thread(r, L, &co_ref);
+
+ if (co == NULL) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "lua: failed to create new coroutine to handle request");
+
+ return NGX_ERROR;
+ }
+
+ /* move code closure to new coroutine */
+ lua_xmove(L, co, 1);
+
+#ifndef OPENRESTY_LUAJIT
+ /* set closure's env table to new coroutine's globals table */
+ ngx_stream_lua_get_globals_table(co);
+ lua_setfenv(co, -2);
+#endif
+
+ /* save nginx request in coroutine globals table */
+ ngx_stream_lua_set_req(co, r);
+
+ ctx->cur_co_ctx = &ctx->entry_co_ctx;
+ ctx->cur_co_ctx->co = co;
+ ctx->cur_co_ctx->co_ref = co_ref;
+#ifdef NGX_LUA_USE_ASSERT
+ ctx->cur_co_ctx->co_top = 1;
+#endif
+
+ ngx_stream_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx);
+
+ /* {{{ register request cleanup hooks */
+ if (ctx->cleanup == NULL) {
+ cln = ngx_stream_lua_cleanup_add(r, 0);
+ if (cln == NULL) {
+ return NGX_ERROR;
+ }
+
+ cln->handler = ngx_stream_lua_request_cleanup_handler;
+ cln->data = ctx;
+ ctx->cleanup = &cln->handler;
+ }
+ /* }}} */
+
+ ctx->context = NGX_STREAM_LUA_CONTEXT_CONTENT;
+
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+
+ r->connection->read->handler = ngx_stream_lua_request_handler;
+ r->connection->write->handler = ngx_stream_lua_request_handler;
+
+ if (llcf->check_client_abort) {
+ r->read_event_handler = ngx_stream_lua_rd_check_broken_connection;
+
+
+ rev = r->connection->read;
+
+ if (!rev->active) {
+ if (ngx_add_event(rev, NGX_READ_EVENT, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+
+ } else {
+ r->read_event_handler = ngx_stream_lua_block_reading;
+ }
+
+ rc = ngx_stream_lua_run_thread(L, r, ctx, 0);
+
+ if (rc == NGX_ERROR || rc >= NGX_OK) {
+ return rc;
+ }
+
+ if (rc == NGX_AGAIN) {
+ return ngx_stream_lua_content_run_posted_threads(L, r, ctx, 0);
+ }
+
+ if (rc == NGX_DONE) {
+ return ngx_stream_lua_content_run_posted_threads(L, r, ctx, 1);
+ }
+
+ return NGX_OK;
+}
+
+
+void
+ngx_stream_lua_content_wev_handler(ngx_stream_lua_request_t *r)
+{
+ ngx_stream_lua_ctx_t *ctx;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua ngx_stream_lua_content_wev_handler");
+
+ (void) ctx->resume_handler(r);
+}
+
+
+void
+ngx_stream_lua_content_handler(ngx_stream_session_t *s)
+{
+ ngx_stream_lua_srv_conf_t *lscf;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_int_t rc;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
+ "stream lua content handler");
+
+ lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_lua_module);
+
+ if (lscf->content_handler == NULL) {
+ dd("no content handler found");
+ ngx_stream_finalize_session(s, NGX_DECLINED);
+
+ return;
+ }
+
+ ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module);
+
+ dd("ctx = %p", ctx);
+
+ if (ctx == NULL) {
+ ctx = ngx_stream_lua_create_ctx(s);
+ if (ctx == NULL) {
+ ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
+ return;
+ }
+ }
+
+ dd("entered? %d", (int) ctx->entered_content_phase);
+
+ if (ctx->entered_content_phase) {
+ dd("calling wev handler");
+ rc = ctx->resume_handler(ctx->request);
+ dd("wev handler returns %d", (int) rc);
+
+ ngx_stream_lua_finalize_request(ctx->request, rc);
+ return;
+ }
+
+ dd("setting entered");
+
+ ctx->entered_content_phase = 1;
+
+ dd("calling content handler");
+ ngx_stream_lua_finalize_request(ctx->request,
+ lscf->content_handler(ctx->request));
+
+ return;
+}
+
+
+
+
+ngx_int_t
+ngx_stream_lua_content_handler_file(ngx_stream_lua_request_t *r)
+{
+ lua_State *L;
+ ngx_int_t rc;
+ u_char *script_path;
+ ngx_str_t eval_src;
+
+ ngx_stream_lua_loc_conf_t *llcf;
+
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+
+ if (ngx_stream_complex_value(r->session, &llcf->content_src, &eval_src)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ script_path = ngx_stream_lua_rebase_path(r->pool, eval_src.data,
+ eval_src.len);
+
+ if (script_path == NULL) {
+ return NGX_ERROR;
+ }
+
+ L = ngx_stream_lua_get_lua_vm(r, NULL);
+
+ /* load Lua script file (w/ cache) sp = 1 */
+ rc = ngx_stream_lua_cache_loadfile(r->connection->log, L, script_path,
+ llcf->content_src_key);
+ if (rc != NGX_OK) {
+
+ return rc;
+ }
+
+ /* make sure we have a valid code chunk */
+ ngx_stream_lua_assert(lua_isfunction(L, -1));
+
+ return ngx_stream_lua_content_by_chunk(L, r);
+}
+
+
+ngx_int_t
+ngx_stream_lua_content_handler_inline(ngx_stream_lua_request_t *r)
+{
+ lua_State *L;
+ ngx_int_t rc;
+
+ ngx_stream_lua_loc_conf_t *llcf;
+
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+
+ L = ngx_stream_lua_get_lua_vm(r, NULL);
+
+ /* load Lua inline script (w/ cache) sp = 1 */
+ rc = ngx_stream_lua_cache_loadbuffer(r->connection->log, L,
+ llcf->content_src.value.data,
+ llcf->content_src.value.len,
+ llcf->content_src_key,
+ (const char *)
+ llcf->content_chunkname);
+ if (rc != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return ngx_stream_lua_content_by_chunk(L, r);
+}
+
+
+ngx_int_t
+ngx_stream_lua_content_run_posted_threads(lua_State *L,
+ ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx, int n)
+{
+ ngx_int_t rc;
+
+ ngx_stream_lua_posted_thread_t *pt;
+
+ dd("run posted threads: %p", ctx->posted_threads);
+
+ for ( ;; ) {
+ pt = ctx->posted_threads;
+ if (pt == NULL) {
+ goto done;
+ }
+
+ ctx->posted_threads = pt->next;
+
+ ngx_stream_lua_probe_run_posted_thread(r, pt->co_ctx->co,
+ (int) pt->co_ctx->co_status);
+
+ dd("posted thread status: %d", pt->co_ctx->co_status);
+
+ if (pt->co_ctx->co_status != NGX_STREAM_LUA_CO_RUNNING) {
+ continue;
+ }
+
+ ctx->cur_co_ctx = pt->co_ctx;
+
+ rc = ngx_stream_lua_run_thread(L, r, ctx, 0);
+
+ if (rc == NGX_AGAIN) {
+ continue;
+ }
+
+ if (rc == NGX_DONE) {
+ n++;
+ continue;
+ }
+
+ if (rc == NGX_OK) {
+ while (n > 0) {
+ ngx_stream_lua_finalize_request(r, NGX_DONE);
+ n--;
+ }
+
+ return NGX_OK;
+ }
+
+ /* rc == NGX_ERROR || rc > NGX_OK */
+
+ return rc;
+ }
+
+done:
+
+ if (n == 1) {
+ return NGX_DONE;
+ }
+
+ if (n == 0) {
+ return NGX_DONE;
+ }
+
+ /* n > 1 */
+
+ do {
+ ngx_stream_lua_finalize_request(r, NGX_DONE);
+ } while (--n > 1);
+
+ return NGX_DONE;
+}
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_contentby.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_contentby.h
new file mode 100644
index 0000000..59766fb
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_contentby.h
@@ -0,0 +1,37 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_contentby.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_CONTENT_BY_H_INCLUDED_
+#define _NGX_STREAM_LUA_CONTENT_BY_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+ngx_int_t ngx_stream_lua_content_by_chunk(lua_State *L,
+ ngx_stream_lua_request_t *r);
+void ngx_stream_lua_content_wev_handler(ngx_stream_lua_request_t *r);
+ngx_int_t ngx_stream_lua_content_handler_file(ngx_stream_lua_request_t *r);
+ngx_int_t ngx_stream_lua_content_handler_inline(ngx_stream_lua_request_t *r);
+
+void ngx_stream_lua_content_handler(ngx_stream_session_t *r);
+
+ngx_int_t ngx_stream_lua_content_run_posted_threads(lua_State *L,
+ ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx, int n);
+
+
+#endif /* _NGX_STREAM_LUA_CONTENT_BY_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_control.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_control.c
new file mode 100644
index 0000000..86dda5d
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_control.c
@@ -0,0 +1,164 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_control.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_control.h"
+#include "ngx_stream_lua_util.h"
+#include "ngx_stream_lua_coroutine.h"
+
+
+
+
+static int ngx_stream_lua_on_abort(lua_State *L);
+
+
+void
+ngx_stream_lua_inject_control_api(ngx_log_t *log, lua_State *L)
+{
+
+ /* ngx.on_abort */
+
+ lua_pushcfunction(L, ngx_stream_lua_on_abort);
+ lua_setfield(L, -2, "on_abort");
+}
+
+
+
+
+static int
+ngx_stream_lua_on_abort(lua_State *L)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx = NULL;
+ ngx_stream_lua_loc_conf_t *llcf;
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return luaL_error(L, "no request ctx found");
+ }
+
+ ngx_stream_lua_check_fake_request2(L, r, ctx);
+
+ if (ctx->on_abort_co_ctx) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "duplicate call");
+ return 2;
+ }
+
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+ if (!llcf->check_client_abort) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "lua_check_client_abort is off");
+ return 2;
+ }
+
+ ngx_stream_lua_coroutine_create_helper(L, r, ctx, &coctx);
+
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ coroutines_key));
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ lua_pushvalue(L, -2);
+
+ dd("on_wait thread 1: %p", lua_tothread(L, -1));
+
+ coctx->co_ref = luaL_ref(L, -2);
+ lua_pop(L, 1);
+
+ coctx->is_uthread = 1;
+ ctx->on_abort_co_ctx = coctx;
+
+ dd("on_wait thread 2: %p", coctx->co);
+
+ coctx->co_status = NGX_STREAM_LUA_CO_SUSPENDED;
+ coctx->parent_co_ctx = ctx->cur_co_ctx;
+
+ lua_pushinteger(L, 1);
+ return 1;
+}
+
+
+int
+ngx_stream_lua_ffi_exit(ngx_stream_lua_request_t *r, int status, u_char *err,
+ size_t *errlen)
+{
+ ngx_stream_lua_ctx_t *ctx;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ *errlen = ngx_snprintf(err, *errlen, "no request ctx found") - err;
+ return NGX_ERROR;
+ }
+
+
+ if (ngx_stream_lua_ffi_check_context(ctx, NGX_STREAM_LUA_CONTEXT_CONTENT
+ | NGX_STREAM_LUA_CONTEXT_TIMER
+ | NGX_STREAM_LUA_CONTEXT_BALANCER
+ | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO
+ | NGX_STREAM_LUA_CONTEXT_SSL_CERT
+ | NGX_STREAM_LUA_CONTEXT_PREREAD,
+ err, errlen) != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (ctx->context & (NGX_STREAM_LUA_CONTEXT_SSL_CERT
+ | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO ))
+ {
+
+#if (NGX_STREAM_SSL)
+
+ ctx->exit_code = status;
+ ctx->exited = 1;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua exit with code %d", status);
+
+
+ return NGX_OK;
+
+#else
+
+ return NGX_ERROR;
+
+#endif
+ }
+
+
+ ctx->exit_code = status;
+ ctx->exited = 1;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua exit with code %i", ctx->exit_code);
+
+ if (ctx->context & NGX_STREAM_LUA_CONTEXT_BALANCER) {
+ return NGX_DONE;
+ }
+
+ return NGX_OK;
+}
+
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_control.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_control.h
new file mode 100644
index 0000000..0533314
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_control.h
@@ -0,0 +1,28 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_control.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_CONTROL_H_INCLUDED_
+#define _NGX_STREAM_LUA_CONTROL_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+void ngx_stream_lua_inject_control_api(ngx_log_t *log, lua_State *L);
+
+
+#endif /* _NGX_STREAM_LUA_CONTROL_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_coroutine.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_coroutine.c
new file mode 100644
index 0000000..d4863dd
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_coroutine.c
@@ -0,0 +1,450 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_coroutine.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_coroutine.h"
+#include "ngx_stream_lua_util.h"
+#include "ngx_stream_lua_probe.h"
+
+
+/*
+ * Design:
+ *
+ * In order to support using ngx.* API in Lua coroutines, we have to create
+ * new coroutine in the main coroutine instead of the calling coroutine
+ */
+
+
+static int ngx_stream_lua_coroutine_create(lua_State *L);
+static int ngx_stream_lua_coroutine_wrap(lua_State *L);
+static int ngx_stream_lua_coroutine_resume(lua_State *L);
+static int ngx_stream_lua_coroutine_yield(lua_State *L);
+static int ngx_stream_lua_coroutine_status(lua_State *L);
+
+
+static const ngx_str_t
+ ngx_stream_lua_co_status_names[] =
+ {
+ ngx_string("running"),
+ ngx_string("suspended"),
+ ngx_string("normal"),
+ ngx_string("dead"),
+ ngx_string("zombie")
+ };
+
+
+
+static int
+ngx_stream_lua_coroutine_create(lua_State *L)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return luaL_error(L, "no request ctx found");
+ }
+
+ return ngx_stream_lua_coroutine_create_helper(L, r, ctx, NULL);
+}
+
+
+static int
+ngx_stream_lua_coroutine_wrap_runner(lua_State *L)
+{
+ /* retrieve closure and insert it at the bottom of
+ * the stack for coroutine.resume() */
+ lua_pushvalue(L, lua_upvalueindex(1));
+ lua_insert(L, 1);
+
+ return ngx_stream_lua_coroutine_resume(L);
+}
+
+
+static int
+ngx_stream_lua_coroutine_wrap(lua_State *L)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx = NULL;
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return luaL_error(L, "no request ctx found");
+ }
+
+ ngx_stream_lua_coroutine_create_helper(L, r, ctx, &coctx);
+
+ coctx->is_wrap = 1;
+
+ lua_pushcclosure(L, ngx_stream_lua_coroutine_wrap_runner, 1);
+
+ return 1;
+}
+
+
+int
+ngx_stream_lua_coroutine_create_helper(lua_State *L,
+ ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx,
+ ngx_stream_lua_co_ctx_t **pcoctx)
+{
+ lua_State *vm; /* the Lua VM */
+ lua_State *co; /* new coroutine to be created */
+
+ /* co ctx for the new coroutine */
+ ngx_stream_lua_co_ctx_t *coctx;
+
+ luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1,
+ "Lua function expected");
+
+ ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_YIELDABLE);
+
+ vm = ngx_stream_lua_get_lua_vm(r, ctx);
+
+ /* create new coroutine on root Lua state, so it always yields
+ * to main Lua thread
+ */
+ co = lua_newthread(vm);
+
+ ngx_stream_lua_probe_user_coroutine_create(r, L, co);
+
+ coctx = ngx_stream_lua_get_co_ctx(co, ctx);
+ if (coctx == NULL) {
+ coctx = ngx_stream_lua_create_co_ctx(r, ctx);
+ if (coctx == NULL) {
+ return luaL_error(L, "no memory");
+ }
+
+ } else {
+ ngx_memzero(coctx, sizeof(ngx_stream_lua_co_ctx_t));
+ coctx->co_ref = LUA_NOREF;
+ }
+
+ coctx->co = co;
+ coctx->co_status = NGX_STREAM_LUA_CO_SUSPENDED;
+
+#ifdef OPENRESTY_LUAJIT
+ ngx_stream_lua_set_req(co, r);
+ ngx_stream_lua_attach_co_ctx_to_L(co, coctx);
+#else
+ /* make new coroutine share globals of the parent coroutine.
+ * NOTE: globals don't have to be separated! */
+ ngx_stream_lua_get_globals_table(L);
+ lua_xmove(L, co, 1);
+ ngx_stream_lua_set_globals_table(co);
+#endif
+
+ lua_xmove(vm, L, 1); /* move coroutine from main thread to L */
+
+ lua_pushvalue(L, 1); /* copy entry function to top of L*/
+ lua_xmove(L, co, 1); /* move entry function from L to co */
+
+ if (pcoctx) {
+ *pcoctx = coctx;
+ }
+
+#ifdef NGX_LUA_USE_ASSERT
+ coctx->co_top = 1;
+#endif
+
+ return 1; /* return new coroutine to Lua */
+}
+
+
+static int
+ngx_stream_lua_coroutine_resume(lua_State *L)
+{
+ lua_State *co;
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx;
+ ngx_stream_lua_co_ctx_t *p_coctx; /* parent co ctx */
+
+ co = lua_tothread(L, 1);
+
+ luaL_argcheck(L, co, 1, "coroutine expected");
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return luaL_error(L, "no request ctx found");
+ }
+
+ ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT
+ | NGX_STREAM_LUA_CONTEXT_TIMER
+ | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO
+ | NGX_STREAM_LUA_CONTEXT_SSL_CERT
+ | NGX_STREAM_LUA_CONTEXT_PREREAD
+ );
+
+ p_coctx = ctx->cur_co_ctx;
+ if (p_coctx == NULL) {
+ return luaL_error(L, "no parent co ctx found");
+ }
+
+ coctx = ngx_stream_lua_get_co_ctx(co, ctx);
+ if (coctx == NULL) {
+ return luaL_error(L, "no co ctx found");
+ }
+
+ ngx_stream_lua_probe_user_coroutine_resume(r, L, co);
+
+ if (coctx->co_status != NGX_STREAM_LUA_CO_SUSPENDED) {
+ dd("coroutine resume: %d", coctx->co_status);
+
+ lua_pushboolean(L, 0);
+ lua_pushfstring(L, "cannot resume %s coroutine",
+ ngx_stream_lua_co_status_names[coctx->co_status].data);
+ return 2;
+ }
+
+ p_coctx->co_status = NGX_STREAM_LUA_CO_NORMAL;
+
+ coctx->parent_co_ctx = p_coctx;
+
+ dd("set coroutine to running");
+ coctx->co_status = NGX_STREAM_LUA_CO_RUNNING;
+
+ ctx->co_op = NGX_STREAM_LUA_USER_CORO_RESUME;
+ ctx->cur_co_ctx = coctx;
+
+ /* yield and pass args to main thread, and resume target coroutine from
+ * there */
+ return lua_yield(L, lua_gettop(L) - 1);
+}
+
+
+static int
+ngx_stream_lua_coroutine_yield(lua_State *L)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx;
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return luaL_error(L, "no request ctx found");
+ }
+
+ ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT
+ | NGX_STREAM_LUA_CONTEXT_TIMER
+ | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO
+ | NGX_STREAM_LUA_CONTEXT_SSL_CERT
+ | NGX_STREAM_LUA_CONTEXT_PREREAD
+ );
+
+ coctx = ctx->cur_co_ctx;
+
+ coctx->co_status = NGX_STREAM_LUA_CO_SUSPENDED;
+
+ ctx->co_op = NGX_STREAM_LUA_USER_CORO_YIELD;
+
+ if (!coctx->is_uthread && coctx->parent_co_ctx) {
+ dd("set coroutine to running");
+ coctx->parent_co_ctx->co_status = NGX_STREAM_LUA_CO_RUNNING;
+
+ ngx_stream_lua_probe_user_coroutine_yield(r,
+ coctx->parent_co_ctx->co, L);
+
+ } else {
+ ngx_stream_lua_probe_user_coroutine_yield(r, NULL, L);
+ }
+
+ /* yield and pass retvals to main thread,
+ * and resume parent coroutine there */
+ return lua_yield(L, lua_gettop(L));
+}
+
+
+void
+ngx_stream_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L)
+{
+ int rc;
+
+ /* new coroutine table */
+ lua_createtable(L, 0 /* narr */, 16 /* nrec */);
+
+ /* get old coroutine table */
+ lua_getglobal(L, "coroutine");
+
+ /* set running to the old one */
+ lua_getfield(L, -1, "running");
+ lua_setfield(L, -3, "running");
+
+ lua_getfield(L, -1, "create");
+ lua_setfield(L, -3, "_create");
+
+ lua_getfield(L, -1, "wrap");
+ lua_setfield(L, -3, "_wrap");
+
+ lua_getfield(L, -1, "resume");
+ lua_setfield(L, -3, "_resume");
+
+ lua_getfield(L, -1, "yield");
+ lua_setfield(L, -3, "_yield");
+
+ lua_getfield(L, -1, "status");
+ lua_setfield(L, -3, "_status");
+
+ /* pop the old coroutine */
+ lua_pop(L, 1);
+
+ lua_pushcfunction(L, ngx_stream_lua_coroutine_create);
+ lua_setfield(L, -2, "__create");
+
+ lua_pushcfunction(L, ngx_stream_lua_coroutine_wrap);
+ lua_setfield(L, -2, "__wrap");
+
+ lua_pushcfunction(L, ngx_stream_lua_coroutine_resume);
+ lua_setfield(L, -2, "__resume");
+
+ lua_pushcfunction(L, ngx_stream_lua_coroutine_yield);
+ lua_setfield(L, -2, "__yield");
+
+ lua_pushcfunction(L, ngx_stream_lua_coroutine_status);
+ lua_setfield(L, -2, "__status");
+
+ lua_setglobal(L, "coroutine");
+
+ /* inject coroutine APIs */
+ {
+ const char buf[] =
+ "local keys = {'create', 'yield', 'resume', 'status', 'wrap'}\n"
+#ifdef OPENRESTY_LUAJIT
+ "local get_req = require 'thread.exdata'\n"
+#else
+ "local getfenv = getfenv\n"
+#endif
+ "for _, key in ipairs(keys) do\n"
+ "local std = coroutine['_' .. key]\n"
+ "local ours = coroutine['__' .. key]\n"
+ "local raw_ctx = ngx._phase_ctx\n"
+ "coroutine[key] = function (...)\n"
+#ifdef OPENRESTY_LUAJIT
+ "local r = get_req()\n"
+#else
+ "local r = getfenv(0).__ngx_req\n"
+#endif
+ "if r ~= nil then\n"
+#ifdef OPENRESTY_LUAJIT
+ "local ctx = raw_ctx()\n"
+#else
+ "local ctx = raw_ctx(r)\n"
+#endif
+ "return ours(...)\n"
+ "end\n"
+ "return std(...)\n"
+ "end\n"
+ "end\n"
+ "package.loaded.coroutine = coroutine"
+#if 0
+ "debug.sethook(function () collectgarbage() end, 'rl', 1)"
+#endif
+ ;
+
+ rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, "=coroutine_api");
+ }
+
+ if (rc != 0) {
+ ngx_log_error(NGX_LOG_ERR, log, 0,
+ "failed to load Lua code for coroutine_api: %i: %s",
+ rc, lua_tostring(L, -1));
+
+ lua_pop(L, 1);
+ return;
+ }
+
+ rc = lua_pcall(L, 0, 0, 0);
+ if (rc != 0) {
+ ngx_log_error(NGX_LOG_ERR, log, 0,
+ "failed to run the Lua code for coroutine_api: %i: %s",
+ rc, lua_tostring(L, -1));
+ lua_pop(L, 1);
+ }
+}
+
+
+static int
+ngx_stream_lua_coroutine_status(lua_State *L)
+{
+ lua_State *co; /* new coroutine to be created */
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx; /* co ctx for the new coroutine */
+
+ co = lua_tothread(L, 1);
+
+ luaL_argcheck(L, co, 1, "coroutine expected");
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return luaL_error(L, "no request ctx found");
+ }
+
+ ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT
+ | NGX_STREAM_LUA_CONTEXT_TIMER
+ | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO
+ | NGX_STREAM_LUA_CONTEXT_SSL_CERT
+ | NGX_STREAM_LUA_CONTEXT_PREREAD
+ );
+
+ coctx = ngx_stream_lua_get_co_ctx(co, ctx);
+ if (coctx == NULL) {
+ lua_pushlstring(L, (const char *)
+ ngx_stream_lua_co_status_names[NGX_STREAM_LUA_CO_DEAD]
+ .data,
+ ngx_stream_lua_co_status_names[NGX_STREAM_LUA_CO_DEAD]
+ .len);
+ return 1;
+ }
+
+ dd("co status: %d", coctx->co_status);
+
+ lua_pushlstring(L, (const char *)
+ ngx_stream_lua_co_status_names[coctx->co_status].data,
+ ngx_stream_lua_co_status_names[coctx->co_status].len);
+ return 1;
+}
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_coroutine.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_coroutine.h
new file mode 100644
index 0000000..cad9693
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_coroutine.h
@@ -0,0 +1,32 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_coroutine.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_COROUTINE_H_INCLUDED_
+#define _NGX_STREAM_LUA_COROUTINE_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+void ngx_stream_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L);
+
+int ngx_stream_lua_coroutine_create_helper(lua_State *L,
+ ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx,
+ ngx_stream_lua_co_ctx_t **pcoctx);
+
+
+#endif /* _NGX_STREAM_LUA_COROUTINE_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_ctx.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_ctx.c
new file mode 100644
index 0000000..096dd8b
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_ctx.c
@@ -0,0 +1,210 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_ctx.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_util.h"
+#include "ngx_stream_lua_ssl.h"
+#include "ngx_stream_lua_ctx.h"
+
+
+typedef struct {
+ int ref;
+ lua_State *vm;
+} ngx_stream_lua_ngx_ctx_cleanup_data_t;
+
+
+static ngx_int_t ngx_stream_lua_ngx_ctx_add_cleanup(ngx_stream_lua_request_t *r,
+ ngx_pool_t *pool, int ref);
+static void ngx_stream_lua_ngx_ctx_cleanup(void *data);
+
+
+int
+ngx_stream_lua_ngx_set_ctx_helper(lua_State *L, ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx, int index)
+{
+ ngx_pool_t *pool;
+
+ if (index < 0) {
+ index = lua_gettop(L) + index + 1;
+ }
+
+ if (ctx->ctx_ref == LUA_NOREF) {
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua create ngx.ctx table for the current request");
+
+ lua_pushliteral(L, ngx_stream_lua_ctx_tables_key);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ lua_pushvalue(L, index);
+ ctx->ctx_ref = luaL_ref(L, -2);
+ lua_pop(L, 1);
+
+ pool = r->pool;
+ if (ngx_stream_lua_ngx_ctx_add_cleanup(r, pool, ctx->ctx_ref) != NGX_OK) {
+ return luaL_error(L, "no memory");
+ }
+
+ return 0;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua fetching existing ngx.ctx table for the current "
+ "request");
+
+ lua_pushliteral(L, ngx_stream_lua_ctx_tables_key);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ luaL_unref(L, -1, ctx->ctx_ref);
+ lua_pushvalue(L, index);
+ ctx->ctx_ref = luaL_ref(L, -2);
+ lua_pop(L, 1);
+
+ return 0;
+}
+
+
+int
+ngx_stream_lua_ffi_get_ctx_ref(ngx_stream_lua_request_t *r, int *in_ssl_phase,
+ int *ssl_ctx_ref)
+{
+ ngx_stream_lua_ctx_t *ctx;
+#if (NGX_STREAM_SSL)
+ ngx_stream_lua_ssl_ctx_t *ssl_ctx;
+#endif
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return NGX_STREAM_LUA_FFI_NO_REQ_CTX;
+ }
+
+ if (ctx->ctx_ref >= 0 || in_ssl_phase == NULL) {
+ return ctx->ctx_ref;
+ }
+
+ *in_ssl_phase = ctx->context & (NGX_STREAM_LUA_CONTEXT_SSL_CERT
+ | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO);
+ *ssl_ctx_ref = LUA_NOREF;
+
+#if (NGX_STREAM_SSL)
+ if (r->connection->ssl != NULL) {
+ ssl_ctx = ngx_stream_lua_ssl_get_ctx(r->connection->ssl->connection);
+
+ if (ssl_ctx != NULL) {
+ *ssl_ctx_ref = ssl_ctx->ctx_ref;
+ }
+ }
+#endif
+
+ return LUA_NOREF;
+}
+
+
+int
+ngx_stream_lua_ffi_set_ctx_ref(ngx_stream_lua_request_t *r, int ref)
+{
+ ngx_pool_t *pool;
+ ngx_stream_lua_ctx_t *ctx;
+#if (NGX_STREAM_SSL)
+ ngx_connection_t *c;
+ ngx_stream_lua_ssl_ctx_t *ssl_ctx;
+#endif
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return NGX_STREAM_LUA_FFI_NO_REQ_CTX;
+ }
+
+#if (NGX_STREAM_SSL)
+ if (ctx->context & (NGX_STREAM_LUA_CONTEXT_SSL_CERT
+ | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO))
+ {
+ ssl_ctx = ngx_stream_lua_ssl_get_ctx(r->connection->ssl->connection);
+ if (ssl_ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ssl_ctx->ctx_ref = ref;
+ c = ngx_ssl_get_connection(r->connection->ssl->connection);
+ pool = c->pool;
+
+ } else {
+ pool = r->pool;
+ }
+
+#else
+ pool = r->pool;
+#endif
+
+ ctx->ctx_ref = ref;
+
+ if (ngx_stream_lua_ngx_ctx_add_cleanup(r, pool, ref) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_ngx_ctx_add_cleanup(ngx_stream_lua_request_t *r, ngx_pool_t *pool,
+ int ref)
+{
+ lua_State *L;
+ ngx_pool_cleanup_t *cln;
+
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_ngx_ctx_cleanup_data_t *data;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ L = ngx_stream_lua_get_lua_vm(r, ctx);
+
+ cln = ngx_pool_cleanup_add(pool,
+ sizeof(ngx_stream_lua_ngx_ctx_cleanup_data_t));
+ if (cln == NULL) {
+ return NGX_ERROR;
+ }
+
+ cln->handler = ngx_stream_lua_ngx_ctx_cleanup;
+
+ data = cln->data;
+ data->vm = L;
+ data->ref = ref;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_stream_lua_ngx_ctx_cleanup(void *data)
+{
+ lua_State *L;
+
+ ngx_stream_lua_ngx_ctx_cleanup_data_t *clndata = data;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "lua release ngx.ctx at ref %d", clndata->ref);
+
+ L = clndata->vm;
+
+ lua_pushliteral(L, ngx_stream_lua_ctx_tables_key);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ luaL_unref(L, -1, clndata->ref);
+ lua_pop(L, 1);
+}
+
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_ctx.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_ctx.h
new file mode 100644
index 0000000..f8dfb86
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_ctx.h
@@ -0,0 +1,29 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_ctx.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_CTX_H_INCLUDED_
+#define _NGX_STREAM_LUA_CTX_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+int ngx_stream_lua_ngx_set_ctx_helper(lua_State *L, ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx, int index);
+
+
+#endif /* _NGX_STREAM_LUA_CTX_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_directive.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_directive.c
new file mode 100644
index 0000000..5580106
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_directive.c
@@ -0,0 +1,1338 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_directive.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_common.h"
+#include "ngx_stream_lua_directive.h"
+#include "ngx_stream_lua_util.h"
+#include "ngx_stream_lua_cache.h"
+#include "ngx_stream_lua_contentby.h"
+#include "ngx_stream_lua_logby.h"
+#include "ngx_stream_lua_initby.h"
+#include "ngx_stream_lua_initworkerby.h"
+#include "ngx_stream_lua_shdict.h"
+#include "ngx_stream_lua_lex.h"
+#include "ngx_stream_lua_log.h"
+#include "ngx_stream_lua_log_ringbuf.h"
+#include "api/ngx_stream_lua_api.h"
+
+#include "ngx_stream_lua_prereadby.h"
+
+
+typedef struct ngx_stream_lua_block_parser_ctx_s
+ ngx_stream_lua_block_parser_ctx_t;
+
+
+
+static u_char *ngx_stream_lua_gen_chunk_name(ngx_conf_t *cf, const char *tag,
+ size_t tag_len, size_t *chunkname_len);
+static ngx_int_t ngx_stream_lua_conf_read_lua_token(ngx_conf_t *cf,
+ ngx_stream_lua_block_parser_ctx_t *ctx);
+static u_char *ngx_stream_lua_strlstrn(u_char *s1, u_char *last, u_char *s2,
+ size_t n);
+
+
+struct ngx_stream_lua_block_parser_ctx_s {
+ ngx_uint_t start_line;
+ int token_len;
+};
+
+
+enum {
+ FOUND_LEFT_CURLY = 0,
+ FOUND_RIGHT_CURLY,
+ FOUND_LEFT_LBRACKET_STR,
+ FOUND_LBRACKET_STR = FOUND_LEFT_LBRACKET_STR,
+ FOUND_LEFT_LBRACKET_CMT,
+ FOUND_LBRACKET_CMT = FOUND_LEFT_LBRACKET_CMT,
+ FOUND_RIGHT_LBRACKET,
+ FOUND_COMMENT_LINE,
+ FOUND_DOUBLE_QUOTED,
+ FOUND_SINGLE_QUOTED
+};
+
+
+char *
+ngx_stream_lua_shared_dict(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_stream_lua_main_conf_t *lmcf = conf;
+ ngx_str_t *value, name;
+ ngx_shm_zone_t *zone;
+ ngx_shm_zone_t **zp;
+ ngx_stream_lua_shdict_ctx_t *ctx;
+ ssize_t size;
+
+ if (lmcf->shdict_zones == NULL) {
+ lmcf->shdict_zones = ngx_palloc(cf->pool, sizeof(ngx_array_t));
+ if (lmcf->shdict_zones == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_array_init(lmcf->shdict_zones, cf->pool, 2,
+ sizeof(ngx_shm_zone_t *))
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ value = cf->args->elts;
+
+ ctx = NULL;
+
+ if (value[1].len == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid lua shared dict name \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ name = value[1];
+
+ size = ngx_parse_size(&value[2]);
+
+ if (size <= 8191) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid lua shared dict size \"%V\"", &value[2]);
+ return NGX_CONF_ERROR;
+ }
+
+ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_lua_shdict_ctx_t));
+ if (ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->name = name;
+ ctx->main_conf = lmcf;
+ ctx->log = &cf->cycle->new_log;
+
+ zone = ngx_stream_lua_shared_memory_add(cf, &name, (size_t) size,
+ &ngx_stream_lua_module);
+ if (zone == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (zone->data) {
+ ctx = zone->data;
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "lua_shared_dict \"%V\" is already defined as "
+ "\"%V\"", &name, &ctx->name);
+ return NGX_CONF_ERROR;
+ }
+
+ zone->init = ngx_stream_lua_shdict_init_zone;
+ zone->data = ctx;
+
+ zp = ngx_array_push(lmcf->shdict_zones);
+ if (zp == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *zp = zone;
+
+ lmcf->requires_shm = 1;
+
+ return NGX_CONF_OK;
+}
+
+
+char *
+ngx_stream_lua_code_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *p = conf;
+ ngx_flag_t *fp;
+ char *ret;
+
+ ret = ngx_conf_set_flag_slot(cf, cmd, conf);
+ if (ret != NGX_CONF_OK) {
+ return ret;
+ }
+
+ fp = (ngx_flag_t *) (p + cmd->offset);
+
+ if (!*fp) {
+ ngx_conf_log_error(NGX_LOG_ALERT, cf, 0,
+ "stream lua_code_cache is off; this will hurt "
+ "performance");
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+char *
+ngx_stream_lua_load_resty_core(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "lua_load_resty_core is deprecated (the lua-resty-core "
+ "library is required since "
+ "ngx_stream_lua v0.0.8)");
+
+ return NGX_CONF_OK;
+}
+
+
+char *
+ngx_stream_lua_package_cpath(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_stream_lua_main_conf_t *lmcf = conf;
+
+ ngx_str_t *value;
+
+ if (lmcf->lua_cpath.len != 0) {
+ return "is duplicate";
+ }
+
+ dd("enter");
+
+ value = cf->args->elts;
+
+ lmcf->lua_cpath.len = value[1].len;
+ lmcf->lua_cpath.data = value[1].data;
+
+ return NGX_CONF_OK;
+}
+
+
+char *
+ngx_stream_lua_package_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_stream_lua_main_conf_t *lmcf = conf;
+
+ ngx_str_t *value;
+
+ if (lmcf->lua_path.len != 0) {
+ return "is duplicate";
+ }
+
+ dd("enter");
+
+ value = cf->args->elts;
+
+ lmcf->lua_path.len = value[1].len;
+ lmcf->lua_path.data = value[1].data;
+
+ return NGX_CONF_OK;
+}
+
+
+
+
+
+
+
+
+char *
+ngx_stream_lua_preread_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ char *rv;
+ ngx_conf_t save;
+
+ save = *cf;
+ cf->handler = ngx_stream_lua_preread_by_lua;
+ cf->handler_conf = conf;
+
+ rv = ngx_stream_lua_conf_lua_block_parse(cf, cmd);
+
+ *cf = save;
+
+ return rv;
+}
+
+
+char *
+ngx_stream_lua_preread_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ size_t chunkname_len;
+ u_char *p, *chunkname;
+ ngx_str_t *value;
+ ngx_stream_lua_main_conf_t *lmcf;
+ ngx_stream_lua_srv_conf_t *lscf = conf;
+
+ ngx_stream_compile_complex_value_t ccv;
+
+ dd("enter");
+
+ /* must specify a content handler */
+ if (cmd->post == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (lscf->preread_handler) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ if (value[1].len == 0) {
+ /* Oops...Invalid server conf */
+ ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
+ "invalid server config: no runnable Lua code");
+
+ return NGX_CONF_ERROR;
+ }
+
+ if (cmd->post == ngx_stream_lua_preread_handler_inline) {
+ chunkname = ngx_stream_lua_gen_chunk_name(cf, "preread_by_lua",
+ sizeof("preread_by_lua") - 1,
+ &chunkname_len);
+ if (chunkname == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ lscf->preread_chunkname = chunkname;
+
+ /* Don't eval nginx variables for inline lua code */
+
+ lscf->preread_src.value = value[1];
+
+ p = ngx_palloc(cf->pool,
+ chunkname_len + NGX_STREAM_LUA_INLINE_KEY_LEN + 1);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ lscf->preread_src_key = p;
+
+ p = ngx_copy(p, chunkname, chunkname_len);
+ p = ngx_copy(p, NGX_STREAM_LUA_INLINE_TAG,
+ NGX_STREAM_LUA_INLINE_TAG_LEN);
+ p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len);
+ *p = '\0';
+
+ } else {
+ ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));
+ ccv.cf = cf;
+ ccv.value = &value[1];
+ ccv.complex_value = &lscf->preread_src;
+
+ if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (lscf->preread_src.lengths == NULL) {
+ /* no variable found */
+ p = ngx_palloc(cf->pool, NGX_STREAM_LUA_FILE_KEY_LEN + 1);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ lscf->preread_src_key = p;
+
+ p = ngx_copy(p, NGX_STREAM_LUA_FILE_TAG,
+ NGX_STREAM_LUA_FILE_TAG_LEN);
+ p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len);
+ *p = '\0';
+ }
+ }
+
+ lscf->preread_handler = (ngx_stream_lua_handler_pt) cmd->post;
+
+ lmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_lua_module);
+
+ lmcf->requires_preread = 1;
+
+ return NGX_CONF_OK;
+}
+
+char *
+ngx_stream_lua_content_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ char *rv;
+ ngx_conf_t save;
+
+ save = *cf;
+ cf->handler = ngx_stream_lua_content_by_lua;
+ cf->handler_conf = conf;
+
+ rv = ngx_stream_lua_conf_lua_block_parse(cf, cmd);
+
+ *cf = save;
+
+ return rv;
+}
+
+
+char *
+ngx_stream_lua_content_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ size_t chunkname_len;
+ u_char *p;
+ u_char *chunkname;
+ ngx_str_t *value;
+
+ ngx_stream_core_srv_conf_t *cxcf;
+
+
+ ngx_stream_compile_complex_value_t ccv;
+
+ ngx_stream_lua_loc_conf_t *llcf = conf;
+
+ dd("enter");
+
+ /* must specify a content handler */
+ if (cmd->post == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (llcf->content_handler) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ dd("value[0]: %.*s", (int) value[0].len, value[0].data);
+ dd("value[1]: %.*s", (int) value[1].len, value[1].data);
+
+ if (value[1].len == 0) {
+ /* Oops...Invalid location conf */
+ ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
+ "invalid location config: no runnable Lua code");
+ return NGX_CONF_ERROR;
+ }
+
+ if (cmd->post == ngx_stream_lua_content_handler_inline) {
+ chunkname = ngx_stream_lua_gen_chunk_name(cf, "content_by_lua",
+ sizeof("content_by_lua") - 1,
+ &chunkname_len);
+ if (chunkname == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ llcf->content_chunkname = chunkname;
+
+ dd("chunkname: %s", chunkname);
+
+ /* Don't eval nginx variables for inline lua code */
+
+ llcf->content_src.value = value[1];
+
+ p = ngx_palloc(cf->pool,
+ chunkname_len + NGX_STREAM_LUA_INLINE_KEY_LEN + 1);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ llcf->content_src_key = p;
+
+ p = ngx_copy(p, chunkname, chunkname_len);
+ p = ngx_copy(p, NGX_STREAM_LUA_INLINE_TAG,
+ NGX_STREAM_LUA_INLINE_TAG_LEN);
+ p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len);
+ *p = '\0';
+
+ } else {
+
+ ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));
+ ccv.cf = cf;
+ ccv.value = &value[1];
+ ccv.complex_value = &llcf->content_src;
+
+ if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (llcf->content_src.lengths == NULL) {
+ /* no variable found */
+ p = ngx_palloc(cf->pool, NGX_STREAM_LUA_FILE_KEY_LEN + 1);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ llcf->content_src_key = p;
+
+ p = ngx_copy(p, NGX_STREAM_LUA_FILE_TAG,
+ NGX_STREAM_LUA_FILE_TAG_LEN);
+ p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len);
+ *p = '\0';
+ }
+ }
+
+ llcf->content_handler = (ngx_stream_lua_handler_pt) cmd->post;
+
+
+ /* register location content handler */
+ cxcf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_core_module);
+ if (cxcf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ cxcf->handler = ngx_stream_lua_content_handler;
+
+ return NGX_CONF_OK;
+}
+
+
+char *
+ngx_stream_lua_log_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ char *rv;
+ ngx_conf_t save;
+
+ save = *cf;
+ cf->handler = ngx_stream_lua_log_by_lua;
+ cf->handler_conf = conf;
+
+ rv = ngx_stream_lua_conf_lua_block_parse(cf, cmd);
+
+ *cf = save;
+
+ return rv;
+}
+
+
+char *
+ngx_stream_lua_log_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ size_t chunkname_len;
+ u_char *p, *chunkname;
+ ngx_str_t *value;
+ ngx_stream_lua_main_conf_t *lmcf;
+ ngx_stream_lua_loc_conf_t *llcf = conf;
+ ngx_stream_compile_complex_value_t ccv;
+
+ dd("enter");
+
+ /* must specify a log handler */
+ if (cmd->post == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (llcf->log_handler) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ if (value[1].len == 0) {
+ /* Oops...Invalid location conf */
+ ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
+ "invalid location config: no runnable Lua code");
+
+ return NGX_CONF_ERROR;
+ }
+
+ if (cmd->post == ngx_stream_lua_log_handler_inline) {
+ chunkname = ngx_stream_lua_gen_chunk_name(cf, "log_by_lua",
+ sizeof("log_by_lua") - 1,
+ &chunkname_len);
+ if (chunkname == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ llcf->log_chunkname = chunkname;
+
+ /* Don't eval nginx variables for inline lua code */
+
+ llcf->log_src.value = value[1];
+
+ p = ngx_palloc(cf->pool,
+ chunkname_len + NGX_STREAM_LUA_INLINE_KEY_LEN + 1);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ llcf->log_src_key = p;
+
+ p = ngx_copy(p, chunkname, chunkname_len);
+ p = ngx_copy(p, NGX_STREAM_LUA_INLINE_TAG,
+ NGX_STREAM_LUA_INLINE_TAG_LEN);
+ p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len);
+ *p = '\0';
+
+ } else {
+ ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));
+ ccv.cf = cf;
+ ccv.value = &value[1];
+ ccv.complex_value = &llcf->log_src;
+
+ if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (llcf->log_src.lengths == NULL) {
+ /* no variable found */
+ p = ngx_palloc(cf->pool, NGX_STREAM_LUA_FILE_KEY_LEN + 1);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ llcf->log_src_key = p;
+
+ p = ngx_copy(p, NGX_STREAM_LUA_FILE_TAG,
+ NGX_STREAM_LUA_FILE_TAG_LEN);
+ p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len);
+ *p = '\0';
+ }
+ }
+
+ llcf->log_handler = (ngx_stream_lua_handler_pt) cmd->post;
+
+ lmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_lua_module);
+
+ lmcf->requires_log = 1;
+
+ return NGX_CONF_OK;
+}
+
+
+
+
+char *
+ngx_stream_lua_init_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ char *rv;
+ ngx_conf_t save;
+
+ save = *cf;
+ cf->handler = ngx_stream_lua_init_by_lua;
+ cf->handler_conf = conf;
+
+ rv = ngx_stream_lua_conf_lua_block_parse(cf, cmd);
+
+ *cf = save;
+
+ return rv;
+}
+
+
+char *
+ngx_stream_lua_init_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ u_char *name;
+ ngx_str_t *value;
+ ngx_stream_lua_main_conf_t *lmcf = conf;
+
+ dd("enter");
+
+ /* must specify a content handler */
+ if (cmd->post == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (lmcf->init_handler) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ if (value[1].len == 0) {
+ /* Oops...Invalid location conf */
+ ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
+ "invalid location config: no runnable Lua code");
+ return NGX_CONF_ERROR;
+ }
+
+ lmcf->init_handler = (ngx_stream_lua_main_conf_handler_pt) cmd->post;
+
+ if (cmd->post == ngx_stream_lua_init_by_file) {
+ name = ngx_stream_lua_rebase_path(cf->pool, value[1].data,
+ value[1].len);
+ if (name == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ lmcf->init_src.data = name;
+ lmcf->init_src.len = ngx_strlen(name);
+
+ } else {
+ lmcf->init_src = value[1];
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+char *
+ngx_stream_lua_init_worker_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ char *rv;
+ ngx_conf_t save;
+
+ save = *cf;
+ cf->handler = ngx_stream_lua_init_worker_by_lua;
+ cf->handler_conf = conf;
+
+ rv = ngx_stream_lua_conf_lua_block_parse(cf, cmd);
+
+ *cf = save;
+
+ return rv;
+}
+
+
+char *
+ngx_stream_lua_init_worker_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ u_char *name;
+ ngx_str_t *value;
+
+ ngx_stream_lua_main_conf_t *lmcf = conf;
+
+ dd("enter");
+
+ /* must specify a content handler */
+ if (cmd->post == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (lmcf->init_worker_handler) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ lmcf->init_worker_handler = (ngx_stream_lua_main_conf_handler_pt) cmd->post;
+
+ if (cmd->post == ngx_stream_lua_init_worker_by_file) {
+ name = ngx_stream_lua_rebase_path(cf->pool, value[1].data,
+ value[1].len);
+ if (name == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ lmcf->init_worker_src.data = name;
+ lmcf->init_worker_src.len = ngx_strlen(name);
+
+ } else {
+ lmcf->init_worker_src = value[1];
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+
+
+static u_char *
+ngx_stream_lua_gen_chunk_name(ngx_conf_t *cf, const char *tag, size_t tag_len,
+ size_t *chunkname_len)
+{
+ u_char *p, *out;
+ size_t len;
+
+ len = sizeof("=(:)") - 1 + tag_len + cf->conf_file->file.name.len
+ + NGX_INT64_LEN + 1;
+
+ out = ngx_palloc(cf->pool, len);
+ if (out == NULL) {
+ return NULL;
+ }
+
+ if (cf->conf_file->file.name.len) {
+ p = cf->conf_file->file.name.data + cf->conf_file->file.name.len;
+ while (--p >= cf->conf_file->file.name.data) {
+ if (*p == '/' || *p == '\\') {
+ p++;
+ goto found;
+ }
+ }
+
+ p++;
+
+ } else {
+ p = cf->conf_file->file.name.data;
+ }
+
+found:
+
+ p = ngx_snprintf(out, len, "=%*s(%*s:%d)%Z",
+ tag_len, tag, cf->conf_file->file.name.data
+ + cf->conf_file->file.name.len - p,
+ p, cf->conf_file->line);
+
+ *chunkname_len = p - out - 1; /* exclude the trailing '\0' byte */
+
+ return out;
+}
+
+
+/* a specialized version of the standard ngx_conf_parse() function */
+char *
+ngx_stream_lua_conf_lua_block_parse(ngx_conf_t *cf, ngx_command_t *cmd)
+{
+ ngx_stream_lua_block_parser_ctx_t ctx;
+
+ int level = 1;
+ char *rv;
+ u_char *p;
+ size_t len;
+ ngx_str_t *src, *dst;
+ ngx_int_t rc;
+ ngx_uint_t i, start_line;
+ ngx_array_t *saved;
+ enum {
+ parse_block = 0,
+ parse_param
+ } type;
+
+ if (cf->conf_file->file.fd != NGX_INVALID_FILE) {
+
+ type = parse_block;
+
+ } else {
+ type = parse_param;
+ }
+
+ saved = cf->args;
+
+ cf->args = ngx_array_create(cf->temp_pool, 4, sizeof(ngx_str_t));
+ if (cf->args == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx.token_len = 0;
+ start_line = cf->conf_file->line;
+
+ dd("init start line: %d", (int) start_line);
+
+ ctx.start_line = start_line;
+
+ for ( ;; ) {
+ rc = ngx_stream_lua_conf_read_lua_token(cf, &ctx);
+
+ dd("parser start line: %d", (int) start_line);
+
+ switch (rc) {
+
+ case NGX_ERROR:
+ goto done;
+
+ case FOUND_LEFT_CURLY:
+
+ ctx.start_line = cf->conf_file->line;
+
+ if (type == parse_param) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "block directives are not supported "
+ "in -g option");
+ goto failed;
+ }
+
+ level++;
+ dd("seen block start: level=%d", (int) level);
+ break;
+
+ case FOUND_RIGHT_CURLY:
+
+ level--;
+ dd("seen block done: level=%d", (int) level);
+
+ if (type != parse_block || level < 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "unexpected \"}\": level %d, "
+ "starting at line %ui", level,
+ start_line);
+ goto failed;
+ }
+
+ if (level == 0) {
+ ngx_stream_lua_assert(cf->handler);
+
+ src = cf->args->elts;
+
+ for (len = 0, i = 0; i < cf->args->nelts; i++) {
+ len += src[i].len;
+ }
+
+ dd("saved nelts: %d", (int) saved->nelts);
+ dd("temp nelts: %d", (int) cf->args->nelts);
+#if 0
+ ngx_stream_lua_assert(saved->nelts == 1);
+#endif
+
+ dst = ngx_array_push(saved);
+ if (dst == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ dst->len = len;
+ dst->len--; /* skip the trailing '}' block terminator */
+
+ p = ngx_palloc(cf->pool, len);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ dst->data = p;
+
+ for (i = 0; i < cf->args->nelts; i++) {
+ p = ngx_copy(p, src[i].data, src[i].len);
+ }
+
+ p[-1] = '\0'; /* override the last '}' char to null */
+
+ cf->args = saved;
+
+ rv = (*cf->handler)(cf, cmd, cf->handler_conf);
+ if (rv == NGX_CONF_OK) {
+ goto done;
+ }
+
+ if (rv == NGX_CONF_ERROR) {
+ goto failed;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, rv);
+
+ goto failed;
+ }
+
+ break;
+
+ case FOUND_LBRACKET_STR:
+ case FOUND_LBRACKET_CMT:
+ case FOUND_RIGHT_LBRACKET:
+ case FOUND_COMMENT_LINE:
+ case FOUND_DOUBLE_QUOTED:
+ case FOUND_SINGLE_QUOTED:
+ break;
+
+ default:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "unknown return value from the lexer: %i", rc);
+ goto failed;
+ }
+ }
+
+failed:
+
+ rc = NGX_ERROR;
+
+done:
+
+ if (rc == NGX_ERROR) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_conf_read_lua_token(ngx_conf_t *cf,
+ ngx_stream_lua_block_parser_ctx_t *ctx)
+{
+ enum {
+ OVEC_SIZE = 2
+ };
+ int i, rc;
+ int ovec[OVEC_SIZE];
+ u_char *start, *p, *q, ch;
+ off_t file_size;
+ size_t len, buf_size;
+ ssize_t n, size;
+ ngx_uint_t start_line;
+ ngx_str_t *word;
+ ngx_buf_t *b;
+#if defined(nginx_version) && nginx_version >= 1009002
+ ngx_buf_t *dump;
+#endif
+
+ b = cf->conf_file->buffer;
+#if defined(nginx_version) && nginx_version >= 1009002
+ dump = cf->conf_file->dump;
+#endif
+ start = b->pos;
+ start_line = cf->conf_file->line;
+ buf_size = b->end - b->start;
+
+ dd("lexer start line: %d", (int) start_line);
+
+ file_size = ngx_file_size(&cf->conf_file->file.info);
+
+ for ( ;; ) {
+
+ if (b->pos >= b->last
+ || (b->last - b->pos < (b->end - b->start) / 3
+ && cf->conf_file->file.offset < file_size))
+ {
+
+ if (cf->conf_file->file.offset >= file_size) {
+
+ cf->conf_file->line = ctx->start_line;
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "unexpected end of file, expecting "
+ "terminating characters for lua code "
+ "block");
+ return NGX_ERROR;
+ }
+
+ len = b->last - start;
+
+ if (len == buf_size) {
+
+ cf->conf_file->line = start_line;
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "too long lua code block, probably "
+ "missing terminating characters");
+
+ return NGX_ERROR;
+ }
+
+ if (len) {
+ ngx_memmove(b->start, start, len);
+ }
+
+ size = (ssize_t) (file_size - cf->conf_file->file.offset);
+
+ if (size > b->end - (b->start + len)) {
+ size = b->end - (b->start + len);
+ }
+
+ n = ngx_read_file(&cf->conf_file->file, b->start + len, size,
+ cf->conf_file->file.offset);
+
+ if (n == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (n != size) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ ngx_read_file_n " returned "
+ "only %z bytes instead of %z",
+ n, size);
+ return NGX_ERROR;
+ }
+
+ b->pos = b->start + (b->pos - start);
+ b->last = b->start + len + n;
+ start = b->start;
+
+#if defined(nginx_version) && nginx_version >= 1009002
+ if (dump) {
+ dump->last = ngx_cpymem(dump->last, b->start + len, size);
+ }
+#endif
+ }
+
+ rc = ngx_stream_lua_lex(b->pos, b->last - b->pos, ovec);
+
+ if (rc < 0) { /* no match */
+ /* alas. the lexer does not yet support streaming processing. need
+ * more work below */
+
+ if (cf->conf_file->file.offset >= file_size) {
+
+ cf->conf_file->line = ctx->start_line;
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "unexpected end of file, expecting "
+ "terminating characters for lua code "
+ "block");
+ return NGX_ERROR;
+ }
+
+ len = b->last - b->pos;
+
+ if (len == buf_size) {
+
+ cf->conf_file->line = start_line;
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "too long lua code block, probably "
+ "missing terminating characters");
+
+ return NGX_ERROR;
+ }
+
+ if (len) {
+ ngx_memcpy(b->start, b->pos, len);
+ }
+
+ size = (ssize_t) (file_size - cf->conf_file->file.offset);
+
+ if (size > b->end - (b->start + len)) {
+ size = b->end - (b->start + len);
+ }
+
+ n = ngx_read_file(&cf->conf_file->file, b->start + len, size,
+ cf->conf_file->file.offset);
+
+ if (n == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (n != size) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ ngx_read_file_n " returned "
+ "only %z bytes instead of %z",
+ n, size);
+ return NGX_ERROR;
+ }
+
+ b->pos = b->start + len;
+ b->last = b->pos + n;
+ start = b->start;
+
+ continue;
+ }
+
+ if (rc == FOUND_LEFT_LBRACKET_STR || rc == FOUND_LEFT_LBRACKET_CMT) {
+
+ /* we update the line numbers for best error messages when the
+ * closing long bracket is missing */
+
+ for (i = 0; i < ovec[0]; i++) {
+ ch = b->pos[i];
+ if (ch == LF) {
+ cf->conf_file->line++;
+ }
+ }
+
+ b->pos += ovec[0];
+ ovec[1] -= ovec[0];
+ ovec[0] = 0;
+
+ if (rc == FOUND_LEFT_LBRACKET_CMT) {
+ p = &b->pos[2]; /* we skip the leading "--" prefix */
+ rc = FOUND_LBRACKET_CMT;
+
+ } else {
+ p = b->pos;
+ rc = FOUND_LBRACKET_STR;
+ }
+
+ /* we temporarily rewrite [=*[ in the input buffer to ]=*] to
+ * construct the pattern for the corresponding closing long
+ * bracket without additional buffers. */
+
+ ngx_stream_lua_assert(p[0] == '[');
+ p[0] = ']';
+
+ ngx_stream_lua_assert(b->pos[ovec[1] - 1] == '[');
+ b->pos[ovec[1] - 1] = ']';
+
+ /* search for the corresponding closing bracket */
+
+ dd("search pattern for the closing long bracket: \"%.*s\" (len=%d)",
+ (int) (b->pos + ovec[1] - p), p, (int) (b->pos + ovec[1] - p));
+
+ q = ngx_stream_lua_strlstrn(b->pos + ovec[1], b->last, p,
+ b->pos + ovec[1] - p - 1);
+
+ if (q == NULL) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "Lua code block missing the closing "
+ "long bracket \"%*s\"",
+ b->pos + ovec[1] - p, p);
+ return NGX_ERROR;
+ }
+
+ /* restore the original opening long bracket */
+
+ p[0] = '[';
+ b->pos[ovec[1] - 1] = '[';
+
+ ovec[1] = q - b->pos + b->pos + ovec[1] - p;
+
+ dd("found long bracket token: \"%.*s\"",
+ (int) (ovec[1] - ovec[0]), b->pos + ovec[0]);
+ }
+
+ for (i = 0; i < ovec[1]; i++) {
+ ch = b->pos[i];
+ if (ch == LF) {
+ cf->conf_file->line++;
+ }
+ }
+
+ b->pos += ovec[1];
+ ctx->token_len = ovec[1] - ovec[0];
+
+ break;
+ }
+
+ word = ngx_array_push(cf->args);
+ if (word == NULL) {
+ return NGX_ERROR;
+ }
+
+ word->data = ngx_pnalloc(cf->temp_pool, b->pos - start);
+ if (word->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ len = b->pos - start;
+ ngx_memcpy(word->data, start, len);
+ word->len = len;
+
+ return rc;
+}
+
+
+char *
+ngx_stream_lua_capture_error_log(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+#ifndef HAVE_INTERCEPT_ERROR_LOG_PATCH
+ return "not found: missing the capture error log patch for nginx";
+#else
+ ngx_str_t *value;
+ ssize_t size;
+ u_char *data;
+ ngx_cycle_t *cycle;
+
+ ngx_stream_lua_main_conf_t *lmcf = conf;
+ ngx_stream_lua_log_ringbuf_t *ringbuf;
+
+ value = cf->args->elts;
+ cycle = cf->cycle;
+
+ if (lmcf->requires_capture_log) {
+ return "is duplicate";
+ }
+
+ if (value[1].len == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid capture error log size \"%V\"",
+ &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ size = ngx_parse_size(&value[1]);
+
+ if (size < NGX_MAX_ERROR_STR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid capture error log size \"%V\", "
+ "minimum size is %d", &value[1],
+ NGX_MAX_ERROR_STR);
+ return NGX_CONF_ERROR;
+ }
+
+ if (cycle->intercept_error_log_handler) {
+ return "capture error log handler has been hooked";
+ }
+
+ ringbuf = (ngx_stream_lua_log_ringbuf_t *)
+ ngx_palloc(cf->pool, sizeof(ngx_stream_lua_log_ringbuf_t));
+ if (ringbuf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ data = ngx_palloc(cf->pool, size);
+ if (data == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_stream_lua_log_ringbuf_init(ringbuf, data, size);
+
+ lmcf->requires_capture_log = 1;
+ cycle->intercept_error_log_handler = (ngx_log_intercept_pt)
+ ngx_stream_lua_capture_log_handler;
+ cycle->intercept_error_log_data = ringbuf;
+
+ return NGX_CONF_OK;
+#endif
+}
+
+
+/*
+ * ngx_stream_lua_strlstrn() is intended to search for static substring
+ * with known length in string until the argument last. The argument n
+ * must be length of the second substring - 1.
+ */
+
+static u_char *
+ngx_stream_lua_strlstrn(u_char *s1, u_char *last, u_char *s2, size_t n)
+{
+ ngx_uint_t c1, c2;
+
+ c2 = (ngx_uint_t) *s2++;
+ last -= n;
+
+ do {
+ do {
+ if (s1 >= last) {
+ return NULL;
+ }
+
+ c1 = (ngx_uint_t) *s1++;
+
+ dd("testing char '%c' vs '%c'", (int) c1, (int) c2);
+
+ } while (c1 != c2);
+
+ dd("testing against pattern \"%.*s\"", (int) n, s2);
+
+ } while (ngx_strncmp(s1, s2, n) != 0);
+
+ return --s1;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_undefined_var(ngx_stream_session_t *s,
+ ngx_stream_variable_value_t *v, uintptr_t data)
+{
+ v->not_found = 1;
+
+ return NGX_OK;
+}
+
+
+char *
+ngx_stream_lua_add_variable(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ ngx_stream_variable_t *var;
+ ngx_str_t *value;
+ ngx_int_t ret;
+
+ value = cf->args->elts;
+
+ if (value[1].data[0] != '$') {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid variable name \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ value[1].len--;
+ value[1].data++;
+
+ var = ngx_stream_add_variable(cf, value + 1, NGX_STREAM_VAR_CHANGEABLE
+ |NGX_STREAM_VAR_WEAK);
+ if (var == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (var->get_handler == NULL) {
+ var->get_handler = ngx_stream_lua_undefined_var;
+ }
+
+ ret = ngx_stream_get_variable_index(cf, value + 1);
+ if (ret == NGX_ERROR) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_directive.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_directive.h
new file mode 100644
index 0000000..6b36bb1
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_directive.h
@@ -0,0 +1,70 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_directive.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_DIRECTIVE_H_INCLUDED_
+#define _NGX_STREAM_LUA_DIRECTIVE_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+char *ngx_stream_lua_shared_dict(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+char *ngx_stream_lua_package_cpath(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+char *ngx_stream_lua_package_path(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+char *ngx_stream_lua_content_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+char *ngx_stream_lua_content_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+char *ngx_stream_lua_log_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+char *ngx_stream_lua_log_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+char *ngx_stream_lua_init_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+char *ngx_stream_lua_init_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+char *ngx_stream_lua_init_worker_by_lua_block(ngx_conf_t *cf,
+ ngx_command_t *cmd, void *conf);
+char *ngx_stream_lua_init_worker_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+char *ngx_stream_lua_code_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+char *ngx_stream_lua_load_resty_core(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+char *
+ngx_stream_lua_preread_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+char *
+ngx_stream_lua_preread_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+char *
+ngx_stream_lua_add_variable(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+char *ngx_stream_lua_conf_lua_block_parse(ngx_conf_t *cf,
+ ngx_command_t *cmd);
+
+
+char *ngx_stream_lua_capture_error_log(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+#endif /* _NGX_STREAM_LUA_DIRECTIVE_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_exception.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_exception.c
new file mode 100644
index 0000000..c1aaed3
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_exception.c
@@ -0,0 +1,66 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_exception.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_exception.h"
+#include "ngx_stream_lua_util.h"
+
+
+/* longjmp mark for restoring nginx execution after Lua VM crashing */
+jmp_buf ngx_stream_lua_exception;
+
+/**
+ * Override default Lua panic handler, output VM crash reason to nginx error
+ * log, and restore execution to the nearest jmp-mark.
+ *
+ * @param L Lua state pointer
+ * @retval Long jump to the nearest jmp-mark, never returns.
+ * @note nginx request pointer should be stored in Lua thread's globals table
+ * in order to make logging working.
+ * */
+int
+ngx_stream_lua_atpanic(lua_State *L)
+{
+#ifdef NGX_LUA_ABORT_AT_PANIC
+ abort();
+#else
+ u_char *s = NULL;
+ size_t len = 0;
+
+ if (lua_type(L, -1) == LUA_TSTRING) {
+ s = (u_char *) lua_tolstring(L, -1, &len);
+ }
+
+ if (s == NULL) {
+ s = (u_char *) "unknown reason";
+ len = sizeof("unknown reason") - 1;
+ }
+
+ ngx_log_stderr(0, "lua atpanic: Lua VM crashed, reason: %*s", len, s);
+ ngx_quit = 1;
+
+ /* restore nginx execution */
+ NGX_LUA_EXCEPTION_THROW(1);
+
+ /* impossible to reach here */
+#endif
+}
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_exception.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_exception.h
new file mode 100644
index 0000000..2e90863
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_exception.h
@@ -0,0 +1,41 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_exception.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_EXCEPTION_H_INCLUDED_
+#define _NGX_STREAM_LUA_EXCEPTION_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+#define NGX_LUA_EXCEPTION_TRY \
+ if (setjmp(ngx_stream_lua_exception) == 0)
+
+#define NGX_LUA_EXCEPTION_CATCH \
+ else
+
+#define NGX_LUA_EXCEPTION_THROW(x) \
+ longjmp(ngx_stream_lua_exception, (x))
+
+
+extern jmp_buf ngx_stream_lua_exception;
+
+
+int ngx_stream_lua_atpanic(lua_State *L);
+
+
+#endif /* _NGX_STREAM_LUA_EXCEPTION_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_initby.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_initby.c
new file mode 100644
index 0000000..c6bbf0d
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_initby.c
@@ -0,0 +1,50 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_initby.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+
+#include "ddebug.h"
+#include "ngx_stream_lua_initby.h"
+#include "ngx_stream_lua_util.h"
+
+
+ngx_int_t
+ngx_stream_lua_init_by_inline(ngx_log_t *log, ngx_stream_lua_main_conf_t *lmcf,
+ lua_State *L)
+{
+ int status;
+
+ status = luaL_loadbuffer(L, (char *) lmcf->init_src.data,
+ lmcf->init_src.len, "=init_by_lua")
+ || ngx_stream_lua_do_call(log, L);
+
+ return ngx_stream_lua_report(log, L, status, "init_by_lua");
+}
+
+
+ngx_int_t
+ngx_stream_lua_init_by_file(ngx_log_t *log, ngx_stream_lua_main_conf_t *lmcf,
+ lua_State *L)
+{
+ int status;
+
+ status = luaL_loadfile(L, (char *) lmcf->init_src.data)
+ || ngx_stream_lua_do_call(log, L);
+
+ return ngx_stream_lua_report(log, L, status, "init_by_lua_file");
+}
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_initby.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_initby.h
new file mode 100644
index 0000000..f2e2c40
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_initby.h
@@ -0,0 +1,31 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_initby.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_INITBY_H_INCLUDED_
+#define _NGX_STREAM_LUA_INITBY_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+ngx_int_t ngx_stream_lua_init_by_inline(ngx_log_t *log,
+ ngx_stream_lua_main_conf_t *lmcf, lua_State *L);
+
+ngx_int_t ngx_stream_lua_init_by_file(ngx_log_t *log,
+ ngx_stream_lua_main_conf_t *lmcf, lua_State *L);
+
+
+#endif /* _NGX_STREAM_LUA_INITBY_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_initworkerby.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_initworkerby.c
new file mode 100644
index 0000000..786ba34
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_initworkerby.c
@@ -0,0 +1,366 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_initworkerby.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_initworkerby.h"
+#include "ngx_stream_lua_util.h"
+
+#include "ngx_stream_lua_contentby.h"
+
+
+
+static u_char *ngx_stream_lua_log_init_worker_error(ngx_log_t *log,
+ u_char *buf, size_t len);
+
+
+ngx_int_t
+ngx_stream_lua_init_worker(ngx_cycle_t *cycle)
+{
+ char *rv;
+ void *cur, *prev;
+ ngx_uint_t i;
+ ngx_conf_t conf;
+ ngx_cycle_t *fake_cycle;
+ ngx_module_t **modules;
+ ngx_open_file_t *file, *ofile;
+ ngx_list_part_t *part;
+ ngx_connection_t *c = NULL;
+ ngx_stream_module_t *module;
+ ngx_stream_lua_request_t *r = NULL;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_conf_ctx_t *conf_ctx, stream_ctx;
+
+ ngx_stream_lua_main_conf_t *lmcf;
+
+ ngx_conf_file_t *conf_file;
+ ngx_stream_session_t *s;
+
+ ngx_stream_core_srv_conf_t *cscf, *top_cscf;
+ ngx_stream_lua_srv_conf_t *lscf, *top_lscf;
+
+ lmcf = ngx_stream_cycle_get_module_main_conf(cycle, ngx_stream_lua_module);
+
+ if (lmcf == NULL || lmcf->lua == NULL) {
+ return NGX_OK;
+ }
+
+ /* lmcf != NULL && lmcf->lua != NULL */
+
+#if !(NGX_WIN32)
+ if (ngx_process == NGX_PROCESS_HELPER
+# ifdef HAVE_PRIVILEGED_PROCESS_PATCH
+ && !ngx_is_privileged_agent
+# endif
+ )
+ {
+ /* disable init_worker_by_lua* and destroy lua VM in cache processes */
+
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "lua close the global Lua VM %p in the "
+ "cache helper process %P", lmcf->lua, ngx_pid);
+
+ lmcf->vm_cleanup->handler(lmcf->vm_cleanup->data);
+ lmcf->vm_cleanup->handler = NULL;
+
+ return NGX_OK;
+ }
+
+
+#endif /* NGX_WIN32 */
+
+#if (NGX_STREAM_LUA_HAVE_SA_RESTART)
+ if (lmcf->set_sa_restart) {
+ ngx_stream_lua_set_sa_restart(ngx_cycle->log);
+ }
+#endif
+
+ if (lmcf->init_worker_handler == NULL) {
+ return NGX_OK;
+ }
+
+ conf_ctx = (ngx_stream_conf_ctx_t *)
+ cycle->conf_ctx[ngx_stream_module.index];
+ stream_ctx.main_conf = conf_ctx->main_conf;
+
+ top_cscf = conf_ctx->srv_conf[ngx_stream_core_module.ctx_index];
+ top_lscf = conf_ctx->srv_conf[ngx_stream_lua_module.ctx_index];
+
+ ngx_memzero(&conf, sizeof(ngx_conf_t));
+
+ conf.temp_pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, cycle->log);
+ if (conf.temp_pool == NULL) {
+ return NGX_ERROR;
+ }
+
+ conf.temp_pool->log = cycle->log;
+
+ /* we fake a temporary ngx_cycle_t here because some
+ * modules' merge conf handler may produce side effects in
+ * cf->cycle (like ngx_proxy vs cf->cycle->paths).
+ * also, we cannot allocate our temp cycle on the stack
+ * because some modules like ngx_stream_core_module reference
+ * addresses within cf->cycle (i.e., via "&cf->cycle->new_log")
+ */
+
+ fake_cycle = ngx_palloc(cycle->pool, sizeof(ngx_cycle_t));
+ if (fake_cycle == NULL) {
+ goto failed;
+ }
+
+ ngx_memcpy(fake_cycle, cycle, sizeof(ngx_cycle_t));
+
+ ngx_queue_init(&fake_cycle->reusable_connections_queue);
+
+ if (ngx_array_init(&fake_cycle->listening, cycle->pool,
+ cycle->listening.nelts || 1,
+ sizeof(ngx_listening_t))
+ != NGX_OK)
+ {
+ goto failed;
+ }
+
+ if (ngx_array_init(&fake_cycle->paths, cycle->pool, cycle->paths.nelts || 1,
+ sizeof(ngx_path_t *))
+ != NGX_OK)
+ {
+ goto failed;
+ }
+
+ part = &cycle->open_files.part;
+ ofile = part->elts;
+
+ if (ngx_list_init(&fake_cycle->open_files, cycle->pool, part->nelts || 1,
+ sizeof(ngx_open_file_t))
+ != NGX_OK)
+ {
+ goto failed;
+ }
+
+ for (i = 0; /* void */ ; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+ part = part->next;
+ ofile = part->elts;
+ i = 0;
+ }
+
+ file = ngx_list_push(&fake_cycle->open_files);
+ if (file == NULL) {
+ goto failed;
+ }
+
+ ngx_memcpy(file, ofile, sizeof(ngx_open_file_t));
+ }
+
+ if (ngx_list_init(&fake_cycle->shared_memory, cycle->pool, 1,
+ sizeof(ngx_shm_zone_t))
+ != NGX_OK)
+ {
+ goto failed;
+ }
+
+ conf_file = ngx_pcalloc(fake_cycle->pool, sizeof(ngx_conf_file_t));
+ if (conf_file == NULL) {
+ return NGX_ERROR;
+ }
+
+ /* workaround to make ngx_stream_core_create_srv_conf not SEGFAULT */
+ conf_file->file.name.data = (u_char *) "dummy";
+ conf_file->file.name.len = sizeof("dummy") - 1;
+ conf_file->line = 1;
+ conf.conf_file = conf_file;
+
+ conf.ctx = &stream_ctx;
+ conf.cycle = fake_cycle;
+ conf.pool = fake_cycle->pool;
+ conf.log = cycle->log;
+
+
+ stream_ctx.srv_conf = ngx_pcalloc(conf.pool,
+ sizeof(void *) * ngx_stream_max_module);
+ if (stream_ctx.srv_conf == NULL) {
+ return NGX_ERROR;
+ }
+
+#if defined(nginx_version) && nginx_version >= 1009011
+ modules = cycle->modules;
+#else
+ modules = ngx_modules;
+#endif
+
+ for (i = 0; modules[i]; i++) {
+ if (modules[i]->type != NGX_STREAM_MODULE) {
+ continue;
+ }
+
+ module = modules[i]->ctx;
+
+ if (module->create_srv_conf) {
+ cur = module->create_srv_conf(&conf);
+ if (cur == NULL) {
+ return NGX_ERROR;
+ }
+
+ stream_ctx.srv_conf[modules[i]->ctx_index] = cur;
+
+ if (modules[i]->ctx_index == ngx_stream_core_module.ctx_index) {
+ cscf = cur;
+ /* just to silence the error in
+ * ngx_stream_core_merge_srv_conf */
+ cscf->handler = ngx_stream_lua_content_handler;
+ }
+
+ if (module->merge_srv_conf) {
+ if (modules[i] == &ngx_stream_lua_module) {
+ prev = top_lscf;
+
+ } else if (modules[i] == &ngx_stream_core_module) {
+ prev = top_cscf;
+
+ } else {
+ prev = module->create_srv_conf(&conf);
+ if (prev == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ rv = module->merge_srv_conf(&conf, prev, cur);
+ if (rv != NGX_CONF_OK) {
+ goto failed;
+ }
+ }
+ }
+
+ }
+
+ ngx_destroy_pool(conf.temp_pool);
+ conf.temp_pool = NULL;
+
+ c = ngx_stream_lua_create_fake_connection(NULL);
+ if (c == NULL) {
+ goto failed;
+ }
+
+ c->log->handler = ngx_stream_lua_log_init_worker_error;
+
+ s = ngx_stream_lua_create_fake_session(c);
+ if (s == NULL) {
+ goto failed;
+ }
+
+ s->main_conf = stream_ctx.main_conf;
+ s->srv_conf = stream_ctx.srv_conf;
+
+ cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module);
+
+ lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_lua_module);
+
+ if (top_lscf->log_socket_errors != NGX_CONF_UNSET) {
+ lscf->log_socket_errors = top_lscf->log_socket_errors;
+ }
+
+ if (top_cscf->resolver != NULL) {
+ cscf->resolver = top_cscf->resolver;
+ }
+
+ if (top_cscf->resolver_timeout != NGX_CONF_UNSET_MSEC) {
+ cscf->resolver_timeout = top_cscf->resolver_timeout;
+ }
+
+#if defined(nginx_version) && nginx_version >= 1009000
+ ngx_set_connection_log(s->connection, cscf->error_log);
+
+#else
+#endif
+
+ ctx = ngx_stream_lua_create_ctx(s);
+ if (ctx == NULL) {
+ goto failed;
+ }
+
+ r = ctx->request;
+
+ ctx->context = NGX_STREAM_LUA_CONTEXT_INIT_WORKER;
+ ctx->cur_co_ctx = NULL;
+ r->read_event_handler = ngx_stream_lua_block_reading;
+
+ ngx_stream_lua_set_req(lmcf->lua, r);
+
+ (void) lmcf->init_worker_handler(cycle->log, lmcf, lmcf->lua);
+
+ ngx_destroy_pool(c->pool);
+ return NGX_OK;
+
+failed:
+
+ if (conf.temp_pool) {
+ ngx_destroy_pool(conf.temp_pool);
+ }
+
+ if (c) {
+ ngx_stream_lua_close_fake_connection(c);
+ }
+
+ return NGX_ERROR;
+}
+
+
+ngx_int_t
+ngx_stream_lua_init_worker_by_inline(ngx_log_t *log,
+ ngx_stream_lua_main_conf_t *lmcf, lua_State *L)
+{
+ int status;
+
+ status = luaL_loadbuffer(L, (char *) lmcf->init_worker_src.data,
+ lmcf->init_worker_src.len, "=init_worker_by_lua")
+ || ngx_stream_lua_do_call(log, L);
+
+ return ngx_stream_lua_report(log, L, status, "init_worker_by_lua");
+}
+
+
+ngx_int_t
+ngx_stream_lua_init_worker_by_file(ngx_log_t *log,
+ ngx_stream_lua_main_conf_t *lmcf, lua_State *L)
+{
+ int status;
+
+ status = luaL_loadfile(L, (char *) lmcf->init_worker_src.data)
+ || ngx_stream_lua_do_call(log, L);
+
+ return ngx_stream_lua_report(log, L, status, "init_worker_by_lua_file");
+}
+
+
+static u_char *
+ngx_stream_lua_log_init_worker_error(ngx_log_t *log, u_char *buf, size_t len)
+{
+ u_char *p;
+
+ if (log->action) {
+ p = ngx_snprintf(buf, len, " while %s", log->action);
+ len -= p - buf;
+ buf = p;
+ }
+
+ return ngx_snprintf(buf, len, ", context: init_worker_by_lua*");
+}
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_initworkerby.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_initworkerby.h
new file mode 100644
index 0000000..43f6de3
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_initworkerby.h
@@ -0,0 +1,33 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_initworkerby.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_INITWORKERBY_H_INCLUDED_
+#define _NGX_STREAM_LUA_INITWORKERBY_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+ngx_int_t ngx_stream_lua_init_worker_by_inline(ngx_log_t *log,
+ ngx_stream_lua_main_conf_t *lmcf, lua_State *L);
+
+ngx_int_t ngx_stream_lua_init_worker_by_file(ngx_log_t *log,
+ ngx_stream_lua_main_conf_t *lmcf, lua_State *L);
+
+ngx_int_t ngx_stream_lua_init_worker(ngx_cycle_t *cycle);
+
+
+#endif /* _NGX_STREAM_LUA_INITWORKERBY_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_input_filters.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_input_filters.c
new file mode 100644
index 0000000..159185c
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_input_filters.c
@@ -0,0 +1,146 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_input_filters.c.tt2
+ */
+
+
+/*
+ * Copyright (C) by OpenResty Inc.
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_common.h"
+
+
+ngx_int_t
+ngx_stream_lua_read_bytes(ngx_buf_t *src, ngx_chain_t *buf_in,
+ size_t *rest, ssize_t bytes, ngx_log_t *log)
+{
+ if (bytes == 0) {
+ return NGX_ERROR;
+ }
+
+ if ((size_t) bytes >= *rest) {
+
+ buf_in->buf->last += *rest;
+ src->pos += *rest;
+ *rest = 0;
+
+ return NGX_OK;
+ }
+
+ /* bytes < *rest */
+
+ buf_in->buf->last += bytes;
+ src->pos += bytes;
+ *rest -= bytes;
+
+ return NGX_AGAIN;
+}
+
+
+ngx_int_t
+ngx_stream_lua_read_all(ngx_buf_t *src, ngx_chain_t *buf_in,
+ ssize_t bytes, ngx_log_t *log)
+{
+ if (bytes == 0) {
+ return NGX_OK;
+ }
+
+ buf_in->buf->last += bytes;
+ src->pos += bytes;
+
+ return NGX_AGAIN;
+}
+
+
+ngx_int_t
+ngx_stream_lua_read_any(ngx_buf_t *src, ngx_chain_t *buf_in, size_t *max,
+ ssize_t bytes, ngx_log_t *log)
+{
+ if (bytes == 0) {
+ return NGX_ERROR;
+ }
+
+ if (bytes >= (ssize_t) *max) {
+ bytes = (ssize_t) *max;
+ }
+
+ buf_in->buf->last += bytes;
+ src->pos += bytes;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_stream_lua_read_line(ngx_buf_t *src, ngx_chain_t *buf_in,
+ ssize_t bytes, ngx_log_t *log)
+{
+ u_char *dst;
+ u_char c;
+#if (NGX_DEBUG)
+ u_char *begin;
+#endif
+
+#if (NGX_DEBUG)
+ begin = src->pos;
+#endif
+
+ if (bytes == 0) {
+ return NGX_ERROR;
+ }
+
+ dd("already read: %p: %.*s", buf_in,
+ (int) (buf_in->buf->last - buf_in->buf->pos), buf_in->buf->pos);
+
+ dd("data read: %.*s", (int) bytes, src->pos);
+
+ dst = buf_in->buf->last;
+
+ while (bytes--) {
+
+ c = *src->pos++;
+
+ switch (c) {
+ case '\n':
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, log, 0,
+ "stream lua read the final line part: "
+ "\"%*s\"", src->pos - 1 - begin, begin);
+
+ buf_in->buf->last = dst;
+
+ dd("read a line: %p: %.*s", buf_in,
+ (int) (buf_in->buf->last - buf_in->buf->pos), buf_in->buf->pos);
+
+ return NGX_OK;
+
+ case '\r':
+ /* ignore it */
+ break;
+
+ default:
+ *dst++ = c;
+ break;
+ }
+ }
+
+#if (NGX_DEBUG)
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, log, 0,
+ "stream lua read partial line data: %*s",
+ dst - begin, begin);
+#endif
+
+ buf_in->buf->last = dst;
+
+ return NGX_AGAIN;
+}
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_input_filters.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_input_filters.h
new file mode 100644
index 0000000..e65d572
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_input_filters.h
@@ -0,0 +1,37 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_input_filters.h.tt2
+ */
+
+
+/*
+ * Copyright (C) by OpenResty Inc.
+ */
+
+
+#ifndef _NGX_STREAM_LUA_INPUT_FILTERS_H_INCLUDED_
+#define _NGX_STREAM_LUA_INPUT_FILTERS_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+ngx_int_t ngx_stream_lua_read_bytes(ngx_buf_t *src, ngx_chain_t *buf_in,
+ size_t *rest, ssize_t bytes, ngx_log_t *log);
+
+ngx_int_t ngx_stream_lua_read_all(ngx_buf_t *src, ngx_chain_t *buf_in,
+ ssize_t bytes, ngx_log_t *log);
+
+ngx_int_t ngx_stream_lua_read_any(ngx_buf_t *src, ngx_chain_t *buf_in,
+ size_t *max, ssize_t bytes, ngx_log_t *log);
+
+ngx_int_t ngx_stream_lua_read_line(ngx_buf_t *src, ngx_chain_t *buf_in,
+ ssize_t bytes, ngx_log_t *log);
+
+
+#endif /* _NGX_STREAM_LUA_INPUT_FILTERS_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_lex.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_lex.c
new file mode 100644
index 0000000..cb1fbc0
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_lex.c
@@ -0,0 +1,8259 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_lex.c.tt2
+ */
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ *
+ * WARNING: DO NOT EVER EDIT THIS FILE!!
+ *
+ * This file was automatically generated by the re.pl script of sregex's
+ * "dfa-multi-re" git branch.
+ */
+
+
+#include "ngx_stream_lua_lex.h"
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+
+
+#if __GNUC__ > 3
+# define likely(x) __builtin_expect((x),1)
+# define unlikely(x) __builtin_expect((x),0)
+#else
+# define likely(x) (x)
+# define unlikely(x) (x)
+#endif
+
+
+#ifndef u_char
+#define u_char unsigned char
+#endif
+
+
+enum {
+ NO_MATCH = -1,
+};
+
+
+/*
+ * ngx_stream_lua_lex: the "ovec" array should be allocated by the
+ * caller with at least 2 elements.
+ */
+int
+ngx_stream_lua_lex(const u_char *const s, size_t len, int *const ovec)
+{
+ unsigned i = 0;
+ int matched_0 = -1;
+ int matched_1 = -1;
+ int matched_id = NO_MATCH; /* (pending) matched regex ID */
+ int c;
+ int caps0_0 = -1;
+ int caps0_10 = -1;
+ int caps0_12 = -1;
+ int caps0_14 = -1;
+ int caps0_2 = -1;
+ int caps0_4 = -1;
+ int caps0_6 = -1;
+ int caps0_8 = -1;
+ int caps1_0 = -1;
+ int caps1_10 = -1;
+ int caps1_12 = -1;
+ int caps1_14 = -1;
+ int caps1_2 = -1;
+ int caps1_4 = -1;
+ int caps1_6 = -1;
+ int caps1_8 = -1;
+ int caps2_0 = -1;
+ int caps2_10 = -1;
+ int caps2_2 = -1;
+ int caps2_4 = -1;
+ int caps2_6 = -1;
+ int caps2_8 = -1;
+ int caps3_10 = -1;
+
+ { /* DFA node {0} 0 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st0_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 34: {
+ /* transfer caps from row 0 to row 1 */
+ /* capture stores */
+ caps0_12 = i - 1;
+ goto st2;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 0 to row 1 */
+ /* capture stores */
+ caps0_14 = i - 1;
+ goto st3;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 0 to row 1 */
+ /* transfer caps from row 0 to row 2 */
+ /* capture stores */
+ caps0_6 = i - 1;
+ caps1_10 = i - 1;
+ goto st4;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 0 to row 1 */
+ /* capture stores */
+ caps0_4 = i - 1;
+ goto st5;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 0 to row 1 */
+ /* capture stores */
+ caps0_8 = i - 1;
+ goto st6;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 0 to row 1 */
+ /* capture stores */
+ caps0_0 = i - 1;
+ goto st7;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 0 to row 1 */
+ /* capture stores */
+ caps0_2 = i - 1;
+ goto st8;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c == 92)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ goto st1;
+ } /* end state */
+
+ goto st0_error;
+
+st1: { /* DFA node {1} 1 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st1_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 34: {
+ /* transfer caps from row 0 to row 1 */
+ /* capture stores */
+ caps0_12 = i - 1;
+ goto st2;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 0 to row 1 */
+ /* capture stores */
+ caps0_14 = i - 1;
+ goto st3;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 0 to row 1 */
+ /* transfer caps from row 0 to row 2 */
+ /* capture stores */
+ caps0_6 = i - 1;
+ caps1_10 = i - 1;
+ goto st4;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 0 to row 1 */
+ /* capture stores */
+ caps0_4 = i - 1;
+ goto st5;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 0 to row 1 */
+ /* capture stores */
+ caps0_8 = i - 1;
+ goto st6;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 0 to row 1 */
+ /* capture stores */
+ caps0_0 = i - 1;
+ goto st7;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 0 to row 1 */
+ /* capture stores */
+ caps0_2 = i - 1;
+ goto st8;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c == 92)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ goto st1;
+ } /* end state */
+
+ goto st1_error;
+
+st2: { /* DFA node {59,1} 2 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st2_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 1 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st10;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st11;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 1 to row 2 */
+ /* transfer caps from row 1 to row 3 */
+ /* capture stores */
+ caps1_6 = i - 1;
+ caps2_10 = i - 1;
+ goto st12;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_4 = i - 1;
+ goto st13;
+ break;
+ }
+ case 92: {
+ goto st14;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_8 = i - 1;
+ goto st15;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st16;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st17;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ goto st9;
+ } /* end state */
+
+ goto st2_error;
+
+st3: { /* DFA node {72,1} 3 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st3_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 1 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st19;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st20;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 1 to row 2 */
+ /* transfer caps from row 1 to row 3 */
+ /* capture stores */
+ caps1_6 = i - 1;
+ caps2_10 = i - 1;
+ goto st21;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_4 = i - 1;
+ goto st22;
+ break;
+ }
+ case 92: {
+ goto st23;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_8 = i - 1;
+ goto st24;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st25;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st26;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ goto st18;
+ } /* end state */
+
+ goto st3_error;
+
+st4: { /* DFA node {30,50,1} 4 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st4_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 34: {
+ /* transfer caps from row 2 to row 0 */
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps0_12 = i - 1;
+ goto st2;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 0 */
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps0_14 = i - 1;
+ goto st3;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 3 */
+ /* transfer caps from row 2 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st27;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 0 */
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps0_4 = i - 1;
+ goto st5;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 0 */
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps0_8 = i - 1;
+ goto st6;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 0 */
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps0_0 = i - 1;
+ goto st7;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 0 */
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps0_2 = i - 1;
+ goto st8;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c == 92)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ } /* end state */
+
+ goto st4_error;
+
+st5: { /* DFA node {21,1} 5 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st5_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 34: {
+ /* transfer caps from row 1 to row 0 */
+ /* capture stores */
+ caps0_12 = i - 1;
+ goto st2;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 1 to row 0 */
+ /* capture stores */
+ caps0_14 = i - 1;
+ goto st3;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 1 to row 0 */
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps0_6 = i - 1;
+ caps1_10 = i - 1;
+ goto st4;
+ break;
+ }
+ case 61: {
+ goto st28;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_4 = i - 1;
+ goto st29;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 1 to row 0 */
+ /* capture stores */
+ caps0_8 = i - 1;
+ goto st6;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 1 to row 0 */
+ /* capture stores */
+ caps0_0 = i - 1;
+ goto st7;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 1 to row 0 */
+ /* capture stores */
+ caps0_2 = i - 1;
+ goto st8;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c == 92)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 1 to row 0 */
+ goto st1;
+ } /* end state */
+
+ goto st5_error;
+
+st6: { /* DFA node {41,1} 6 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st6_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 34: {
+ /* transfer caps from row 1 to row 0 */
+ /* capture stores */
+ caps0_12 = i - 1;
+ goto st2;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 1 to row 0 */
+ /* capture stores */
+ caps0_14 = i - 1;
+ goto st3;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 1 to row 0 */
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps0_6 = i - 1;
+ caps1_10 = i - 1;
+ goto st4;
+ break;
+ }
+ case 61: {
+ goto st30;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 1 to row 0 */
+ /* capture stores */
+ caps0_4 = i - 1;
+ goto st5;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_8 = i - 1;
+ goto st31;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 1 to row 0 */
+ /* capture stores */
+ caps0_0 = i - 1;
+ goto st7;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 1 to row 0 */
+ /* capture stores */
+ caps0_2 = i - 1;
+ goto st8;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c == 92)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 1 to row 0 */
+ goto st1;
+ } /* end state */
+
+ goto st6_error;
+
+st7: { /* DFA node {11,1} 7 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 0 to matched */
+ matched_0 = caps0_0;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 0;
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st8: { /* DFA node {16,1} 8 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 0 to matched */
+ matched_0 = caps0_2;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 1;
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st9: { /* DFA node {65,1} 9 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st9_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 1 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st10;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st11;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 1 to row 2 */
+ /* transfer caps from row 1 to row 3 */
+ /* capture stores */
+ caps1_6 = i - 1;
+ caps2_10 = i - 1;
+ goto st12;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_4 = i - 1;
+ goto st13;
+ break;
+ }
+ case 92: {
+ goto st14;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_8 = i - 1;
+ goto st15;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st16;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st17;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ goto st9;
+ } /* end state */
+
+ goto st9_error;
+
+st10: { /* DFA node {67,59,1} 10 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 0 to matched */
+ matched_0 = caps0_12;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 6;
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st11: { /* DFA node {65,72,1} 11 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st11_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ goto st36;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ goto st37;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 3 */
+ /* transfer caps from row 2 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st38;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st39;
+ break;
+ }
+ case 92: {
+ goto st40;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st41;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st42;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st43;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ goto st35;
+ } /* end state */
+
+ goto st11_error;
+
+st12: { /* DFA node {65,30,50,1} 12 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st12_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 3 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 3 to row 1 */
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st10;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 3 to row 1 */
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st11;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 3 to row 4 */
+ /* transfer caps from row 3 to row 5 */
+ /* capture stores */
+ goto st44;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 3 to row 1 */
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps1_4 = i - 1;
+ goto st13;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 3 to row 1 */
+ goto st14;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 3 to row 1 */
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps1_8 = i - 1;
+ goto st15;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 3 to row 1 */
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st16;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 3 to row 1 */
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st17;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 3 to row 1 */
+ goto st9;
+ } /* end state */
+
+ goto st12_error;
+
+st13: { /* DFA node {65,21,1} 13 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st13_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st10;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st11;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 1 */
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps1_6 = i - 1;
+ caps2_10 = i - 1;
+ goto st12;
+ break;
+ }
+ case 61: {
+ goto st45;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st46;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 2 to row 1 */
+ goto st14;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_8 = i - 1;
+ goto st15;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st16;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st17;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 2 to row 1 */
+ goto st9;
+ } /* end state */
+
+ goto st13_error;
+
+st14: { /* DFA node {62,1} 14 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st14_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 1 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st48;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st49;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 1 to row 2 */
+ /* transfer caps from row 1 to row 3 */
+ /* capture stores */
+ caps1_6 = i - 1;
+ caps2_10 = i - 1;
+ goto st50;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_4 = i - 1;
+ goto st51;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_8 = i - 1;
+ goto st52;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st53;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st54;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c == 92)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ goto st47;
+ } /* end state */
+
+ goto st14_error;
+
+st15: { /* DFA node {65,41,1} 15 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st15_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st10;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st11;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 1 */
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps1_6 = i - 1;
+ caps2_10 = i - 1;
+ goto st12;
+ break;
+ }
+ case 61: {
+ goto st55;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_4 = i - 1;
+ goto st13;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 2 to row 1 */
+ goto st14;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st56;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st16;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st17;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 2 to row 1 */
+ goto st9;
+ } /* end state */
+
+ goto st15_error;
+
+st16: { /* DFA node {65,11,1} 16 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_0;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 0;
+ if (c != -1) {
+ if (c == 34) {
+ goto st58;
+ }
+ if (c == 92) {
+ goto st59;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st57;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st17: { /* DFA node {65,16,1} 17 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_2;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 1;
+ if (c != -1) {
+ if (c == 34) {
+ goto st58;
+ }
+ if (c == 92) {
+ goto st59;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st57;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st18: { /* DFA node {78,1} 18 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st18_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 1 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st19;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st20;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 1 to row 2 */
+ /* transfer caps from row 1 to row 3 */
+ /* capture stores */
+ caps1_6 = i - 1;
+ caps2_10 = i - 1;
+ goto st21;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_4 = i - 1;
+ goto st22;
+ break;
+ }
+ case 92: {
+ goto st23;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_8 = i - 1;
+ goto st24;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st25;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st26;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ goto st18;
+ } /* end state */
+
+ goto st18_error;
+
+st19: { /* DFA node {78,59,1} 19 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st19_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ goto st61;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ goto st62;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 3 */
+ /* transfer caps from row 2 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st63;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st64;
+ break;
+ }
+ case 92: {
+ goto st65;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st66;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st67;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st68;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ goto st60;
+ } /* end state */
+
+ goto st19_error;
+
+st20: { /* DFA node {80,72,1} 20 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 0 to matched */
+ matched_0 = caps0_14;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 7;
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st21: { /* DFA node {78,30,50,1} 21 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st21_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 3 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 3 to row 1 */
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st19;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 3 to row 1 */
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st20;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 3 to row 4 */
+ /* transfer caps from row 3 to row 5 */
+ /* capture stores */
+ goto st70;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 3 to row 1 */
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps1_4 = i - 1;
+ goto st22;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 3 to row 1 */
+ goto st23;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 3 to row 1 */
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps1_8 = i - 1;
+ goto st24;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 3 to row 1 */
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st25;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 3 to row 1 */
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st26;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 3 to row 1 */
+ goto st18;
+ } /* end state */
+
+ goto st21_error;
+
+st22: { /* DFA node {78,21,1} 22 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st22_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st19;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st20;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 1 */
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps1_6 = i - 1;
+ caps2_10 = i - 1;
+ goto st21;
+ break;
+ }
+ case 61: {
+ goto st71;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st72;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 2 to row 1 */
+ goto st23;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_8 = i - 1;
+ goto st24;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st25;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st26;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 2 to row 1 */
+ goto st18;
+ } /* end state */
+
+ goto st22_error;
+
+st23: { /* DFA node {75,1} 23 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st23_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 1 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st74;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st75;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 1 to row 2 */
+ /* transfer caps from row 1 to row 3 */
+ /* capture stores */
+ caps1_6 = i - 1;
+ caps2_10 = i - 1;
+ goto st76;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_4 = i - 1;
+ goto st77;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_8 = i - 1;
+ goto st78;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st79;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st80;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c == 92)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ goto st73;
+ } /* end state */
+
+ goto st23_error;
+
+st24: { /* DFA node {78,41,1} 24 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st24_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st19;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st20;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 1 */
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps1_6 = i - 1;
+ caps2_10 = i - 1;
+ goto st21;
+ break;
+ }
+ case 61: {
+ goto st81;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_4 = i - 1;
+ goto st22;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 2 to row 1 */
+ goto st23;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st82;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st25;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st26;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 2 to row 1 */
+ goto st18;
+ } /* end state */
+
+ goto st24_error;
+
+st25: { /* DFA node {78,11,1} 25 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_0;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 0;
+ if (c != -1) {
+ if (c == 39) {
+ goto st84;
+ }
+ if (c == 92) {
+ goto st85;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st83;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st26: { /* DFA node {78,16,1} 26 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_2;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 1;
+ if (c != -1) {
+ if (c == 39) {
+ goto st84;
+ }
+ if (c == 92) {
+ goto st85;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st83;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st27: { /* DFA node {31,51,30,50,1} 27 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if (c == 91) {
+ goto st88;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 90)
+ || (c >= 92 && c <= 255))
+ {
+ /* transfer caps from row 1 to row 0 */
+ caps0_10 = caps1_10;
+ goto st87;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st28: { /* DFA node {23,1} 28 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st28_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 34: {
+ /* transfer caps from row 1 to row 0 */
+ /* capture stores */
+ caps0_12 = i - 1;
+ goto st2;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 1 to row 0 */
+ /* capture stores */
+ caps0_14 = i - 1;
+ goto st3;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 1 to row 0 */
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps0_6 = i - 1;
+ caps1_10 = i - 1;
+ goto st4;
+ break;
+ }
+ case 61: {
+ goto st28;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_4 = i - 1;
+ goto st29;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 1 to row 0 */
+ /* capture stores */
+ caps0_8 = i - 1;
+ goto st6;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 1 to row 0 */
+ /* capture stores */
+ caps0_0 = i - 1;
+ goto st7;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 1 to row 0 */
+ /* capture stores */
+ caps0_2 = i - 1;
+ goto st8;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c == 92)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 1 to row 0 */
+ goto st1;
+ } /* end state */
+
+ goto st28_error;
+
+st29: { /* DFA node {25,21,1} 29 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 0 to matched */
+ matched_0 = caps0_4;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 2;
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st30: { /* DFA node {43,1} 30 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st30_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 34: {
+ /* transfer caps from row 1 to row 0 */
+ /* capture stores */
+ caps0_12 = i - 1;
+ goto st2;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 1 to row 0 */
+ /* capture stores */
+ caps0_14 = i - 1;
+ goto st3;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 1 to row 0 */
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps0_6 = i - 1;
+ caps1_10 = i - 1;
+ goto st4;
+ break;
+ }
+ case 61: {
+ goto st30;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 1 to row 0 */
+ /* capture stores */
+ caps0_4 = i - 1;
+ goto st5;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_8 = i - 1;
+ goto st31;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 1 to row 0 */
+ /* capture stores */
+ caps0_0 = i - 1;
+ goto st7;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 1 to row 0 */
+ /* capture stores */
+ caps0_2 = i - 1;
+ goto st8;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c == 92)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 1 to row 0 */
+ goto st1;
+ } /* end state */
+
+ goto st30_error;
+
+st31: { /* DFA node {45,41,1} 31 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 0 to matched */
+ matched_0 = caps0_8;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 4;
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st35: { /* DFA node {65,78,1} 35 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st35_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ goto st36;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ goto st37;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 3 */
+ /* transfer caps from row 2 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st38;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st39;
+ break;
+ }
+ case 92: {
+ goto st40;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st41;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st42;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st43;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ goto st35;
+ } /* end state */
+
+ goto st35_error;
+
+st36: { /* DFA node {67,78,59,1} 36 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 0 to matched */
+ matched_0 = caps0_12;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 6;
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st37: { /* DFA node {65,80,72,1} 37 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_14;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 7;
+ if (c != -1) {
+ if (c == 34) {
+ goto st58;
+ }
+ if (c == 92) {
+ goto st59;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st57;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st38: { /* DFA node {65,78,30,50,1} 38 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st38_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 4 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 4 to row 2 */
+ /* transfer caps from row 4 to row 3 */
+ /* capture stores */
+ goto st36;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 4 to row 2 */
+ /* transfer caps from row 4 to row 3 */
+ /* capture stores */
+ goto st37;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 4 to row 5 */
+ /* transfer caps from row 4 to row 6 */
+ /* capture stores */
+ goto st91;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 4 to row 2 */
+ /* transfer caps from row 4 to row 3 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st39;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 4 to row 2 */
+ goto st40;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 4 to row 2 */
+ /* transfer caps from row 4 to row 3 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st41;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 4 to row 2 */
+ /* transfer caps from row 4 to row 3 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st42;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 4 to row 2 */
+ /* transfer caps from row 4 to row 3 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st43;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 4 to row 2 */
+ goto st35;
+ } /* end state */
+
+ goto st38_error;
+
+st39: { /* DFA node {65,78,21,1} 39 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st39_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 3 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st36;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st37;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 3 to row 2 */
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st38;
+ break;
+ }
+ case 61: {
+ goto st92;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ goto st93;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 3 to row 2 */
+ goto st40;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st41;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st42;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st43;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 3 to row 2 */
+ goto st35;
+ } /* end state */
+
+ goto st39_error;
+
+st40: { /* DFA node {62,75,1} 40 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st40_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ goto st95;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ goto st96;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 3 */
+ /* transfer caps from row 2 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st97;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st98;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st99;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st100;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st101;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c == 92)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ goto st94;
+ } /* end state */
+
+ goto st40_error;
+
+st41: { /* DFA node {65,78,41,1} 41 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st41_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 3 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st36;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st37;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 3 to row 2 */
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st38;
+ break;
+ }
+ case 61: {
+ goto st102;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st39;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 3 to row 2 */
+ goto st40;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ goto st103;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st42;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st43;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 3 to row 2 */
+ goto st35;
+ } /* end state */
+
+ goto st41_error;
+
+st42: { /* DFA node {65,78,11,1} 42 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_0;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 0;
+ if (c != -1) {
+ if (c == 34) {
+ goto st105;
+ }
+ if (c == 39) {
+ goto st106;
+ }
+ if (c == 92) {
+ goto st107;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st104;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st43: { /* DFA node {65,78,16,1} 43 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_2;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 1;
+ if (c != -1) {
+ if (c == 34) {
+ goto st105;
+ }
+ if (c == 39) {
+ goto st106;
+ }
+ if (c == 92) {
+ goto st107;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st104;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st44: { /* DFA node {65,31,51,30,50,1} 44 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if (c == 34) {
+ /* transfer caps from row 2 to row 1 */
+ caps1_10 = caps2_10;
+ goto st109;
+ }
+ if (c == 91) {
+ goto st110;
+ }
+ if (c == 92) {
+ /* transfer caps from row 2 to row 1 */
+ caps1_10 = caps2_10;
+ goto st111;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 90)
+ || (c >= 93 && c <= 255))
+ {
+ /* transfer caps from row 2 to row 1 */
+ caps1_10 = caps2_10;
+ goto st108;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st45: { /* DFA node {65,23,1} 45 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st45_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st10;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st11;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 1 */
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps1_6 = i - 1;
+ caps2_10 = i - 1;
+ goto st12;
+ break;
+ }
+ case 61: {
+ goto st45;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st46;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 2 to row 1 */
+ goto st14;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_8 = i - 1;
+ goto st15;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st16;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st17;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 2 to row 1 */
+ goto st9;
+ } /* end state */
+
+ goto st45_error;
+
+st46: { /* DFA node {65,25,21,1} 46 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_4;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 2;
+ if (c != -1) {
+ if (c == 34) {
+ goto st58;
+ }
+ if (c == 92) {
+ goto st59;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st57;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st47: { /* DFA node {63,1} 47 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st47_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 1 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st10;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st11;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 1 to row 2 */
+ /* transfer caps from row 1 to row 3 */
+ /* capture stores */
+ caps1_6 = i - 1;
+ caps2_10 = i - 1;
+ goto st12;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_4 = i - 1;
+ goto st13;
+ break;
+ }
+ case 92: {
+ goto st14;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_8 = i - 1;
+ goto st15;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st16;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st17;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ goto st9;
+ } /* end state */
+
+ goto st47_error;
+
+st48: { /* DFA node {63,59,1} 48 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st48_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st10;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st11;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 1 */
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps1_6 = i - 1;
+ caps2_10 = i - 1;
+ goto st12;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_4 = i - 1;
+ goto st13;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 2 to row 1 */
+ goto st14;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_8 = i - 1;
+ goto st15;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st16;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st17;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 2 to row 1 */
+ goto st9;
+ } /* end state */
+
+ goto st48_error;
+
+st49: { /* DFA node {63,72,1} 49 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st49_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ goto st36;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ goto st37;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 3 */
+ /* transfer caps from row 2 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st38;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st39;
+ break;
+ }
+ case 92: {
+ goto st40;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st41;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st42;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st43;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ goto st35;
+ } /* end state */
+
+ goto st49_error;
+
+st50: { /* DFA node {63,30,50,1} 50 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st50_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 3 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 3 to row 1 */
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st10;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 3 to row 1 */
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st11;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 3 to row 4 */
+ /* transfer caps from row 3 to row 5 */
+ /* capture stores */
+ goto st44;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 3 to row 1 */
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps1_4 = i - 1;
+ goto st13;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 3 to row 1 */
+ goto st14;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 3 to row 1 */
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps1_8 = i - 1;
+ goto st15;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 3 to row 1 */
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st16;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 3 to row 1 */
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st17;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 3 to row 1 */
+ goto st9;
+ } /* end state */
+
+ goto st50_error;
+
+st51: { /* DFA node {63,21,1} 51 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st51_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st10;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st11;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 1 */
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps1_6 = i - 1;
+ caps2_10 = i - 1;
+ goto st12;
+ break;
+ }
+ case 61: {
+ goto st45;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st46;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 2 to row 1 */
+ goto st14;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_8 = i - 1;
+ goto st15;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st16;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st17;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 2 to row 1 */
+ goto st9;
+ } /* end state */
+
+ goto st51_error;
+
+st52: { /* DFA node {63,41,1} 52 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st52_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st10;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st11;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 1 */
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps1_6 = i - 1;
+ caps2_10 = i - 1;
+ goto st12;
+ break;
+ }
+ case 61: {
+ goto st55;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_4 = i - 1;
+ goto st13;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 2 to row 1 */
+ goto st14;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st56;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st16;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st17;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 2 to row 1 */
+ goto st9;
+ } /* end state */
+
+ goto st52_error;
+
+st53: { /* DFA node {63,11,1} 53 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_0;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 0;
+ if (c != -1) {
+ if (c == 34) {
+ goto st58;
+ }
+ if (c == 92) {
+ goto st59;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st57;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st54: { /* DFA node {63,16,1} 54 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_2;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 1;
+ if (c != -1) {
+ if (c == 34) {
+ goto st58;
+ }
+ if (c == 92) {
+ goto st59;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st57;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st55: { /* DFA node {65,43,1} 55 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st55_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st10;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st11;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 1 */
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps1_6 = i - 1;
+ caps2_10 = i - 1;
+ goto st12;
+ break;
+ }
+ case 61: {
+ goto st55;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_4 = i - 1;
+ goto st13;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 2 to row 1 */
+ goto st14;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st56;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st16;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st17;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 2 to row 1 */
+ goto st9;
+ } /* end state */
+
+ goto st55_error;
+
+st56: { /* DFA node {65,45,41,1} 56 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_8;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 4;
+ if (c != -1) {
+ if (c == 34) {
+ goto st58;
+ }
+ if (c == 92) {
+ goto st59;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st57;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st57: { /* DFA node {65} 57 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st57_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 34: {
+ goto st58;
+ break;
+ }
+ case 92: {
+ goto st59;
+ break;
+ }
+ default:
+ break;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st57;
+ }
+ } /* end state */
+
+ goto st57_error;
+
+st58: { /* DFA node {67} 58 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 0 to matched */
+ matched_0 = caps0_12;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 6;
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st59: { /* DFA node {62} 59 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st59_error;
+ }
+
+ c = s[i];
+ i++;
+ if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) {
+ goto st112;
+ }
+ } /* end state */
+
+ goto st59_error;
+
+st60: { /* DFA node {78,65,1} 60 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st60_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ goto st61;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ goto st62;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 3 */
+ /* transfer caps from row 2 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st63;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st64;
+ break;
+ }
+ case 92: {
+ goto st65;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st66;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st67;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st68;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ goto st60;
+ } /* end state */
+
+ goto st60_error;
+
+st61: { /* DFA node {78,67,59,1} 61 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_12;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 6;
+ if (c != -1) {
+ if (c == 39) {
+ goto st84;
+ }
+ if (c == 92) {
+ goto st85;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st83;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st62: { /* DFA node {80,65,72,1} 62 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 0 to matched */
+ matched_0 = caps0_14;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 7;
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st63: { /* DFA node {78,65,30,50,1} 63 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st63_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 4 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 4 to row 2 */
+ /* transfer caps from row 4 to row 3 */
+ /* capture stores */
+ goto st61;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 4 to row 2 */
+ /* transfer caps from row 4 to row 3 */
+ /* capture stores */
+ goto st62;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 4 to row 5 */
+ /* transfer caps from row 4 to row 6 */
+ /* capture stores */
+ goto st113;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 4 to row 2 */
+ /* transfer caps from row 4 to row 3 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st64;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 4 to row 2 */
+ goto st65;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 4 to row 2 */
+ /* transfer caps from row 4 to row 3 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st66;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 4 to row 2 */
+ /* transfer caps from row 4 to row 3 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st67;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 4 to row 2 */
+ /* transfer caps from row 4 to row 3 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st68;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 4 to row 2 */
+ goto st60;
+ } /* end state */
+
+ goto st63_error;
+
+st64: { /* DFA node {78,65,21,1} 64 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st64_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 3 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st61;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st62;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 3 to row 2 */
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st63;
+ break;
+ }
+ case 61: {
+ goto st114;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ goto st115;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 3 to row 2 */
+ goto st65;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st66;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st67;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st68;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 3 to row 2 */
+ goto st60;
+ } /* end state */
+
+ goto st64_error;
+
+st65: { /* DFA node {75,62,1} 65 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st65_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ goto st117;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ goto st118;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 3 */
+ /* transfer caps from row 2 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st119;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st120;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st121;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st122;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st123;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c == 92)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ goto st116;
+ } /* end state */
+
+ goto st65_error;
+
+st66: { /* DFA node {78,65,41,1} 66 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st66_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 3 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st61;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st62;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 3 to row 2 */
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st63;
+ break;
+ }
+ case 61: {
+ goto st124;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st64;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 3 to row 2 */
+ goto st65;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ goto st125;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st67;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st68;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 3 to row 2 */
+ goto st60;
+ } /* end state */
+
+ goto st66_error;
+
+st67: { /* DFA node {78,65,11,1} 67 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_0;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 0;
+ if (c != -1) {
+ if (c == 34) {
+ goto st127;
+ }
+ if (c == 39) {
+ goto st128;
+ }
+ if (c == 92) {
+ goto st129;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st126;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st68: { /* DFA node {78,65,16,1} 68 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_2;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 1;
+ if (c != -1) {
+ if (c == 34) {
+ goto st127;
+ }
+ if (c == 39) {
+ goto st128;
+ }
+ if (c == 92) {
+ goto st129;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st126;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st70: { /* DFA node {78,31,51,30,50,1} 70 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if (c == 39) {
+ /* transfer caps from row 2 to row 1 */
+ caps1_10 = caps2_10;
+ goto st131;
+ }
+ if (c == 91) {
+ goto st132;
+ }
+ if (c == 92) {
+ /* transfer caps from row 2 to row 1 */
+ caps1_10 = caps2_10;
+ goto st133;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 38)
+ || (c >= 40 && c <= 90)
+ || (c >= 93 && c <= 255))
+ {
+ /* transfer caps from row 2 to row 1 */
+ caps1_10 = caps2_10;
+ goto st130;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st71: { /* DFA node {78,23,1} 71 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st71_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st19;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st20;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 1 */
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps1_6 = i - 1;
+ caps2_10 = i - 1;
+ goto st21;
+ break;
+ }
+ case 61: {
+ goto st71;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st72;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 2 to row 1 */
+ goto st23;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_8 = i - 1;
+ goto st24;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st25;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st26;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 2 to row 1 */
+ goto st18;
+ } /* end state */
+
+ goto st71_error;
+
+st72: { /* DFA node {78,25,21,1} 72 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_4;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 2;
+ if (c != -1) {
+ if (c == 39) {
+ goto st84;
+ }
+ if (c == 92) {
+ goto st85;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st83;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st73: { /* DFA node {76,1} 73 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st73_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 1 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st19;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st20;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 1 to row 2 */
+ /* transfer caps from row 1 to row 3 */
+ /* capture stores */
+ caps1_6 = i - 1;
+ caps2_10 = i - 1;
+ goto st21;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_4 = i - 1;
+ goto st22;
+ break;
+ }
+ case 92: {
+ goto st23;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_8 = i - 1;
+ goto st24;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st25;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 1 to row 2 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st26;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ goto st18;
+ } /* end state */
+
+ goto st73_error;
+
+st74: { /* DFA node {76,59,1} 74 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st74_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ goto st61;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ goto st62;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 3 */
+ /* transfer caps from row 2 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st63;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st64;
+ break;
+ }
+ case 92: {
+ goto st65;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st66;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st67;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st68;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ goto st60;
+ } /* end state */
+
+ goto st74_error;
+
+st75: { /* DFA node {76,72,1} 75 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st75_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st19;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st20;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 1 */
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps1_6 = i - 1;
+ caps2_10 = i - 1;
+ goto st21;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_4 = i - 1;
+ goto st22;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 2 to row 1 */
+ goto st23;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_8 = i - 1;
+ goto st24;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st25;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st26;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 2 to row 1 */
+ goto st18;
+ } /* end state */
+
+ goto st75_error;
+
+st76: { /* DFA node {76,30,50,1} 76 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st76_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 3 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 3 to row 1 */
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st19;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 3 to row 1 */
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st20;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 3 to row 4 */
+ /* transfer caps from row 3 to row 5 */
+ /* capture stores */
+ goto st70;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 3 to row 1 */
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps1_4 = i - 1;
+ goto st22;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 3 to row 1 */
+ goto st23;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 3 to row 1 */
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps1_8 = i - 1;
+ goto st24;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 3 to row 1 */
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st25;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 3 to row 1 */
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st26;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 3 to row 1 */
+ goto st18;
+ } /* end state */
+
+ goto st76_error;
+
+st77: { /* DFA node {76,21,1} 77 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st77_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st19;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st20;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 1 */
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps1_6 = i - 1;
+ caps2_10 = i - 1;
+ goto st21;
+ break;
+ }
+ case 61: {
+ goto st71;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st72;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 2 to row 1 */
+ goto st23;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_8 = i - 1;
+ goto st24;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st25;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st26;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 2 to row 1 */
+ goto st18;
+ } /* end state */
+
+ goto st77_error;
+
+st78: { /* DFA node {76,41,1} 78 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st78_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st19;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st20;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 1 */
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps1_6 = i - 1;
+ caps2_10 = i - 1;
+ goto st21;
+ break;
+ }
+ case 61: {
+ goto st81;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_4 = i - 1;
+ goto st22;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 2 to row 1 */
+ goto st23;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st82;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st25;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st26;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 2 to row 1 */
+ goto st18;
+ } /* end state */
+
+ goto st78_error;
+
+st79: { /* DFA node {76,11,1} 79 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_0;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 0;
+ if (c != -1) {
+ if (c == 39) {
+ goto st84;
+ }
+ if (c == 92) {
+ goto st85;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st83;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st80: { /* DFA node {76,16,1} 80 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_2;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 1;
+ if (c != -1) {
+ if (c == 39) {
+ goto st84;
+ }
+ if (c == 92) {
+ goto st85;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st83;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st81: { /* DFA node {78,43,1} 81 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st81_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_12 = i - 1;
+ goto st19;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_14 = i - 1;
+ goto st20;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 1 */
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps1_6 = i - 1;
+ caps2_10 = i - 1;
+ goto st21;
+ break;
+ }
+ case 61: {
+ goto st81;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_4 = i - 1;
+ goto st22;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 2 to row 1 */
+ goto st23;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st82;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_0 = i - 1;
+ goto st25;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 1 */
+ /* capture stores */
+ caps1_2 = i - 1;
+ goto st26;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 2 to row 1 */
+ goto st18;
+ } /* end state */
+
+ goto st81_error;
+
+st82: { /* DFA node {78,45,41,1} 82 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_8;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 4;
+ if (c != -1) {
+ if (c == 39) {
+ goto st84;
+ }
+ if (c == 92) {
+ goto st85;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st83;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st83: { /* DFA node {78} 83 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st83_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 39: {
+ goto st84;
+ break;
+ }
+ case 92: {
+ goto st85;
+ break;
+ }
+ default:
+ break;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st83;
+ }
+ } /* end state */
+
+ goto st83_error;
+
+st84: { /* DFA node {80} 84 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 0 to matched */
+ matched_0 = caps0_14;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 7;
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st85: { /* DFA node {75} 85 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st85_error;
+ }
+
+ c = s[i];
+ i++;
+ if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) {
+ goto st134;
+ }
+ } /* end state */
+
+ goto st85_error;
+
+st87: { /* DFA node {53} 87 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 0 to matched */
+ matched_0 = caps0_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) {
+ goto st87;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st88: { /* DFA node {32,53} 88 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if (c == 61) {
+ goto st135;
+ }
+ if (c == 91) {
+ goto st136;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 60)
+ || (c >= 62 && c <= 90)
+ || (c >= 92 && c <= 255))
+ {
+ /* transfer caps from row 1 to row 0 */
+ caps0_10 = caps1_10;
+ goto st87;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st91: { /* DFA node {65,78,31,51,30,50,1} 91 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 3 to matched */
+ matched_0 = caps3_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if (c == 34) {
+ /* transfer caps from row 3 to row 2 */
+ caps2_10 = caps3_10;
+ goto st138;
+ }
+ if (c == 39) {
+ /* transfer caps from row 3 to row 2 */
+ caps2_10 = caps3_10;
+ goto st139;
+ }
+ if (c == 91) {
+ goto st140;
+ }
+ if (c == 92) {
+ /* transfer caps from row 3 to row 2 */
+ caps2_10 = caps3_10;
+ goto st141;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 90)
+ || (c >= 93 && c <= 255))
+ {
+ /* transfer caps from row 3 to row 2 */
+ caps2_10 = caps3_10;
+ goto st137;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st92: { /* DFA node {65,78,23,1} 92 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st92_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 3 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st36;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st37;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 3 to row 2 */
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st38;
+ break;
+ }
+ case 61: {
+ goto st92;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ goto st93;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 3 to row 2 */
+ goto st40;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st41;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st42;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st43;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 3 to row 2 */
+ goto st35;
+ } /* end state */
+
+ goto st92_error;
+
+st93: { /* DFA node {65,78,25,21,1} 93 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_4;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 2;
+ if (c != -1) {
+ if (c == 34) {
+ goto st105;
+ }
+ if (c == 39) {
+ goto st106;
+ }
+ if (c == 92) {
+ goto st107;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st104;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st94: { /* DFA node {63,76,1} 94 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st94_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ goto st36;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ goto st37;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 3 */
+ /* transfer caps from row 2 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st38;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st39;
+ break;
+ }
+ case 92: {
+ goto st40;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st41;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st42;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st43;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ goto st35;
+ } /* end state */
+
+ goto st94_error;
+
+st95: { /* DFA node {63,76,59,1} 95 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st95_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 3 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st36;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st37;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 3 to row 2 */
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st38;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st39;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 3 to row 2 */
+ goto st40;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st41;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st42;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st43;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 3 to row 2 */
+ goto st35;
+ } /* end state */
+
+ goto st95_error;
+
+st96: { /* DFA node {63,76,72,1} 96 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st96_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 3 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st36;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st37;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 3 to row 2 */
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st38;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st39;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 3 to row 2 */
+ goto st40;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st41;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st42;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st43;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 3 to row 2 */
+ goto st35;
+ } /* end state */
+
+ goto st96_error;
+
+st97: { /* DFA node {63,76,30,50,1} 97 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st97_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 4 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 4 to row 2 */
+ /* transfer caps from row 4 to row 3 */
+ /* capture stores */
+ goto st36;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 4 to row 2 */
+ /* transfer caps from row 4 to row 3 */
+ /* capture stores */
+ goto st37;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 4 to row 5 */
+ /* transfer caps from row 4 to row 6 */
+ /* capture stores */
+ goto st91;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 4 to row 2 */
+ /* transfer caps from row 4 to row 3 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st39;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 4 to row 2 */
+ goto st40;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 4 to row 2 */
+ /* transfer caps from row 4 to row 3 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st41;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 4 to row 2 */
+ /* transfer caps from row 4 to row 3 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st42;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 4 to row 2 */
+ /* transfer caps from row 4 to row 3 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st43;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 4 to row 2 */
+ goto st35;
+ } /* end state */
+
+ goto st97_error;
+
+st98: { /* DFA node {63,76,21,1} 98 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st98_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 3 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st36;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st37;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 3 to row 2 */
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st38;
+ break;
+ }
+ case 61: {
+ goto st92;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ goto st93;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 3 to row 2 */
+ goto st40;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st41;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st42;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st43;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 3 to row 2 */
+ goto st35;
+ } /* end state */
+
+ goto st98_error;
+
+st99: { /* DFA node {63,76,41,1} 99 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st99_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 3 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st36;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st37;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 3 to row 2 */
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st38;
+ break;
+ }
+ case 61: {
+ goto st102;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st39;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 3 to row 2 */
+ goto st40;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ goto st103;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st42;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st43;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 3 to row 2 */
+ goto st35;
+ } /* end state */
+
+ goto st99_error;
+
+st100: { /* DFA node {63,76,11,1} 100 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_0;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 0;
+ if (c != -1) {
+ if (c == 34) {
+ goto st105;
+ }
+ if (c == 39) {
+ goto st106;
+ }
+ if (c == 92) {
+ goto st107;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st104;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st101: { /* DFA node {63,76,16,1} 101 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_2;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 1;
+ if (c != -1) {
+ if (c == 34) {
+ goto st105;
+ }
+ if (c == 39) {
+ goto st106;
+ }
+ if (c == 92) {
+ goto st107;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st104;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st102: { /* DFA node {65,78,43,1} 102 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st102_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 3 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st36;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st37;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 3 to row 2 */
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st38;
+ break;
+ }
+ case 61: {
+ goto st102;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st39;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 3 to row 2 */
+ goto st40;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ goto st103;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st42;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st43;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 3 to row 2 */
+ goto st35;
+ } /* end state */
+
+ goto st102_error;
+
+st103: { /* DFA node {65,78,45,41,1} 103 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_8;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 4;
+ if (c != -1) {
+ if (c == 34) {
+ goto st105;
+ }
+ if (c == 39) {
+ goto st106;
+ }
+ if (c == 92) {
+ goto st107;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st104;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st104: { /* DFA node {65,78} 104 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st104_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 34: {
+ goto st105;
+ break;
+ }
+ case 39: {
+ goto st106;
+ break;
+ }
+ case 92: {
+ goto st107;
+ break;
+ }
+ default:
+ break;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st104;
+ }
+ } /* end state */
+
+ goto st104_error;
+
+st105: { /* DFA node {67,78} 105 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 0 to matched */
+ matched_0 = caps0_12;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 6;
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st106: { /* DFA node {65,80} 106 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_14;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 7;
+ if (c != -1) {
+ if (c == 34) {
+ goto st58;
+ }
+ if (c == 92) {
+ goto st59;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st57;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st107: { /* DFA node {62,75} 107 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st107_error;
+ }
+
+ c = s[i];
+ i++;
+ if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) {
+ goto st142;
+ }
+ } /* end state */
+
+ goto st107_error;
+
+st108: { /* DFA node {65,53} 108 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if (c == 34) {
+ goto st109;
+ }
+ if (c == 92) {
+ goto st111;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st108;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st109: { /* DFA node {67,53} 109 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 0 to matched */
+ matched_0 = caps0_12;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 6;
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st110: { /* DFA node {65,32,53} 110 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if (c == 34) {
+ /* transfer caps from row 2 to row 1 */
+ caps1_10 = caps2_10;
+ goto st109;
+ }
+ if (c == 61) {
+ goto st143;
+ }
+ if (c == 91) {
+ goto st144;
+ }
+ if (c == 92) {
+ /* transfer caps from row 2 to row 1 */
+ caps1_10 = caps2_10;
+ goto st111;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 60)
+ || (c >= 62 && c <= 90)
+ || (c >= 93 && c <= 255))
+ {
+ /* transfer caps from row 2 to row 1 */
+ caps1_10 = caps2_10;
+ goto st108;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st111: { /* DFA node {62,53} 111 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) {
+ goto st145;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st112: { /* DFA node {63} 112 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st112_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 34: {
+ goto st58;
+ break;
+ }
+ case 92: {
+ goto st59;
+ break;
+ }
+ default:
+ break;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st57;
+ }
+ } /* end state */
+
+ goto st112_error;
+
+st113: { /* DFA node {78,65,31,51,30,50,1} 113 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 3 to matched */
+ matched_0 = caps3_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if (c == 34) {
+ /* transfer caps from row 3 to row 2 */
+ caps2_10 = caps3_10;
+ goto st147;
+ }
+ if (c == 39) {
+ /* transfer caps from row 3 to row 2 */
+ caps2_10 = caps3_10;
+ goto st148;
+ }
+ if (c == 91) {
+ goto st149;
+ }
+ if (c == 92) {
+ /* transfer caps from row 3 to row 2 */
+ caps2_10 = caps3_10;
+ goto st150;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 90)
+ || (c >= 93 && c <= 255))
+ {
+ /* transfer caps from row 3 to row 2 */
+ caps2_10 = caps3_10;
+ goto st146;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st114: { /* DFA node {78,65,23,1} 114 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st114_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 3 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st61;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st62;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 3 to row 2 */
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st63;
+ break;
+ }
+ case 61: {
+ goto st114;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ goto st115;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 3 to row 2 */
+ goto st65;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st66;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st67;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st68;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 3 to row 2 */
+ goto st60;
+ } /* end state */
+
+ goto st114_error;
+
+st115: { /* DFA node {78,65,25,21,1} 115 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_4;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 2;
+ if (c != -1) {
+ if (c == 34) {
+ goto st127;
+ }
+ if (c == 39) {
+ goto st128;
+ }
+ if (c == 92) {
+ goto st129;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st126;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st116: { /* DFA node {76,63,1} 116 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st116_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 2 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ goto st61;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ goto st62;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 2 to row 3 */
+ /* transfer caps from row 2 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st63;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st64;
+ break;
+ }
+ case 92: {
+ goto st65;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st66;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st67;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 2 to row 3 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st68;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ goto st60;
+ } /* end state */
+
+ goto st116_error;
+
+st117: { /* DFA node {76,63,59,1} 117 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st117_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 3 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st61;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st62;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 3 to row 2 */
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st63;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st64;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 3 to row 2 */
+ goto st65;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st66;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st67;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st68;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 3 to row 2 */
+ goto st60;
+ } /* end state */
+
+ goto st117_error;
+
+st118: { /* DFA node {76,63,72,1} 118 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st118_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 3 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st61;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st62;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 3 to row 2 */
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st63;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st64;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 3 to row 2 */
+ goto st65;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st66;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st67;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st68;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 3 to row 2 */
+ goto st60;
+ } /* end state */
+
+ goto st118_error;
+
+st119: { /* DFA node {76,63,30,50,1} 119 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st119_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 4 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 4 to row 2 */
+ /* transfer caps from row 4 to row 3 */
+ /* capture stores */
+ goto st61;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 4 to row 2 */
+ /* transfer caps from row 4 to row 3 */
+ /* capture stores */
+ goto st62;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 4 to row 5 */
+ /* transfer caps from row 4 to row 6 */
+ /* capture stores */
+ goto st113;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 4 to row 2 */
+ /* transfer caps from row 4 to row 3 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st64;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 4 to row 2 */
+ goto st65;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 4 to row 2 */
+ /* transfer caps from row 4 to row 3 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st66;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 4 to row 2 */
+ /* transfer caps from row 4 to row 3 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st67;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 4 to row 2 */
+ /* transfer caps from row 4 to row 3 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st68;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 4 to row 2 */
+ goto st60;
+ } /* end state */
+
+ goto st119_error;
+
+st120: { /* DFA node {76,63,21,1} 120 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st120_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 3 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st61;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st62;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 3 to row 2 */
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st63;
+ break;
+ }
+ case 61: {
+ goto st114;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ goto st115;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 3 to row 2 */
+ goto st65;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_8 = i - 1;
+ goto st66;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st67;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st68;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 3 to row 2 */
+ goto st60;
+ } /* end state */
+
+ goto st120_error;
+
+st121: { /* DFA node {76,63,41,1} 121 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st121_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 3 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st61;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st62;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 3 to row 2 */
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st63;
+ break;
+ }
+ case 61: {
+ goto st124;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st64;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 3 to row 2 */
+ goto st65;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ goto st125;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st67;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st68;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 3 to row 2 */
+ goto st60;
+ } /* end state */
+
+ goto st121_error;
+
+st122: { /* DFA node {76,63,11,1} 122 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_0;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 0;
+ if (c != -1) {
+ if (c == 34) {
+ goto st127;
+ }
+ if (c == 39) {
+ goto st128;
+ }
+ if (c == 92) {
+ goto st129;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st126;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st123: { /* DFA node {76,63,16,1} 123 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_2;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 1;
+ if (c != -1) {
+ if (c == 34) {
+ goto st127;
+ }
+ if (c == 39) {
+ goto st128;
+ }
+ if (c == 92) {
+ goto st129;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st126;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st124: { /* DFA node {78,65,43,1} 124 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st124_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 10: {
+ /* transfer caps from row 3 to row 0 */
+ goto st1;
+ break;
+ }
+ case 34: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st61;
+ break;
+ }
+ case 39: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ goto st62;
+ break;
+ }
+ case 45: {
+ /* transfer caps from row 3 to row 2 */
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ caps2_6 = i - 1;
+ caps3_10 = i - 1;
+ goto st63;
+ break;
+ }
+ case 61: {
+ goto st124;
+ break;
+ }
+ case 91: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_4 = i - 1;
+ goto st64;
+ break;
+ }
+ case 92: {
+ /* transfer caps from row 3 to row 2 */
+ goto st65;
+ break;
+ }
+ case 93: {
+ /* transfer caps from row 3 to row 4 */
+ /* capture stores */
+ goto st125;
+ break;
+ }
+ case 123: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_0 = i - 1;
+ goto st67;
+ break;
+ }
+ case 125: {
+ /* transfer caps from row 3 to row 2 */
+ /* capture stores */
+ caps2_2 = i - 1;
+ goto st68;
+ break;
+ }
+ default:
+ break;
+ }
+ /* (c >= 0 && c <= 9)
+ * || (c >= 11 && c <= 33)
+ * || (c >= 35 && c <= 38)
+ * || (c >= 40 && c <= 44)
+ * || (c >= 46 && c <= 60)
+ * || (c >= 62 && c <= 90)
+ * || (c >= 94 && c <= 122)
+ * || (c == 124)
+ * || (c >= 126 && c <= 255)
+ */
+ /* transfer caps from row 3 to row 2 */
+ goto st60;
+ } /* end state */
+
+ goto st124_error;
+
+st125: { /* DFA node {78,65,45,41,1} 125 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_8;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 4;
+ if (c != -1) {
+ if (c == 34) {
+ goto st127;
+ }
+ if (c == 39) {
+ goto st128;
+ }
+ if (c == 92) {
+ goto st129;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st126;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st126: { /* DFA node {78,65} 126 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st126_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 34: {
+ goto st127;
+ break;
+ }
+ case 39: {
+ goto st128;
+ break;
+ }
+ case 92: {
+ goto st129;
+ break;
+ }
+ default:
+ break;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st126;
+ }
+ } /* end state */
+
+ goto st126_error;
+
+st127: { /* DFA node {78,67} 127 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_12;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 6;
+ if (c != -1) {
+ if (c == 39) {
+ goto st84;
+ }
+ if (c == 92) {
+ goto st85;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st83;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st128: { /* DFA node {80,65} 128 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 0 to matched */
+ matched_0 = caps0_14;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 7;
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st129: { /* DFA node {75,62} 129 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st129_error;
+ }
+
+ c = s[i];
+ i++;
+ if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) {
+ goto st151;
+ }
+ } /* end state */
+
+ goto st129_error;
+
+st130: { /* DFA node {78,53} 130 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if (c == 39) {
+ goto st131;
+ }
+ if (c == 92) {
+ goto st133;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st130;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st131: { /* DFA node {80,53} 131 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 0 to matched */
+ matched_0 = caps0_14;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 7;
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st132: { /* DFA node {78,32,53} 132 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if (c == 39) {
+ /* transfer caps from row 2 to row 1 */
+ caps1_10 = caps2_10;
+ goto st131;
+ }
+ if (c == 61) {
+ goto st152;
+ }
+ if (c == 91) {
+ goto st153;
+ }
+ if (c == 92) {
+ /* transfer caps from row 2 to row 1 */
+ caps1_10 = caps2_10;
+ goto st133;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 38)
+ || (c >= 40 && c <= 60)
+ || (c >= 62 && c <= 90)
+ || (c >= 93 && c <= 255))
+ {
+ /* transfer caps from row 2 to row 1 */
+ caps1_10 = caps2_10;
+ goto st130;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st133: { /* DFA node {75,53} 133 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) {
+ goto st154;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st134: { /* DFA node {76} 134 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st134_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 39: {
+ goto st84;
+ break;
+ }
+ case 92: {
+ goto st85;
+ break;
+ }
+ default:
+ break;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st83;
+ }
+ } /* end state */
+
+ goto st134_error;
+
+st135: { /* DFA node {34,53} 135 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if (c == 61) {
+ goto st135;
+ }
+ if (c == 91) {
+ goto st136;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 60)
+ || (c >= 62 && c <= 90)
+ || (c >= 92 && c <= 255))
+ {
+ /* transfer caps from row 1 to row 0 */
+ caps0_10 = caps1_10;
+ goto st87;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st136: { /* DFA node {36,53} 136 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 0 to matched */
+ matched_0 = caps0_6;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 3;
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st137: { /* DFA node {65,78,53} 137 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if (c == 34) {
+ goto st138;
+ }
+ if (c == 39) {
+ goto st139;
+ }
+ if (c == 92) {
+ goto st141;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st137;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st138: { /* DFA node {67,78,53} 138 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 0 to matched */
+ matched_0 = caps0_12;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 6;
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st139: { /* DFA node {65,80,53} 139 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_14;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 7;
+ if (c != -1) {
+ if (c == 34) {
+ goto st58;
+ }
+ if (c == 92) {
+ goto st59;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st57;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st140: { /* DFA node {65,78,32,53} 140 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 3 to matched */
+ matched_0 = caps3_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if (c == 34) {
+ /* transfer caps from row 3 to row 2 */
+ caps2_10 = caps3_10;
+ goto st138;
+ }
+ if (c == 39) {
+ /* transfer caps from row 3 to row 2 */
+ caps2_10 = caps3_10;
+ goto st139;
+ }
+ if (c == 61) {
+ goto st156;
+ }
+ if (c == 91) {
+ goto st157;
+ }
+ if (c == 92) {
+ /* transfer caps from row 3 to row 2 */
+ caps2_10 = caps3_10;
+ goto st141;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 60)
+ || (c >= 62 && c <= 90)
+ || (c >= 93 && c <= 255))
+ {
+ /* transfer caps from row 3 to row 2 */
+ caps2_10 = caps3_10;
+ goto st137;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st141: { /* DFA node {62,75,53} 141 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) {
+ goto st158;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st142: { /* DFA node {63,76} 142 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st142_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 34: {
+ goto st105;
+ break;
+ }
+ case 39: {
+ goto st106;
+ break;
+ }
+ case 92: {
+ goto st107;
+ break;
+ }
+ default:
+ break;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st104;
+ }
+ } /* end state */
+
+ goto st142_error;
+
+st143: { /* DFA node {65,34,53} 143 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if (c == 34) {
+ /* transfer caps from row 2 to row 1 */
+ caps1_10 = caps2_10;
+ goto st109;
+ }
+ if (c == 61) {
+ goto st143;
+ }
+ if (c == 91) {
+ goto st144;
+ }
+ if (c == 92) {
+ /* transfer caps from row 2 to row 1 */
+ caps1_10 = caps2_10;
+ goto st111;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 60)
+ || (c >= 62 && c <= 90)
+ || (c >= 93 && c <= 255))
+ {
+ /* transfer caps from row 2 to row 1 */
+ caps1_10 = caps2_10;
+ goto st108;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st144: { /* DFA node {65,36,53} 144 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_6;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 3;
+ if (c != -1) {
+ if (c == 34) {
+ goto st58;
+ }
+ if (c == 92) {
+ goto st59;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st57;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st145: { /* DFA node {63,53} 145 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if (c == 34) {
+ goto st109;
+ }
+ if (c == 92) {
+ goto st111;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st108;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st146: { /* DFA node {78,65,53} 146 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if (c == 34) {
+ goto st147;
+ }
+ if (c == 39) {
+ goto st148;
+ }
+ if (c == 92) {
+ goto st150;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st146;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st147: { /* DFA node {78,67,53} 147 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_12;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 6;
+ if (c != -1) {
+ if (c == 39) {
+ goto st84;
+ }
+ if (c == 92) {
+ goto st85;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st83;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st148: { /* DFA node {80,65,53} 148 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 0 to matched */
+ matched_0 = caps0_14;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 7;
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st149: { /* DFA node {78,65,32,53} 149 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 3 to matched */
+ matched_0 = caps3_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if (c == 34) {
+ /* transfer caps from row 3 to row 2 */
+ caps2_10 = caps3_10;
+ goto st147;
+ }
+ if (c == 39) {
+ /* transfer caps from row 3 to row 2 */
+ caps2_10 = caps3_10;
+ goto st148;
+ }
+ if (c == 61) {
+ goto st159;
+ }
+ if (c == 91) {
+ goto st160;
+ }
+ if (c == 92) {
+ /* transfer caps from row 3 to row 2 */
+ caps2_10 = caps3_10;
+ goto st150;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 60)
+ || (c >= 62 && c <= 90)
+ || (c >= 93 && c <= 255))
+ {
+ /* transfer caps from row 3 to row 2 */
+ caps2_10 = caps3_10;
+ goto st146;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st150: { /* DFA node {75,62,53} 150 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if ((c >= 0 && c <= 9) || (c >= 11 && c <= 255)) {
+ goto st161;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st151: { /* DFA node {76,63} 151 */
+ if (unlikely(i >= len)) {
+ i++;
+ goto st151_error;
+ }
+
+ c = s[i];
+ i++;
+ switch (c) {
+ case 34: {
+ goto st127;
+ break;
+ }
+ case 39: {
+ goto st128;
+ break;
+ }
+ case 92: {
+ goto st129;
+ break;
+ }
+ default:
+ break;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st126;
+ }
+ } /* end state */
+
+ goto st151_error;
+
+st152: { /* DFA node {78,34,53} 152 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if (c == 39) {
+ /* transfer caps from row 2 to row 1 */
+ caps1_10 = caps2_10;
+ goto st131;
+ }
+ if (c == 61) {
+ goto st152;
+ }
+ if (c == 91) {
+ goto st153;
+ }
+ if (c == 92) {
+ /* transfer caps from row 2 to row 1 */
+ caps1_10 = caps2_10;
+ goto st133;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 38)
+ || (c >= 40 && c <= 60)
+ || (c >= 62 && c <= 90)
+ || (c >= 93 && c <= 255))
+ {
+ /* transfer caps from row 2 to row 1 */
+ caps1_10 = caps2_10;
+ goto st130;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st153: { /* DFA node {78,36,53} 153 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_6;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 3;
+ if (c != -1) {
+ if (c == 39) {
+ goto st84;
+ }
+ if (c == 92) {
+ goto st85;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st83;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st154: { /* DFA node {76,53} 154 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 1 to matched */
+ matched_0 = caps1_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if (c == 39) {
+ goto st131;
+ }
+ if (c == 92) {
+ goto st133;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st130;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st156: { /* DFA node {65,78,34,53} 156 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 3 to matched */
+ matched_0 = caps3_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if (c == 34) {
+ /* transfer caps from row 3 to row 2 */
+ caps2_10 = caps3_10;
+ goto st138;
+ }
+ if (c == 39) {
+ /* transfer caps from row 3 to row 2 */
+ caps2_10 = caps3_10;
+ goto st139;
+ }
+ if (c == 61) {
+ goto st156;
+ }
+ if (c == 91) {
+ goto st157;
+ }
+ if (c == 92) {
+ /* transfer caps from row 3 to row 2 */
+ caps2_10 = caps3_10;
+ goto st141;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 60)
+ || (c >= 62 && c <= 90)
+ || (c >= 93 && c <= 255))
+ {
+ /* transfer caps from row 3 to row 2 */
+ caps2_10 = caps3_10;
+ goto st137;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st157: { /* DFA node {65,78,36,53} 157 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_6;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 3;
+ if (c != -1) {
+ if (c == 34) {
+ goto st105;
+ }
+ if (c == 39) {
+ goto st106;
+ }
+ if (c == 92) {
+ goto st107;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st104;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st158: { /* DFA node {63,76,53} 158 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if (c == 34) {
+ goto st138;
+ }
+ if (c == 39) {
+ goto st139;
+ }
+ if (c == 92) {
+ goto st141;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st137;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st159: { /* DFA node {78,65,34,53} 159 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 3 to matched */
+ matched_0 = caps3_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if (c == 34) {
+ /* transfer caps from row 3 to row 2 */
+ caps2_10 = caps3_10;
+ goto st147;
+ }
+ if (c == 39) {
+ /* transfer caps from row 3 to row 2 */
+ caps2_10 = caps3_10;
+ goto st148;
+ }
+ if (c == 61) {
+ goto st159;
+ }
+ if (c == 91) {
+ goto st160;
+ }
+ if (c == 92) {
+ /* transfer caps from row 3 to row 2 */
+ caps2_10 = caps3_10;
+ goto st150;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 60)
+ || (c >= 62 && c <= 90)
+ || (c >= 93 && c <= 255))
+ {
+ /* transfer caps from row 3 to row 2 */
+ caps2_10 = caps3_10;
+ goto st146;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st160: { /* DFA node {78,65,36,53} 160 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_6;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 3;
+ if (c != -1) {
+ if (c == 34) {
+ goto st127;
+ }
+ if (c == 39) {
+ goto st128;
+ }
+ if (c == 92) {
+ goto st129;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st126;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st161: { /* DFA node {76,63,53} 161 */
+ c = i < len ? s[i] : -1;
+ i++;
+ /* transfer caps from row 2 to matched */
+ matched_0 = caps2_10;
+ /* capture stores */
+ matched_1 = i - 1;
+ matched_id = 5;
+ if (c != -1) {
+ if (c == 34) {
+ goto st147;
+ }
+ if (c == 39) {
+ goto st148;
+ }
+ if (c == 92) {
+ goto st150;
+ }
+ if ((c >= 0 && c <= 9)
+ || (c >= 11 && c <= 33)
+ || (c >= 35 && c <= 38)
+ || (c >= 40 && c <= 91)
+ || (c >= 93 && c <= 255))
+ {
+ goto st146;
+ }
+ }
+ } /* end state */
+
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+
+st0_error:
+st1_error:
+st2_error:
+st3_error:
+st4_error:
+st5_error:
+st6_error:
+st9_error:
+st11_error:
+st12_error:
+st13_error:
+st14_error:
+st15_error:
+st18_error:
+st19_error:
+st21_error:
+st22_error:
+st23_error:
+st24_error:
+st28_error:
+st30_error:
+st35_error:
+st38_error:
+st39_error:
+st40_error:
+st41_error:
+st45_error:
+st47_error:
+st48_error:
+st49_error:
+st50_error:
+st51_error:
+st52_error:
+st55_error:
+st57_error:
+st59_error:
+st60_error:
+st63_error:
+st64_error:
+st65_error:
+st66_error:
+st71_error:
+st73_error:
+st74_error:
+st75_error:
+st76_error:
+st77_error:
+st78_error:
+st81_error:
+st83_error:
+st85_error:
+st92_error:
+st94_error:
+st95_error:
+st96_error:
+st97_error:
+st98_error:
+st99_error:
+st102_error:
+st104_error:
+st107_error:
+st112_error:
+st114_error:
+st116_error:
+st117_error:
+st118_error:
+st119_error:
+st120_error:
+st121_error:
+st124_error:
+st126_error:
+st129_error:
+st134_error:
+st142_error:
+st151_error:
+
+ if (matched_0 != -1) {
+ ovec[0] = matched_0;
+ ovec[1] = matched_1;
+ return matched_id; /* fallback */
+ }
+ return NO_MATCH;
+}
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_lex.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_lex.h
new file mode 100644
index 0000000..4155da7
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_lex.h
@@ -0,0 +1,25 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_lex.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_LEX_H_INCLUDED_
+#define _NGX_STREAM_LUA_LEX_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+int ngx_stream_lua_lex(const u_char *const s, size_t len, int *const ovec);
+
+
+#endif
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_log.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_log.c
new file mode 100644
index 0000000..c6e9c4c
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_log.c
@@ -0,0 +1,463 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_log.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_log.h"
+#include "ngx_stream_lua_util.h"
+
+#include "ngx_stream_lua_log_ringbuf.h"
+
+
+static int ngx_stream_lua_print(lua_State *L);
+static int ngx_stream_lua_ngx_log(lua_State *L);
+static int log_wrapper(ngx_log_t *log, const char *ident,
+ ngx_uint_t level, lua_State *L);
+static void ngx_stream_lua_inject_log_consts(lua_State *L);
+
+
+/**
+ * Wrapper of nginx log functionality. Take a log level param and varargs of
+ * log message params.
+ *
+ * @param L Lua state pointer
+ * @retval always 0 (don't return values to Lua)
+ * */
+int
+ngx_stream_lua_ngx_log(lua_State *L)
+{
+ ngx_log_t *log;
+ ngx_stream_lua_request_t *r;
+ const char *msg;
+ int level;
+
+ r = ngx_stream_lua_get_req(L);
+
+ if (r && r->connection && r->connection->log) {
+ log = r->connection->log;
+
+ } else {
+ log = ngx_cycle->log;
+ }
+
+ level = luaL_checkint(L, 1);
+ if (level < NGX_LOG_STDERR || level > NGX_LOG_DEBUG) {
+ msg = lua_pushfstring(L, "bad log level: %d", level);
+ return luaL_argerror(L, 1, msg);
+ }
+
+ /* remove log-level param from stack */
+ lua_remove(L, 1);
+
+ return log_wrapper(log, "stream [lua] ", (ngx_uint_t) level, L);
+}
+
+
+/**
+ * Override Lua print function, output message to nginx error logs. Equal to
+ * ngx.log(ngx.NOTICE, ...).
+ *
+ * @param L Lua state pointer
+ * @retval always 0 (don't return values to Lua)
+ * */
+int
+ngx_stream_lua_print(lua_State *L)
+{
+ ngx_log_t *log;
+ ngx_stream_lua_request_t *r;
+
+ r = ngx_stream_lua_get_req(L);
+
+ if (r && r->connection && r->connection->log) {
+ log = r->connection->log;
+
+ } else {
+ log = ngx_cycle->log;
+ }
+
+ return log_wrapper(log, "stream [lua] ", NGX_LOG_NOTICE, L);
+}
+
+
+static int
+log_wrapper(ngx_log_t *log, const char *ident, ngx_uint_t level,
+ lua_State *L)
+{
+ u_char *buf;
+ u_char *p, *q;
+ ngx_str_t name;
+ int nargs, i;
+ size_t size, len;
+ size_t src_len = 0;
+ int type;
+ const char *msg;
+ lua_Debug ar;
+
+ if (level > log->log_level) {
+ return 0;
+ }
+
+#if 1
+ /* add debug info */
+
+ lua_getstack(L, 1, &ar);
+ lua_getinfo(L, "Snl", &ar);
+
+ /* get the basename of the Lua source file path, stored in q */
+ name.data = (u_char *) ar.short_src;
+ if (name.data == NULL) {
+ name.len = 0;
+
+ } else {
+ p = name.data;
+ while (*p != '\0') {
+ if (*p == '/' || *p == '\\') {
+ name.data = p + 1;
+ }
+ p++;
+ }
+
+ name.len = p - name.data;
+ }
+
+#endif
+
+ nargs = lua_gettop(L);
+
+ size = name.len + NGX_INT_T_LEN + sizeof(":: ") - 1;
+
+ if (*ar.namewhat != '\0' && *ar.what == 'L') {
+ src_len = ngx_strlen(ar.name);
+ size += src_len + sizeof("(): ") - 1;
+ }
+
+ for (i = 1; i <= nargs; i++) {
+ type = lua_type(L, i);
+ switch (type) {
+ case LUA_TNUMBER:
+ case LUA_TSTRING:
+ lua_tolstring(L, i, &len);
+ size += len;
+ break;
+
+ case LUA_TNIL:
+ size += sizeof("nil") - 1;
+ break;
+
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, i)) {
+ size += sizeof("true") - 1;
+
+ } else {
+ size += sizeof("false") - 1;
+ }
+
+ break;
+
+ case LUA_TTABLE:
+ if (!luaL_callmeta(L, i, "__tostring")) {
+ return luaL_argerror(L, i, "expected table to have "
+ "__tostring metamethod");
+ }
+
+ lua_tolstring(L, -1, &len);
+ size += len;
+ break;
+
+ case LUA_TLIGHTUSERDATA:
+ if (lua_touserdata(L, i) == NULL) {
+ size += sizeof("null") - 1;
+ break;
+ }
+
+ continue;
+
+ default:
+ msg = lua_pushfstring(L, "string, number, boolean, or nil "
+ "expected, got %s",
+ lua_typename(L, type));
+ return luaL_argerror(L, i, msg);
+ }
+ }
+
+ buf = lua_newuserdata(L, size);
+
+ p = ngx_copy(buf, name.data, name.len);
+
+ *p++ = ':';
+
+ p = ngx_snprintf(p, NGX_INT_T_LEN, "%d",
+ ar.currentline > 0 ? ar.currentline : ar.linedefined);
+
+ *p++ = ':'; *p++ = ' ';
+
+ if (*ar.namewhat != '\0' && *ar.what == 'L') {
+ p = ngx_copy(p, ar.name, src_len);
+ *p++ = '(';
+ *p++ = ')';
+ *p++ = ':';
+ *p++ = ' ';
+ }
+
+ for (i = 1; i <= nargs; i++) {
+ type = lua_type(L, i);
+ switch (type) {
+ case LUA_TNUMBER:
+ case LUA_TSTRING:
+ q = (u_char *) lua_tolstring(L, i, &len);
+ p = ngx_copy(p, q, len);
+ break;
+
+ case LUA_TNIL:
+ *p++ = 'n';
+ *p++ = 'i';
+ *p++ = 'l';
+ break;
+
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, i)) {
+ *p++ = 't';
+ *p++ = 'r';
+ *p++ = 'u';
+ *p++ = 'e';
+
+ } else {
+ *p++ = 'f';
+ *p++ = 'a';
+ *p++ = 'l';
+ *p++ = 's';
+ *p++ = 'e';
+ }
+
+ break;
+
+ case LUA_TTABLE:
+ luaL_callmeta(L, i, "__tostring");
+ q = (u_char *) lua_tolstring(L, -1, &len);
+ p = ngx_copy(p, q, len);
+ break;
+
+ case LUA_TLIGHTUSERDATA:
+ *p++ = 'n';
+ *p++ = 'u';
+ *p++ = 'l';
+ *p++ = 'l';
+
+ break;
+
+ default:
+ return luaL_error(L, "impossible to reach here");
+ }
+ }
+
+ if (p - buf > (off_t) size) {
+ return luaL_error(L, "buffer error: %d > %d", (int) (p - buf),
+ (int) size);
+ }
+
+ ngx_log_error(level, log, 0, "%s%*s", ident, (size_t) (p - buf), buf);
+
+ return 0;
+}
+
+
+void
+ngx_stream_lua_inject_log_api(lua_State *L)
+{
+ ngx_stream_lua_inject_log_consts(L);
+
+ lua_pushcfunction(L, ngx_stream_lua_ngx_log);
+ lua_setfield(L, -2, "log");
+
+ lua_pushcfunction(L, ngx_stream_lua_print);
+ lua_setglobal(L, "print");
+}
+
+
+static void
+ngx_stream_lua_inject_log_consts(lua_State *L)
+{
+ /* {{{ nginx log level constants */
+ lua_pushinteger(L, NGX_LOG_STDERR);
+ lua_setfield(L, -2, "STDERR");
+
+ lua_pushinteger(L, NGX_LOG_EMERG);
+ lua_setfield(L, -2, "EMERG");
+
+ lua_pushinteger(L, NGX_LOG_ALERT);
+ lua_setfield(L, -2, "ALERT");
+
+ lua_pushinteger(L, NGX_LOG_CRIT);
+ lua_setfield(L, -2, "CRIT");
+
+ lua_pushinteger(L, NGX_LOG_ERR);
+ lua_setfield(L, -2, "ERR");
+
+ lua_pushinteger(L, NGX_LOG_WARN);
+ lua_setfield(L, -2, "WARN");
+
+ lua_pushinteger(L, NGX_LOG_NOTICE);
+ lua_setfield(L, -2, "NOTICE");
+
+ lua_pushinteger(L, NGX_LOG_INFO);
+ lua_setfield(L, -2, "INFO");
+
+ lua_pushinteger(L, NGX_LOG_DEBUG);
+ lua_setfield(L, -2, "DEBUG");
+ /* }}} */
+}
+
+
+#ifdef HAVE_INTERCEPT_ERROR_LOG_PATCH
+ngx_int_t
+ngx_stream_lua_capture_log_handler(ngx_log_t *log,
+ ngx_uint_t level, u_char *buf, size_t n)
+{
+ ngx_stream_lua_log_ringbuf_t *ringbuf;
+
+ dd("enter");
+
+ ringbuf = (ngx_stream_lua_log_ringbuf_t *)
+ ngx_cycle->intercept_error_log_data;
+
+ if (level > ringbuf->filter_level) {
+ return NGX_OK;
+ }
+
+ ngx_stream_lua_log_ringbuf_write(ringbuf, level, buf, n);
+
+ dd("capture log: %s\n", buf);
+
+ return NGX_OK;
+}
+#endif
+
+
+int
+ngx_stream_lua_ffi_errlog_set_filter_level(int level, u_char *err,
+ size_t *errlen)
+{
+#ifdef HAVE_INTERCEPT_ERROR_LOG_PATCH
+ ngx_stream_lua_log_ringbuf_t *ringbuf;
+
+ ringbuf = ngx_cycle->intercept_error_log_data;
+
+ if (ringbuf == NULL) {
+ *errlen = ngx_snprintf(err, *errlen,
+ "directive \"lua_capture_error_log\" is not set")
+ - err;
+ return NGX_ERROR;
+ }
+
+ if (level > NGX_LOG_DEBUG || level < NGX_LOG_STDERR) {
+ *errlen = ngx_snprintf(err, *errlen, "bad log level: %d", level)
+ - err;
+ return NGX_ERROR;
+ }
+
+ ringbuf->filter_level = level;
+ return NGX_OK;
+#else
+ *errlen = ngx_snprintf(err, *errlen,
+ "missing the capture error log patch for nginx")
+ - err;
+ return NGX_ERROR;
+#endif
+}
+
+
+int
+ngx_stream_lua_ffi_errlog_get_msg(char **log, int *loglevel, u_char *err,
+ size_t *errlen, double *log_time)
+{
+#ifdef HAVE_INTERCEPT_ERROR_LOG_PATCH
+ ngx_uint_t loglen;
+
+ ngx_stream_lua_log_ringbuf_t *ringbuf;
+
+ ringbuf = ngx_cycle->intercept_error_log_data;
+
+ if (ringbuf == NULL) {
+ *errlen = ngx_snprintf(err, *errlen,
+ "directive \"lua_capture_error_log\" is not set")
+ - err;
+ return NGX_ERROR;
+ }
+
+ if (ringbuf->count == 0) {
+ return NGX_DONE;
+ }
+
+ ngx_stream_lua_log_ringbuf_read(ringbuf, loglevel, (void **) log, &loglen,
+ log_time);
+ return loglen;
+#else
+ *errlen = ngx_snprintf(err, *errlen,
+ "missing the capture error log patch for nginx")
+ - err;
+ return NGX_ERROR;
+#endif
+}
+
+
+int
+ngx_stream_lua_ffi_errlog_get_sys_filter_level(ngx_stream_lua_request_t *r)
+{
+ ngx_log_t *log;
+ int log_level;
+
+ if (r && r->connection && r->connection->log) {
+ log = r->connection->log;
+
+ } else {
+ log = ngx_cycle->log;
+ }
+
+ log_level = log->log_level;
+ if (log_level == NGX_LOG_DEBUG_ALL) {
+ log_level = NGX_LOG_DEBUG;
+ }
+
+ return log_level;
+}
+
+
+int
+ngx_stream_lua_ffi_raw_log(ngx_stream_lua_request_t *r, int level, u_char *s,
+ size_t s_len)
+{
+ ngx_log_t *log;
+
+ if (level > NGX_LOG_DEBUG || level < NGX_LOG_STDERR) {
+ return NGX_ERROR;
+ }
+
+ if (r && r->connection && r->connection->log) {
+ log = r->connection->log;
+
+ } else {
+ log = ngx_cycle->log;
+ }
+
+ ngx_log_error((unsigned) level, log, 0, "%*s", s_len, s);
+
+ return NGX_OK;
+}
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_log.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_log.h
new file mode 100644
index 0000000..9ad0234
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_log.h
@@ -0,0 +1,33 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_log.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_LOG_H_INCLUDED_
+#define _NGX_STREAM_LUA_LOG_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+void ngx_stream_lua_inject_log_api(lua_State *L);
+
+#ifdef HAVE_INTERCEPT_ERROR_LOG_PATCH
+ngx_int_t ngx_stream_lua_capture_log_handler(ngx_log_t *log,
+ ngx_uint_t level, u_char *buf, size_t n);
+#endif
+
+
+#endif /* _NGX_STREAM_LUA_LOG_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_log_ringbuf.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_log_ringbuf.c
new file mode 100644
index 0000000..bcdc638
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_log_ringbuf.c
@@ -0,0 +1,236 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_log_ringbuf.c.tt2
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_common.h"
+#include "ngx_stream_lua_log_ringbuf.h"
+
+
+typedef struct {
+ double time;
+ unsigned len;
+ unsigned log_level;
+} ngx_stream_lua_log_ringbuf_header_t;
+
+
+enum {
+ HEADER_LEN = sizeof(ngx_stream_lua_log_ringbuf_header_t)
+};
+
+
+static void *ngx_stream_lua_log_ringbuf_next_header(
+ ngx_stream_lua_log_ringbuf_t *rb);
+static void ngx_stream_lua_log_ringbuf_append(
+ ngx_stream_lua_log_ringbuf_t *rb, int log_level, void *buf, int n);
+static size_t ngx_stream_lua_log_ringbuf_free_spaces(
+ ngx_stream_lua_log_ringbuf_t *rb);
+
+
+void
+ngx_stream_lua_log_ringbuf_init(ngx_stream_lua_log_ringbuf_t *rb, void *buf,
+ size_t len)
+{
+ rb->data = buf;
+ rb->size = len;
+
+ rb->tail = rb->data;
+ rb->head = rb->data;
+ rb->sentinel = rb->data + rb->size;
+ rb->count = 0;
+ rb->filter_level = NGX_LOG_DEBUG;
+
+ return;
+}
+
+
+void
+ngx_stream_lua_log_ringbuf_reset(ngx_stream_lua_log_ringbuf_t *rb)
+{
+ rb->tail = rb->data;
+ rb->head = rb->data;
+ rb->sentinel = rb->data + rb->size;
+ rb->count = 0;
+
+ return;
+}
+
+
+/*
+ * get the next data header, it'll skip the useless data space or
+ * placehold data
+ */
+static void *
+ngx_stream_lua_log_ringbuf_next_header(ngx_stream_lua_log_ringbuf_t *rb)
+{
+ /* useless data */
+ if (rb->size - (rb->head - rb->data) < HEADER_LEN)
+ {
+ return rb->data;
+ }
+
+ /* placehold data */
+ if (rb->head >= rb->sentinel) {
+ return rb->data;
+ }
+
+ return rb->head;
+}
+
+
+/* append data to ring buffer directly */
+static void
+ngx_stream_lua_log_ringbuf_append(ngx_stream_lua_log_ringbuf_t *rb,
+ int log_level, void *buf, int n)
+{
+ ngx_stream_lua_log_ringbuf_header_t *head;
+ ngx_time_t *tp;
+
+ head = (ngx_stream_lua_log_ringbuf_header_t *) rb->tail;
+ head->len = n;
+ head->log_level = log_level;
+
+ tp = ngx_timeofday();
+ head->time = tp->sec + tp->msec / 1000.0L;
+
+ rb->tail += HEADER_LEN;
+ ngx_memcpy(rb->tail, buf, n);
+ rb->tail += n;
+ rb->count++;
+
+ if (rb->tail > rb->sentinel) {
+ rb->sentinel = rb->tail;
+ }
+
+ return;
+}
+
+
+/* throw away data at head */
+static void
+ngx_stream_lua_log_ringbuf_throw_away(ngx_stream_lua_log_ringbuf_t *rb)
+{
+ ngx_stream_lua_log_ringbuf_header_t *head;
+
+ if (rb->count == 0) {
+ return;
+ }
+
+ head = (ngx_stream_lua_log_ringbuf_header_t *) rb->head;
+
+ rb->head += HEADER_LEN + head->len;
+ rb->count--;
+
+ if (rb->count == 0) {
+ ngx_stream_lua_log_ringbuf_reset(rb);
+ }
+
+ rb->head = ngx_stream_lua_log_ringbuf_next_header(rb);
+
+ return;
+}
+
+
+/* size of free spaces */
+static size_t
+ngx_stream_lua_log_ringbuf_free_spaces(ngx_stream_lua_log_ringbuf_t *rb)
+{
+ if (rb->count == 0) {
+ return rb->size;
+ }
+
+ if (rb->tail > rb->head) {
+ return rb->data + rb->size - rb->tail;
+ }
+
+ return rb->head - rb->tail;
+}
+
+
+/*
+ * try to write log data to ring buffer, throw away old data
+ * if there was not enough free spaces.
+ */
+ngx_int_t
+ngx_stream_lua_log_ringbuf_write(ngx_stream_lua_log_ringbuf_t *rb,
+ int log_level, void *buf, size_t n)
+{
+ if (n + HEADER_LEN > rb->size) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_stream_lua_log_ringbuf_free_spaces(rb) < n + HEADER_LEN) {
+ /* if the right space is not enough, mark it as placehold data */
+ if ((size_t)(rb->data + rb->size - rb->tail) < n + HEADER_LEN) {
+
+ while (rb->head >= rb->tail && rb->count) {
+ /* head is after tail, so we will throw away all data between
+ * head and sentinel */
+ ngx_stream_lua_log_ringbuf_throw_away(rb);
+ }
+
+ rb->sentinel = rb->tail;
+ rb->tail = rb->data;
+ }
+
+ while (ngx_stream_lua_log_ringbuf_free_spaces(rb) < n + HEADER_LEN) {
+ ngx_stream_lua_log_ringbuf_throw_away(rb);
+ }
+ }
+
+ ngx_stream_lua_log_ringbuf_append(rb, log_level, buf, n);
+
+ return NGX_OK;
+}
+
+
+/* read log from ring buffer, do reset if all of the logs were readed. */
+ngx_int_t
+ngx_stream_lua_log_ringbuf_read(ngx_stream_lua_log_ringbuf_t *rb,
+ int *log_level, void **buf, size_t *n, double *log_time)
+{
+ ngx_stream_lua_log_ringbuf_header_t *head;
+
+ if (rb->count == 0) {
+ return NGX_ERROR;
+ }
+
+ head = (ngx_stream_lua_log_ringbuf_header_t *) rb->head;
+
+ if (rb->head >= rb->sentinel) {
+ return NGX_ERROR;
+ }
+
+ *log_level = head->log_level;
+ *n = head->len;
+ rb->head += HEADER_LEN;
+ *buf = rb->head;
+ rb->head += head->len;
+
+ if (log_time) {
+ *log_time = head->time;
+ }
+
+ rb->count--;
+
+ if (rb->count == 0) {
+ ngx_stream_lua_log_ringbuf_reset(rb);
+ }
+
+ rb->head = ngx_stream_lua_log_ringbuf_next_header(rb);
+
+ return NGX_OK;
+}
+
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_log_ringbuf.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_log_ringbuf.h
new file mode 100644
index 0000000..32b33c8
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_log_ringbuf.h
@@ -0,0 +1,39 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_log_ringbuf.h.tt2
+ */
+
+
+#ifndef _NGX_STREAM_LUA_RINGBUF_H_INCLUDED_
+#define _NGX_STREAM_LUA_RINGBUF_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+typedef struct {
+ ngx_uint_t filter_level;
+ char *tail; /* writed point */
+ char *head; /* readed point */
+ char *data; /* buffer */
+ char *sentinel;
+ size_t size; /* buffer total size */
+ size_t count; /* count of logs */
+} ngx_stream_lua_log_ringbuf_t;
+
+
+void ngx_stream_lua_log_ringbuf_init(ngx_stream_lua_log_ringbuf_t *rb,
+ void *buf, size_t len);
+void ngx_stream_lua_log_ringbuf_reset(ngx_stream_lua_log_ringbuf_t *rb);
+ngx_int_t ngx_stream_lua_log_ringbuf_read(ngx_stream_lua_log_ringbuf_t *rb,
+ int *log_level, void **buf, size_t *n, double *log_time);
+ngx_int_t ngx_stream_lua_log_ringbuf_write(ngx_stream_lua_log_ringbuf_t *rb,
+ int log_level, void *buf, size_t n);
+
+
+#endif /* _NGX_STREAM_LUA_RINGBUF_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_logby.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_logby.c
new file mode 100644
index 0000000..8d83792
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_logby.c
@@ -0,0 +1,275 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_logby.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_directive.h"
+#include "ngx_stream_lua_logby.h"
+#include "ngx_stream_lua_exception.h"
+#include "ngx_stream_lua_util.h"
+#include "ngx_stream_lua_pcrefix.h"
+#include "ngx_stream_lua_cache.h"
+#include "ngx_stream_lua_misc.h"
+#include "ngx_stream_lua_consts.h"
+#include "ngx_stream_lua_util.h"
+#include "ngx_stream_lua_exception.h"
+#if (NGX_STREAM_LUA_HAVE_MALLOC_TRIM)
+#include <malloc.h>
+#endif
+
+
+static ngx_int_t ngx_stream_lua_log_by_chunk(lua_State *L,
+ ngx_stream_lua_request_t *r);
+
+
+static void
+ngx_stream_lua_log_by_lua_env(lua_State *L, ngx_stream_lua_request_t *r)
+{
+ /* set nginx request pointer to current lua thread's globals table */
+ ngx_stream_lua_set_req(L, r);
+
+#ifndef OPENRESTY_LUAJIT
+ /**
+ * we want to create empty environment for current script
+ *
+ * newt = {}
+ * newt["_G"] = newt
+ * setmetatable(newt, {__index = _G})
+ *
+ * if a function or symbol is not defined in our env, __index will lookup
+ * in the global env.
+ *
+ * all variables created in the script-env will be thrown away at the end
+ * of the script run.
+ * */
+ ngx_stream_lua_create_new_globals_table(L, 0 /* narr */, 1 /* nrec */);
+
+ /* {{{ make new env inheriting main thread's globals table */
+ lua_createtable(L, 0, 1); /* the metatable for the new env */
+ ngx_stream_lua_get_globals_table(L);
+ lua_setfield(L, -2, "__index");
+ lua_setmetatable(L, -2); /* setmetatable({}, {__index = _G}) */
+ /* }}} */
+
+ lua_setfenv(L, -2); /* set new running env for the code closure */
+#endif /* OPENRESTY_LUAJIT */
+}
+
+
+ngx_int_t
+ngx_stream_lua_log_handler(ngx_stream_session_t *r)
+{
+#if (NGX_STREAM_LUA_HAVE_MALLOC_TRIM)
+ ngx_uint_t trim_cycle, trim_nreq;
+ ngx_stream_lua_main_conf_t *lmcf;
+#if (NGX_DEBUG)
+ ngx_int_t trim_ret;
+#endif
+#endif
+ ngx_stream_lua_loc_conf_t *llcf;
+ ngx_stream_lua_ctx_t *ctx;
+
+#if (NGX_STREAM_LUA_HAVE_MALLOC_TRIM)
+ lmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_lua_module);
+
+ trim_cycle = lmcf->malloc_trim_cycle;
+
+ if (trim_cycle > 0) {
+
+ dd("cycle: %d", (int) trim_cycle);
+
+ trim_nreq = ++lmcf->malloc_trim_req_count;
+
+ if (trim_nreq >= trim_cycle) {
+ lmcf->malloc_trim_req_count = 0;
+
+#if (NGX_DEBUG)
+ trim_ret = malloc_trim(1);
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "malloc_trim(1) returned %d", trim_ret);
+#else
+ (void) malloc_trim(1);
+#endif
+ }
+ }
+# if (NGX_DEBUG)
+ else {
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "malloc_trim() disabled");
+ }
+# endif
+#endif
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua log handler");
+
+ llcf = ngx_stream_get_module_srv_conf(r, ngx_stream_lua_module);
+
+ if (llcf->log_handler == NULL) {
+ dd("no log handler found");
+ return NGX_DECLINED;
+ }
+
+ ctx = ngx_stream_get_module_ctx(r, ngx_stream_lua_module);
+
+ dd("ctx = %p", ctx);
+
+ if (ctx == NULL) {
+ ctx = ngx_stream_lua_create_ctx(r);
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ ctx->context = NGX_STREAM_LUA_CONTEXT_LOG;
+
+ dd("calling log handler");
+ return llcf->log_handler(ctx->request);
+}
+
+
+ngx_int_t
+ngx_stream_lua_log_handler_inline(ngx_stream_lua_request_t *r)
+{
+ lua_State *L;
+ ngx_int_t rc;
+ ngx_stream_lua_loc_conf_t *llcf;
+
+ dd("log by lua inline");
+
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+
+ L = ngx_stream_lua_get_lua_vm(r, NULL);
+
+ /* load Lua inline script (w/ cache) sp = 1 */
+ rc = ngx_stream_lua_cache_loadbuffer(r->connection->log, L,
+ llcf->log_src.value.data,
+ llcf->log_src.value.len,
+ llcf->log_src_key,
+ (const char *) llcf->log_chunkname);
+ if (rc != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return ngx_stream_lua_log_by_chunk(L, r);
+}
+
+
+ngx_int_t
+ngx_stream_lua_log_handler_file(ngx_stream_lua_request_t *r)
+{
+ lua_State *L;
+ ngx_int_t rc;
+ u_char *script_path;
+ ngx_stream_lua_loc_conf_t *llcf;
+ ngx_str_t eval_src;
+
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+
+ if (ngx_stream_complex_value(r->session, &llcf->log_src, &eval_src)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ script_path = ngx_stream_lua_rebase_path(r->pool, eval_src.data,
+ eval_src.len);
+
+ if (script_path == NULL) {
+ return NGX_ERROR;
+ }
+
+ L = ngx_stream_lua_get_lua_vm(r, NULL);
+
+ /* load Lua script file (w/ cache) sp = 1 */
+ rc = ngx_stream_lua_cache_loadfile(r->connection->log, L, script_path,
+ llcf->log_src_key);
+ if (rc != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return ngx_stream_lua_log_by_chunk(L, r);
+}
+
+
+ngx_int_t
+ngx_stream_lua_log_by_chunk(lua_State *L, ngx_stream_lua_request_t *r)
+{
+ ngx_int_t rc;
+ u_char *err_msg;
+ size_t len;
+#if (NGX_PCRE)
+ ngx_pool_t *old_pool;
+#endif
+
+ /* set Lua VM panic handler */
+ lua_atpanic(L, ngx_stream_lua_atpanic);
+
+ NGX_LUA_EXCEPTION_TRY {
+
+ /* initialize nginx context in Lua VM, code chunk at stack top sp = 1 */
+ ngx_stream_lua_log_by_lua_env(L, r);
+
+#if (NGX_PCRE)
+ /* XXX: work-around to nginx regex subsystem */
+ old_pool = ngx_stream_lua_pcre_malloc_init(r->pool);
+#endif
+
+ lua_pushcfunction(L, ngx_stream_lua_traceback);
+ lua_insert(L, 1); /* put it under chunk and args */
+
+ /* protected call user code */
+ rc = lua_pcall(L, 0, 1, 1);
+
+ lua_remove(L, 1); /* remove traceback function */
+
+#if (NGX_PCRE)
+ /* XXX: work-around to nginx regex subsystem */
+ ngx_stream_lua_pcre_malloc_done(old_pool);
+#endif
+
+ if (rc != 0) {
+ /* error occurred when running loaded code */
+ err_msg = (u_char *) lua_tolstring(L, -1, &len);
+
+ if (err_msg == NULL) {
+ err_msg = (u_char *) "unknown reason";
+ len = sizeof("unknown reason") - 1;
+ }
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "failed to run log_by_lua*: %*s", len, err_msg);
+
+ lua_settop(L, 0); /* clear remaining elems on stack */
+
+ return NGX_ERROR;
+ }
+
+ } NGX_LUA_EXCEPTION_CATCH {
+
+ dd("nginx execution restored");
+ return NGX_ERROR;
+ }
+
+ /* clear Lua stack */
+ lua_settop(L, 0);
+
+ return NGX_OK;
+}
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_logby.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_logby.h
new file mode 100644
index 0000000..713e7e3
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_logby.h
@@ -0,0 +1,30 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_logby.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_LOGBY_H_INCLUDED_
+#define _NGX_STREAM_LUA_LOGBY_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+ngx_int_t ngx_stream_lua_log_handler(ngx_stream_session_t *r);
+
+ngx_int_t ngx_stream_lua_log_handler_inline(ngx_stream_lua_request_t *r);
+ngx_int_t ngx_stream_lua_log_handler_file(ngx_stream_lua_request_t *r);
+
+
+#endif /* _NGX_STREAM_LUA_LOGBY_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_misc.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_misc.c
new file mode 100644
index 0000000..1e4bd8a
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_misc.c
@@ -0,0 +1,65 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_misc.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_misc.h"
+#include "ngx_stream_lua_util.h"
+
+
+
+
+int
+ngx_stream_lua_ffi_get_resp_status(ngx_stream_lua_request_t *r)
+{
+ return r->session->status;
+}
+
+
+
+
+int
+ngx_stream_lua_ffi_get_conf_env(u_char *name, u_char **env_buf,
+ size_t *name_len)
+{
+ ngx_uint_t i;
+ ngx_str_t *var;
+ ngx_core_conf_t *ccf;
+
+ ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
+ ngx_core_module);
+
+ var = ccf->env.elts;
+
+ for (i = 0; i < ccf->env.nelts; i++) {
+ if (var[i].data[var[i].len] == '='
+ && ngx_strncmp(name, var[i].data, var[i].len) == 0)
+ {
+ *env_buf = var[i].data;
+ *name_len = var[i].len;
+
+ return NGX_OK;
+ }
+ }
+
+ return NGX_DECLINED;
+}
+
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_misc.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_misc.h
new file mode 100644
index 0000000..64f99e1
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_misc.h
@@ -0,0 +1,27 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_misc.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_MISC_H_INCLUDED_
+#define _NGX_STREAM_LUA_MISC_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+
+
+#endif /* _NGX_STREAM_LUA_MISC_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_module.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_module.c
new file mode 100644
index 0000000..5c9024e
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_module.c
@@ -0,0 +1,1170 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_module.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_directive.h"
+#include "ngx_stream_lua_contentby.h"
+#include "ngx_stream_lua_util.h"
+#include "ngx_stream_lua_initby.h"
+#include "ngx_stream_lua_initworkerby.h"
+#include "ngx_stream_lua_probe.h"
+#include "ngx_stream_lua_balancer.h"
+#include "ngx_stream_lua_logby.h"
+#include "ngx_stream_lua_semaphore.h"
+#include "ngx_stream_lua_ssl_client_helloby.h"
+#include "ngx_stream_lua_ssl_certby.h"
+
+
+#include "ngx_stream_lua_prereadby.h"
+
+
+static void *ngx_stream_lua_create_main_conf(ngx_conf_t *cf);
+static char *ngx_stream_lua_init_main_conf(ngx_conf_t *cf, void *conf);
+static void *ngx_stream_lua_create_srv_conf(ngx_conf_t *cf);
+static char *ngx_stream_lua_merge_srv_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+
+
+
+
+static ngx_int_t ngx_stream_lua_init(ngx_conf_t *cf);
+static char *ngx_stream_lua_lowat_check(ngx_conf_t *cf, void *post, void *data);
+#if (NGX_STREAM_SSL)
+static ngx_int_t ngx_stream_lua_set_ssl(ngx_conf_t *cf,
+ ngx_stream_lua_loc_conf_t *llcf);
+#if (nginx_version >= 1019004)
+static char *ngx_stream_lua_ssl_conf_command_check(ngx_conf_t *cf, void *post,
+ void *data);
+#endif
+#endif
+static char *ngx_stream_lua_malloc_trim(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+#if (NGX_PCRE2)
+extern void ngx_stream_lua_regex_cleanup(void *data);
+#endif
+
+
+static ngx_conf_post_t ngx_stream_lua_lowat_post =
+ { ngx_stream_lua_lowat_check };
+#if (NGX_STREAM_SSL)
+#if (nginx_version >= 1019004)
+static ngx_conf_post_t ngx_stream_lua_ssl_conf_command_post =
+ { ngx_stream_lua_ssl_conf_command_check };
+#endif
+#endif
+
+
+
+#if (NGX_STREAM_SSL)
+
+static ngx_conf_bitmask_t ngx_stream_lua_ssl_protocols[] = {
+ { ngx_string("SSLv2"), NGX_SSL_SSLv2 },
+ { ngx_string("SSLv3"), NGX_SSL_SSLv3 },
+ { ngx_string("TLSv1"), NGX_SSL_TLSv1 },
+ { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 },
+ { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 },
+#ifdef NGX_SSL_TLSv1_3
+ { ngx_string("TLSv1.3"), NGX_SSL_TLSv1_3 },
+#endif
+ { ngx_null_string, 0 }
+};
+
+#endif
+
+
+
+
+static ngx_command_t ngx_stream_lua_cmds[] = {
+
+ { ngx_string("lua_load_resty_core"),
+ NGX_STREAM_MAIN_CONF|NGX_CONF_FLAG,
+ ngx_stream_lua_load_resty_core,
+ NGX_STREAM_MAIN_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("lua_max_running_timers"),
+ NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_STREAM_MAIN_CONF_OFFSET,
+ offsetof(ngx_stream_lua_main_conf_t, max_running_timers),
+ NULL },
+
+ { ngx_string("lua_max_pending_timers"),
+ NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_STREAM_MAIN_CONF_OFFSET,
+ offsetof(ngx_stream_lua_main_conf_t, max_pending_timers),
+ NULL },
+
+ { ngx_string("lua_shared_dict"),
+ NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE2,
+ ngx_stream_lua_shared_dict,
+ 0,
+ 0,
+ NULL },
+
+ { ngx_string("lua_sa_restart"),
+ NGX_STREAM_MAIN_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_STREAM_MAIN_CONF_OFFSET,
+ offsetof(ngx_stream_lua_main_conf_t, set_sa_restart),
+ NULL },
+
+ { ngx_string("lua_capture_error_log"),
+ NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_stream_lua_capture_error_log,
+ 0,
+ 0,
+ NULL },
+
+#if (NGX_PCRE)
+ { ngx_string("lua_regex_cache_max_entries"),
+ NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_STREAM_MAIN_CONF_OFFSET,
+ offsetof(ngx_stream_lua_main_conf_t, regex_cache_max_entries),
+ NULL },
+
+ { ngx_string("lua_regex_match_limit"),
+ NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_STREAM_MAIN_CONF_OFFSET,
+ offsetof(ngx_stream_lua_main_conf_t, regex_match_limit),
+ NULL },
+#endif
+
+ { ngx_string("lua_package_cpath"),
+ NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_stream_lua_package_cpath,
+ NGX_STREAM_MAIN_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("lua_package_path"),
+ NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_stream_lua_package_path,
+ NGX_STREAM_MAIN_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("lua_code_cache"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF
+ |NGX_CONF_FLAG,
+ ngx_stream_lua_code_cache,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_lua_loc_conf_t, enable_code_cache),
+ NULL },
+
+
+ { ngx_string("lua_socket_log_errors"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF
+ |NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_lua_loc_conf_t, log_socket_errors),
+ NULL },
+
+ { ngx_string("init_by_lua_block"),
+ NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
+ ngx_stream_lua_init_by_lua_block,
+ NGX_STREAM_MAIN_CONF_OFFSET,
+ 0,
+ (void *) ngx_stream_lua_init_by_inline },
+
+ { ngx_string("init_by_lua"),
+ NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_stream_lua_init_by_lua,
+ NGX_STREAM_MAIN_CONF_OFFSET,
+ 0,
+ (void *) ngx_stream_lua_init_by_inline },
+
+ { ngx_string("init_by_lua_file"),
+ NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_stream_lua_init_by_lua,
+ NGX_STREAM_MAIN_CONF_OFFSET,
+ 0,
+ (void *) ngx_stream_lua_init_by_file },
+
+ { ngx_string("init_worker_by_lua_block"),
+ NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
+ ngx_stream_lua_init_worker_by_lua_block,
+ NGX_STREAM_MAIN_CONF_OFFSET,
+ 0,
+ (void *) ngx_stream_lua_init_worker_by_inline },
+
+ { ngx_string("init_worker_by_lua"),
+ NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_stream_lua_init_worker_by_lua,
+ NGX_STREAM_MAIN_CONF_OFFSET,
+ 0,
+ (void *) ngx_stream_lua_init_worker_by_inline },
+
+ { ngx_string("init_worker_by_lua_file"),
+ NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_stream_lua_init_worker_by_lua,
+ NGX_STREAM_MAIN_CONF_OFFSET,
+ 0,
+ (void *) ngx_stream_lua_init_worker_by_file },
+
+ /* preread_by_lua_file rel/or/abs/path/to/script */
+ { ngx_string("preread_by_lua_file"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_stream_lua_preread_by_lua,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ 0,
+ (void *) ngx_stream_lua_preread_handler_file },
+
+ /* preread_by_lua_block { <inline script> } */
+ { ngx_string("preread_by_lua_block"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
+ ngx_stream_lua_preread_by_lua_block,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ 0,
+ (void *) ngx_stream_lua_preread_handler_inline },
+
+
+ /* content_by_lua "<inline script>" */
+ { ngx_string("content_by_lua"),
+ NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_stream_lua_content_by_lua,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ 0,
+ (void *) ngx_stream_lua_content_handler_inline },
+
+ /* content_by_lua_block { <inline script> } */
+ { ngx_string("content_by_lua_block"),
+ NGX_STREAM_SRV_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
+ ngx_stream_lua_content_by_lua_block,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ 0,
+ (void *) ngx_stream_lua_content_handler_inline },
+
+ /* content_by_lua_file rel/or/abs/path/to/script */
+ { ngx_string("content_by_lua_file"),
+ NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_stream_lua_content_by_lua,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ 0,
+ (void *) ngx_stream_lua_content_handler_file },
+
+
+
+ /* log_by_lua_block { <inline script> } */
+ { ngx_string("log_by_lua_block"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF
+ |NGX_CONF_BLOCK|NGX_CONF_NOARGS,
+ ngx_stream_lua_log_by_lua_block,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ 0,
+ (void *) ngx_stream_lua_log_handler_inline },
+
+ { ngx_string("log_by_lua_file"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF
+ |NGX_CONF_TAKE1,
+ ngx_stream_lua_log_by_lua,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ 0,
+ (void *) ngx_stream_lua_log_handler_file },
+
+ { ngx_string("preread_by_lua_no_postpone"),
+ NGX_STREAM_MAIN_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_STREAM_MAIN_CONF_OFFSET,
+ offsetof(ngx_stream_lua_main_conf_t, postponed_to_preread_phase_end),
+ NULL },
+
+ { ngx_string("balancer_by_lua_block"),
+ NGX_STREAM_UPS_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
+ ngx_stream_lua_balancer_by_lua_block,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ 0,
+ (void *) ngx_stream_lua_balancer_handler_inline },
+
+ { ngx_string("balancer_by_lua_file"),
+ NGX_STREAM_UPS_CONF|NGX_CONF_TAKE1,
+ ngx_stream_lua_balancer_by_lua,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ 0,
+ (void *) ngx_stream_lua_balancer_handler_file },
+
+
+ { ngx_string("lua_socket_keepalive_timeout"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF
+ |NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_lua_srv_conf_t, keepalive_timeout),
+ NULL },
+
+ { ngx_string("lua_socket_connect_timeout"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF
+ |NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_lua_srv_conf_t, connect_timeout),
+ NULL },
+
+ { ngx_string("lua_socket_send_timeout"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF
+ |NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_lua_srv_conf_t, send_timeout),
+ NULL },
+
+ { ngx_string("lua_socket_send_lowat"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF
+ |NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_lua_srv_conf_t, send_lowat),
+ &ngx_stream_lua_lowat_post },
+
+ { ngx_string("lua_socket_buffer_size"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF
+ |NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_lua_srv_conf_t, buffer_size),
+ NULL },
+
+ { ngx_string("lua_socket_pool_size"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF
+ |NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_lua_srv_conf_t, pool_size),
+ NULL },
+
+ { ngx_string("lua_socket_read_timeout"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF
+ |NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_lua_srv_conf_t, read_timeout),
+ NULL },
+
+
+ { ngx_string("lua_check_client_abort"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF
+ |NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_lua_srv_conf_t, check_client_abort),
+ NULL },
+
+
+
+#if (NGX_STREAM_SSL)
+
+ { ngx_string("lua_ssl_protocols"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_lua_srv_conf_t, ssl_protocols),
+ &ngx_stream_lua_ssl_protocols },
+
+ { ngx_string("lua_ssl_ciphers"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_lua_srv_conf_t, ssl_ciphers),
+ NULL },
+
+ { ngx_string("ssl_client_hello_by_lua_block"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
+ ngx_stream_lua_ssl_client_hello_by_lua_block,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ 0,
+ (void *) ngx_stream_lua_ssl_client_hello_handler_inline },
+
+ { ngx_string("ssl_client_hello_by_lua_file"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_stream_lua_ssl_client_hello_by_lua,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ 0,
+ (void *) ngx_stream_lua_ssl_client_hello_handler_file },
+
+ { ngx_string("ssl_certificate_by_lua_block"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
+ ngx_stream_lua_ssl_cert_by_lua_block,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ 0,
+ (void *) ngx_stream_lua_ssl_cert_handler_inline },
+
+ { ngx_string("ssl_certificate_by_lua_file"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_stream_lua_ssl_cert_by_lua,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ 0,
+ (void *) ngx_stream_lua_ssl_cert_handler_file },
+
+
+ { ngx_string("lua_ssl_verify_depth"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_lua_srv_conf_t, ssl_verify_depth),
+ NULL },
+
+ { ngx_string("lua_ssl_certificate"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_array_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_lua_srv_conf_t, ssl_certificates),
+ NULL },
+
+ { ngx_string("lua_ssl_certificate_key"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_array_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_lua_srv_conf_t, ssl_certificate_keys),
+ NULL },
+
+ { ngx_string("lua_ssl_trusted_certificate"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_lua_srv_conf_t, ssl_trusted_certificate),
+ NULL },
+
+ { ngx_string("lua_ssl_crl"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_lua_srv_conf_t, ssl_crl),
+ NULL },
+
+#if (nginx_version >= 1019004)
+ { ngx_string("lua_ssl_conf_command"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE2,
+ ngx_conf_set_keyval_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_lua_srv_conf_t, ssl_conf_commands),
+ &ngx_stream_lua_ssl_conf_command_post },
+#endif
+#endif /* NGX_STREAM_SSL */
+
+ { ngx_string("lua_malloc_trim"),
+ NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_stream_lua_malloc_trim,
+ NGX_STREAM_MAIN_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("lua_add_variable"),
+ NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_stream_lua_add_variable,
+ 0,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+ngx_stream_module_t ngx_stream_lua_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_stream_lua_init, /* postconfiguration */
+
+ ngx_stream_lua_create_main_conf, /* create main configuration */
+ ngx_stream_lua_init_main_conf, /* init main configuration */
+
+ ngx_stream_lua_create_srv_conf, /* create server configuration */
+ ngx_stream_lua_merge_srv_conf, /* merge server configuration */
+
+};
+
+
+ngx_module_t ngx_stream_lua_module = {
+ NGX_MODULE_V1,
+ &ngx_stream_lua_module_ctx, /* module context */
+ ngx_stream_lua_cmds, /* module directives */
+ NGX_STREAM_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ ngx_stream_lua_init_worker, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_stream_lua_init(ngx_conf_t *cf)
+{
+ ngx_int_t rc;
+ volatile ngx_cycle_t *saved_cycle;
+ ngx_array_t *arr;
+ ngx_pool_cleanup_t *cln;
+
+ ngx_stream_handler_pt *h;
+ ngx_stream_lua_main_conf_t *lmcf;
+ ngx_stream_core_main_conf_t *cmcf;
+
+
+ if (ngx_process == NGX_PROCESS_SIGNALLER || ngx_test_config) {
+ return NGX_OK;
+ }
+
+ lmcf = ngx_stream_conf_get_module_main_conf(cf,
+ ngx_stream_lua_module);
+
+
+ cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
+
+ if (lmcf->requires_preread) {
+ h = ngx_array_push(&cmcf->phases[NGX_STREAM_PREREAD_PHASE].handlers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ *h = ngx_stream_lua_preread_handler;
+ }
+
+ if (lmcf->postponed_to_preread_phase_end == NGX_CONF_UNSET) {
+ lmcf->postponed_to_preread_phase_end = 0;
+ }
+
+ dd("requires log: %d", (int) lmcf->requires_log);
+
+ if (lmcf->requires_log) {
+ arr = &cmcf->phases[NGX_STREAM_LOG_PHASE].handlers;
+ h = ngx_array_push(arr);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (arr->nelts > 1) {
+
+ /*
+ * if there are other log handlers, move them back and put ourself
+ * to the front of the list
+ */
+
+ h = arr->elts;
+ ngx_memmove(&h[1], h,
+ (arr->nelts - 1) * sizeof(ngx_stream_handler_pt));
+ }
+
+ *h = ngx_stream_lua_log_handler;
+ }
+
+
+ /* add the cleanup of semaphores after the lua_close */
+ cln = ngx_pool_cleanup_add(cf->pool, 0);
+ if (cln == NULL) {
+ return NGX_ERROR;
+ }
+
+ cln->data = lmcf;
+ cln->handler = ngx_stream_lua_sema_mm_cleanup;
+
+#if (NGX_PCRE2)
+ /* add the cleanup of pcre2 regex */
+ cln = ngx_pool_cleanup_add(cf->pool, 0);
+ if (cln == NULL) {
+ return NGX_ERROR;
+ }
+
+ cln->data = lmcf;
+ cln->handler = ngx_stream_lua_regex_cleanup;
+#endif
+
+ if (lmcf->lua == NULL) {
+ dd("initializing lua vm");
+
+#ifndef OPENRESTY_LUAJIT
+ if (ngx_process != NGX_PROCESS_SIGNALLER && !ngx_test_config) {
+ ngx_log_error(NGX_LOG_ALERT, cf->log, 0,
+ "detected a LuaJIT version which is not OpenResty's"
+ "; many optimizations will be disabled and "
+ "performance will be compromised (see "
+ "https://github.com/openresty/luajit2 for "
+ "OpenResty's LuaJIT or, even better, consider using "
+ "the OpenResty releases from https://openresty.org/"
+ "en/download.html)");
+ }
+#else
+# if !defined(HAVE_LUA_EXDATA2)
+ ngx_log_error(NGX_LOG_ALERT, cf->log, 0,
+ "detected an old version of OpenResty's LuaJIT missing "
+ "the exdata2 API and thus the "
+ "performance will be compromised; please upgrade to the "
+ "latest version of OpenResty's LuaJIT: "
+ "https://github.com/openresty/luajit2");
+# endif
+#endif
+
+
+ rc = ngx_stream_lua_init_vm(&lmcf->lua, NULL, cf->cycle, cf->pool,
+ lmcf, cf->log, NULL);
+ if (rc != NGX_OK) {
+ if (rc == NGX_DECLINED) {
+ ngx_stream_lua_assert(lmcf->lua != NULL);
+
+ ngx_conf_log_error(NGX_LOG_ALERT, cf, 0,
+ "failed to load the 'resty.core' module "
+ "(https://github.com/openresty/lua-resty"
+ "-core); ensure you are using an OpenResty "
+ "release from https://openresty.org/en/"
+ "download.html (reason: %s)",
+ lua_tostring(lmcf->lua, -1));
+
+ } else {
+ /* rc == NGX_ERROR */
+ ngx_conf_log_error(NGX_LOG_ALERT, cf, 0,
+ "failed to initialize Lua VM");
+ }
+
+ return NGX_ERROR;
+ }
+
+ /* rc == NGX_OK */
+
+ ngx_stream_lua_assert(lmcf->lua != NULL);
+
+ if (!lmcf->requires_shm && lmcf->init_handler) {
+ saved_cycle = ngx_cycle;
+ ngx_cycle = cf->cycle;
+
+ rc = lmcf->init_handler(cf->log, lmcf, lmcf->lua);
+
+ ngx_cycle = saved_cycle;
+
+ if (rc != NGX_OK) {
+ /* an error happened */
+ return NGX_ERROR;
+ }
+ }
+
+ dd("Lua VM initialized!");
+ }
+
+ return NGX_OK;
+}
+
+
+static char *
+ngx_stream_lua_lowat_check(ngx_conf_t *cf, void *post, void *data)
+{
+#if (NGX_FREEBSD)
+ ssize_t *np = data;
+
+ if ((u_long) *np >= ngx_freebsd_net_inet_tcp_sendspace) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"lua_send_lowat\" must be less than %d "
+ "(sysctl net.inet.tcp.sendspace)",
+ ngx_freebsd_net_inet_tcp_sendspace);
+
+ return NGX_CONF_ERROR;
+ }
+
+#elif !(NGX_HAVE_SO_SNDLOWAT)
+ ssize_t *np = data;
+
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "\"lua_send_lowat\" is not supported, ignored");
+
+ *np = 0;
+
+#endif
+
+ return NGX_CONF_OK;
+}
+
+
+static void *
+ngx_stream_lua_create_main_conf(ngx_conf_t *cf)
+{
+ ngx_int_t rc;
+
+ ngx_stream_lua_main_conf_t *lmcf;
+
+ lmcf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_lua_main_conf_t));
+ if (lmcf == NULL) {
+ return NULL;
+ }
+
+ /* set by ngx_pcalloc:
+ * lmcf->lua = NULL;
+ * lmcf->lua_path = { 0, NULL };
+ * lmcf->lua_cpath = { 0, NULL };
+ * lmcf->pending_timers = 0;
+ * lmcf->running_timers = 0;
+ * lmcf->watcher = NULL;
+ * lmcf->regex_cache_entries = 0;
+ * lmcf->jit_stack = NULL;
+ * lmcf->shm_zones = NULL;
+ * lmcf->init_handler = NULL;
+ * lmcf->init_src = { 0, NULL };
+ * lmcf->shm_zones_inited = 0;
+ * lmcf->shdict_zones = NULL;
+ * lmcf->preload_hooks = NULL;
+ * lmcf->requires_header_filter = 0;
+ * lmcf->requires_body_filter = 0;
+ * lmcf->requires_capture_filter = 0;
+ * lmcf->requires_rewrite = 0;
+ * lmcf->requires_access = 0;
+ * lmcf->requires_log = 0;
+ * lmcf->requires_shm = 0;
+ */
+
+ lmcf->pool = cf->pool;
+ lmcf->max_pending_timers = NGX_CONF_UNSET;
+ lmcf->max_running_timers = NGX_CONF_UNSET;
+#if (NGX_PCRE)
+ lmcf->regex_cache_max_entries = NGX_CONF_UNSET;
+ lmcf->regex_match_limit = NGX_CONF_UNSET;
+#endif
+
+ lmcf->postponed_to_preread_phase_end = NGX_CONF_UNSET;
+
+ lmcf->set_sa_restart = NGX_CONF_UNSET;
+
+#if (NGX_STREAM_LUA_HAVE_MALLOC_TRIM)
+ lmcf->malloc_trim_cycle = NGX_CONF_UNSET_UINT;
+#endif
+
+ rc = ngx_stream_lua_sema_mm_init(cf, lmcf);
+ if (rc != NGX_OK) {
+ return NULL;
+ }
+
+ dd("nginx Lua module main config structure initialized!");
+
+ return lmcf;
+}
+
+
+static char *
+ngx_stream_lua_init_main_conf(ngx_conf_t *cf, void *conf)
+{
+ ngx_stream_lua_main_conf_t *lmcf = conf;
+
+#if (NGX_PCRE)
+ if (lmcf->regex_cache_max_entries == NGX_CONF_UNSET) {
+ lmcf->regex_cache_max_entries = 1024;
+ }
+
+ if (lmcf->regex_match_limit == NGX_CONF_UNSET) {
+ lmcf->regex_match_limit = 0;
+ }
+#endif
+
+ if (lmcf->max_pending_timers == NGX_CONF_UNSET) {
+ lmcf->max_pending_timers = 1024;
+ }
+
+ if (lmcf->max_running_timers == NGX_CONF_UNSET) {
+ lmcf->max_running_timers = 256;
+ }
+
+#if (NGX_STREAM_LUA_HAVE_SA_RESTART)
+ if (lmcf->set_sa_restart == NGX_CONF_UNSET) {
+ lmcf->set_sa_restart = 1;
+ }
+#endif
+
+#if (NGX_STREAM_LUA_HAVE_MALLOC_TRIM)
+ if (lmcf->malloc_trim_cycle == NGX_CONF_UNSET_UINT) {
+ lmcf->malloc_trim_cycle = 1000; /* number of reqs */
+ }
+#endif
+
+ lmcf->cycle = cf->cycle;
+
+ return NGX_CONF_OK;
+}
+
+
+
+
+
+
+static void *
+ngx_stream_lua_create_srv_conf(ngx_conf_t *cf)
+{
+ ngx_stream_lua_srv_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_lua_srv_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /* set by ngx_pcalloc:
+ * lscf->srv.ssl_client_hello_handler = NULL;
+ * lscf->srv.ssl_client_hello_src = { 0, NULL };
+ * lscf->srv.ssl_client_hello_src_key = NULL;
+ *
+ * lscf->srv.ssl_cert_handler = NULL;
+ * lscf->srv.ssl_cert_src = { 0, NULL };
+ * lscf->srv.ssl_cert_src_key = NULL;
+ *
+ * lscf->srv.ssl_session_store_handler = NULL;
+ * lscf->srv.ssl_session_store_src = { 0, NULL };
+ * lscf->srv.ssl_session_store_src_key = NULL;
+ *
+ * lscf->srv.ssl_session_fetch_handler = NULL;
+ * lscf->srv.ssl_session_fetch_src = { 0, NULL };
+ * lscf->srv.ssl_session_fetch_src_key = NULL;
+ *
+ * lscf->balancer.handler = NULL;
+ * lscf->balancer.src = { 0, NULL };
+ * lscf->balancer.src_key = NULL;
+ */
+
+ conf->enable_code_cache = NGX_CONF_UNSET;
+ conf->check_client_abort = NGX_CONF_UNSET;
+
+ conf->keepalive_timeout = NGX_CONF_UNSET_MSEC;
+ conf->connect_timeout = NGX_CONF_UNSET_MSEC;
+ conf->send_timeout = NGX_CONF_UNSET_MSEC;
+ conf->read_timeout = NGX_CONF_UNSET_MSEC;
+ conf->send_lowat = NGX_CONF_UNSET_SIZE;
+ conf->buffer_size = NGX_CONF_UNSET_SIZE;
+ conf->pool_size = NGX_CONF_UNSET_UINT;
+
+ conf->log_socket_errors = NGX_CONF_UNSET;
+
+#if (NGX_STREAM_SSL)
+ conf->ssl_verify_depth = NGX_CONF_UNSET_UINT;
+ conf->ssl_certificates = NGX_CONF_UNSET_PTR;
+ conf->ssl_certificate_keys = NGX_CONF_UNSET_PTR;
+#endif
+
+ return conf;
+}
+
+
+static char *
+ngx_stream_lua_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_stream_lua_srv_conf_t *prev = parent;
+ ngx_stream_lua_srv_conf_t *conf = child;
+
+#if (NGX_STREAM_SSL)
+#if defined(nginx_version) && nginx_version >= 1025005
+ ngx_stream_ssl_srv_conf_t *sscf;
+#else
+ ngx_stream_ssl_conf_t *sscf;
+#endif
+
+ dd("merge srv conf");
+
+ sscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_ssl_module);
+#if defined(nginx_version) && nginx_version >= 1025005
+ if (sscf && sscf->ssl.ctx) {
+#else
+ if (sscf && sscf->listen) {
+#endif
+ if (conf->srv.ssl_client_hello_src.len == 0) {
+ conf->srv.ssl_client_hello_src = prev->srv.ssl_client_hello_src;
+ conf->srv.ssl_client_hello_src_key =
+ prev->srv.ssl_client_hello_src_key;
+ conf->srv.ssl_client_hello_handler =
+ prev->srv.ssl_client_hello_handler;
+ }
+
+ if (conf->srv.ssl_client_hello_src.len) {
+ if (sscf->ssl.ctx == NULL) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no ssl configured for the server");
+
+ return NGX_CONF_ERROR;
+ }
+#ifdef LIBRESSL_VERSION_NUMBER
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "LibreSSL does not support by "
+ "ssl_client_hello_by_lua*");
+ return NGX_CONF_ERROR;
+
+#else
+
+# ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB
+
+ SSL_CTX_set_client_hello_cb(sscf->ssl.ctx,
+ ngx_stream_lua_ssl_client_hello_handler,
+ NULL);
+
+# else
+
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "OpenSSL too old to support "
+ "ssl_client_hello_by_lua*");
+ return NGX_CONF_ERROR;
+
+# endif
+#endif
+ }
+
+ if (conf->srv.ssl_cert_src.len == 0) {
+ conf->srv.ssl_cert_src = prev->srv.ssl_cert_src;
+ conf->srv.ssl_cert_src_key = prev->srv.ssl_cert_src_key;
+ conf->srv.ssl_cert_handler = prev->srv.ssl_cert_handler;
+ }
+
+ if (conf->srv.ssl_cert_src.len) {
+ if (sscf->ssl.ctx == NULL) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no ssl configured for the server");
+
+ return NGX_CONF_ERROR;
+ }
+
+#ifdef LIBRESSL_VERSION_NUMBER
+
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "LibreSSL is not supported by ssl_certificate_by_lua*");
+ return NGX_CONF_ERROR;
+
+#else
+
+# if OPENSSL_VERSION_NUMBER >= 0x1000205fL
+
+ SSL_CTX_set_cert_cb(sscf->ssl.ctx, ngx_stream_lua_ssl_cert_handler, NULL);
+
+# else
+
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "OpenSSL too old to support ssl_certificate_by_lua*");
+ return NGX_CONF_ERROR;
+
+# endif
+
+#endif
+ }
+ }
+
+
+#endif /* NGX_STREAM_SSL */
+
+#if (NGX_STREAM_SSL)
+
+ ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols,
+ NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3
+ |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1
+ |NGX_SSL_TLSv1_2);
+
+ ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers,
+ "DEFAULT");
+
+ ngx_conf_merge_uint_value(conf->ssl_verify_depth,
+ prev->ssl_verify_depth, 1);
+ ngx_conf_merge_ptr_value(conf->ssl_certificates,
+ prev->ssl_certificates, NULL);
+ ngx_conf_merge_ptr_value(conf->ssl_certificate_keys,
+ prev->ssl_certificate_keys, NULL);
+ ngx_conf_merge_str_value(conf->ssl_trusted_certificate,
+ prev->ssl_trusted_certificate, "");
+ ngx_conf_merge_str_value(conf->ssl_crl, prev->ssl_crl, "");
+#if (nginx_version >= 1019004)
+ ngx_conf_merge_ptr_value(conf->ssl_conf_commands, prev->ssl_conf_commands,
+ NULL);
+#endif
+
+ if (ngx_stream_lua_set_ssl(cf, conf) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+#endif
+
+ ngx_conf_merge_value(conf->enable_code_cache, prev->enable_code_cache, 1);
+ ngx_conf_merge_value(conf->check_client_abort, prev->check_client_abort, 0);
+
+ ngx_conf_merge_msec_value(conf->keepalive_timeout,
+ prev->keepalive_timeout, 60000);
+
+ ngx_conf_merge_msec_value(conf->connect_timeout,
+ prev->connect_timeout, 60000);
+
+ ngx_conf_merge_msec_value(conf->send_timeout,
+ prev->send_timeout, 60000);
+
+ ngx_conf_merge_msec_value(conf->read_timeout,
+ prev->read_timeout, 60000);
+
+ ngx_conf_merge_size_value(conf->send_lowat,
+ prev->send_lowat, 0);
+
+ ngx_conf_merge_size_value(conf->buffer_size,
+ prev->buffer_size,
+ (size_t) ngx_pagesize);
+
+ ngx_conf_merge_uint_value(conf->pool_size, prev->pool_size, 30);
+
+ ngx_conf_merge_value(conf->log_socket_errors, prev->log_socket_errors, 1);
+
+ if (conf->preread_src.value.len == 0) {
+ conf->preread_src = prev->preread_src;
+ conf->preread_handler = prev->preread_handler;
+ conf->preread_src_key = prev->preread_src_key;
+ conf->preread_chunkname = prev->preread_chunkname;
+ }
+
+ if (conf->log_src.value.len == 0) {
+ conf->log_src = prev->log_src;
+ conf->log_handler = prev->log_handler;
+ conf->log_src_key = prev->log_src_key;
+ conf->log_chunkname = prev->log_chunkname;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+
+
+#if (NGX_STREAM_SSL)
+
+static ngx_int_t
+ngx_stream_lua_set_ssl(ngx_conf_t *cf, ngx_stream_lua_srv_conf_t *lscf)
+{
+ ngx_pool_cleanup_t *cln;
+
+ lscf->ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));
+ if (lscf->ssl == NULL) {
+ return NGX_ERROR;
+ }
+
+ lscf->ssl->log = cf->log;
+
+ if (lscf->ssl_certificates) {
+ if (lscf->ssl_certificate_keys == NULL
+ || lscf->ssl_certificate_keys->nelts
+ < lscf->ssl_certificates->nelts)
+ {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no \"lua_ssl_certificate_key\" is defined "
+ "for certificate \"%V\"",
+ ((ngx_str_t *) lscf->ssl_certificates->elts)
+ + lscf->ssl_certificates->nelts - 1);
+ return NGX_ERROR;
+ }
+ }
+
+ if (ngx_ssl_create(lscf->ssl, lscf->ssl_protocols, NULL) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ cln = ngx_pool_cleanup_add(cf->pool, 0);
+ if (cln == NULL) {
+ return NGX_ERROR;
+ }
+
+ cln->handler = ngx_ssl_cleanup_ctx;
+ cln->data = lscf->ssl;
+
+ if (SSL_CTX_set_cipher_list(lscf->ssl->ctx,
+ (const char *) lscf->ssl_ciphers.data)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,
+ "SSL_CTX_set_cipher_list(\"%V\") failed",
+ &lscf->ssl_ciphers);
+ return NGX_ERROR;
+ }
+
+ if (lscf->ssl_certificates
+ && ngx_ssl_certificates(cf, lscf->ssl,
+ lscf->ssl_certificates,
+ lscf->ssl_certificate_keys,
+ NULL)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (lscf->ssl_trusted_certificate.len
+ && ngx_ssl_trusted_certificate(cf, lscf->ssl,
+ &lscf->ssl_trusted_certificate,
+ lscf->ssl_verify_depth)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ dd("ssl crl: %.*s", (int) lscf->ssl_crl.len, lscf->ssl_crl.data);
+
+ if (ngx_ssl_crl(cf, lscf->ssl, &lscf->ssl_crl) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+#if (nginx_version >= 1019004)
+ if (ngx_ssl_conf_commands(cf, lscf->ssl, lscf->ssl_conf_commands)
+ != NGX_OK) {
+ return NGX_ERROR;
+ }
+#endif
+ return NGX_OK;
+}
+
+#if (nginx_version >= 1019004)
+static char *
+ngx_stream_lua_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data)
+{
+#ifndef SSL_CONF_FLAG_FILE
+ return "is not supported on this platform";
+#endif
+
+ return NGX_CONF_OK;
+}
+#endif
+#endif /* NGX_STREAM_SSL */
+
+
+static char *
+ngx_stream_lua_malloc_trim(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+#if (NGX_STREAM_LUA_HAVE_MALLOC_TRIM)
+
+ ngx_int_t nreqs;
+ ngx_str_t *value;
+
+ ngx_stream_lua_main_conf_t *lmcf = conf;
+
+ value = cf->args->elts;
+
+ nreqs = ngx_atoi(value[1].data, value[1].len);
+ if (nreqs == NGX_ERROR) {
+ return "invalid number in the 1st argument";
+ }
+
+ lmcf->malloc_trim_cycle = (ngx_uint_t) nreqs;
+
+ if (nreqs == 0) {
+ return NGX_CONF_OK;
+ }
+
+ lmcf->requires_log = 1;
+
+#else
+
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "lua_malloc_trim is not supported "
+ "on this platform, ignored");
+
+#endif
+ return NGX_CONF_OK;
+}
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_output.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_output.c
new file mode 100644
index 0000000..cfb33cb
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_output.c
@@ -0,0 +1,739 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_output.c.tt2
+ */
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_output.h"
+#include "ngx_stream_lua_util.h"
+#include "ngx_stream_lua_contentby.h"
+#include <math.h>
+
+
+static int ngx_stream_lua_ngx_say(lua_State *L);
+static int ngx_stream_lua_ngx_print(lua_State *L);
+static int ngx_stream_lua_ngx_flush(lua_State *L);
+static int ngx_stream_lua_ngx_eof(lua_State *L);
+
+
+static int ngx_stream_lua_ngx_echo(lua_State *L, unsigned newline);
+static void ngx_stream_lua_flush_cleanup(void *data);
+
+
+static int
+ngx_stream_lua_ngx_print(lua_State *L)
+{
+ dd("calling lua print");
+ return ngx_stream_lua_ngx_echo(L, 0);
+}
+
+
+static int
+ngx_stream_lua_ngx_say(lua_State *L)
+{
+ dd("calling");
+ return ngx_stream_lua_ngx_echo(L, 1);
+}
+
+
+static int
+ngx_stream_lua_ngx_echo(lua_State *L, unsigned newline)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+ const char *p;
+ size_t len;
+ size_t size;
+ ngx_buf_t *b;
+ ngx_chain_t *cl;
+ ngx_int_t rc;
+ int i;
+ int nargs;
+ int type;
+ const char *msg;
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request object found");
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+
+ if (ctx == NULL) {
+ return luaL_error(L, "no request ctx found");
+ }
+
+ if (r->connection->type == SOCK_DGRAM) {
+ return luaL_error(L, "API disabled in the current context");
+ }
+
+ ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT
+ | NGX_STREAM_LUA_CONTEXT_PREREAD);
+
+
+ if (ctx->eof) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "seen eof");
+ return 2;
+ }
+
+ nargs = lua_gettop(L);
+ size = 0;
+
+ for (i = 1; i <= nargs; i++) {
+
+ type = lua_type(L, i);
+
+ switch (type) {
+ case LUA_TNUMBER:
+ case LUA_TSTRING:
+
+ lua_tolstring(L, i, &len);
+ size += len;
+ break;
+
+ case LUA_TNIL:
+
+ size += sizeof("nil") - 1;
+ break;
+
+ case LUA_TBOOLEAN:
+
+ if (lua_toboolean(L, i)) {
+ size += sizeof("true") - 1;
+
+ } else {
+ size += sizeof("false") - 1;
+ }
+
+ break;
+
+ case LUA_TTABLE:
+
+ size += ngx_stream_lua_calc_strlen_in_table(L, i, i,
+ 0 /* strict */);
+ break;
+
+ case LUA_TLIGHTUSERDATA:
+
+ dd("userdata: %p", lua_touserdata(L, i));
+
+ if (lua_touserdata(L, i) == NULL) {
+ size += sizeof("null") - 1;
+ break;
+ }
+
+ continue;
+
+ default:
+
+ msg = lua_pushfstring(L, "string, number, boolean, nil, "
+ "ngx.null, or array table expected, "
+ "but got %s", lua_typename(L, type));
+
+ return luaL_argerror(L, i, msg);
+ }
+ }
+
+ if (newline) {
+ size += sizeof("\n") - 1;
+ }
+
+ if (size == 0) {
+
+ lua_pushinteger(L, 1);
+ return 1;
+ }
+
+ ctx->seen_body_data = 1;
+
+ cl = ngx_stream_lua_chain_get_free_buf(r->connection->log, r->pool,
+ &ctx->free_bufs, size);
+
+ if (cl == NULL) {
+ return luaL_error(L, "no memory");
+ }
+
+ b = cl->buf;
+
+ for (i = 1; i <= nargs; i++) {
+ type = lua_type(L, i);
+ switch (type) {
+ case LUA_TNUMBER:
+ case LUA_TSTRING:
+ p = lua_tolstring(L, i, &len);
+ b->last = ngx_copy(b->last, (u_char *) p, len);
+ break;
+
+ case LUA_TNIL:
+ *b->last++ = 'n';
+ *b->last++ = 'i';
+ *b->last++ = 'l';
+ break;
+
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, i)) {
+ *b->last++ = 't';
+ *b->last++ = 'r';
+ *b->last++ = 'u';
+ *b->last++ = 'e';
+
+ } else {
+ *b->last++ = 'f';
+ *b->last++ = 'a';
+ *b->last++ = 'l';
+ *b->last++ = 's';
+ *b->last++ = 'e';
+ }
+
+ break;
+
+ case LUA_TTABLE:
+ b->last = ngx_stream_lua_copy_str_in_table(L, i, b->last);
+ break;
+
+ case LUA_TLIGHTUSERDATA:
+ *b->last++ = 'n';
+ *b->last++ = 'u';
+ *b->last++ = 'l';
+ *b->last++ = 'l';
+ break;
+
+ default:
+ return luaL_error(L, "impossible to reach here");
+ }
+ }
+
+ if (newline) {
+ *b->last++ = '\n';
+ }
+
+#if 0
+ if (b->last != b->end) {
+ return luaL_error(L, "buffer error: %p != %p", b->last, b->end);
+ }
+#endif
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ newline ? "lua say response" : "lua print response");
+
+ rc = ngx_stream_lua_send_chain_link(r, ctx, cl);
+
+ if (rc == NGX_ERROR) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "nginx output filter error");
+ return 2;
+ }
+
+ dd("downstream write: %d, buf len: %d", (int) rc,
+ (int) (b->last - b->pos));
+
+ lua_pushinteger(L, 1);
+ return 1;
+}
+
+
+size_t
+ngx_stream_lua_calc_strlen_in_table(lua_State *L, int index, int arg_i,
+ unsigned strict)
+{
+ double key;
+ int max;
+ int i;
+ int type;
+ size_t size;
+ size_t len;
+ const char *msg;
+
+ if (index < 0) {
+ index = lua_gettop(L) + index + 1;
+ }
+
+ dd("table index: %d", index);
+
+ max = 0;
+
+ lua_pushnil(L); /* stack: table key */
+ while (lua_next(L, index) != 0) { /* stack: table key value */
+ dd("key type: %s", luaL_typename(L, -2));
+
+ if (lua_type(L, -2) == LUA_TNUMBER) {
+
+ key = lua_tonumber(L, -2);
+
+ dd("key value: %d", (int) key);
+
+ if (floor(key) == key && key >= 1) {
+ if (key > max) {
+ max = (int) key;
+ }
+
+ lua_pop(L, 1); /* stack: table key */
+ continue;
+ }
+ }
+
+ /* not an array (non positive integer key) */
+ lua_pop(L, 2); /* stack: table */
+
+ luaL_argerror(L, arg_i, "non-array table found");
+ return 0;
+ }
+
+ size = 0;
+
+ for (i = 1; i <= max; i++) {
+ lua_rawgeti(L, index, i); /* stack: table value */
+ type = lua_type(L, -1);
+
+ switch (type) {
+ case LUA_TNUMBER:
+ case LUA_TSTRING:
+
+ lua_tolstring(L, -1, &len);
+ size += len;
+ break;
+
+ case LUA_TNIL:
+
+ if (strict) {
+ goto bad_type;
+ }
+
+ size += sizeof("nil") - 1;
+ break;
+
+ case LUA_TBOOLEAN:
+
+ if (strict) {
+ goto bad_type;
+ }
+
+ if (lua_toboolean(L, -1)) {
+ size += sizeof("true") - 1;
+
+ } else {
+ size += sizeof("false") - 1;
+ }
+
+ break;
+
+ case LUA_TTABLE:
+
+ size += ngx_stream_lua_calc_strlen_in_table(L, -1, arg_i,
+ strict);
+ break;
+
+ case LUA_TLIGHTUSERDATA:
+
+ if (strict) {
+ goto bad_type;
+ }
+
+ if (lua_touserdata(L, -1) == NULL) {
+ size += sizeof("null") - 1;
+ break;
+ }
+
+ continue;
+
+ default:
+
+bad_type:
+
+ msg = lua_pushfstring(L, "bad data type %s found",
+ lua_typename(L, type));
+ return luaL_argerror(L, arg_i, msg);
+ }
+
+ lua_pop(L, 1); /* stack: table */
+ }
+
+ return size;
+}
+
+
+u_char *
+ngx_stream_lua_copy_str_in_table(lua_State *L, int index, u_char *dst)
+{
+ double key;
+ int max;
+ int i;
+ int type;
+ size_t len;
+ u_char *p;
+
+ if (index < 0) {
+ index = lua_gettop(L) + index + 1;
+ }
+
+ max = 0;
+
+ lua_pushnil(L); /* stack: table key */
+ while (lua_next(L, index) != 0) { /* stack: table key value */
+ key = lua_tonumber(L, -2);
+ if (key > max) {
+ max = (int) key;
+ }
+
+ lua_pop(L, 1); /* stack: table key */
+ }
+
+ for (i = 1; i <= max; i++) {
+ lua_rawgeti(L, index, i); /* stack: table value */
+ type = lua_type(L, -1);
+ switch (type) {
+ case LUA_TNUMBER:
+ case LUA_TSTRING:
+ p = (u_char *) lua_tolstring(L, -1, &len);
+ dst = ngx_copy(dst, p, len);
+ break;
+
+ case LUA_TNIL:
+ *dst++ = 'n';
+ *dst++ = 'i';
+ *dst++ = 'l';
+ break;
+
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, -1)) {
+ *dst++ = 't';
+ *dst++ = 'r';
+ *dst++ = 'u';
+ *dst++ = 'e';
+
+ } else {
+ *dst++ = 'f';
+ *dst++ = 'a';
+ *dst++ = 'l';
+ *dst++ = 's';
+ *dst++ = 'e';
+ }
+
+ break;
+
+ case LUA_TTABLE:
+ dst = ngx_stream_lua_copy_str_in_table(L, -1, dst);
+ break;
+
+ case LUA_TLIGHTUSERDATA:
+
+ *dst++ = 'n';
+ *dst++ = 'u';
+ *dst++ = 'l';
+ *dst++ = 'l';
+ break;
+
+ default:
+ luaL_error(L, "impossible to reach here");
+ return NULL;
+ }
+
+ lua_pop(L, 1); /* stack: table */
+ }
+
+ return dst;
+}
+
+
+/**
+ * Force flush out response content
+ * */
+static int
+ngx_stream_lua_ngx_flush(lua_State *L)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_chain_t *cl;
+ ngx_int_t rc;
+ int n;
+ unsigned wait = 0;
+ ngx_event_t *wev;
+
+ ngx_stream_lua_srv_conf_t *cllscf;
+
+ ngx_stream_lua_co_ctx_t *coctx;
+
+ n = lua_gettop(L);
+ if (n > 1) {
+ return luaL_error(L, "attempt to pass %d arguments, but accepted 0 "
+ "or 1", n);
+ }
+
+ r = ngx_stream_lua_get_req(L);
+
+ if (n == 1) {
+ luaL_checktype(L, 1, LUA_TBOOLEAN);
+ wait = lua_toboolean(L, 1);
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return luaL_error(L, "no request ctx found");
+ }
+
+ if (r->connection->type == SOCK_DGRAM) {
+ return luaL_error(L, "API disabled in the current context");
+ }
+
+ ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT
+ | NGX_STREAM_LUA_CONTEXT_PREREAD);
+
+
+ coctx = ctx->cur_co_ctx;
+ if (coctx == NULL) {
+ return luaL_error(L, "no co ctx found");
+ }
+
+
+ if (ctx->eof) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "seen eof");
+ return 2;
+ }
+
+
+ cl = ngx_stream_lua_get_flush_chain(r, ctx);
+ if (cl == NULL) {
+ return luaL_error(L, "no memory");
+ }
+
+ rc = ngx_stream_lua_send_chain_link(r, ctx, cl);
+
+ dd("send chain: %d", (int) rc);
+
+ if (rc == NGX_ERROR) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "nginx output filter error");
+ return 2;
+ }
+
+ dd("wait:%d, rc:%d, buffered:0x%x", wait, (int) rc,
+ r->connection->buffered);
+
+ wev = r->connection->write;
+
+ if (wait && (r->connection->buffered || wev->delayed))
+ {
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua flush requires waiting: buffered 0x%uxd, "
+ "delayed:%d", (unsigned) r->connection->buffered,
+ wev->delayed);
+
+ coctx->flushing = 1;
+ ctx->flushing_coros++;
+
+ if (ctx->entered_content_phase) {
+ /* mimic ngx_http_set_write_handler */
+ r->write_event_handler = ngx_stream_lua_content_wev_handler;
+
+ } else {
+ r->write_event_handler = ngx_stream_lua_core_run_phases;
+ }
+
+ cllscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module);
+
+ if (!wev->delayed) {
+ ngx_add_timer(wev, cllscf->send_timeout);
+ }
+
+
+ if (ngx_handle_write_event(wev, cllscf->send_lowat) != NGX_OK) {
+ if (wev->timer_set) {
+ wev->delayed = 0;
+ ngx_del_timer(wev);
+ }
+
+ lua_pushnil(L);
+ lua_pushliteral(L, "connection broken");
+ return 2;
+ }
+
+ ngx_stream_lua_cleanup_pending_operation(ctx->cur_co_ctx);
+ coctx->cleanup = ngx_stream_lua_flush_cleanup;
+ coctx->data = r;
+
+ return lua_yield(L, 0);
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua flush asynchronously");
+
+ lua_pushinteger(L, 1);
+ return 1;
+}
+
+
+/**
+ * Send last_buf, terminate output stream
+ * */
+static int
+ngx_stream_lua_ngx_eof(lua_State *L)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_int_t rc;
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request object found");
+ }
+
+ if (lua_gettop(L) != 0) {
+ return luaL_error(L, "no argument is expected");
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return luaL_error(L, "no ctx found");
+ }
+
+
+ if (ctx->eof) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "seen eof");
+ return 2;
+ }
+
+ if (r->connection->type == SOCK_DGRAM) {
+ return luaL_error(L, "API disabled in the current context");
+ }
+
+ ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT
+ | NGX_STREAM_LUA_CONTEXT_PREREAD);
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua send eof");
+
+ rc = ngx_stream_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */);
+
+ dd("send chain: %d", (int) rc);
+
+ if (rc == NGX_ERROR) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "nginx output filter error");
+ return 2;
+ }
+
+ lua_pushinteger(L, 1);
+ return 1;
+}
+
+
+void
+ngx_stream_lua_inject_output_api(lua_State *L)
+{
+
+ lua_pushcfunction(L, ngx_stream_lua_ngx_print);
+ lua_setfield(L, -2, "print");
+
+ lua_pushcfunction(L, ngx_stream_lua_ngx_say);
+ lua_setfield(L, -2, "say");
+
+ lua_pushcfunction(L, ngx_stream_lua_ngx_flush);
+ lua_setfield(L, -2, "flush");
+
+ lua_pushcfunction(L, ngx_stream_lua_ngx_eof);
+ lua_setfield(L, -2, "eof");
+}
+
+
+
+
+ngx_int_t
+ngx_stream_lua_flush_resume_helper(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx)
+{
+ int n;
+ lua_State *vm;
+ ngx_int_t rc;
+ ngx_uint_t nreqs;
+ ngx_connection_t *c;
+
+ c = r->connection;
+
+ ctx->cur_co_ctx->cleanup = NULL;
+
+ /* push the return values */
+
+ if (c->timedout) {
+ lua_pushnil(ctx->cur_co_ctx->co);
+ lua_pushliteral(ctx->cur_co_ctx->co, "timeout");
+ n = 2;
+
+ } else if (c->error) {
+ lua_pushnil(ctx->cur_co_ctx->co);
+ lua_pushliteral(ctx->cur_co_ctx->co, "client aborted");
+ n = 2;
+
+ } else {
+ lua_pushinteger(ctx->cur_co_ctx->co, 1);
+ n = 1;
+ }
+
+ vm = ngx_stream_lua_get_lua_vm(r, ctx);
+ nreqs = c->requests;
+
+ rc = ngx_stream_lua_run_thread(vm, r, ctx, n);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua run thread returned %d", rc);
+
+ if (rc == NGX_AGAIN) {
+ return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs);
+ }
+
+ if (rc == NGX_DONE) {
+ ngx_stream_lua_finalize_request(r, NGX_DONE);
+ return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs);
+ }
+
+ /* rc == NGX_ERROR || rc >= NGX_OK */
+
+ if (ctx->entered_content_phase) {
+ ngx_stream_lua_finalize_request(r, rc);
+ return NGX_DONE;
+ }
+
+ return rc;
+}
+
+
+static void
+ngx_stream_lua_flush_cleanup(void *data)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_event_t *wev;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx = data;
+
+ coctx->flushing = 0;
+
+ r = coctx->data;
+ if (r == NULL) {
+ return;
+ }
+
+ wev = r->connection->write;
+
+ if (wev && wev->timer_set) {
+ ngx_del_timer(wev);
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return;
+ }
+
+ ctx->flushing_coros--;
+}
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_output.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_output.h
new file mode 100644
index 0000000..6cf941d
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_output.h
@@ -0,0 +1,36 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_output.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_OUTPUT_H_INCLUDED_
+#define _NGX_STREAM_LUA_OUTPUT_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+void ngx_stream_lua_inject_output_api(lua_State *L);
+
+size_t ngx_stream_lua_calc_strlen_in_table(lua_State *L, int index, int arg_i,
+ unsigned strict);
+
+u_char *ngx_stream_lua_copy_str_in_table(lua_State *L, int index, u_char *dst);
+
+ngx_int_t ngx_stream_lua_flush_resume_helper(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx);
+
+
+#endif /* _NGX_STREAM_LUA_OUTPUT_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_pcrefix.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_pcrefix.c
new file mode 100644
index 0000000..2e8a9f8
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_pcrefix.c
@@ -0,0 +1,195 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_pcrefix.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_pcrefix.h"
+#include "stdio.h"
+
+#if (NGX_PCRE)
+
+static ngx_pool_t *ngx_stream_lua_pcre_pool = NULL;
+
+#if (NGX_PCRE2)
+static ngx_uint_t ngx_regex_direct_alloc;
+#else
+static void *(*old_pcre_malloc)(size_t);
+static void (*old_pcre_free)(void *ptr);
+#endif
+
+
+/* XXX: work-around to nginx regex subsystem, must init a memory pool
+ * to use PCRE functions. As PCRE still has memory-leaking problems,
+ * and nginx overwrote pcre_malloc/free hooks with its own static
+ * functions, so nobody else can reuse nginx regex subsystem... */
+#if (NGX_PCRE2)
+
+void *
+ngx_stream_lua_pcre_malloc(size_t size, void *data)
+{
+ dd("lua pcre pool is %p", ngx_stream_lua_pcre_pool);
+
+ if (ngx_stream_lua_pcre_pool) {
+ return ngx_palloc(ngx_stream_lua_pcre_pool, size);
+ }
+
+ if (ngx_regex_direct_alloc) {
+ return ngx_alloc(size, ngx_cycle->log);
+ }
+
+ fprintf(stderr, "error: lua pcre malloc failed due to empty pcre pool");
+
+ return NULL;
+}
+
+
+void
+ngx_stream_lua_pcre_free(void *ptr, void *data)
+{
+ dd("lua pcre pool is %p", ngx_stream_lua_pcre_pool);
+
+ if (ngx_stream_lua_pcre_pool) {
+ ngx_pfree(ngx_stream_lua_pcre_pool, ptr);
+ return;
+ }
+
+ if (ngx_regex_direct_alloc) {
+ ngx_free(ptr);
+ return;
+ }
+
+ fprintf(stderr, "error: lua pcre free failed due to empty pcre pool");
+}
+
+#else
+
+static void *
+ngx_stream_lua_pcre_malloc(size_t size)
+{
+ dd("lua pcre pool is %p", ngx_stream_lua_pcre_pool);
+
+ if (ngx_stream_lua_pcre_pool) {
+ return ngx_palloc(ngx_stream_lua_pcre_pool, size);
+ }
+
+ fprintf(stderr, "error: lua pcre malloc failed due to empty pcre pool");
+
+ return NULL;
+}
+
+
+static void
+ngx_stream_lua_pcre_free(void *ptr)
+{
+ dd("lua pcre pool is %p", ngx_stream_lua_pcre_pool);
+
+ if (ngx_stream_lua_pcre_pool) {
+ ngx_pfree(ngx_stream_lua_pcre_pool, ptr);
+ return;
+ }
+
+ fprintf(stderr, "error: lua pcre free failed due to empty pcre pool");
+}
+
+#endif
+
+
+#if (NGX_PCRE2)
+
+ngx_pool_t *
+ngx_stream_lua_pcre_malloc_init(ngx_pool_t *pool)
+{
+ ngx_pool_t *old_pool;
+
+ dd("lua pcre pool was %p", ngx_stream_lua_pcre_pool);
+
+ ngx_regex_direct_alloc = (pool == NULL) ? 1 : 0;
+
+ old_pool = ngx_stream_lua_pcre_pool;
+ ngx_stream_lua_pcre_pool = pool;
+
+ dd("lua pcre pool is %p", ngx_stream_lua_pcre_pool);
+
+ return old_pool;
+}
+
+
+void
+ngx_stream_lua_pcre_malloc_done(ngx_pool_t *old_pool)
+{
+ dd("lua pcre pool was %p", ngx_stream_lua_pcre_pool);
+
+ ngx_stream_lua_pcre_pool = old_pool;
+ ngx_regex_direct_alloc = 0;
+
+ dd("lua pcre pool is %p", ngx_stream_lua_pcre_pool);
+}
+
+#else
+
+ngx_pool_t *
+ngx_stream_lua_pcre_malloc_init(ngx_pool_t *pool)
+{
+ ngx_pool_t *old_pool;
+
+ if (pcre_malloc != ngx_stream_lua_pcre_malloc) {
+
+ dd("overriding nginx pcre malloc and free");
+
+ ngx_stream_lua_pcre_pool = pool;
+
+ old_pcre_malloc = pcre_malloc;
+ old_pcre_free = pcre_free;
+
+ pcre_malloc = ngx_stream_lua_pcre_malloc;
+ pcre_free = ngx_stream_lua_pcre_free;
+
+ return NULL;
+ }
+
+ dd("lua pcre pool was %p", ngx_stream_lua_pcre_pool);
+
+ old_pool = ngx_stream_lua_pcre_pool;
+ ngx_stream_lua_pcre_pool = pool;
+
+ dd("lua pcre pool is %p", ngx_stream_lua_pcre_pool);
+
+ return old_pool;
+}
+
+
+void
+ngx_stream_lua_pcre_malloc_done(ngx_pool_t *old_pool)
+{
+ dd("lua pcre pool was %p", ngx_stream_lua_pcre_pool);
+
+ ngx_stream_lua_pcre_pool = old_pool;
+
+ dd("lua pcre pool is %p", ngx_stream_lua_pcre_pool);
+
+ if (old_pool == NULL) {
+ pcre_malloc = old_pcre_malloc;
+ pcre_free = old_pcre_free;
+ }
+}
+
+#endif
+#endif /* NGX_PCRE */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_pcrefix.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_pcrefix.h
new file mode 100644
index 0000000..885e12d
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_pcrefix.h
@@ -0,0 +1,36 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_pcrefix.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_PCREFIX_H_INCLUDED_
+#define _NGX_STREAM_LUA_PCREFIX_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+#if (NGX_PCRE)
+ngx_pool_t *ngx_stream_lua_pcre_malloc_init(ngx_pool_t *pool);
+void ngx_stream_lua_pcre_malloc_done(ngx_pool_t *old_pool);
+
+#if (NGX_PCRE2)
+void *ngx_stream_lua_pcre_malloc(size_t size, void *data);
+void ngx_stream_lua_pcre_free(void *ptr, void *data);
+#endif
+#endif
+
+
+#endif /* _NGX_STREAM_LUA_PCREFIX_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_phase.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_phase.c
new file mode 100644
index 0000000..db1f765
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_phase.c
@@ -0,0 +1,99 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_phase.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_phase.h"
+#include "ngx_stream_lua_util.h"
+#include "ngx_stream_lua_ctx.h"
+
+
+
+static int
+ngx_stream_lua_ngx_get_phase(lua_State *L)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+
+ r = ngx_stream_lua_get_req(L);
+
+ /* If we have no request object, assume we are called from the "init"
+ * phase. */
+
+ if (r == NULL) {
+ lua_pushliteral(L, "init");
+ return 1;
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return luaL_error(L, "no request ctx found");
+ }
+
+ switch (ctx->context) {
+ case NGX_STREAM_LUA_CONTEXT_INIT_WORKER:
+ lua_pushliteral(L, "init_worker");
+ break;
+
+ case NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO:
+ lua_pushliteral(L, "ssl_client_hello");
+ break;
+
+ case NGX_STREAM_LUA_CONTEXT_SSL_CERT:
+ lua_pushliteral(L, "ssl_cert");
+ break;
+
+ case NGX_STREAM_LUA_CONTEXT_PREREAD:
+ lua_pushliteral(L, "preread");
+ break;
+
+ case NGX_STREAM_LUA_CONTEXT_CONTENT:
+ lua_pushliteral(L, "content");
+ break;
+
+ case NGX_STREAM_LUA_CONTEXT_LOG:
+ lua_pushliteral(L, "log");
+ break;
+
+ case NGX_STREAM_LUA_CONTEXT_TIMER:
+ lua_pushliteral(L, "timer");
+ break;
+
+ case NGX_STREAM_LUA_CONTEXT_BALANCER:
+ lua_pushliteral(L, "balancer");
+ break;
+
+ default:
+ return luaL_error(L, "unknown phase: %#x", (int) ctx->context);
+ }
+
+ return 1;
+}
+
+
+void
+ngx_stream_lua_inject_phase_api(lua_State *L)
+{
+ lua_pushcfunction(L, ngx_stream_lua_ngx_get_phase);
+ lua_setfield(L, -2, "get_phase");
+}
+
+
+
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_phase.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_phase.h
new file mode 100644
index 0000000..f31cb89
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_phase.h
@@ -0,0 +1,21 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_phase.h.tt2
+ */
+
+#ifndef _NGX_STREAM_LUA_PHASE_H_INCLUDED_
+#define _NGX_STREAM_LUA_PHASE_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+void ngx_stream_lua_inject_phase_api(lua_State *L);
+
+
+#endif /* _NGX_STREAM_LUA_PHASE_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_prereadby.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_prereadby.c
new file mode 100644
index 0000000..61f27b5
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_prereadby.c
@@ -0,0 +1,335 @@
+
+/*
+ * Copyright (C) OpenResty Inc.
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include <nginx.h>
+#include "ngx_stream_lua_prereadby.h"
+#include "ngx_stream_lua_util.h"
+#include "ngx_stream_lua_exception.h"
+#include "ngx_stream_lua_cache.h"
+
+
+static ngx_int_t ngx_stream_lua_preread_by_chunk(lua_State *L,
+ ngx_stream_lua_request_t *r);
+
+
+ngx_int_t
+ngx_stream_lua_preread_handler(ngx_stream_session_t *s)
+{
+ ngx_int_t rc;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_srv_conf_t *lscf;
+ ngx_stream_lua_main_conf_t *lmcf;
+ ngx_stream_lua_request_t *r;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
+ "lua preread handler");
+
+ lmcf = ngx_stream_get_module_main_conf(s, ngx_stream_lua_module);
+
+ if (!lmcf->postponed_to_preread_phase_end) {
+ ngx_stream_core_main_conf_t *cmcf;
+ ngx_stream_phase_handler_t tmp;
+ ngx_stream_phase_handler_t *ph;
+ ngx_stream_phase_handler_t *cur_ph;
+ ngx_stream_phase_handler_t *last_ph;
+
+ lmcf->postponed_to_preread_phase_end = 1;
+
+ cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);
+
+ ph = cmcf->phase_engine.handlers;
+ cur_ph = &ph[s->phase_handler];
+ last_ph = &ph[cur_ph->next - 1];
+
+ if (cur_ph < last_ph) {
+ tmp = *cur_ph;
+
+ ngx_memmove(cur_ph, cur_ph + 1, (last_ph - cur_ph)
+ * sizeof (ngx_stream_phase_handler_t));
+
+ *last_ph = tmp;
+
+ s->phase_handler--; /* redo the current ph */
+
+ return NGX_DECLINED;
+ }
+ }
+
+ lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_lua_module);
+
+ if (lscf->preread_handler == NULL) {
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
+ "no preread handler found");
+ return NGX_DECLINED;
+ }
+
+ ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module);
+
+ dd("ctx = %p", ctx);
+
+ if (ctx == NULL) {
+ ctx = ngx_stream_lua_create_ctx(s);
+ if (ctx == NULL) {
+ return NGX_STREAM_INTERNAL_SERVER_ERROR;
+ }
+ }
+
+ r = ctx->request;
+
+ dd("entered? %d", (int) ctx->entered_preread_phase);
+
+ if (ctx->entered_preread_phase) {
+ dd("calling wev handler");
+ rc = ctx->resume_handler(r);
+ dd("wev handler returns %d", (int) rc);
+
+ if (rc == NGX_ERROR || rc > NGX_OK) {
+ ngx_stream_lua_finalize_request(ctx->request, rc);
+ return NGX_DONE;
+ }
+
+ if (rc == NGX_DONE && ctx->peek_needs_more_data) {
+ return NGX_AGAIN;
+ }
+
+ if (rc == NGX_OK || rc == NGX_DONE) {
+ return rc;
+ }
+
+ return NGX_DECLINED;
+ }
+
+ r->connection->read->handler = ngx_stream_lua_request_handler;
+ r->connection->write->handler = ngx_stream_lua_request_handler;
+
+ dd("calling preread handler");
+ rc = lscf->preread_handler(r);
+
+ if (rc == NGX_ERROR || rc > NGX_OK) {
+ ngx_stream_lua_finalize_request(ctx->request, rc);
+ return NGX_DONE;
+ }
+
+ return rc;
+}
+
+
+ngx_int_t
+ngx_stream_lua_preread_handler_inline(ngx_stream_lua_request_t *r)
+{
+ ngx_int_t rc;
+ lua_State *L;
+ ngx_stream_lua_srv_conf_t *lscf;
+
+ lscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module);
+
+ L = ngx_stream_lua_get_lua_vm(r, NULL);
+
+ /* load Lua inline script (w/ cache) sp = 1 */
+ rc = ngx_stream_lua_cache_loadbuffer(r->connection->log, L,
+ lscf->preread_src.value.data,
+ lscf->preread_src.value.len,
+ lscf->preread_src_key,
+ (const char *)
+ lscf->preread_chunkname);
+
+ if (rc != NGX_OK) {
+ return NGX_STREAM_INTERNAL_SERVER_ERROR;
+ }
+
+ return ngx_stream_lua_preread_by_chunk(L, r);
+}
+
+
+ngx_int_t
+ngx_stream_lua_preread_handler_file(ngx_stream_lua_request_t *r)
+{
+ u_char *script_path;
+ ngx_int_t rc;
+ ngx_str_t eval_src;
+ lua_State *L;
+ ngx_stream_lua_srv_conf_t *lscf;
+
+ lscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module);
+
+ /* Eval nginx variables in code path string first */
+ if (ngx_stream_complex_value(r->session, &lscf->preread_src, &eval_src)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ script_path = ngx_stream_lua_rebase_path(r->pool, eval_src.data,
+ eval_src.len);
+
+ if (script_path == NULL) {
+ return NGX_ERROR;
+ }
+
+ L = ngx_stream_lua_get_lua_vm(r, NULL);
+
+ /* load Lua script file (w/ cache) sp = 1 */
+ rc = ngx_stream_lua_cache_loadfile(r->connection->log, L, script_path,
+ lscf->preread_src_key);
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ /* make sure we have a valid code chunk */
+ ngx_stream_lua_assert(lua_isfunction(L, -1));
+
+ return ngx_stream_lua_preread_by_chunk(L, r);
+}
+
+
+static ngx_int_t
+ngx_stream_lua_preread_by_chunk(lua_State *L, ngx_stream_lua_request_t *r)
+{
+ int co_ref;
+ ngx_int_t rc;
+ lua_State *co;
+ ngx_event_t *rev;
+ ngx_connection_t *c;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_cleanup_t *cln;
+
+ ngx_stream_lua_srv_conf_t *lscf;
+
+ /* {{{ new coroutine to handle request */
+ co = ngx_stream_lua_new_thread(r, L, &co_ref);
+
+ if (co == NULL) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "lua: failed to create new coroutine "
+ "to handle request");
+
+ return NGX_STREAM_INTERNAL_SERVER_ERROR;
+ }
+
+ /* move code closure to new coroutine */
+ lua_xmove(L, co, 1);
+
+#ifndef OPENRESTY_LUAJIT
+ /* set closure's env table to new coroutine's globals table */
+ ngx_stream_lua_get_globals_table(co);
+ lua_setfenv(co, -2);
+#endif
+
+ /* save nginx request in coroutine globals table */
+ ngx_stream_lua_set_req(co, r);
+
+ /* {{{ initialize request context */
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+
+ dd("ctx = %p", ctx);
+
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_stream_lua_reset_ctx(r, L, ctx);
+
+ ctx->entered_preread_phase = 1;
+
+ ctx->cur_co_ctx = &ctx->entry_co_ctx;
+ ctx->cur_co_ctx->co = co;
+ ctx->cur_co_ctx->co_ref = co_ref;
+#ifdef NGX_LUA_USE_ASSERT
+ ctx->cur_co_ctx->co_top = 1;
+#endif
+
+ ngx_stream_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx);
+
+ /* }}} */
+
+ /* {{{ register request cleanup hooks */
+ if (ctx->cleanup == NULL) {
+ cln = ngx_stream_lua_cleanup_add(r, 0);
+ if (cln == NULL) {
+ return NGX_STREAM_INTERNAL_SERVER_ERROR;
+ }
+
+ cln->handler = ngx_stream_lua_request_cleanup_handler;
+ cln->data = ctx;
+ ctx->cleanup = &cln->handler;
+ }
+ /* }}} */
+
+ ctx->context = NGX_STREAM_LUA_CONTEXT_PREREAD;
+
+ lscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module);
+
+ if (lscf->check_client_abort) {
+ r->read_event_handler = ngx_stream_lua_rd_check_broken_connection;
+
+ rev = r->connection->read;
+
+ if (!rev->active) {
+ if (ngx_add_event(rev, NGX_READ_EVENT, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ } else {
+ r->read_event_handler = ngx_stream_lua_block_reading;
+ }
+
+ rc = ngx_stream_lua_run_thread(L, r, ctx, 0);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "preread run thread returned %d", (int) rc);
+
+ if (rc == NGX_ERROR || rc > NGX_OK) {
+ return rc;
+ }
+
+ c = r->connection;
+
+ if (rc == NGX_AGAIN) {
+ rc = ngx_stream_lua_run_posted_threads(c, L, r, ctx, 0);
+
+ if (rc == NGX_DONE && ctx->peek_needs_more_data) {
+ return NGX_AGAIN;
+ }
+
+ if (rc == NGX_ERROR || rc == NGX_DONE || rc > NGX_OK) {
+ return rc;
+ }
+
+ if (rc != NGX_OK) {
+ return NGX_DECLINED;
+ }
+
+ } else if (rc == NGX_DONE) {
+ ngx_stream_lua_finalize_request(r, NGX_DONE);
+
+ rc = ngx_stream_lua_run_posted_threads(c, L, r, ctx, 0);
+
+ if (rc == NGX_ERROR || rc == NGX_DONE || rc > NGX_OK) {
+ return rc;
+ }
+
+ if (rc != NGX_OK) {
+ return NGX_DECLINED;
+ }
+ }
+
+#if 1
+ if (rc == NGX_OK) {
+ return NGX_OK;
+ }
+#endif
+
+ return NGX_DECLINED;
+}
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_prereadby.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_prereadby.h
new file mode 100644
index 0000000..9248a3a
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_prereadby.h
@@ -0,0 +1,21 @@
+
+/*
+ * Copyright (C) OpenResty Inc.
+ */
+
+
+#ifndef _NGX_STREAM_LUA_PREREAD_H_INCLUDED_
+#define _NGX_STREAM_LUA_PREREAD_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+ngx_int_t ngx_stream_lua_preread_handler(ngx_stream_session_t *s);
+ngx_int_t ngx_stream_lua_preread_handler_inline(ngx_stream_lua_request_t *r);
+ngx_int_t ngx_stream_lua_preread_handler_file(ngx_stream_lua_request_t *r);
+
+
+#endif /* _NGX_STREAM_LUA_PREREAD_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_probe.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_probe.h
new file mode 100644
index 0000000..47eabd4
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_probe.h
@@ -0,0 +1,96 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_probe.h.tt2
+ */
+
+/*
+ * automatically generated from the file dtrace/ngx_lua_provider.d by the
+ * gen-dtrace-probe-header tool in the nginx-devel-utils project:
+ * https://github.com/agentzh/nginx-devel-utils
+ */
+
+#ifndef _NGX_STREAM_LUA_PROBE_H_INCLUDED_
+#define _NGX_STREAM_LUA_PROBE_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+
+
+#if defined(NGX_DTRACE) && NGX_DTRACE
+
+#include <ngx_dtrace_provider.h>
+
+#define ngx_stream_lua_probe_info(s) \
+ NGINX_LUA_HTTP_LUA_INFO(s)
+
+#define ngx_stream_lua_probe_register_preload_package(L, pkg) \
+ NGINX_LUA_HTTP_LUA_REGISTER_PRELOAD_PACKAGE(L, pkg)
+
+#define ngx_stream_lua_probe_req_socket_consume_preread(r, data, len) \
+ NGINX_LUA_HTTP_LUA_REQ_SOCKET_CONSUME_PREREAD(r, data, len)
+
+#define ngx_stream_lua_probe_user_coroutine_create(r, parent, child) \
+ NGINX_LUA_HTTP_LUA_USER_COROUTINE_CREATE(r, parent, child)
+
+#define ngx_stream_lua_probe_user_coroutine_resume(r, parent, child) \
+ NGINX_LUA_HTTP_LUA_USER_COROUTINE_RESUME(r, parent, child)
+
+#define ngx_stream_lua_probe_user_coroutine_yield(r, parent, child) \
+ NGINX_LUA_HTTP_LUA_USER_COROUTINE_YIELD(r, parent, child)
+
+#define ngx_stream_lua_probe_thread_yield(r, L) \
+ NGINX_LUA_HTTP_LUA_THREAD_YIELD(r, L)
+
+#define ngx_stream_lua_probe_socket_tcp_send_start(r, u, data, len) \
+ NGINX_LUA_HTTP_LUA_SOCKET_TCP_SEND_START(r, u, data, len)
+
+#define ngx_stream_lua_probe_socket_tcp_receive_done(r, u, data, len) \
+ NGINX_LUA_HTTP_LUA_SOCKET_TCP_RECEIVE_DONE(r, u, data, len)
+
+#define ngx_stream_lua_probe_socket_tcp_setkeepalive_buf_unread(r, u, \
+ data, \
+ len) \
+ NGINX_LUA_HTTP_LUA_SOCKET_TCP_SETKEEPALIVE_BUF_UNREAD(r, u, data, len)
+
+#define ngx_stream_lua_probe_user_thread_spawn(r, creator, newthread) \
+ NGINX_LUA_HTTP_LUA_USER_THREAD_SPAWN(r, creator, newthread)
+
+#define ngx_stream_lua_probe_thread_delete(r, thread, ctx) \
+ NGINX_LUA_HTTP_LUA_THREAD_DELETE(r, thread, ctx)
+
+#define ngx_stream_lua_probe_run_posted_thread(r, thread, status) \
+ NGINX_LUA_HTTP_LUA_RUN_POSTED_THREAD(r, thread, status)
+
+#define ngx_stream_lua_probe_coroutine_done(r, co, success) \
+ NGINX_LUA_HTTP_LUA_COROUTINE_DONE(r, co, success)
+
+#define ngx_stream_lua_probe_user_thread_wait(parent, child) \
+ NGINX_LUA_HTTP_LUA_USER_THREAD_WAIT(parent, child)
+
+#else /* !(NGX_DTRACE) */
+
+#define ngx_stream_lua_probe_info(s)
+#define ngx_stream_lua_probe_register_preload_package(L, pkg)
+#define ngx_stream_lua_probe_req_socket_consume_preread(r, data, len)
+#define ngx_stream_lua_probe_user_coroutine_create(r, parent, child)
+#define ngx_stream_lua_probe_user_coroutine_resume(r, parent, child)
+#define ngx_stream_lua_probe_user_coroutine_yield(r, parent, child)
+#define ngx_stream_lua_probe_thread_yield(r, L)
+#define ngx_stream_lua_probe_socket_tcp_send_start(r, u, data, len)
+#define ngx_stream_lua_probe_socket_tcp_receive_done(r, u, data, len)
+#define ngx_stream_lua_probe_socket_tcp_setkeepalive_buf_unread(r, u, data, len)
+#define ngx_stream_lua_probe_user_thread_spawn(r, creator, newthread)
+#define ngx_stream_lua_probe_thread_delete(r, thread, ctx)
+#define ngx_stream_lua_probe_run_posted_thread(r, thread, status)
+#define ngx_stream_lua_probe_coroutine_done(r, co, success)
+#define ngx_stream_lua_probe_user_thread_wait(parent, child)
+
+#endif
+
+#endif /* _NGX_STREAM_LUA_PROBE_H_INCLUDED_ */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_regex.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_regex.c
new file mode 100644
index 0000000..080e5dd
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_regex.c
@@ -0,0 +1,1007 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_regex.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#if (NGX_PCRE)
+
+#include "ngx_stream_lua_pcrefix.h"
+#include "ngx_stream_lua_script.h"
+#include "ngx_stream_lua_util.h"
+
+
+#if (PCRE_MAJOR >= 6 || NGX_PCRE2)
+# define LUA_HAVE_PCRE_DFA 1
+#else
+# define LUA_HAVE_PCRE_DFA 0
+#endif
+
+
+#if (NGX_PCRE2)
+static pcre2_compile_context *ngx_regex_compile_context;
+static pcre2_match_context *ngx_regex_match_context;
+static pcre2_match_data *ngx_regex_match_data;
+static ngx_uint_t ngx_regex_match_data_size = 0;
+
+#define PCRE2_VERSION_SIZE 64
+static char ngx_pcre2_version[PCRE2_VERSION_SIZE];
+#endif
+
+
+#define NGX_LUA_RE_MODE_DFA (1<<1)
+#define NGX_LUA_RE_MODE_JIT (1<<2)
+#define NGX_LUA_RE_NO_UTF8_CHECK (1<<4)
+
+#define NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT (100)
+
+#define NGX_LUA_RE_MIN_JIT_STACK_SIZE 32 * 1024
+
+
+typedef struct {
+ ngx_pool_t *pool;
+ u_char *name_table;
+ int name_count;
+ int name_entry_size;
+
+ int ncaptures;
+ int *captures;
+
+#if (NGX_PCRE2)
+ pcre2_code *regex;
+ /*
+ * pcre2 doesn't use pcre_extra any more,
+ * just for keeping same memory layout in the lua ffi cdef
+ */
+ void *regex_sd;
+#else
+ pcre *regex;
+ pcre_extra *regex_sd;
+#endif
+
+ ngx_stream_lua_complex_value_t *replace;
+
+ /* only for (stap) debugging, and may be an invalid pointer */
+ const u_char *pattern;
+} ngx_stream_lua_regex_t;
+
+
+typedef struct {
+ ngx_str_t pattern;
+ ngx_pool_t *pool;
+ ngx_int_t options;
+
+#if (NGX_PCRE2)
+ pcre2_code *regex;
+#else
+ pcre *regex;
+#endif
+ int captures;
+ ngx_str_t err;
+} ngx_stream_lua_regex_compile_t;
+
+
+typedef struct {
+ ngx_stream_lua_request_t *request;
+
+#if (NGX_PCRE2)
+ pcre2_code *regex;
+#else
+ pcre *regex;
+ pcre_extra *regex_sd;
+#endif
+ int ncaptures;
+ int *captures;
+ int captures_len;
+ uint8_t flags;
+} ngx_stream_lua_regex_ctx_t;
+
+
+static ngx_int_t ngx_stream_lua_regex_compile(
+ ngx_stream_lua_regex_compile_t *rc);
+
+
+#define ngx_stream_lua_regex_exec(re, e, s, start, captures, size, \
+ opts) \
+ pcre_exec(re, e, (const char *) (s)->data, (s)->len, start, opts, \
+ captures, size)
+
+
+#define ngx_stream_lua_regex_dfa_exec(re, e, s, start, captures, size, \
+ ws, wscount, opts) \
+ pcre_dfa_exec(re, e, (const char *) (s)->data, (s)->len, start, opts, \
+ captures, size, ws, wscount)
+
+
+static void
+ngx_stream_lua_regex_free_study_data(ngx_pool_t *pool,
+ ngx_stream_lua_regex_t *re)
+{
+ ngx_pool_t *old_pool;
+
+#if (NGX_PCRE2)
+ if (re && re->regex) {
+ old_pool = ngx_stream_lua_pcre_malloc_init(pool);
+
+ pcre2_code_free(re->regex);
+
+ ngx_stream_lua_pcre_malloc_done(old_pool);
+
+ re->regex = NULL;
+ }
+#else
+ if (re && re->regex_sd) {
+ old_pool = ngx_stream_lua_pcre_malloc_init(pool);
+#if LUA_HAVE_PCRE_JIT
+ pcre_free_study(re->regex_sd);
+#else
+ pcre_free(re->regex_sd);
+#endif
+ ngx_stream_lua_pcre_malloc_done(old_pool);
+
+ re->regex_sd = NULL;
+ }
+#endif
+}
+
+
+#if (NGX_PCRE2)
+static ngx_int_t
+ngx_stream_lua_regex_compile(ngx_stream_lua_regex_compile_t *rc)
+{
+ int n, errcode;
+ char *p;
+ size_t erroff;
+ u_char errstr[128];
+ pcre2_code *re;
+ ngx_pool_t *old_pool;
+ pcre2_general_context *gctx;
+ pcre2_compile_context *cctx;
+
+ ngx_stream_lua_main_conf_t *lmcf;
+
+ if (ngx_regex_compile_context == NULL) {
+ /*
+ * Allocate a compile context if not yet allocated. This uses
+ * direct allocations from heap, so the result can be cached
+ * even at runtime.
+ */
+
+ old_pool = ngx_stream_lua_pcre_malloc_init(NULL);
+
+ gctx = pcre2_general_context_create(ngx_stream_lua_pcre_malloc,
+ ngx_stream_lua_pcre_free,
+ NULL);
+ if (gctx == NULL) {
+ ngx_stream_lua_pcre_malloc_done(old_pool);
+ goto nomem;
+ }
+
+ cctx = pcre2_compile_context_create(gctx);
+ if (cctx == NULL) {
+ pcre2_general_context_free(gctx);
+ ngx_stream_lua_pcre_malloc_done(old_pool);
+ goto nomem;
+ }
+
+ ngx_regex_compile_context = cctx;
+
+ ngx_regex_match_context = pcre2_match_context_create(gctx);
+ if (ngx_regex_match_context == NULL) {
+ pcre2_general_context_free(gctx);
+ ngx_stream_lua_pcre_malloc_done(old_pool);
+ goto nomem;
+ }
+
+ lmcf = ngx_stream_cycle_get_module_main_conf(ngx_cycle,
+ ngx_stream_lua_module);
+ if (lmcf && lmcf->regex_match_limit > 0) {
+ pcre2_set_match_limit(ngx_regex_match_context,
+ lmcf->regex_match_limit);
+ }
+
+ pcre2_general_context_free(gctx);
+ ngx_stream_lua_pcre_malloc_done(old_pool);
+ }
+
+ old_pool = ngx_stream_lua_pcre_malloc_init(rc->pool);
+
+ re = pcre2_compile(rc->pattern.data,
+ rc->pattern.len, rc->options,
+ &errcode, &erroff, ngx_regex_compile_context);
+
+ ngx_stream_lua_pcre_malloc_done(old_pool);
+
+ if (re == NULL) {
+ pcre2_get_error_message(errcode, errstr, 128);
+
+ if ((size_t) erroff == rc->pattern.len) {
+ rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
+ "pcre2_compile() failed: %s in \"%V\"",
+ errstr, &rc->pattern)
+ - rc->err.data;
+
+ } else {
+ rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
+ "pcre2_compile() failed: %s in "
+ "\"%V\" at \"%s\"", errstr, &rc->pattern,
+ rc->pattern.data + erroff)
+ - rc->err.data;
+ }
+
+ return NGX_ERROR;
+ }
+
+ rc->regex = re;
+
+ n = pcre2_pattern_info(re, PCRE2_INFO_CAPTURECOUNT, &rc->captures);
+ if (n < 0) {
+ p = "pcre2_pattern_info(\"%V\", PCRE_INFO_CAPTURECOUNT) failed: %d";
+ goto failed;
+ }
+
+#if (NGX_DEBUG)
+ ngx_log_debug3(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "pcre2_compile: pattern[%V], options 0x%08Xd, ncaptures %d",
+ &rc->pattern, rc->options, rc->captures);
+#endif
+
+ return NGX_OK;
+
+failed:
+
+ rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, p, &rc->pattern, n)
+ - rc->err.data;
+ return NGX_ERROR;
+
+nomem:
+
+ rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
+ "regex \"%V\" compilation failed: no memory",
+ &rc->pattern)
+ - rc->err.data;
+ return NGX_ERROR;
+}
+
+#else
+
+static ngx_int_t
+ngx_stream_lua_regex_compile(ngx_stream_lua_regex_compile_t *rc)
+{
+ int n, erroff;
+ char *p;
+ const char *errstr;
+ pcre *re;
+ ngx_pool_t *old_pool;
+
+ old_pool = ngx_stream_lua_pcre_malloc_init(rc->pool);
+
+ re = pcre_compile((const char *) rc->pattern.data, (int) rc->options,
+ &errstr, &erroff, NULL);
+
+ ngx_stream_lua_pcre_malloc_done(old_pool);
+
+ if (re == NULL) {
+ if ((size_t) erroff == rc->pattern.len) {
+ rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
+ "pcre_compile() failed: %s in \"%V\"",
+ errstr, &rc->pattern)
+ - rc->err.data;
+
+ } else {
+ rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
+ "pcre_compile() failed: %s in \"%V\" "
+ "at \"%s\"", errstr, &rc->pattern,
+ rc->pattern.data + erroff)
+ - rc->err.data;
+ }
+
+ return NGX_ERROR;
+ }
+
+ rc->regex = re;
+
+#if 1
+ n = pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, &rc->captures);
+ if (n < 0) {
+ p = "pcre_fullinfo(\"%V\", PCRE_INFO_CAPTURECOUNT) failed: %d";
+ goto failed;
+ }
+#endif
+
+ return NGX_OK;
+
+failed:
+
+ rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, p, &rc->pattern, n)
+ - rc->err.data;
+ return NGX_OK;
+}
+#endif
+
+
+ngx_int_t
+ngx_stream_lua_ffi_set_jit_stack_size(int size, u_char *errstr,
+ size_t *errstr_size)
+{
+#if (LUA_HAVE_PCRE_JIT)
+
+ ngx_stream_lua_main_conf_t *lmcf;
+ ngx_pool_t *pool, *old_pool;
+
+ lmcf = ngx_stream_cycle_get_module_main_conf(ngx_cycle,
+ ngx_stream_lua_module);
+
+ if (size < NGX_LUA_RE_MIN_JIT_STACK_SIZE) {
+ size = NGX_LUA_RE_MIN_JIT_STACK_SIZE;
+ }
+
+ pool = lmcf->pool;
+
+ dd("server pool %p", lmcf->pool);
+
+ if (lmcf->jit_stack) {
+ old_pool = ngx_stream_lua_pcre_malloc_init(pool);
+
+#if (NGX_PCRE2)
+ pcre2_jit_stack_free(lmcf->jit_stack);
+#else
+ pcre_jit_stack_free(lmcf->jit_stack);
+#endif
+
+ ngx_stream_lua_pcre_malloc_done(old_pool);
+ }
+
+ old_pool = ngx_stream_lua_pcre_malloc_init(pool);
+
+#if (NGX_PCRE2)
+ lmcf->jit_stack = pcre2_jit_stack_create(NGX_LUA_RE_MIN_JIT_STACK_SIZE,
+ size, NULL);
+#else
+ lmcf->jit_stack = pcre_jit_stack_alloc(NGX_LUA_RE_MIN_JIT_STACK_SIZE,
+ size);
+#endif
+
+ ngx_stream_lua_pcre_malloc_done(old_pool);
+
+ if (lmcf->jit_stack == NULL) {
+ *errstr_size = ngx_snprintf(errstr, *errstr_size,
+ "pcre jit stack allocation failed")
+ - errstr;
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+
+#else /* LUA_HAVE_PCRE_JIT */
+
+ *errstr_size = ngx_snprintf(errstr, *errstr_size,
+ "no pcre jit support found") - errstr;
+ return NGX_ERROR;
+
+#endif
+}
+
+
+#if (NGX_PCRE2)
+static void
+ngx_stream_lua_regex_jit_compile(ngx_stream_lua_regex_t *re, int flags,
+ ngx_pool_t *pool, ngx_stream_lua_main_conf_t *lmcf,
+ ngx_stream_lua_regex_compile_t *re_comp)
+{
+ ngx_int_t ret;
+ ngx_pool_t *old_pool;
+
+ if (flags & NGX_LUA_RE_MODE_JIT) {
+ old_pool = ngx_stream_lua_pcre_malloc_init(pool);
+ ret = pcre2_jit_compile(re_comp->regex, PCRE2_JIT_COMPLETE);
+
+ if (ret != 0) {
+ ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
+ "pcre2_jit_compile() failed: %d in \"%V\", "
+ "ignored",
+ ret, &re_comp->pattern);
+
+#if (NGX_DEBUG)
+
+ } else {
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "pcre2 JIT compiled successfully");
+# endif /* !(NGX_DEBUG) */
+ }
+
+ ngx_stream_lua_pcre_malloc_done(old_pool);
+
+ }
+
+ if (lmcf && lmcf->jit_stack) {
+ pcre2_jit_stack_assign(ngx_regex_match_context, NULL,
+ lmcf->jit_stack);
+ }
+
+ return;
+}
+
+#else
+
+static void
+ngx_stream_lua_regex_jit_compile(ngx_stream_lua_regex_t *re, int flags,
+ ngx_pool_t *pool, ngx_stream_lua_main_conf_t *lmcf,
+ ngx_stream_lua_regex_compile_t *re_comp)
+{
+ const char *msg;
+ pcre_extra *sd = NULL;
+ ngx_pool_t *old_pool;
+
+
+#if (LUA_HAVE_PCRE_JIT)
+ if (flags & NGX_LUA_RE_MODE_JIT) {
+ old_pool = ngx_stream_lua_pcre_malloc_init(pool);
+ sd = pcre_study(re_comp->regex, PCRE_STUDY_JIT_COMPILE, &msg);
+ ngx_stream_lua_pcre_malloc_done(old_pool);
+
+# if (NGX_DEBUG)
+ if (msg != NULL) {
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "pcre study failed with PCRE_STUDY_JIT_COMPILE: "
+ "%s (%p)", msg, sd);
+ }
+
+ if (sd != NULL) {
+ int jitted;
+
+ old_pool = ngx_stream_lua_pcre_malloc_init(pool);
+
+ pcre_fullinfo(re_comp->regex, sd, PCRE_INFO_JIT, &jitted);
+
+ ngx_stream_lua_pcre_malloc_done(old_pool);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "pcre JIT compiling result: %d", jitted);
+ }
+# endif /* !(NGX_DEBUG) */
+
+ } else {
+ old_pool = ngx_stream_lua_pcre_malloc_init(pool);
+ sd = pcre_study(re_comp->regex, 0, &msg);
+ ngx_stream_lua_pcre_malloc_done(old_pool);
+ }
+
+ if (sd && lmcf && lmcf->jit_stack) {
+ pcre_assign_jit_stack(sd, NULL, lmcf->jit_stack);
+ }
+
+ if (sd
+ && lmcf && lmcf->regex_match_limit > 0
+ && !(flags & NGX_LUA_RE_MODE_DFA))
+ {
+ sd->flags |= PCRE_EXTRA_MATCH_LIMIT;
+ sd->match_limit = lmcf->regex_match_limit;
+ }
+
+#endif /* LUA_HAVE_PCRE_JIT */
+
+ re->regex_sd = sd;
+}
+#endif
+
+
+#if (NGX_PCRE2)
+void
+ngx_stream_lua_regex_cleanup(void *data)
+{
+ ngx_pool_t *old_pool;
+ ngx_stream_lua_main_conf_t *lmcf;
+
+ lmcf = data;
+
+ if (ngx_regex_compile_context) {
+ old_pool = ngx_stream_lua_pcre_malloc_init(NULL);
+ pcre2_compile_context_free(ngx_regex_compile_context);
+ ngx_regex_compile_context = NULL;
+ ngx_stream_lua_pcre_malloc_done(old_pool);
+ }
+
+ if (lmcf && lmcf->jit_stack) {
+ old_pool = ngx_stream_lua_pcre_malloc_init(NULL);
+
+ pcre2_jit_stack_free(lmcf->jit_stack);
+ lmcf->jit_stack = NULL;
+
+ ngx_stream_lua_pcre_malloc_done(old_pool);
+ }
+
+ if (ngx_regex_match_data) {
+ old_pool = ngx_stream_lua_pcre_malloc_init(NULL);
+ pcre2_match_data_free(ngx_regex_match_data);
+ ngx_regex_match_data = NULL;
+ ngx_regex_match_data_size = 0;
+ ngx_stream_lua_pcre_malloc_done(old_pool);
+ }
+
+}
+#endif
+
+
+ngx_stream_lua_regex_t *
+ngx_stream_lua_ffi_compile_regex(const unsigned char *pat, size_t pat_len,
+ int flags, int pcre_opts, u_char *errstr,
+ size_t errstr_size)
+{
+ int *cap = NULL, ovecsize;
+ u_char *p;
+ ngx_int_t rc;
+ const char *msg;
+ ngx_pool_t *pool, *old_pool;
+ ngx_stream_lua_regex_t *re = NULL;
+ ngx_stream_lua_main_conf_t *lmcf;
+ ngx_stream_lua_regex_compile_t re_comp;
+
+ pool = ngx_create_pool(512, ngx_cycle->log);
+ if (pool == NULL) {
+ msg = "no memory";
+ goto error;
+ }
+
+ pool->log = (ngx_log_t *) &ngx_cycle->new_log;
+
+ re = ngx_palloc(pool, sizeof(ngx_stream_lua_regex_t));
+ if (re == NULL) {
+ ngx_destroy_pool(pool);
+ pool = NULL;
+ msg = "no memory";
+ goto error;
+ }
+
+ re->pool = pool;
+ re->regex = NULL;
+ re->regex_sd = NULL;
+
+ re_comp.options = pcre_opts;
+ re_comp.pattern.data = (u_char *) pat;
+ re_comp.pattern.len = pat_len;
+ re_comp.err.len = errstr_size - 1;
+ re_comp.err.data = errstr;
+ re_comp.pool = pool;
+
+ old_pool = ngx_stream_lua_pcre_malloc_init(pool);
+ rc = ngx_stream_lua_regex_compile(&re_comp);
+ ngx_stream_lua_pcre_malloc_done(old_pool);
+
+ if (rc != NGX_OK) {
+ re_comp.err.data[re_comp.err.len] = '\0';
+ msg = (char *) re_comp.err.data;
+ goto error;
+ }
+
+ lmcf = ngx_stream_cycle_get_module_main_conf(ngx_cycle,
+ ngx_stream_lua_module);
+
+ ngx_stream_lua_regex_jit_compile(re, flags, pool, lmcf, &re_comp);
+
+ if (flags & NGX_LUA_RE_MODE_DFA) {
+ ovecsize = 2;
+ re_comp.captures = 0;
+
+ } else {
+#if (NGX_PCRE2)
+ ovecsize = (re_comp.captures + 1) * 2;
+#else
+ ovecsize = (re_comp.captures + 1) * 3;
+#endif
+ }
+
+ dd("allocating cap with size: %d", (int) ovecsize);
+
+ cap = ngx_palloc(pool, ovecsize * sizeof(int));
+ if (cap == NULL) {
+ msg = "no memory";
+ goto error;
+ }
+
+#if (NGX_PCRE2)
+ if (pcre2_pattern_info(re_comp.regex, PCRE2_INFO_NAMECOUNT,
+ &re->name_count) < 0)
+ {
+ msg = "cannot acquire named subpattern count";
+ goto error;
+ }
+
+ if (re->name_count > 0) {
+ if (pcre2_pattern_info(re_comp.regex, PCRE2_INFO_NAMEENTRYSIZE,
+ &re->name_entry_size) != 0)
+ {
+ msg = "cannot acquire named subpattern entry size";
+ goto error;
+ }
+
+ if (pcre2_pattern_info(re_comp.regex, PCRE2_INFO_NAMETABLE,
+ &re->name_table) != 0)
+ {
+ msg = "cannot acquire named subpattern table";
+ goto error;
+ }
+ }
+
+#else
+ if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMECOUNT,
+ &re->name_count) != 0)
+ {
+ msg = "cannot acquire named subpattern count";
+ goto error;
+ }
+
+ if (re->name_count > 0) {
+ if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMEENTRYSIZE,
+ &re->name_entry_size) != 0)
+ {
+ msg = "cannot acquire named subpattern entry size";
+ goto error;
+ }
+
+ if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMETABLE,
+ &re->name_table) != 0)
+ {
+ msg = "cannot acquire named subpattern table";
+ goto error;
+ }
+ }
+#endif
+
+ re->regex = re_comp.regex;
+ re->ncaptures = re_comp.captures;
+ re->captures = cap;
+ re->replace = NULL;
+
+ /* only for (stap) debugging, the pointer might be invalid when the
+ * string is collected later on.... */
+ re->pattern = pat;
+
+ return re;
+
+error:
+
+ p = ngx_snprintf(errstr, errstr_size - 1, "%s", msg);
+ *p = '\0';
+
+ ngx_stream_lua_regex_free_study_data(pool, re);
+
+ if (pool) {
+ ngx_destroy_pool(pool);
+ }
+
+ return NULL;
+}
+
+
+#if (NGX_PCRE2)
+int
+ngx_stream_lua_ffi_exec_regex(ngx_stream_lua_regex_t *re, int flags,
+ const u_char *s, size_t len, int pos)
+{
+ int rc, exec_opts = 0;
+ size_t *ov;
+ ngx_uint_t ovecpair, n, i;
+ ngx_pool_t *old_pool;
+
+ if (flags & NGX_LUA_RE_MODE_DFA) {
+ ovecpair = 1;
+ re->ncaptures = 0;
+
+ } else {
+ ovecpair = re->ncaptures + 1;
+ }
+
+ old_pool = ngx_stream_lua_pcre_malloc_init(NULL);
+
+ if (ngx_regex_match_data == NULL
+ || ovecpair > ngx_regex_match_data_size)
+ {
+ /*
+ * Allocate a match data if not yet allocated or smaller than
+ * needed.
+ */
+
+ if (ngx_regex_match_data) {
+ pcre2_match_data_free(ngx_regex_match_data);
+ }
+
+ ngx_regex_match_data_size = ovecpair;
+ ngx_regex_match_data = pcre2_match_data_create(ovecpair, NULL);
+
+ if (ngx_regex_match_data == NULL) {
+ rc = PCRE2_ERROR_NOMEMORY;
+ goto failed;
+ }
+ }
+
+ if (flags & NGX_LUA_RE_NO_UTF8_CHECK) {
+ exec_opts = PCRE2_NO_UTF_CHECK;
+
+ } else {
+ exec_opts = 0;
+ }
+
+ if (flags & NGX_LUA_RE_MODE_DFA) {
+ int ws[NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT];
+ rc = pcre2_dfa_match(re->regex, s, len, pos, exec_opts,
+ ngx_regex_match_data, ngx_regex_match_context,
+ ws, sizeof(ws) / sizeof(ws[0]));
+
+ } else {
+ rc = pcre2_match(re->regex, s, len, pos, exec_opts,
+ ngx_regex_match_data, ngx_regex_match_context);
+ }
+
+ if (rc < 0) {
+#if (NGX_DEBUG)
+ ngx_log_debug4(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "pcre2_match failed: flags 0x%05Xd, options 0x%08Xd, rc %d, "
+ "ovecpair %ui", flags, exec_opts, rc, ovecpair);
+#endif
+
+ goto failed;
+ }
+
+ n = pcre2_get_ovector_count(ngx_regex_match_data);
+ ov = pcre2_get_ovector_pointer(ngx_regex_match_data);
+
+#if (NGX_DEBUG)
+ ngx_log_debug5(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "pcre2_match: flags 0x%05Xd, options 0x%08Xd, rc %d, "
+ "n %ui, ovecpair %ui", flags, exec_opts, rc, n, ovecpair);
+#endif
+
+ if (n > ovecpair) {
+ n = ovecpair;
+ }
+
+ for (i = 0; i < n; i++) {
+ re->captures[i * 2] = ov[i * 2];
+ re->captures[i * 2 + 1] = ov[i * 2 + 1];
+ }
+
+failed:
+
+ ngx_stream_lua_pcre_malloc_done(old_pool);
+
+ return rc;
+}
+
+#else
+
+int
+ngx_stream_lua_ffi_exec_regex(ngx_stream_lua_regex_t *re, int flags,
+ const u_char *s, size_t len, int pos)
+{
+ int rc, ovecsize, exec_opts, *cap;
+ ngx_str_t subj;
+ pcre_extra *sd;
+
+ cap = re->captures;
+ sd = re->regex_sd;
+
+ if (flags & NGX_LUA_RE_MODE_DFA) {
+ ovecsize = 2;
+ re->ncaptures = 0;
+
+ } else {
+ /* How pcre_exec() returns captured substrings
+ * The first two-thirds of the vector is used to pass back captured
+ * substrings, each substring using a pair of integers. The remaining
+ * third of the vector is used as workspace by pcre_exec() while
+ * matching capturing subpatterns, and is not available for passing
+ * back information. The number passed in ovecsize should always be a
+ * multiple of three. If it is not, it is rounded down.
+ *
+ * When a match is successful, information about captured substrings is
+ * returned in pairs of integers, starting at the beginning of ovector,
+ * and continuing up to two-thirds of its length at the most. The first
+ * element of each pair is set to the byte offset of the first character
+ * in a substring, and the second is set to the byte offset of the first
+ * character after the end of a substring.
+ */
+ ovecsize = (re->ncaptures + 1) * 3;
+ }
+
+ if (flags & NGX_LUA_RE_NO_UTF8_CHECK) {
+ exec_opts = PCRE_NO_UTF8_CHECK;
+
+ } else {
+ exec_opts = 0;
+ }
+
+ subj.data = (u_char *) s;
+ subj.len = len;
+
+ if (flags & NGX_LUA_RE_MODE_DFA) {
+
+#if LUA_HAVE_PCRE_DFA
+
+ int ws[NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT];
+ rc = ngx_stream_lua_regex_dfa_exec(re->regex, sd, &subj,
+ (int) pos, cap, ovecsize, ws,
+ sizeof(ws) / sizeof(ws[0]), exec_opts);
+
+#else
+
+ return PCRE_ERROR_BADOPTION;
+
+#endif /* LUA_HAVE_PCRE_DFA */
+
+ } else {
+ rc = ngx_stream_lua_regex_exec(re->regex, sd, &subj, (int) pos, cap,
+ ovecsize, exec_opts);
+ }
+
+ return rc;
+}
+
+#endif
+
+
+void
+ngx_stream_lua_ffi_destroy_regex(ngx_stream_lua_regex_t *re)
+{
+ dd("destroy regex called");
+
+ if (re == NULL || re->pool == NULL) {
+ return;
+ }
+
+ ngx_stream_lua_regex_free_study_data(re->pool, re);
+
+ ngx_destroy_pool(re->pool);
+}
+
+
+int
+ngx_stream_lua_ffi_compile_replace_template(ngx_stream_lua_regex_t *re,
+ const u_char *replace_data, size_t replace_len)
+{
+ ngx_int_t rc;
+ ngx_str_t tpl;
+
+ ngx_stream_lua_complex_value_t *ctpl;
+ ngx_stream_lua_compile_complex_value_t ccv;
+
+ ctpl = ngx_palloc(re->pool, sizeof(ngx_stream_lua_complex_value_t));
+ if (ctpl == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (replace_len != 0) {
+ /* copy the string buffer pointed to by tpl.data from Lua VM */
+ tpl.data = ngx_palloc(re->pool, replace_len + 1);
+ if (tpl.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(tpl.data, replace_data, replace_len);
+ tpl.data[replace_len] = '\0';
+
+ } else {
+ tpl.data = (u_char *) replace_data;
+ }
+
+ tpl.len = replace_len;
+
+ ngx_memzero(&ccv, sizeof(ngx_stream_lua_compile_complex_value_t));
+ ccv.pool = re->pool;
+ ccv.log = ngx_cycle->log;
+ ccv.value = &tpl;
+ ccv.complex_value = ctpl;
+
+ rc = ngx_stream_lua_compile_complex_value(&ccv);
+
+ re->replace = ctpl;
+
+ return rc;
+}
+
+
+ngx_stream_lua_script_engine_t *
+ngx_stream_lua_ffi_create_script_engine(void)
+{
+ return ngx_calloc(sizeof(ngx_stream_lua_script_engine_t), ngx_cycle->log);
+}
+
+
+void
+ngx_stream_lua_ffi_init_script_engine(ngx_stream_lua_script_engine_t *e,
+ const unsigned char *subj, ngx_stream_lua_regex_t *compiled, int count)
+{
+ e->log = ngx_cycle->log;
+ e->ncaptures = count * 2;
+ e->captures = compiled->captures;
+ e->captures_data = (u_char *) subj;
+}
+
+
+void
+ngx_stream_lua_ffi_destroy_script_engine(ngx_stream_lua_script_engine_t *e)
+{
+ ngx_free(e);
+}
+
+
+size_t
+ngx_stream_lua_ffi_script_eval_len(ngx_stream_lua_script_engine_t *e,
+ ngx_stream_lua_complex_value_t *val)
+{
+ size_t len;
+
+ ngx_stream_lua_script_len_code_pt lcode;
+
+ e->ip = val->lengths;
+ len = 0;
+
+ while (*(uintptr_t *) e->ip) {
+ lcode = *(ngx_stream_lua_script_len_code_pt *) e->ip;
+ len += lcode(e);
+ }
+
+ return len;
+}
+
+
+void
+ngx_stream_lua_ffi_script_eval_data(ngx_stream_lua_script_engine_t *e,
+ ngx_stream_lua_complex_value_t *val, u_char *dst)
+{
+ ngx_stream_lua_script_code_pt code;
+
+ e->ip = val->values;
+ e->pos = dst;
+
+ while (*(uintptr_t *) e->ip) {
+ code = *(ngx_stream_lua_script_code_pt *) e->ip;
+ code(e);
+ }
+}
+
+
+uint32_t
+ngx_stream_lua_ffi_max_regex_cache_size(void)
+{
+ ngx_stream_lua_main_conf_t *lmcf;
+ lmcf = ngx_stream_cycle_get_module_main_conf(ngx_cycle,
+ ngx_stream_lua_module);
+ if (lmcf == NULL) {
+ return 0;
+ }
+ return (uint32_t) lmcf->regex_cache_max_entries;
+}
+
+
+const char *
+ngx_stream_lua_ffi_pcre_version(void)
+{
+#if (NGX_PCRE2)
+ pcre2_config(PCRE2_CONFIG_VERSION, ngx_pcre2_version);
+
+ return ngx_pcre2_version;
+#else
+ return pcre_version();
+#endif
+}
+
+
+#endif /* NGX_PCRE */
+
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_request.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_request.c
new file mode 100644
index 0000000..39fda63
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_request.c
@@ -0,0 +1,350 @@
+
+/*
+ * Copyright (C) OpenResty Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_stream.h>
+#include "ddebug.h"
+#include "ngx_stream_lua_common.h"
+#include "ngx_stream_lua_request.h"
+#include "ngx_stream_lua_contentby.h"
+
+
+static ngx_int_t ngx_stream_lua_set_write_handler(ngx_stream_lua_request_t *r);
+static void ngx_stream_lua_writer(ngx_stream_lua_request_t *r);
+static void ngx_stream_lua_request_cleanup(void *data);
+
+
+ngx_stream_lua_cleanup_t *
+ngx_stream_lua_cleanup_add(ngx_stream_lua_request_t *r, size_t size)
+{
+ ngx_stream_lua_cleanup_t *cln;
+ ngx_stream_lua_ctx_t *ctx;
+
+ if (size == 0) {
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+
+ if (ctx != NULL && ctx->free_cleanup) {
+ cln = ctx->free_cleanup;
+ ctx->free_cleanup = cln->next;
+
+ dd("reuse cleanup: %p", cln);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua stream cleanup reuse: %p", cln);
+
+ cln->handler = NULL;
+ cln->next = r->cleanup;
+
+ r->cleanup = cln;
+
+ return cln;
+ }
+ }
+
+ cln = ngx_palloc(r->pool, sizeof(ngx_stream_lua_cleanup_t));
+ if (cln == NULL) {
+ return NULL;
+ }
+
+ if (size) {
+ cln->data = ngx_palloc(r->pool, size);
+ if (cln->data == NULL) {
+ return NULL;
+ }
+
+ } else {
+ cln->data = NULL;
+ }
+
+ cln->handler = NULL;
+ cln->next = r->cleanup;
+
+ r->cleanup = cln;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream cleanup add: %p", cln);
+
+ return cln;
+}
+
+
+static void
+ngx_stream_lua_request_cleanup(void *data)
+{
+ ngx_stream_lua_request_t *r = data;
+ ngx_stream_lua_cleanup_t *cln;
+
+ cln = r->cleanup;
+ r->cleanup = NULL;
+
+ while (cln) {
+ if (cln->handler) {
+ cln->handler(cln->data);
+ }
+
+ cln = cln->next;
+ }
+}
+
+
+ngx_stream_lua_request_t *
+ngx_stream_lua_create_request(ngx_stream_session_t *s)
+{
+ ngx_pool_t *pool;
+ ngx_stream_lua_request_t *r;
+ ngx_pool_cleanup_t *cln;
+
+#if 0
+ pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, s->connection->log);
+ if (pool == NULL) {
+ return NULL;
+ }
+#endif
+
+ pool = s->connection->pool;
+
+ r = ngx_pcalloc(pool, sizeof(ngx_stream_lua_request_t));
+ if (r == NULL) {
+ return NULL;
+ }
+
+ r->connection = s->connection;
+ r->session = s;
+ r->pool = pool;
+
+ cln = ngx_pool_cleanup_add(pool, 0);
+ if (cln == NULL) {
+ return NULL;
+ }
+
+ cln->handler = ngx_stream_lua_request_cleanup;
+ cln->data = r;
+
+ return r;
+}
+
+
+void
+ngx_stream_lua_request_handler(ngx_event_t *ev)
+{
+ ngx_connection_t *c;
+ ngx_stream_session_t *s;
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+
+ c = ev->data;
+ s = c->data;
+
+ if (ev->delayed && ev->timedout) {
+ ev->delayed = 0;
+ ev->timedout = 0;
+ }
+
+ ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return;
+ }
+
+ r = ctx->request;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "session run request: \"%p\"", r);
+
+ if (ev->write) {
+ r->write_event_handler(r);
+
+ } else {
+ r->read_event_handler(r);
+ }
+}
+
+
+void
+ngx_stream_lua_empty_handler(ngx_event_t *wev)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, wev->log, 0,
+ "stream lua empty handler");
+ return;
+}
+
+
+void
+ngx_stream_lua_block_reading(ngx_stream_lua_request_t *r)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream reading blocked");
+
+ /* aio does not call this handler */
+
+ if ((ngx_event_flags & NGX_USE_LEVEL_EVENT)
+ && r->connection->read->active)
+ {
+ if (ngx_del_event(r->connection->read, NGX_READ_EVENT, 0) != NGX_OK) {
+ ngx_stream_lua_finalize_real_request(r,
+ NGX_STREAM_INTERNAL_SERVER_ERROR);
+ }
+ }
+}
+
+
+void
+ngx_stream_lua_finalize_real_request(ngx_stream_lua_request_t *r, ngx_int_t rc)
+{
+#if 0
+ ngx_pool_t *pool;
+#endif
+ ngx_stream_session_t *s;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "finalize stream request: %i", rc);
+
+ s = r->session;
+
+ if (rc == NGX_ERROR) {
+ rc = NGX_STREAM_INTERNAL_SERVER_ERROR;
+ }
+
+ if (rc == NGX_DECLINED || rc == NGX_STREAM_INTERNAL_SERVER_ERROR) {
+ goto done;
+ }
+
+ if (rc == NGX_DONE) {
+ return;
+ }
+
+ if (rc == NGX_OK) {
+ rc = NGX_STREAM_OK;
+ }
+
+ if (r->connection->buffered) {
+ if (ngx_stream_lua_set_write_handler(r) != NGX_OK) {
+ goto done;
+ }
+
+ return;
+ }
+
+done:
+
+#if 0
+ pool = r->pool;
+ r->pool = NULL;
+
+ ngx_destroy_pool(pool);
+#endif
+
+ ngx_stream_finalize_session(s, rc);
+ return;
+}
+
+
+void
+ngx_stream_lua_request_empty_handler(ngx_stream_lua_request_t *r)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream request empty handler");
+
+ return;
+}
+
+
+static void
+ngx_stream_lua_writer(ngx_stream_lua_request_t *r)
+{
+ ngx_int_t rc;
+ ngx_event_t *wev;
+ ngx_connection_t *c;
+ ngx_stream_lua_srv_conf_t *lscf;
+
+ c = r->connection;
+ wev = c->write;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, wev->log, 0,
+ "stream writer handler");
+
+ lscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module);
+
+ if (wev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+ "client timed out");
+ c->timedout = 1;
+
+ ngx_stream_lua_finalize_real_request(r, NGX_ERROR);
+ return;
+ }
+
+ rc = ngx_stream_top_filter(r->session, NULL, 1);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "stream writer output filter: %i", rc);
+
+ if (rc == NGX_ERROR) {
+ ngx_stream_lua_finalize_real_request(r, rc);
+ return;
+ }
+
+ if (c->buffered) {
+ if (!wev->delayed) {
+ ngx_add_timer(wev, lscf->send_timeout);
+ }
+
+ if (ngx_handle_write_event(wev, lscf->send_lowat) != NGX_OK) {
+ ngx_stream_lua_finalize_real_request(r, NGX_ERROR);
+ }
+
+ return;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, wev->log, 0,
+ "stream writer done");
+
+ r->write_event_handler = ngx_stream_lua_request_empty_handler;
+
+ ngx_stream_lua_finalize_real_request(r, rc);
+}
+
+
+static ngx_int_t
+ngx_stream_lua_set_write_handler(ngx_stream_lua_request_t *r)
+{
+ ngx_event_t *wev;
+ ngx_stream_lua_srv_conf_t *lscf;
+
+ r->read_event_handler = ngx_stream_lua_request_empty_handler;
+ r->write_event_handler = ngx_stream_lua_writer;
+
+ wev = r->connection->write;
+
+ if (wev->ready && wev->delayed) {
+ return NGX_OK;
+ }
+
+ lscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module);
+ if (!wev->delayed) {
+ ngx_add_timer(wev, lscf->send_timeout);
+ }
+
+ if (ngx_handle_write_event(wev, lscf->send_lowat) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+void
+ngx_stream_lua_core_run_phases(ngx_stream_lua_request_t *r)
+{
+ ngx_stream_session_t *s;
+
+ s = r->session;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua session run phases: \"%p\"", r);
+
+ ngx_stream_core_run_phases(s);
+}
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_request.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_request.h
new file mode 100644
index 0000000..ea4e05f
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_request.h
@@ -0,0 +1,67 @@
+
+/*
+ * Copyright (C) OpenResty Inc.
+ */
+
+
+#ifndef _NGX_STREAM_LUA_REQUEST_H_INCLUDED_
+#define _NGX_STREAM_LUA_REQUEST_H_INCLUDED_
+
+
+typedef void (*ngx_stream_lua_cleanup_pt)(void *data);
+
+typedef struct ngx_stream_lua_cleanup_s ngx_stream_lua_cleanup_t;
+
+struct ngx_stream_lua_cleanup_s {
+ ngx_stream_lua_cleanup_pt handler;
+ void *data;
+ ngx_stream_lua_cleanup_t *next;
+};
+
+
+typedef struct ngx_stream_lua_request_s ngx_stream_lua_request_t;
+
+typedef void (*ngx_stream_lua_event_handler_pt)(ngx_stream_lua_request_t *r);
+
+
+struct ngx_stream_lua_request_s {
+ ngx_connection_t *connection;
+ ngx_stream_session_t *session;
+ ngx_pool_t *pool;
+ ngx_stream_lua_cleanup_t *cleanup;
+
+ ngx_stream_lua_event_handler_pt read_event_handler;
+ ngx_stream_lua_event_handler_pt write_event_handler;
+};
+
+
+void ngx_stream_lua_empty_handler(ngx_event_t *wev);
+void ngx_stream_lua_request_handler(ngx_event_t *ev);
+void ngx_stream_lua_block_reading(ngx_stream_lua_request_t *r);
+
+ngx_stream_lua_cleanup_t *
+ngx_stream_lua_cleanup_add(ngx_stream_lua_request_t *r, size_t size);
+
+ngx_stream_lua_request_t *
+ngx_stream_lua_create_request(ngx_stream_session_t *s);
+void ngx_stream_lua_finalize_real_request(ngx_stream_lua_request_t *r,
+ ngx_int_t rc);
+void ngx_stream_lua_core_run_phases(ngx_stream_lua_request_t *r);
+
+
+typedef ngx_int_t (*ngx_stream_lua_handler_pt)(ngx_stream_lua_request_t *r);
+
+
+#define ngx_stream_lua_get_module_ctx(r, module) \
+ ngx_stream_get_module_ctx((r)->session, module)
+#define ngx_stream_lua_set_ctx(r, c, module) \
+ ngx_stream_set_ctx((r)->session, c, module)
+#define ngx_stream_lua_get_module_main_conf(r, module) \
+ ngx_stream_get_module_main_conf((r)->session, module)
+#define ngx_stream_lua_get_module_srv_conf(r, module) \
+ ngx_stream_get_module_srv_conf((r)->session, module)
+#define ngx_stream_lua_get_module_loc_conf \
+ ngx_stream_lua_get_module_srv_conf
+
+
+#endif /* _NGX_STREAM_LUA_REQUEST_H_INCLUDED_ */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_script.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_script.c
new file mode 100644
index 0000000..2c82585
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_script.c
@@ -0,0 +1,555 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_script.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_script.h"
+
+
+static void *ngx_stream_lua_script_add_code(ngx_array_t *codes, size_t size);
+static size_t ngx_stream_lua_script_copy_len_code(
+ ngx_stream_lua_script_engine_t *e);
+static void ngx_stream_lua_script_copy_code(ngx_stream_lua_script_engine_t *e);
+static ngx_int_t ngx_stream_lua_script_add_copy_code(
+ ngx_stream_lua_script_compile_t *sc, ngx_str_t *value, ngx_uint_t last);
+static ngx_int_t ngx_stream_lua_script_compile(
+ ngx_stream_lua_script_compile_t *sc);
+static ngx_int_t ngx_stream_lua_script_add_capture_code(
+ ngx_stream_lua_script_compile_t *sc, ngx_uint_t n);
+static size_t ngx_stream_lua_script_copy_capture_len_code(
+ ngx_stream_lua_script_engine_t *e);
+static void ngx_stream_lua_script_copy_capture_code(
+ ngx_stream_lua_script_engine_t *e);
+static ngx_int_t ngx_stream_lua_script_done(
+ ngx_stream_lua_script_compile_t *sc);
+static ngx_int_t ngx_stream_lua_script_init_arrays(
+ ngx_stream_lua_script_compile_t *sc);
+
+
+ngx_int_t
+ngx_stream_lua_compile_complex_value(
+ ngx_stream_lua_compile_complex_value_t *ccv)
+{
+ ngx_str_t *v;
+ ngx_uint_t i, n, nv;
+ ngx_array_t lengths, values, *pl, *pv;
+
+ ngx_stream_lua_script_compile_t sc;
+
+ v = ccv->value;
+
+ nv = 0;
+
+ for (i = 0; i < v->len; i++) {
+ if (v->data[i] == '$') {
+ nv++;
+ }
+ }
+
+ ccv->complex_value->value = *v;
+ ccv->complex_value->lengths = NULL;
+ ccv->complex_value->values = NULL;
+
+ if (nv == 0) {
+ return NGX_OK;
+ }
+
+ n = nv * (2 * sizeof(ngx_stream_lua_script_copy_code_t)
+ + sizeof(ngx_stream_lua_script_capture_code_t))
+ + sizeof(uintptr_t);
+
+ if (ngx_array_init(&lengths, ccv->pool, n, 1) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ n = (nv * (2 * sizeof(ngx_stream_lua_script_copy_code_t)
+ + sizeof(ngx_stream_lua_script_capture_code_t))
+ + sizeof(uintptr_t)
+ + sizeof(uintptr_t) - 1)
+ & ~(sizeof(uintptr_t) - 1);
+
+ if (ngx_array_init(&values, ccv->pool, n, 1) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ pl = &lengths;
+ pv = &values;
+
+ ngx_memzero(&sc, sizeof(ngx_stream_lua_script_compile_t));
+
+ sc.pool = ccv->pool;
+ sc.log = ccv->log;
+ sc.source = v;
+ sc.lengths = &pl;
+ sc.values = &pv;
+ sc.complete_lengths = 1;
+ sc.complete_values = 1;
+
+ if (ngx_stream_lua_script_compile(&sc) != NGX_OK) {
+ ngx_array_destroy(&lengths);
+ ngx_array_destroy(&values);
+ return NGX_ERROR;
+ }
+
+ ccv->complex_value->lengths = lengths.elts;
+ ccv->complex_value->values = values.elts;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_stream_lua_complex_value(ngx_stream_lua_request_t *r, ngx_str_t *subj,
+ size_t offset, ngx_int_t count, int *cap,
+ ngx_stream_lua_complex_value_t *val, luaL_Buffer *luabuf)
+{
+ size_t len;
+ u_char *p;
+
+ ngx_stream_lua_script_code_pt code;
+ ngx_stream_lua_script_len_code_pt lcode;
+ ngx_stream_lua_script_engine_t e;
+
+ if (val->lengths == NULL) {
+ luaL_addlstring(luabuf, (char *) &subj->data[offset], cap[0] - offset);
+ luaL_addlstring(luabuf, (char *) val->value.data, val->value.len);
+
+ return NGX_OK;
+ }
+
+ ngx_memzero(&e, sizeof(ngx_stream_lua_script_engine_t));
+
+ e.log = r->connection->log;
+ e.ncaptures = count * 2;
+ e.captures = cap;
+ e.captures_data = subj->data;
+
+ e.ip = val->lengths;
+
+ len = 0;
+
+ while (*(uintptr_t *) e.ip) {
+ lcode = *(ngx_stream_lua_script_len_code_pt *) e.ip;
+ len += lcode(&e);
+ }
+
+ p = ngx_pnalloc(r->pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ e.ip = val->values;
+ e.pos = p;
+
+ while (*(uintptr_t *) e.ip) {
+ code = *(ngx_stream_lua_script_code_pt *) e.ip;
+ code((ngx_stream_lua_script_engine_t *) &e);
+ }
+
+ luaL_addlstring(luabuf, (char *) &subj->data[offset], cap[0] - offset);
+ luaL_addlstring(luabuf, (char *) p, len);
+
+ ngx_pfree(r->pool, p);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_script_compile(ngx_stream_lua_script_compile_t *sc)
+{
+ u_char ch;
+ ngx_str_t name;
+ ngx_uint_t i, bracket;
+ unsigned num_var;
+ ngx_uint_t n = 0;
+
+ if (ngx_stream_lua_script_init_arrays(sc) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ for (i = 0; i < sc->source->len; /* void */ ) {
+
+ name.len = 0;
+
+ if (sc->source->data[i] == '$') {
+
+ if (++i == sc->source->len) {
+ goto invalid_variable;
+ }
+
+ if (sc->source->data[i] == '$') {
+ name.data = &sc->source->data[i];
+ i++;
+ name.len++;
+
+ if (ngx_stream_lua_script_add_copy_code(sc, &name,
+ i == sc->source->len)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ continue;
+ }
+
+ if (sc->source->data[i] >= '0' && sc->source->data[i] <= '9') {
+ num_var = 1;
+ n = 0;
+
+ } else {
+ num_var = 0;
+ }
+
+ if (sc->source->data[i] == '{') {
+ bracket = 1;
+
+ if (++i == sc->source->len) {
+ goto invalid_variable;
+ }
+
+ if (sc->source->data[i] >= '0' && sc->source->data[i] <= '9') {
+ num_var = 1;
+ n = 0;
+ }
+
+ name.data = &sc->source->data[i];
+
+ } else {
+ bracket = 0;
+ name.data = &sc->source->data[i];
+ }
+
+ for ( /* void */ ; i < sc->source->len; i++, name.len++) {
+ ch = sc->source->data[i];
+
+ if (ch == '}' && bracket) {
+ i++;
+ bracket = 0;
+ break;
+ }
+
+ if (num_var) {
+ if (ch >= '0' && ch <= '9') {
+ n = n * 10 + (ch - '0');
+ continue;
+ }
+
+ break;
+ }
+
+ /* not a number variable like $1, $2, etc */
+
+ if ((ch >= 'A' && ch <= 'Z')
+ || (ch >= 'a' && ch <= 'z')
+ || (ch >= '0' && ch <= '9')
+ || ch == '_')
+ {
+ continue;
+ }
+
+ break;
+ }
+
+ if (bracket) {
+ ngx_log_error(NGX_LOG_ERR, sc->log, 0,
+ "the closing bracket in \"%V\" "
+ "variable is missing", &name);
+ return NGX_ERROR;
+ }
+
+ if (name.len == 0) {
+ goto invalid_variable;
+ }
+
+ if (!num_var) {
+ ngx_log_error(NGX_LOG_ERR, sc->log, 0,
+ "attempt to use named capturing variable "
+ "\"%V\" (named captures not supported yet)",
+ &name);
+
+ return NGX_ERROR;
+ }
+
+ sc->variables++;
+
+ if (ngx_stream_lua_script_add_capture_code(sc, n) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ continue;
+ }
+
+ name.data = &sc->source->data[i];
+
+ while (i < sc->source->len) {
+
+ if (sc->source->data[i] == '$') {
+ break;
+ }
+
+ i++;
+ name.len++;
+ }
+
+ if (ngx_stream_lua_script_add_copy_code(sc, &name,
+ i == sc->source->len)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+ }
+
+ return ngx_stream_lua_script_done(sc);
+
+invalid_variable:
+
+ ngx_log_error(NGX_LOG_ERR, sc->log, 0,
+ "lua script: invalid capturing variable name found in \"%V\"",
+ sc->source);
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_script_add_copy_code(ngx_stream_lua_script_compile_t *sc,
+ ngx_str_t *value, ngx_uint_t last)
+{
+ size_t size, len;
+
+ ngx_stream_lua_script_copy_code_t *code;
+
+ len = value->len;
+
+ code = ngx_stream_lua_script_add_code(*sc->lengths,
+ sizeof(ngx_stream_lua_script_copy_code_t));
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ code->code = (ngx_stream_lua_script_code_pt) (void *)
+ ngx_stream_lua_script_copy_len_code;
+ code->len = len;
+
+ size = (sizeof(ngx_stream_lua_script_copy_code_t) + len +
+ sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1);
+
+ code = ngx_stream_lua_script_add_code(*sc->values, size);
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ code->code = ngx_stream_lua_script_copy_code;
+ code->len = len;
+
+ ngx_memcpy((u_char *) code + sizeof(ngx_stream_lua_script_copy_code_t),
+ value->data, value->len);
+
+ return NGX_OK;
+}
+
+
+static size_t
+ngx_stream_lua_script_copy_len_code(ngx_stream_lua_script_engine_t *e)
+{
+ ngx_stream_lua_script_copy_code_t *code;
+
+ code = (ngx_stream_lua_script_copy_code_t *) e->ip;
+
+ e->ip += sizeof(ngx_stream_lua_script_copy_code_t);
+
+ return code->len;
+}
+
+
+static void
+ngx_stream_lua_script_copy_code(ngx_stream_lua_script_engine_t *e)
+{
+ u_char *p;
+
+ ngx_stream_lua_script_copy_code_t *code;
+
+ code = (ngx_stream_lua_script_copy_code_t *) e->ip;
+
+ p = e->pos;
+
+ if (!e->skip) {
+ e->pos = ngx_copy(p, e->ip + sizeof(ngx_stream_lua_script_copy_code_t),
+ code->len);
+ }
+
+ e->ip += sizeof(ngx_stream_lua_script_copy_code_t)
+ + ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1));
+
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, e->log, 0,
+ "lua script copy: \"%*s\"", e->pos - p, p);
+}
+
+
+static ngx_int_t
+ngx_stream_lua_script_add_capture_code(ngx_stream_lua_script_compile_t *sc,
+ ngx_uint_t n)
+{
+ ngx_stream_lua_script_capture_code_t *code;
+
+ code = ngx_stream_lua_script_add_code(*sc->lengths,
+ sizeof(ngx_stream_lua_script_capture_code_t));
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ code->code = (ngx_stream_lua_script_code_pt) (void *)
+ ngx_stream_lua_script_copy_capture_len_code;
+ code->n = 2 * n;
+
+ code = ngx_stream_lua_script_add_code(*sc->values,
+ sizeof(ngx_stream_lua_script_capture_code_t));
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ code->code = ngx_stream_lua_script_copy_capture_code;
+ code->n = 2 * n;
+
+ return NGX_OK;
+}
+
+
+static size_t
+ngx_stream_lua_script_copy_capture_len_code(ngx_stream_lua_script_engine_t *e)
+{
+ int *cap;
+ ngx_uint_t n;
+
+ ngx_stream_lua_script_capture_code_t *code;
+
+ code = (ngx_stream_lua_script_capture_code_t *) e->ip;
+
+ e->ip += sizeof(ngx_stream_lua_script_capture_code_t);
+
+ n = code->n;
+
+ if (n < e->ncaptures) {
+ cap = e->captures;
+ return cap[n + 1] - cap[n];
+ }
+
+ return 0;
+}
+
+
+static void
+ngx_stream_lua_script_copy_capture_code(ngx_stream_lua_script_engine_t *e)
+{
+ int *cap;
+ u_char *p, *pos;
+ ngx_uint_t n;
+
+ ngx_stream_lua_script_capture_code_t *code;
+
+ code = (ngx_stream_lua_script_capture_code_t *) e->ip;
+
+ e->ip += sizeof(ngx_stream_lua_script_capture_code_t);
+
+ n = code->n;
+
+ pos = e->pos;
+
+ if (n < e->ncaptures) {
+
+ cap = e->captures;
+ p = e->captures_data;
+
+ e->pos = ngx_copy(pos, &p[cap[n]], cap[n + 1] - cap[n]);
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, e->log, 0,
+ "lua script capture: \"%*s\"", e->pos - pos, pos);
+}
+
+
+static ngx_int_t
+ngx_stream_lua_script_init_arrays(ngx_stream_lua_script_compile_t *sc)
+{
+ ngx_uint_t n;
+
+ if (*sc->lengths == NULL) {
+ n = sc->variables * (2 * sizeof(ngx_stream_lua_script_copy_code_t)
+ + sizeof(ngx_stream_lua_script_capture_code_t))
+ + sizeof(uintptr_t);
+
+ *sc->lengths = ngx_array_create(sc->pool, n, 1);
+ if (*sc->lengths == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ if (*sc->values == NULL) {
+ n = (sc->variables * (2 * sizeof(ngx_stream_lua_script_copy_code_t)
+ + sizeof(ngx_stream_lua_script_capture_code_t))
+ + sizeof(uintptr_t)
+ + sizeof(uintptr_t) - 1)
+ & ~(sizeof(uintptr_t) - 1);
+
+ *sc->values = ngx_array_create(sc->pool, n, 1);
+ if (*sc->values == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ sc->variables = 0;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_script_done(ngx_stream_lua_script_compile_t *sc)
+{
+ uintptr_t *code;
+
+ if (sc->complete_lengths) {
+ code = ngx_stream_lua_script_add_code(*sc->lengths, sizeof(uintptr_t));
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ *code = (uintptr_t) NULL;
+ }
+
+ if (sc->complete_values) {
+ code = ngx_stream_lua_script_add_code(*sc->values, sizeof(uintptr_t));
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ *code = (uintptr_t) NULL;
+ }
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_stream_lua_script_add_code(ngx_array_t *codes, size_t size)
+{
+ return ngx_array_push_n(codes, size);
+}
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_script.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_script.h
new file mode 100644
index 0000000..a43da06
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_script.h
@@ -0,0 +1,96 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_script.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_SCRIPT_H_INCLUDED_
+#define _NGX_STREAM_LUA_SCRIPT_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+typedef struct {
+ ngx_log_t *log;
+ ngx_pool_t *pool;
+ ngx_str_t *source;
+
+ ngx_array_t **lengths;
+ ngx_array_t **values;
+
+ ngx_uint_t variables;
+
+ unsigned complete_lengths:1;
+ unsigned complete_values:1;
+} ngx_stream_lua_script_compile_t;
+
+
+typedef struct {
+ ngx_str_t value;
+ void *lengths;
+ void *values;
+} ngx_stream_lua_complex_value_t;
+
+
+typedef struct {
+ ngx_log_t *log;
+ ngx_pool_t *pool;
+ ngx_str_t *value;
+
+ ngx_stream_lua_complex_value_t *complex_value;
+} ngx_stream_lua_compile_complex_value_t;
+
+
+typedef struct {
+ u_char *ip;
+ u_char *pos;
+
+ ngx_str_t buf;
+
+ int *captures;
+ ngx_uint_t ncaptures;
+ u_char *captures_data;
+
+ unsigned skip:1;
+
+ ngx_log_t *log;
+} ngx_stream_lua_script_engine_t;
+
+
+typedef void (*ngx_stream_lua_script_code_pt) (
+ ngx_stream_lua_script_engine_t *e);
+typedef size_t (*ngx_stream_lua_script_len_code_pt)
+ (ngx_stream_lua_script_engine_t *e);
+
+
+typedef struct {
+ ngx_stream_lua_script_code_pt code;
+ uintptr_t len;
+} ngx_stream_lua_script_copy_code_t;
+
+
+typedef struct {
+ ngx_stream_lua_script_code_pt code;
+ uintptr_t n;
+} ngx_stream_lua_script_capture_code_t;
+
+
+ngx_int_t ngx_stream_lua_compile_complex_value(
+ ngx_stream_lua_compile_complex_value_t *ccv);
+ngx_int_t ngx_stream_lua_complex_value(ngx_stream_lua_request_t *r,
+ ngx_str_t *subj, size_t offset, ngx_int_t count, int *cap,
+ ngx_stream_lua_complex_value_t *val, luaL_Buffer *luabuf);
+
+
+#endif /* _NGX_STREAM_LUA_SCRIPT_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_semaphore.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_semaphore.c
new file mode 100644
index 0000000..ab22718
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_semaphore.c
@@ -0,0 +1,583 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_semaphore.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ * Copyright (C) cuiweixie
+ * I hereby assign copyright in this code to the lua-nginx-module project,
+ * to be licensed under the same terms as the rest of the code.
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_util.h"
+#include "ngx_stream_lua_semaphore.h"
+#include "ngx_stream_lua_contentby.h"
+
+
+ngx_int_t ngx_stream_lua_sema_mm_init(ngx_conf_t *cf,
+ ngx_stream_lua_main_conf_t *lmcf);
+void ngx_stream_lua_sema_mm_cleanup(void *data);
+static ngx_stream_lua_sema_t *ngx_stream_lua_alloc_sema(void);
+static void ngx_stream_lua_free_sema(ngx_stream_lua_sema_t *sem);
+static ngx_int_t ngx_stream_lua_sema_resume(ngx_stream_lua_request_t *r);
+int ngx_stream_lua_ffi_sema_new(ngx_stream_lua_sema_t **psem,
+ int n, char **errmsg);
+int ngx_stream_lua_ffi_sema_post(ngx_stream_lua_sema_t *sem, int n);
+int ngx_stream_lua_ffi_sema_wait(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_sema_t *sem, int wait_ms, u_char *err, size_t *errlen);
+static void ngx_stream_lua_sema_cleanup(void *data);
+static void ngx_stream_lua_sema_handler(ngx_event_t *ev);
+static void ngx_stream_lua_sema_timeout_handler(ngx_event_t *ev);
+void ngx_stream_lua_ffi_sema_gc(ngx_stream_lua_sema_t *sem);
+
+
+enum {
+ SEMAPHORE_WAIT_SUCC = 0,
+ SEMAPHORE_WAIT_TIMEOUT = 1
+};
+
+
+ngx_int_t
+ngx_stream_lua_sema_mm_init(ngx_conf_t *cf, ngx_stream_lua_main_conf_t *lmcf)
+{
+ ngx_stream_lua_sema_mm_t *mm;
+
+ mm = ngx_palloc(cf->pool, sizeof(ngx_stream_lua_sema_mm_t));
+ if (mm == NULL) {
+ return NGX_ERROR;
+ }
+
+ lmcf->sema_mm = mm;
+ mm->lmcf = lmcf;
+
+ ngx_queue_init(&mm->free_queue);
+ mm->cur_epoch = 0;
+ mm->total = 0;
+ mm->used = 0;
+
+ /* it's better to be 4096, but it needs some space for
+ * ngx_stream_lua_sema_mm_block_t, one is enough, so it is 4095
+ */
+ mm->num_per_block = 4095;
+
+ return NGX_OK;
+}
+
+
+static ngx_stream_lua_sema_t *
+ngx_stream_lua_alloc_sema(void)
+{
+ ngx_uint_t i, n;
+ ngx_queue_t *q;
+ ngx_stream_lua_sema_t *sem, *iter;
+ ngx_stream_lua_sema_mm_t *mm;
+ ngx_stream_lua_main_conf_t *lmcf;
+ ngx_stream_lua_sema_mm_block_t *block;
+
+ ngx_stream_lua_assert(ngx_cycle && ngx_cycle->conf_ctx);
+
+ lmcf = ngx_stream_cycle_get_module_main_conf(ngx_cycle,
+ ngx_stream_lua_module);
+
+ ngx_stream_lua_assert(lmcf != NULL);
+
+ mm = lmcf->sema_mm;
+
+ if (!ngx_queue_empty(&mm->free_queue)) {
+ q = ngx_queue_head(&mm->free_queue);
+ ngx_queue_remove(q);
+
+ sem = ngx_queue_data(q, ngx_stream_lua_sema_t, chain);
+
+ sem->block->used++;
+
+ ngx_memzero(&sem->sem_event, sizeof(ngx_event_t));
+
+ sem->sem_event.handler = ngx_stream_lua_sema_handler;
+ sem->sem_event.data = sem;
+ sem->sem_event.log = ngx_cycle->log;
+
+ mm->used++;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "from head of free queue, alloc semaphore: %p", sem);
+
+ return sem;
+ }
+
+ /* free_queue is empty */
+
+ n = sizeof(ngx_stream_lua_sema_mm_block_t)
+ + mm->num_per_block * sizeof(ngx_stream_lua_sema_t);
+
+ dd("block size: %d, item size: %d",
+ (int) sizeof(ngx_stream_lua_sema_mm_block_t),
+ (int) sizeof(ngx_stream_lua_sema_t));
+
+ block = ngx_alloc(n, ngx_cycle->log);
+ if (block == NULL) {
+ return NULL;
+ }
+
+ mm->cur_epoch++;
+ mm->total += mm->num_per_block;
+ mm->used++;
+
+ block->mm = mm;
+ block->epoch = mm->cur_epoch;
+
+ sem = (ngx_stream_lua_sema_t *) (block + 1);
+ sem->block = block;
+ sem->block->used = 1;
+
+ ngx_memzero(&sem->sem_event, sizeof(ngx_event_t));
+
+ sem->sem_event.handler = ngx_stream_lua_sema_handler;
+ sem->sem_event.data = sem;
+ sem->sem_event.log = ngx_cycle->log;
+
+ for (iter = sem + 1, i = 1; i < mm->num_per_block; i++, iter++) {
+ iter->block = block;
+ ngx_queue_insert_tail(&mm->free_queue, &iter->chain);
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "new block, alloc semaphore: %p block: %p", sem, block);
+
+ return sem;
+}
+
+
+void
+ngx_stream_lua_sema_mm_cleanup(void *data)
+{
+ ngx_uint_t i;
+ ngx_queue_t *q;
+ ngx_stream_lua_sema_t *sem, *iter;
+ ngx_stream_lua_sema_mm_t *mm;
+ ngx_stream_lua_main_conf_t *lmcf;
+ ngx_stream_lua_sema_mm_block_t *block;
+
+ lmcf = (ngx_stream_lua_main_conf_t *) data;
+ mm = lmcf->sema_mm;
+
+ while (!ngx_queue_empty(&mm->free_queue)) {
+ q = ngx_queue_head(&mm->free_queue);
+
+ sem = ngx_queue_data(q, ngx_stream_lua_sema_t, chain);
+ block = sem->block;
+
+ ngx_stream_lua_assert(block != NULL);
+
+ if (block->used == 0) {
+ iter = (ngx_stream_lua_sema_t *) (block + 1);
+
+ for (i = 0; i < block->mm->num_per_block; i++, iter++) {
+ ngx_queue_remove(&iter->chain);
+ }
+
+ dd("free sema block: %p at final", block);
+
+ ngx_free(block);
+
+ } else {
+ /* just return directly when some thing goes wrong */
+
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+ "lua sema mm: freeing a block %p that is still "
+ " used by someone", block);
+
+ return;
+ }
+ }
+
+ dd("lua sema mm cleanup done");
+}
+
+
+static void
+ngx_stream_lua_free_sema(ngx_stream_lua_sema_t *sem)
+{
+ ngx_stream_lua_sema_t *iter;
+ ngx_uint_t i, mid_epoch;
+ ngx_stream_lua_sema_mm_block_t *block;
+ ngx_stream_lua_sema_mm_t *mm;
+
+ block = sem->block;
+ block->used--;
+
+ mm = block->mm;
+ mm->used--;
+
+ mid_epoch = mm->cur_epoch - ((mm->total / mm->num_per_block) >> 1);
+
+ if (block->epoch < mid_epoch) {
+ ngx_queue_insert_tail(&mm->free_queue, &sem->chain);
+ ngx_log_debug4(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "add to free queue tail semaphore: %p epoch: %d"
+ "mid_epoch: %d cur_epoch: %d", sem, (int) block->epoch,
+ (int) mid_epoch, (int) mm->cur_epoch);
+
+ } else {
+ ngx_queue_insert_head(&mm->free_queue, &sem->chain);
+ ngx_log_debug4(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "add to free queue head semaphore: %p epoch: %d"
+ "mid_epoch: %d cur_epoch: %d", sem, (int) block->epoch,
+ (int) mid_epoch, (int) mm->cur_epoch);
+ }
+
+ dd("used: %d", (int) block->used);
+
+ if (block->used == 0
+ && mm->used <= (mm->total >> 1)
+ && block->epoch < mid_epoch)
+ {
+ /* load <= 50% and it's on the older side */
+ iter = (ngx_stream_lua_sema_t *) (block + 1);
+
+ for (i = 0; i < mm->num_per_block; i++, iter++) {
+ ngx_queue_remove(&iter->chain);
+ }
+
+ mm->total -= mm->num_per_block;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "free semaphore block: %p", block);
+
+ ngx_free(block);
+ }
+}
+
+
+static ngx_int_t
+ngx_stream_lua_sema_resume(ngx_stream_lua_request_t *r)
+{
+ lua_State *vm;
+ ngx_connection_t *c;
+ ngx_int_t rc;
+ ngx_uint_t nreqs;
+ ngx_stream_lua_ctx_t *ctx;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ctx->resume_handler = ngx_stream_lua_wev_handler;
+
+ c = r->connection;
+ vm = ngx_stream_lua_get_lua_vm(r, ctx);
+ nreqs = c->requests;
+
+ if (ctx->cur_co_ctx->sem_resume_status == SEMAPHORE_WAIT_SUCC) {
+ lua_pushboolean(ctx->cur_co_ctx->co, 1);
+ lua_pushnil(ctx->cur_co_ctx->co);
+
+ } else {
+ lua_pushboolean(ctx->cur_co_ctx->co, 0);
+ lua_pushliteral(ctx->cur_co_ctx->co, "timeout");
+ }
+
+ rc = ngx_stream_lua_run_thread(vm, r, ctx, 2);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua run thread returned %d", rc);
+
+ if (rc == NGX_AGAIN) {
+ return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs);
+ }
+
+ if (rc == NGX_DONE) {
+ ngx_stream_lua_finalize_request(r, NGX_DONE);
+ return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs);
+ }
+
+ /* rc == NGX_ERROR || rc >= NGX_OK */
+
+ if (ctx->entered_content_phase) {
+ ngx_stream_lua_finalize_request(r, rc);
+ return NGX_DONE;
+ }
+
+ return rc;
+}
+
+
+int
+ngx_stream_lua_ffi_sema_new(ngx_stream_lua_sema_t **psem,
+ int n, char **errmsg)
+{
+ ngx_stream_lua_sema_t *sem;
+
+ sem = ngx_stream_lua_alloc_sema();
+ if (sem == NULL) {
+ *errmsg = "no memory";
+ return NGX_ERROR;
+ }
+
+ ngx_queue_init(&sem->wait_queue);
+
+ sem->resource_count = n;
+ sem->wait_count = 0;
+ *psem = sem;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "stream lua semaphore new: %p, resources: %d",
+ sem, sem->resource_count);
+
+ return NGX_OK;
+}
+
+
+int
+ngx_stream_lua_ffi_sema_post(ngx_stream_lua_sema_t *sem, int n)
+{
+ ngx_log_debug3(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "stream lua semaphore post: %p, n: %d, resources: %d",
+ sem, n, sem->resource_count);
+
+ sem->resource_count += n;
+
+ if (!ngx_queue_empty(&sem->wait_queue)) {
+ /* we need the extra paranthese around the first argument of
+ * ngx_post_event() just to work around macro issues in nginx
+ * cores older than nginx 1.7.12 (exclusive).
+ */
+ ngx_post_event((&sem->sem_event), &ngx_posted_events);
+ }
+
+ return NGX_OK;
+}
+
+
+int
+ngx_stream_lua_ffi_sema_wait(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_sema_t *sem, int wait_ms, u_char *err, size_t *errlen)
+{
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *wait_co_ctx;
+ ngx_int_t rc;
+
+ ngx_log_debug4(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "stream lua semaphore wait: %p, timeout: %d, "
+ "resources: %d, event posted: %d",
+ sem, wait_ms, sem->resource_count,
+#if defined(nginx_version) && nginx_version >= 1007005
+ (int) sem->sem_event.posted
+#else
+ sem->sem_event.prev ? 1 : 0
+#endif
+ );
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ *errlen = ngx_snprintf(err, *errlen, "no request ctx found") - err;
+ return NGX_ERROR;
+ }
+
+ rc = ngx_stream_lua_ffi_check_context(ctx, NGX_STREAM_LUA_CONTEXT_YIELDABLE,
+ err, errlen);
+
+ if (rc != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ /* we keep the order, will first resume the thread waiting for the
+ * longest time in ngx_stream_lua_sema_handler
+ */
+
+ if (ngx_queue_empty(&sem->wait_queue) && sem->resource_count > 0) {
+ sem->resource_count--;
+ return NGX_OK;
+ }
+
+ if (wait_ms == 0) {
+ return NGX_DECLINED;
+ }
+
+ sem->wait_count++;
+ wait_co_ctx = ctx->cur_co_ctx;
+
+ wait_co_ctx->sleep.handler = ngx_stream_lua_sema_timeout_handler;
+ wait_co_ctx->sleep.data = ctx->cur_co_ctx;
+ wait_co_ctx->sleep.log = r->connection->log;
+
+ ngx_add_timer(&wait_co_ctx->sleep, (ngx_msec_t) wait_ms);
+
+ dd("ngx_stream_lua_ffi_sema_wait add timer coctx:%p wait: %d(ms)",
+ wait_co_ctx, wait_ms);
+
+ ngx_queue_insert_tail(&sem->wait_queue, &wait_co_ctx->sem_wait_queue);
+
+ wait_co_ctx->data = sem;
+ wait_co_ctx->cleanup = ngx_stream_lua_sema_cleanup;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "stream lua semaphore wait yielding");
+
+ return NGX_AGAIN;
+}
+
+
+int
+ngx_stream_lua_ffi_sema_count(ngx_stream_lua_sema_t *sem)
+{
+ return sem->resource_count - sem->wait_count;
+}
+
+
+static void
+ngx_stream_lua_sema_cleanup(void *data)
+{
+ ngx_stream_lua_co_ctx_t *coctx = data;
+ ngx_queue_t *q;
+ ngx_stream_lua_sema_t *sem;
+
+ sem = coctx->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "stream lua semaphore cleanup");
+
+ if (coctx->sleep.timer_set) {
+ ngx_del_timer(&coctx->sleep);
+ }
+
+ q = &coctx->sem_wait_queue;
+
+ ngx_queue_remove(q);
+ sem->wait_count--;
+ coctx->cleanup = NULL;
+}
+
+
+static void
+ngx_stream_lua_sema_handler(ngx_event_t *ev)
+{
+ ngx_stream_lua_sema_t *sem;
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *wait_co_ctx;
+ ngx_queue_t *q;
+
+ sem = ev->data;
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "semaphore handler: wait queue: %sempty, resource count: %d",
+ ngx_queue_empty(&sem->wait_queue) ? "" : "not ",
+ sem->resource_count);
+
+ while (!ngx_queue_empty(&sem->wait_queue) && sem->resource_count > 0) {
+
+ q = ngx_queue_head(&sem->wait_queue);
+ ngx_queue_remove(q);
+
+ sem->wait_count--;
+
+ wait_co_ctx = ngx_queue_data(q, ngx_stream_lua_co_ctx_t, sem_wait_queue);
+ wait_co_ctx->cleanup = NULL;
+
+ if (wait_co_ctx->sleep.timer_set) {
+ ngx_del_timer(&wait_co_ctx->sleep);
+ }
+
+ r = ngx_stream_lua_get_req(wait_co_ctx->co);
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ ngx_stream_lua_assert(ctx != NULL);
+
+ sem->resource_count--;
+
+ ctx->cur_co_ctx = wait_co_ctx;
+
+ wait_co_ctx->sem_resume_status = SEMAPHORE_WAIT_SUCC;
+
+ if (ctx->entered_content_phase) {
+ (void) ngx_stream_lua_sema_resume(r);
+
+ } else {
+ ctx->resume_handler = ngx_stream_lua_sema_resume;
+ ngx_stream_lua_core_run_phases(r);
+ }
+
+ }
+}
+
+
+static void
+ngx_stream_lua_sema_timeout_handler(ngx_event_t *ev)
+{
+ ngx_stream_lua_co_ctx_t *wait_co_ctx;
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_sema_t *sem;
+
+ wait_co_ctx = ev->data;
+ wait_co_ctx->cleanup = NULL;
+
+ dd("ngx_stream_lua_sema_timeout_handler timeout coctx:%p", wait_co_ctx);
+
+ sem = wait_co_ctx->data;
+
+ ngx_queue_remove(&wait_co_ctx->sem_wait_queue);
+ sem->wait_count--;
+
+ r = ngx_stream_lua_get_req(wait_co_ctx->co);
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ ngx_stream_lua_assert(ctx != NULL);
+
+ ctx->cur_co_ctx = wait_co_ctx;
+
+ wait_co_ctx->sem_resume_status = SEMAPHORE_WAIT_TIMEOUT;
+
+ if (ctx->entered_content_phase) {
+ (void) ngx_stream_lua_sema_resume(r);
+
+ } else {
+ ctx->resume_handler = ngx_stream_lua_sema_resume;
+ ngx_stream_lua_core_run_phases(r);
+ }
+
+}
+
+
+void
+ngx_stream_lua_ffi_sema_gc(ngx_stream_lua_sema_t *sem)
+{
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "in lua gc, semaphore %p", sem);
+
+ if (sem == NULL) {
+ return;
+ }
+
+ if (!ngx_terminate
+ && !ngx_quit
+ && !ngx_queue_empty(&sem->wait_queue))
+ {
+ ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
+ "in lua semaphore gc wait queue is"
+ " not empty while the semaphore %p is being "
+ "destroyed", sem);
+ }
+
+ if (sem->sem_event.posted) {
+ ngx_delete_posted_event(&sem->sem_event);
+ }
+
+ ngx_stream_lua_free_sema(sem);
+}
+
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_semaphore.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_semaphore.h
new file mode 100644
index 0000000..a4a2e54
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_semaphore.h
@@ -0,0 +1,59 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_semaphore.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ * Copyright (C) cuiweixie
+ * I hereby assign copyright in this code to the lua-nginx-module project,
+ * to be licensed under the same terms as the rest of the code.
+ */
+
+
+#ifndef _NGX_STREAM_LUA_SEMAPHORE_H_INCLUDED_
+#define _NGX_STREAM_LUA_SEMAPHORE_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+typedef struct ngx_stream_lua_sema_mm_block_s {
+ ngx_uint_t used;
+ ngx_stream_lua_sema_mm_t *mm;
+ ngx_uint_t epoch;
+} ngx_stream_lua_sema_mm_block_t;
+
+
+struct ngx_stream_lua_sema_mm_s {
+ ngx_queue_t free_queue;
+ ngx_uint_t total;
+ ngx_uint_t used;
+ ngx_uint_t num_per_block;
+ ngx_uint_t cur_epoch;
+ ngx_stream_lua_main_conf_t *lmcf;
+};
+
+
+typedef struct ngx_stream_lua_sema_s {
+ ngx_queue_t wait_queue;
+ ngx_queue_t chain;
+ ngx_event_t sem_event;
+ ngx_stream_lua_sema_mm_block_t *block;
+ int resource_count;
+ unsigned wait_count;
+} ngx_stream_lua_sema_t;
+
+
+void ngx_stream_lua_sema_mm_cleanup(void *data);
+ngx_int_t ngx_stream_lua_sema_mm_init(ngx_conf_t *cf,
+ ngx_stream_lua_main_conf_t *lmcf);
+
+
+#endif /* _NGX_STREAM_LUA_SEMAPHORE_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_shdict.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_shdict.c
new file mode 100644
index 0000000..9f7f61b
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_shdict.c
@@ -0,0 +1,2068 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_shdict.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_shdict.h"
+#include "ngx_stream_lua_util.h"
+#include "ngx_stream_lua_api.h"
+
+
+static int ngx_stream_lua_shdict_expire(ngx_stream_lua_shdict_ctx_t *ctx,
+ ngx_uint_t n);
+static ngx_int_t ngx_stream_lua_shdict_lookup(ngx_shm_zone_t *shm_zone,
+ ngx_uint_t hash, u_char *kdata, size_t klen,
+ ngx_stream_lua_shdict_node_t **sdp);
+static int ngx_stream_lua_shdict_flush_expired(lua_State *L);
+static int ngx_stream_lua_shdict_get_keys(lua_State *L);
+static int ngx_stream_lua_shdict_lpush(lua_State *L);
+static int ngx_stream_lua_shdict_rpush(lua_State *L);
+static int ngx_stream_lua_shdict_push_helper(lua_State *L, int flags);
+static int ngx_stream_lua_shdict_lpop(lua_State *L);
+static int ngx_stream_lua_shdict_rpop(lua_State *L);
+static int ngx_stream_lua_shdict_pop_helper(lua_State *L, int flags);
+static int ngx_stream_lua_shdict_llen(lua_State *L);
+
+
+static ngx_inline ngx_shm_zone_t *ngx_stream_lua_shdict_get_zone(lua_State *L,
+ int index);
+
+
+#define NGX_STREAM_LUA_SHDICT_ADD 0x0001
+#define NGX_STREAM_LUA_SHDICT_REPLACE 0x0002
+#define NGX_STREAM_LUA_SHDICT_SAFE_STORE 0x0004
+
+
+#define NGX_STREAM_LUA_SHDICT_LEFT 0x0001
+#define NGX_STREAM_LUA_SHDICT_RIGHT 0x0002
+
+
+enum {
+ SHDICT_USERDATA_INDEX = 1,
+};
+
+
+enum {
+ SHDICT_TNIL = 0, /* same as LUA_TNIL */
+ SHDICT_TBOOLEAN = 1, /* same as LUA_TBOOLEAN */
+ SHDICT_TNUMBER = 3, /* same as LUA_TNUMBER */
+ SHDICT_TSTRING = 4, /* same as LUA_TSTRING */
+ SHDICT_TLIST = 5,
+};
+
+
+static ngx_inline ngx_queue_t *
+ngx_stream_lua_shdict_get_list_head(ngx_stream_lua_shdict_node_t *sd,
+ size_t len)
+{
+ return (ngx_queue_t *) ngx_align_ptr(((u_char *) &sd->data + len),
+ NGX_ALIGNMENT);
+}
+
+
+ngx_int_t
+ngx_stream_lua_shdict_init_zone(ngx_shm_zone_t *shm_zone, void *data)
+{
+ ngx_stream_lua_shdict_ctx_t *octx = data;
+ ngx_stream_lua_shdict_ctx_t *ctx;
+
+ size_t len;
+
+ dd("init zone");
+
+ ctx = shm_zone->data;
+
+ if (octx) {
+ ctx->sh = octx->sh;
+ ctx->shpool = octx->shpool;
+
+ return NGX_OK;
+ }
+
+ ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
+
+ if (shm_zone->shm.exists) {
+ ctx->sh = ctx->shpool->data;
+
+ return NGX_OK;
+ }
+
+ ctx->sh = ngx_slab_alloc(ctx->shpool,
+ sizeof(ngx_stream_lua_shdict_shctx_t));
+ if (ctx->sh == NULL) {
+ return NGX_ERROR;
+ }
+
+ ctx->shpool->data = ctx->sh;
+
+ ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel,
+ ngx_stream_lua_shdict_rbtree_insert_value);
+
+ ngx_queue_init(&ctx->sh->lru_queue);
+
+ len = sizeof(" in lua_shared_dict zone \"\"") + shm_zone->shm.name.len;
+
+ ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len);
+ if (ctx->shpool->log_ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_sprintf(ctx->shpool->log_ctx, " in lua_shared_dict zone \"%V\"%Z",
+ &shm_zone->shm.name);
+
+ ctx->shpool->log_nomem = 0;
+
+ return NGX_OK;
+}
+
+
+void
+ngx_stream_lua_shdict_rbtree_insert_value(ngx_rbtree_node_t *temp,
+ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
+{
+ ngx_rbtree_node_t **p;
+
+ ngx_stream_lua_shdict_node_t *sdn, *sdnt;
+
+ for ( ;; ) {
+
+ if (node->key < temp->key) {
+
+ p = &temp->left;
+
+ } else if (node->key > temp->key) {
+
+ p = &temp->right;
+
+ } else { /* node->key == temp->key */
+
+ sdn = (ngx_stream_lua_shdict_node_t *) &node->color;
+ sdnt = (ngx_stream_lua_shdict_node_t *) &temp->color;
+
+ p = ngx_memn2cmp(sdn->data, sdnt->data, sdn->key_len,
+ sdnt->key_len) < 0 ? &temp->left : &temp->right;
+ }
+
+ if (*p == sentinel) {
+ break;
+ }
+
+ temp = *p;
+ }
+
+ *p = node;
+ node->parent = temp;
+ node->left = sentinel;
+ node->right = sentinel;
+ ngx_rbt_red(node);
+}
+
+
+static ngx_int_t
+ngx_stream_lua_shdict_lookup(ngx_shm_zone_t *shm_zone, ngx_uint_t hash,
+ u_char *kdata, size_t klen, ngx_stream_lua_shdict_node_t **sdp)
+{
+ ngx_int_t rc;
+ ngx_time_t *tp;
+ uint64_t now;
+ int64_t ms;
+ ngx_rbtree_node_t *node, *sentinel;
+
+ ngx_stream_lua_shdict_ctx_t *ctx;
+ ngx_stream_lua_shdict_node_t *sd;
+
+ ctx = shm_zone->data;
+
+ node = ctx->sh->rbtree.root;
+ sentinel = ctx->sh->rbtree.sentinel;
+
+ while (node != sentinel) {
+
+ if (hash < node->key) {
+ node = node->left;
+ continue;
+ }
+
+ if (hash > node->key) {
+ node = node->right;
+ continue;
+ }
+
+ /* hash == node->key */
+
+ sd = (ngx_stream_lua_shdict_node_t *) &node->color;
+
+ rc = ngx_memn2cmp(kdata, sd->data, klen, (size_t) sd->key_len);
+
+ if (rc == 0) {
+ ngx_queue_remove(&sd->queue);
+ ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
+
+ *sdp = sd;
+
+ dd("node expires: %lld", (long long) sd->expires);
+
+ if (sd->expires != 0) {
+ tp = ngx_timeofday();
+
+ now = (uint64_t) tp->sec * 1000 + tp->msec;
+ ms = sd->expires - now;
+
+ dd("time to live: %lld", (long long) ms);
+
+ if (ms <= 0) {
+ dd("node already expired");
+ return NGX_DONE;
+ }
+ }
+
+ return NGX_OK;
+ }
+
+ node = (rc < 0) ? node->left : node->right;
+ }
+
+ *sdp = NULL;
+
+ return NGX_DECLINED;
+}
+
+
+static int
+ngx_stream_lua_shdict_expire(ngx_stream_lua_shdict_ctx_t *ctx, ngx_uint_t n)
+{
+ ngx_time_t *tp;
+ uint64_t now;
+ ngx_queue_t *q, *list_queue, *lq;
+ int64_t ms;
+ ngx_rbtree_node_t *node;
+ int freed = 0;
+
+ ngx_stream_lua_shdict_node_t *sd;
+ ngx_stream_lua_shdict_list_node_t *lnode;
+
+ tp = ngx_timeofday();
+
+ now = (uint64_t) tp->sec * 1000 + tp->msec;
+
+ /*
+ * n == 1 deletes one or two expired entries
+ * n == 0 deletes oldest entry by force
+ * and one or two zero rate entries
+ */
+
+ while (n < 3) {
+
+ if (ngx_queue_empty(&ctx->sh->lru_queue)) {
+ return freed;
+ }
+
+ q = ngx_queue_last(&ctx->sh->lru_queue);
+
+ sd = ngx_queue_data(q, ngx_stream_lua_shdict_node_t, queue);
+
+ if (n++ != 0) {
+
+ if (sd->expires == 0) {
+ return freed;
+ }
+
+ ms = sd->expires - now;
+ if (ms > 0) {
+ return freed;
+ }
+ }
+
+ if (sd->value_type == SHDICT_TLIST) {
+ list_queue = ngx_stream_lua_shdict_get_list_head(sd,
+ sd->key_len);
+
+ for (lq = ngx_queue_head(list_queue);
+ lq != ngx_queue_sentinel(list_queue);
+ lq = ngx_queue_next(lq))
+ {
+ lnode = ngx_queue_data(lq, ngx_stream_lua_shdict_list_node_t,
+ queue);
+
+ ngx_slab_free_locked(ctx->shpool, lnode);
+ }
+ }
+
+ ngx_queue_remove(q);
+
+ node = (ngx_rbtree_node_t *)
+ ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));
+
+ ngx_rbtree_delete(&ctx->sh->rbtree, node);
+
+ ngx_slab_free_locked(ctx->shpool, node);
+
+ freed++;
+ }
+
+ return freed;
+}
+
+
+void
+ngx_stream_lua_inject_shdict_api(ngx_stream_lua_main_conf_t *lmcf, lua_State *L)
+{
+ ngx_stream_lua_shdict_ctx_t *ctx;
+
+ ngx_uint_t i;
+ ngx_shm_zone_t **zone;
+ ngx_shm_zone_t **zone_udata;
+
+ if (lmcf->shdict_zones != NULL) {
+ lua_createtable(L, 0, lmcf->shdict_zones->nelts /* nrec */);
+ /* ngx.shared */
+
+ lua_createtable(L, 0 /* narr */, 22 /* nrec */); /* shared mt */
+
+ lua_pushcfunction(L, ngx_stream_lua_shdict_lpush);
+ lua_setfield(L, -2, "lpush");
+
+ lua_pushcfunction(L, ngx_stream_lua_shdict_rpush);
+ lua_setfield(L, -2, "rpush");
+
+ lua_pushcfunction(L, ngx_stream_lua_shdict_lpop);
+ lua_setfield(L, -2, "lpop");
+
+ lua_pushcfunction(L, ngx_stream_lua_shdict_rpop);
+ lua_setfield(L, -2, "rpop");
+
+ lua_pushcfunction(L, ngx_stream_lua_shdict_llen);
+ lua_setfield(L, -2, "llen");
+
+ lua_pushcfunction(L, ngx_stream_lua_shdict_flush_expired);
+ lua_setfield(L, -2, "flush_expired");
+
+ lua_pushcfunction(L, ngx_stream_lua_shdict_get_keys);
+ lua_setfield(L, -2, "get_keys");
+
+ lua_pushvalue(L, -1); /* shared mt mt */
+ lua_setfield(L, -2, "__index"); /* shared mt */
+
+ zone = lmcf->shdict_zones->elts;
+
+ for (i = 0; i < lmcf->shdict_zones->nelts; i++) {
+ ctx = zone[i]->data;
+
+ lua_pushlstring(L, (char *) ctx->name.data, ctx->name.len);
+ /* shared mt key */
+
+ lua_createtable(L, 1 /* narr */, 0 /* nrec */);
+ /* table of zone[i] */
+ zone_udata = lua_newuserdata(L, sizeof(ngx_shm_zone_t *));
+ /* shared mt key ud */
+ *zone_udata = zone[i];
+ lua_rawseti(L, -2, SHDICT_USERDATA_INDEX); /* {zone[i]} */
+ lua_pushvalue(L, -3); /* shared mt key ud mt */
+ lua_setmetatable(L, -2); /* shared mt key ud */
+ lua_rawset(L, -4); /* shared mt */
+ }
+
+ lua_pop(L, 1); /* shared */
+
+ } else {
+ lua_newtable(L); /* ngx.shared */
+ }
+
+ lua_setfield(L, -2, "shared");
+}
+
+
+static ngx_inline ngx_shm_zone_t *
+ngx_stream_lua_shdict_get_zone(lua_State *L, int index)
+{
+ ngx_shm_zone_t *zone;
+ ngx_shm_zone_t **zone_udata;
+
+ lua_rawgeti(L, index, SHDICT_USERDATA_INDEX);
+ zone_udata = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ if (zone_udata == NULL) {
+ return NULL;
+ }
+
+ zone = *zone_udata;
+ return zone;
+}
+
+
+static int
+ngx_stream_lua_shdict_flush_expired(lua_State *L)
+{
+ ngx_queue_t *q, *prev, *list_queue, *lq;
+ ngx_shm_zone_t *zone;
+ ngx_time_t *tp;
+ int freed = 0;
+ int attempts = 0;
+ ngx_rbtree_node_t *node;
+ uint64_t now;
+ int n;
+
+ ngx_stream_lua_shdict_node_t *sd;
+ ngx_stream_lua_shdict_ctx_t *ctx;
+ ngx_stream_lua_shdict_list_node_t *lnode;
+
+ n = lua_gettop(L);
+
+ if (n != 1 && n != 2) {
+ return luaL_error(L, "expecting 1 or 2 argument(s), but saw %d", n);
+ }
+
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ zone = ngx_stream_lua_shdict_get_zone(L, 1);
+ if (zone == NULL) {
+ return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer");
+ }
+
+ if (n == 2) {
+ attempts = luaL_checkint(L, 2);
+ }
+
+ ctx = zone->data;
+
+ ngx_shmtx_lock(&ctx->shpool->mutex);
+
+ if (ngx_queue_empty(&ctx->sh->lru_queue)) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+ lua_pushnumber(L, 0);
+ return 1;
+ }
+
+ tp = ngx_timeofday();
+
+ now = (uint64_t) tp->sec * 1000 + tp->msec;
+
+ q = ngx_queue_last(&ctx->sh->lru_queue);
+
+ while (q != ngx_queue_sentinel(&ctx->sh->lru_queue)) {
+ prev = ngx_queue_prev(q);
+
+ sd = ngx_queue_data(q, ngx_stream_lua_shdict_node_t, queue);
+
+ if (sd->expires != 0 && sd->expires <= now) {
+
+ if (sd->value_type == SHDICT_TLIST) {
+ list_queue = ngx_stream_lua_shdict_get_list_head(sd,
+ sd->key_len);
+
+ for (lq = ngx_queue_head(list_queue);
+ lq != ngx_queue_sentinel(list_queue);
+ lq = ngx_queue_next(lq))
+ {
+ lnode = ngx_queue_data(lq,
+ ngx_stream_lua_shdict_list_node_t,
+ queue);
+
+ ngx_slab_free_locked(ctx->shpool, lnode);
+ }
+ }
+
+ ngx_queue_remove(q);
+
+ node = (ngx_rbtree_node_t *)
+ ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));
+
+ ngx_rbtree_delete(&ctx->sh->rbtree, node);
+ ngx_slab_free_locked(ctx->shpool, node);
+ freed++;
+
+ if (attempts && freed == attempts) {
+ break;
+ }
+ }
+
+ q = prev;
+ }
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ lua_pushnumber(L, freed);
+ return 1;
+}
+
+
+/*
+ * This trades CPU for memory. This is potentially slow. O(2n)
+ */
+
+static int
+ngx_stream_lua_shdict_get_keys(lua_State *L)
+{
+ ngx_queue_t *q, *prev;
+ ngx_shm_zone_t *zone;
+ ngx_time_t *tp;
+ int total = 0;
+ int attempts = 1024;
+ uint64_t now;
+ int n;
+
+ ngx_stream_lua_shdict_node_t *sd;
+ ngx_stream_lua_shdict_ctx_t *ctx;
+
+ n = lua_gettop(L);
+
+ if (n != 1 && n != 2) {
+ return luaL_error(L, "expecting 1 or 2 argument(s), "
+ "but saw %d", n);
+ }
+
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ zone = ngx_stream_lua_shdict_get_zone(L, 1);
+ if (zone == NULL) {
+ return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer");
+ }
+
+ if (n == 2) {
+ attempts = luaL_checkint(L, 2);
+ }
+
+ ctx = zone->data;
+
+ ngx_shmtx_lock(&ctx->shpool->mutex);
+
+ if (ngx_queue_empty(&ctx->sh->lru_queue)) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+ lua_createtable(L, 0, 0);
+ return 1;
+ }
+
+ tp = ngx_timeofday();
+
+ now = (uint64_t) tp->sec * 1000 + tp->msec;
+
+ /* first run through: get total number of elements we need to allocate */
+
+ q = ngx_queue_last(&ctx->sh->lru_queue);
+
+ while (q != ngx_queue_sentinel(&ctx->sh->lru_queue)) {
+ prev = ngx_queue_prev(q);
+
+ sd = ngx_queue_data(q, ngx_stream_lua_shdict_node_t, queue);
+
+ if (sd->expires == 0 || sd->expires > now) {
+ total++;
+ if (attempts && total == attempts) {
+ break;
+ }
+ }
+
+ q = prev;
+ }
+
+ lua_createtable(L, total, 0);
+
+ /* second run through: add keys to table */
+
+ total = 0;
+ q = ngx_queue_last(&ctx->sh->lru_queue);
+
+ while (q != ngx_queue_sentinel(&ctx->sh->lru_queue)) {
+ prev = ngx_queue_prev(q);
+
+ sd = ngx_queue_data(q, ngx_stream_lua_shdict_node_t, queue);
+
+ if (sd->expires == 0 || sd->expires > now) {
+ lua_pushlstring(L, (char *) sd->data, sd->key_len);
+ lua_rawseti(L, -2, ++total);
+ if (attempts && total == attempts) {
+ break;
+ }
+ }
+
+ q = prev;
+ }
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ /* table is at top of stack */
+ return 1;
+}
+
+
+
+
+static int
+ngx_stream_lua_shdict_lpush(lua_State *L)
+{
+ return ngx_stream_lua_shdict_push_helper(L, NGX_STREAM_LUA_SHDICT_LEFT);
+}
+
+
+static int
+ngx_stream_lua_shdict_rpush(lua_State *L)
+{
+ return ngx_stream_lua_shdict_push_helper(L, NGX_STREAM_LUA_SHDICT_RIGHT);
+}
+
+
+static int
+ngx_stream_lua_shdict_push_helper(lua_State *L, int flags)
+{
+ int n;
+ ngx_str_t key;
+ uint32_t hash;
+ ngx_int_t rc;
+ ngx_str_t value;
+ int value_type;
+ double num;
+ ngx_rbtree_node_t *node;
+ ngx_shm_zone_t *zone;
+ ngx_queue_t *queue, *q;
+
+ ngx_stream_lua_shdict_ctx_t *ctx;
+ ngx_stream_lua_shdict_node_t *sd;
+ ngx_stream_lua_shdict_list_node_t *lnode;
+
+ n = lua_gettop(L);
+
+ if (n != 3) {
+ return luaL_error(L, "expecting 3 arguments, "
+ "but only seen %d", n);
+ }
+
+ if (lua_type(L, 1) != LUA_TTABLE) {
+ return luaL_error(L, "bad \"zone\" argument");
+ }
+
+ zone = ngx_stream_lua_shdict_get_zone(L, 1);
+ if (zone == NULL) {
+ return luaL_error(L, "bad \"zone\" argument");
+ }
+
+ ctx = zone->data;
+
+ if (lua_isnil(L, 2)) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "nil key");
+ return 2;
+ }
+
+ key.data = (u_char *) luaL_checklstring(L, 2, &key.len);
+
+ if (key.len == 0) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "empty key");
+ return 2;
+ }
+
+ if (key.len > 65535) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "key too long");
+ return 2;
+ }
+
+ hash = ngx_crc32_short(key.data, key.len);
+
+ value_type = lua_type(L, 3);
+
+ switch (value_type) {
+
+ case SHDICT_TSTRING:
+ value.data = (u_char *) lua_tolstring(L, 3, &value.len);
+ break;
+
+ case SHDICT_TNUMBER:
+ value.len = sizeof(double);
+ num = lua_tonumber(L, 3);
+ value.data = (u_char *) &num;
+ break;
+
+ default:
+ lua_pushnil(L);
+ lua_pushliteral(L, "bad value type");
+ return 2;
+ }
+
+ ngx_shmtx_lock(&ctx->shpool->mutex);
+
+#if 1
+ ngx_stream_lua_shdict_expire(ctx, 1);
+#endif
+
+ rc = ngx_stream_lua_shdict_lookup(zone, hash, key.data, key.len, &sd);
+
+ dd("shdict lookup returned %d", (int) rc);
+
+ /* exists but expired */
+
+ if (rc == NGX_DONE) {
+
+ if (sd->value_type != SHDICT_TLIST) {
+ /* TODO: reuse when length matched */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
+ "lua shared dict push: found old entry and value "
+ "type not matched, remove it first");
+
+ ngx_queue_remove(&sd->queue);
+
+ node = (ngx_rbtree_node_t *)
+ ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));
+
+ ngx_rbtree_delete(&ctx->sh->rbtree, node);
+
+ ngx_slab_free_locked(ctx->shpool, node);
+
+ dd("go to init_list");
+ goto init_list;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
+ "lua shared dict push: found old entry and value "
+ "type matched, reusing it");
+
+ sd->expires = 0;
+ sd->value_len = 0;
+ /* free list nodes */
+
+ queue = ngx_stream_lua_shdict_get_list_head(sd, key.len);
+
+ for (q = ngx_queue_head(queue);
+ q != ngx_queue_sentinel(queue);
+ q = ngx_queue_next(q))
+ {
+ /* TODO: reuse matched size list node */
+ lnode = ngx_queue_data(q, ngx_stream_lua_shdict_list_node_t, queue);
+ ngx_slab_free_locked(ctx->shpool, lnode);
+ }
+
+ ngx_queue_init(queue);
+
+ ngx_queue_remove(&sd->queue);
+ ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
+
+ dd("go to push_node");
+ goto push_node;
+ }
+
+ /* exists and not expired */
+
+ if (rc == NGX_OK) {
+
+ if (sd->value_type != SHDICT_TLIST) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ lua_pushnil(L);
+ lua_pushliteral(L, "value not a list");
+ return 2;
+ }
+
+ queue = ngx_stream_lua_shdict_get_list_head(sd, key.len);
+
+ ngx_queue_remove(&sd->queue);
+ ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
+
+ dd("go to push_node");
+ goto push_node;
+ }
+
+ /* rc == NGX_DECLINED, not found */
+
+init_list:
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
+ "lua shared dict list: creating a new entry");
+
+ /* NOTICE: we assume the begin point aligned in slab, be careful */
+ n = offsetof(ngx_rbtree_node_t, color)
+ + offsetof(ngx_stream_lua_shdict_node_t, data)
+ + key.len
+ + sizeof(ngx_queue_t);
+
+ dd("length before aligned: %d", n);
+
+ n = (int) (uintptr_t) ngx_align_ptr(n, NGX_ALIGNMENT);
+
+ dd("length after aligned: %d", n);
+
+ node = ngx_slab_alloc_locked(ctx->shpool, n);
+
+ if (node == NULL) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ lua_pushboolean(L, 0);
+ lua_pushliteral(L, "no memory");
+ return 2;
+ }
+
+ sd = (ngx_stream_lua_shdict_node_t *) &node->color;
+
+ queue = ngx_stream_lua_shdict_get_list_head(sd, key.len);
+
+ node->key = hash;
+ sd->key_len = (u_short) key.len;
+
+ sd->expires = 0;
+
+ sd->value_len = 0;
+
+ dd("setting value type to %d", (int) SHDICT_TLIST);
+
+ sd->value_type = (uint8_t) SHDICT_TLIST;
+
+ ngx_memcpy(sd->data, key.data, key.len);
+
+ ngx_queue_init(queue);
+
+ ngx_rbtree_insert(&ctx->sh->rbtree, node);
+
+ ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
+
+push_node:
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
+ "lua shared dict list: creating a new list node");
+
+ n = offsetof(ngx_stream_lua_shdict_list_node_t, data)
+ + value.len;
+
+ dd("list node length: %d", n);
+
+ lnode = ngx_slab_alloc_locked(ctx->shpool, n);
+
+ if (lnode == NULL) {
+
+ if (sd->value_len == 0) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
+ "lua shared dict list: no memory for create"
+ " list node and list empty, remove it");
+
+ ngx_queue_remove(&sd->queue);
+
+ node = (ngx_rbtree_node_t *)
+ ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));
+
+ ngx_rbtree_delete(&ctx->sh->rbtree, node);
+
+ ngx_slab_free_locked(ctx->shpool, node);
+ }
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ lua_pushboolean(L, 0);
+ lua_pushliteral(L, "no memory");
+ return 2;
+ }
+
+ dd("setting list length to %d", sd->value_len + 1);
+
+ sd->value_len = sd->value_len + 1;
+
+ dd("setting list node value length to %d", (int) value.len);
+
+ lnode->value_len = (uint32_t) value.len;
+
+ dd("setting list node value type to %d", value_type);
+
+ lnode->value_type = (uint8_t) value_type;
+
+ ngx_memcpy(lnode->data, value.data, value.len);
+
+ if (flags == NGX_STREAM_LUA_SHDICT_LEFT) {
+ ngx_queue_insert_head(queue, &lnode->queue);
+
+ } else {
+ ngx_queue_insert_tail(queue, &lnode->queue);
+ }
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ lua_pushnumber(L, sd->value_len);
+ return 1;
+}
+
+
+static int
+ngx_stream_lua_shdict_lpop(lua_State *L)
+{
+ return ngx_stream_lua_shdict_pop_helper(L, NGX_STREAM_LUA_SHDICT_LEFT);
+}
+
+
+static int
+ngx_stream_lua_shdict_rpop(lua_State *L)
+{
+ return ngx_stream_lua_shdict_pop_helper(L, NGX_STREAM_LUA_SHDICT_RIGHT);
+}
+
+
+static int
+ngx_stream_lua_shdict_pop_helper(lua_State *L, int flags)
+{
+ int n;
+ ngx_str_t name;
+ ngx_str_t key;
+ uint32_t hash;
+ ngx_int_t rc;
+ ngx_str_t value;
+ int value_type;
+ double num;
+ ngx_rbtree_node_t *node;
+ ngx_shm_zone_t *zone;
+ ngx_queue_t *queue;
+
+ ngx_stream_lua_shdict_ctx_t *ctx;
+ ngx_stream_lua_shdict_node_t *sd;
+ ngx_stream_lua_shdict_list_node_t *lnode;
+
+ n = lua_gettop(L);
+
+ if (n != 2) {
+ return luaL_error(L, "expecting 2 arguments, "
+ "but only seen %d", n);
+ }
+
+ if (lua_type(L, 1) != LUA_TTABLE) {
+ return luaL_error(L, "bad \"zone\" argument");
+ }
+
+ zone = ngx_stream_lua_shdict_get_zone(L, 1);
+ if (zone == NULL) {
+ return luaL_error(L, "bad \"zone\" argument");
+ }
+
+ ctx = zone->data;
+ name = ctx->name;
+
+ if (lua_isnil(L, 2)) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "nil key");
+ return 2;
+ }
+
+ key.data = (u_char *) luaL_checklstring(L, 2, &key.len);
+
+ if (key.len == 0) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "empty key");
+ return 2;
+ }
+
+ if (key.len > 65535) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "key too long");
+ return 2;
+ }
+
+ hash = ngx_crc32_short(key.data, key.len);
+
+ ngx_shmtx_lock(&ctx->shpool->mutex);
+
+#if 1
+ ngx_stream_lua_shdict_expire(ctx, 1);
+#endif
+
+ rc = ngx_stream_lua_shdict_lookup(zone, hash, key.data, key.len, &sd);
+
+ dd("shdict lookup returned %d", (int) rc);
+
+ if (rc == NGX_DECLINED || rc == NGX_DONE) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+ lua_pushnil(L);
+ return 1;
+ }
+
+ /* rc == NGX_OK */
+
+ if (sd->value_type != SHDICT_TLIST) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ lua_pushnil(L);
+ lua_pushliteral(L, "value not a list");
+ return 2;
+ }
+
+ if (sd->value_len <= 0) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ return luaL_error(L, "bad lua list length found for key %s "
+ "in shared_dict %s: %lu", key.data, name.data,
+ (unsigned long) sd->value_len);
+ }
+
+ queue = ngx_stream_lua_shdict_get_list_head(sd, key.len);
+
+ if (flags == NGX_STREAM_LUA_SHDICT_LEFT) {
+ queue = ngx_queue_head(queue);
+
+ } else {
+ queue = ngx_queue_last(queue);
+ }
+
+ lnode = ngx_queue_data(queue, ngx_stream_lua_shdict_list_node_t, queue);
+
+ value_type = lnode->value_type;
+
+ dd("data: %p", lnode->data);
+ dd("value len: %d", (int) sd->value_len);
+
+ value.data = lnode->data;
+ value.len = (size_t) lnode->value_len;
+
+ switch (value_type) {
+
+ case SHDICT_TSTRING:
+
+ lua_pushlstring(L, (char *) value.data, value.len);
+ break;
+
+ case SHDICT_TNUMBER:
+
+ if (value.len != sizeof(double)) {
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ return luaL_error(L, "bad lua list node number value size found "
+ "for key %s in shared_dict %s: %lu", key.data,
+ name.data, (unsigned long) value.len);
+ }
+
+ ngx_memcpy(&num, value.data, sizeof(double));
+
+ lua_pushnumber(L, num);
+ break;
+
+ default:
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ return luaL_error(L, "bad list node value type found for key %s in "
+ "shared_dict %s: %d", key.data, name.data,
+ value_type);
+ }
+
+ ngx_queue_remove(queue);
+
+ ngx_slab_free_locked(ctx->shpool, lnode);
+
+ if (sd->value_len == 1) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
+ "lua shared dict list: empty node after pop, "
+ "remove it");
+
+ ngx_queue_remove(&sd->queue);
+
+ node = (ngx_rbtree_node_t *)
+ ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));
+
+ ngx_rbtree_delete(&ctx->sh->rbtree, node);
+
+ ngx_slab_free_locked(ctx->shpool, node);
+
+ } else {
+ sd->value_len = sd->value_len - 1;
+
+ ngx_queue_remove(&sd->queue);
+ ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
+ }
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ return 1;
+}
+
+
+static int
+ngx_stream_lua_shdict_llen(lua_State *L)
+{
+ int n;
+ ngx_str_t key;
+ uint32_t hash;
+ ngx_int_t rc;
+ ngx_shm_zone_t *zone;
+
+ ngx_stream_lua_shdict_ctx_t *ctx;
+ ngx_stream_lua_shdict_node_t *sd;
+
+ n = lua_gettop(L);
+
+ if (n != 2) {
+ return luaL_error(L, "expecting 2 arguments, "
+ "but only seen %d", n);
+ }
+
+ if (lua_type(L, 1) != LUA_TTABLE) {
+ return luaL_error(L, "bad \"zone\" argument");
+ }
+
+ zone = ngx_stream_lua_shdict_get_zone(L, 1);
+ if (zone == NULL) {
+ return luaL_error(L, "bad \"zone\" argument");
+ }
+
+ ctx = zone->data;
+
+ if (lua_isnil(L, 2)) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "nil key");
+ return 2;
+ }
+
+ key.data = (u_char *) luaL_checklstring(L, 2, &key.len);
+
+ if (key.len == 0) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "empty key");
+ return 2;
+ }
+
+ if (key.len > 65535) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "key too long");
+ return 2;
+ }
+
+ hash = ngx_crc32_short(key.data, key.len);
+
+ ngx_shmtx_lock(&ctx->shpool->mutex);
+
+#if 1
+ ngx_stream_lua_shdict_expire(ctx, 1);
+#endif
+
+ rc = ngx_stream_lua_shdict_lookup(zone, hash, key.data, key.len, &sd);
+
+ dd("shdict lookup returned %d", (int) rc);
+
+ if (rc == NGX_OK) {
+
+ if (sd->value_type != SHDICT_TLIST) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ lua_pushnil(L);
+ lua_pushliteral(L, "value not a list");
+ return 2;
+ }
+
+ ngx_queue_remove(&sd->queue);
+ ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ lua_pushnumber(L, (lua_Number) sd->value_len);
+ return 1;
+ }
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ lua_pushnumber(L, 0);
+ return 1;
+}
+
+
+ngx_shm_zone_t *
+ngx_stream_lua_find_zone(u_char *name_data, size_t name_len)
+{
+ ngx_str_t *name;
+ ngx_uint_t i;
+ ngx_shm_zone_t *zone;
+ volatile ngx_list_part_t *part;
+
+ ngx_stream_lua_shm_zone_ctx_t *ctx;
+
+ part = &ngx_cycle->shared_memory.part;
+ zone = part->elts;
+
+ for (i = 0; /* void */ ; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ zone = part->elts;
+ i = 0;
+ }
+
+ name = &zone[i].shm.name;
+
+ dd("name: [%.*s] %d", (int) name->len, name->data, (int) name->len);
+ dd("name2: [%.*s] %d", (int) name_len, name_data, (int) name_len);
+
+ if (name->len == name_len
+ && ngx_strncmp(name->data, name_data, name_len) == 0)
+ {
+ ctx = (ngx_stream_lua_shm_zone_ctx_t *) zone[i].data;
+ return &ctx->zone;
+ }
+ }
+
+ return NULL;
+}
+
+
+ngx_shm_zone_t *
+ngx_stream_lua_ffi_shdict_udata_to_zone(void *zone_udata)
+{
+ if (zone_udata == NULL) {
+ return NULL;
+ }
+
+ return *(ngx_shm_zone_t **) zone_udata;
+}
+
+
+int
+ngx_stream_lua_ffi_shdict_store(ngx_shm_zone_t *zone, int op, u_char *key,
+ size_t key_len, int value_type, u_char *str_value_buf,
+ size_t str_value_len, double num_value, long exptime, int user_flags,
+ char **errmsg, int *forcible)
+{
+ int i, n;
+ u_char c, *p;
+ uint32_t hash;
+ ngx_int_t rc;
+ ngx_time_t *tp;
+ ngx_queue_t *queue, *q;
+ ngx_rbtree_node_t *node;
+
+ ngx_stream_lua_shdict_ctx_t *ctx;
+ ngx_stream_lua_shdict_node_t *sd;
+
+ dd("exptime: %ld", exptime);
+
+ ctx = zone->data;
+
+ *forcible = 0;
+
+ hash = ngx_crc32_short(key, key_len);
+
+ switch (value_type) {
+
+ case SHDICT_TSTRING:
+ /* do nothing */
+ break;
+
+ case SHDICT_TNUMBER:
+ dd("num value: %lf", num_value);
+ str_value_buf = (u_char *) &num_value;
+ str_value_len = sizeof(double);
+ break;
+
+ case SHDICT_TBOOLEAN:
+ c = num_value ? 1 : 0;
+ str_value_buf = &c;
+ str_value_len = sizeof(u_char);
+ break;
+
+ case LUA_TNIL:
+ if (op & (NGX_STREAM_LUA_SHDICT_ADD|NGX_STREAM_LUA_SHDICT_REPLACE)) {
+ *errmsg = "attempt to add or replace nil values";
+ return NGX_ERROR;
+ }
+
+ str_value_buf = NULL;
+ str_value_len = 0;
+ break;
+
+ default:
+ *errmsg = "unsupported value type";
+ return NGX_ERROR;
+ }
+
+ ngx_shmtx_lock(&ctx->shpool->mutex);
+
+#if 1
+ ngx_stream_lua_shdict_expire(ctx, 1);
+#endif
+
+ rc = ngx_stream_lua_shdict_lookup(zone, hash, key, key_len, &sd);
+
+ dd("lookup returns %d", (int) rc);
+
+ if (op & NGX_STREAM_LUA_SHDICT_REPLACE) {
+
+ if (rc == NGX_DECLINED || rc == NGX_DONE) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+ *errmsg = "not found";
+ return NGX_DECLINED;
+ }
+
+ /* rc == NGX_OK */
+
+ goto replace;
+ }
+
+ if (op & NGX_STREAM_LUA_SHDICT_ADD) {
+
+ if (rc == NGX_OK) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+ *errmsg = "exists";
+ return NGX_DECLINED;
+ }
+
+ if (rc == NGX_DONE) {
+ /* exists but expired */
+
+ dd("go to replace");
+ goto replace;
+ }
+
+ /* rc == NGX_DECLINED */
+
+ dd("go to insert");
+ goto insert;
+ }
+
+ if (rc == NGX_OK || rc == NGX_DONE) {
+
+ if (value_type == LUA_TNIL) {
+ goto remove;
+ }
+
+replace:
+
+ if (str_value_buf
+ && str_value_len == (size_t) sd->value_len
+ && sd->value_type != SHDICT_TLIST)
+ {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
+ "lua shared dict set: found old entry and value "
+ "size matched, reusing it");
+
+ ngx_queue_remove(&sd->queue);
+ ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
+
+ sd->key_len = (u_short) key_len;
+
+ if (exptime > 0) {
+ tp = ngx_timeofday();
+ sd->expires = (uint64_t) tp->sec * 1000 + tp->msec
+ + (uint64_t) exptime;
+
+ } else {
+ sd->expires = 0;
+ }
+
+ sd->user_flags = user_flags;
+
+ sd->value_len = (uint32_t) str_value_len;
+
+ dd("setting value type to %d", value_type);
+
+ sd->value_type = (uint8_t) value_type;
+
+ p = ngx_copy(sd->data, key, key_len);
+ ngx_memcpy(p, str_value_buf, str_value_len);
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ return NGX_OK;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
+ "lua shared dict set: found old entry but value size "
+ "NOT matched, removing it first");
+
+remove:
+
+ if (sd->value_type == SHDICT_TLIST) {
+ queue = ngx_stream_lua_shdict_get_list_head(sd, key_len);
+
+ for (q = ngx_queue_head(queue);
+ q != ngx_queue_sentinel(queue);
+ q = ngx_queue_next(q))
+ {
+ p = (u_char *) ngx_queue_data(q,
+ ngx_stream_lua_shdict_list_node_t,
+ queue);
+
+ ngx_slab_free_locked(ctx->shpool, p);
+ }
+ }
+
+ ngx_queue_remove(&sd->queue);
+
+ node = (ngx_rbtree_node_t *)
+ ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));
+
+ ngx_rbtree_delete(&ctx->sh->rbtree, node);
+
+ ngx_slab_free_locked(ctx->shpool, node);
+
+ }
+
+insert:
+
+ /* rc == NGX_DECLINED or value size unmatch */
+
+ if (str_value_buf == NULL) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+ return NGX_OK;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
+ "lua shared dict set: creating a new entry");
+
+ n = offsetof(ngx_rbtree_node_t, color)
+ + offsetof(ngx_stream_lua_shdict_node_t, data)
+ + key_len
+ + str_value_len;
+
+ node = ngx_slab_alloc_locked(ctx->shpool, n);
+
+ if (node == NULL) {
+
+ if (op & NGX_STREAM_LUA_SHDICT_SAFE_STORE) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ *errmsg = "no memory";
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
+ "lua shared dict set: overriding non-expired items "
+ "due to memory shortage for entry \"%*s\"", key_len,
+ key);
+
+ for (i = 0; i < 30; i++) {
+ if (ngx_stream_lua_shdict_expire(ctx, 0) == 0) {
+ break;
+ }
+
+ *forcible = 1;
+
+ node = ngx_slab_alloc_locked(ctx->shpool, n);
+ if (node != NULL) {
+ goto allocated;
+ }
+ }
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ *errmsg = "no memory";
+ return NGX_ERROR;
+ }
+
+allocated:
+
+ sd = (ngx_stream_lua_shdict_node_t *) &node->color;
+
+ node->key = hash;
+ sd->key_len = (u_short) key_len;
+
+ if (exptime > 0) {
+ tp = ngx_timeofday();
+ sd->expires = (uint64_t) tp->sec * 1000 + tp->msec
+ + (uint64_t) exptime;
+
+ } else {
+ sd->expires = 0;
+ }
+
+ sd->user_flags = user_flags;
+ sd->value_len = (uint32_t) str_value_len;
+ dd("setting value type to %d", value_type);
+ sd->value_type = (uint8_t) value_type;
+
+ p = ngx_copy(sd->data, key, key_len);
+ ngx_memcpy(p, str_value_buf, str_value_len);
+
+ ngx_rbtree_insert(&ctx->sh->rbtree, node);
+ ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ return NGX_OK;
+}
+
+
+int
+ngx_stream_lua_ffi_shdict_get(ngx_shm_zone_t *zone, u_char *key,
+ size_t key_len, int *value_type, u_char **str_value_buf,
+ size_t *str_value_len, double *num_value, int *user_flags,
+ int get_stale, int *is_stale, char **err)
+{
+ ngx_str_t name;
+ uint32_t hash;
+ ngx_int_t rc;
+ ngx_str_t value;
+
+ ngx_stream_lua_shdict_ctx_t *ctx;
+ ngx_stream_lua_shdict_node_t *sd;
+
+ *err = NULL;
+
+ ctx = zone->data;
+ name = ctx->name;
+
+ hash = ngx_crc32_short(key, key_len);
+
+#if (NGX_DEBUG)
+ ngx_log_debug3(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
+ "fetching key \"%*s\" in shared dict \"%V\"", key_len,
+ key, &name);
+#endif /* NGX_DEBUG */
+
+ ngx_shmtx_lock(&ctx->shpool->mutex);
+
+#if 1
+ if (!get_stale) {
+ ngx_stream_lua_shdict_expire(ctx, 1);
+ }
+#endif
+
+ rc = ngx_stream_lua_shdict_lookup(zone, hash, key, key_len, &sd);
+
+ dd("shdict lookup returns %d", (int) rc);
+
+ if (rc == NGX_DECLINED || (rc == NGX_DONE && !get_stale)) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+ *value_type = LUA_TNIL;
+ return NGX_OK;
+ }
+
+ /* rc == NGX_OK || (rc == NGX_DONE && get_stale) */
+
+ *value_type = sd->value_type;
+
+ dd("data: %p", sd->data);
+ dd("key len: %d", (int) sd->key_len);
+
+ value.data = sd->data + sd->key_len;
+ value.len = (size_t) sd->value_len;
+
+ if (*str_value_len < (size_t) value.len) {
+ if (*value_type == SHDICT_TBOOLEAN) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+ return NGX_ERROR;
+ }
+
+ if (*value_type == SHDICT_TSTRING) {
+ *str_value_buf = malloc(value.len);
+ if (*str_value_buf == NULL) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ switch (*value_type) {
+
+ case SHDICT_TSTRING:
+ *str_value_len = value.len;
+ ngx_memcpy(*str_value_buf, value.data, value.len);
+ break;
+
+ case SHDICT_TNUMBER:
+
+ if (value.len != sizeof(double)) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+ ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
+ "bad lua number value size found for key %*s "
+ "in shared_dict %V: %z", key_len, key,
+ &name, value.len);
+ return NGX_ERROR;
+ }
+
+ *str_value_len = value.len;
+ ngx_memcpy(num_value, value.data, sizeof(double));
+ break;
+
+ case SHDICT_TBOOLEAN:
+
+ if (value.len != sizeof(u_char)) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+ ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
+ "bad lua boolean value size found for key %*s "
+ "in shared_dict %V: %z", key_len, key, &name,
+ value.len);
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(*str_value_buf, value.data, value.len);
+ break;
+
+ case SHDICT_TLIST:
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ *err = "value is a list";
+ return NGX_ERROR;
+
+ default:
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+ ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
+ "bad value type found for key %*s in "
+ "shared_dict %V: %d", key_len, key, &name,
+ *value_type);
+ return NGX_ERROR;
+ }
+
+ *user_flags = sd->user_flags;
+ dd("user flags: %d", *user_flags);
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ if (get_stale) {
+
+ /* always return value, flags, stale */
+
+ *is_stale = (rc == NGX_DONE);
+ return NGX_OK;
+ }
+
+ return NGX_OK;
+}
+
+
+int
+ngx_stream_lua_ffi_shdict_incr(ngx_shm_zone_t *zone, u_char *key,
+ size_t key_len, double *value, char **err, int has_init, double init,
+ long init_ttl, int *forcible)
+{
+ int i, n;
+ uint32_t hash;
+ ngx_int_t rc;
+ ngx_time_t *tp = NULL;
+ double num;
+ ngx_rbtree_node_t *node;
+ u_char *p;
+ ngx_queue_t *queue, *q;
+
+ ngx_stream_lua_shdict_ctx_t *ctx;
+ ngx_stream_lua_shdict_node_t *sd;
+
+ if (init_ttl > 0) {
+ tp = ngx_timeofday();
+ }
+
+ ctx = zone->data;
+
+ *forcible = 0;
+
+ hash = ngx_crc32_short(key, key_len);
+
+ dd("looking up key %.*s in shared dict %.*s", (int) key_len, key,
+ (int) ctx->name.len, ctx->name.data);
+
+ ngx_shmtx_lock(&ctx->shpool->mutex);
+#if 1
+ ngx_stream_lua_shdict_expire(ctx, 1);
+#endif
+ rc = ngx_stream_lua_shdict_lookup(zone, hash, key, key_len, &sd);
+
+ dd("shdict lookup returned %d", (int) rc);
+
+ if (rc == NGX_DECLINED || rc == NGX_DONE) {
+ if (!has_init) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+ *err = "not found";
+ return NGX_ERROR;
+ }
+
+ /* add value */
+ num = *value + init;
+
+ if (rc == NGX_DONE) {
+
+ /* found an expired item */
+
+ if ((size_t) sd->value_len == sizeof(double)
+ && sd->value_type != SHDICT_TLIST)
+ {
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
+ "lua shared dict incr: found old entry and "
+ "value size matched, reusing it");
+
+ ngx_queue_remove(&sd->queue);
+ ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
+
+ dd("go to setvalue");
+ goto setvalue;
+ }
+
+ dd("go to remove");
+ goto remove;
+ }
+
+ dd("go to insert");
+ goto insert;
+ }
+
+ /* rc == NGX_OK */
+
+ if (sd->value_type != SHDICT_TNUMBER || sd->value_len != sizeof(double)) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+ *err = "not a number";
+ return NGX_ERROR;
+ }
+
+ ngx_queue_remove(&sd->queue);
+ ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
+
+ dd("setting value type to %d", (int) sd->value_type);
+
+ p = sd->data + key_len;
+
+ ngx_memcpy(&num, p, sizeof(double));
+ num += *value;
+
+ ngx_memcpy(p, (double *) &num, sizeof(double));
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ *value = num;
+ return NGX_OK;
+
+remove:
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
+ "lua shared dict incr: found old entry but value size "
+ "NOT matched, removing it first");
+
+ if (sd->value_type == SHDICT_TLIST) {
+ queue = ngx_stream_lua_shdict_get_list_head(sd, key_len);
+
+ for (q = ngx_queue_head(queue);
+ q != ngx_queue_sentinel(queue);
+ q = ngx_queue_next(q))
+ {
+ p = (u_char *) ngx_queue_data(q, ngx_stream_lua_shdict_list_node_t,
+ queue);
+
+ ngx_slab_free_locked(ctx->shpool, p);
+ }
+ }
+
+ ngx_queue_remove(&sd->queue);
+
+ node = (ngx_rbtree_node_t *)
+ ((u_char *) sd - offsetof(ngx_rbtree_node_t, color));
+
+ ngx_rbtree_delete(&ctx->sh->rbtree, node);
+
+ ngx_slab_free_locked(ctx->shpool, node);
+
+insert:
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
+ "lua shared dict incr: creating a new entry");
+
+ n = offsetof(ngx_rbtree_node_t, color)
+ + offsetof(ngx_stream_lua_shdict_node_t, data)
+ + key_len
+ + sizeof(double);
+
+ node = ngx_slab_alloc_locked(ctx->shpool, n);
+
+ if (node == NULL) {
+
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
+ "lua shared dict incr: overriding non-expired items "
+ "due to memory shortage for entry \"%*s\"", key_len,
+ key);
+
+ for (i = 0; i < 30; i++) {
+ if (ngx_stream_lua_shdict_expire(ctx, 0) == 0) {
+ break;
+ }
+
+ *forcible = 1;
+
+ node = ngx_slab_alloc_locked(ctx->shpool, n);
+ if (node != NULL) {
+ goto allocated;
+ }
+ }
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ *err = "no memory";
+ return NGX_ERROR;
+ }
+
+allocated:
+
+ sd = (ngx_stream_lua_shdict_node_t *) &node->color;
+
+ node->key = hash;
+
+ sd->key_len = (u_short) key_len;
+
+ sd->value_len = (uint32_t) sizeof(double);
+
+ ngx_rbtree_insert(&ctx->sh->rbtree, node);
+
+ ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
+
+setvalue:
+
+ sd->user_flags = 0;
+
+ if (init_ttl > 0) {
+ sd->expires = (uint64_t) tp->sec * 1000 + tp->msec
+ + (uint64_t) init_ttl;
+
+ } else {
+ sd->expires = 0;
+ }
+
+ dd("setting value type to %d", LUA_TNUMBER);
+
+ sd->value_type = (uint8_t) LUA_TNUMBER;
+
+ p = ngx_copy(sd->data, key, key_len);
+ ngx_memcpy(p, (double *) &num, sizeof(double));
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ *value = num;
+ return NGX_OK;
+}
+
+
+int
+ngx_stream_lua_ffi_shdict_flush_all(ngx_shm_zone_t *zone)
+{
+ ngx_queue_t *q;
+
+ ngx_stream_lua_shdict_node_t *sd;
+ ngx_stream_lua_shdict_ctx_t *ctx;
+
+ ctx = zone->data;
+
+ ngx_shmtx_lock(&ctx->shpool->mutex);
+
+ for (q = ngx_queue_head(&ctx->sh->lru_queue);
+ q != ngx_queue_sentinel(&ctx->sh->lru_queue);
+ q = ngx_queue_next(q))
+ {
+ sd = ngx_queue_data(q, ngx_stream_lua_shdict_node_t, queue);
+ sd->expires = 1;
+ }
+
+ ngx_stream_lua_shdict_expire(ctx, 0);
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_shdict_peek(ngx_shm_zone_t *shm_zone, ngx_uint_t hash,
+ u_char *kdata, size_t klen, ngx_stream_lua_shdict_node_t **sdp)
+{
+ ngx_int_t rc;
+ ngx_rbtree_node_t *node, *sentinel;
+
+ ngx_stream_lua_shdict_ctx_t *ctx;
+ ngx_stream_lua_shdict_node_t *sd;
+
+ ctx = shm_zone->data;
+
+ node = ctx->sh->rbtree.root;
+ sentinel = ctx->sh->rbtree.sentinel;
+
+ while (node != sentinel) {
+
+ if (hash < node->key) {
+ node = node->left;
+ continue;
+ }
+
+ if (hash > node->key) {
+ node = node->right;
+ continue;
+ }
+
+ /* hash == node->key */
+
+ sd = (ngx_stream_lua_shdict_node_t *) &node->color;
+
+ rc = ngx_memn2cmp(kdata, sd->data, klen, (size_t) sd->key_len);
+
+ if (rc == 0) {
+ *sdp = sd;
+
+ return NGX_OK;
+ }
+
+ node = (rc < 0) ? node->left : node->right;
+ }
+
+ *sdp = NULL;
+
+ return NGX_DECLINED;
+}
+
+
+long
+ngx_stream_lua_ffi_shdict_get_ttl(ngx_shm_zone_t *zone, u_char *key,
+ size_t key_len)
+{
+ uint32_t hash;
+ uint64_t now;
+ uint64_t expires;
+ ngx_int_t rc;
+ ngx_time_t *tp;
+
+ ngx_stream_lua_shdict_ctx_t *ctx;
+ ngx_stream_lua_shdict_node_t *sd;
+
+ ctx = zone->data;
+ hash = ngx_crc32_short(key, key_len);
+
+ ngx_shmtx_lock(&ctx->shpool->mutex);
+
+ rc = ngx_stream_lua_shdict_peek(zone, hash, key, key_len, &sd);
+
+ if (rc == NGX_DECLINED) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ return NGX_DECLINED;
+ }
+
+ /* rc == NGX_OK */
+
+ expires = sd->expires;
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ if (expires == 0) {
+ return 0;
+ }
+
+ tp = ngx_timeofday();
+ now = (uint64_t) tp->sec * 1000 + tp->msec;
+
+ return expires - now;
+}
+
+
+int
+ngx_stream_lua_ffi_shdict_set_expire(ngx_shm_zone_t *zone, u_char *key,
+ size_t key_len, long exptime)
+{
+ uint32_t hash;
+ ngx_int_t rc;
+ ngx_time_t *tp = NULL;
+
+ ngx_stream_lua_shdict_ctx_t *ctx;
+ ngx_stream_lua_shdict_node_t *sd;
+
+ if (exptime > 0) {
+ tp = ngx_timeofday();
+ }
+
+ ctx = zone->data;
+ hash = ngx_crc32_short(key, key_len);
+
+ ngx_shmtx_lock(&ctx->shpool->mutex);
+
+ rc = ngx_stream_lua_shdict_peek(zone, hash, key, key_len, &sd);
+
+ if (rc == NGX_DECLINED) {
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ return NGX_DECLINED;
+ }
+
+ /* rc == NGX_OK */
+
+ if (exptime > 0) {
+ sd->expires = (uint64_t) tp->sec * 1000 + tp->msec
+ + (uint64_t) exptime;
+
+ } else {
+ sd->expires = 0;
+ }
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ return NGX_OK;
+}
+
+
+size_t
+ngx_stream_lua_ffi_shdict_capacity(ngx_shm_zone_t *zone)
+{
+ return zone->shm.size;
+}
+
+
+#if defined(nginx_version) && nginx_version >= 1011007
+size_t
+ngx_stream_lua_ffi_shdict_free_space(ngx_shm_zone_t *zone)
+{
+ size_t bytes;
+
+ ngx_stream_lua_shdict_ctx_t *ctx;
+
+ ctx = zone->data;
+
+ ngx_shmtx_lock(&ctx->shpool->mutex);
+ bytes = ctx->shpool->pfree * ngx_pagesize;
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ return bytes;
+}
+#endif
+
+
+#if (NGX_DARWIN)
+int
+ngx_stream_lua_ffi_shdict_get_macos(ngx_stream_lua_shdict_get_params_t *p)
+{
+ return ngx_stream_lua_ffi_shdict_get(p->zone,
+ (u_char *) p->key, p->key_len,
+ p->value_type, p->str_value_buf,
+ p->str_value_len, p->num_value,
+ p->user_flags, p->get_stale,
+ p->is_stale, p->errmsg);
+}
+
+
+int
+ngx_stream_lua_ffi_shdict_store_macos(ngx_stream_lua_shdict_store_params_t *p)
+{
+ return ngx_stream_lua_ffi_shdict_store(p->zone, p->op,
+ (u_char *) p->key, p->key_len,
+ p->value_type,
+ (u_char *) p->str_value_buf,
+ p->str_value_len, p->num_value,
+ p->exptime, p->user_flags,
+ p->errmsg, p->forcible);
+}
+
+
+int
+ngx_stream_lua_ffi_shdict_incr_macos(ngx_stream_lua_shdict_incr_params_t *p)
+{
+ return ngx_stream_lua_ffi_shdict_incr(p->zone,
+ (u_char *) p->key, p->key_len,
+ p->num_value, p->errmsg,
+ p->has_init, p->init, p->init_ttl,
+ p->forcible);
+}
+#endif
+
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_shdict.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_shdict.h
new file mode 100644
index 0000000..3554764
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_shdict.h
@@ -0,0 +1,121 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_shdict.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_SHDICT_H_INCLUDED_
+#define _NGX_STREAM_LUA_SHDICT_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+typedef struct {
+ u_char color;
+ uint8_t value_type;
+ u_short key_len;
+ uint32_t value_len;
+ uint64_t expires;
+ ngx_queue_t queue;
+ uint32_t user_flags;
+ u_char data[1];
+} ngx_stream_lua_shdict_node_t;
+
+
+typedef struct {
+ ngx_queue_t queue;
+ uint32_t value_len;
+ uint8_t value_type;
+ u_char data[1];
+} ngx_stream_lua_shdict_list_node_t;
+
+
+typedef struct {
+ ngx_rbtree_t rbtree;
+ ngx_rbtree_node_t sentinel;
+ ngx_queue_t lru_queue;
+} ngx_stream_lua_shdict_shctx_t;
+
+
+typedef struct {
+ ngx_stream_lua_shdict_shctx_t *sh;
+ ngx_slab_pool_t *shpool;
+ ngx_str_t name;
+ ngx_stream_lua_main_conf_t *main_conf;
+ ngx_log_t *log;
+} ngx_stream_lua_shdict_ctx_t;
+
+
+typedef struct {
+ ngx_log_t *log;
+ ngx_stream_lua_main_conf_t *lmcf;
+ ngx_cycle_t *cycle;
+ ngx_shm_zone_t zone;
+} ngx_stream_lua_shm_zone_ctx_t;
+
+
+#if (NGX_DARWIN)
+typedef struct {
+ void *zone;
+ const unsigned char *key;
+ size_t key_len;
+ int *value_type;
+ unsigned char **str_value_buf;
+ size_t *str_value_len;
+ double *num_value;
+ int *user_flags;
+ int get_stale;
+ int *is_stale;
+ char **errmsg;
+} ngx_stream_lua_shdict_get_params_t;
+
+
+typedef struct {
+ void *zone;
+ int op;
+ const unsigned char *key;
+ size_t key_len;
+ int value_type;
+ const unsigned char *str_value_buf;
+ size_t str_value_len;
+ double num_value;
+ long exptime;
+ int user_flags;
+ char **errmsg;
+ int *forcible;
+} ngx_stream_lua_shdict_store_params_t;
+
+
+typedef struct {
+ void *zone;
+ const unsigned char *key;
+ size_t key_len;
+ double *num_value;
+ char **errmsg;
+ int has_init;
+ double init;
+ long init_ttl;
+ int *forcible;
+} ngx_stream_lua_shdict_incr_params_t;
+#endif
+
+
+ngx_int_t ngx_stream_lua_shdict_init_zone(ngx_shm_zone_t *shm_zone, void *data);
+void ngx_stream_lua_shdict_rbtree_insert_value(ngx_rbtree_node_t *temp,
+ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
+void ngx_stream_lua_inject_shdict_api(ngx_stream_lua_main_conf_t *lmcf,
+ lua_State *L);
+
+
+#endif /* _NGX_STREAM_LUA_SHDICT_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_sleep.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_sleep.c
new file mode 100644
index 0000000..fb590c5
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_sleep.c
@@ -0,0 +1,223 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_sleep.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_util.h"
+#include "ngx_stream_lua_sleep.h"
+#include "ngx_stream_lua_contentby.h"
+
+
+static int ngx_stream_lua_ngx_sleep(lua_State *L);
+static void ngx_stream_lua_sleep_handler(ngx_event_t *ev);
+static void ngx_stream_lua_sleep_cleanup(void *data);
+static ngx_int_t ngx_stream_lua_sleep_resume(ngx_stream_lua_request_t *r);
+
+
+static int
+ngx_stream_lua_ngx_sleep(lua_State *L)
+{
+ int n;
+ ngx_int_t delay; /* in msec */
+ ngx_stream_lua_request_t *r;
+
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx;
+
+ n = lua_gettop(L);
+ if (n != 1) {
+ return luaL_error(L, "attempt to pass %d arguments, but accepted 1", n);
+ }
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ delay = (ngx_int_t) (luaL_checknumber(L, 1) * 1000);
+
+ if (delay < 0) {
+ return luaL_error(L, "invalid sleep duration \"%d\"", delay);
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return luaL_error(L, "no request ctx found");
+ }
+
+ ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_YIELDABLE);
+
+ coctx = ctx->cur_co_ctx;
+ if (coctx == NULL) {
+ return luaL_error(L, "no co ctx found");
+ }
+
+ ngx_stream_lua_cleanup_pending_operation(coctx);
+ coctx->cleanup = ngx_stream_lua_sleep_cleanup;
+ coctx->data = r;
+
+ coctx->sleep.handler = ngx_stream_lua_sleep_handler;
+ coctx->sleep.data = coctx;
+ coctx->sleep.log = r->connection->log;
+
+ if (delay == 0) {
+#ifdef HAVE_POSTED_DELAYED_EVENTS_PATCH
+ dd("posting 0 sec sleep event to head of delayed queue");
+
+ coctx->sleep.delayed = 1;
+ ngx_post_event(&coctx->sleep, &ngx_posted_delayed_events);
+#else
+ ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "ngx.sleep(0)"
+ " called without delayed events patch, this will"
+ " hurt performance");
+ ngx_add_timer(&coctx->sleep, (ngx_msec_t) delay);
+#endif
+
+ } else {
+ dd("adding timer with delay %lu ms, r:%p", (unsigned long) delay, r);
+
+ ngx_add_timer(&coctx->sleep, (ngx_msec_t) delay);
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua ready to sleep for %d ms", delay);
+
+ return lua_yield(L, 0);
+}
+
+
+void
+ngx_stream_lua_sleep_handler(ngx_event_t *ev)
+{
+#if (NGX_DEBUG)
+ ngx_connection_t *c;
+#endif
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx;
+
+ coctx = ev->data;
+
+ r = coctx->data;
+
+#if (NGX_DEBUG)
+
+ c = r->connection;
+
+#endif
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+
+ if (ctx == NULL) {
+ return;
+ }
+
+
+ coctx->cleanup = NULL;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "stream lua sleep timer expired");
+
+ ctx->cur_co_ctx = coctx;
+
+ if (ctx->entered_content_phase) {
+ (void) ngx_stream_lua_sleep_resume(r);
+
+ } else {
+ ctx->resume_handler = ngx_stream_lua_sleep_resume;
+ ngx_stream_lua_core_run_phases(r);
+ }
+
+}
+
+
+void
+ngx_stream_lua_inject_sleep_api(lua_State *L)
+{
+ lua_pushcfunction(L, ngx_stream_lua_ngx_sleep);
+ lua_setfield(L, -2, "sleep");
+}
+
+
+static void
+ngx_stream_lua_sleep_cleanup(void *data)
+{
+ ngx_stream_lua_co_ctx_t *coctx = data;
+
+ if (coctx->sleep.timer_set) {
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "lua clean up the timer for pending ngx.sleep");
+
+ ngx_del_timer(&coctx->sleep);
+ }
+
+#ifdef HAVE_POSTED_DELAYED_EVENTS_PATCH
+ if (coctx->sleep.posted) {
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "lua clean up the posted event for pending ngx.sleep");
+
+ ngx_delete_posted_event(&coctx->sleep);
+ }
+#endif
+}
+
+
+static ngx_int_t
+ngx_stream_lua_sleep_resume(ngx_stream_lua_request_t *r)
+{
+ lua_State *vm;
+ ngx_connection_t *c;
+ ngx_int_t rc;
+ ngx_uint_t nreqs;
+ ngx_stream_lua_ctx_t *ctx;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ctx->resume_handler = ngx_stream_lua_wev_handler;
+
+ c = r->connection;
+ vm = ngx_stream_lua_get_lua_vm(r, ctx);
+ nreqs = c->requests;
+
+ rc = ngx_stream_lua_run_thread(vm, r, ctx, 0);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua run thread returned %d", rc);
+
+ if (rc == NGX_AGAIN) {
+ return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs);
+ }
+
+ if (rc == NGX_DONE) {
+ ngx_stream_lua_finalize_request(r, NGX_DONE);
+ return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs);
+ }
+
+ if (ctx->entered_content_phase) {
+ ngx_stream_lua_finalize_request(r, rc);
+ return NGX_DONE;
+ }
+
+ return rc;
+}
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_sleep.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_sleep.h
new file mode 100644
index 0000000..cea10bd
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_sleep.h
@@ -0,0 +1,28 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_sleep.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_SLEEP_H_INCLUDED_
+#define _NGX_STREAM_LUA_SLEEP_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+void ngx_stream_lua_inject_sleep_api(lua_State *L);
+
+
+#endif /* _NGX_STREAM_LUA_SLEEP_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_socket_tcp.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_socket_tcp.c
new file mode 100644
index 0000000..c5b33df
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_socket_tcp.c
@@ -0,0 +1,6242 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_socket_tcp.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_socket_tcp.h"
+#include "ngx_stream_lua_input_filters.h"
+#include "ngx_stream_lua_util.h"
+#include "ngx_stream_lua_uthread.h"
+#include "ngx_stream_lua_output.h"
+#include "ngx_stream_lua_contentby.h"
+#include "ngx_stream_lua_probe.h"
+
+
+static int ngx_stream_lua_socket_tcp(lua_State *L);
+static int ngx_stream_lua_socket_tcp_connect(lua_State *L);
+#if (NGX_STREAM_SSL)
+static int ngx_stream_lua_socket_tcp_sslhandshake(lua_State *L);
+#endif
+static int ngx_stream_lua_socket_tcp_receive(lua_State *L);
+static int ngx_stream_lua_socket_tcp_receiveany(lua_State *L);
+static int ngx_stream_lua_socket_tcp_send(lua_State *L);
+static int ngx_stream_lua_socket_tcp_close(lua_State *L);
+static int ngx_stream_lua_socket_tcp_setoption(lua_State *L);
+static int ngx_stream_lua_socket_tcp_settimeout(lua_State *L);
+static int ngx_stream_lua_socket_tcp_settimeouts(lua_State *L);
+static void ngx_stream_lua_socket_tcp_handler(ngx_event_t *ev);
+static ngx_int_t ngx_stream_lua_socket_tcp_get_peer(ngx_peer_connection_t *pc,
+ void *data);
+static void ngx_stream_lua_socket_init_peer_connection_addr_text(
+ ngx_peer_connection_t *pc);
+static void ngx_stream_lua_socket_read_handler(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u);
+static void ngx_stream_lua_socket_send_handler(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u);
+static void ngx_stream_lua_socket_connected_handler(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u);
+static void ngx_stream_lua_socket_tcp_cleanup(void *data);
+static void ngx_stream_lua_socket_tcp_finalize(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u);
+static void ngx_stream_lua_socket_tcp_finalize_read_part(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u);
+static void ngx_stream_lua_socket_tcp_finalize_write_part(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u,
+ int do_shutdown);
+
+static ngx_int_t ngx_stream_lua_socket_send(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u);
+static ngx_int_t ngx_stream_lua_socket_test_connect(ngx_stream_lua_request_t *r,
+ ngx_connection_t *c);
+static void ngx_stream_lua_socket_handle_conn_error(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type);
+static void ngx_stream_lua_socket_handle_read_error(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type);
+static void ngx_stream_lua_socket_handle_write_error(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u,
+ ngx_uint_t ft_type);
+static void ngx_stream_lua_socket_handle_conn_success(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u);
+static void ngx_stream_lua_socket_handle_read_success(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u);
+static void ngx_stream_lua_socket_handle_write_success(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u);
+static int ngx_stream_lua_socket_tcp_send_retval_handler(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u,
+ lua_State *L);
+static int ngx_stream_lua_socket_tcp_conn_retval_handler(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u,
+ lua_State *L);
+static void ngx_stream_lua_socket_dummy_handler(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u);
+static int ngx_stream_lua_socket_tcp_receive_helper(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L);
+static ngx_int_t ngx_stream_lua_socket_tcp_read(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u);
+static int ngx_stream_lua_socket_tcp_receive_retval_handler(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u,
+ lua_State *L);
+static ngx_int_t ngx_stream_lua_socket_read_line(void *data, ssize_t bytes);
+static void ngx_stream_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx);
+static int ngx_stream_lua_socket_resolve_retval_handler(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u,
+ lua_State *L);
+static int ngx_stream_lua_socket_conn_error_retval_handler(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u,
+ lua_State *L);
+static int ngx_stream_lua_socket_read_error_retval_handler(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u,
+ lua_State *L);
+static int ngx_stream_lua_socket_write_error_retval_handler(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u,
+ lua_State *L);
+static ngx_int_t ngx_stream_lua_socket_read_all(void *data, ssize_t bytes);
+static ngx_int_t ngx_stream_lua_socket_read_until(void *data, ssize_t bytes);
+static ngx_int_t ngx_stream_lua_socket_read_chunk(void *data, ssize_t bytes);
+static ngx_int_t ngx_stream_lua_socket_read_any(void *data, ssize_t bytes);
+static int ngx_stream_lua_socket_tcp_receiveuntil(lua_State *L);
+static int ngx_stream_lua_socket_receiveuntil_iterator(lua_State *L);
+static ngx_int_t ngx_stream_lua_socket_compile_pattern(u_char *data, size_t len,
+ ngx_stream_lua_socket_compiled_pattern_t *cp, ngx_log_t *log);
+static int ngx_stream_lua_socket_cleanup_compiled_pattern(lua_State *L);
+static void ngx_stream_lua_req_socket_rev_handler(ngx_stream_lua_request_t *r);
+static int ngx_stream_lua_socket_tcp_getreusedtimes(lua_State *L);
+static int ngx_stream_lua_socket_tcp_setkeepalive(lua_State *L);
+static void ngx_stream_lua_socket_tcp_create_socket_pool(lua_State *L,
+ ngx_stream_lua_request_t *r, ngx_str_t key, ngx_int_t pool_size,
+ ngx_int_t backlog, ngx_stream_lua_socket_pool_t **spool);
+static ngx_int_t ngx_stream_lua_get_keepalive_peer(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u);
+static void ngx_stream_lua_socket_keepalive_dummy_handler(ngx_event_t *ev);
+static int ngx_stream_lua_socket_tcp_connect_helper(lua_State *L,
+ ngx_stream_lua_socket_tcp_upstream_t *u, ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx, u_char *host_ref, size_t host_len,
+ in_port_t port, unsigned resuming);
+static void ngx_stream_lua_socket_tcp_conn_op_timeout_handler(
+ ngx_event_t *ev);
+static int ngx_stream_lua_socket_tcp_conn_op_timeout_retval_handler(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u,
+ lua_State *L);
+static void ngx_stream_lua_socket_tcp_resume_conn_op(
+ ngx_stream_lua_socket_pool_t *spool);
+static void ngx_stream_lua_socket_tcp_conn_op_ctx_cleanup(void *data);
+static void ngx_stream_lua_socket_tcp_conn_op_resume_handler(ngx_event_t *ev);
+static ngx_int_t ngx_stream_lua_socket_keepalive_close_handler(ngx_event_t *ev);
+static void ngx_stream_lua_socket_keepalive_rev_handler(ngx_event_t *ev);
+static int ngx_stream_lua_socket_tcp_conn_op_resume_retval_handler(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u,
+ lua_State *L);
+static int ngx_stream_lua_socket_tcp_upstream_destroy(lua_State *L);
+static int ngx_stream_lua_socket_downstream_destroy(lua_State *L);
+static ngx_int_t ngx_stream_lua_socket_push_input_data(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx,
+ ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L);
+static ngx_int_t ngx_stream_lua_socket_add_pending_data(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u,
+ u_char *pos, size_t len, u_char *pat, int prefix, int old_state);
+static ngx_int_t ngx_stream_lua_socket_add_input_buffer(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u);
+static ngx_int_t ngx_stream_lua_socket_insert_buffer(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u,
+ u_char *pat, size_t prefix);
+static ngx_int_t ngx_stream_lua_socket_tcp_conn_op_resume(
+ ngx_stream_lua_request_t *r);
+static ngx_int_t ngx_stream_lua_socket_tcp_conn_resume(
+ ngx_stream_lua_request_t *r);
+static ngx_int_t ngx_stream_lua_socket_tcp_read_resume(
+ ngx_stream_lua_request_t *r);
+static ngx_int_t ngx_stream_lua_socket_tcp_write_resume(
+ ngx_stream_lua_request_t *r);
+static ngx_int_t ngx_stream_lua_socket_tcp_resume_helper(
+ ngx_stream_lua_request_t *r, int socket_op);
+static void ngx_stream_lua_tcp_queue_conn_op_cleanup(void *data);
+static void ngx_stream_lua_tcp_resolve_cleanup(void *data);
+static void ngx_stream_lua_coctx_cleanup(void *data);
+static void ngx_stream_lua_socket_free_pool(ngx_log_t *log,
+ ngx_stream_lua_socket_pool_t *spool);
+static int ngx_stream_lua_socket_shutdown_pool(lua_State *L);
+static void ngx_stream_lua_socket_shutdown_pool_helper(
+ ngx_stream_lua_socket_pool_t *spool);
+static int ngx_stream_lua_socket_prepare_error_retvals(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u,
+ lua_State *L, ngx_uint_t ft_type);
+#if (NGX_STREAM_SSL)
+static int ngx_stream_lua_ssl_handshake_retval_handler(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u,
+ lua_State *L);
+static void ngx_stream_lua_ssl_handshake_handler(ngx_connection_t *c);
+static int ngx_stream_lua_ssl_free_session(lua_State *L);
+#endif
+static void ngx_stream_lua_socket_tcp_close_connection(ngx_connection_t *c);
+
+static int ngx_stream_lua_socket_tcp_peek(lua_State *L);
+static ngx_int_t ngx_stream_lua_socket_tcp_peek_resume(ngx_stream_lua_request_t *r);
+static int ngx_stream_lua_socket_tcp_shutdown(lua_State *L);
+
+
+enum {
+ SOCKET_CTX_INDEX = 1,
+ SOCKET_KEY_INDEX = 3,
+ SOCKET_CONNECT_TIMEOUT_INDEX = 2,
+ SOCKET_SEND_TIMEOUT_INDEX = 4,
+ SOCKET_READ_TIMEOUT_INDEX = 5,
+};
+
+
+enum {
+ SOCKET_OP_CONNECT,
+ SOCKET_OP_READ,
+ SOCKET_OP_WRITE,
+ SOCKET_OP_RESUME_CONN
+};
+
+
+#define ngx_stream_lua_socket_check_busy_connecting(r, u, L) \
+ if ((u)->conn_waiting) { \
+ lua_pushnil(L); \
+ lua_pushliteral(L, "socket busy connecting"); \
+ return 2; \
+ }
+
+
+#define ngx_stream_lua_socket_check_busy_reading(r, u, L) \
+ if ((u)->read_waiting) { \
+ lua_pushnil(L); \
+ lua_pushliteral(L, "socket busy reading"); \
+ return 2; \
+ }
+
+
+#define ngx_stream_lua_socket_check_busy_writing(r, u, L) \
+ if ((u)->write_waiting) { \
+ lua_pushnil(L); \
+ lua_pushliteral(L, "socket busy writing"); \
+ return 2; \
+ } \
+ if ((u)->raw_downstream \
+ && ((r)->connection->buffered)) \
+ { \
+ lua_pushnil(L); \
+ lua_pushliteral(L, "socket busy writing"); \
+ return 2; \
+ }
+
+
+
+static char ngx_stream_lua_raw_req_socket_metatable_key;
+static char ngx_stream_lua_tcp_socket_metatable_key;
+static char ngx_stream_lua_upstream_udata_metatable_key;
+static char ngx_stream_lua_downstream_udata_metatable_key;
+static char ngx_stream_lua_pool_udata_metatable_key;
+static char ngx_stream_lua_pattern_udata_metatable_key;
+#if (NGX_STREAM_SSL)
+static char ngx_stream_lua_ssl_session_metatable_key;
+#endif
+
+
+void
+ngx_stream_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L)
+{
+ ngx_int_t rc;
+
+ lua_createtable(L, 0, 4 /* nrec */); /* ngx.socket */
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_tcp);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -3, "tcp");
+ lua_setfield(L, -2, "stream");
+
+ {
+ const char buf[] = "local sock = ngx.socket.tcp()"
+ " local ok, err = sock:connect(...)"
+ " if ok then return sock else return nil, err end";
+
+ rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, "=ngx.socket.connect");
+ }
+
+ if (rc != NGX_OK) {
+ ngx_log_error(NGX_LOG_CRIT, log, 0,
+ "failed to load Lua code for ngx.socket.connect(): %i",
+ rc);
+
+ } else {
+ lua_setfield(L, -2, "connect");
+ }
+
+ lua_setfield(L, -2, "socket");
+
+
+ /* {{{raw req socket object metatable */
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ raw_req_socket_metatable_key));
+ lua_createtable(L, 0 /* narr */, 9 /* nrec */);
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_tcp_receive);
+ lua_setfield(L, -2, "receive");
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_tcp_receiveany);
+ lua_setfield(L, -2, "receiveany");
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_tcp_receiveuntil);
+ lua_setfield(L, -2, "receiveuntil");
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_tcp_send);
+ lua_setfield(L, -2, "send");
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_tcp_settimeout);
+ lua_setfield(L, -2, "settimeout"); /* ngx socket mt */
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_tcp_settimeouts);
+ lua_setfield(L, -2, "settimeouts"); /* ngx socket mt */
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_tcp_peek);
+ lua_setfield(L, -2, "peek");
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_tcp_shutdown);
+ lua_setfield(L, -2, "shutdown");
+
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "__index");
+
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ /* }}} */
+
+ /* {{{tcp object metatable */
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ tcp_socket_metatable_key));
+ lua_createtable(L, 0 /* narr */, 14 /* nrec */);
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_tcp_connect);
+ lua_setfield(L, -2, "connect");
+
+#if (NGX_STREAM_SSL)
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_tcp_sslhandshake);
+ lua_setfield(L, -2, "sslhandshake");
+
+#endif
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_tcp_receive);
+ lua_setfield(L, -2, "receive");
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_tcp_receiveuntil);
+ lua_setfield(L, -2, "receiveuntil");
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_tcp_receiveany);
+ lua_setfield(L, -2, "receiveany");
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_tcp_send);
+ lua_setfield(L, -2, "send");
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_tcp_close);
+ lua_setfield(L, -2, "close");
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_tcp_setoption);
+ lua_setfield(L, -2, "setoption");
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_tcp_settimeout);
+ lua_setfield(L, -2, "settimeout"); /* ngx socket mt */
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_tcp_settimeouts);
+ lua_setfield(L, -2, "settimeouts"); /* ngx socket mt */
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_tcp_getreusedtimes);
+ lua_setfield(L, -2, "getreusedtimes");
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_tcp_setkeepalive);
+ lua_setfield(L, -2, "setkeepalive");
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_tcp_shutdown);
+ lua_setfield(L, -2, "shutdown");
+
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "__index");
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ /* }}} */
+
+ /* {{{upstream userdata metatable */
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ upstream_udata_metatable_key));
+ lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */
+ lua_pushcfunction(L, ngx_stream_lua_socket_tcp_upstream_destroy);
+ lua_setfield(L, -2, "__gc");
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ /* }}} */
+
+ /* {{{downstream userdata metatable */
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ downstream_udata_metatable_key));
+ lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */
+ lua_pushcfunction(L, ngx_stream_lua_socket_downstream_destroy);
+ lua_setfield(L, -2, "__gc");
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ /* }}} */
+
+ /* {{{socket pool userdata metatable */
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ pool_udata_metatable_key));
+ lua_createtable(L, 0, 1); /* metatable */
+ lua_pushcfunction(L, ngx_stream_lua_socket_shutdown_pool);
+ lua_setfield(L, -2, "__gc");
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ /* }}} */
+
+ /* {{{socket compiled pattern userdata metatable */
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ pattern_udata_metatable_key));
+ lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */
+ lua_pushcfunction(L, ngx_stream_lua_socket_cleanup_compiled_pattern);
+ lua_setfield(L, -2, "__gc");
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ /* }}} */
+
+#if (NGX_STREAM_SSL)
+
+ /* {{{ssl session userdata metatable */
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ ssl_session_metatable_key));
+ lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */
+ lua_pushcfunction(L, ngx_stream_lua_ssl_free_session);
+ lua_setfield(L, -2, "__gc");
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ /* }}} */
+
+#endif
+}
+
+
+static int
+ngx_stream_lua_socket_tcp(lua_State *L)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+
+ if (lua_gettop(L) != 0) {
+ return luaL_error(L, "expecting zero arguments, but got %d",
+ lua_gettop(L));
+ }
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return luaL_error(L, "no ctx found");
+ }
+
+ ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_YIELDABLE);
+
+ lua_createtable(L, 5 /* narr */, 1 /* nrec */);
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ tcp_socket_metatable_key));
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ lua_setmetatable(L, -2);
+
+ dd("top: %d", lua_gettop(L));
+
+ return 1;
+}
+
+
+static void
+ngx_stream_lua_socket_tcp_create_socket_pool(lua_State *L,
+ ngx_stream_lua_request_t *r, ngx_str_t key, ngx_int_t pool_size, ngx_int_t backlog,
+ ngx_stream_lua_socket_pool_t **spool)
+{
+ u_char *p;
+ size_t size, key_len;
+ ngx_int_t i;
+
+ ngx_stream_lua_socket_pool_t *sp;
+ ngx_stream_lua_socket_pool_item_t *items;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket connection pool size: "
+ "%i, backlog: %i",
+ pool_size, backlog);
+
+ key_len = ngx_align(key.len + 1, sizeof(void *));
+
+ size = sizeof(ngx_stream_lua_socket_pool_t) - 1 + key_len
+ + sizeof(ngx_stream_lua_socket_pool_item_t) * pool_size;
+
+ /* before calling this function, the Lua stack is:
+ * -1 key
+ * -2 pools
+ */
+ sp = lua_newuserdata(L, size);
+ if (sp == NULL) {
+ luaL_error(L, "no memory");
+ return;
+ }
+
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ pool_udata_metatable_key));
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ lua_setmetatable(L, -2);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket keepalive create "
+ "connection pool for key \"%V\"", &key);
+
+ /* a new socket pool with metatable is push to the stack, so now we have:
+ * -1 sp
+ * -2 key
+ * -3 pools
+ *
+ * it is time to set pools[key] to sp.
+ */
+ lua_rawset(L, -3);
+
+ /* clean up the stack for consistency's sake */
+ lua_pop(L, 1);
+
+ sp->backlog = backlog;
+ sp->size = pool_size;
+ sp->connections = 0;
+ sp->lua_vm = ngx_stream_lua_get_lua_vm(r, NULL);
+
+ ngx_queue_init(&sp->cache_connect_op);
+ ngx_queue_init(&sp->wait_connect_op);
+ ngx_queue_init(&sp->cache);
+ ngx_queue_init(&sp->free);
+
+ p = ngx_copy(sp->key, key.data, key.len);
+ *p++ = '\0';
+
+ items = (ngx_stream_lua_socket_pool_item_t *) (sp->key + key_len);
+
+ dd("items: %p", items);
+
+ ngx_stream_lua_assert((void *) items
+ == ngx_align_ptr(items, sizeof(void *)));
+
+ for (i = 0; i < pool_size; i++) {
+ ngx_queue_insert_head(&sp->free, &items[i].queue);
+ items[i].socket_pool = sp;
+ }
+
+ *spool = sp;
+}
+
+
+static int
+ngx_stream_lua_socket_tcp_connect_helper(lua_State *L,
+ ngx_stream_lua_socket_tcp_upstream_t *u, ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx, u_char *host_ref, size_t host_len,
+ in_port_t port, unsigned resuming)
+{
+ int n;
+ int host_size;
+ int saved_top;
+ ngx_int_t rc;
+ ngx_str_t host;
+ ngx_str_t *conn_op_host;
+ ngx_url_t url;
+ ngx_queue_t *q;
+ ngx_resolver_ctx_t *rctx, temp;
+
+ ngx_stream_lua_co_ctx_t *coctx;
+ ngx_stream_lua_socket_pool_t *spool;
+ ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx;
+
+ ngx_stream_core_srv_conf_t *clcf;
+
+ spool = u->socket_pool;
+ if (spool != NULL) {
+ rc = ngx_stream_lua_get_keepalive_peer(r, u);
+
+ if (rc == NGX_OK) {
+ lua_pushinteger(L, 1);
+ return 1;
+ }
+
+ /* rc == NGX_DECLINED */
+
+ spool->connections++;
+
+ /* check if backlog is enabled and
+ * don't queue resuming connection operation */
+ if (spool->backlog >= 0 && !resuming) {
+
+ dd("lua tcp socket %s connections %ld",
+ spool->key, spool->connections);
+
+ if (spool->connections > spool->size + spool->backlog) {
+ spool->connections--;
+ lua_pushnil(L);
+ lua_pushliteral(L, "too many waiting connect operations");
+ return 2;
+ }
+
+ if (spool->connections > spool->size) {
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, u->peer.log, 0,
+ "stream lua tcp socket queue connect "
+ "operation for connection pool \"%s\", "
+ "connections: %i",
+ spool->key, spool->connections);
+
+ host_size = sizeof(u_char) *
+ (ngx_max(host_len, NGX_INET_ADDRSTRLEN) + 1);
+
+ if (!ngx_queue_empty(&spool->cache_connect_op)) {
+ q = ngx_queue_last(&spool->cache_connect_op);
+ ngx_queue_remove(q);
+ conn_op_ctx = ngx_queue_data(
+ q, ngx_stream_lua_socket_tcp_conn_op_ctx_t, queue);
+
+ conn_op_host = &conn_op_ctx->host;
+ if (host_len > conn_op_host->len
+ && host_len > NGX_INET_ADDRSTRLEN)
+ {
+ ngx_free(conn_op_host->data);
+ conn_op_host->data = ngx_alloc(host_size,
+ ngx_cycle->log);
+ if (conn_op_host->data == NULL) {
+ ngx_free(conn_op_ctx);
+ goto no_memory_and_not_resuming;
+ }
+ }
+
+ } else {
+ conn_op_ctx = ngx_alloc(
+ sizeof(ngx_stream_lua_socket_tcp_conn_op_ctx_t),
+ ngx_cycle->log);
+ if (conn_op_ctx == NULL) {
+ goto no_memory_and_not_resuming;
+ }
+
+ conn_op_host = &conn_op_ctx->host;
+ conn_op_host->data = ngx_alloc(host_size, ngx_cycle->log);
+ if (conn_op_host->data == NULL) {
+ ngx_free(conn_op_ctx);
+ goto no_memory_and_not_resuming;
+ }
+ }
+
+ conn_op_ctx->cleanup = NULL;
+
+ ngx_memcpy(conn_op_host->data, host_ref, host_len);
+ conn_op_host->data[host_len] = '\0';
+ conn_op_host->len = host_len;
+
+ conn_op_ctx->port = port;
+
+ u->write_co_ctx = ctx->cur_co_ctx;
+
+ conn_op_ctx->u = u;
+ ctx->cur_co_ctx->cleanup =
+ ngx_stream_lua_tcp_queue_conn_op_cleanup;
+ ctx->cur_co_ctx->data = conn_op_ctx;
+
+ ngx_memzero(&conn_op_ctx->event, sizeof(ngx_event_t));
+ conn_op_ctx->event.handler =
+ ngx_stream_lua_socket_tcp_conn_op_timeout_handler;
+ conn_op_ctx->event.data = conn_op_ctx;
+ conn_op_ctx->event.log = ngx_cycle->log;
+
+ ngx_add_timer(&conn_op_ctx->event, u->connect_timeout);
+
+ ngx_queue_insert_tail(&spool->wait_connect_op,
+ &conn_op_ctx->queue);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "stream lua tcp socket queued connect "
+ "operation for %d(ms), u: %p, ctx: %p",
+ u->connect_timeout, conn_op_ctx->u, conn_op_ctx);
+
+ return lua_yield(L, 0);
+ }
+ }
+
+ } /* end spool != NULL */
+
+ host.data = ngx_palloc(r->pool, host_len + 1);
+ if (host.data == NULL) {
+ return luaL_error(L, "no memory");
+ }
+
+ host.len = host_len;
+
+ ngx_memcpy(host.data, host_ref, host_len);
+ host.data[host_len] = '\0';
+
+ ngx_memzero(&url, sizeof(ngx_url_t));
+ url.url = host;
+ url.default_port = port;
+ url.no_resolve = 1;
+
+ coctx = ctx->cur_co_ctx;
+
+ if (ngx_parse_url(r->pool, &url) != NGX_OK) {
+ lua_pushnil(L);
+
+ if (url.err) {
+ lua_pushfstring(L, "failed to parse host name \"%s\": %s",
+ url.url.data, url.err);
+
+ } else {
+ lua_pushfstring(L, "failed to parse host name \"%s\"",
+ url.url.data);
+ }
+
+ goto failed;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket connect timeout: %M",
+ u->connect_timeout);
+
+ u->resolved = ngx_pcalloc(r->pool,
+ sizeof(ngx_stream_upstream_resolved_t));
+ if (u->resolved == NULL) {
+ if (resuming) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "no memory");
+ goto failed;
+ }
+
+ goto no_memory_and_not_resuming;
+ }
+
+ if (url.addrs && url.addrs[0].sockaddr) {
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket network address given "
+ "directly");
+
+ u->resolved->sockaddr = url.addrs[0].sockaddr;
+ u->resolved->socklen = url.addrs[0].socklen;
+ u->resolved->naddrs = 1;
+ u->resolved->host = url.addrs[0].name;
+
+ } else {
+ u->resolved->host = host;
+ u->resolved->port = url.default_port;
+ }
+
+ if (u->resolved->sockaddr) {
+ rc = ngx_stream_lua_socket_resolve_retval_handler(r, u, L);
+ if (rc == NGX_AGAIN && !resuming) {
+ return lua_yield(L, 0);
+ }
+
+ if (rc > 1) {
+ goto failed;
+ }
+
+ return rc;
+ }
+
+ clcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_core_module);
+
+ temp.name = host;
+ rctx = ngx_resolve_start(clcf->resolver, &temp);
+ if (rctx == NULL) {
+ u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_RESOLVER;
+ lua_pushnil(L);
+ lua_pushliteral(L, "failed to start the resolver");
+ goto failed;
+ }
+
+ if (rctx == NGX_NO_RESOLVER) {
+ u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_RESOLVER;
+ lua_pushnil(L);
+ lua_pushfstring(L, "no resolver defined to resolve \"%s\"", host.data);
+ goto failed;
+ }
+
+ rctx->name = host;
+ rctx->handler = ngx_stream_lua_socket_resolve_handler;
+ rctx->data = u;
+ rctx->timeout = clcf->resolver_timeout;
+
+ u->resolved->ctx = rctx;
+ u->write_co_ctx = ctx->cur_co_ctx;
+
+ ngx_stream_lua_cleanup_pending_operation(coctx);
+ coctx->cleanup = ngx_stream_lua_tcp_resolve_cleanup;
+ coctx->data = u;
+
+ saved_top = lua_gettop(L);
+
+ if (ngx_resolve_name(rctx) != NGX_OK) {
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket fail to run resolver "
+ "immediately");
+
+ u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_RESOLVER;
+
+ coctx->cleanup = NULL;
+ coctx->data = NULL;
+
+ u->resolved->ctx = NULL;
+ lua_pushnil(L);
+ lua_pushfstring(L, "%s could not be resolved", host.data);
+ goto failed;
+ }
+
+ if (u->conn_waiting) {
+ dd("resolved and already connecting");
+
+ if (resuming) {
+ return NGX_AGAIN;
+ }
+
+ return lua_yield(L, 0);
+ }
+
+ n = lua_gettop(L) - saved_top;
+ if (n) {
+ dd("errors occurred during resolving or connecting"
+ "or already connected");
+
+ if (n > 1) {
+ goto failed;
+ }
+
+ return n;
+ }
+
+ /* still resolving */
+
+ u->conn_waiting = 1;
+ u->write_prepare_retvals = ngx_stream_lua_socket_resolve_retval_handler;
+
+ dd("setting data to %p", u);
+
+ if (ctx->entered_content_phase) {
+ r->write_event_handler = ngx_stream_lua_content_wev_handler;
+
+ } else {
+ r->write_event_handler = ngx_stream_lua_core_run_phases;
+ }
+
+ if (resuming) {
+ return NGX_AGAIN;
+ }
+
+ return lua_yield(L, 0);
+
+failed:
+
+ if (spool != NULL) {
+ spool->connections--;
+ ngx_stream_lua_socket_tcp_resume_conn_op(spool);
+ }
+
+ return 2;
+
+no_memory_and_not_resuming:
+
+ if (spool != NULL) {
+ spool->connections--;
+ ngx_stream_lua_socket_tcp_resume_conn_op(spool);
+ }
+
+ return luaL_error(L, "no memory");
+}
+
+
+static int
+ngx_stream_lua_socket_tcp_connect(lua_State *L)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+ int port;
+ int n;
+ u_char *p;
+ size_t len;
+ ngx_peer_connection_t *pc;
+ int connect_timeout, send_timeout, read_timeout;
+ unsigned custom_pool;
+ int key_index;
+ ngx_int_t backlog;
+ ngx_int_t pool_size;
+ ngx_str_t key;
+ const char *msg;
+
+ ngx_stream_lua_loc_conf_t *llcf;
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+ ngx_stream_lua_socket_pool_t *spool;
+
+ n = lua_gettop(L);
+ if (n != 2 && n != 3 && n != 4) {
+ return luaL_error(L, "ngx.socket connect: expecting 2, 3, or 4 "
+ "arguments (including the object), but seen %d", n);
+ }
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return luaL_error(L, "no ctx found");
+ }
+
+ ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_YIELDABLE);
+
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ p = (u_char *) luaL_checklstring(L, 2, &len);
+
+ backlog = -1;
+ key_index = 2;
+ pool_size = 0;
+ custom_pool = 0;
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+
+ if (lua_type(L, n) == LUA_TTABLE) {
+
+ /* found the last optional option table */
+
+ lua_getfield(L, n, "pool_size");
+
+ if (lua_isnumber(L, -1)) {
+ pool_size = (ngx_int_t) lua_tointeger(L, -1);
+
+ if (pool_size <= 0) {
+ msg = lua_pushfstring(L, "bad \"pool_size\" option value: %i",
+ pool_size);
+ return luaL_argerror(L, n, msg);
+ }
+
+ } else if (!lua_isnil(L, -1)) {
+ msg = lua_pushfstring(L, "bad \"pool_size\" option type: %s",
+ lua_typename(L, lua_type(L, -1)));
+ return luaL_argerror(L, n, msg);
+ }
+
+ lua_pop(L, 1);
+
+ lua_getfield(L, n, "backlog");
+
+ if (lua_isnumber(L, -1)) {
+ backlog = (ngx_int_t) lua_tointeger(L, -1);
+
+ if (backlog < 0) {
+ msg = lua_pushfstring(L, "bad \"backlog\" option value: %i",
+ backlog);
+ return luaL_argerror(L, n, msg);
+ }
+
+ /* use default value for pool size if only backlog specified */
+ if (pool_size == 0) {
+ pool_size = llcf->pool_size;
+ }
+ }
+
+ lua_pop(L, 1);
+
+ lua_getfield(L, n, "pool");
+
+ switch (lua_type(L, -1)) {
+ case LUA_TNUMBER:
+ lua_tostring(L, -1);
+ /* FALLTHROUGH */
+
+ case LUA_TSTRING:
+ custom_pool = 1;
+
+ lua_pushvalue(L, -1);
+ lua_rawseti(L, 1, SOCKET_KEY_INDEX);
+
+ key_index = n + 1;
+
+ break;
+
+ case LUA_TNIL:
+ lua_pop(L, 2);
+ break;
+
+ default:
+ msg = lua_pushfstring(L, "bad \"pool\" option type: %s",
+ luaL_typename(L, -1));
+ luaL_argerror(L, n, msg);
+ break;
+ }
+
+ n--;
+ }
+
+ /* the fourth argument is not a table */
+ if (n == 4) {
+ lua_pop(L, 1);
+ n--;
+ }
+
+ if (n == 3) {
+ port = luaL_checkinteger(L, 3);
+
+ if (port < 0 || port > 65535) {
+ lua_pushnil(L);
+ lua_pushfstring(L, "bad port number: %d", port);
+ return 2;
+ }
+
+ if (!custom_pool) {
+ lua_pushliteral(L, ":");
+ lua_insert(L, 3);
+ lua_concat(L, 3);
+ }
+
+ dd("socket key: %s", lua_tostring(L, -1));
+
+ } else { /* n == 2 */
+ port = 0;
+ }
+
+ if (!custom_pool) {
+ /* the key's index is 2 */
+
+ lua_pushvalue(L, 2);
+ lua_rawseti(L, 1, SOCKET_KEY_INDEX);
+ }
+
+ lua_rawgeti(L, 1, SOCKET_CTX_INDEX);
+ u = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ if (u) {
+ if (u->request && u->request != r) {
+ return luaL_error(L, "bad request");
+ }
+
+ ngx_stream_lua_socket_check_busy_connecting(r, u, L);
+ ngx_stream_lua_socket_check_busy_reading(r, u, L);
+ ngx_stream_lua_socket_check_busy_writing(r, u, L);
+
+ if (u->body_downstream || u->raw_downstream) {
+ return luaL_error(L, "attempt to re-connect a request socket");
+ }
+
+ if (u->peer.connection) {
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket reconnect without "
+ "shutting down");
+
+ ngx_stream_lua_socket_tcp_finalize(r, u);
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua reuse socket upstream ctx");
+
+ } else {
+ u = lua_newuserdata(L, sizeof(ngx_stream_lua_socket_tcp_upstream_t));
+ if (u == NULL) {
+ return luaL_error(L, "no memory");
+ }
+
+#if 1
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ upstream_udata_metatable_key));
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ lua_setmetatable(L, -2);
+#endif
+
+ lua_rawseti(L, 1, SOCKET_CTX_INDEX);
+ }
+
+ ngx_memzero(u, sizeof(ngx_stream_lua_socket_tcp_upstream_t));
+
+ u->request = r; /* set the controlling request */
+
+ u->conf = llcf;
+
+ pc = &u->peer;
+
+ pc->log = r->connection->log;
+ pc->log_error = NGX_ERROR_ERR;
+
+ dd("lua peer connection log: %p", pc->log);
+
+ lua_rawgeti(L, 1, SOCKET_CONNECT_TIMEOUT_INDEX);
+ lua_rawgeti(L, 1, SOCKET_SEND_TIMEOUT_INDEX);
+ lua_rawgeti(L, 1, SOCKET_READ_TIMEOUT_INDEX);
+
+ read_timeout = (ngx_int_t) lua_tointeger(L, -1);
+ send_timeout = (ngx_int_t) lua_tointeger(L, -2);
+ connect_timeout = (ngx_int_t) lua_tointeger(L, -3);
+
+ lua_pop(L, 3);
+
+ if (connect_timeout > 0) {
+ u->connect_timeout = (ngx_msec_t) connect_timeout;
+
+ } else {
+ u->connect_timeout = u->conf->connect_timeout;
+ }
+
+ if (send_timeout > 0) {
+ u->send_timeout = (ngx_msec_t) send_timeout;
+
+ } else {
+ u->send_timeout = u->conf->send_timeout;
+ }
+
+ if (read_timeout > 0) {
+ u->read_timeout = (ngx_msec_t) read_timeout;
+
+ } else {
+ u->read_timeout = u->conf->read_timeout;
+ }
+
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ socket_pool_key));
+ lua_rawget(L, LUA_REGISTRYINDEX); /* table */
+ lua_pushvalue(L, key_index); /* key */
+
+ lua_rawget(L, -2);
+ spool = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ if (spool != NULL) {
+ u->socket_pool = spool;
+
+ } else if (pool_size > 0) {
+ lua_pushvalue(L, key_index);
+ key.data = (u_char *) lua_tolstring(L, -1, &key.len);
+
+ ngx_stream_lua_socket_tcp_create_socket_pool(L, r, key, pool_size,
+ backlog, &spool);
+ u->socket_pool = spool;
+ }
+
+ return ngx_stream_lua_socket_tcp_connect_helper(L, u, r, ctx, p,
+ len, port, 0);
+}
+
+
+static void
+ngx_stream_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx)
+{
+ ngx_stream_lua_request_t *r;
+#if (NGX_DEBUG)
+ ngx_connection_t *c;
+#endif
+ lua_State *L;
+ u_char *p;
+ size_t len;
+ socklen_t socklen;
+ struct sockaddr *sockaddr;
+ ngx_uint_t i;
+ unsigned waiting;
+
+ ngx_stream_upstream_resolved_t *ur;
+ ngx_stream_lua_ctx_t *lctx;
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+
+ u = ctx->data;
+ r = u->request;
+
+#if (NGX_DEBUG)
+
+ c = r->connection;
+
+#endif
+
+ ur = u->resolved;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "stream lua tcp socket resolve handler");
+
+ lctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (lctx == NULL) {
+ return;
+ }
+
+ lctx->cur_co_ctx = u->write_co_ctx;
+
+ u->write_co_ctx->cleanup = NULL;
+
+ L = lctx->cur_co_ctx->co;
+
+ waiting = u->conn_waiting;
+
+ if (ctx->state) {
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "stream lua tcp socket resolver error: %s "
+ "(connect waiting: %d)",
+ ngx_resolver_strerror(ctx->state), (int) waiting);
+
+ lua_pushnil(L);
+ lua_pushlstring(L, (char *) ctx->name.data, ctx->name.len);
+ lua_pushfstring(L, " could not be resolved (%d: %s)",
+ (int) ctx->state,
+ ngx_resolver_strerror(ctx->state));
+ lua_concat(L, 2);
+
+ u->write_prepare_retvals =
+ ngx_stream_lua_socket_conn_error_retval_handler;
+ ngx_stream_lua_socket_handle_conn_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_RESOLVER);
+
+
+ return;
+ }
+
+ ur->naddrs = ctx->naddrs;
+ ur->addrs = ctx->addrs;
+
+#if (NGX_DEBUG)
+ {
+ u_char text[NGX_SOCKADDR_STRLEN];
+ ngx_str_t addr;
+ ngx_uint_t i;
+
+ addr.data = text;
+
+ for (i = 0; i < ctx->naddrs; i++) {
+ addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr, ur->addrs[i].socklen,
+ text, NGX_SOCKADDR_STRLEN, 0);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "name was resolved to %V", &addr);
+ }
+ }
+#endif
+
+ ngx_stream_lua_assert(ur->naddrs > 0);
+
+ if (ur->naddrs == 1) {
+ i = 0;
+
+ } else {
+ i = ngx_random() % ur->naddrs;
+ }
+
+ dd("selected addr index: %d", (int) i);
+
+ socklen = ur->addrs[i].socklen;
+
+ sockaddr = ngx_palloc(r->pool, socklen);
+ if (sockaddr == NULL) {
+ goto nomem;
+ }
+
+ ngx_memcpy(sockaddr, ur->addrs[i].sockaddr, socklen);
+
+ switch (sockaddr->sa_family) {
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ ((struct sockaddr_in6 *) sockaddr)->sin6_port = htons(ur->port);
+ break;
+#endif
+ default: /* AF_INET */
+ ((struct sockaddr_in *) sockaddr)->sin_port = htons(ur->port);
+ }
+
+ p = ngx_pnalloc(r->pool, NGX_SOCKADDR_STRLEN);
+ if (p == NULL) {
+ goto nomem;
+ }
+
+ len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1);
+ ur->sockaddr = sockaddr;
+ ur->socklen = socklen;
+ ur->host.data = p;
+ ur->host.len = len;
+ ur->naddrs = 1;
+
+ ngx_resolve_name_done(ctx);
+ ur->ctx = NULL;
+
+ u->conn_waiting = 0;
+ u->write_co_ctx = NULL;
+
+ if (waiting) {
+ lctx->resume_handler = ngx_stream_lua_socket_tcp_conn_resume;
+ r->write_event_handler(r);
+
+
+ } else {
+ (void) ngx_stream_lua_socket_resolve_retval_handler(r, u, L);
+ }
+
+ return;
+
+nomem:
+
+ if (ur->ctx) {
+ ngx_resolve_name_done(ctx);
+ ur->ctx = NULL;
+ }
+
+ u->write_prepare_retvals = ngx_stream_lua_socket_conn_error_retval_handler;
+ ngx_stream_lua_socket_handle_conn_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_NOMEM);
+
+ if (waiting) {
+ dd("run posted requests");
+
+
+ } else {
+ lua_pushnil(L);
+ lua_pushliteral(L, "no memory");
+ }
+}
+
+
+static void
+ngx_stream_lua_socket_init_peer_connection_addr_text(ngx_peer_connection_t *pc)
+{
+ ngx_connection_t *c;
+ size_t addr_text_max_len;
+
+ c = pc->connection;
+
+ switch (pc->sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ addr_text_max_len = NGX_INET6_ADDRSTRLEN;
+ break;
+#endif
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+ case AF_UNIX:
+ addr_text_max_len = NGX_UNIX_ADDRSTRLEN;
+ break;
+#endif
+
+ case AF_INET:
+ addr_text_max_len = NGX_INET_ADDRSTRLEN;
+ break;
+
+ default:
+ addr_text_max_len = NGX_SOCKADDR_STRLEN;
+ break;
+ }
+
+ c->addr_text.data = ngx_pnalloc(c->pool, addr_text_max_len);
+ if (c->addr_text.data == NULL) {
+ ngx_log_error(NGX_LOG_ERR, pc->log, 0,
+ "init peer connection addr_text failed: no memory");
+ return;
+ }
+
+ c->addr_text.len = ngx_sock_ntop(pc->sockaddr, pc->socklen,
+ c->addr_text.data,
+ addr_text_max_len, 0);
+}
+
+
+static int
+ngx_stream_lua_socket_resolve_retval_handler(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L)
+{
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_peer_connection_t *pc;
+ ngx_connection_t *c;
+ ngx_stream_lua_cleanup_t *cln;
+ ngx_stream_upstream_resolved_t *ur;
+ ngx_int_t rc;
+ ngx_stream_lua_co_ctx_t *coctx;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket resolve retval handler");
+
+ if (u->ft_type & NGX_STREAM_LUA_SOCKET_FT_RESOLVER) {
+ return 2;
+ }
+
+ pc = &u->peer;
+
+ ur = u->resolved;
+
+ if (ur->sockaddr) {
+ pc->sockaddr = ur->sockaddr;
+ pc->socklen = ur->socklen;
+ pc->name = &ur->host;
+
+ } else {
+ lua_pushnil(L);
+ lua_pushliteral(L, "resolver not working");
+ return 2;
+ }
+
+ pc->get = ngx_stream_lua_socket_tcp_get_peer;
+
+ rc = ngx_event_connect_peer(pc);
+
+ if (rc == NGX_ERROR) {
+ u->socket_errno = ngx_socket_errno;
+ }
+
+ if (u->cleanup == NULL) {
+ cln = ngx_stream_lua_cleanup_add(r, 0);
+ if (cln == NULL) {
+ u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_ERROR;
+ lua_pushnil(L);
+ lua_pushliteral(L, "no memory");
+ return 2;
+ }
+
+ cln->handler = ngx_stream_lua_socket_tcp_cleanup;
+ cln->data = u;
+ u->cleanup = &cln->handler;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket connect: %i", rc);
+
+ if (rc == NGX_ERROR) {
+ return ngx_stream_lua_socket_conn_error_retval_handler(r, u, L);
+ }
+
+ if (rc == NGX_BUSY) {
+ u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_ERROR;
+ lua_pushnil(L);
+ lua_pushliteral(L, "no live connection");
+ return 2;
+ }
+
+ if (rc == NGX_DECLINED) {
+ dd("socket errno: %d", (int) ngx_socket_errno);
+ u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_ERROR;
+ u->socket_errno = ngx_socket_errno;
+ return ngx_stream_lua_socket_conn_error_retval_handler(r, u, L);
+ }
+
+ /* rc == NGX_OK || rc == NGX_AGAIN */
+
+ c = pc->connection;
+
+ c->data = u;
+
+ c->write->handler = ngx_stream_lua_socket_tcp_handler;
+ c->read->handler = ngx_stream_lua_socket_tcp_handler;
+
+ u->write_event_handler = ngx_stream_lua_socket_connected_handler;
+ u->read_event_handler = ngx_stream_lua_socket_connected_handler;
+
+ c->sendfile &= r->connection->sendfile;
+
+ if (c->pool == NULL) {
+
+ /* we need separate pool here to be able to cache SSL connections */
+
+ c->pool = ngx_create_pool(128, r->connection->log);
+ if (c->pool == NULL) {
+ return ngx_stream_lua_socket_prepare_error_retvals(r, u, L,
+ NGX_STREAM_LUA_SOCKET_FT_NOMEM);
+ }
+ }
+
+ c->log = r->connection->log;
+ c->pool->log = c->log;
+ c->read->log = c->log;
+ c->write->log = c->log;
+
+ /* init or reinit the ngx_output_chain() and ngx_chain_writer() contexts */
+
+#if 0
+ u->writer.out = NULL;
+ u->writer.last = &u->writer.out;
+#endif
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+
+ coctx = ctx->cur_co_ctx;
+
+ dd("setting data to %p", u);
+
+ if (rc == NGX_OK) {
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket connected: fd:%d", (int) c->fd);
+
+ /* We should delete the current write/read event
+ * here because the socket object may not be used immediately
+ * on the Lua land, thus causing hot spin around level triggered
+ * event poll and wasting CPU cycles. */
+
+ if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+ ngx_stream_lua_socket_handle_conn_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_ERROR);
+ lua_pushnil(L);
+ lua_pushliteral(L, "failed to handle write event");
+ return 2;
+ }
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ ngx_stream_lua_socket_handle_conn_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_ERROR);
+ lua_pushnil(L);
+ lua_pushliteral(L, "failed to handle read event");
+ return 2;
+ }
+
+ u->read_event_handler = ngx_stream_lua_socket_dummy_handler;
+ u->write_event_handler = ngx_stream_lua_socket_dummy_handler;
+
+ lua_pushinteger(L, 1);
+ return 1;
+ }
+
+ /* rc == NGX_AGAIN */
+
+ ngx_stream_lua_cleanup_pending_operation(coctx);
+ coctx->cleanup = ngx_stream_lua_coctx_cleanup;
+ coctx->data = u;
+
+ ngx_add_timer(c->write, u->connect_timeout);
+
+ u->write_co_ctx = ctx->cur_co_ctx;
+ u->conn_waiting = 1;
+ u->write_prepare_retvals = ngx_stream_lua_socket_tcp_conn_retval_handler;
+
+ dd("setting data to %p", u);
+
+ if (ctx->entered_content_phase) {
+ r->write_event_handler = ngx_stream_lua_content_wev_handler;
+
+ } else {
+ r->write_event_handler = ngx_stream_lua_core_run_phases;
+ }
+
+ return NGX_AGAIN;
+}
+
+
+static int
+ngx_stream_lua_socket_conn_error_retval_handler(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L)
+{
+ ngx_uint_t ft_type;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket error retval handler");
+
+ if (u->write_co_ctx) {
+ u->write_co_ctx->cleanup = NULL;
+ }
+
+ ngx_stream_lua_socket_tcp_finalize(r, u);
+
+ ft_type = u->ft_type;
+ u->ft_type = 0;
+ return ngx_stream_lua_socket_prepare_error_retvals(r, u, L, ft_type);
+}
+
+
+#if (NGX_STREAM_SSL)
+
+static int
+ngx_stream_lua_socket_tcp_sslhandshake(lua_State *L)
+{
+ int n, top;
+ ngx_int_t rc;
+ ngx_str_t name = ngx_null_string;
+ ngx_connection_t *c;
+ ngx_ssl_session_t **psession;
+
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx;
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+
+ /* Lua function arguments: self [,session] [,host] [,verify]
+ [,send_status_req] */
+
+ n = lua_gettop(L);
+ if (n < 1 || n > 5) {
+ return luaL_error(L, "ngx.socket sslhandshake: expecting 1 ~ 5 "
+ "arguments (including the object), but seen %d", n);
+ }
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket ssl handshake");
+
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ lua_rawgeti(L, 1, SOCKET_CTX_INDEX);
+ u = lua_touserdata(L, -1);
+
+ if (u == NULL
+ || u->peer.connection == NULL
+ || u->read_closed
+ || u->write_closed)
+ {
+ lua_pushnil(L);
+ lua_pushliteral(L, "closed");
+ return 2;
+ }
+
+ if (u->request != r) {
+ return luaL_error(L, "bad request");
+ }
+
+ ngx_stream_lua_socket_check_busy_connecting(r, u, L);
+ ngx_stream_lua_socket_check_busy_reading(r, u, L);
+ ngx_stream_lua_socket_check_busy_writing(r, u, L);
+
+ if (u->raw_downstream || u->body_downstream) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "not supported for downstream");
+ return 2;
+ }
+
+ c = u->peer.connection;
+
+ u->ssl_session_reuse = 1;
+
+ if (c->ssl && c->ssl->handshaked) {
+ switch (lua_type(L, 2)) {
+ case LUA_TUSERDATA:
+ lua_pushvalue(L, 2);
+ break;
+
+ case LUA_TBOOLEAN:
+ if (!lua_toboolean(L, 2)) {
+ /* avoid generating the ssl session */
+ lua_pushboolean(L, 1);
+ break;
+ }
+ /* fall through */
+
+ default:
+ ngx_stream_lua_ssl_handshake_retval_handler(r, u, L);
+ break;
+ }
+
+ return 1;
+ }
+
+ if (ngx_ssl_create_connection(u->conf->ssl, c,
+ NGX_SSL_BUFFER|NGX_SSL_CLIENT)
+ != NGX_OK)
+ {
+ lua_pushnil(L);
+ lua_pushliteral(L, "failed to create ssl connection");
+ return 2;
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return luaL_error(L, "no ctx found");
+ }
+
+ coctx = ctx->cur_co_ctx;
+
+ c->sendfile = 0;
+
+ if (n >= 2) {
+ if (lua_type(L, 2) == LUA_TBOOLEAN) {
+ u->ssl_session_reuse = lua_toboolean(L, 2);
+
+ } else {
+ psession = lua_touserdata(L, 2);
+
+ if (psession != NULL && *psession != NULL) {
+ if (ngx_ssl_set_session(c, *psession) != NGX_OK) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "lua ssl set session failed");
+ return 2;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "stream lua ssl set session: %p", *psession);
+ }
+ }
+
+ if (n >= 3) {
+ name.data = (u_char *) lua_tolstring(L, 3, &name.len);
+
+ if (name.data) {
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua ssl server name: \"%*s\"", name.len,
+ name.data);
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+
+ if (SSL_set_tlsext_host_name(c->ssl->connection,
+ (char *) name.data)
+ == 0)
+ {
+ lua_pushnil(L);
+ lua_pushliteral(L, "SSL_set_tlsext_host_name failed");
+ return 2;
+ }
+
+#else
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "lua socket SNI disabled because the current "
+ "version of OpenSSL lacks the support");
+
+#endif
+ }
+
+ if (n >= 4) {
+ u->ssl_verify = lua_toboolean(L, 4);
+
+ if (n >= 5) {
+ if (lua_toboolean(L, 5)) {
+#ifdef NGX_STREAM_LUA_USE_OCSP
+ SSL_set_tlsext_status_type(c->ssl->connection,
+ TLSEXT_STATUSTYPE_ocsp);
+#else
+ return luaL_error(L, "no OCSP support");
+#endif
+ }
+ }
+ }
+ }
+ }
+
+ dd("found sni name: %.*s %p", (int) name.len, name.data, name.data);
+
+ if (name.len == 0) {
+ u->ssl_name.len = 0;
+
+ } else {
+ if (u->ssl_name.data) {
+ /* buffer already allocated */
+
+ if (u->ssl_name.len >= name.len) {
+ /* reuse it */
+ ngx_memcpy(u->ssl_name.data, name.data, name.len);
+ u->ssl_name.len = name.len;
+
+ } else {
+ ngx_free(u->ssl_name.data);
+ goto new_ssl_name;
+ }
+
+ } else {
+
+new_ssl_name:
+
+ u->ssl_name.data = ngx_alloc(name.len, ngx_cycle->log);
+ if (u->ssl_name.data == NULL) {
+ u->ssl_name.len = 0;
+
+ lua_pushnil(L);
+ lua_pushliteral(L, "no memory");
+ return 2;
+ }
+
+ ngx_memcpy(u->ssl_name.data, name.data, name.len);
+ u->ssl_name.len = name.len;
+ }
+ }
+
+ u->write_co_ctx = coctx;
+
+#if 0
+#ifdef NGX_STREAM_LUA_USE_OCSP
+ SSL_set_tlsext_status_type(c->ssl->connection, TLSEXT_STATUSTYPE_ocsp);
+#endif
+#endif
+
+ rc = ngx_ssl_handshake(c);
+
+ dd("ngx_ssl_handshake returned %d", (int) rc);
+
+ if (rc == NGX_AGAIN) {
+ if (c->write->timer_set) {
+ ngx_del_timer(c->write);
+ }
+
+ ngx_add_timer(c->read, u->connect_timeout);
+
+ u->conn_waiting = 1;
+ u->write_prepare_retvals = ngx_stream_lua_ssl_handshake_retval_handler;
+
+ ngx_stream_lua_cleanup_pending_operation(coctx);
+ coctx->cleanup = ngx_stream_lua_coctx_cleanup;
+ coctx->data = u;
+
+ c->ssl->handler = ngx_stream_lua_ssl_handshake_handler;
+
+ if (ctx->entered_content_phase) {
+ r->write_event_handler = ngx_stream_lua_content_wev_handler;
+
+ } else {
+ r->write_event_handler = ngx_stream_lua_core_run_phases;
+ }
+
+ return lua_yield(L, 0);
+ }
+
+ top = lua_gettop(L);
+ ngx_stream_lua_ssl_handshake_handler(c);
+ return lua_gettop(L) - top;
+}
+
+
+static void
+ngx_stream_lua_ssl_handshake_handler(ngx_connection_t *c)
+{
+ const char *err;
+ int waiting;
+ lua_State *L;
+ ngx_int_t rc;
+ ngx_connection_t *dc; /* downstream connection */
+ ngx_stream_lua_request_t *r;
+
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_loc_conf_t *llcf;
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+
+ u = c->data;
+ r = u->request;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return;
+ }
+
+ c->write->handler = ngx_stream_lua_socket_tcp_handler;
+ c->read->handler = ngx_stream_lua_socket_tcp_handler;
+
+ waiting = u->conn_waiting;
+
+ dc = r->connection;
+ L = u->write_co_ctx->co;
+
+ if (c->read->timedout) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "timeout");
+ goto failed;
+ }
+
+ if (c->read->timer_set) {
+ ngx_del_timer(c->read);
+ }
+
+ if (c->ssl->handshaked) {
+
+ if (u->ssl_verify) {
+ rc = SSL_get_verify_result(c->ssl->connection);
+
+ if (rc != X509_V_OK) {
+ lua_pushnil(L);
+ err = lua_pushfstring(L, "%d: %s", (int) rc,
+ X509_verify_cert_error_string(rc));
+
+ llcf = ngx_stream_lua_get_module_loc_conf(r,
+ ngx_stream_lua_module);
+ if (llcf->log_socket_errors) {
+ ngx_log_error(NGX_LOG_ERR, dc->log, 0, "stream lua ssl "
+ "certificate verify error: (%s)", err);
+ }
+
+ goto failed;
+ }
+
+#if defined(nginx_version) && nginx_version >= 1007000
+
+ if (u->ssl_name.len
+ && ngx_ssl_check_host(c, &u->ssl_name) != NGX_OK)
+ {
+ lua_pushnil(L);
+ lua_pushliteral(L, "certificate host mismatch");
+
+ llcf = ngx_stream_lua_get_module_loc_conf(r,
+ ngx_stream_lua_module);
+ if (llcf->log_socket_errors) {
+ ngx_log_error(NGX_LOG_ERR, dc->log, 0, "stream lua ssl "
+ "certificate does not match host \"%V\"",
+ &u->ssl_name);
+ }
+
+ goto failed;
+ }
+
+#endif
+ }
+
+ if (waiting) {
+ ngx_stream_lua_socket_handle_conn_success(r, u);
+
+ } else {
+ (void) ngx_stream_lua_ssl_handshake_retval_handler(r, u, L);
+ }
+
+
+ return;
+ }
+
+ lua_pushnil(L);
+ lua_pushliteral(L, "handshake failed");
+
+failed:
+
+ if (waiting) {
+ u->write_prepare_retvals =
+ ngx_stream_lua_socket_conn_error_retval_handler;
+ ngx_stream_lua_socket_handle_conn_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_SSL);
+
+
+ } else {
+ (void) ngx_stream_lua_socket_conn_error_retval_handler(r, u, L);
+ }
+}
+
+
+static int
+ngx_stream_lua_ssl_handshake_retval_handler(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L)
+{
+ ngx_connection_t *c;
+ ngx_ssl_session_t *ssl_session, **ud;
+
+ if (!u->ssl_session_reuse) {
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+
+ ud = lua_newuserdata(L, sizeof(ngx_ssl_session_t *));
+
+ c = u->peer.connection;
+
+ ssl_session = ngx_ssl_get_session(c);
+ if (ssl_session == NULL) {
+ *ud = NULL;
+
+ } else {
+ *ud = ssl_session;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "stream lua ssl save session: %p", ssl_session);
+
+ /* set up the __gc metamethod */
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ ssl_session_metatable_key));
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ lua_setmetatable(L, -2);
+ }
+
+ return 1;
+}
+
+#endif /* NGX_STREAM_SSL */
+
+
+static int
+ngx_stream_lua_socket_read_error_retval_handler(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L)
+{
+ ngx_uint_t ft_type;
+
+ if (u->read_co_ctx) {
+ u->read_co_ctx->cleanup = NULL;
+ }
+
+ ft_type = u->ft_type;
+ u->ft_type = 0;
+
+ if (u->no_close) {
+ u->no_close = 0;
+
+ } else {
+ ngx_stream_lua_socket_tcp_finalize_read_part(r, u);
+ }
+
+ return ngx_stream_lua_socket_prepare_error_retvals(r, u, L, ft_type);
+}
+
+
+static int
+ngx_stream_lua_socket_write_error_retval_handler(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L)
+{
+ ngx_uint_t ft_type;
+
+ if (u->write_co_ctx) {
+ u->write_co_ctx->cleanup = NULL;
+ }
+
+ ngx_stream_lua_socket_tcp_finalize_write_part(r, u, 0);
+
+ ft_type = u->ft_type;
+ u->ft_type = 0;
+ return ngx_stream_lua_socket_prepare_error_retvals(r, u, L, ft_type);
+}
+
+
+static int
+ngx_stream_lua_socket_prepare_error_retvals(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L, ngx_uint_t ft_type)
+{
+ u_char errstr[NGX_MAX_ERROR_STR];
+ u_char *p;
+
+ if (ft_type & (NGX_STREAM_LUA_SOCKET_FT_RESOLVER
+ | NGX_STREAM_LUA_SOCKET_FT_SSL))
+ {
+ return 2;
+ }
+
+ lua_pushnil(L);
+
+ if (ft_type & NGX_STREAM_LUA_SOCKET_FT_TIMEOUT) {
+ lua_pushliteral(L, "timeout");
+
+ } else if (ft_type & NGX_STREAM_LUA_SOCKET_FT_CLOSED) {
+ lua_pushliteral(L, "closed");
+
+ } else if (ft_type & NGX_STREAM_LUA_SOCKET_FT_BUFTOOSMALL) {
+ lua_pushliteral(L, "buffer too small");
+
+ } else if (ft_type & NGX_STREAM_LUA_SOCKET_FT_NOMEM) {
+ lua_pushliteral(L, "no memory");
+
+ } else if (ft_type & NGX_STREAM_LUA_SOCKET_FT_CLIENTABORT) {
+ lua_pushliteral(L, "client aborted");
+
+ } else {
+
+ if (u->socket_errno) {
+ p = ngx_strerror(u->socket_errno, errstr, sizeof(errstr));
+ /* for compatibility with LuaSocket */
+ ngx_strlow(errstr, errstr, p - errstr);
+ lua_pushlstring(L, (char *) errstr, p - errstr);
+
+ } else {
+ lua_pushliteral(L, "error");
+ }
+ }
+
+ return 2;
+}
+
+
+static int
+ngx_stream_lua_socket_tcp_conn_retval_handler(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L)
+{
+ if (u->ft_type) {
+ return ngx_stream_lua_socket_conn_error_retval_handler(r, u, L);
+ }
+
+ lua_pushinteger(L, 1);
+ return 1;
+}
+
+
+static int
+ngx_stream_lua_socket_tcp_peek(lua_State *L)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_connection_t *c;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_loc_conf_t *llcf;
+ ngx_stream_lua_co_ctx_t *coctx;
+ int n;
+ lua_Integer bytes;
+ size_t size;
+
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_PREREAD);
+
+ n = lua_gettop(L);
+ if (n != 2) {
+ return luaL_error(L, "expecting 2 arguments "
+ "(including the object), but got %d", n);
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket calling peek() method");
+
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ lua_rawgeti(L, 1, SOCKET_CTX_INDEX);
+ u = lua_touserdata(L, -1);
+
+ if (u == NULL) {
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+
+ if (llcf->log_socket_errors) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "attempt to peek data on a closed socket: u:%p", u);
+ }
+
+ lua_pushnil(L);
+ lua_pushliteral(L, "closed");
+ return 2;
+ }
+
+ if (u->read_consumed) {
+ return luaL_error(L, "attempt to peek on a consumed socket");
+ }
+
+ c = u->peer.connection;
+
+ if (u->request != r) {
+ return luaL_error(L, "bad request");
+ }
+
+ ngx_stream_lua_socket_check_busy_reading(r, u, L);
+
+ if (!lua_isnumber(L, 2)) {
+ return luaL_error(L, "argument must be a number");
+ }
+
+ bytes = lua_tointeger(L, 2);
+ if (bytes < 0) {
+ return luaL_argerror(L, 2, "bytes can not be negative");
+ }
+
+ if (bytes == 0) {
+ lua_pushliteral(L, "");
+ return 1;
+ }
+
+ u->length = (size_t) bytes;
+
+ if (c->buffer != NULL) {
+ size = c->buffer->last - c->buffer->pos;
+
+ if (size >= u->length) {
+ lua_pushlstring(L, (char *) c->buffer->pos, u->length);
+ return 1;
+ }
+ }
+
+ /* not enough data in the preread buffer */
+
+ coctx = ctx->cur_co_ctx;
+
+ ngx_stream_lua_cleanup_pending_operation(coctx);
+ coctx->cleanup = ngx_stream_lua_coctx_cleanup;
+ coctx->data = u;
+
+ dd("setting data to %p, coctx:%p", u, coctx);
+
+ ctx->downstream = u;
+ ctx->resume_handler = ngx_stream_lua_socket_tcp_peek_resume;
+ ctx->peek_needs_more_data = 1;
+ u->read_co_ctx = coctx;
+ u->read_waiting = 1;
+
+ return lua_yield(L, 0);
+}
+
+
+static ngx_int_t
+ngx_stream_lua_socket_tcp_peek_resume(ngx_stream_lua_request_t *r)
+{
+ lua_State *vm;
+ ngx_int_t rc;
+ ngx_uint_t nreqs;
+ ngx_connection_t *c;
+ ngx_stream_lua_ctx_t *ctx;
+ size_t size;
+
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket resuming peek");
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ u = ctx->downstream;
+ c = r->connection;
+ vm = ngx_stream_lua_get_lua_vm(r, ctx);
+ nreqs = c->requests;
+
+ size = c->buffer->last - c->buffer->pos;
+
+ if (size < u->length) {
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua peek does not have enough data, returning NGX_AGAIN");
+
+ return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs);
+ }
+
+ ctx->resume_handler = ngx_stream_lua_wev_handler;
+ /* read handler might have been changed by ngx_stream_core_preread_phase */
+ r->connection->read->handler = ngx_stream_lua_request_handler;
+
+ lua_pushlstring(u->read_co_ctx->co, (char *) c->buffer->pos, u->length);
+
+ u->read_co_ctx->cleanup = NULL;
+ ctx->cur_co_ctx = u->read_co_ctx;
+ u->read_co_ctx = NULL;
+ ctx->peek_needs_more_data = 0;
+ u->read_waiting = 0;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua tcp operation done, resuming lua thread");
+
+ rc = ngx_stream_lua_run_thread(vm, r, ctx, 1);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua run thread returned %d", rc);
+
+ if (rc == NGX_AGAIN) {
+ return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs);
+ }
+
+ if (rc == NGX_DONE) {
+ ngx_stream_lua_finalize_request(r, NGX_DONE);
+ return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs);
+ }
+
+ return rc;
+}
+
+
+static int
+ngx_stream_lua_socket_tcp_receive_helper(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L)
+{
+ ngx_int_t rc;
+ ngx_stream_lua_ctx_t *lctx;
+ ngx_stream_lua_co_ctx_t *coctx;
+
+ u->input_filter_ctx = u;
+
+ lctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+
+ if (u->bufs_in == NULL) {
+ u->bufs_in =
+ ngx_stream_lua_chain_get_free_buf(r->connection->log,
+ r->pool,
+ &lctx->free_recv_bufs,
+ u->conf->buffer_size);
+
+ if (u->bufs_in == NULL) {
+ return luaL_error(L, "no memory");
+ }
+
+ u->buf_in = u->bufs_in;
+ u->buffer = *u->buf_in->buf;
+ }
+
+ dd("tcp receive: buf_in: %p, bufs_in: %p", u->buf_in, u->bufs_in);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket read timeout: %M", u->read_timeout);
+
+ if (u->raw_downstream || u->body_downstream) {
+ r->read_event_handler = ngx_stream_lua_req_socket_rev_handler;
+ }
+
+ u->read_waiting = 0;
+ u->read_co_ctx = NULL;
+
+ rc = ngx_stream_lua_socket_tcp_read(r, u);
+
+ if (rc == NGX_ERROR) {
+ dd("read failed: %d", (int) u->ft_type);
+ rc = ngx_stream_lua_socket_tcp_receive_retval_handler(r, u, L);
+ dd("tcp receive retval returned: %d", (int) rc);
+ return rc;
+ }
+
+ if (rc == NGX_OK) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket receive done in a single run");
+
+ return ngx_stream_lua_socket_tcp_receive_retval_handler(r, u, L);
+ }
+
+ /* rc == NGX_AGAIN */
+
+ u->read_event_handler = ngx_stream_lua_socket_read_handler;
+
+ coctx = lctx->cur_co_ctx;
+
+ ngx_stream_lua_cleanup_pending_operation(coctx);
+ coctx->cleanup = ngx_stream_lua_coctx_cleanup;
+ coctx->data = u;
+
+ if (lctx->entered_content_phase) {
+ r->write_event_handler = ngx_stream_lua_content_wev_handler;
+
+ } else {
+ r->write_event_handler = ngx_stream_lua_core_run_phases;
+ }
+
+ u->read_co_ctx = coctx;
+ u->read_waiting = 1;
+ u->read_prepare_retvals = ngx_stream_lua_socket_tcp_receive_retval_handler;
+
+ dd("setting data to %p, coctx:%p", u, coctx);
+
+ if (u->raw_downstream || u->body_downstream) {
+ lctx->downstream = u;
+ }
+
+ return lua_yield(L, 0);
+}
+
+
+static int
+ngx_stream_lua_socket_tcp_receiveany(lua_State *L)
+{
+ int n;
+ lua_Integer bytes;
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_loc_conf_t *llcf;
+
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+
+ n = lua_gettop(L);
+ if (n != 2) {
+ return luaL_error(L, "expecting 2 arguments "
+ "(including the object), but got %d", n);
+ }
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ lua_rawgeti(L, 1, SOCKET_CTX_INDEX);
+ u = lua_touserdata(L, -1);
+
+ if (u == NULL || u->peer.connection == NULL || u->read_closed) {
+
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+
+ if (llcf->log_socket_errors) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "attempt to receive data on a closed "
+ "socket: u:%p, c:%p, ft:%d eof:%d",
+ u, u ? u->peer.connection : NULL,
+ u ? (int) u->ft_type : 0, u ? (int) u->eof : 0);
+ }
+
+ lua_pushnil(L);
+ lua_pushliteral(L, "closed");
+ return 2;
+ }
+
+ if (u->request != r) {
+ return luaL_error(L, "bad request");
+ }
+
+ ngx_stream_lua_socket_check_busy_connecting(r, u, L);
+ ngx_stream_lua_socket_check_busy_reading(r, u, L);
+
+ if (!lua_isnumber(L, 2)) {
+ return luaL_argerror(L, 2, "bad max argument");
+ }
+
+ bytes = lua_tointeger(L, 2);
+ if (bytes <= 0) {
+ return luaL_argerror(L, 2, "bad max argument");
+ }
+
+ u->input_filter = ngx_stream_lua_socket_read_any;
+ u->rest = (size_t) bytes;
+ u->length = u->rest;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket calling receiveany() "
+ "method to read at most %uz bytes", u->rest);
+
+ return ngx_stream_lua_socket_tcp_receive_helper(r, u, L);
+}
+
+
+static int
+ngx_stream_lua_socket_tcp_receive(lua_State *L)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_loc_conf_t *llcf;
+ int n;
+ ngx_str_t pat;
+ lua_Integer bytes;
+ char *p;
+ int typ;
+
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+
+ n = lua_gettop(L);
+ if (n != 1 && n != 2) {
+ return luaL_error(L, "expecting 1 or 2 arguments "
+ "(including the object), but got %d", n);
+ }
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket calling receive() method");
+
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ lua_rawgeti(L, 1, SOCKET_CTX_INDEX);
+ u = lua_touserdata(L, -1);
+
+ if (u == NULL || u->peer.connection == NULL || u->read_closed) {
+
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+
+ if (llcf->log_socket_errors) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "stream attempt to receive data on a closed "
+ "socket: u:%p, c:%p, ft:%d eof:%d",
+ u, u ? u->peer.connection : NULL,
+ u ? (int) u->ft_type : 0, u ? (int) u->eof : 0);
+ }
+
+ lua_pushnil(L);
+ lua_pushliteral(L, "closed");
+ return 2;
+ }
+
+ if (u->request != r) {
+ return luaL_error(L, "bad request");
+ }
+
+ ngx_stream_lua_socket_check_busy_connecting(r, u, L);
+ ngx_stream_lua_socket_check_busy_reading(r, u, L);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket read timeout: %M", u->read_timeout);
+
+ if (n > 1) {
+ if (lua_isnumber(L, 2)) {
+ typ = LUA_TNUMBER;
+
+ } else {
+ typ = lua_type(L, 2);
+ }
+
+ switch (typ) {
+ case LUA_TSTRING:
+ pat.data = (u_char *) luaL_checklstring(L, 2, &pat.len);
+ if (pat.len != 2 || pat.data[0] != '*') {
+ p = (char *) lua_pushfstring(L, "bad pattern argument: %s",
+ (char *) pat.data);
+
+ return luaL_argerror(L, 2, p);
+ }
+
+ switch (pat.data[1]) {
+ case 'l':
+ u->input_filter = ngx_stream_lua_socket_read_line;
+ break;
+
+ case 'a':
+ u->input_filter = ngx_stream_lua_socket_read_all;
+ break;
+
+ default:
+ return luaL_argerror(L, 2, "bad pattern argument");
+ break;
+ }
+
+ u->length = 0;
+ u->rest = 0;
+
+ break;
+
+ case LUA_TNUMBER:
+ bytes = lua_tointeger(L, 2);
+ if (bytes < 0) {
+ return luaL_argerror(L, 2, "bad pattern argument");
+ }
+
+#if 1
+ if (bytes == 0) {
+ lua_pushliteral(L, "");
+ return 1;
+ }
+#endif
+
+ u->input_filter = ngx_stream_lua_socket_read_chunk;
+ u->length = (size_t) bytes;
+ u->rest = u->length;
+
+ break;
+
+ default:
+ return luaL_argerror(L, 2, "bad pattern argument");
+ break;
+ }
+
+ } else {
+ u->input_filter = ngx_stream_lua_socket_read_line;
+ u->length = 0;
+ u->rest = 0;
+ }
+
+ return ngx_stream_lua_socket_tcp_receive_helper(r, u, L);
+}
+
+
+static ngx_int_t
+ngx_stream_lua_socket_read_chunk(void *data, ssize_t bytes)
+{
+ ngx_int_t rc;
+ ngx_stream_lua_socket_tcp_upstream_t *u = data;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, u->request->connection->log, 0,
+ "stream lua tcp socket read chunk %z", bytes);
+
+ rc = ngx_stream_lua_read_bytes(&u->buffer, u->buf_in, &u->rest,
+ bytes, u->request->connection->log);
+ if (rc == NGX_ERROR) {
+ u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_CLOSED;
+ return NGX_ERROR;
+ }
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_socket_read_all(void *data, ssize_t bytes)
+{
+ ngx_stream_lua_socket_tcp_upstream_t *u = data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, u->request->connection->log, 0,
+ "stream lua tcp socket read all");
+ return ngx_stream_lua_read_all(&u->buffer, u->buf_in, bytes,
+ u->request->connection->log);
+}
+
+
+static ngx_int_t
+ngx_stream_lua_socket_read_line(void *data, ssize_t bytes)
+{
+ ngx_int_t rc;
+ ngx_stream_lua_socket_tcp_upstream_t *u = data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, u->request->connection->log, 0,
+ "stream lua tcp socket read line");
+
+ rc = ngx_stream_lua_read_line(&u->buffer, u->buf_in, bytes,
+ u->request->connection->log);
+ if (rc == NGX_ERROR) {
+ u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_CLOSED;
+ return NGX_ERROR;
+ }
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_socket_read_any(void *data, ssize_t bytes)
+{
+ ngx_int_t rc;
+ ngx_stream_lua_socket_tcp_upstream_t *u = data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, u->request->connection->log, 0,
+ "stream lua tcp socket read any");
+
+ rc = ngx_stream_lua_read_any(&u->buffer, u->buf_in, &u->rest, bytes,
+ u->request->connection->log);
+ if (rc == NGX_ERROR) {
+ u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_CLOSED;
+ return NGX_ERROR;
+ }
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_socket_tcp_read(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u)
+{
+ ngx_int_t rc;
+ ngx_connection_t *c;
+ ngx_buf_t *b;
+ ngx_event_t *rev;
+ size_t size;
+ ssize_t n;
+ unsigned read;
+ off_t preread = 0;
+
+ ngx_stream_lua_loc_conf_t *llcf;
+
+ c = u->peer.connection;
+ rev = c->read;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "stream lua tcp socket read data: wait:%d",
+ (int) u->read_waiting);
+
+ b = &u->buffer;
+ read = 0;
+
+ for ( ;; ) {
+
+ size = b->last - b->pos;
+
+ if (size || u->eof) {
+
+ rc = u->input_filter(u->input_filter_ctx, size);
+
+ if (rc == NGX_OK) {
+
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket receive done: "
+ "wait:%d, eof:%d, ", (int) u->read_waiting,
+ (int) u->eof);
+
+
+#if 1
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_stream_lua_socket_handle_read_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_ERROR);
+ return NGX_ERROR;
+ }
+#endif
+
+
+ u->read_consumed = 1;
+
+ ngx_stream_lua_socket_handle_read_success(r, u);
+ return NGX_OK;
+ }
+
+ if (rc == NGX_ERROR) {
+ dd("input filter error: ft_type:%d wait:%d",
+ (int) u->ft_type, (int) u->read_waiting);
+
+ ngx_stream_lua_socket_handle_read_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_ERROR);
+ return NGX_ERROR;
+ }
+
+ /* rc == NGX_AGAIN */
+
+
+ continue;
+ }
+
+ if (read && !rev->ready) {
+ rc = NGX_AGAIN;
+ break;
+ }
+
+ size = b->end - b->last;
+
+ if (size == 0) {
+ rc = ngx_stream_lua_socket_add_input_buffer(r, u);
+ if (rc == NGX_ERROR) {
+ ngx_stream_lua_socket_handle_read_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_NOMEM);
+
+ return NGX_ERROR;
+ }
+
+ b = &u->buffer;
+ size = (size_t) (b->end - b->last);
+ }
+
+ if (u->raw_downstream) {
+ if (r->connection->buffer != NULL) {
+ preread = ngx_buf_size(r->connection->buffer);
+ }
+
+ if (preread) {
+
+ if ((off_t) size > preread) {
+ size = (size_t) preread;
+ }
+
+ ngx_stream_lua_probe_req_socket_consume_preread(r,
+ r->connection->buffer->pos,
+ size);
+
+ b->last = ngx_copy(b->last, r->connection->buffer->pos, size);
+ r->connection->buffer->pos += size;
+ continue;
+ }
+
+ }
+
+#if 1
+ if (rev->active && !rev->ready) {
+ rc = NGX_AGAIN;
+ break;
+ }
+#endif
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket try to recv data %uz", size);
+
+ n = c->recv(c, b->last, size);
+
+ dd("read event ready: %d", (int) c->read->ready);
+
+ read = 1;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket recv returned %d", (int) n);
+
+ if (n == NGX_AGAIN) {
+ rc = NGX_AGAIN;
+ dd("socket recv busy");
+ break;
+ }
+
+ if (n == 0) {
+
+ if (u->raw_downstream || u->body_downstream) {
+
+ llcf = ngx_stream_lua_get_module_loc_conf(r,
+ ngx_stream_lua_module);
+
+ if (llcf->check_client_abort) {
+
+ ngx_stream_lua_socket_handle_read_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_CLIENTABORT);
+ return NGX_ERROR;
+ }
+
+ /* llcf->check_client_abort == 0 */
+
+ }
+
+ u->eof = 1;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket closed");
+
+ continue;
+ }
+
+ if (n == NGX_ERROR) {
+ u->socket_errno = ngx_socket_errno;
+ ngx_stream_lua_socket_handle_read_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_ERROR);
+ return NGX_ERROR;
+ }
+
+ b->last += n;
+
+ }
+
+#if 1
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_stream_lua_socket_handle_read_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_ERROR);
+ return NGX_ERROR;
+ }
+#endif
+
+ if (rev->active) {
+ ngx_add_timer(rev, u->read_timeout);
+
+ } else if (rev->timer_set) {
+ ngx_del_timer(rev);
+ }
+
+ return rc;
+}
+
+
+static int
+ngx_stream_lua_socket_tcp_send(lua_State *L)
+{
+ ngx_int_t rc;
+ ngx_stream_lua_request_t *r;
+ u_char *p;
+ size_t len;
+ ngx_chain_t *cl;
+ int type;
+ int tcp_nodelay;
+ const char *msg;
+ ngx_buf_t *b;
+ ngx_connection_t *c;
+
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+ ngx_stream_lua_loc_conf_t *llcf;
+
+ ngx_stream_core_srv_conf_t *clcf;
+
+ ngx_stream_lua_co_ctx_t *coctx;
+
+ /* TODO: add support for the optional "i" and "j" arguments */
+
+ if (lua_gettop(L) != 2) {
+ return luaL_error(L, "expecting 2 arguments (including the object), "
+ "but got %d", lua_gettop(L));
+ }
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ lua_rawgeti(L, 1, SOCKET_CTX_INDEX);
+ u = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ dd("tcp send: u=%p, u->write_closed=%d", u, (unsigned) u->write_closed);
+
+ if (u == NULL || u->peer.connection == NULL || u->write_closed) {
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+
+ if (llcf->log_socket_errors) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "attempt to send data on a closed socket: u:%p, "
+ "c:%p, ft:%d eof:%d",
+ u, u ? u->peer.connection : NULL,
+ u ? (int) u->ft_type : 0, u ? (int) u->eof : 0);
+ }
+
+ lua_pushnil(L);
+ lua_pushliteral(L, "closed");
+ return 2;
+ }
+
+ if (u->request != r) {
+ return luaL_error(L, "bad request");
+ }
+
+ ngx_stream_lua_socket_check_busy_connecting(r, u, L);
+ ngx_stream_lua_socket_check_busy_writing(r, u, L);
+
+ if (u->body_downstream) {
+ return luaL_error(L, "attempt to write to request sockets");
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket send timeout: %M", u->send_timeout);
+
+ type = lua_type(L, 2);
+ switch (type) {
+ case LUA_TNUMBER:
+ case LUA_TSTRING:
+ lua_tolstring(L, 2, &len);
+ break;
+
+ case LUA_TTABLE:
+ len = ngx_stream_lua_calc_strlen_in_table(L, 2, 2, 1 /* strict */);
+ break;
+
+ case LUA_TNIL:
+ len = sizeof("nil") - 1;
+ break;
+
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 2)) {
+ len = sizeof("true") - 1;
+
+ } else {
+ len = sizeof("false") - 1;
+ }
+
+ break;
+
+ default:
+ msg = lua_pushfstring(L, "string, number, boolean, nil, "
+ "or array table expected, got %s",
+ lua_typename(L, type));
+
+ return luaL_argerror(L, 2, msg);
+ }
+
+ if (len == 0) {
+ lua_pushinteger(L, 0);
+ return 1;
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+
+ cl = ngx_stream_lua_chain_get_free_buf(r->connection->log, r->pool,
+ &ctx->free_bufs, len);
+
+ if (cl == NULL) {
+ return luaL_error(L, "no memory");
+ }
+
+ b = cl->buf;
+
+ switch (type) {
+ case LUA_TNUMBER:
+ case LUA_TSTRING:
+ p = (u_char *) lua_tolstring(L, -1, &len);
+ b->last = ngx_copy(b->last, (u_char *) p, len);
+ break;
+
+ case LUA_TTABLE:
+ b->last = ngx_stream_lua_copy_str_in_table(L, -1, b->last);
+ break;
+
+ case LUA_TNIL:
+ *b->last++ = 'n';
+ *b->last++ = 'i';
+ *b->last++ = 'l';
+ break;
+
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 2)) {
+ *b->last++ = 't';
+ *b->last++ = 'r';
+ *b->last++ = 'u';
+ *b->last++ = 'e';
+
+ } else {
+ *b->last++ = 'f';
+ *b->last++ = 'a';
+ *b->last++ = 'l';
+ *b->last++ = 's';
+ *b->last++ = 'e';
+ }
+
+ break;
+
+ default:
+ return luaL_error(L, "impossible to reach here");
+ }
+
+ u->request_bufs = cl;
+
+ u->request_len = len;
+
+ /* mimic ngx_http_upstream_init_request here */
+
+ clcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_core_module);
+ c = u->peer.connection;
+
+ if (clcf->tcp_nodelay && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "stream lua socket tcp_nodelay");
+
+ tcp_nodelay = 1;
+
+ if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,
+ (const void *) &tcp_nodelay, sizeof(int))
+ == -1)
+ {
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+ if (llcf->log_socket_errors) {
+ ngx_connection_error(c, ngx_socket_errno,
+ "setsockopt(TCP_NODELAY) "
+ "failed");
+ }
+
+ lua_pushnil(L);
+ lua_pushliteral(L, "setsocketopt tcp_nodelay failed");
+ return 2;
+ }
+
+ c->tcp_nodelay = NGX_TCP_NODELAY_SET;
+ }
+
+#if 1
+ u->write_waiting = 0;
+ u->write_co_ctx = NULL;
+#endif
+
+ ngx_stream_lua_probe_socket_tcp_send_start(r, u, b->pos, len);
+
+ rc = ngx_stream_lua_socket_send(r, u);
+
+ dd("socket send returned %d", (int) rc);
+
+ if (rc == NGX_ERROR) {
+ return ngx_stream_lua_socket_write_error_retval_handler(r, u, L);
+ }
+
+ if (rc == NGX_OK) {
+ lua_pushinteger(L, len);
+ return 1;
+ }
+
+ /* rc == NGX_AGAIN */
+
+ coctx = ctx->cur_co_ctx;
+
+ ngx_stream_lua_cleanup_pending_operation(coctx);
+ coctx->cleanup = ngx_stream_lua_coctx_cleanup;
+ coctx->data = u;
+
+ if (u->raw_downstream) {
+ ctx->writing_raw_req_socket = 1;
+ }
+
+ if (ctx->entered_content_phase) {
+ r->write_event_handler = ngx_stream_lua_content_wev_handler;
+
+ } else {
+ r->write_event_handler = ngx_stream_lua_core_run_phases;
+ }
+
+ u->write_co_ctx = coctx;
+ u->write_waiting = 1;
+ u->write_prepare_retvals = ngx_stream_lua_socket_tcp_send_retval_handler;
+
+ dd("setting data to %p", u);
+
+ return lua_yield(L, 0);
+}
+
+
+static int
+ngx_stream_lua_socket_tcp_send_retval_handler(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket send return value handler");
+
+ if (u->ft_type) {
+ return ngx_stream_lua_socket_write_error_retval_handler(r, u, L);
+ }
+
+ lua_pushinteger(L, u->request_len);
+ return 1;
+}
+
+
+static int
+ngx_stream_lua_socket_tcp_receive_retval_handler(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L)
+{
+ int n;
+ ngx_int_t rc;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_event_t *ev;
+
+ ngx_stream_lua_loc_conf_t *llcf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket receive return value handler");
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+
+#if 1
+ if (u->raw_downstream || u->body_downstream) {
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+
+ if (llcf->check_client_abort) {
+
+ r->read_event_handler = ngx_stream_lua_rd_check_broken_connection;
+
+ ev = r->connection->read;
+
+ dd("rev active: %d", ev->active);
+
+ if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && !ev->active) {
+ if (ngx_add_event(ev, NGX_READ_EVENT, 0) != NGX_OK) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "failed to add event");
+ return 2;
+ }
+ }
+
+ } else {
+ /* llcf->check_client_abort == 0 */
+ r->read_event_handler = ngx_stream_lua_block_reading;
+ }
+ }
+#endif
+
+ if (u->ft_type) {
+
+ if (u->ft_type & NGX_STREAM_LUA_SOCKET_FT_TIMEOUT) {
+ u->no_close = 1;
+ }
+
+ dd("u->bufs_in: %p", u->bufs_in);
+
+ if (u->bufs_in) {
+ rc = ngx_stream_lua_socket_push_input_data(r, ctx, u, L);
+ if (rc == NGX_ERROR) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "no memory");
+ return 2;
+ }
+
+ (void) ngx_stream_lua_socket_read_error_retval_handler(r, u, L);
+
+ lua_pushvalue(L, -3);
+ lua_remove(L, -4);
+ return 3;
+ }
+
+ n = ngx_stream_lua_socket_read_error_retval_handler(r, u, L);
+ lua_pushliteral(L, "");
+ return n + 1;
+ }
+
+ rc = ngx_stream_lua_socket_push_input_data(r, ctx, u, L);
+ if (rc == NGX_ERROR) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "no memory");
+ return 2;
+ }
+
+ return 1;
+}
+
+
+static int
+ngx_stream_lua_socket_tcp_close(lua_State *L)
+{
+ ngx_stream_lua_request_t *r;
+
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+
+ if (lua_gettop(L) != 1) {
+ return luaL_error(L, "expecting 1 argument "
+ "(including the object) but seen %d", lua_gettop(L));
+ }
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ lua_rawgeti(L, 1, SOCKET_CTX_INDEX);
+ u = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ if (u == NULL
+ || u->peer.connection == NULL
+ || (u->read_closed && u->write_closed))
+ {
+ lua_pushnil(L);
+ lua_pushliteral(L, "closed");
+ return 2;
+ }
+
+ if (u->request != r) {
+ return luaL_error(L, "bad request");
+ }
+
+ ngx_stream_lua_socket_check_busy_connecting(r, u, L);
+ ngx_stream_lua_socket_check_busy_reading(r, u, L);
+ ngx_stream_lua_socket_check_busy_writing(r, u, L);
+
+ if (u->raw_downstream || u->body_downstream) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "attempt to close a request socket");
+ return 2;
+ }
+
+ ngx_stream_lua_socket_tcp_finalize(r, u);
+
+ lua_pushinteger(L, 1);
+ return 1;
+}
+
+
+static int
+ngx_stream_lua_socket_tcp_shutdown(lua_State *L)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+ ngx_str_t direction;
+ char *p;
+ ngx_stream_lua_ctx_t *ctx;
+
+ if (lua_gettop(L) != 2) {
+ return luaL_error(L, "expecting 2 arguments "
+ "(including the object) but seen %d", lua_gettop(L));
+ }
+
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ lua_rawgeti(L, 1, SOCKET_CTX_INDEX);
+ u = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ if (u == NULL
+ || u->peer.connection == NULL
+ || (u->read_closed && u->write_closed))
+ {
+ lua_pushnil(L);
+ lua_pushliteral(L, "closed");
+ return 2;
+ }
+
+ if (u->write_closed) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "already shutdown");
+ return 2;
+ }
+
+ if (u->request != r) {
+ return luaL_error(L, "bad request");
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ ngx_stream_lua_socket_handle_write_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_ERROR);
+ return NGX_ERROR;
+ }
+
+ /*
+ * only allow shutdown on raw request in stream module in content phase.
+ * in http module, lingering close will take care of the shutdown.
+ * in stream module, it is unsafe to shutdown prior on reaching content
+ * phase as later phases may still need to write to the socket.
+ */
+
+ if (u->raw_downstream) {
+ ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT);
+
+ if (ctx->eof) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "seen eof");
+ return 2;
+ }
+
+ /* prevent all further output attempt */
+ ctx->eof = 1;
+ }
+
+ ngx_stream_lua_socket_check_busy_connecting(r, u, L);
+ ngx_stream_lua_socket_check_busy_writing(r, u, L);
+
+ /* shutdown */
+ direction.data = (u_char *) luaL_checklstring(L, 2, &direction.len);
+ if (direction.len == 0) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "pattern is empty");
+ return 2;
+ }
+
+ if (direction.len != 4 || ngx_strcmp(direction.data, "send") != 0) {
+ p = (char *) lua_pushfstring(L, "bad shutdown argument: %s",
+ (char *) direction.data);
+
+ return luaL_argerror(L, 2, p);
+ }
+
+ ngx_stream_lua_socket_tcp_finalize_write_part(r, u, 1);
+
+ lua_pushinteger(L, 1);
+ return 1;
+}
+
+
+static int
+ngx_stream_lua_socket_tcp_setoption(lua_State *L)
+{
+ /* TODO */
+ return 0;
+}
+
+
+static int
+ngx_stream_lua_socket_tcp_settimeout(lua_State *L)
+{
+ int n;
+ ngx_int_t timeout;
+
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+
+ n = lua_gettop(L);
+
+ if (n != 2) {
+ return luaL_error(L, "ngx.socket settimout: expecting 2 arguments "
+ "(including the object) but seen %d", lua_gettop(L));
+ }
+
+ timeout = (ngx_int_t) lua_tonumber(L, 2);
+ if (timeout >> 31) {
+ return luaL_error(L, "bad timeout value");
+ }
+
+ lua_pushinteger(L, timeout);
+ lua_pushinteger(L, timeout);
+
+ lua_rawseti(L, 1, SOCKET_CONNECT_TIMEOUT_INDEX);
+ lua_rawseti(L, 1, SOCKET_SEND_TIMEOUT_INDEX);
+ lua_rawseti(L, 1, SOCKET_READ_TIMEOUT_INDEX);
+
+ lua_rawgeti(L, 1, SOCKET_CTX_INDEX);
+ u = lua_touserdata(L, -1);
+
+ if (u) {
+ if (timeout > 0) {
+ u->read_timeout = (ngx_msec_t) timeout;
+ u->send_timeout = (ngx_msec_t) timeout;
+ u->connect_timeout = (ngx_msec_t) timeout;
+
+ } else {
+ u->read_timeout = u->conf->read_timeout;
+ u->send_timeout = u->conf->send_timeout;
+ u->connect_timeout = u->conf->connect_timeout;
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+ngx_stream_lua_socket_tcp_settimeouts(lua_State *L)
+{
+ int n;
+ ngx_int_t connect_timeout, send_timeout, read_timeout;
+
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+
+ n = lua_gettop(L);
+
+ if (n != 4) {
+ return luaL_error(L, "ngx.socket settimout: expecting 4 arguments "
+ "(including the object) but seen %d", lua_gettop(L));
+ }
+
+ connect_timeout = (ngx_int_t) lua_tonumber(L, 2);
+ if (connect_timeout >> 31) {
+ return luaL_error(L, "bad timeout value");
+ }
+
+ send_timeout = (ngx_int_t) lua_tonumber(L, 3);
+ if (send_timeout >> 31) {
+ return luaL_error(L, "bad timeout value");
+ }
+
+ read_timeout = (ngx_int_t) lua_tonumber(L, 4);
+ if (read_timeout >> 31) {
+ return luaL_error(L, "bad timeout value");
+ }
+
+ lua_rawseti(L, 1, SOCKET_READ_TIMEOUT_INDEX);
+ lua_rawseti(L, 1, SOCKET_SEND_TIMEOUT_INDEX);
+ lua_rawseti(L, 1, SOCKET_CONNECT_TIMEOUT_INDEX);
+
+ lua_rawgeti(L, 1, SOCKET_CTX_INDEX);
+ u = lua_touserdata(L, -1);
+
+ if (u) {
+ if (connect_timeout > 0) {
+ u->connect_timeout = (ngx_msec_t) connect_timeout;
+
+ } else {
+ u->connect_timeout = u->conf->connect_timeout;
+ }
+
+ if (send_timeout > 0) {
+ u->send_timeout = (ngx_msec_t) send_timeout;
+
+ } else {
+ u->send_timeout = u->conf->send_timeout;
+ }
+
+ if (read_timeout > 0) {
+ u->read_timeout = (ngx_msec_t) read_timeout;
+
+ } else {
+ u->read_timeout = u->conf->read_timeout;
+ }
+ }
+
+ return 0;
+}
+
+
+static void
+ngx_stream_lua_socket_tcp_handler(ngx_event_t *ev)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+ ngx_connection_t *c;
+
+ c = ev->data;
+ u = c->data;
+ r = u->request;
+ c = r->connection;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "stream lua tcp socket handler: wev %d", (int) ev->write);
+
+ if (ev->write) {
+ u->write_event_handler(r, u);
+
+ } else {
+ u->read_event_handler(r, u);
+ }
+
+}
+
+
+static ngx_int_t
+ngx_stream_lua_socket_tcp_get_peer(ngx_peer_connection_t *pc, void *data)
+{
+ /* empty */
+ return NGX_OK;
+}
+
+
+static void
+ngx_stream_lua_socket_read_handler(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u)
+{
+ ngx_connection_t *c;
+ ngx_stream_lua_loc_conf_t *llcf;
+
+ c = u->peer.connection;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket read handler");
+
+ if (c->read->timedout) {
+ c->read->timedout = 0;
+
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+
+ if (llcf->log_socket_errors) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "stream lua tcp socket read timed out");
+ }
+
+ ngx_stream_lua_socket_handle_read_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_TIMEOUT);
+ return;
+ }
+
+#if 1
+ if (c->read->timer_set) {
+ ngx_del_timer(c->read);
+ }
+#endif
+
+ if (u->buffer.start != NULL) {
+ (void) ngx_stream_lua_socket_tcp_read(r, u);
+ }
+}
+
+
+static void
+ngx_stream_lua_socket_send_handler(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u)
+{
+ ngx_connection_t *c;
+ ngx_stream_lua_loc_conf_t *llcf;
+
+ c = u->peer.connection;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket send handler");
+
+ if (c->write->timedout) {
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+
+ if (llcf->log_socket_errors) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "stream lua tcp socket write timed out");
+ }
+
+ ngx_stream_lua_socket_handle_write_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_TIMEOUT);
+ return;
+ }
+
+ if (u->request_bufs) {
+ (void) ngx_stream_lua_socket_send(r, u);
+ }
+}
+
+
+static ngx_int_t
+ngx_stream_lua_socket_send(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u)
+{
+ ngx_int_t n;
+ ngx_connection_t *c;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_buf_t *b;
+
+ c = u->peer.connection;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket send data");
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ ngx_stream_lua_socket_handle_write_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_ERROR);
+ return NGX_ERROR;
+ }
+
+ b = u->request_bufs->buf;
+
+ for (;;) {
+ n = c->send(c, b->pos, b->last - b->pos);
+
+ if (n >= 0) {
+ b->pos += n;
+
+ if (b->pos == b->last) {
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "stream lua tcp socket sent all the data");
+
+ if (c->write->timer_set) {
+ ngx_del_timer(c->write);
+ }
+
+
+ ngx_chain_update_chains(r->pool,
+ &ctx->free_bufs, &ctx->busy_bufs,
+ &u->request_bufs,
+ (ngx_buf_tag_t) &ngx_stream_lua_module);
+
+ u->write_event_handler = ngx_stream_lua_socket_dummy_handler;
+
+ if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+ ngx_stream_lua_socket_handle_write_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_ERROR);
+ return NGX_ERROR;
+ }
+
+ ngx_stream_lua_socket_handle_write_success(r, u);
+ return NGX_OK;
+ }
+
+ /* keep sending more data */
+ continue;
+ }
+
+ /* NGX_ERROR || NGX_AGAIN */
+ break;
+ }
+
+ if (n == NGX_ERROR) {
+ c->error = 1;
+ u->socket_errno = ngx_socket_errno;
+ ngx_stream_lua_socket_handle_write_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_ERROR);
+ return NGX_ERROR;
+ }
+
+ /* n == NGX_AGAIN */
+
+ if (u->raw_downstream) {
+ ctx->writing_raw_req_socket = 1;
+ }
+
+ u->write_event_handler = ngx_stream_lua_socket_send_handler;
+
+ ngx_add_timer(c->write, u->send_timeout);
+
+ if (ngx_handle_write_event(c->write, u->conf->send_lowat) != NGX_OK) {
+ ngx_stream_lua_socket_handle_write_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_ERROR);
+ return NGX_ERROR;
+ }
+
+ return NGX_AGAIN;
+}
+
+
+static void
+ngx_stream_lua_socket_handle_conn_success(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u)
+{
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx;
+
+#if 1
+ u->read_event_handler = ngx_stream_lua_socket_dummy_handler;
+ u->write_event_handler = ngx_stream_lua_socket_dummy_handler;
+#endif
+
+ if (u->conn_waiting) {
+ u->conn_waiting = 0;
+
+ coctx = u->write_co_ctx;
+ coctx->cleanup = NULL;
+ u->write_co_ctx = NULL;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return;
+ }
+
+ ctx->resume_handler = ngx_stream_lua_socket_tcp_conn_resume;
+ ctx->cur_co_ctx = coctx;
+
+ ngx_stream_lua_assert(coctx && (!ngx_stream_lua_is_thread(ctx)
+ || coctx->co_ref >= 0));
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket waking up the current "
+ "request (conn)");
+
+ r->write_event_handler(r);
+ }
+}
+
+
+static void
+ngx_stream_lua_socket_handle_read_success(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u)
+{
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx;
+
+#if 1
+ u->read_event_handler = ngx_stream_lua_socket_dummy_handler;
+#endif
+
+ if (u->read_waiting) {
+ u->read_waiting = 0;
+
+ coctx = u->read_co_ctx;
+ coctx->cleanup = NULL;
+ u->read_co_ctx = NULL;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return;
+ }
+
+ ctx->resume_handler = ngx_stream_lua_socket_tcp_read_resume;
+ ctx->cur_co_ctx = coctx;
+
+ ngx_stream_lua_assert(coctx && (!ngx_stream_lua_is_thread(ctx)
+ || coctx->co_ref >= 0));
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket waking up the current "
+ "request (read)");
+
+ r->write_event_handler(r);
+ }
+}
+
+
+static void
+ngx_stream_lua_socket_handle_write_success(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u)
+{
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx;
+
+#if 1
+ u->write_event_handler = ngx_stream_lua_socket_dummy_handler;
+#endif
+
+ if (u->write_waiting) {
+ u->write_waiting = 0;
+
+ coctx = u->write_co_ctx;
+ coctx->cleanup = NULL;
+ u->write_co_ctx = NULL;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return;
+ }
+
+ ctx->resume_handler = ngx_stream_lua_socket_tcp_write_resume;
+ ctx->cur_co_ctx = coctx;
+
+ ngx_stream_lua_assert(coctx && (!ngx_stream_lua_is_thread(ctx)
+ || coctx->co_ref >= 0));
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket waking up the current "
+ "request (read)");
+
+ r->write_event_handler(r);
+ }
+}
+
+
+static void
+ngx_stream_lua_socket_handle_conn_error(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type)
+{
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket handle connect error");
+
+ u->ft_type |= ft_type;
+
+#if 1
+ ngx_stream_lua_socket_tcp_finalize(r, u);
+#endif
+
+ u->read_event_handler = ngx_stream_lua_socket_dummy_handler;
+ u->write_event_handler = ngx_stream_lua_socket_dummy_handler;
+
+ dd("connection waiting: %d", (int) u->conn_waiting);
+
+ coctx = u->write_co_ctx;
+
+ if (u->conn_waiting) {
+ u->conn_waiting = 0;
+
+ coctx->cleanup = NULL;
+ u->write_co_ctx = NULL;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+
+ ctx->resume_handler = ngx_stream_lua_socket_tcp_conn_resume;
+ ctx->cur_co_ctx = coctx;
+
+ ngx_stream_lua_assert(coctx && (!ngx_stream_lua_is_thread(ctx)
+ || coctx->co_ref >= 0));
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket waking up the current request");
+
+ r->write_event_handler(r);
+ }
+}
+
+
+static void
+ngx_stream_lua_socket_handle_read_error(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type)
+{
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket handle read error");
+
+ u->ft_type |= ft_type;
+
+#if 0
+ ngx_stream_lua_socket_tcp_finalize(r, u);
+#endif
+
+ u->read_event_handler = ngx_stream_lua_socket_dummy_handler;
+
+ if (u->read_waiting) {
+ u->read_waiting = 0;
+
+ coctx = u->read_co_ctx;
+ coctx->cleanup = NULL;
+ u->read_co_ctx = NULL;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+
+ ctx->resume_handler = ngx_stream_lua_socket_tcp_read_resume;
+ ctx->cur_co_ctx = coctx;
+
+ ngx_stream_lua_assert(coctx && (!ngx_stream_lua_is_thread(ctx)
+ || coctx->co_ref >= 0));
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket waking up the current request");
+
+ r->write_event_handler(r);
+ }
+}
+
+
+static void
+ngx_stream_lua_socket_handle_write_error(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type)
+{
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket handle write error");
+
+ u->ft_type |= ft_type;
+
+#if 0
+ ngx_stream_lua_socket_tcp_finalize(r, u);
+#endif
+
+ u->write_event_handler = ngx_stream_lua_socket_dummy_handler;
+
+ if (u->write_waiting) {
+ u->write_waiting = 0;
+
+ coctx = u->write_co_ctx;
+ coctx->cleanup = NULL;
+ u->write_co_ctx = NULL;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+
+ ctx->resume_handler = ngx_stream_lua_socket_tcp_write_resume;
+ ctx->cur_co_ctx = coctx;
+
+ ngx_stream_lua_assert(coctx && (!ngx_stream_lua_is_thread(ctx)
+ || coctx->co_ref >= 0));
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket waking up the current request");
+
+ r->write_event_handler(r);
+ }
+}
+
+
+static void
+ngx_stream_lua_socket_connected_handler(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u)
+{
+ ngx_int_t rc;
+ ngx_connection_t *c;
+
+ ngx_stream_lua_loc_conf_t *llcf;
+
+ c = u->peer.connection;
+
+ if (c->write->timedout) {
+
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+
+ if (llcf->log_socket_errors) {
+ ngx_stream_lua_socket_init_peer_connection_addr_text(&u->peer);
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "stream lua tcp socket connect timed out,"
+ " when connecting to %V:%ud",
+ &c->addr_text, ngx_inet_get_port(u->peer.sockaddr));
+ }
+
+ ngx_stream_lua_socket_handle_conn_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_TIMEOUT);
+ return;
+ }
+
+ if (c->write->timer_set) {
+ ngx_del_timer(c->write);
+ }
+
+ rc = ngx_stream_lua_socket_test_connect(r, c);
+ if (rc != NGX_OK) {
+ if (rc > 0) {
+ u->socket_errno = (ngx_err_t) rc;
+ }
+
+ ngx_stream_lua_socket_handle_conn_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_ERROR);
+ return;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket connected");
+
+ /* We should delete the current write/read event
+ * here because the socket object may not be used immediately
+ * on the Lua land, thus causing hot spin around level triggered
+ * event poll and wasting CPU cycles. */
+
+ if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+ ngx_stream_lua_socket_handle_conn_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_ERROR);
+ return;
+ }
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ ngx_stream_lua_socket_handle_conn_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_ERROR);
+ return;
+ }
+
+ ngx_stream_lua_socket_handle_conn_success(r, u);
+}
+
+
+static void
+ngx_stream_lua_socket_tcp_cleanup(void *data)
+{
+ ngx_stream_lua_socket_tcp_upstream_t *u = data;
+
+ ngx_stream_lua_request_t *r;
+
+ r = u->request;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "cleanup lua tcp socket request");
+
+ ngx_stream_lua_socket_tcp_finalize(r, u);
+}
+
+
+static void
+ngx_stream_lua_socket_tcp_finalize_read_part(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u)
+{
+ ngx_chain_t *cl;
+ ngx_chain_t **ll;
+ ngx_connection_t *c;
+ ngx_stream_lua_ctx_t *ctx;
+
+ if (u->read_closed) {
+ return;
+ }
+
+ u->read_closed = 1;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+
+ if (ctx && u->bufs_in) {
+
+ ll = &u->bufs_in;
+ for (cl = u->bufs_in; cl; cl = cl->next) {
+ dd("bufs_in chain: %p, next %p", cl, cl->next);
+ cl->buf->pos = cl->buf->last;
+ ll = &cl->next;
+ }
+
+ dd("ctx: %p", ctx);
+ dd("free recv bufs: %p", ctx->free_recv_bufs);
+ *ll = ctx->free_recv_bufs;
+ ctx->free_recv_bufs = u->bufs_in;
+ u->bufs_in = NULL;
+ u->buf_in = NULL;
+ ngx_memzero(&u->buffer, sizeof(ngx_buf_t));
+ }
+
+ if (u->raw_downstream || u->body_downstream) {
+ if (r->connection->read->timer_set) {
+ ngx_del_timer(r->connection->read);
+ }
+ return;
+ }
+
+ c = u->peer.connection;
+
+ if (c) {
+ if (c->read->timer_set) {
+ ngx_del_timer(c->read);
+ }
+
+ if (c->read->active || c->read->disabled) {
+ ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT);
+ }
+
+#if defined(nginx_version) && nginx_version >= 1007005
+ if (c->read->posted) {
+#else
+ if (c->read->prev) {
+#endif
+ ngx_delete_posted_event(c->read);
+ }
+
+ c->read->closed = 1;
+
+ /* TODO: shutdown the reading part of the connection */
+ }
+}
+
+
+static void
+ngx_stream_lua_socket_tcp_finalize_write_part(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u, int do_shutdown)
+
+{
+ ngx_connection_t *c;
+ ngx_stream_lua_ctx_t *ctx;
+
+ c = u->peer.connection;
+
+ if (u->write_closed) {
+ return;
+ }
+
+ u->write_closed = 1;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+
+ if (c && do_shutdown) {
+ if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) {
+ ngx_connection_error(c, ngx_socket_errno,
+ ngx_shutdown_socket_n " failed");
+ return;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua shutdown socket write direction");
+ }
+
+ if (u->raw_downstream || u->body_downstream) {
+ if (ctx && ctx->writing_raw_req_socket) {
+ ctx->writing_raw_req_socket = 0;
+ if (r->connection->write->timer_set) {
+ ngx_del_timer(r->connection->write);
+ }
+
+ r->connection->write->error = 1;
+ }
+ return;
+ }
+
+ if (c) {
+ if (c->write->timer_set) {
+ ngx_del_timer(c->write);
+ }
+
+ if (c->write->active || c->write->disabled) {
+ ngx_del_event(c->write, NGX_WRITE_EVENT, NGX_CLOSE_EVENT);
+ }
+
+#if defined(nginx_version) && nginx_version >= 1007005
+ if (c->write->posted) {
+#else
+ if (c->write->prev) {
+#endif
+ ngx_delete_posted_event(c->write);
+ }
+
+ c->write->closed = 1;
+ }
+}
+
+
+static void
+ngx_stream_lua_socket_tcp_conn_op_timeout_handler(ngx_event_t *ev)
+{
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_co_ctx_t *coctx;
+ ngx_stream_lua_loc_conf_t *llcf;
+ ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx;
+
+ conn_op_ctx = ev->data;
+ ngx_queue_remove(&conn_op_ctx->queue);
+
+ u = conn_op_ctx->u;
+ r = u->request;
+
+ coctx = u->write_co_ctx;
+ coctx->cleanup = NULL;
+ /* note that we store conn_op_ctx in coctx->data instead of u */
+ coctx->data = conn_op_ctx;
+ u->write_co_ctx = NULL;
+
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+
+ if (llcf->log_socket_errors) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "stream lua tcp socket queued connect "
+ "timed out, when trying to connect to %V:%ud",
+ &conn_op_ctx->host, conn_op_ctx->port);
+ }
+
+ ngx_queue_insert_head(&u->socket_pool->cache_connect_op,
+ &conn_op_ctx->queue);
+ u->socket_pool->connections--;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return;
+ }
+
+ ctx->cur_co_ctx = coctx;
+
+ ngx_stream_lua_assert(coctx && (!ngx_stream_lua_is_thread(ctx)
+ || coctx->co_ref >= 0));
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket waking up the current "
+ "request");
+
+ u->write_prepare_retvals =
+ ngx_stream_lua_socket_tcp_conn_op_timeout_retval_handler;
+
+ if (ctx->entered_content_phase) {
+ (void) ngx_stream_lua_socket_tcp_conn_op_resume(r);
+
+ } else {
+ ctx->resume_handler = ngx_stream_lua_socket_tcp_conn_op_resume;
+ ngx_stream_lua_core_run_phases(r);
+ }
+
+}
+
+
+static int
+ngx_stream_lua_socket_tcp_conn_op_timeout_retval_handler(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u,
+ lua_State *L)
+{
+ lua_pushnil(L);
+ lua_pushliteral(L, "timeout");
+ return 2;
+}
+
+
+static void
+ngx_stream_lua_socket_tcp_resume_conn_op(
+ ngx_stream_lua_socket_pool_t *spool)
+{
+ ngx_queue_t *q;
+ ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx;
+
+#if (NGX_DEBUG)
+ ngx_stream_lua_assert(spool->connections >= 0);
+
+#else
+ if (spool->connections < 0) {
+ ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
+ "stream lua tcp socket connections count "
+ "mismatched for connection pool \"%s\", connections: "
+ "%i, size: %i",
+ spool->key, spool->connections, spool->size);
+ spool->connections = 0;
+ }
+#endif
+
+ /* we manually destroy wait_connect_op before triggering connect
+ * operation resumption, so that there is no resumption happens when Nginx
+ * is exiting.
+ */
+ if (ngx_queue_empty(&spool->wait_connect_op)) {
+ return;
+ }
+
+ q = ngx_queue_head(&spool->wait_connect_op);
+ conn_op_ctx = ngx_queue_data(q,
+ ngx_stream_lua_socket_tcp_conn_op_ctx_t,
+ queue);
+ ngx_log_debug4(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "stream lua tcp socket post connect operation "
+ "resumption u: %p, ctx: %p for connection pool \"%s\", "
+ "connections: %i",
+ conn_op_ctx->u, conn_op_ctx, spool->key, spool->connections);
+
+ if (conn_op_ctx->event.timer_set) {
+ ngx_del_timer(&conn_op_ctx->event);
+ }
+
+ conn_op_ctx->event.handler =
+ ngx_stream_lua_socket_tcp_conn_op_resume_handler;
+
+ ngx_post_event((&conn_op_ctx->event), &ngx_posted_events);
+}
+
+
+static void
+ngx_stream_lua_socket_tcp_conn_op_ctx_cleanup(void *data)
+{
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+ ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx = data;
+
+ u = conn_op_ctx->u;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, u->request->connection->log, 0,
+ "stream cleanup lua tcp socket "
+ "conn_op_ctx: %p, u: %p",
+ conn_op_ctx, u);
+
+ ngx_queue_insert_head(&u->socket_pool->cache_connect_op,
+ &conn_op_ctx->queue);
+}
+
+
+static void
+ngx_stream_lua_socket_tcp_conn_op_resume_handler(ngx_event_t *ev)
+{
+ ngx_queue_t *q;
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_cleanup_t *cln;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx;
+ ngx_stream_lua_socket_pool_t *spool;
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+ ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx;
+
+ conn_op_ctx = ev->data;
+ u = conn_op_ctx->u;
+ r = u->request;
+ spool = u->socket_pool;
+
+ if (ngx_queue_empty(&spool->wait_connect_op)) {
+#if (NGX_DEBUG)
+ ngx_stream_lua_assert(!(spool->backlog >= 0
+ && spool->connections > spool->size));
+
+#else
+ if (spool->backlog >= 0 && spool->connections > spool->size) {
+ ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
+ "stream lua tcp socket connections count "
+ "mismatched for connection pool \"%s\", connections: "
+ "%i, size: %i",
+ spool->key, spool->connections, spool->size);
+ spool->connections = spool->size;
+ }
+#endif
+
+ return;
+ }
+
+ q = ngx_queue_head(&spool->wait_connect_op);
+ ngx_queue_remove(q);
+
+ coctx = u->write_co_ctx;
+ coctx->cleanup = NULL;
+ /* note that we store conn_op_ctx in coctx->data instead of u */
+ coctx->data = conn_op_ctx;
+ /* clear ngx_stream_lua_tcp_queue_conn_op_cleanup */
+ u->write_co_ctx = NULL;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ ngx_queue_insert_head(&spool->cache_connect_op,
+ &conn_op_ctx->queue);
+ return;
+ }
+
+ ctx->cur_co_ctx = coctx;
+
+ ngx_stream_lua_assert(coctx && (!ngx_stream_lua_is_thread(ctx)
+ || coctx->co_ref >= 0));
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket waking up the current "
+ "request");
+
+ u->write_prepare_retvals =
+ ngx_stream_lua_socket_tcp_conn_op_resume_retval_handler;
+
+ if (ctx->entered_content_phase) {
+ (void) ngx_stream_lua_socket_tcp_conn_op_resume(r);
+
+ } else {
+ cln = ngx_stream_lua_cleanup_add(r, 0);
+ if (cln != NULL) {
+ cln->handler = ngx_stream_lua_socket_tcp_conn_op_ctx_cleanup;
+ cln->data = conn_op_ctx;
+ conn_op_ctx->cleanup = &cln->handler;
+ }
+
+ ctx->resume_handler = ngx_stream_lua_socket_tcp_conn_op_resume;
+ ngx_stream_lua_core_run_phases(r);
+ }
+
+}
+
+
+static int
+ngx_stream_lua_socket_tcp_conn_op_resume_retval_handler(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L)
+{
+ int nret;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx;
+ ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ coctx = ctx->cur_co_ctx;
+ dd("coctx: %p", coctx);
+ conn_op_ctx = coctx->data;
+ if (conn_op_ctx->cleanup != NULL) {
+ *conn_op_ctx->cleanup = NULL;
+ ngx_stream_lua_cleanup_free(r, conn_op_ctx->cleanup);
+ conn_op_ctx->cleanup = NULL;
+ }
+
+ /* decrease pending connect operation counter */
+ u->socket_pool->connections--;
+
+ nret = ngx_stream_lua_socket_tcp_connect_helper(L, u, r, ctx,
+ conn_op_ctx->host.data,
+ conn_op_ctx->host.len,
+ conn_op_ctx->port, 1);
+ ngx_queue_insert_head(&u->socket_pool->cache_connect_op,
+ &conn_op_ctx->queue);
+
+ return nret;
+}
+
+
+static void
+ngx_stream_lua_socket_tcp_finalize(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u)
+{
+ ngx_connection_t *c;
+
+ ngx_stream_lua_socket_pool_t *spool;
+
+ dd("request: %p, u: %p, u->cleanup: %p", r, u, u->cleanup);
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua finalize socket");
+
+ if (u->cleanup) {
+ *u->cleanup = NULL;
+ ngx_stream_lua_cleanup_free(r, u->cleanup);
+ u->cleanup = NULL;
+ }
+
+ ngx_stream_lua_socket_tcp_finalize_read_part(r, u);
+
+ ngx_stream_lua_socket_tcp_finalize_write_part(r, u, 0);
+
+ if (u->raw_downstream || u->body_downstream) {
+ u->peer.connection = NULL;
+ return;
+ }
+
+ if (u->resolved && u->resolved->ctx) {
+ ngx_resolve_name_done(u->resolved->ctx);
+ u->resolved->ctx = NULL;
+ }
+
+ if (u->peer.free) {
+ u->peer.free(&u->peer, u->peer.data, 0);
+ }
+
+#if (NGX_STREAM_SSL)
+ if (u->ssl_name.data) {
+ ngx_free(u->ssl_name.data);
+ u->ssl_name.data = NULL;
+ u->ssl_name.len = 0;
+ }
+#endif
+
+ c = u->peer.connection;
+ if (c) {
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua close socket connection");
+
+ ngx_stream_lua_socket_tcp_close_connection(c);
+ u->peer.connection = NULL;
+
+ u->conn_closed = 1;
+
+ spool = u->socket_pool;
+ if (spool == NULL) {
+ return;
+ }
+
+ spool->connections--;
+
+ if (spool->connections == 0) {
+ ngx_stream_lua_socket_free_pool(r->connection->log, spool);
+ return;
+ }
+
+ ngx_stream_lua_socket_tcp_resume_conn_op(spool);
+ }
+}
+
+
+static void
+ngx_stream_lua_socket_tcp_close_connection(ngx_connection_t *c)
+{
+#if (NGX_STREAM_SSL)
+
+ if (c->ssl) {
+ c->ssl->no_wait_shutdown = 1;
+ c->ssl->no_send_shutdown = 1;
+
+ (void) ngx_ssl_shutdown(c);
+ }
+
+#endif
+
+ if (c->pool) {
+ ngx_destroy_pool(c->pool);
+ c->pool = NULL;
+ }
+
+ ngx_close_connection(c);
+}
+
+
+static ngx_int_t
+ngx_stream_lua_socket_test_connect(ngx_stream_lua_request_t *r,
+ ngx_connection_t *c)
+{
+ int err;
+ socklen_t len;
+
+ ngx_stream_lua_loc_conf_t *llcf;
+
+#if (NGX_HAVE_KQUEUE)
+
+ ngx_event_t *ev;
+
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+ dd("pending eof: (%p)%d (%p)%d", c->write, c->write->pending_eof,
+ c->read, c->read->pending_eof);
+
+ if (c->write->pending_eof) {
+ ev = c->write;
+
+ } else if (c->read->pending_eof) {
+ ev = c->read;
+
+ } else {
+ ev = NULL;
+ }
+
+ if (ev) {
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+ if (llcf->log_socket_errors) {
+ (void) ngx_connection_error(c, ev->kq_errno,
+ "kevent() reported that "
+ "connect() failed");
+ }
+ return ev->kq_errno;
+ }
+
+ } else
+#endif
+ {
+ err = 0;
+ len = sizeof(int);
+
+ /*
+ * BSDs and Linux return 0 and set a pending error in err
+ * Solaris returns -1 and sets errno
+ */
+
+ if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len)
+ == -1)
+ {
+ err = ngx_errno;
+ }
+
+ if (err) {
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+ if (llcf->log_socket_errors) {
+ (void) ngx_connection_error(c, err, "connect() failed");
+ }
+ return err;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_stream_lua_socket_dummy_handler(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket dummy handler");
+}
+
+
+static int
+ngx_stream_lua_socket_tcp_receiveuntil(lua_State *L)
+{
+ ngx_stream_lua_request_t *r;
+ int n;
+ ngx_str_t pat;
+ ngx_int_t rc;
+ size_t size;
+ unsigned inclusive = 0;
+
+ ngx_stream_lua_socket_compiled_pattern_t *cp;
+
+ n = lua_gettop(L);
+ if (n != 2 && n != 3) {
+ return luaL_error(L, "expecting 2 or 3 arguments "
+ "(including the object), but got %d", n);
+ }
+
+ if (n == 3) {
+ /* check out the options table */
+
+ luaL_checktype(L, 3, LUA_TTABLE);
+
+ lua_getfield(L, 3, "inclusive");
+
+ switch (lua_type(L, -1)) {
+ case LUA_TNIL:
+ /* do nothing */
+ break;
+
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, -1)) {
+ inclusive = 1;
+ }
+ break;
+
+ default:
+ return luaL_error(L, "bad \"inclusive\" option value type: %s",
+ luaL_typename(L, -1));
+
+ }
+
+ lua_pop(L, 2);
+ }
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket calling receiveuntil() method");
+
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ pat.data = (u_char *) luaL_checklstring(L, 2, &pat.len);
+ if (pat.len == 0) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "pattern is empty");
+ return 2;
+ }
+
+ size = sizeof(ngx_stream_lua_socket_compiled_pattern_t);
+
+ cp = lua_newuserdata(L, size);
+ if (cp == NULL) {
+ return luaL_error(L, "no memory");
+ }
+
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ pattern_udata_metatable_key));
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ lua_setmetatable(L, -2);
+
+ ngx_memzero(cp, size);
+
+ cp->inclusive = inclusive;
+
+ rc = ngx_stream_lua_socket_compile_pattern(pat.data, pat.len, cp,
+ r->connection->log);
+
+ if (rc != NGX_OK) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "failed to compile pattern");
+ return 2;
+ }
+
+ lua_pushcclosure(L, ngx_stream_lua_socket_receiveuntil_iterator, 3);
+ return 1;
+}
+
+
+static int
+ngx_stream_lua_socket_receiveuntil_iterator(lua_State *L)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_int_t rc;
+ ngx_stream_lua_ctx_t *ctx;
+ lua_Integer bytes;
+ int n;
+
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+ ngx_stream_lua_co_ctx_t *coctx;
+ ngx_stream_lua_socket_compiled_pattern_t *cp;
+
+ n = lua_gettop(L);
+ if (n > 1) {
+ return luaL_error(L, "expecting 0 or 1 arguments, "
+ "but seen %d", n);
+ }
+
+ if (n >= 1) {
+ bytes = luaL_checkinteger(L, 1);
+ if (bytes < 0) {
+ bytes = 0;
+ }
+
+ } else {
+ bytes = 0;
+ }
+
+ lua_rawgeti(L, lua_upvalueindex(1), SOCKET_CTX_INDEX);
+ u = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ if (u == NULL || u->peer.connection == NULL || u->read_closed) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "closed");
+ return 2;
+ }
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ if (u->request != r) {
+ return luaL_error(L, "bad request");
+ }
+
+ ngx_stream_lua_socket_check_busy_connecting(r, u, L);
+ ngx_stream_lua_socket_check_busy_reading(r, u, L);
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket receiveuntil iterator");
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket read timeout: %M", u->read_timeout);
+
+ u->input_filter = ngx_stream_lua_socket_read_until;
+
+ cp = lua_touserdata(L, lua_upvalueindex(3));
+
+ dd("checking existing state: %d", cp->state);
+
+ if (cp->state == -1) {
+ cp->state = 0;
+
+ lua_pushnil(L);
+ lua_pushnil(L);
+ lua_pushnil(L);
+ return 3;
+ }
+
+ cp->upstream = u;
+
+ cp->pattern.data =
+ (u_char *) lua_tolstring(L, lua_upvalueindex(2),
+ &cp->pattern.len);
+
+ u->input_filter_ctx = cp;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+
+ if (u->bufs_in == NULL) {
+ u->bufs_in =
+ ngx_stream_lua_chain_get_free_buf(r->connection->log, r->pool,
+ &ctx->free_recv_bufs,
+ u->conf->buffer_size);
+
+ if (u->bufs_in == NULL) {
+ return luaL_error(L, "no memory");
+ }
+
+ u->buf_in = u->bufs_in;
+ u->buffer = *u->buf_in->buf;
+ }
+
+ u->length = (size_t) bytes;
+ u->rest = u->length;
+
+ if (u->raw_downstream || u->body_downstream) {
+ r->read_event_handler = ngx_stream_lua_req_socket_rev_handler;
+ }
+
+ u->read_waiting = 0;
+ u->read_co_ctx = NULL;
+
+ rc = ngx_stream_lua_socket_tcp_read(r, u);
+
+ if (rc == NGX_ERROR) {
+ dd("read failed: %d", (int) u->ft_type);
+ rc = ngx_stream_lua_socket_tcp_receive_retval_handler(r, u, L);
+ dd("tcp receive retval returned: %d", (int) rc);
+ return rc;
+ }
+
+ if (rc == NGX_OK) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket receive done in a single run");
+
+ return ngx_stream_lua_socket_tcp_receive_retval_handler(r, u, L);
+ }
+
+ /* rc == NGX_AGAIN */
+
+ coctx = ctx->cur_co_ctx;
+
+ u->read_event_handler = ngx_stream_lua_socket_read_handler;
+
+ ngx_stream_lua_cleanup_pending_operation(coctx);
+ coctx->cleanup = ngx_stream_lua_coctx_cleanup;
+ coctx->data = u;
+
+ if (ctx->entered_content_phase) {
+ r->write_event_handler = ngx_stream_lua_content_wev_handler;
+
+ } else {
+ r->write_event_handler = ngx_stream_lua_core_run_phases;
+ }
+
+ u->read_co_ctx = coctx;
+ u->read_waiting = 1;
+ u->read_prepare_retvals = ngx_stream_lua_socket_tcp_receive_retval_handler;
+
+ dd("setting data to %p", u);
+
+ if (u->raw_downstream || u->body_downstream) {
+ ctx->downstream = u;
+ }
+
+ return lua_yield(L, 0);
+}
+
+
+static ngx_int_t
+ngx_stream_lua_socket_compile_pattern(u_char *data, size_t len,
+ ngx_stream_lua_socket_compiled_pattern_t *cp, ngx_log_t *log)
+{
+ size_t i;
+ size_t prefix_len;
+ size_t size;
+ unsigned found;
+ int cur_state, new_state;
+
+ ngx_stream_lua_dfa_edge_t *edge;
+ ngx_stream_lua_dfa_edge_t **last = NULL;
+
+ cp->pattern.len = len;
+
+ if (len <= 2) {
+ return NGX_OK;
+ }
+
+ for (i = 1; i < len; i++) {
+ prefix_len = 1;
+
+ while (prefix_len <= len - i - 1) {
+
+ if (ngx_memcmp(data, &data[i], prefix_len) == 0) {
+ if (data[prefix_len] == data[i + prefix_len]) {
+ prefix_len++;
+ continue;
+ }
+
+ cur_state = i + prefix_len;
+ new_state = prefix_len + 1;
+
+ if (cp->recovering == NULL) {
+ size = sizeof(void *) * (len - 2);
+ cp->recovering = ngx_alloc(size, log);
+ if (cp->recovering == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memzero(cp->recovering, size);
+ }
+
+ edge = cp->recovering[cur_state - 2];
+
+ found = 0;
+
+ if (edge == NULL) {
+ last = &cp->recovering[cur_state - 2];
+
+ } else {
+
+ for (; edge; edge = edge->next) {
+ last = &edge->next;
+
+ if (edge->chr == data[prefix_len]) {
+ found = 1;
+
+ if (edge->new_state < new_state) {
+ edge->new_state = new_state;
+ }
+
+ break;
+ }
+ }
+ }
+
+ if (!found) {
+ ngx_log_debug7(NGX_LOG_DEBUG_STREAM, log, 0,
+ "stream lua tcp socket read until "
+ "recovering point: on state %d (%*s), if "
+ "next is '%c', then recover to state %d "
+ "(%*s)", cur_state, (size_t) cur_state, data,
+ data[prefix_len], new_state,
+ (size_t) new_state, data);
+
+ edge = ngx_alloc(sizeof(ngx_stream_lua_dfa_edge_t), log);
+ if (edge == NULL) {
+ return NGX_ERROR;
+ }
+
+ edge->chr = data[prefix_len];
+ edge->new_state = new_state;
+ edge->next = NULL;
+
+ *last = edge;
+ }
+
+ break;
+ }
+
+ break;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_socket_read_until(void *data, ssize_t bytes)
+{
+ ngx_stream_lua_socket_compiled_pattern_t *cp = data;
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+
+ ngx_stream_lua_request_t *r;
+ ngx_buf_t *b;
+ u_char c;
+ u_char *pat;
+ size_t pat_len;
+ int i;
+ int state;
+ int old_state = 0; /* just to make old
+ gcc happy */
+ ngx_stream_lua_dfa_edge_t *edge;
+ unsigned matched;
+ ngx_int_t rc;
+
+ u = cp->upstream;
+ r = u->request;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket read until");
+
+ if (bytes == 0) {
+ u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_CLOSED;
+ return NGX_ERROR;
+ }
+
+ b = &u->buffer;
+
+ pat = cp->pattern.data;
+ pat_len = cp->pattern.len;
+ state = cp->state;
+
+ i = 0;
+ while (i < bytes) {
+ c = b->pos[i];
+
+ dd("%d: read char %d, state: %d", i, c, state);
+
+ if (c == pat[state]) {
+ i++;
+ state++;
+
+ if (state == (int) pat_len) {
+ /* already matched the whole pattern */
+ dd("pat len: %d", (int) pat_len);
+
+ b->pos += i;
+
+ if (u->length) {
+ cp->state = -1;
+
+ } else {
+ cp->state = 0;
+ }
+
+ if (cp->inclusive) {
+ rc = ngx_stream_lua_socket_add_pending_data(r, u, b->pos, 0,
+ pat, state,
+ state);
+
+ if (rc != NGX_OK) {
+ u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_ERROR;
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+ }
+
+ continue;
+ }
+
+ if (state == 0) {
+ u->buf_in->buf->last++;
+
+ i++;
+
+ if (u->length && --u->rest == 0) {
+ cp->state = state;
+ b->pos += i;
+ return NGX_OK;
+ }
+
+ continue;
+ }
+
+ matched = 0;
+
+ if (cp->recovering && state >= 2) {
+ dd("accessing state: %d, index: %d", state, state - 2);
+ for (edge = cp->recovering[state - 2]; edge; edge = edge->next) {
+
+ if (edge->chr == c) {
+ dd("matched '%c' and jumping to state %d", c,
+ edge->new_state);
+
+ old_state = state;
+ state = edge->new_state;
+ matched = 1;
+ break;
+ }
+ }
+ }
+
+ if (!matched) {
+#if 1
+ dd("adding pending data: %.*s", state, pat);
+ rc = ngx_stream_lua_socket_add_pending_data(r, u, b->pos, i, pat,
+ state, state);
+
+ if (rc != NGX_OK) {
+ u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_ERROR;
+ return NGX_ERROR;
+ }
+
+#endif
+
+ if (u->length) {
+ if (u->rest <= (size_t) state) {
+ u->rest = 0;
+ cp->state = 0;
+ b->pos += i;
+ return NGX_OK;
+
+ } else {
+ u->rest -= state;
+ }
+ }
+
+ state = 0;
+ continue;
+ }
+
+ /* matched */
+
+ dd("adding pending data: %.*s", (int) (old_state + 1 - state),
+ (char *) pat);
+
+ rc = ngx_stream_lua_socket_add_pending_data(r, u, b->pos, i, pat,
+ old_state + 1 - state,
+ old_state);
+
+ if (rc != NGX_OK) {
+ u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_ERROR;
+ return NGX_ERROR;
+ }
+
+ i++;
+
+ if (u->length) {
+ if (u->rest <= (size_t) state) {
+ u->rest = 0;
+ cp->state = state;
+ b->pos += i;
+ return NGX_OK;
+
+ } else {
+ u->rest -= state;
+ }
+ }
+
+ continue;
+ }
+
+ b->pos += i;
+ cp->state = state;
+
+ return NGX_AGAIN;
+}
+
+
+static int
+ngx_stream_lua_socket_cleanup_compiled_pattern(lua_State *L)
+{
+ ngx_stream_lua_socket_compiled_pattern_t *cp;
+
+ ngx_stream_lua_dfa_edge_t *edge, *p;
+ unsigned i;
+
+ dd("cleanup compiled pattern");
+
+ cp = lua_touserdata(L, 1);
+ if (cp == NULL || cp->recovering == NULL) {
+ return 0;
+ }
+
+ dd("pattern len: %d", (int) cp->pattern.len);
+
+ for (i = 0; i < cp->pattern.len - 2; i++) {
+ edge = cp->recovering[i];
+
+ while (edge) {
+ p = edge;
+ edge = edge->next;
+
+ dd("freeing edge %p", p);
+
+ ngx_free(p);
+
+ dd("edge: %p", edge);
+ }
+ }
+
+#if 1
+ ngx_free(cp->recovering);
+ cp->recovering = NULL;
+#endif
+
+ return 0;
+}
+
+
+int
+ngx_stream_lua_req_socket_tcp(lua_State *L)
+{
+ int n, raw;
+ ngx_peer_connection_t *pc;
+ ngx_stream_lua_loc_conf_t *llcf;
+ ngx_connection_t *c;
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_cleanup_t *cln;
+ ngx_stream_lua_co_ctx_t *coctx;
+
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+
+ n = lua_gettop(L);
+
+ if (n != 0 && n != 1) {
+ return luaL_error(L, "expecting zero arguments, but got %d",
+ lua_gettop(L));
+ }
+
+ if (n == 1) {
+ lua_pop(L, 1);
+ }
+
+ raw = 1;
+
+ r = ngx_stream_lua_get_req(L);
+
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return luaL_error(L, "no ctx found");
+ }
+
+ ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT
+ |NGX_STREAM_LUA_CONTEXT_PREREAD);
+
+ c = r->connection;
+
+ if (raw) {
+
+ if (c->buffered) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "pending data to write");
+ return 2;
+ }
+
+
+
+ dd("ctx acquired raw req socket: %d", ctx->acquired_raw_req_socket);
+
+ if (ctx->acquired_raw_req_socket) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "duplicate call");
+ return 2;
+ }
+
+ ctx->acquired_raw_req_socket = 1;
+
+
+ } else {
+ }
+
+ lua_createtable(L, 2 /* narr */, 3 /* nrec */); /* the object */
+
+ if (raw) {
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ raw_req_socket_metatable_key));
+
+ }
+
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ lua_setmetatable(L, -2);
+
+ u = lua_newuserdata(L, sizeof(ngx_stream_lua_socket_tcp_upstream_t));
+ if (u == NULL) {
+ return luaL_error(L, "no memory");
+ }
+
+#if 1
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ downstream_udata_metatable_key));
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ lua_setmetatable(L, -2);
+#endif
+
+ lua_rawseti(L, 1, SOCKET_CTX_INDEX);
+
+ ngx_memzero(u, sizeof(ngx_stream_lua_socket_tcp_upstream_t));
+
+ if (raw) {
+ u->raw_downstream = 1;
+
+ } else {
+ }
+
+ coctx = ctx->cur_co_ctx;
+
+ u->request = r;
+
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+
+ u->conf = llcf;
+
+ u->read_timeout = u->conf->read_timeout;
+ u->connect_timeout = u->conf->connect_timeout;
+ u->send_timeout = u->conf->send_timeout;
+
+ cln = ngx_stream_lua_cleanup_add(r, 0);
+ if (cln == NULL) {
+ u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_ERROR;
+ lua_pushnil(L);
+ lua_pushliteral(L, "no memory");
+ return 2;
+ }
+
+ cln->handler = ngx_stream_lua_socket_tcp_cleanup;
+ cln->data = u;
+ u->cleanup = &cln->handler;
+
+ pc = &u->peer;
+
+ pc->log = c->log;
+ pc->log_error = NGX_ERROR_ERR;
+
+ pc->connection = c;
+
+ dd("setting data to %p", u);
+
+ coctx->data = u;
+ ctx->downstream = u;
+
+ if (c->read->timer_set) {
+ ngx_del_timer(c->read);
+ }
+
+ if (raw) {
+ if (c->write->timer_set) {
+ ngx_del_timer(c->write);
+ }
+ }
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+
+static void
+ngx_stream_lua_req_socket_rev_handler(ngx_stream_lua_request_t *r)
+{
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua request socket read event handler");
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ r->read_event_handler = ngx_stream_lua_block_reading;
+ return;
+ }
+
+ u = ctx->downstream;
+ if (u == NULL || u->peer.connection == NULL) {
+ r->read_event_handler = ngx_stream_lua_block_reading;
+ return;
+ }
+
+ u->read_event_handler(r, u);
+}
+
+static int
+ngx_stream_lua_socket_tcp_getreusedtimes(lua_State *L)
+{
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+
+ if (lua_gettop(L) != 1) {
+ return luaL_error(L, "expecting 1 argument "
+ "(including the object), but got %d", lua_gettop(L));
+ }
+
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ lua_rawgeti(L, 1, SOCKET_CTX_INDEX);
+ u = lua_touserdata(L, -1);
+
+ if (u == NULL
+ || u->peer.connection == NULL
+ || (u->read_closed && u->write_closed))
+ {
+ lua_pushnil(L);
+ lua_pushliteral(L, "closed");
+ return 2;
+ }
+
+ lua_pushinteger(L, u->reused);
+ return 1;
+}
+
+
+static int
+ngx_stream_lua_socket_tcp_setkeepalive(lua_State *L)
+{
+ ngx_stream_lua_loc_conf_t *llcf;
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+ ngx_stream_lua_socket_pool_t *spool;
+ ngx_stream_lua_socket_pool_item_t *item;
+
+ ngx_connection_t *c;
+ ngx_str_t key;
+ ngx_queue_t *q;
+ ngx_peer_connection_t *pc;
+ ngx_stream_lua_request_t *r;
+ ngx_msec_t timeout;
+ ngx_int_t pool_size;
+ int n;
+ ngx_int_t rc;
+ ngx_buf_t *b;
+ const char *msg;
+
+ n = lua_gettop(L);
+
+ if (n < 1 || n > 3) {
+ return luaL_error(L, "expecting 1 to 3 arguments "
+ "(including the object), but got %d", n);
+ }
+
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+
+ /* luaL_checkinteger will throw error if the argument is not a number.
+ * e.g.: bad argument \#2 to '?' (number expected, got string)
+ *
+ * We should check the argument in advance; otherwise,
+ * throwing an exception in the middle can compromise data integrity.
+ * e.g.: set pc->connection to NULL without following cleanup.
+ */
+ if (n >= 2 && !lua_isnil(L, 2)) {
+ timeout = (ngx_msec_t) luaL_checkinteger(L, 2);
+
+ } else {
+ timeout = llcf->keepalive_timeout;
+ }
+
+ if (n >= 3 && !lua_isnil(L, 3)) {
+ pool_size = luaL_checkinteger(L, 3);
+
+ } else {
+ pool_size = llcf->pool_size;
+ }
+
+ lua_rawgeti(L, 1, SOCKET_CTX_INDEX);
+ u = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ if (u == NULL) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "closed");
+ return 2;
+ }
+
+ /* stack: obj timeout? size? */
+
+ pc = &u->peer;
+ c = pc->connection;
+
+ if (c == NULL || u->read_closed || u->write_closed) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "closed");
+ return 2;
+ }
+
+ if (u->request != r) {
+ return luaL_error(L, "bad request");
+ }
+
+ ngx_stream_lua_socket_check_busy_connecting(r, u, L);
+ ngx_stream_lua_socket_check_busy_reading(r, u, L);
+ ngx_stream_lua_socket_check_busy_writing(r, u, L);
+
+ b = &u->buffer;
+
+ if (b->start && ngx_buf_size(b)) {
+ ngx_stream_lua_probe_socket_tcp_setkeepalive_buf_unread(r, u, b->pos,
+ b->last - b->pos);
+
+ lua_pushnil(L);
+ lua_pushliteral(L, "unread data in buffer");
+ return 2;
+ }
+
+ if (c->read->eof
+ || c->read->error
+ || c->read->timedout
+ || c->write->error
+ || c->write->timedout)
+ {
+ lua_pushnil(L);
+ lua_pushliteral(L, "invalid connection");
+ return 2;
+ }
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "failed to handle read event");
+ return 2;
+ }
+
+ if (ngx_terminate || ngx_exiting) {
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "stream lua tcp socket set keepalive while "
+ "process exiting, closing connection %p", c);
+
+ ngx_stream_lua_socket_tcp_finalize(r, u);
+ lua_pushinteger(L, 1);
+ return 1;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "stream lua tcp socket set keepalive: saving "
+ "connection %p", c);
+
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ socket_pool_key));
+ lua_rawget(L, LUA_REGISTRYINDEX);
+
+ /* stack: obj timeout? size? pools */
+
+ lua_rawgeti(L, 1, SOCKET_KEY_INDEX);
+ key.data = (u_char *) lua_tolstring(L, -1, &key.len);
+ if (key.data == NULL) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "key not found");
+ return 2;
+ }
+
+ dd("saving connection to key %s", lua_tostring(L, -1));
+
+ lua_pushvalue(L, -1);
+ lua_rawget(L, -3);
+ spool = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ /* stack: obj timeout? size? pools cache_key */
+
+ if (spool == NULL) {
+ /* create a new socket pool for the current peer key */
+
+ if (pool_size <= 0) {
+ msg = lua_pushfstring(L, "bad \"pool_size\" option value: %i",
+ pool_size);
+ return luaL_argerror(L, n, msg);
+ }
+
+ ngx_stream_lua_socket_tcp_create_socket_pool(L, r, key,
+ pool_size, -1,
+ &spool);
+ }
+
+ if (ngx_queue_empty(&spool->free)) {
+
+ q = ngx_queue_last(&spool->cache);
+ ngx_queue_remove(q);
+
+ item = ngx_queue_data(q, ngx_stream_lua_socket_pool_item_t, queue);
+
+ ngx_stream_lua_socket_tcp_close_connection(item->connection);
+
+ /* only decrease the counter for connections which were counted */
+ if (u->socket_pool != NULL) {
+ u->socket_pool->connections--;
+ }
+
+ } else {
+ q = ngx_queue_head(&spool->free);
+ ngx_queue_remove(q);
+
+ item = ngx_queue_data(q, ngx_stream_lua_socket_pool_item_t, queue);
+
+ /* we should always increase connections after getting connected,
+ * and decrease connections after getting closed.
+ * however, we don't create connection pool in previous connect method.
+ * so we increase connections here for backward compatibility.
+ */
+ if (u->socket_pool == NULL) {
+ spool->connections++;
+ }
+ }
+
+ item->connection = c;
+ ngx_queue_insert_head(&spool->cache, q);
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "stream lua tcp socket clear current socket connection");
+
+ pc->connection = NULL;
+
+#if 0
+ if (u->cleanup) {
+ *u->cleanup = NULL;
+ u->cleanup = NULL;
+ }
+#endif
+
+ if (c->read->timer_set) {
+ ngx_del_timer(c->read);
+ }
+
+ if (c->write->timer_set) {
+ ngx_del_timer(c->write);
+ }
+
+#if (NGX_DEBUG)
+ if (timeout == 0) {
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket keepalive timeout: unlimited");
+ }
+#endif
+
+ if (timeout) {
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket keepalive timeout: %M ms",
+ timeout);
+
+ ngx_add_timer(c->read, timeout);
+ }
+
+ c->write->handler = ngx_stream_lua_socket_keepalive_dummy_handler;
+ c->read->handler = ngx_stream_lua_socket_keepalive_rev_handler;
+
+ c->data = item;
+ c->idle = 1;
+ c->log = ngx_cycle->log;
+ c->pool->log = ngx_cycle->log;
+ c->read->log = ngx_cycle->log;
+ c->write->log = ngx_cycle->log;
+
+ item->socklen = pc->socklen;
+ ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen);
+ item->reused = u->reused;
+
+ if (c->read->ready) {
+ rc = ngx_stream_lua_socket_keepalive_close_handler(c->read);
+ if (rc != NGX_OK) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "connection in dubious state");
+ return 2;
+ }
+ }
+
+#if 1
+ ngx_stream_lua_socket_tcp_finalize(r, u);
+#endif
+
+ /* since we set u->peer->connection to NULL previously, the connect
+ * operation won't be resumed in the
+ * ngx_stream_lua_socket_tcp_finalize.
+ * Therefore we need to resume it here.
+ */
+ ngx_stream_lua_socket_tcp_resume_conn_op(spool);
+
+ lua_pushinteger(L, 1);
+ return 1;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_get_keepalive_peer(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u)
+{
+ ngx_stream_lua_socket_pool_item_t *item;
+ ngx_stream_lua_socket_pool_t *spool;
+
+ ngx_stream_lua_cleanup_t *cln;
+ ngx_queue_t *q;
+ ngx_peer_connection_t *pc;
+ ngx_connection_t *c;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket pool get keepalive peer");
+
+ pc = &u->peer;
+
+ spool = u->socket_pool;
+
+ if (!ngx_queue_empty(&spool->cache)) {
+ q = ngx_queue_head(&spool->cache);
+
+ item = ngx_queue_data(q, ngx_stream_lua_socket_pool_item_t, queue);
+ c = item->connection;
+
+ ngx_queue_remove(q);
+ ngx_queue_insert_head(&spool->free, q);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "stream lua tcp socket get keepalive peer: "
+ "using connection %p, fd:%d", c, c->fd);
+
+ c->idle = 0;
+ c->log = pc->log;
+ c->pool->log = pc->log;
+ c->read->log = pc->log;
+ c->write->log = pc->log;
+ c->data = u;
+
+#if 1
+ c->write->handler = ngx_stream_lua_socket_tcp_handler;
+ c->read->handler = ngx_stream_lua_socket_tcp_handler;
+#endif
+
+ if (c->read->timer_set) {
+ ngx_del_timer(c->read);
+ }
+
+ pc->connection = c;
+ pc->cached = 1;
+
+ u->reused = item->reused + 1;
+
+#if 1
+ u->write_event_handler = ngx_stream_lua_socket_dummy_handler;
+ u->read_event_handler = ngx_stream_lua_socket_dummy_handler;
+#endif
+
+ if (u->cleanup == NULL) {
+ cln = ngx_stream_lua_cleanup_add(r, 0);
+ if (cln == NULL) {
+ u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_ERROR;
+ return NGX_ERROR;
+ }
+
+ cln->handler = ngx_stream_lua_socket_tcp_cleanup;
+ cln->data = u;
+ u->cleanup = &cln->handler;
+ }
+
+ return NGX_OK;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "stream lua tcp socket keepalive: connection pool empty");
+
+ return NGX_DECLINED;
+}
+
+
+static void
+ngx_stream_lua_socket_keepalive_dummy_handler(ngx_event_t *ev)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ev->log, 0,
+ "stream keepalive dummy handler");
+}
+
+
+static void
+ngx_stream_lua_socket_keepalive_rev_handler(ngx_event_t *ev)
+{
+ (void) ngx_stream_lua_socket_keepalive_close_handler(ev);
+}
+
+
+static ngx_int_t
+ngx_stream_lua_socket_keepalive_close_handler(ngx_event_t *ev)
+{
+ ngx_stream_lua_socket_pool_item_t *item;
+ ngx_stream_lua_socket_pool_t *spool;
+
+ int n;
+ unsigned char buf[1];
+ ngx_connection_t *c;
+
+ c = ev->data;
+
+ if (c->close) {
+ goto close;
+ }
+
+ if (c->read->timedout) {
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ev->log, 0,
+ "stream lua tcp socket keepalive max idle timeout");
+
+ goto close;
+ }
+
+ dd("read event ready: %d", (int) c->read->ready);
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ev->log, 0,
+ "stream lua tcp socket keepalive close handler "
+ "check stale events");
+
+ /* consume the possible ssl-layer data implicitly */
+ n = c->recv(c, buf, 1);
+
+ if (n == NGX_AGAIN) {
+ /* stale event */
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ goto close;
+ }
+
+ return NGX_OK;
+ }
+
+close:
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ev->log, 0,
+ "stream lua tcp socket keepalive close handler: fd:%d",
+ c->fd);
+
+ item = c->data;
+ spool = item->socket_pool;
+
+ ngx_stream_lua_socket_tcp_close_connection(c);
+
+ ngx_queue_remove(&item->queue);
+ ngx_queue_insert_head(&spool->free, &item->queue);
+ spool->connections--;
+
+ dd("keepalive: connections: %u", (unsigned) spool->connections);
+
+ if (spool->connections == 0) {
+ ngx_stream_lua_socket_free_pool(ev->log, spool);
+
+ } else {
+ ngx_stream_lua_socket_tcp_resume_conn_op(spool);
+ }
+
+ return NGX_DECLINED;
+}
+
+
+static void
+ngx_stream_lua_socket_free_pool(ngx_log_t *log,
+ ngx_stream_lua_socket_pool_t *spool)
+{
+ lua_State *L;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, log, 0,
+ "stream lua tcp socket keepalive: free "
+ "connection pool for \"%s\"", spool->key);
+
+ L = spool->lua_vm;
+
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ socket_pool_key));
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ lua_pushstring(L, (char *) spool->key);
+ lua_pushnil(L);
+ lua_rawset(L, -3);
+ lua_pop(L, 1);
+}
+
+
+static void
+ngx_stream_lua_socket_shutdown_pool_helper(
+ ngx_stream_lua_socket_pool_t *spool)
+{
+ ngx_queue_t *q;
+ ngx_connection_t *c;
+ ngx_stream_lua_socket_pool_item_t *item;
+ ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx;
+
+ while (!ngx_queue_empty(&spool->cache)) {
+ q = ngx_queue_head(&spool->cache);
+
+ item = ngx_queue_data(q, ngx_stream_lua_socket_pool_item_t, queue);
+ c = item->connection;
+
+ ngx_stream_lua_socket_tcp_close_connection(c);
+
+ ngx_queue_remove(q);
+ ngx_queue_insert_head(&spool->free, q);
+ }
+
+ while (!ngx_queue_empty(&spool->cache_connect_op)) {
+ q = ngx_queue_head(&spool->cache_connect_op);
+ ngx_queue_remove(q);
+ conn_op_ctx = ngx_queue_data(q, ngx_stream_lua_socket_tcp_conn_op_ctx_t,
+ queue);
+ ngx_stream_lua_socket_tcp_free_conn_op_ctx(conn_op_ctx);
+ }
+
+ while (!ngx_queue_empty(&spool->wait_connect_op)) {
+ q = ngx_queue_head(&spool->wait_connect_op);
+ ngx_queue_remove(q);
+ conn_op_ctx = ngx_queue_data(q, ngx_stream_lua_socket_tcp_conn_op_ctx_t,
+ queue);
+
+ if (conn_op_ctx->event.timer_set) {
+ ngx_del_timer(&conn_op_ctx->event);
+ }
+
+ ngx_stream_lua_socket_tcp_free_conn_op_ctx(conn_op_ctx);
+ }
+
+ /* spool->connections will be decreased down to zero in
+ * ngx_stream_lua_socket_tcp_finalize */
+}
+
+
+static int
+ngx_stream_lua_socket_shutdown_pool(lua_State *L)
+{
+ ngx_stream_lua_socket_pool_t *spool;
+
+ spool = lua_touserdata(L, 1);
+
+ if (spool != NULL) {
+ ngx_stream_lua_socket_shutdown_pool_helper(spool);
+ }
+
+ return 0;
+}
+
+
+static int
+ngx_stream_lua_socket_tcp_upstream_destroy(lua_State *L)
+{
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+
+ dd("upstream destroy triggered by Lua GC");
+
+ u = lua_touserdata(L, 1);
+ if (u == NULL) {
+ return 0;
+ }
+
+ if (u->cleanup) {
+ ngx_stream_lua_socket_tcp_cleanup(u); /* it will clear u->cleanup */
+ }
+
+ return 0;
+}
+
+
+static int
+ngx_stream_lua_socket_downstream_destroy(lua_State *L)
+{
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+
+ dd("downstream destroy");
+
+ u = lua_touserdata(L, 1);
+ if (u == NULL) {
+ dd("u is NULL");
+ return 0;
+ }
+
+ if (u->cleanup) {
+ ngx_stream_lua_socket_tcp_cleanup(u); /* it will clear u->cleanup */
+ }
+
+ return 0;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_socket_push_input_data(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx, ngx_stream_lua_socket_tcp_upstream_t *u,
+ lua_State *L)
+{
+ ngx_chain_t *cl;
+ ngx_chain_t **ll;
+#if (DDEBUG) || (NGX_DTRACE)
+ size_t size = 0;
+#endif
+ size_t chunk_size;
+ ngx_buf_t *b;
+ size_t nbufs;
+ luaL_Buffer luabuf;
+
+ dd("bufs_in: %p, buf_in: %p", u->bufs_in, u->buf_in);
+
+ nbufs = 0;
+ ll = NULL;
+
+ luaL_buffinit(L, &luabuf);
+
+ for (cl = u->bufs_in; cl; cl = cl->next) {
+ b = cl->buf;
+ chunk_size = b->last - b->pos;
+
+ dd("copying input data chunk from %p: \"%.*s\"", cl,
+ (int) chunk_size, b->pos);
+
+ luaL_addlstring(&luabuf, (char *) b->pos, chunk_size);
+
+ if (cl->next) {
+ ll = &cl->next;
+ }
+
+#if (DDEBUG) || (NGX_DTRACE)
+ size += chunk_size;
+#endif
+
+ nbufs++;
+ }
+
+ luaL_pushresult(&luabuf);
+
+#if (DDEBUG)
+ dd("size: %d, nbufs: %d", (int) size, (int) nbufs);
+#endif
+
+#if (NGX_DTRACE)
+ ngx_stream_lua_probe_socket_tcp_receive_done(r, u,
+ (u_char *) lua_tostring(L, -1),
+ size);
+#endif
+
+ if (nbufs > 1 && ll) {
+ dd("recycle buffers: %d", (int) (nbufs - 1));
+
+ *ll = ctx->free_recv_bufs;
+ ctx->free_recv_bufs = u->bufs_in;
+ u->bufs_in = u->buf_in;
+ }
+
+ if (u->buffer.pos == u->buffer.last) {
+ dd("resetting u->buffer pos & last");
+ u->buffer.pos = u->buffer.start;
+ u->buffer.last = u->buffer.start;
+ }
+
+ if (u->bufs_in) {
+ u->buf_in->buf->last = u->buffer.pos;
+ u->buf_in->buf->pos = u->buffer.pos;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_socket_add_input_buffer(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u)
+{
+ ngx_chain_t *cl;
+ ngx_stream_lua_ctx_t *ctx;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+
+ cl = ngx_stream_lua_chain_get_free_buf(r->connection->log, r->pool,
+ &ctx->free_recv_bufs,
+ u->conf->buffer_size);
+
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ u->buf_in->next = cl;
+ u->buf_in = cl;
+ u->buffer = *cl->buf;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_socket_add_pending_data(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u, u_char *pos, size_t len,
+ u_char *pat, int prefix, int old_state)
+{
+ u_char *last;
+ ngx_buf_t *b;
+
+ dd("resuming data: %d: [%.*s]", prefix, prefix, pat);
+
+ last = &pos[len];
+
+ b = u->buf_in->buf;
+
+ if (last - b->last == old_state) {
+ b->last += prefix;
+ return NGX_OK;
+ }
+
+ dd("need more buffers because %d != %d", (int) (last - b->last),
+ (int) old_state);
+
+ if (ngx_stream_lua_socket_insert_buffer(r, u, pat, prefix) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ b->pos = last;
+ b->last = last;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t ngx_stream_lua_socket_insert_buffer(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u,
+ u_char *pat, size_t prefix)
+{
+ ngx_chain_t *cl, *new_cl, **ll;
+ size_t size;
+ ngx_buf_t *b;
+
+ ngx_stream_lua_ctx_t *ctx;
+
+ if (prefix <= u->conf->buffer_size) {
+ size = u->conf->buffer_size;
+
+ } else {
+ size = prefix;
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+
+ new_cl = ngx_stream_lua_chain_get_free_buf(r->connection->log, r->pool,
+ &ctx->free_recv_bufs,
+ size);
+
+ if (new_cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ b = new_cl->buf;
+
+ b->last = ngx_copy(b->last, pat, prefix);
+
+ dd("copy resumed data to %p: %d: \"%.*s\"",
+ new_cl, (int) (b->last - b->pos), (int) (b->last - b->pos), b->pos);
+
+ dd("before resuming data: bufs_in %p, buf_in %p, buf_in next %p",
+ u->bufs_in, u->buf_in, u->buf_in->next);
+
+ ll = &u->bufs_in;
+ for (cl = u->bufs_in; cl->next; cl = cl->next) {
+ ll = &cl->next;
+ }
+
+ *ll = new_cl;
+ new_cl->next = u->buf_in;
+
+ dd("after resuming data: bufs_in %p, buf_in %p, buf_in next %p",
+ u->bufs_in, u->buf_in, u->buf_in->next);
+
+#if (DDEBUG)
+ for (cl = u->bufs_in; cl; cl = cl->next) {
+ b = cl->buf;
+
+ dd("result buf after resuming data: %p: %.*s", cl,
+ (int) ngx_buf_size(b), b->pos);
+ }
+#endif
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_socket_tcp_conn_op_resume(ngx_stream_lua_request_t *r)
+{
+ return ngx_stream_lua_socket_tcp_resume_helper(r,
+ SOCKET_OP_RESUME_CONN);
+}
+
+
+static ngx_int_t
+ngx_stream_lua_socket_tcp_conn_resume(ngx_stream_lua_request_t *r)
+{
+ return ngx_stream_lua_socket_tcp_resume_helper(r, SOCKET_OP_CONNECT);
+}
+
+
+static ngx_int_t
+ngx_stream_lua_socket_tcp_read_resume(ngx_stream_lua_request_t *r)
+{
+ return ngx_stream_lua_socket_tcp_resume_helper(r, SOCKET_OP_READ);
+}
+
+
+static ngx_int_t
+ngx_stream_lua_socket_tcp_write_resume(ngx_stream_lua_request_t *r)
+{
+ return ngx_stream_lua_socket_tcp_resume_helper(r, SOCKET_OP_WRITE);
+}
+
+
+static ngx_int_t
+ngx_stream_lua_socket_tcp_resume_helper(ngx_stream_lua_request_t *r,
+ int socket_op)
+{
+ int nret;
+ lua_State *vm;
+ ngx_int_t rc;
+ ngx_uint_t nreqs;
+ ngx_connection_t *c;
+
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx;
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+ ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx;
+ ngx_stream_lua_socket_tcp_retval_handler prepare_retvals;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ctx->resume_handler = ngx_stream_lua_wev_handler;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp operation done, resuming lua "
+ "thread");
+
+ coctx = ctx->cur_co_ctx;
+
+ dd("coctx: %p", coctx);
+
+ switch (socket_op) {
+
+ case SOCKET_OP_RESUME_CONN:
+ conn_op_ctx = coctx->data;
+ u = conn_op_ctx->u;
+ prepare_retvals = u->write_prepare_retvals;
+ break;
+
+ case SOCKET_OP_CONNECT:
+ case SOCKET_OP_WRITE:
+ u = coctx->data;
+ prepare_retvals = u->write_prepare_retvals;
+ break;
+
+ case SOCKET_OP_READ:
+ u = coctx->data;
+ prepare_retvals = u->read_prepare_retvals;
+ break;
+
+ default:
+ /* impossible to reach here */
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua tcp socket calling prepare retvals handler %p, "
+ "u:%p", prepare_retvals, u);
+
+ nret = prepare_retvals(r, u, ctx->cur_co_ctx->co);
+ if (socket_op == SOCKET_OP_CONNECT
+ && nret > 1
+ && !u->conn_closed
+ && u->socket_pool != NULL)
+ {
+ u->socket_pool->connections--;
+ ngx_stream_lua_socket_tcp_resume_conn_op(u->socket_pool);
+ }
+
+ if (nret == NGX_AGAIN) {
+ return NGX_DONE;
+ }
+
+ c = r->connection;
+ vm = ngx_stream_lua_get_lua_vm(r, ctx);
+ nreqs = c->requests;
+
+ rc = ngx_stream_lua_run_thread(vm, r, ctx, nret);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua run thread returned %d", rc);
+
+ if (rc == NGX_AGAIN) {
+ return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs);
+ }
+
+ if (rc == NGX_DONE) {
+ ngx_stream_lua_finalize_request(r, NGX_DONE);
+ return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs);
+ }
+
+ if (ctx->entered_content_phase) {
+ ngx_stream_lua_finalize_request(r, rc);
+ return NGX_DONE;
+ }
+
+ return rc;
+}
+
+
+static void
+ngx_stream_lua_tcp_queue_conn_op_cleanup(void *data)
+{
+ ngx_stream_lua_co_ctx_t *coctx = data;
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+ ngx_stream_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx;
+
+ conn_op_ctx = coctx->data;
+ u = conn_op_ctx->u;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "stream lua tcp socket abort queueing, "
+ "conn_op_ctx: %p, u: %p",
+ conn_op_ctx, u);
+
+ if (conn_op_ctx->event.posted) {
+ ngx_delete_posted_event(&conn_op_ctx->event);
+
+ } else if (conn_op_ctx->event.timer_set) {
+ ngx_del_timer(&conn_op_ctx->event);
+ }
+
+ ngx_queue_remove(&conn_op_ctx->queue);
+ ngx_queue_insert_head(&u->socket_pool->cache_connect_op,
+ &conn_op_ctx->queue);
+
+ u->socket_pool->connections--;
+ ngx_stream_lua_socket_tcp_resume_conn_op(u->socket_pool);
+}
+
+
+static void
+ngx_stream_lua_tcp_resolve_cleanup(void *data)
+{
+ ngx_resolver_ctx_t *rctx;
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+ ngx_stream_lua_co_ctx_t *coctx = data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "stream lua tcp socket abort resolver");
+
+ u = coctx->data;
+ if (u == NULL) {
+ return;
+ }
+
+ if (u->socket_pool != NULL) {
+ u->socket_pool->connections--;
+ ngx_stream_lua_socket_tcp_resume_conn_op(u->socket_pool);
+ }
+
+ rctx = u->resolved->ctx;
+ if (rctx == NULL) {
+ return;
+ }
+
+ /* postpone free the rctx in the handler */
+ rctx->handler = ngx_resolve_name_done;
+}
+
+
+static void
+ngx_stream_lua_coctx_cleanup(void *data)
+{
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+ ngx_stream_lua_co_ctx_t *coctx = data;
+
+ dd("running coctx cleanup");
+
+ u = coctx->data;
+ if (u == NULL) {
+ return;
+ }
+
+ if (u->request == NULL) {
+ return;
+ }
+
+ ngx_stream_lua_socket_tcp_finalize(u->request, u);
+}
+
+
+#if (NGX_STREAM_SSL)
+
+static int
+ngx_stream_lua_ssl_free_session(lua_State *L)
+{
+ ngx_ssl_session_t **psession;
+
+ psession = lua_touserdata(L, 1);
+ if (psession && *psession != NULL) {
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "stream lua ssl free session: %p", *psession);
+
+ ngx_ssl_free_session(*psession);
+ }
+
+ return 0;
+}
+
+#endif /* NGX_STREAM_SSL */
+
+
+void
+ngx_stream_lua_cleanup_conn_pools(lua_State *L)
+{
+ ngx_stream_lua_socket_pool_t *spool;
+
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ socket_pool_key));
+ lua_rawget(L, LUA_REGISTRYINDEX); /* table */
+
+ lua_pushnil(L); /* first key */
+ while (lua_next(L, -2) != 0) {
+ /* tb key val */
+ spool = lua_touserdata(L, -1);
+
+ if (spool != NULL) {
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "stream lua tcp socket keepalive: free "
+ "connection pool %p for \"%s\"", spool, spool->key);
+
+ ngx_stream_lua_socket_shutdown_pool_helper(spool);
+ }
+
+ lua_pop(L, 1);
+ }
+
+ lua_pop(L, 1);
+}
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_socket_tcp.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_socket_tcp.h
new file mode 100644
index 0000000..3473250
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_socket_tcp.h
@@ -0,0 +1,189 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_socket_tcp.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_SOCKET_TCP_H_INCLUDED_
+#define _NGX_STREAM_LUA_SOCKET_TCP_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+#define NGX_STREAM_LUA_SOCKET_FT_ERROR 0x0001
+#define NGX_STREAM_LUA_SOCKET_FT_TIMEOUT 0x0002
+#define NGX_STREAM_LUA_SOCKET_FT_CLOSED 0x0004
+#define NGX_STREAM_LUA_SOCKET_FT_RESOLVER 0x0008
+#define NGX_STREAM_LUA_SOCKET_FT_BUFTOOSMALL 0x0010
+#define NGX_STREAM_LUA_SOCKET_FT_NOMEM 0x0020
+#define NGX_STREAM_LUA_SOCKET_FT_PARTIALWRITE 0x0040
+#define NGX_STREAM_LUA_SOCKET_FT_CLIENTABORT 0x0080
+#define NGX_STREAM_LUA_SOCKET_FT_SSL 0x0100
+
+
+typedef struct ngx_stream_lua_socket_tcp_upstream_s
+ ngx_stream_lua_socket_tcp_upstream_t;
+
+
+typedef
+ int (*ngx_stream_lua_socket_tcp_retval_handler)(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L);
+
+
+typedef void (*ngx_stream_lua_socket_tcp_upstream_handler_pt)
+ (ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u);
+
+
+typedef struct {
+ ngx_event_t event;
+ ngx_queue_t queue;
+ ngx_str_t host;
+ ngx_stream_lua_cleanup_pt *cleanup;
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+ in_port_t port;
+} ngx_stream_lua_socket_tcp_conn_op_ctx_t;
+
+
+#define ngx_stream_lua_socket_tcp_free_conn_op_ctx(conn_op_ctx) \
+ ngx_free(conn_op_ctx->host.data); \
+ ngx_free(conn_op_ctx)
+
+
+typedef struct {
+ lua_State *lua_vm;
+
+ ngx_int_t size;
+ ngx_queue_t cache_connect_op;
+ ngx_queue_t wait_connect_op;
+
+ /* connections == active connections + pending connect operations,
+ * while active connections == out-of-pool reused connections
+ * + in-pool connections */
+ ngx_int_t connections;
+
+ /* queues of ngx_stream_lua_socket_pool_item_t: */
+ ngx_queue_t cache;
+ ngx_queue_t free;
+
+ ngx_int_t backlog;
+
+ u_char key[1];
+
+} ngx_stream_lua_socket_pool_t;
+
+
+struct ngx_stream_lua_socket_tcp_upstream_s {
+ ngx_stream_lua_socket_tcp_retval_handler read_prepare_retvals;
+ ngx_stream_lua_socket_tcp_retval_handler write_prepare_retvals;
+ ngx_stream_lua_socket_tcp_upstream_handler_pt read_event_handler;
+ ngx_stream_lua_socket_tcp_upstream_handler_pt write_event_handler;
+
+ ngx_stream_lua_socket_pool_t *socket_pool;
+
+ ngx_stream_lua_loc_conf_t *conf;
+ ngx_stream_lua_cleanup_pt *cleanup;
+ ngx_stream_lua_request_t *request;
+
+ ngx_peer_connection_t peer;
+
+ ngx_msec_t read_timeout;
+ ngx_msec_t send_timeout;
+ ngx_msec_t connect_timeout;
+
+ ngx_stream_upstream_resolved_t *resolved;
+
+ ngx_chain_t *bufs_in; /* input data buffers */
+ ngx_chain_t *buf_in; /* last input data buffer */
+ ngx_buf_t buffer; /* receive buffer */
+
+ size_t length;
+ size_t rest;
+
+ ngx_err_t socket_errno;
+
+ ngx_int_t (*input_filter)(void *data, ssize_t bytes);
+ void *input_filter_ctx;
+
+ size_t request_len;
+ ngx_chain_t *request_bufs;
+
+ ngx_stream_lua_co_ctx_t *read_co_ctx;
+ ngx_stream_lua_co_ctx_t *write_co_ctx;
+
+ ngx_uint_t reused;
+
+#if (NGX_STREAM_SSL)
+ ngx_str_t ssl_name;
+#endif
+
+ unsigned ft_type:16;
+ unsigned no_close:1;
+ unsigned conn_waiting:1;
+ unsigned read_waiting:1;
+ unsigned write_waiting:1;
+ unsigned eof:1;
+ unsigned body_downstream:1;
+ unsigned raw_downstream:1;
+ unsigned read_closed:1;
+ unsigned write_closed:1;
+ unsigned conn_closed:1;
+ unsigned read_consumed:1;
+#if (NGX_STREAM_SSL)
+ unsigned ssl_verify:1;
+ unsigned ssl_session_reuse:1;
+#endif
+};
+
+
+typedef struct ngx_stream_lua_dfa_edge_s ngx_stream_lua_dfa_edge_t;
+
+
+struct ngx_stream_lua_dfa_edge_s {
+ ngx_stream_lua_dfa_edge_t *next;
+ int new_state;
+ u_char chr;
+};
+
+
+typedef struct {
+ ngx_stream_lua_socket_tcp_upstream_t *upstream;
+
+ ngx_str_t pattern;
+ ngx_stream_lua_dfa_edge_t **recovering;
+ int state;
+
+ unsigned inclusive:1;
+} ngx_stream_lua_socket_compiled_pattern_t;
+
+
+typedef struct {
+ ngx_stream_lua_socket_pool_t *socket_pool;
+
+ ngx_queue_t queue;
+ ngx_connection_t *connection;
+
+ socklen_t socklen;
+ struct sockaddr_storage sockaddr;
+
+ ngx_uint_t reused;
+
+} ngx_stream_lua_socket_pool_item_t;
+
+
+void ngx_stream_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L);
+void ngx_stream_lua_cleanup_conn_pools(lua_State *L);
+int ngx_stream_lua_req_socket_tcp(lua_State *L);
+
+
+#endif /* _NGX_STREAM_LUA_SOCKET_TCP_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_socket_udp.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_socket_udp.c
new file mode 100644
index 0000000..a951d00
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_socket_udp.c
@@ -0,0 +1,1954 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_socket_udp.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_socket_udp.h"
+#include "ngx_stream_lua_socket_tcp.h"
+#include "ngx_stream_lua_util.h"
+#include "ngx_stream_lua_contentby.h"
+#include "ngx_stream_lua_output.h"
+#include "ngx_stream_lua_probe.h"
+
+
+#if 1
+#undef ngx_stream_lua_probe_info
+#define ngx_stream_lua_probe_info(msg)
+#endif
+
+
+#define UDP_MAX_DATAGRAM_SIZE 8192
+
+
+static int ngx_stream_lua_socket_udp(lua_State *L);
+static int ngx_stream_lua_socket_udp_setpeername(lua_State *L);
+static int ngx_stream_lua_socket_udp_send(lua_State *L);
+static int ngx_stream_lua_socket_udp_receive(lua_State *L);
+static int ngx_stream_lua_socket_udp_settimeout(lua_State *L);
+static void ngx_stream_lua_socket_udp_finalize(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_udp_upstream_t *u);
+static int ngx_stream_lua_socket_udp_upstream_destroy(lua_State *L);
+static int ngx_stream_lua_socket_resolve_retval_handler(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_udp_upstream_t *u,
+ lua_State *L);
+static void ngx_stream_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx);
+static int ngx_stream_lua_socket_error_retval_handler(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_udp_upstream_t *u,
+ lua_State *L);
+static void ngx_stream_lua_socket_udp_handle_error(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_udp_upstream_t *u, ngx_uint_t ft_type);
+static void ngx_stream_lua_socket_udp_cleanup(void *data);
+static void ngx_stream_lua_socket_udp_handler(ngx_event_t *ev);
+static void ngx_stream_lua_socket_dummy_handler(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_udp_upstream_t *u);
+static int ngx_stream_lua_socket_udp_receive_retval_handler(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_udp_upstream_t *u,
+ lua_State *L);
+static ngx_int_t ngx_stream_lua_socket_udp_read(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_udp_upstream_t *u);
+static void ngx_stream_lua_socket_udp_read_handler(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_udp_upstream_t *u);
+static void ngx_stream_lua_socket_udp_handle_success(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_socket_udp_upstream_t *u);
+static ngx_int_t ngx_stream_lua_udp_connect(
+ ngx_stream_lua_udp_connection_t *uc);
+static int ngx_stream_lua_socket_udp_close(lua_State *L);
+static ngx_int_t ngx_stream_lua_socket_udp_resume(ngx_stream_lua_request_t *r);
+static void ngx_stream_lua_udp_resolve_cleanup(void *data);
+static void ngx_stream_lua_udp_socket_cleanup(void *data);
+#ifndef NGX_WIN32
+static ssize_t ngx_stream_lua_udp_sendmsg(ngx_connection_t *c,
+ ngx_iovec_t *vec);
+#endif
+
+
+enum {
+ SOCKET_CTX_INDEX = 1,
+ SOCKET_TIMEOUT_INDEX = 2
+};
+
+
+static char ngx_stream_lua_socket_udp_metatable_key;
+static char ngx_stream_lua_udp_udata_metatable_key;
+static char ngx_stream_lua_socket_udp_raw_req_socket_metatable_key;
+static char ngx_stream_lua_socket_udp_downstream_udata_metatable_key;
+static u_char ngx_stream_lua_socket_udp_buffer[UDP_MAX_DATAGRAM_SIZE];
+
+
+void
+ngx_stream_lua_inject_socket_udp_api(ngx_log_t *log, lua_State *L)
+{
+ lua_getfield(L, -1, "socket"); /* ngx socket */
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_udp);
+ lua_setfield(L, -2, "udp"); /* ngx socket */
+
+ /* udp upstream socket object metatable */
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ socket_udp_metatable_key));
+ lua_createtable(L, 0 /* narr */, 6 /* nrec */);
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_udp_setpeername);
+ lua_setfield(L, -2, "setpeername"); /* ngx socket mt */
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_udp_send);
+ lua_setfield(L, -2, "send");
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_udp_receive);
+ lua_setfield(L, -2, "receive");
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_udp_settimeout);
+ lua_setfield(L, -2, "settimeout"); /* ngx socket mt */
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_udp_close);
+ lua_setfield(L, -2, "close"); /* ngx socket mt */
+
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "__index");
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ /* }}} */
+
+ /* udp downstream socket object metatable */
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ socket_udp_raw_req_socket_metatable_key));
+ lua_createtable(L, 0 /* narr */, 4 /* nrec */);
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_udp_send);
+ lua_setfield(L, -2, "send");
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_udp_receive);
+ lua_setfield(L, -2, "receive");
+
+ lua_pushcfunction(L, ngx_stream_lua_socket_udp_settimeout);
+ lua_setfield(L, -2, "settimeout"); /* ngx socket mt */
+
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "__index");
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ /* }}} */
+
+ /* udp upstream socket object metatable */
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ udp_udata_metatable_key));
+ lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */
+ lua_pushcfunction(L, ngx_stream_lua_socket_udp_upstream_destroy);
+ lua_setfield(L, -2, "__gc");
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ /* }}} */
+
+ /* udp downstream socket object metatable */
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ socket_udp_downstream_udata_metatable_key));
+ lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */
+ /* share the same destructor as upstream */
+ lua_pushcfunction(L, ngx_stream_lua_socket_udp_upstream_destroy);
+ lua_setfield(L, -2, "__gc");
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ /* }}} */
+
+ lua_pop(L, 1);
+}
+
+
+static int
+ngx_stream_lua_socket_udp(lua_State *L)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+
+ if (lua_gettop(L) != 0) {
+ return luaL_error(L, "expecting zero arguments, but got %d",
+ lua_gettop(L));
+ }
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return luaL_error(L, "no ctx found");
+ }
+
+ ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_YIELDABLE);
+
+ lua_createtable(L, 3 /* narr */, 1 /* nrec */);
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ socket_udp_metatable_key));
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ lua_setmetatable(L, -2);
+
+ dd("top: %d", lua_gettop(L));
+
+ return 1;
+}
+
+
+static int
+ngx_stream_lua_socket_udp_setpeername(lua_State *L)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_str_t host;
+ int port;
+ ngx_resolver_ctx_t *rctx, temp;
+ ngx_stream_core_srv_conf_t *clcf;
+ int saved_top;
+ int n;
+ u_char *p;
+ size_t len;
+ ngx_url_t url;
+ ngx_int_t rc;
+ int timeout;
+
+ ngx_stream_lua_loc_conf_t *llcf;
+ ngx_stream_lua_co_ctx_t *coctx;
+ ngx_stream_lua_udp_connection_t *uc;
+ ngx_stream_lua_socket_udp_upstream_t *u;
+
+ /*
+ * TODO: we should probably accept an extra argument to setpeername()
+ * to allow the user bind the datagram unix domain socket himself,
+ * which is necessary for systems without autobind support.
+ */
+
+ n = lua_gettop(L);
+ if (n != 2 && n != 3) {
+ return luaL_error(L, "ngx.socket.udp setpeername: expecting 2 or 3 "
+ "arguments (including the object), but seen %d", n);
+ }
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return luaL_error(L, "no ctx found");
+ }
+
+ ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_YIELDABLE);
+
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ p = (u_char *) luaL_checklstring(L, 2, &len);
+
+ host.data = ngx_palloc(r->pool, len + 1);
+ if (host.data == NULL) {
+ return luaL_error(L, "no memory");
+ }
+
+ host.len = len;
+
+ ngx_memcpy(host.data, p, len);
+ host.data[len] = '\0';
+
+ if (n == 3) {
+ port = luaL_checkinteger(L, 3);
+
+ if (port < 0 || port > 65535) {
+ lua_pushnil(L);
+ lua_pushfstring(L, "bad port number: %d", port);
+ return 2;
+ }
+
+ } else { /* n == 2 */
+ port = 0;
+ }
+
+ lua_rawgeti(L, 1, SOCKET_CTX_INDEX);
+ u = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ if (u) {
+ if (u->request && u->request != r) {
+ return luaL_error(L, "bad request");
+ }
+
+ if (u->waiting) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "socket busy");
+ return 2;
+ }
+
+ if (u->udp_connection.connection) {
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua udp socket reconnect without shutting down");
+
+ ngx_stream_lua_socket_udp_finalize(r, u);
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua reuse socket upstream ctx");
+
+ } else {
+ u = lua_newuserdata(L, sizeof(ngx_stream_lua_socket_udp_upstream_t));
+ if (u == NULL) {
+ return luaL_error(L, "no memory");
+ }
+
+#if 1
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ udp_udata_metatable_key));
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ lua_setmetatable(L, -2);
+#endif
+
+ lua_rawseti(L, 1, SOCKET_CTX_INDEX);
+ }
+
+ ngx_memzero(u, sizeof(ngx_stream_lua_socket_udp_upstream_t));
+
+ u->request = r; /* set the controlling request */
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+
+ u->conf = llcf;
+
+ uc = &u->udp_connection;
+
+ uc->log = *r->connection->log;
+
+ dd("lua peer connection log: %p", &uc->log);
+
+ lua_rawgeti(L, 1, SOCKET_TIMEOUT_INDEX);
+ timeout = (ngx_int_t) lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ if (timeout > 0) {
+ u->read_timeout = (ngx_msec_t) timeout;
+
+ } else {
+ u->read_timeout = u->conf->read_timeout;
+ }
+
+ ngx_memzero(&url, sizeof(ngx_url_t));
+
+ url.url.len = host.len;
+ url.url.data = host.data;
+ url.default_port = (in_port_t) port;
+ url.no_resolve = 1;
+
+ if (ngx_parse_url(r->pool, &url) != NGX_OK) {
+ lua_pushnil(L);
+
+ if (url.err) {
+ lua_pushfstring(L, "failed to parse host name \"%s\": %s",
+ host.data, url.err);
+
+ } else {
+ lua_pushfstring(L, "failed to parse host name \"%s\"", host.data);
+ }
+
+ return 2;
+ }
+
+ u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_stream_upstream_resolved_t));
+ if (u->resolved == NULL) {
+ return luaL_error(L, "no memory");
+ }
+
+ if (url.addrs && url.addrs[0].sockaddr) {
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua udp socket network address given directly");
+
+ u->resolved->sockaddr = url.addrs[0].sockaddr;
+ u->resolved->socklen = url.addrs[0].socklen;
+ u->resolved->naddrs = 1;
+ u->resolved->host = url.addrs[0].name;
+
+ } else {
+ u->resolved->host = host;
+ u->resolved->port = (in_port_t) port;
+ }
+
+ if (u->resolved->sockaddr) {
+ rc = ngx_stream_lua_socket_resolve_retval_handler(r, u, L);
+ if (rc == NGX_AGAIN) {
+ return lua_yield(L, 0);
+ }
+
+ return rc;
+ }
+
+ clcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_core_module);
+
+ temp.name = host;
+ rctx = ngx_resolve_start(clcf->resolver, &temp);
+ if (rctx == NULL) {
+ u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_RESOLVER;
+ lua_pushnil(L);
+ lua_pushliteral(L, "failed to start the resolver");
+ return 2;
+ }
+
+ if (rctx == NGX_NO_RESOLVER) {
+ u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_RESOLVER;
+ lua_pushnil(L);
+ lua_pushfstring(L, "no resolver defined to resolve \"%s\"", host.data);
+ return 2;
+ }
+
+ rctx->name = host;
+ rctx->handler = ngx_stream_lua_socket_resolve_handler;
+ rctx->data = u;
+ rctx->timeout = clcf->resolver_timeout;
+
+ u->co_ctx = ctx->cur_co_ctx;
+ u->resolved->ctx = rctx;
+
+ saved_top = lua_gettop(L);
+
+ coctx = ctx->cur_co_ctx;
+ ngx_stream_lua_cleanup_pending_operation(coctx);
+ coctx->cleanup = ngx_stream_lua_udp_resolve_cleanup;
+
+ if (ngx_resolve_name(rctx) != NGX_OK) {
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua udp socket fail to run resolver immediately");
+
+ u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_RESOLVER;
+
+ u->resolved->ctx = NULL;
+ lua_pushnil(L);
+ lua_pushfstring(L, "%s could not be resolved", host.data);
+
+ return 2;
+ }
+
+ if (u->waiting == 1) {
+ /* resolved and already connecting */
+ return lua_yield(L, 0);
+ }
+
+ n = lua_gettop(L) - saved_top;
+ if (n) {
+ /* errors occurred during resolving or connecting
+ * or already connected */
+ return n;
+ }
+
+ /* still resolving */
+
+ u->waiting = 1;
+ u->prepare_retvals = ngx_stream_lua_socket_resolve_retval_handler;
+
+ coctx->data = u;
+
+ if (ctx->entered_content_phase) {
+ r->write_event_handler = ngx_stream_lua_content_wev_handler;
+
+ } else {
+ r->write_event_handler = ngx_stream_lua_core_run_phases;
+ }
+
+ return lua_yield(L, 0);
+}
+
+
+static void
+ngx_stream_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx)
+{
+ ngx_stream_lua_request_t *r;
+#if (NGX_DEBUG)
+ ngx_connection_t *c;
+#endif
+ lua_State *L;
+ u_char *p;
+ size_t len;
+ socklen_t socklen;
+ struct sockaddr *sockaddr;
+ ngx_uint_t i;
+ unsigned waiting;
+
+ ngx_stream_upstream_resolved_t *ur;
+ ngx_stream_lua_ctx_t *lctx;
+ ngx_stream_lua_socket_udp_upstream_t *u;
+
+ u = ctx->data;
+ r = u->request;
+
+#if (NGX_DEBUG)
+
+ c = r->connection;
+
+#endif
+
+ ur = u->resolved;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "lua udp socket resolve handler");
+
+ lctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (lctx == NULL) {
+ return;
+ }
+
+ lctx->cur_co_ctx = u->co_ctx;
+
+ u->co_ctx->cleanup = NULL;
+
+ L = lctx->cur_co_ctx->co;
+
+ dd("setting socket_ready to 1");
+
+ waiting = u->waiting;
+
+ if (ctx->state) {
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "lua udp socket resolver error: %s (waiting: %d)",
+ ngx_resolver_strerror(ctx->state), (int) u->waiting);
+
+ lua_pushnil(L);
+ lua_pushlstring(L, (char *) ctx->name.data, ctx->name.len);
+ lua_pushfstring(L, " could not be resolved (%d: %s)",
+ (int) ctx->state,
+ ngx_resolver_strerror(ctx->state));
+ lua_concat(L, 2);
+
+#if 1
+ ngx_resolve_name_done(ctx);
+ ur->ctx = NULL;
+#endif
+
+ u->prepare_retvals = ngx_stream_lua_socket_error_retval_handler;
+ ngx_stream_lua_socket_udp_handle_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_RESOLVER);
+
+
+ return;
+ }
+
+ ur->naddrs = ctx->naddrs;
+ ur->addrs = ctx->addrs;
+
+#if (NGX_DEBUG)
+ {
+ u_char text[NGX_SOCKADDR_STRLEN];
+ ngx_str_t addr;
+ ngx_uint_t i;
+
+ addr.data = text;
+
+ for (i = 0; i < ctx->naddrs; i++) {
+ addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr, ur->addrs[i].socklen,
+ text, NGX_SOCKADDR_STRLEN, 0);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "name was resolved to %V", &addr);
+ }
+ }
+#endif
+
+ ngx_stream_lua_assert(ur->naddrs > 0);
+
+ if (ur->naddrs == 1) {
+ i = 0;
+
+ } else {
+ i = ngx_random() % ur->naddrs;
+ }
+
+ dd("selected addr index: %d", (int) i);
+
+ socklen = ur->addrs[i].socklen;
+
+ sockaddr = ngx_palloc(r->pool, socklen);
+ if (sockaddr == NULL) {
+ goto nomem;
+ }
+
+ ngx_memcpy(sockaddr, ur->addrs[i].sockaddr, socklen);
+
+ switch (sockaddr->sa_family) {
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ ((struct sockaddr_in6 *) sockaddr)->sin6_port = htons(ur->port);
+ break;
+#endif
+ default: /* AF_INET */
+ ((struct sockaddr_in *) sockaddr)->sin_port = htons(ur->port);
+ }
+
+ p = ngx_pnalloc(r->pool, NGX_SOCKADDR_STRLEN);
+ if (p == NULL) {
+ goto nomem;
+ }
+
+ len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1);
+ ur->sockaddr = sockaddr;
+ ur->socklen = socklen;
+ ur->host.data = p;
+ ur->host.len = len;
+ ur->naddrs = 1;
+
+ ngx_resolve_name_done(ctx);
+ ur->ctx = NULL;
+
+ u->waiting = 0;
+
+ if (waiting) {
+ lctx->resume_handler = ngx_stream_lua_socket_udp_resume;
+ r->write_event_handler(r);
+
+
+ } else {
+ (void) ngx_stream_lua_socket_resolve_retval_handler(r, u, L);
+ }
+
+ return;
+
+nomem:
+
+ if (ur->ctx) {
+ ngx_resolve_name_done(ctx);
+ ur->ctx = NULL;
+ }
+
+ u->prepare_retvals = ngx_stream_lua_socket_error_retval_handler;
+ ngx_stream_lua_socket_udp_handle_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_NOMEM);
+
+ if (waiting) {
+
+ } else {
+ lua_pushnil(L);
+ lua_pushliteral(L, "no memory");
+ }
+}
+
+
+static int
+ngx_stream_lua_socket_resolve_retval_handler(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_udp_upstream_t *u, lua_State *L)
+{
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx;
+ ngx_connection_t *c;
+ ngx_stream_lua_cleanup_t *cln;
+ ngx_stream_upstream_resolved_t *ur;
+ ngx_int_t rc;
+ ngx_stream_lua_udp_connection_t *uc;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua udp socket resolve retval handler");
+
+ if (u->ft_type & NGX_STREAM_LUA_SOCKET_FT_RESOLVER) {
+ return 2;
+ }
+
+ uc = &u->udp_connection;
+
+ ur = u->resolved;
+
+ if (ur->sockaddr) {
+ uc->sockaddr = ur->sockaddr;
+ uc->socklen = ur->socklen;
+ uc->server = ur->host;
+
+ } else {
+ lua_pushnil(L);
+ lua_pushliteral(L, "resolver not working");
+ return 2;
+ }
+
+ rc = ngx_stream_lua_udp_connect(uc);
+
+ if (rc != NGX_OK) {
+ u->socket_errno = ngx_socket_errno;
+ }
+
+ if (u->cleanup == NULL) {
+ cln = ngx_stream_lua_cleanup_add(r, 0);
+ if (cln == NULL) {
+ u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_ERROR;
+ lua_pushnil(L);
+ lua_pushliteral(L, "no memory");
+ return 2;
+ }
+
+ cln->handler = ngx_stream_lua_socket_udp_cleanup;
+ cln->data = u;
+ u->cleanup = &cln->handler;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua udp socket connect: %i", rc);
+
+ if (rc != NGX_OK) {
+ return ngx_stream_lua_socket_error_retval_handler(r, u, L);
+ }
+
+ /* rc == NGX_OK */
+
+ c = uc->connection;
+
+ c->data = u;
+
+ c->write->handler = NULL;
+ c->read->handler = ngx_stream_lua_socket_udp_handler;
+ c->read->resolver = 0;
+
+ c->pool = r->pool;
+ c->log = r->connection->log;
+ c->read->log = c->log;
+ c->write->log = c->log;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+
+ coctx = ctx->cur_co_ctx;
+
+ coctx->data = u;
+
+ u->read_event_handler = ngx_stream_lua_socket_dummy_handler;
+
+ lua_pushinteger(L, 1);
+ return 1;
+}
+
+
+static int
+ngx_stream_lua_socket_error_retval_handler(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_udp_upstream_t *u, lua_State *L)
+{
+ u_char errstr[NGX_MAX_ERROR_STR];
+ u_char *p;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua udp socket error retval handler");
+
+ if (u->ft_type & NGX_STREAM_LUA_SOCKET_FT_RESOLVER) {
+ return 2;
+ }
+
+ lua_pushnil(L);
+
+ if (u->ft_type & NGX_STREAM_LUA_SOCKET_FT_PARTIALWRITE) {
+ lua_pushliteral(L, "partial write");
+
+ } else if (u->ft_type & NGX_STREAM_LUA_SOCKET_FT_TIMEOUT) {
+ lua_pushliteral(L, "timeout");
+
+ } else if (u->ft_type & NGX_STREAM_LUA_SOCKET_FT_CLOSED) {
+ lua_pushliteral(L, "closed");
+
+ } else if (u->ft_type & NGX_STREAM_LUA_SOCKET_FT_BUFTOOSMALL) {
+ lua_pushliteral(L, "buffer too small");
+
+ } else if (u->ft_type & NGX_STREAM_LUA_SOCKET_FT_NOMEM) {
+ lua_pushliteral(L, "no memory");
+
+ } else {
+
+ if (u->socket_errno) {
+ p = ngx_strerror(u->socket_errno, errstr, sizeof(errstr));
+ /* for compatibility with LuaSocket */
+ ngx_strlow(errstr, errstr, p - errstr);
+ lua_pushlstring(L, (char *) errstr, p - errstr);
+
+ } else {
+ lua_pushliteral(L, "error");
+ }
+ }
+
+ return 2;
+}
+
+
+static int
+ngx_stream_lua_socket_udp_send(lua_State *L)
+{
+ ssize_t n;
+ ngx_stream_lua_request_t *r;
+ u_char *p;
+ size_t len;
+ int type;
+ const char *msg;
+ ngx_str_t query;
+#ifndef NGX_WIN32
+ ngx_iovec_t vec;
+ struct iovec iovs[1];
+#endif
+
+ ngx_stream_lua_socket_udp_upstream_t *u;
+ ngx_stream_lua_loc_conf_t *llcf;
+
+ if (lua_gettop(L) != 2) {
+ return luaL_error(L, "expecting 2 arguments (including the object), "
+ "but got %d", lua_gettop(L));
+ }
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "request object not found");
+ }
+
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ lua_rawgeti(L, 1, SOCKET_CTX_INDEX);
+ u = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ if (u == NULL || u->udp_connection.connection == NULL) {
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+
+ if (llcf->log_socket_errors) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "attempt to send data on a closed socket: u:%p, c:%p",
+ u, u ? u->udp_connection.connection : NULL);
+ }
+
+ lua_pushnil(L);
+ lua_pushliteral(L, "closed");
+ return 2;
+ }
+
+ if (u->request != r) {
+ return luaL_error(L, "bad request");
+ }
+
+ if (u->ft_type) {
+ u->ft_type = 0;
+ }
+
+ if (u->waiting) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "socket busy");
+ return 2;
+ }
+
+ type = lua_type(L, 2);
+ switch (type) {
+ case LUA_TNUMBER:
+ case LUA_TSTRING:
+ lua_tolstring(L, 2, &len);
+ break;
+
+ case LUA_TTABLE:
+ len = ngx_stream_lua_calc_strlen_in_table(L, 2, 2, 1 /* strict */);
+ break;
+
+ case LUA_TNIL:
+ len = sizeof("nil") - 1;
+ break;
+
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 2)) {
+ len = sizeof("true") - 1;
+
+ } else {
+ len = sizeof("false") - 1;
+ }
+
+ break;
+
+ default:
+ msg = lua_pushfstring(L, "string, number, boolean, nil, "
+ "or array table expected, got %s",
+ lua_typename(L, type));
+
+ return luaL_argerror(L, 2, msg);
+ }
+
+ query.data = lua_newuserdata(L, len);
+ query.len = len;
+
+ switch (type) {
+ case LUA_TNUMBER:
+ case LUA_TSTRING:
+ p = (u_char *) lua_tolstring(L, 2, &len);
+ ngx_memcpy(query.data, (u_char *) p, len);
+ break;
+
+ case LUA_TTABLE:
+ (void) ngx_stream_lua_copy_str_in_table(L, 2, query.data);
+ break;
+
+ case LUA_TNIL:
+ p = query.data;
+ *p++ = 'n';
+ *p++ = 'i';
+ *p++ = 'l';
+ break;
+
+ case LUA_TBOOLEAN:
+ p = query.data;
+
+ if (lua_toboolean(L, 2)) {
+ *p++ = 't';
+ *p++ = 'r';
+ *p++ = 'u';
+ *p++ = 'e';
+
+ } else {
+ *p++ = 'f';
+ *p++ = 'a';
+ *p++ = 'l';
+ *p++ = 's';
+ *p++ = 'e';
+ }
+
+ break;
+
+ default:
+ return luaL_error(L, "impossible to reach here");
+ }
+
+ u->ft_type = 0;
+
+ /* mimic ngx_http_upstream_init_request here */
+
+#if 1
+ u->waiting = 0;
+#endif
+
+ dd("sending query %.*s", (int) query.len, query.data);
+#ifdef NGX_WIN32
+ n = ngx_udp_send(u->udp_connection.connection, query.data, query.len);
+ dd("ngx_udp_send returns %d (query len %d)", (int) n, (int) query.len);
+
+#else
+ vec.iovs = iovs;
+ vec.nalloc = 1;
+ vec.count = 1;
+ iovs[0].iov_base = query.data;
+ iovs[0].iov_len = query.len;
+ vec.size = query.len;
+ n = ngx_stream_lua_udp_sendmsg(u->udp_connection.connection, &vec);
+
+ dd("ngx_stream_lua_udp_sendmsg returns %d (query len %d)",
+ (int) n, (int) query.len);
+#endif
+
+ if (n == NGX_ERROR || n == NGX_AGAIN) {
+ u->socket_errno = ngx_socket_errno;
+
+ return ngx_stream_lua_socket_error_retval_handler(r, u, L);
+ }
+
+ if (n != (ssize_t) query.len) {
+ dd("not the while query was sent");
+
+ u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_PARTIALWRITE;
+ return ngx_stream_lua_socket_error_retval_handler(r, u, L);
+ }
+
+ dd("n == len");
+
+ lua_pushinteger(L, 1);
+ return 1;
+}
+
+
+static int
+ngx_stream_lua_socket_udp_receive(lua_State *L)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_int_t rc;
+ size_t size;
+ int nargs;
+
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx;
+ ngx_stream_lua_socket_udp_upstream_t *u;
+ ngx_stream_lua_loc_conf_t *llcf;
+
+ nargs = lua_gettop(L);
+ if (nargs != 1 && nargs != 2) {
+ return luaL_error(L, "expecting 1 or 2 arguments "
+ "(including the object), but got %d", nargs);
+ }
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua udp socket calling receive() method");
+
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ lua_rawgeti(L, 1, SOCKET_CTX_INDEX);
+ u = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ if (u == NULL || u->udp_connection.connection == NULL) {
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+
+ if (llcf->log_socket_errors) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "attempt to receive data on a closed socket: u:%p, "
+ "c:%p", u, u ? u->udp_connection.connection : NULL);
+ }
+
+ lua_pushnil(L);
+ lua_pushliteral(L, "closed");
+ return 2;
+ }
+
+ if (u->request != r) {
+ return luaL_error(L, "bad request");
+ }
+
+ if (u->ft_type) {
+ u->ft_type = 0;
+ }
+
+#if 1
+ if (u->waiting) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "socket busy");
+ return 2;
+ }
+#endif
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua udp socket read timeout: %M", u->read_timeout);
+
+ size = (size_t) luaL_optnumber(L, 2, UDP_MAX_DATAGRAM_SIZE);
+ size = ngx_min(size, UDP_MAX_DATAGRAM_SIZE);
+
+ u->recv_buf_size = size;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua udp socket receive buffer size: %uz", u->recv_buf_size);
+
+ if (u->raw_downstream) {
+ if (ngx_buf_size(r->connection->buffer) > 0) {
+ /* we still have unread data */
+ u->received = ngx_min((size_t) ngx_buf_size(r->connection->buffer),
+ u->recv_buf_size);
+ ngx_memcpy(ngx_stream_lua_socket_udp_buffer,
+ r->connection->buffer->pos, u->received);
+ r->connection->buffer->pos += u->received;
+
+ ngx_stream_lua_socket_udp_handle_success(r, u);
+
+ rc = NGX_OK;
+
+ } else {
+ lua_pushnil(L);
+ lua_pushliteral(L, "no more data");
+ return 2;
+ }
+
+ } else {
+ rc = ngx_stream_lua_socket_udp_read(r, u);
+ }
+
+ if (rc == NGX_ERROR) {
+ dd("read failed: %d", (int) u->ft_type);
+ rc = ngx_stream_lua_socket_udp_receive_retval_handler(r, u, L);
+ dd("udp receive retval returned: %d", (int) rc);
+ return rc;
+ }
+
+ if (rc == NGX_OK) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua udp socket receive done in a single run");
+
+ return ngx_stream_lua_socket_udp_receive_retval_handler(r, u, L);
+ }
+
+ /* n == NGX_AGAIN */
+
+ u->read_event_handler = ngx_stream_lua_socket_udp_read_handler;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return luaL_error(L, "no request ctx found");
+ }
+
+ coctx = ctx->cur_co_ctx;
+
+ ngx_stream_lua_cleanup_pending_operation(coctx);
+ coctx->cleanup = ngx_stream_lua_udp_socket_cleanup;
+ coctx->data = u;
+
+ if (ctx->entered_content_phase) {
+ r->write_event_handler = ngx_stream_lua_content_wev_handler;
+
+ } else {
+ r->write_event_handler = ngx_stream_lua_core_run_phases;
+ }
+
+ u->co_ctx = coctx;
+ u->waiting = 1;
+ u->prepare_retvals = ngx_stream_lua_socket_udp_receive_retval_handler;
+
+ return lua_yield(L, 0);
+}
+
+
+static int
+ngx_stream_lua_socket_udp_receive_retval_handler(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_udp_upstream_t *u, lua_State *L)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua udp socket receive return value handler");
+
+ if (u->ft_type) {
+ return ngx_stream_lua_socket_error_retval_handler(r, u, L);
+ }
+
+ lua_pushlstring(L, (char *) ngx_stream_lua_socket_udp_buffer, u->received);
+ return 1;
+}
+
+
+static int
+ngx_stream_lua_socket_udp_settimeout(lua_State *L)
+{
+ int n;
+ ngx_int_t timeout;
+
+ ngx_stream_lua_socket_udp_upstream_t *u;
+
+ n = lua_gettop(L);
+
+ if (n != 2) {
+ return luaL_error(L, "ngx.socket settimout: expecting at least 2 "
+ "arguments (including the object) but seen %d",
+ lua_gettop(L));
+ }
+
+ timeout = (ngx_int_t) lua_tonumber(L, 2);
+
+ lua_rawseti(L, 1, SOCKET_TIMEOUT_INDEX);
+
+ lua_rawgeti(L, 1, SOCKET_CTX_INDEX);
+ u = lua_touserdata(L, -1);
+
+ if (u) {
+ if (timeout > 0) {
+ u->read_timeout = (ngx_msec_t) timeout;
+
+ } else {
+ u->read_timeout = u->conf->read_timeout;
+ }
+ }
+
+ return 0;
+}
+
+
+static void
+ngx_stream_lua_socket_udp_finalize(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_udp_upstream_t *u)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "stream lua finalize socket");
+
+ if (u->cleanup) {
+ *u->cleanup = NULL;
+ u->cleanup = NULL;
+ }
+
+ if (u->resolved && u->resolved->ctx) {
+ ngx_resolve_name_done(u->resolved->ctx);
+ u->resolved->ctx = NULL;
+ }
+
+ /*
+ * do not close if it is a downstream connection as that will
+ * be handled by stream subsystem itself
+ */
+ if (u->udp_connection.connection && !u->raw_downstream) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua close socket connection");
+
+ ngx_close_connection(u->udp_connection.connection);
+ u->udp_connection.connection = NULL;
+ }
+
+ if (u->waiting) {
+ u->waiting = 0;
+ }
+}
+
+
+static int
+ngx_stream_lua_socket_udp_upstream_destroy(lua_State *L)
+{
+ ngx_stream_lua_socket_udp_upstream_t *u;
+
+ dd("upstream destroy triggered by Lua GC");
+
+ u = lua_touserdata(L, 1);
+ if (u == NULL) {
+ return 0;
+ }
+
+ if (u->cleanup) {
+ ngx_stream_lua_socket_udp_cleanup(u); /* it will clear u->cleanup */
+ }
+
+ return 0;
+}
+
+
+static void
+ngx_stream_lua_socket_dummy_handler(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_udp_upstream_t *u)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua udp socket dummy handler");
+}
+
+
+static ngx_int_t
+ngx_stream_lua_socket_udp_read(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_udp_upstream_t *u)
+{
+ ngx_connection_t *c;
+ ngx_event_t *rev;
+ ssize_t n;
+
+ c = u->udp_connection.connection;
+ rev = c->read;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "lua udp socket read data: waiting: %d", (int) u->waiting);
+
+ n = ngx_udp_recv(u->udp_connection.connection,
+ ngx_stream_lua_socket_udp_buffer, u->recv_buf_size);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "lua udp recv returned %z", n);
+
+ if (n >= 0) {
+ u->received = n;
+ ngx_stream_lua_socket_udp_handle_success(r, u);
+ return NGX_OK;
+ }
+
+ if (n == NGX_ERROR) {
+ u->socket_errno = ngx_socket_errno;
+ ngx_stream_lua_socket_udp_handle_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_ERROR);
+ return NGX_ERROR;
+ }
+
+ /* n == NGX_AGAIN */
+
+#if 1
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_stream_lua_socket_udp_handle_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_ERROR);
+ return NGX_ERROR;
+ }
+#endif
+
+ if (rev->active) {
+ ngx_add_timer(rev, u->read_timeout);
+
+ } else if (rev->timer_set) {
+ ngx_del_timer(rev);
+ }
+
+ return NGX_AGAIN;
+}
+
+
+static void
+ngx_stream_lua_socket_udp_read_handler(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_udp_upstream_t *u)
+{
+ ngx_connection_t *c;
+
+ ngx_stream_lua_loc_conf_t *llcf;
+
+ c = u->udp_connection.connection;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua udp socket read handler");
+
+ if (c->read->timedout) {
+ c->read->timedout = 0;
+
+ llcf = ngx_stream_lua_get_module_loc_conf(r, ngx_stream_lua_module);
+
+ if (llcf->log_socket_errors) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "lua udp socket read timed out");
+ }
+
+ ngx_stream_lua_socket_udp_handle_error(r, u,
+ NGX_STREAM_LUA_SOCKET_FT_TIMEOUT);
+ return;
+ }
+
+#if 1
+ if (c->read->timer_set) {
+ ngx_del_timer(c->read);
+ }
+#endif
+
+ (void) ngx_stream_lua_socket_udp_read(r, u);
+}
+
+
+static void
+ngx_stream_lua_socket_udp_handle_error(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_udp_upstream_t *u, ngx_uint_t ft_type)
+{
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua udp socket handle error");
+
+ u->ft_type |= ft_type;
+
+#if 0
+ ngx_stream_lua_socket_udp_finalize(r, u);
+#endif
+
+ u->read_event_handler = ngx_stream_lua_socket_dummy_handler;
+
+ coctx = u->co_ctx;
+
+ if (coctx) {
+ coctx->cleanup = NULL;
+ }
+
+ if (u->waiting) {
+ u->waiting = 0;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return;
+ }
+
+ ctx->resume_handler = ngx_stream_lua_socket_udp_resume;
+ ctx->cur_co_ctx = coctx;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua udp socket waking up the current request");
+
+ r->write_event_handler(r);
+ }
+}
+
+
+static void
+ngx_stream_lua_socket_udp_cleanup(void *data)
+{
+ ngx_stream_lua_socket_udp_upstream_t *u = data;
+
+ ngx_stream_lua_request_t *r;
+
+ r = u->request;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "cleanup lua udp socket upstream request");
+
+ ngx_stream_lua_socket_udp_finalize(r, u);
+}
+
+
+static void
+ngx_stream_lua_socket_udp_handler(ngx_event_t *ev)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_socket_udp_upstream_t *u;
+ ngx_connection_t *c;
+
+ c = ev->data;
+ u = c->data;
+ r = u->request;
+ c = r->connection;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "lua udp socket handler: wev %d", (int) ev->write);
+
+ u->read_event_handler(r, u);
+
+}
+
+
+static void
+ngx_stream_lua_socket_udp_handle_success(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_udp_upstream_t *u)
+{
+ ngx_stream_lua_ctx_t *ctx;
+
+ u->read_event_handler = ngx_stream_lua_socket_dummy_handler;
+
+ if (u->co_ctx) {
+ u->co_ctx->cleanup = NULL;
+ }
+
+ if (u->waiting) {
+ u->waiting = 0;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return;
+ }
+
+ ctx->resume_handler = ngx_stream_lua_socket_udp_resume;
+ ctx->cur_co_ctx = u->co_ctx;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua udp socket waking up the current request");
+
+ r->write_event_handler(r);
+ }
+}
+
+
+static ngx_int_t
+ngx_stream_lua_udp_connect(ngx_stream_lua_udp_connection_t *uc)
+{
+ int rc;
+ ngx_int_t event;
+ ngx_event_t *rev, *wev;
+ ngx_socket_t s;
+ ngx_connection_t *c;
+
+ s = ngx_socket(uc->sockaddr->sa_family, SOCK_DGRAM, 0);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &uc->log, 0, "UDP socket %d", s);
+
+ if (s == (ngx_socket_t) -1) {
+ ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno,
+ ngx_socket_n " failed");
+
+ return NGX_ERROR;
+ }
+
+ c = ngx_get_connection(s, &uc->log);
+
+ if (c == NULL) {
+ if (ngx_close_socket(s) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno,
+ ngx_close_socket_n "failed");
+ }
+
+ return NGX_ERROR;
+ }
+
+ if (ngx_nonblocking(s) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno,
+ ngx_nonblocking_n " failed");
+
+ ngx_free_connection(c);
+
+ if (ngx_close_socket(s) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno,
+ ngx_close_socket_n " failed");
+ }
+
+ return NGX_ERROR;
+ }
+
+ rev = c->read;
+ wev = c->write;
+
+ rev->log = &uc->log;
+ wev->log = &uc->log;
+
+ uc->connection = c;
+
+ c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
+
+#if (NGX_STREAM_LUA_HAVE_SO_PASSCRED)
+ if (uc->sockaddr->sa_family == AF_UNIX) {
+ struct sockaddr addr;
+
+ addr.sa_family = AF_UNIX;
+
+ /* just to make valgrind happy */
+ ngx_memzero(addr.sa_data, sizeof(addr.sa_data));
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, &uc->log, 0, "datagram unix "
+ "domain socket autobind");
+
+ if (bind(uc->connection->fd, &addr, sizeof(sa_family_t)) != 0) {
+ ngx_log_error(NGX_LOG_CRIT, &uc->log, ngx_socket_errno,
+ "bind() failed");
+
+ return NGX_ERROR;
+ }
+ }
+#endif
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, &uc->log, 0,
+ "connect to %V, fd:%d #%d", &uc->server, s, c->number);
+
+ rc = connect(s, uc->sockaddr, uc->socklen);
+
+ /* TODO: aio, iocp */
+
+ if (rc == -1) {
+ ngx_log_error(NGX_LOG_CRIT, &uc->log, ngx_socket_errno,
+ "connect() failed");
+
+ return NGX_ERROR;
+ }
+
+ /* UDP sockets are always ready to write */
+ wev->ready = 1;
+
+ if (ngx_add_event) {
+
+ event = (ngx_event_flags & NGX_USE_CLEAR_EVENT) ?
+ /* kqueue, epoll */ NGX_CLEAR_EVENT:
+ /* select, poll, /dev/poll */ NGX_LEVEL_EVENT;
+ /* eventport event type has no meaning: oneshot only */
+
+ if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ } else {
+ /* rtsig */
+
+ if (ngx_add_conn(c) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static int
+ngx_stream_lua_socket_udp_close(lua_State *L)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_socket_udp_upstream_t *u;
+
+ if (lua_gettop(L) != 1) {
+ return luaL_error(L, "expecting 1 argument "
+ "(including the object) but seen %d", lua_gettop(L));
+ }
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ lua_rawgeti(L, 1, SOCKET_CTX_INDEX);
+ u = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ if (u == NULL || u->udp_connection.connection == NULL) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "closed");
+ return 2;
+ }
+
+ if (u->request != r) {
+ return luaL_error(L, "bad request");
+ }
+
+ if (u->waiting) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "socket busy");
+ return 2;
+ }
+
+ ngx_stream_lua_socket_udp_finalize(r, u);
+
+ lua_pushinteger(L, 1);
+ return 1;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_socket_udp_resume(ngx_stream_lua_request_t *r)
+{
+ int nret;
+ lua_State *vm;
+ ngx_int_t rc;
+ ngx_uint_t nreqs;
+ ngx_connection_t *c;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx;
+
+ ngx_stream_lua_socket_udp_upstream_t *u;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ctx->resume_handler = ngx_stream_lua_wev_handler;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua udp operation done, resuming lua thread");
+
+ coctx = ctx->cur_co_ctx;
+
+#if 0
+ ngx_stream_lua_probe_info("udp resume");
+#endif
+
+ u = coctx->data;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua udp socket calling prepare retvals handler %p, "
+ "u:%p", u->prepare_retvals, u);
+
+ nret = u->prepare_retvals(r, u, ctx->cur_co_ctx->co);
+ if (nret == NGX_AGAIN) {
+ return NGX_DONE;
+ }
+
+ c = r->connection;
+ vm = ngx_stream_lua_get_lua_vm(r, ctx);
+ nreqs = c->requests;
+
+ rc = ngx_stream_lua_run_thread(vm, r, ctx, nret);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua run thread returned %d", rc);
+
+ if (rc == NGX_AGAIN) {
+ return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs);
+ }
+
+ if (rc == NGX_DONE) {
+ ngx_stream_lua_finalize_request(r, NGX_DONE);
+ return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs);
+ }
+
+ if (ctx->entered_content_phase) {
+ ngx_stream_lua_finalize_request(r, rc);
+ return NGX_DONE;
+ }
+
+ return rc;
+}
+
+
+static void
+ngx_stream_lua_udp_resolve_cleanup(void *data)
+{
+ ngx_resolver_ctx_t *rctx;
+
+ ngx_stream_lua_socket_udp_upstream_t *u;
+ ngx_stream_lua_co_ctx_t *coctx = data;
+
+ u = coctx->data;
+ if (u == NULL) {
+ return;
+ }
+
+ rctx = u->resolved->ctx;
+ if (rctx == NULL) {
+ return;
+ }
+
+ /* postpone free the rctx in the handler */
+ rctx->handler = ngx_resolve_name_done;
+}
+
+
+static void
+ngx_stream_lua_udp_socket_cleanup(void *data)
+{
+ ngx_stream_lua_socket_udp_upstream_t *u;
+ ngx_stream_lua_co_ctx_t *coctx = data;
+
+ u = coctx->data;
+ if (u == NULL) {
+ return;
+ }
+
+ if (u->request == NULL) {
+ return;
+ }
+
+ ngx_stream_lua_socket_udp_finalize(u->request, u);
+}
+
+
+int
+ngx_stream_lua_req_socket_udp(lua_State *L)
+{
+ int n;
+ ngx_stream_lua_udp_connection_t *pc;
+ ngx_stream_lua_srv_conf_t *lscf;
+ ngx_connection_t *c;
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+
+ ngx_stream_lua_cleanup_t *cln;
+ ngx_stream_lua_co_ctx_t *coctx;
+
+ ngx_stream_lua_socket_udp_upstream_t *u;
+
+ n = lua_gettop(L);
+
+ if (n != 0 && n != 1) {
+ return luaL_error(L, "expecting zero arguments, but got %d",
+ lua_gettop(L));
+ }
+
+ if (n == 1) {
+ lua_pop(L, 1);
+ }
+
+ r = ngx_stream_lua_get_req(L);
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return luaL_error(L, "no ctx found");
+ }
+
+ ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT
+ |NGX_STREAM_LUA_CONTEXT_PREREAD);
+
+ c = r->connection;
+
+ if (c->buffered) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "pending data to write");
+ return 2;
+ }
+
+ dd("ctx acquired raw req socket: %d", ctx->acquired_raw_req_socket);
+
+ if (ctx->acquired_raw_req_socket) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "duplicate call");
+ return 2;
+ }
+
+ ctx->acquired_raw_req_socket = 1;
+
+ lua_createtable(L, 3 /* narr */, 1 /* nrec */); /* the object */
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ socket_udp_raw_req_socket_metatable_key));
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ lua_setmetatable(L, -2);
+
+ u = lua_newuserdata(L, sizeof(ngx_stream_lua_socket_udp_upstream_t));
+ if (u == NULL) {
+ return luaL_error(L, "no memory");
+ }
+
+#if 1
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ socket_udp_downstream_udata_metatable_key));
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ lua_setmetatable(L, -2);
+#endif
+
+ lua_rawseti(L, 1, SOCKET_CTX_INDEX);
+
+ ngx_memzero(u, sizeof(ngx_stream_lua_socket_udp_upstream_t));
+
+ u->raw_downstream = 1;
+
+ coctx = ctx->cur_co_ctx;
+
+ u->request = r;
+
+ lscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module);
+
+ u->conf = lscf;
+
+ u->read_timeout = u->conf->read_timeout;
+
+ cln = ngx_stream_lua_cleanup_add(r, 0);
+ if (cln == NULL) {
+ u->ft_type |= NGX_STREAM_LUA_SOCKET_FT_ERROR;
+ lua_pushnil(L);
+ lua_pushliteral(L, "no memory");
+ return 2;
+ }
+
+ cln->handler = ngx_stream_lua_socket_udp_cleanup;
+ cln->data = u;
+ u->cleanup = &cln->handler;
+
+ pc = &u->udp_connection;
+ pc->log = *c->log;
+ pc->connection = c;
+
+ dd("setting data to %p", u);
+
+ coctx->data = u;
+ ctx->downstream = u;
+
+ if (c->read->timer_set) {
+ ngx_del_timer(c->read);
+ }
+
+ if (c->write->timer_set) {
+ ngx_del_timer(c->write);
+ }
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+
+#ifndef NGX_WIN32
+
+static ssize_t
+ngx_stream_lua_udp_sendmsg(ngx_connection_t *c, ngx_iovec_t *vec)
+{
+ ssize_t n;
+ ngx_err_t err;
+ struct msghdr msg;
+
+#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
+
+#if (NGX_HAVE_IP_SENDSRCADDR)
+ u_char msg_control[CMSG_SPACE(sizeof(struct in_addr))];
+#elif (NGX_HAVE_IP_PKTINFO)
+ u_char msg_control[CMSG_SPACE(sizeof(struct in_pktinfo))];
+#endif
+
+#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)
+ u_char msg_control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+#endif
+
+#endif
+
+ ngx_memzero(&msg, sizeof(struct msghdr));
+
+ if (c->socklen) {
+ msg.msg_name = c->sockaddr;
+ msg.msg_namelen = c->socklen;
+ }
+
+ msg.msg_iov = vec->iovs;
+ msg.msg_iovlen = vec->count;
+
+#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
+
+ if (c->listening && c->listening->wildcard && c->local_sockaddr) {
+
+#if (NGX_HAVE_IP_SENDSRCADDR)
+
+ if (c->local_sockaddr->sa_family == AF_INET) {
+ struct cmsghdr *cmsg;
+ struct in_addr *addr;
+ struct sockaddr_in *sin;
+
+ msg.msg_control = &msg_control;
+ msg.msg_controllen = sizeof(msg_control);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = IPPROTO_IP;
+ cmsg->cmsg_type = IP_SENDSRCADDR;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
+
+ sin = (struct sockaddr_in *) c->local_sockaddr;
+
+ addr = (struct in_addr *) CMSG_DATA(cmsg);
+ *addr = sin->sin_addr;
+ }
+
+#elif (NGX_HAVE_IP_PKTINFO)
+
+ if (c->local_sockaddr->sa_family == AF_INET) {
+ struct cmsghdr *cmsg;
+ struct in_pktinfo *pkt;
+ struct sockaddr_in *sin;
+
+ msg.msg_control = &msg_control;
+ msg.msg_controllen = sizeof(msg_control);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = IPPROTO_IP;
+ cmsg->cmsg_type = IP_PKTINFO;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+
+ sin = (struct sockaddr_in *) c->local_sockaddr;
+
+ pkt = (struct in_pktinfo *) CMSG_DATA(cmsg);
+ ngx_memzero(pkt, sizeof(struct in_pktinfo));
+ pkt->ipi_spec_dst = sin->sin_addr;
+ }
+
+#endif
+
+#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)
+
+ if (c->local_sockaddr->sa_family == AF_INET6) {
+ struct cmsghdr *cmsg;
+ struct in6_pktinfo *pkt6;
+ struct sockaddr_in6 *sin6;
+
+ msg.msg_control = &msg_control6;
+ msg.msg_controllen = sizeof(msg_control6);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = IPPROTO_IPV6;
+ cmsg->cmsg_type = IPV6_PKTINFO;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+
+ sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
+
+ pkt6 = (struct in6_pktinfo *) CMSG_DATA(cmsg);
+ ngx_memzero(pkt6, sizeof(struct in6_pktinfo));
+ pkt6->ipi6_addr = sin6->sin6_addr;
+ }
+
+#endif
+ }
+
+#endif
+
+eintr:
+
+ n = sendmsg(c->fd, &msg, 0);
+
+ ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "sendto: fd:%d %z of %uz to \"%V\"",
+ c->fd, n, vec->size, &c->addr_text);
+ if (n == -1) {
+ err = ngx_errno;
+
+ switch (err) {
+ case NGX_EAGAIN:
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
+ "sendmsg() not ready");
+ return NGX_AGAIN;
+
+ case NGX_EINTR:
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
+ "sendmsg() was interrupted");
+ goto eintr;
+
+ default:
+ c->write->error = 1;
+ ngx_connection_error(c, err, "sendmsg() failed");
+ return NGX_ERROR;
+ }
+ }
+
+ return n;
+}
+
+#endif
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_socket_udp.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_socket_udp.h
new file mode 100644
index 0000000..abcc76d
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_socket_udp.h
@@ -0,0 +1,76 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_socket_udp.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_SOCKET_UDP_H_INCLUDED_
+#define _NGX_STREAM_LUA_SOCKET_UDP_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+typedef struct ngx_stream_lua_socket_udp_upstream_s
+ ngx_stream_lua_socket_udp_upstream_t;
+
+
+typedef
+ int (*ngx_stream_lua_socket_udp_retval_handler)(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_socket_udp_upstream_t *u, lua_State *L);
+
+
+typedef void (*ngx_stream_lua_socket_udp_upstream_handler_pt)
+ (ngx_stream_lua_request_t *r, ngx_stream_lua_socket_udp_upstream_t *u);
+
+
+typedef struct {
+ ngx_connection_t *connection;
+ struct sockaddr *sockaddr;
+ socklen_t socklen;
+ ngx_str_t server;
+ ngx_log_t log;
+} ngx_stream_lua_udp_connection_t;
+
+
+struct ngx_stream_lua_socket_udp_upstream_s {
+ ngx_stream_lua_socket_udp_retval_handler prepare_retvals;
+ ngx_stream_lua_socket_udp_upstream_handler_pt read_event_handler;
+
+ ngx_stream_lua_loc_conf_t *conf;
+ ngx_stream_lua_cleanup_pt *cleanup;
+ ngx_stream_lua_request_t *request;
+ ngx_stream_lua_udp_connection_t udp_connection;
+
+ ngx_msec_t read_timeout;
+
+ ngx_stream_upstream_resolved_t *resolved;
+
+ ngx_uint_t ft_type;
+ ngx_err_t socket_errno;
+ size_t received; /* for receive */
+ size_t recv_buf_size;
+
+ ngx_stream_lua_co_ctx_t *co_ctx;
+
+ unsigned waiting:1;
+
+ unsigned raw_downstream:1;
+};
+
+
+void ngx_stream_lua_inject_socket_udp_api(ngx_log_t *log, lua_State *L);
+int ngx_stream_lua_req_socket_udp(lua_State *L);
+
+
+#endif /* _NGX_STREAM_LUA_SOCKET_UDP_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_ssl.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_ssl.c
new file mode 100644
index 0000000..2a6a951
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_ssl.c
@@ -0,0 +1,47 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_ssl.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#if (NGX_STREAM_SSL)
+
+
+int ngx_stream_lua_ssl_ctx_index = -1;
+
+
+ngx_int_t
+ngx_stream_lua_ssl_init(ngx_log_t *log)
+{
+ if (ngx_stream_lua_ssl_ctx_index == -1) {
+ ngx_stream_lua_ssl_ctx_index = SSL_get_ex_new_index(0, NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ if (ngx_stream_lua_ssl_ctx_index == -1) {
+ ngx_ssl_error(NGX_LOG_ALERT, log, 0,
+ "lua: SSL_get_ex_new_index() for ctx failed");
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+#endif /* NGX_STREAM_SSL */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_ssl.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_ssl.h
new file mode 100644
index 0000000..352b709
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_ssl.h
@@ -0,0 +1,61 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_ssl.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_SSL_H_INCLUDED_
+#define _NGX_STREAM_LUA_SSL_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+#if (NGX_STREAM_SSL)
+
+
+typedef struct {
+ ngx_connection_t *connection; /* original true connection */
+ ngx_stream_lua_request_t *request; /* fake request */
+ ngx_pool_cleanup_pt *cleanup;
+
+ ngx_ssl_session_t *session; /* retrurn value for openssl's
+ * session_get_cb */
+
+ ngx_str_t session_id;
+
+ int exit_code; /* exit code for openssl's
+ set_client_hello_cb or
+ set_cert_cb callback */
+
+ int ctx_ref; /* reference to anchor
+ request ctx data in lua
+ registry */
+
+ unsigned done:1;
+ unsigned aborted:1;
+
+ unsigned entered_client_hello_handler:1;
+ unsigned entered_cert_handler:1;
+ unsigned entered_sess_fetch_handler:1;
+} ngx_stream_lua_ssl_ctx_t;
+
+
+ngx_int_t ngx_stream_lua_ssl_init(ngx_log_t *log);
+
+
+extern int ngx_stream_lua_ssl_ctx_index;
+
+
+#endif
+
+
+#endif /* _NGX_STREAM_LUA_SSL_H_INCLUDED_ */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_ssl_certby.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_ssl_certby.c
new file mode 100644
index 0000000..a34e187
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_ssl_certby.c
@@ -0,0 +1,1655 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_ssl_certby.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#if (NGX_STREAM_SSL)
+
+
+#include "ngx_stream_lua_cache.h"
+#include "ngx_stream_lua_initworkerby.h"
+#include "ngx_stream_lua_util.h"
+#include "ngx_stream_ssl_module.h"
+#include "ngx_stream_lua_contentby.h"
+#include "ngx_stream_lua_ssl_certby.h"
+#include "ngx_stream_lua_directive.h"
+#include "ngx_stream_lua_ssl.h"
+
+
+enum {
+ NGX_STREAM_LUA_ADDR_TYPE_UNIX = 0,
+ NGX_STREAM_LUA_ADDR_TYPE_INET = 1,
+ NGX_STREAM_LUA_ADDR_TYPE_INET6 = 2
+};
+
+
+static void ngx_stream_lua_ssl_cert_done(void *data);
+static void ngx_stream_lua_ssl_cert_aborted(void *data);
+static u_char *ngx_stream_lua_log_ssl_cert_error(ngx_log_t *log, u_char *buf,
+ size_t len);
+static ngx_int_t ngx_stream_lua_ssl_cert_by_chunk(lua_State *L,
+ ngx_stream_lua_request_t *r);
+
+
+ngx_int_t
+ngx_stream_lua_ssl_cert_handler_file(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_srv_conf_t *lscf, lua_State *L)
+{
+ ngx_int_t rc;
+
+ rc = ngx_stream_lua_cache_loadfile(r->connection->log, L,
+ lscf->srv.ssl_cert_src.data,
+ lscf->srv.ssl_cert_src_key);
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ /* make sure we have a valid code chunk */
+ ngx_stream_lua_assert(lua_isfunction(L, -1));
+
+ return ngx_stream_lua_ssl_cert_by_chunk(L, r);
+}
+
+
+ngx_int_t
+ngx_stream_lua_ssl_cert_handler_inline(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_srv_conf_t *lscf, lua_State *L)
+{
+ ngx_int_t rc;
+
+ rc = ngx_stream_lua_cache_loadbuffer(r->connection->log, L,
+ lscf->srv.ssl_cert_src.data,
+ lscf->srv.ssl_cert_src.len,
+ lscf->srv.ssl_cert_src_key,
+ "=ssl_certificate_by_lua");
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ /* make sure we have a valid code chunk */
+ ngx_stream_lua_assert(lua_isfunction(L, -1));
+
+ return ngx_stream_lua_ssl_cert_by_chunk(L, r);
+}
+
+
+char *
+ngx_stream_lua_ssl_cert_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ char *rv;
+ ngx_conf_t save;
+
+ save = *cf;
+ cf->handler = ngx_stream_lua_ssl_cert_by_lua;
+ cf->handler_conf = conf;
+
+ rv = ngx_stream_lua_conf_lua_block_parse(cf, cmd);
+
+ *cf = save;
+
+ return rv;
+}
+
+
+char *
+ngx_stream_lua_ssl_cert_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+#if OPENSSL_VERSION_NUMBER < 0x1000205fL
+
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "at least OpenSSL 1.0.2e required but found "
+ OPENSSL_VERSION_TEXT);
+
+ return NGX_CONF_ERROR;
+
+#else
+
+ u_char *p;
+ u_char *name;
+ ngx_str_t *value;
+ ngx_stream_lua_srv_conf_t *lscf = conf;
+
+ /* must specify a concrete handler */
+ if (cmd->post == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (lscf->srv.ssl_cert_handler) {
+ return "is duplicate";
+ }
+
+ if (ngx_stream_lua_ssl_init(cf->log) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ value = cf->args->elts;
+
+ lscf->srv.ssl_cert_handler = (ngx_stream_lua_srv_conf_handler_pt) cmd->post;
+
+ if (cmd->post == ngx_stream_lua_ssl_cert_handler_file) {
+ /* Lua code in an external file */
+
+ name = ngx_stream_lua_rebase_path(cf->pool, value[1].data,
+ value[1].len);
+ if (name == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ lscf->srv.ssl_cert_src.data = name;
+ lscf->srv.ssl_cert_src.len = ngx_strlen(name);
+
+ p = ngx_palloc(cf->pool, NGX_STREAM_LUA_FILE_KEY_LEN + 1);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ lscf->srv.ssl_cert_src_key = p;
+
+ p = ngx_copy(p, NGX_STREAM_LUA_FILE_TAG, NGX_STREAM_LUA_FILE_TAG_LEN);
+ p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len);
+ *p = '\0';
+
+ } else {
+ /* inlined Lua code */
+
+ lscf->srv.ssl_cert_src = value[1];
+
+ p = ngx_palloc(cf->pool,
+ sizeof("ssl_certificate_by_lua") +
+ NGX_STREAM_LUA_INLINE_KEY_LEN);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ lscf->srv.ssl_cert_src_key = p;
+
+ p = ngx_copy(p, "ssl_certificate_by_lua",
+ sizeof("ssl_certificate_by_lua") - 1);
+ p = ngx_copy(p, NGX_STREAM_LUA_INLINE_TAG, NGX_STREAM_LUA_INLINE_TAG_LEN);
+ p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len);
+ *p = '\0';
+ }
+
+ return NGX_CONF_OK;
+
+#endif /* OPENSSL_VERSION_NUMBER < 0x1000205fL */
+}
+
+
+int
+ngx_stream_lua_ssl_cert_handler(ngx_ssl_conn_t *ssl_conn, void *data)
+{
+ lua_State *L;
+ ngx_int_t rc;
+ ngx_connection_t *c, *fc;
+ ngx_stream_lua_request_t *r = NULL;
+ ngx_pool_cleanup_t *cln;
+ ngx_stream_lua_srv_conf_t *lscf;
+ ngx_stream_lua_ssl_ctx_t *cctx;
+ ngx_stream_core_srv_conf_t *cscf;
+ ngx_stream_session_t *s, *fs;
+
+ c = ngx_ssl_get_connection(ssl_conn);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "stream ssl cert: connection reusable: %ud", c->reusable);
+
+ cctx = ngx_stream_lua_ssl_get_ctx(c->ssl->connection);
+
+ dd("ssl cert handler, cert-ctx=%p", cctx);
+
+ if (cctx && cctx->entered_cert_handler) {
+ /* not the first time */
+
+ if (cctx->done) {
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "stream lua_certificate_by_lua:"
+ " cert cb exit code: %d",
+ cctx->exit_code);
+
+ dd("lua ssl cert done, finally");
+ return cctx->exit_code;
+ }
+
+ return -1;
+ }
+
+ dd("first time");
+
+ ngx_reusable_connection(c, 0);
+
+ s = c->data;
+
+ fc = ngx_stream_lua_create_fake_connection(NULL);
+ if (fc == NULL) {
+ goto failed;
+ }
+
+ fc->log->handler = ngx_stream_lua_log_ssl_cert_error;
+ fc->log->data = fc;
+
+ fc->addr_text = c->addr_text;
+ fc->listening = c->listening;
+
+ fs = ngx_stream_lua_create_fake_session(fc);
+ if (fs == NULL) {
+ goto failed;
+ }
+
+ fs->main_conf = s->main_conf;
+ fs->srv_conf = s->srv_conf;
+
+ r = ngx_stream_lua_create_fake_request(fs);
+ if (r == NULL) {
+ goto failed;
+ }
+
+ fc->log->file = c->log->file;
+ fc->log->log_level = c->log->log_level;
+ fc->ssl = c->ssl;
+
+ cscf = ngx_stream_get_module_srv_conf(fs, ngx_stream_core_module);
+
+#if defined(nginx_version) && nginx_version >= 1009000
+ ngx_set_connection_log(fc, cscf->error_log);
+
+#else
+# error "stream ssl_cert_by_lua only supports nginx >= 1.13.0"
+#endif
+
+ if (cctx == NULL) {
+ cctx = ngx_pcalloc(c->pool, sizeof(ngx_stream_lua_ssl_ctx_t));
+ if (cctx == NULL) {
+ goto failed; /* error */
+ }
+
+ cctx->ctx_ref = LUA_NOREF;
+ }
+
+ cctx->exit_code = 1; /* successful by default */
+ cctx->connection = c;
+ cctx->request = r;
+ cctx->entered_cert_handler = 1;
+ cctx->done = 0;
+
+ dd("setting cctx");
+
+ if (SSL_set_ex_data(c->ssl->connection, ngx_stream_lua_ssl_ctx_index,
+ cctx) == 0)
+ {
+ ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_ex_data() failed");
+ goto failed;
+ }
+
+ lscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module);
+
+ /* TODO honor lua_code_cache off */
+ L = ngx_stream_lua_get_lua_vm(r, NULL);
+
+ c->log->action = "loading SSL certificate by lua";
+
+ if (lscf->srv.ssl_cert_handler == NULL) {
+
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "no ssl_certificate_by_lua* defined in "
+ "server %s:%ui", &cscf->file_name, &cscf->line);
+
+ goto failed;
+ }
+
+ rc = lscf->srv.ssl_cert_handler(r, lscf, L);
+
+ if (rc >= NGX_OK || rc == NGX_ERROR) {
+ cctx->done = 1;
+
+ if (cctx->cleanup) {
+ *cctx->cleanup = NULL;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "stream lua_certificate_by_lua:"
+ " handler return value: %i, "
+ "cert cb exit code: %d", rc, cctx->exit_code);
+
+ c->log->action = "SSL handshaking";
+ return cctx->exit_code;
+ }
+
+ /* rc == NGX_DONE */
+
+ cln = ngx_pool_cleanup_add(fc->pool, 0);
+ if (cln == NULL) {
+ goto failed;
+ }
+
+ cln->handler = ngx_stream_lua_ssl_cert_done;
+ cln->data = cctx;
+
+ if (cctx->cleanup == NULL) {
+ cln = ngx_pool_cleanup_add(c->pool, 0);
+ if (cln == NULL) {
+ goto failed;
+ }
+
+ cln->data = cctx;
+ cctx->cleanup = &cln->handler;
+ }
+
+ *cctx->cleanup = ngx_stream_lua_ssl_cert_aborted;
+
+ return -1;
+
+#if 1
+failed:
+
+ if (r && r->pool) {
+ ngx_stream_lua_free_fake_request(r);
+ }
+
+ if (fc) {
+ ngx_stream_lua_close_fake_connection(fc);
+ }
+
+ return 0;
+#endif
+}
+
+
+static void
+ngx_stream_lua_ssl_cert_done(void *data)
+{
+ ngx_connection_t *c;
+ ngx_stream_lua_ssl_ctx_t *cctx = data;
+
+ dd("lua ssl cert done");
+
+ if (cctx->aborted) {
+ return;
+ }
+
+ ngx_stream_lua_assert(cctx->done == 0);
+
+ cctx->done = 1;
+
+ if (cctx->cleanup) {
+ *cctx->cleanup = NULL;
+ }
+
+ c = cctx->connection;
+
+ c->log->action = "SSL handshaking";
+
+ ngx_post_event(c->write, &ngx_posted_events);
+}
+
+
+static void
+ngx_stream_lua_ssl_cert_aborted(void *data)
+{
+ ngx_stream_lua_ssl_ctx_t *cctx = data;
+
+ dd("lua ssl cert done");
+
+ if (cctx->done) {
+ /* completed successfully already */
+ return;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, cctx->connection->log, 0,
+ "stream lua_certificate_by_lua: cert cb aborted");
+
+ cctx->aborted = 1;
+ cctx->request->connection->ssl = NULL;
+
+ ngx_stream_lua_finalize_fake_request(cctx->request, NGX_ERROR);
+}
+
+
+static u_char *
+ngx_stream_lua_log_ssl_cert_error(ngx_log_t *log, u_char *buf, size_t len)
+{
+ u_char *p;
+ ngx_connection_t *c;
+
+ if (log->action) {
+ p = ngx_snprintf(buf, len, " while %s", log->action);
+ len -= p - buf;
+ buf = p;
+ }
+
+ p = ngx_snprintf(buf, len, ", context: ssl_certificate_by_lua*");
+ len -= p - buf;
+ buf = p;
+
+ c = log->data;
+
+ if (c != NULL) {
+ if (c->addr_text.len) {
+ p = ngx_snprintf(buf, len, ", client: %V", &c->addr_text);
+ len -= p - buf;
+ buf = p;
+ }
+
+ if (c->listening && c->listening->addr_text.len) {
+ p = ngx_snprintf(buf, len, ", server: %V",
+ &c->listening->addr_text);
+ /* len -= p - buf; */
+ buf = p;
+ }
+ }
+
+ return buf;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_ssl_cert_by_chunk(lua_State *L, ngx_stream_lua_request_t *r)
+{
+ int co_ref;
+ ngx_int_t rc;
+ lua_State *co;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_cleanup_t *cln;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+
+ if (ctx == NULL) {
+ ctx = ngx_stream_lua_create_ctx(r->session);
+ if (ctx == NULL) {
+ rc = NGX_ERROR;
+ ngx_stream_lua_finalize_request(r, rc);
+ return rc;
+ }
+
+ } else {
+ dd("reset ctx");
+ ngx_stream_lua_reset_ctx(r, L, ctx);
+ }
+
+ ctx->entered_content_phase = 1;
+
+ /* {{{ new coroutine to handle request */
+ co = ngx_stream_lua_new_thread(r, L, &co_ref);
+
+ if (co == NULL) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "stream failed to create new"
+ " coroutine to handle request");
+
+ rc = NGX_ERROR;
+ ngx_stream_lua_finalize_request(r, rc);
+ return rc;
+ }
+
+ /* move code closure to new coroutine */
+ lua_xmove(L, co, 1);
+
+#ifndef OPENRESTY_LUAJIT
+ /* set closure's env table to new coroutine's globals table */
+ ngx_stream_lua_get_globals_table(co);
+ lua_setfenv(co, -2);
+#endif
+
+ /* save nginx request in coroutine globals table */
+ ngx_stream_lua_set_req(co, r);
+
+ ctx->cur_co_ctx = &ctx->entry_co_ctx;
+ ctx->cur_co_ctx->co = co;
+ ctx->cur_co_ctx->co_ref = co_ref;
+#ifdef NGX_LUA_USE_ASSERT
+ ctx->cur_co_ctx->co_top = 1;
+#endif
+
+ ngx_stream_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx);
+
+ /* register request cleanup hooks */
+ if (ctx->cleanup == NULL) {
+ cln = ngx_stream_lua_cleanup_add(r, 0);
+ if (cln == NULL) {
+ rc = NGX_ERROR;
+ ngx_stream_lua_finalize_request(r, rc);
+ return rc;
+ }
+
+ cln->handler = ngx_stream_lua_request_cleanup_handler;
+ cln->data = ctx;
+ ctx->cleanup = &cln->handler;
+ }
+
+ ctx->context = NGX_STREAM_LUA_CONTEXT_SSL_CERT;
+
+ rc = ngx_stream_lua_run_thread(L, r, ctx, 0);
+
+ if (rc == NGX_ERROR || rc >= NGX_OK) {
+ /* do nothing */
+
+ } else if (rc == NGX_AGAIN) {
+ rc = ngx_stream_lua_content_run_posted_threads(L, r, ctx, 0);
+
+ } else if (rc == NGX_DONE) {
+ rc = ngx_stream_lua_content_run_posted_threads(L, r, ctx, 1);
+
+ } else {
+ rc = NGX_OK;
+ }
+
+ ngx_stream_lua_finalize_request(r, rc);
+ return rc;
+}
+
+
+int
+ngx_stream_lua_ffi_ssl_get_tls1_version(ngx_stream_lua_request_t *r, char **err)
+{
+ ngx_ssl_conn_t *ssl_conn;
+
+ if (r->connection == NULL || r->connection->ssl == NULL) {
+ *err = "bad request";
+ return NGX_ERROR;
+ }
+
+ ssl_conn = r->connection->ssl->connection;
+ if (ssl_conn == NULL) {
+ *err = "bad ssl conn";
+ return NGX_ERROR;
+ }
+
+ dd("tls1 ver: %d", SSL_version(ssl_conn));
+
+ return SSL_version(ssl_conn);
+}
+
+
+int
+ngx_stream_lua_ffi_ssl_clear_certs(ngx_stream_lua_request_t *r, char **err)
+{
+#ifdef LIBRESSL_VERSION_NUMBER
+
+ *err = "LibreSSL not supported";
+ return NGX_ERROR;
+
+#else
+
+# if OPENSSL_VERSION_NUMBER < 0x1000205fL
+
+ *err = "at least OpenSSL 1.0.2e required but found " OPENSSL_VERSION_TEXT;
+ return NGX_ERROR;
+
+# else
+
+ ngx_ssl_conn_t *ssl_conn;
+
+ if (r->connection == NULL || r->connection->ssl == NULL) {
+ *err = "bad request";
+ return NGX_ERROR;
+ }
+
+ ssl_conn = r->connection->ssl->connection;
+ if (ssl_conn == NULL) {
+ *err = "bad ssl conn";
+ return NGX_ERROR;
+ }
+
+ SSL_certs_clear(ssl_conn);
+ return NGX_OK;
+
+# endif /* OPENSSL_VERSION_NUMBER < 0x1000205fL */
+#endif
+}
+
+
+int
+ngx_stream_lua_ffi_ssl_set_der_certificate(ngx_stream_lua_request_t *r,
+ const char *data, size_t len, char **err)
+{
+#ifdef LIBRESSL_VERSION_NUMBER
+
+ *err = "LibreSSL not supported";
+ return NGX_ERROR;
+
+#else
+
+# if OPENSSL_VERSION_NUMBER < 0x1000205fL
+
+ *err = "at least OpenSSL 1.0.2e required but found " OPENSSL_VERSION_TEXT;
+ return NGX_ERROR;
+
+# else
+
+ BIO *bio = NULL;
+ X509 *x509 = NULL;
+ ngx_ssl_conn_t *ssl_conn;
+
+ if (r->connection == NULL || r->connection->ssl == NULL) {
+ *err = "bad request";
+ return NGX_ERROR;
+ }
+
+ ssl_conn = r->connection->ssl->connection;
+ if (ssl_conn == NULL) {
+ *err = "bad ssl conn";
+ return NGX_ERROR;
+ }
+
+ bio = BIO_new_mem_buf((char *) data, len);
+ if (bio == NULL) {
+ *err = "BIO_new_mem_buf() failed";
+ goto failed;
+ }
+
+ x509 = d2i_X509_bio(bio, NULL);
+ if (x509 == NULL) {
+ *err = "d2i_X509_bio() failed";
+ goto failed;
+ }
+
+ if (SSL_use_certificate(ssl_conn, x509) == 0) {
+ *err = "SSL_use_certificate() failed";
+ goto failed;
+ }
+
+#if 0
+ if (SSL_set_ex_data(ssl_conn, ngx_ssl_certificate_index, x509) == 0) {
+ *err = "SSL_set_ex_data() failed";
+ goto failed;
+ }
+#endif
+
+ X509_free(x509);
+ x509 = NULL;
+
+ /* read rest of the chain */
+
+ while (!BIO_eof(bio)) {
+
+ x509 = d2i_X509_bio(bio, NULL);
+ if (x509 == NULL) {
+ *err = "d2i_X509_bio() failed";
+ goto failed;
+ }
+
+ if (SSL_add0_chain_cert(ssl_conn, x509) == 0) {
+ *err = "SSL_add0_chain_cert() failed";
+ goto failed;
+ }
+ }
+
+ BIO_free(bio);
+
+ *err = NULL;
+ return NGX_OK;
+
+failed:
+
+ if (bio) {
+ BIO_free(bio);
+ }
+
+ if (x509) {
+ X509_free(x509);
+ }
+
+ ERR_clear_error();
+
+ return NGX_ERROR;
+
+# endif /* OPENSSL_VERSION_NUMBER < 0x1000205fL */
+#endif
+}
+
+
+int
+ngx_stream_lua_ffi_ssl_set_der_private_key(ngx_stream_lua_request_t *r,
+ const char *data, size_t len, char **err)
+{
+ BIO *bio = NULL;
+ EVP_PKEY *pkey = NULL;
+ ngx_ssl_conn_t *ssl_conn;
+
+ if (r->connection == NULL || r->connection->ssl == NULL) {
+ *err = "bad request";
+ return NGX_ERROR;
+ }
+
+ ssl_conn = r->connection->ssl->connection;
+ if (ssl_conn == NULL) {
+ *err = "bad ssl conn";
+ return NGX_ERROR;
+ }
+
+ bio = BIO_new_mem_buf((char *) data, len);
+ if (bio == NULL) {
+ *err = "BIO_new_mem_buf() failed";
+ goto failed;
+ }
+
+ pkey = d2i_PrivateKey_bio(bio, NULL);
+ if (pkey == NULL) {
+ *err = "d2i_PrivateKey_bio() failed";
+ goto failed;
+ }
+
+ if (SSL_use_PrivateKey(ssl_conn, pkey) == 0) {
+ *err = "SSL_use_PrivateKey() failed";
+ goto failed;
+ }
+
+ EVP_PKEY_free(pkey);
+ BIO_free(bio);
+
+ return NGX_OK;
+
+failed:
+
+ if (pkey) {
+ EVP_PKEY_free(pkey);
+ }
+
+ if (bio) {
+ BIO_free(bio);
+ }
+
+ ERR_clear_error();
+
+ return NGX_ERROR;
+}
+
+
+int
+ngx_stream_lua_ffi_ssl_raw_server_addr(ngx_stream_lua_request_t *r, char **addr,
+ size_t *addrlen, int *addrtype, char **err)
+{
+#if (NGX_HAVE_UNIX_DOMAIN)
+ struct sockaddr_un *saun;
+#endif
+ ngx_ssl_conn_t *ssl_conn;
+ ngx_connection_t *c;
+ struct sockaddr_in *sin;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+
+ if (r->connection == NULL || r->connection->ssl == NULL) {
+ *err = "bad request";
+ return NGX_ERROR;
+ }
+
+ ssl_conn = r->connection->ssl->connection;
+ if (ssl_conn == NULL) {
+ *err = "bad ssl conn";
+ return NGX_ERROR;
+ }
+
+ c = ngx_ssl_get_connection(ssl_conn);
+
+ if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
+ return 0;
+ }
+
+ switch (c->local_sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
+ *addrlen = 16;
+ *addr = (char *) &sin6->sin6_addr.s6_addr;
+ *addrtype = NGX_STREAM_LUA_ADDR_TYPE_INET6;
+
+ break;
+#endif
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+ case AF_UNIX:
+ saun = (struct sockaddr_un *) c->local_sockaddr;
+
+ /* on Linux sockaddr might not include sun_path at all */
+ if (c->local_socklen <= (socklen_t)
+ offsetof(struct sockaddr_un, sun_path))
+ {
+ *addr = "";
+ *addrlen = 0;
+
+ } else {
+ *addr = saun->sun_path;
+ *addrlen = ngx_strlen(saun->sun_path);
+ }
+
+ *addrtype = NGX_STREAM_LUA_ADDR_TYPE_UNIX;
+ break;
+#endif
+
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) c->local_sockaddr;
+ *addr = (char *) &sin->sin_addr.s_addr;
+ *addrlen = 4;
+ *addrtype = NGX_STREAM_LUA_ADDR_TYPE_INET;
+ break;
+ }
+
+ return NGX_OK;
+}
+
+
+int
+ngx_stream_lua_ffi_ssl_server_name(ngx_stream_lua_request_t *r, char **name,
+ size_t *namelen, char **err)
+{
+ ngx_ssl_conn_t *ssl_conn;
+
+ if (r->connection == NULL || r->connection->ssl == NULL) {
+ *err = "bad request";
+ return NGX_ERROR;
+ }
+
+ ssl_conn = r->connection->ssl->connection;
+ if (ssl_conn == NULL) {
+ *err = "bad ssl conn";
+ return NGX_ERROR;
+ }
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+
+ *name = (char *) SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name);
+
+ if (*name) {
+ *namelen = ngx_strlen(*name);
+ return NGX_OK;
+ }
+
+ return NGX_DECLINED;
+
+#else
+
+ *err = "no TLS extension support";
+ return NGX_ERROR;
+
+#endif
+}
+
+
+int
+ngx_stream_lua_ffi_ssl_server_port(ngx_stream_lua_request_t *r,
+ unsigned short *server_port, char **err)
+{
+ ngx_ssl_conn_t *ssl_conn;
+ ngx_connection_t *c;
+
+ if (r->connection == NULL || r->connection->ssl == NULL) {
+ *err = "bad request";
+ return NGX_ERROR;
+ }
+
+ ssl_conn = r->connection->ssl->connection;
+ if (ssl_conn == NULL) {
+ *err = "bad ssl conn";
+ return NGX_ERROR;
+ }
+
+ c = ngx_ssl_get_connection(ssl_conn);
+
+ switch (c->local_sockaddr->sa_family) {
+
+ case AF_UNIX:
+ *err = "unix domain has no port";
+ return NGX_ERROR;
+
+ default:
+ *server_port = (unsigned short) ngx_inet_get_port(c->local_sockaddr);
+ return NGX_OK;
+ }
+}
+
+
+int
+ngx_stream_lua_ffi_ssl_raw_client_addr(ngx_stream_lua_request_t *r, char **addr,
+ size_t *addrlen, int *addrtype, char **err)
+{
+#if (NGX_HAVE_UNIX_DOMAIN)
+ struct sockaddr_un *saun;
+#endif
+ ngx_ssl_conn_t *ssl_conn;
+ ngx_connection_t *c;
+ struct sockaddr_in *sin;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+
+ if (r->connection == NULL || r->connection->ssl == NULL) {
+ *err = "bad request";
+ return NGX_ERROR;
+ }
+
+ ssl_conn = r->connection->ssl->connection;
+ if (ssl_conn == NULL) {
+ *err = "bad ssl conn";
+ return NGX_ERROR;
+ }
+
+ c = ngx_ssl_get_connection(ssl_conn);
+
+ switch (c->sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) c->sockaddr;
+ *addrlen = 16;
+ *addr = (char *) &sin6->sin6_addr.s6_addr;
+ *addrtype = NGX_STREAM_LUA_ADDR_TYPE_INET6;
+
+ break;
+#endif
+
+# if (NGX_HAVE_UNIX_DOMAIN)
+ case AF_UNIX:
+ saun = (struct sockaddr_un *)c->sockaddr;
+ /* on Linux sockaddr might not include sun_path at all */
+ if (c->socklen <= (socklen_t) offsetof(struct sockaddr_un, sun_path)) {
+ *addr = "";
+ *addrlen = 0;
+
+ } else {
+ *addr = saun->sun_path;
+ *addrlen = ngx_strlen(saun->sun_path);
+ }
+
+ *addrtype = NGX_STREAM_LUA_ADDR_TYPE_UNIX;
+ break;
+#endif
+
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) c->sockaddr;
+ *addr = (char *) &sin->sin_addr.s_addr;
+ *addrlen = 4;
+ *addrtype = NGX_STREAM_LUA_ADDR_TYPE_INET;
+ break;
+ }
+
+ return NGX_OK;
+}
+
+
+int
+ngx_stream_lua_ffi_cert_pem_to_der(const u_char *pem, size_t pem_len,
+ u_char *der, char **err)
+{
+ int total, len;
+ BIO *bio;
+ X509 *x509;
+ u_long n;
+
+ bio = BIO_new_mem_buf((char *) pem, (int) pem_len);
+ if (bio == NULL) {
+ *err = "BIO_new_mem_buf() failed";
+ ERR_clear_error();
+ return NGX_ERROR;
+ }
+
+ x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
+ if (x509 == NULL) {
+ *err = "PEM_read_bio_X509_AUX() failed";
+ BIO_free(bio);
+ ERR_clear_error();
+ return NGX_ERROR;
+ }
+
+ total = i2d_X509(x509, &der);
+ if (total < 0) {
+ *err = "i2d_X509() failed";
+ X509_free(x509);
+ BIO_free(bio);
+ ERR_clear_error();
+ return NGX_ERROR;
+ }
+
+ X509_free(x509);
+
+ /* read rest of the chain */
+
+ for ( ;; ) {
+
+ x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+ if (x509 == NULL) {
+ n = ERR_peek_last_error();
+
+ if (ERR_GET_LIB(n) == ERR_LIB_PEM
+ && ERR_GET_REASON(n) == PEM_R_NO_START_LINE)
+ {
+ /* end of file */
+ ERR_clear_error();
+ break;
+ }
+
+ /* some real error */
+
+ *err = "PEM_read_bio_X509() failed";
+ BIO_free(bio);
+ ERR_clear_error();
+ return NGX_ERROR;
+ }
+
+ len = i2d_X509(x509, &der);
+ if (len < 0) {
+ *err = "i2d_X509() failed";
+ X509_free(x509);
+ BIO_free(bio);
+ ERR_clear_error();
+ return NGX_ERROR;
+ }
+
+ total += len;
+
+ X509_free(x509);
+ }
+
+ BIO_free(bio);
+
+ return total;
+}
+
+
+int
+ngx_stream_lua_ffi_priv_key_pem_to_der(const u_char *pem, size_t pem_len,
+ const u_char *passphrase, u_char *der, char **err)
+{
+ int len;
+ BIO *in;
+ EVP_PKEY *pkey;
+
+ in = BIO_new_mem_buf((char *) pem, (int) pem_len);
+ if (in == NULL) {
+ *err = "BIO_new_mem_buf() failed";
+ ERR_clear_error();
+ return NGX_ERROR;
+ }
+
+ pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, (void *) passphrase);
+ if (pkey == NULL) {
+ BIO_free(in);
+ *err = "PEM_read_bio_PrivateKey() failed";
+ ERR_clear_error();
+ return NGX_ERROR;
+ }
+
+ BIO_free(in);
+
+ len = i2d_PrivateKey(pkey, &der);
+ if (len < 0) {
+ EVP_PKEY_free(pkey);
+ *err = "i2d_PrivateKey() failed";
+ ERR_clear_error();
+ return NGX_ERROR;
+ }
+
+ EVP_PKEY_free(pkey);
+
+ return len;
+}
+
+
+void *
+ngx_stream_lua_ffi_parse_pem_cert(const u_char *pem, size_t pem_len,
+ char **err)
+{
+ BIO *bio;
+ X509 *x509;
+ u_long n;
+ STACK_OF(X509) *chain;
+
+ bio = BIO_new_mem_buf((char *) pem, (int) pem_len);
+ if (bio == NULL) {
+ *err = "BIO_new_mem_buf() failed";
+ ERR_clear_error();
+ return NULL;
+ }
+
+ x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
+ if (x509 == NULL) {
+ *err = "PEM_read_bio_X509_AUX() failed";
+ BIO_free(bio);
+ ERR_clear_error();
+ return NULL;
+ }
+
+ chain = sk_X509_new_null();
+ if (chain == NULL) {
+ *err = "sk_X509_new_null() failed";
+ X509_free(x509);
+ BIO_free(bio);
+ ERR_clear_error();
+ return NULL;
+ }
+
+ if (sk_X509_push(chain, x509) == 0) {
+ *err = "sk_X509_push() failed";
+ sk_X509_free(chain);
+ X509_free(x509);
+ BIO_free(bio);
+ ERR_clear_error();
+ return NULL;
+ }
+
+ /* read rest of the chain */
+
+ for ( ;; ) {
+
+ x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+ if (x509 == NULL) {
+ n = ERR_peek_last_error();
+
+ if (ERR_GET_LIB(n) == ERR_LIB_PEM
+ && ERR_GET_REASON(n) == PEM_R_NO_START_LINE)
+ {
+ /* end of file */
+ ERR_clear_error();
+ break;
+ }
+
+ /* some real error */
+
+ *err = "PEM_read_bio_X509() failed";
+ sk_X509_pop_free(chain, X509_free);
+ BIO_free(bio);
+ ERR_clear_error();
+ return NULL;
+ }
+
+ if (sk_X509_push(chain, x509) == 0) {
+ *err = "sk_X509_push() failed";
+ sk_X509_pop_free(chain, X509_free);
+ X509_free(x509);
+ BIO_free(bio);
+ ERR_clear_error();
+ return NULL;
+ }
+ }
+
+ BIO_free(bio);
+
+ return chain;
+}
+
+
+void *
+ngx_stream_lua_ffi_parse_der_cert(const char *data, size_t len,
+ char **err)
+{
+ BIO *bio;
+ X509 *x509;
+ STACK_OF(X509) *chain;
+
+ bio = BIO_new_mem_buf((char *) data, len);
+ if (bio == NULL) {
+ *err = "BIO_new_mem_buf() failed";
+ ERR_clear_error();
+ return NULL;
+ }
+
+ x509 = d2i_X509_bio(bio, NULL);
+ if (x509 == NULL) {
+ *err = "d2i_X509_bio() failed";
+ BIO_free(bio);
+ ERR_clear_error();
+ return NULL;
+ }
+
+ chain = sk_X509_new_null();
+ if (chain == NULL) {
+ *err = "sk_X509_new_null() failed";
+ X509_free(x509);
+ BIO_free(bio);
+ ERR_clear_error();
+ return NULL;
+ }
+
+ if (sk_X509_push(chain, x509) == 0) {
+ *err = "sk_X509_push() failed";
+ sk_X509_free(chain);
+ X509_free(x509);
+ BIO_free(bio);
+ ERR_clear_error();
+ return NULL;
+ }
+
+ /* read rest of the chain */
+
+ while (!BIO_eof(bio)) {
+ x509 = d2i_X509_bio(bio, NULL);
+ if (x509 == NULL) {
+ *err = "d2i_X509_bio() failed in rest of chain";
+ sk_X509_pop_free(chain, X509_free);
+ BIO_free(bio);
+ ERR_clear_error();
+ return NULL;
+ }
+
+ if (sk_X509_push(chain, x509) == 0) {
+ *err = "sk_X509_push() failed in rest of chain";
+ sk_X509_pop_free(chain, X509_free);
+ X509_free(x509);
+ BIO_free(bio);
+ ERR_clear_error();
+ return NULL;
+ }
+ }
+
+ BIO_free(bio);
+
+ return chain;
+}
+
+
+void
+ngx_stream_lua_ffi_free_cert(void *cdata)
+{
+ STACK_OF(X509) *chain = cdata;
+
+ sk_X509_pop_free(chain, X509_free);
+}
+
+
+void *
+ngx_stream_lua_ffi_parse_pem_priv_key(const u_char *pem, size_t pem_len,
+ char **err)
+{
+ BIO *in;
+ EVP_PKEY *pkey;
+
+ in = BIO_new_mem_buf((char *) pem, (int) pem_len);
+ if (in == NULL) {
+ *err = "BIO_new_mem_buf() failed";
+ ERR_clear_error();
+ return NULL;
+ }
+
+ pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
+ if (pkey == NULL) {
+ *err = "PEM_read_bio_PrivateKey() failed";
+ BIO_free(in);
+ ERR_clear_error();
+ return NULL;
+ }
+
+ BIO_free(in);
+
+ return pkey;
+}
+
+
+void *
+ngx_stream_lua_ffi_parse_der_priv_key(const char *data, size_t len,
+ char **err)
+{
+ BIO *bio = NULL;
+ EVP_PKEY *pkey = NULL;
+
+ bio = BIO_new_mem_buf((char *) data, len);
+ if (bio == NULL) {
+ *err = "BIO_new_mem_buf() failed";
+ BIO_free(bio);
+ ERR_clear_error();
+ return NULL;
+ }
+
+ pkey = d2i_PrivateKey_bio(bio, NULL);
+ if (pkey == NULL) {
+ *err = "d2i_PrivateKey_bio() failed";
+ BIO_free(bio);
+ ERR_clear_error();
+ return NULL;
+ }
+
+ BIO_free(bio);
+
+ return pkey;
+}
+
+
+void
+ngx_stream_lua_ffi_free_priv_key(void *cdata)
+{
+ EVP_PKEY *pkey = cdata;
+
+ EVP_PKEY_free(pkey);
+}
+
+
+int
+ngx_stream_lua_ffi_set_cert(ngx_stream_lua_request_t *r,
+ void *cdata, char **err)
+{
+#ifdef LIBRESSL_VERSION_NUMBER
+
+ *err = "LibreSSL not supported";
+ return NGX_ERROR;
+
+#else
+
+# if OPENSSL_VERSION_NUMBER < 0x1000205fL
+
+ *err = "at least OpenSSL 1.0.2e required but found " OPENSSL_VERSION_TEXT;
+ return NGX_ERROR;
+
+# else
+
+#ifdef OPENSSL_IS_BORINGSSL
+ size_t i;
+#else
+ int i;
+#endif
+ X509 *x509 = NULL;
+ ngx_ssl_conn_t *ssl_conn;
+ STACK_OF(X509) *chain = cdata;
+
+ if (r->connection == NULL || r->connection->ssl == NULL) {
+ *err = "bad request";
+ return NGX_ERROR;
+ }
+
+ ssl_conn = r->connection->ssl->connection;
+ if (ssl_conn == NULL) {
+ *err = "bad ssl conn";
+ return NGX_ERROR;
+ }
+
+ if (sk_X509_num(chain) < 1) {
+ *err = "invalid certificate chain";
+ goto failed;
+ }
+
+ x509 = sk_X509_value(chain, 0);
+ if (x509 == NULL) {
+ *err = "sk_X509_value() failed";
+ goto failed;
+ }
+
+ if (SSL_use_certificate(ssl_conn, x509) == 0) {
+ *err = "SSL_use_certificate() failed";
+ goto failed;
+ }
+
+ x509 = NULL;
+
+ /* read rest of the chain */
+
+ for (i = 1; i < sk_X509_num(chain); i++) {
+
+ x509 = sk_X509_value(chain, i);
+ if (x509 == NULL) {
+ *err = "sk_X509_value() failed";
+ goto failed;
+ }
+
+ if (SSL_add1_chain_cert(ssl_conn, x509) == 0) {
+ *err = "SSL_add1_chain_cert() failed";
+ goto failed;
+ }
+ }
+
+ *err = NULL;
+ return NGX_OK;
+
+failed:
+
+ ERR_clear_error();
+
+ return NGX_ERROR;
+
+# endif /* OPENSSL_VERSION_NUMBER < 0x1000205fL */
+#endif
+}
+
+
+int
+ngx_stream_lua_ffi_set_priv_key(ngx_stream_lua_request_t *r,
+ void *cdata, char **err)
+{
+ EVP_PKEY *pkey = NULL;
+ ngx_ssl_conn_t *ssl_conn;
+
+ if (r->connection == NULL || r->connection->ssl == NULL) {
+ *err = "bad request";
+ return NGX_ERROR;
+ }
+
+ ssl_conn = r->connection->ssl->connection;
+ if (ssl_conn == NULL) {
+ *err = "bad ssl conn";
+ return NGX_ERROR;
+ }
+
+ pkey = cdata;
+ if (pkey == NULL) {
+ *err = "invalid private key failed";
+ goto failed;
+ }
+
+ if (SSL_use_PrivateKey(ssl_conn, pkey) == 0) {
+ *err = "SSL_use_PrivateKey() failed";
+ goto failed;
+ }
+
+ return NGX_OK;
+
+failed:
+
+ ERR_clear_error();
+
+ return NGX_ERROR;
+}
+
+
+#ifndef LIBRESSL_VERSION_NUMBER
+static int
+ngx_stream_lua_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store)
+{
+ /*
+ * we never terminate handshake here and user can later use
+ * $ssl_client_verify to check verification result.
+ *
+ * this is consistent with Nginx behavior.
+ */
+ return 1;
+}
+#endif
+
+
+int
+ngx_stream_lua_ffi_ssl_verify_client(ngx_stream_lua_request_t *r,
+ void *client_cert, void *trusted_certs, int depth, char **err)
+{
+#ifdef LIBRESSL_VERSION_NUMBER
+
+ *err = "LibreSSL not supported";
+ return NGX_ERROR;
+
+#else
+
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_ssl_conn_t *ssl_conn;
+#if defined(nginx_version) && nginx_version >= 1025005
+ ngx_stream_ssl_srv_conf_t *sscf;
+#else
+ ngx_stream_ssl_conf_t *sscf;
+#endif
+ STACK_OF(X509) *client_chain = client_cert;
+ STACK_OF(X509) *trusted_chain = trusted_certs;
+ STACK_OF(X509_NAME) *name_chain = NULL;
+ X509 *x509 = NULL;
+ X509_NAME *subject = NULL;
+ X509_STORE *ca_store = NULL;
+#ifdef OPENSSL_IS_BORINGSSL
+ size_t i;
+#else
+ int i;
+#endif
+
+ ctx = ngx_stream_get_module_ctx(r->session, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ *err = "no request ctx found";
+ return NGX_ERROR;
+ }
+
+ if (!(ctx->context & NGX_STREAM_LUA_CONTEXT_SSL_CERT)) {
+ *err = "API disabled in the current context";
+ return NGX_ERROR;
+ }
+
+ if (r->connection == NULL || r->connection->ssl == NULL) {
+ *err = "bad request";
+ return NGX_ERROR;
+ }
+
+ ssl_conn = r->connection->ssl->connection;
+ if (ssl_conn == NULL) {
+ *err = "bad ssl conn";
+ return NGX_ERROR;
+ }
+
+ /* enable verify */
+
+ SSL_set_verify(ssl_conn, SSL_VERIFY_PEER,
+ ngx_stream_lua_ssl_verify_callback);
+
+ /* set depth */
+
+ if (depth < 0) {
+ sscf = ngx_stream_get_module_srv_conf(r->session,
+ ngx_stream_ssl_module);
+ if (sscf != NULL) {
+ depth = sscf->verify_depth;
+
+ } else {
+ /* same as the default value of ssl_verify_depth */
+ depth = 1;
+ }
+ }
+
+ SSL_set_verify_depth(ssl_conn, depth);
+
+ /* set CA chain */
+
+ if (client_chain != NULL || trusted_chain != NULL) {
+
+ ca_store = X509_STORE_new();
+ if (ca_store == NULL) {
+ *err = "X509_STORE_new() failed";
+ return NGX_ERROR;
+ }
+
+ if (client_chain != NULL) {
+
+ /* construct name chain */
+ name_chain = sk_X509_NAME_new_null();
+ if (name_chain == NULL) {
+ *err = "sk_X509_NAME_new_null() failed";
+ goto failed;
+ }
+
+ for (i = 0; i < sk_X509_num(client_chain); i++) {
+ x509 = sk_X509_value(client_chain, i);
+ if (x509 == NULL) {
+ *err = "sk_X509_value() failed";
+ goto failed;
+ }
+
+ /* add subject to name chain, which will be sent to client */
+ subject = X509_NAME_dup(X509_get_subject_name(x509));
+ if (subject == NULL) {
+ *err = "X509_get_subject_name() failed";
+ goto failed;
+ }
+
+ if (!sk_X509_NAME_push(name_chain, subject)) {
+ *err = "sk_X509_NAME_push() failed";
+ X509_NAME_free(subject);
+ goto failed;
+ }
+
+ /* add to trusted CA store */
+ if (X509_STORE_add_cert(ca_store, x509) == 0) {
+ *err = "X509_STORE_add_cert() failed";
+ goto failed;
+ }
+ }
+
+ /* clean subject name list, and set it for send to client */
+ SSL_set_client_CA_list(ssl_conn, name_chain);
+ }
+
+ if (trusted_chain != NULL) {
+ for (i = 0; i < sk_X509_num(trusted_chain); i++) {
+ x509 = sk_X509_value(trusted_chain, i);
+ if (x509 == NULL) {
+ *err = "sk_X509_value() failed";
+ goto failed;
+ }
+
+ /* add to trusted CA store */
+ if (X509_STORE_add_cert(ca_store, x509) == 0) {
+ *err = "X509_STORE_add_cert() failed";
+ goto failed;
+ }
+ }
+ }
+
+ /* clean ca_store, and store new ca_store */
+ if (SSL_set0_verify_cert_store(ssl_conn, ca_store) == 0) {
+ *err = "SSL_set0_verify_cert_store() failed";
+ goto failed;
+ }
+ }
+
+ return NGX_OK;
+
+failed:
+
+ sk_X509_NAME_free(name_chain);
+
+ X509_STORE_free(ca_store);
+
+ return NGX_ERROR;
+#endif
+}
+
+
+int
+ngx_stream_lua_ffi_ssl_client_random(ngx_stream_lua_request_t *r,
+ unsigned char *out, size_t *outlen, char **err)
+{
+ ngx_ssl_conn_t *ssl_conn;
+
+ if (r->connection == NULL || r->connection->ssl == NULL) {
+ *err = "bad request";
+ return NGX_ERROR;
+ }
+
+ ssl_conn = r->connection->ssl->connection;
+ if (ssl_conn == NULL) {
+ *err = "bad ssl conn";
+ return NGX_ERROR;
+ }
+
+ *outlen = SSL_get_client_random(ssl_conn, out, *outlen);
+
+ return NGX_OK;
+}
+
+
+#endif /* NGX_STREAM_SSL */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_ssl_certby.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_ssl_certby.h
new file mode 100644
index 0000000..9762341
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_ssl_certby.h
@@ -0,0 +1,45 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_ssl_certby.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_SSL_CERTBY_H_INCLUDED_
+#define _NGX_STREAM_LUA_SSL_CERTBY_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+#if (NGX_STREAM_SSL)
+
+
+ngx_int_t ngx_stream_lua_ssl_cert_handler_inline(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_srv_conf_t *lscf, lua_State *L);
+
+ngx_int_t ngx_stream_lua_ssl_cert_handler_file(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_srv_conf_t *lscf, lua_State *L);
+
+char *ngx_stream_lua_ssl_cert_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+char *ngx_stream_lua_ssl_cert_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+int ngx_stream_lua_ssl_cert_handler(ngx_ssl_conn_t *ssl_conn, void *data);
+
+
+#endif /* NGX_STREAM_SSL */
+
+
+#endif /* _NGX_STREAM_LUA_SSL_CERTBY_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_ssl_client_helloby.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_ssl_client_helloby.c
new file mode 100644
index 0000000..57b5913
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_ssl_client_helloby.c
@@ -0,0 +1,718 @@
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#if (NGX_STREAM_SSL)
+
+#include "ngx_stream_lua_cache.h"
+#include "ngx_stream_lua_initworkerby.h"
+#include "ngx_stream_lua_util.h"
+#include "ngx_stream_ssl_module.h"
+#include "ngx_stream_lua_contentby.h"
+#include "ngx_stream_lua_ssl_certby.h"
+#include "ngx_stream_lua_ssl_client_helloby.h"
+#include "ngx_stream_lua_directive.h"
+#include "ngx_stream_lua_ssl.h"
+
+
+static void ngx_stream_lua_ssl_client_hello_done(void *data);
+static void ngx_stream_lua_ssl_client_hello_aborted(void *data);
+static u_char *ngx_stream_lua_log_ssl_client_hello_error(ngx_log_t *log,
+ u_char *buf, size_t len);
+static ngx_int_t ngx_stream_lua_ssl_client_hello_by_chunk(lua_State *L,
+ ngx_stream_lua_request_t *r);
+
+
+ngx_int_t
+ngx_stream_lua_ssl_client_hello_handler_file(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_srv_conf_t *lscf, lua_State *L)
+{
+ ngx_int_t rc;
+
+ rc = ngx_stream_lua_cache_loadfile(r->connection->log, L,
+ lscf->srv.ssl_client_hello_src.data,
+ lscf->srv.ssl_client_hello_src_key);
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ /* make sure we have a valid code chunk */
+ ngx_stream_lua_assert(lua_isfunction(L, -1));
+
+ return ngx_stream_lua_ssl_client_hello_by_chunk(L, r);
+}
+
+
+ngx_int_t
+ngx_stream_lua_ssl_client_hello_handler_inline(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_srv_conf_t *lscf, lua_State *L)
+{
+ ngx_int_t rc;
+
+ rc = ngx_stream_lua_cache_loadbuffer(r->connection->log, L,
+ lscf->srv.ssl_client_hello_src.data,
+ lscf->srv.ssl_client_hello_src.len,
+ lscf->srv.ssl_client_hello_src_key,
+ "=ssl_client_hello_by_lua");
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ /* make sure we have a valid code chunk */
+ ngx_stream_lua_assert(lua_isfunction(L, -1));
+
+ return ngx_stream_lua_ssl_client_hello_by_chunk(L, r);
+}
+
+
+char *
+ngx_stream_lua_ssl_client_hello_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ char *rv;
+ ngx_conf_t save;
+
+ save = *cf;
+ cf->handler = ngx_stream_lua_ssl_client_hello_by_lua;
+ cf->handler_conf = conf;
+
+ rv = ngx_stream_lua_conf_lua_block_parse(cf, cmd);
+
+ *cf = save;
+
+ return rv;
+}
+
+
+char *
+ngx_stream_lua_ssl_client_hello_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+#ifndef SSL_ERROR_WANT_CLIENT_HELLO_CB
+
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "at least OpenSSL 1.1.1 required but found "
+ OPENSSL_VERSION_TEXT);
+
+ return NGX_CONF_ERROR;
+
+#else
+
+ u_char *p;
+ u_char *name;
+ ngx_str_t *value;
+ ngx_stream_lua_srv_conf_t *lscf = conf;
+
+ /* must specify a concrete handler */
+ if (cmd->post == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (lscf->srv.ssl_client_hello_handler) {
+ return "is duplicate";
+ }
+
+ if (ngx_stream_lua_ssl_init(cf->log) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ value = cf->args->elts;
+
+ lscf->srv.ssl_client_hello_handler =
+ (ngx_stream_lua_srv_conf_handler_pt) cmd->post;
+
+ if (cmd->post == ngx_stream_lua_ssl_client_hello_handler_file) {
+ /* Lua code in an external file */
+
+ name = ngx_stream_lua_rebase_path(cf->pool, value[1].data,
+ value[1].len);
+ if (name == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ lscf->srv.ssl_client_hello_src.data = name;
+ lscf->srv.ssl_client_hello_src.len = ngx_strlen(name);
+
+ p = ngx_palloc(cf->pool, NGX_STREAM_LUA_FILE_KEY_LEN + 1);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ lscf->srv.ssl_client_hello_src_key = p;
+
+ p = ngx_copy(p, NGX_STREAM_LUA_FILE_TAG, NGX_STREAM_LUA_FILE_TAG_LEN);
+ p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len);
+ *p = '\0';
+
+ } else {
+ /* inlined Lua code */
+
+ lscf->srv.ssl_client_hello_src = value[1];
+
+ p = ngx_palloc(cf->pool,
+ sizeof("ssl_client_hello_by_lua") +
+ NGX_STREAM_LUA_INLINE_KEY_LEN);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ lscf->srv.ssl_client_hello_src_key = p;
+
+ p = ngx_copy(p, "ssl_client_hello_by_lua",
+ sizeof("ssl_client_hello_by_lua") - 1);
+ p = ngx_copy(p, NGX_STREAM_LUA_INLINE_TAG,
+ NGX_STREAM_LUA_INLINE_TAG_LEN);
+ p = ngx_stream_lua_digest_hex(p, value[1].data, value[1].len);
+ *p = '\0';
+ }
+
+ return NGX_CONF_OK;
+
+#endif /* NO SSL_ERROR_WANT_CLIENT_HELLO_CB */
+}
+
+
+int
+ngx_stream_lua_ssl_client_hello_handler(ngx_ssl_conn_t *ssl_conn,
+ int *al, void *arg)
+{
+ lua_State *L;
+ ngx_int_t rc;
+ ngx_connection_t *c, *fc;
+ ngx_stream_lua_request_t *r = NULL;
+ ngx_pool_cleanup_t *cln;
+ ngx_stream_lua_srv_conf_t *lscf;
+ ngx_stream_lua_ssl_ctx_t *cctx;
+ ngx_stream_core_srv_conf_t *cscf;
+ ngx_stream_session_t *s, *fs;
+
+ c = ngx_ssl_get_connection(ssl_conn);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "stream ssl client hello: connection reusable: %ud",
+ c->reusable);
+
+ cctx = ngx_stream_lua_ssl_get_ctx(c->ssl->connection);
+
+ dd("ssl client hello handler, client-hello-ctx=%p", cctx);
+
+ if (cctx && cctx->entered_client_hello_handler) {
+ /* not the first time */
+
+ if (cctx->done) {
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "stream lua_client_hello_by_lua:"
+ " client hello cb exit code: %d",
+ cctx->exit_code);
+
+ dd("lua ssl client hello done, finally");
+ return cctx->exit_code;
+ }
+
+ return -1;
+ }
+
+ dd("first time");
+
+ ngx_reusable_connection(c, 0);
+
+ s = c->data;
+
+ fc = ngx_stream_lua_create_fake_connection(NULL);
+ if (fc == NULL) {
+ goto failed;
+ }
+
+ fc->log->handler = ngx_stream_lua_log_ssl_client_hello_error;
+ fc->log->data = fc;
+
+ fc->addr_text = c->addr_text;
+ fc->listening = c->listening;
+
+ fs = ngx_stream_lua_create_fake_session(fc);
+ if (fs == NULL) {
+ goto failed;
+ }
+
+ fs->main_conf = s->main_conf;
+ fs->srv_conf = s->srv_conf;
+
+ r = ngx_stream_lua_create_fake_request(fs);
+ if (r == NULL) {
+ goto failed;
+ }
+
+ fc->log->file = c->log->file;
+ fc->log->log_level = c->log->log_level;
+ fc->ssl = c->ssl;
+
+ cscf = ngx_stream_get_module_srv_conf(fs, ngx_stream_core_module);
+
+#if defined(nginx_version) && nginx_version >= 1009000
+ ngx_set_connection_log(fc, cscf->error_log);
+
+#else
+#error "stream ssl_client_hello_by_lua only supports nginx >= 1.19.3"
+#endif
+
+ if (cctx == NULL) {
+ cctx = ngx_pcalloc(c->pool, sizeof(ngx_stream_lua_ssl_ctx_t));
+ if (cctx == NULL) {
+ goto failed; /* error */
+ }
+
+ cctx->ctx_ref = LUA_NOREF;
+ }
+
+ cctx->exit_code = 1; /* successful by default */
+ cctx->connection = c;
+ cctx->request = r;
+ cctx->entered_client_hello_handler = 1;
+ cctx->done = 0;
+
+ dd("setting cctx");
+
+ if (SSL_set_ex_data(c->ssl->connection, ngx_stream_lua_ssl_ctx_index,
+ cctx) == 0)
+ {
+ ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_ex_data() failed");
+ goto failed;
+ }
+
+ lscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module);
+
+ /* TODO honor lua_code_cache off */
+ L = ngx_stream_lua_get_lua_vm(r, NULL);
+
+ c->log->action = "loading SSL client hello by lua";
+
+ if (lscf->srv.ssl_client_hello_handler == NULL) {
+
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "no ssl_client_hello_by_lua* defined in "
+ "server %s:%ui", &cscf->file_name, &cscf->line);
+
+ goto failed;
+ }
+
+ rc = lscf->srv.ssl_client_hello_handler(r, lscf, L);
+
+ if (rc >= NGX_OK || rc == NGX_ERROR) {
+ cctx->done = 1;
+
+ if (cctx->cleanup) {
+ *cctx->cleanup = NULL;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "stream lua_client_hello_by_lua:"
+ " handler return value: %i, "
+ "client hello cb exit code: %d", rc, cctx->exit_code);
+
+ c->log->action = "SSL handshaking";
+ return cctx->exit_code;
+ }
+
+ /* rc == NGX_DONE */
+
+ cln = ngx_pool_cleanup_add(fc->pool, 0);
+ if (cln == NULL) {
+ goto failed;
+ }
+
+ cln->handler = ngx_stream_lua_ssl_client_hello_done;
+ cln->data = cctx;
+
+ if (cctx->cleanup == NULL) {
+ cln = ngx_pool_cleanup_add(c->pool, 0);
+ if (cln == NULL) {
+ goto failed;
+ }
+
+ cln->data = cctx;
+ cctx->cleanup = &cln->handler;
+ }
+
+ *cctx->cleanup = ngx_stream_lua_ssl_client_hello_aborted;
+
+ return -1;
+
+#if 1
+failed:
+
+ if (r && r->pool) {
+ ngx_stream_lua_free_fake_request(r);
+ }
+
+ if (fc) {
+ ngx_stream_lua_close_fake_connection(fc);
+ }
+
+ return 0;
+#endif
+}
+
+
+static void
+ngx_stream_lua_ssl_client_hello_done(void *data)
+{
+ ngx_connection_t *c;
+ ngx_stream_lua_ssl_ctx_t *cctx = data;
+
+ dd("lua ssl client hello done");
+
+ if (cctx->aborted) {
+ return;
+ }
+
+ ngx_stream_lua_assert(cctx->done == 0);
+
+ cctx->done = 1;
+
+ if (cctx->cleanup) {
+ *cctx->cleanup = NULL;
+ }
+
+ c = cctx->connection;
+
+ c->log->action = "SSL handshaking";
+
+ ngx_post_event(c->write, &ngx_posted_events);
+}
+
+
+static void
+ngx_stream_lua_ssl_client_hello_aborted(void *data)
+{
+ ngx_stream_lua_ssl_ctx_t *cctx = data;
+
+ dd("lua ssl client hello done");
+
+ if (cctx->done) {
+ /* completed successfully already */
+ return;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, cctx->connection->log, 0,
+ "stream lua_client_hello_by_lua: client hello cb aborted");
+
+ cctx->aborted = 1;
+ cctx->request->connection->ssl = NULL;
+
+ ngx_stream_lua_finalize_fake_request(cctx->request, NGX_ERROR);
+}
+
+
+static u_char *
+ngx_stream_lua_log_ssl_client_hello_error(ngx_log_t *log, u_char *buf,
+ size_t len)
+{
+ u_char *p;
+ ngx_connection_t *c;
+
+ if (log->action) {
+ p = ngx_snprintf(buf, len, " while %s", log->action);
+ len -= p - buf;
+ buf = p;
+ }
+
+ p = ngx_snprintf(buf, len, ", context: ssl_client_hello_by_lua*");
+ len -= p - buf;
+ buf = p;
+
+ c = log->data;
+
+ if (c && c->addr_text.len) {
+ p = ngx_snprintf(buf, len, ", client: %V", &c->addr_text);
+ len -= p - buf;
+ buf = p;
+ }
+
+ if (c && c->listening && c->listening->addr_text.len) {
+ p = ngx_snprintf(buf, len, ", server: %V", &c->listening->addr_text);
+ /* len -= p - buf; */
+ buf = p;
+ }
+
+ return buf;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_ssl_client_hello_by_chunk(lua_State *L,
+ ngx_stream_lua_request_t *r)
+{
+ int co_ref;
+ ngx_int_t rc;
+ lua_State *co;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_cleanup_t *cln;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+
+ if (ctx == NULL) {
+ ctx = ngx_stream_lua_create_ctx(r->session);
+ if (ctx == NULL) {
+ rc = NGX_ERROR;
+ ngx_stream_lua_finalize_request(r, rc);
+ return rc;
+ }
+
+ } else {
+ dd("reset ctx");
+ ngx_stream_lua_reset_ctx(r, L, ctx);
+ }
+
+ ctx->entered_content_phase = 1;
+
+ /* {{{ new coroutine to handle request */
+ co = ngx_stream_lua_new_thread(r, L, &co_ref);
+
+ if (co == NULL) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "stream failed to create new"
+ " coroutine to handle request");
+
+ rc = NGX_ERROR;
+ ngx_stream_lua_finalize_request(r, rc);
+ return rc;
+ }
+
+ /* move code closure to new coroutine */
+ lua_xmove(L, co, 1);
+
+#ifndef OPENRESTY_LUAJIT
+ /* set closure's env table to new coroutine's globals table */
+ ngx_stream_lua_get_globals_table(co);
+ lua_setfenv(co, -2);
+#endif
+
+ /* save nginx request in coroutine globals table */
+ ngx_stream_lua_set_req(co, r);
+
+ ctx->cur_co_ctx = &ctx->entry_co_ctx;
+ ctx->cur_co_ctx->co = co;
+ ctx->cur_co_ctx->co_ref = co_ref;
+#ifdef NGX_LUA_USE_ASSERT
+ ctx->cur_co_ctx->co_top = 1;
+#endif
+
+ ngx_stream_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx);
+
+ /* register request cleanup hooks */
+ if (ctx->cleanup == NULL) {
+ cln = ngx_stream_lua_cleanup_add(r, 0);
+ if (cln == NULL) {
+ rc = NGX_ERROR;
+ ngx_stream_lua_finalize_request(r, rc);
+ return rc;
+ }
+
+ cln->handler = ngx_stream_lua_request_cleanup_handler;
+ cln->data = ctx;
+ ctx->cleanup = &cln->handler;
+ }
+
+ ctx->context = NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO;
+
+ rc = ngx_stream_lua_run_thread(L, r, ctx, 0);
+
+ if (rc == NGX_ERROR || rc >= NGX_OK) {
+ /* do nothing */
+
+ } else if (rc == NGX_AGAIN) {
+ rc = ngx_stream_lua_content_run_posted_threads(L, r, ctx, 0);
+
+ } else if (rc == NGX_DONE) {
+ rc = ngx_stream_lua_content_run_posted_threads(L, r, ctx, 1);
+
+ } else {
+ rc = NGX_OK;
+ }
+
+ ngx_stream_lua_finalize_request(r, rc);
+ return rc;
+}
+
+
+int ngx_stream_lua_ffi_ssl_get_client_hello_server_name(
+ ngx_stream_lua_request_t *r, const char **name,
+ size_t *namelen, char **err)
+{
+ ngx_ssl_conn_t *ssl_conn;
+#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB
+ const unsigned char *p;
+ size_t remaining, len;
+#endif
+
+ if (r->connection == NULL || r->connection->ssl == NULL) {
+ *err = "bad request";
+ return NGX_ERROR;
+ }
+
+ ssl_conn = r->connection->ssl->connection;
+ if (ssl_conn == NULL) {
+ *err = "bad ssl conn";
+ return NGX_ERROR;
+ }
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+
+#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB
+ remaining = 0;
+
+ /* This code block is taken from OpenSSL's client_hello_select_server_ctx()
+ * */
+ if (!SSL_client_hello_get0_ext(ssl_conn, TLSEXT_TYPE_server_name, &p,
+ &remaining))
+ {
+ return NGX_DECLINED;
+ }
+
+ if (remaining <= 2) {
+ *err = "Bad SSL Client Hello Extension";
+ return NGX_ERROR;
+ }
+
+ len = (*(p++) << 8);
+ len += *(p++);
+ if (len + 2 != remaining) {
+ *err = "Bad SSL Client Hello Extension";
+ return NGX_ERROR;
+ }
+
+ remaining = len;
+ if (remaining == 0 || *p++ != TLSEXT_NAMETYPE_host_name) {
+ *err = "Bad SSL Client Hello Extension";
+ return NGX_ERROR;
+ }
+
+ remaining--;
+ if (remaining <= 2) {
+ *err = "Bad SSL Client Hello Extension";
+ return NGX_ERROR;
+ }
+
+ len = (*(p++) << 8);
+ len += *(p++);
+ if (len + 2 > remaining) {
+ *err = "Bad SSL Client Hello Extension";
+ return NGX_ERROR;
+ }
+
+ remaining = len;
+ *name = (const char *) p;
+ *namelen = len;
+
+ return NGX_OK;
+
+#else
+ *err = "OpenSSL too old to support this function";
+ return NGX_ERROR;
+#endif
+
+#else
+ *err = "no TLS extension support";
+ return NGX_ERROR;
+#endif
+}
+
+
+int
+ngx_stream_lua_ffi_ssl_get_client_hello_ext(ngx_stream_lua_request_t *r,
+ unsigned int type, const unsigned char **out, size_t *outlen, char **err)
+{
+ ngx_ssl_conn_t *ssl_conn;
+
+ if (r->connection == NULL || r->connection->ssl == NULL) {
+ *err = "bad request";
+ return NGX_ERROR;
+ }
+
+ ssl_conn = r->connection->ssl->connection;
+ if (ssl_conn == NULL) {
+ *err = "bad ssl conn";
+ return NGX_ERROR;
+ }
+
+#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB
+ if (SSL_client_hello_get0_ext(ssl_conn, type, out, outlen) == 0) {
+ return NGX_DECLINED;
+ }
+
+ return NGX_OK;
+
+#else
+ *err = "OpenSSL too old to support this function";
+ return NGX_ERROR;
+#endif
+}
+
+
+int
+ngx_stream_lua_ffi_ssl_set_protocols(ngx_stream_lua_request_t *r,
+ int protocols, char **err)
+{
+
+ ngx_ssl_conn_t *ssl_conn;
+
+ if (r->connection == NULL || r->connection->ssl == NULL) {
+ *err = "bad request";
+ return NGX_ERROR;
+ }
+
+ ssl_conn = r->connection->ssl->connection;
+ if (ssl_conn == NULL) {
+ *err = "bad ssl conn";
+ return NGX_ERROR;
+ }
+
+#if OPENSSL_VERSION_NUMBER >= 0x009080dfL
+ /* only in 0.9.8m+ */
+ SSL_clear_options(ssl_conn,
+ SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1);
+#endif
+
+ if (!(protocols & NGX_SSL_SSLv2)) {
+ SSL_set_options(ssl_conn, SSL_OP_NO_SSLv2);
+ }
+
+ if (!(protocols & NGX_SSL_SSLv3)) {
+ SSL_set_options(ssl_conn, SSL_OP_NO_SSLv3);
+ }
+
+ if (!(protocols & NGX_SSL_TLSv1)) {
+ SSL_set_options(ssl_conn, SSL_OP_NO_TLSv1);
+ }
+
+#ifdef SSL_OP_NO_TLSv1_1
+ SSL_clear_options(ssl_conn, SSL_OP_NO_TLSv1_1);
+ if (!(protocols & NGX_SSL_TLSv1_1)) {
+ SSL_set_options(ssl_conn, SSL_OP_NO_TLSv1_1);
+ }
+#endif
+
+#ifdef SSL_OP_NO_TLSv1_2
+ SSL_clear_options(ssl_conn, SSL_OP_NO_TLSv1_2);
+ if (!(protocols & NGX_SSL_TLSv1_2)) {
+ SSL_set_options(ssl_conn, SSL_OP_NO_TLSv1_2);
+ }
+#endif
+
+#ifdef SSL_OP_NO_TLSv1_3
+ SSL_clear_options(ssl_conn, SSL_OP_NO_TLSv1_3);
+ if (!(protocols & NGX_SSL_TLSv1_3)) {
+ SSL_set_options(ssl_conn, SSL_OP_NO_TLSv1_3);
+ }
+#endif
+
+ return NGX_OK;
+}
+
+#endif /* NGX_STREAM_SSL */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_ssl_client_helloby.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_ssl_client_helloby.h
new file mode 100644
index 0000000..05b0e17
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_ssl_client_helloby.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+#ifndef _NGX_STREAM_LUA_SSL_CLIENT_HELLOBY_H_INCLUDED_
+#define _NGX_STREAM_LUA_SSL_CLIENT_HELLOBY_H_INCLUDED_
+
+#include "ngx_stream_lua_common.h"
+
+#if (NGX_STREAM_SSL)
+
+ngx_int_t ngx_stream_lua_ssl_client_hello_handler_inline(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_srv_conf_t *lscf, lua_State *L);
+
+ngx_int_t ngx_stream_lua_ssl_client_hello_handler_file(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_srv_conf_t *lscf, lua_State *L);
+
+char *ngx_stream_lua_ssl_client_hello_by_lua_block(ngx_conf_t *cf,
+ ngx_command_t *cmd, void *conf);
+
+char *ngx_stream_lua_ssl_client_hello_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+int ngx_stream_lua_ssl_client_hello_handler(ngx_ssl_conn_t *ssl_conn,
+ int *al, void *arg);
+
+#endif /* NGX_STREAM_SSL */
+#endif /* _NGX_STREAM_LUA_SSL_CLIENT_HELLOBY_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_string.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_string.c
new file mode 100644
index 0000000..23460a1
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_string.c
@@ -0,0 +1,463 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_string.c.tt2
+ */
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_string.h"
+#include "ngx_stream_lua_util.h"
+#include "ngx_stream_lua_args.h"
+#include "ngx_crc32.h"
+
+#if (NGX_HAVE_SHA1)
+#include "ngx_sha1.h"
+#endif
+
+#include "ngx_md5.h"
+
+#if (NGX_OPENSSL)
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#endif
+
+
+static uintptr_t ngx_stream_lua_ngx_escape_sql_str(u_char *dst,
+ u_char *src, size_t size);
+static int ngx_stream_lua_ngx_quote_sql_str(lua_State *L);
+static int ngx_stream_lua_ngx_encode_args(lua_State *L);
+static int ngx_stream_lua_ngx_decode_args(lua_State *L);
+#if (NGX_OPENSSL)
+static int ngx_stream_lua_ngx_hmac_sha1(lua_State *L);
+#endif
+
+
+void
+ngx_stream_lua_inject_string_api(lua_State *L)
+{
+ lua_pushcfunction(L, ngx_stream_lua_ngx_encode_args);
+ lua_setfield(L, -2, "encode_args");
+
+ lua_pushcfunction(L, ngx_stream_lua_ngx_decode_args);
+ lua_setfield(L, -2, "decode_args");
+
+ lua_pushcfunction(L, ngx_stream_lua_ngx_quote_sql_str);
+ lua_setfield(L, -2, "quote_sql_str");
+
+#if (NGX_OPENSSL)
+ lua_pushcfunction(L, ngx_stream_lua_ngx_hmac_sha1);
+ lua_setfield(L, -2, "hmac_sha1");
+#endif
+}
+
+
+static int
+ngx_stream_lua_ngx_quote_sql_str(lua_State *L)
+{
+ size_t len, dlen, escape;
+ u_char *p;
+ u_char *src, *dst;
+
+ if (lua_gettop(L) != 1) {
+ return luaL_error(L, "expecting one argument");
+ }
+
+ src = (u_char *) luaL_checklstring(L, 1, &len);
+
+ if (len == 0) {
+ dst = (u_char *) "''";
+ dlen = sizeof("''") - 1;
+ lua_pushlstring(L, (char *) dst, dlen);
+ return 1;
+ }
+
+ escape = ngx_stream_lua_ngx_escape_sql_str(NULL, src, len);
+
+ dlen = sizeof("''") - 1 + len + escape;
+
+ p = lua_newuserdata(L, dlen);
+
+ dst = p;
+
+ *p++ = '\'';
+
+ if (escape == 0) {
+ p = ngx_copy(p, src, len);
+
+ } else {
+ p = (u_char *) ngx_stream_lua_ngx_escape_sql_str(p, src, len);
+ }
+
+ *p++ = '\'';
+
+ if (p != dst + dlen) {
+ return NGX_ERROR;
+ }
+
+ lua_pushlstring(L, (char *) dst, p - dst);
+
+ return 1;
+}
+
+
+static uintptr_t
+ngx_stream_lua_ngx_escape_sql_str(u_char *dst, u_char *src, size_t size)
+{
+ ngx_uint_t n;
+
+ if (dst == NULL) {
+ /* find the number of chars to be escaped */
+ n = 0;
+ while (size) {
+ /* the highest bit of all the UTF-8 chars
+ * is always 1 */
+ if ((*src & 0x80) == 0) {
+ switch (*src) {
+ case '\0':
+ case '\b':
+ case '\n':
+ case '\r':
+ case '\t':
+ case 26: /* \Z */
+ case '\\':
+ case '\'':
+ case '"':
+ n++;
+ break;
+ default:
+ break;
+ }
+ }
+ src++;
+ size--;
+ }
+
+ return (uintptr_t) n;
+ }
+
+ while (size) {
+ if ((*src & 0x80) == 0) {
+ switch (*src) {
+ case '\0':
+ *dst++ = '\\';
+ *dst++ = '0';
+ break;
+
+ case '\b':
+ *dst++ = '\\';
+ *dst++ = 'b';
+ break;
+
+ case '\n':
+ *dst++ = '\\';
+ *dst++ = 'n';
+ break;
+
+ case '\r':
+ *dst++ = '\\';
+ *dst++ = 'r';
+ break;
+
+ case '\t':
+ *dst++ = '\\';
+ *dst++ = 't';
+ break;
+
+ case 26:
+ *dst++ = '\\';
+ *dst++ = 'Z';
+ break;
+
+ case '\\':
+ *dst++ = '\\';
+ *dst++ = '\\';
+ break;
+
+ case '\'':
+ *dst++ = '\\';
+ *dst++ = '\'';
+ break;
+
+ case '"':
+ *dst++ = '\\';
+ *dst++ = '"';
+ break;
+
+ default:
+ *dst++ = *src;
+ break;
+ }
+ } else {
+ *dst++ = *src;
+ }
+ src++;
+ size--;
+ } /* while (size) */
+
+ return (uintptr_t) dst;
+}
+
+
+static void
+ngx_stream_lua_encode_base64(ngx_str_t *dst, ngx_str_t *src, int no_padding)
+{
+ u_char *d, *s;
+ size_t len;
+ static u_char basis[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ len = src->len;
+ s = src->data;
+ d = dst->data;
+
+ while (len > 2) {
+ *d++ = basis[(s[0] >> 2) & 0x3f];
+ *d++ = basis[((s[0] & 3) << 4) | (s[1] >> 4)];
+ *d++ = basis[((s[1] & 0x0f) << 2) | (s[2] >> 6)];
+ *d++ = basis[s[2] & 0x3f];
+
+ s += 3;
+ len -= 3;
+ }
+
+ if (len) {
+ *d++ = basis[(s[0] >> 2) & 0x3f];
+
+ if (len == 1) {
+ *d++ = basis[(s[0] & 3) << 4];
+ if (!no_padding) {
+ *d++ = '=';
+ }
+
+ } else {
+ *d++ = basis[((s[0] & 3) << 4) | (s[1] >> 4)];
+ *d++ = basis[(s[1] & 0x0f) << 2];
+ }
+
+ if (!no_padding) {
+ *d++ = '=';
+ }
+ }
+
+ dst->len = d - dst->data;
+}
+
+
+static int
+ngx_stream_lua_ngx_encode_args(lua_State *L)
+{
+ ngx_str_t args;
+
+ if (lua_gettop(L) != 1) {
+ return luaL_error(L, "expecting 1 argument but seen %d",
+ lua_gettop(L));
+ }
+
+ luaL_checktype(L, 1, LUA_TTABLE);
+ ngx_stream_lua_process_args_option(NULL, L, 1, &args);
+ lua_pushlstring(L, (char *) args.data, args.len);
+ return 1;
+}
+
+
+static int
+ngx_stream_lua_ngx_decode_args(lua_State *L)
+{
+ u_char *buf;
+ u_char *tmp;
+ size_t len = 0;
+ int n;
+ int max;
+
+ n = lua_gettop(L);
+
+ if (n != 1 && n != 2) {
+ return luaL_error(L, "expecting 1 or 2 arguments but seen %d", n);
+ }
+
+ buf = (u_char *) luaL_checklstring(L, 1, &len);
+
+ if (n == 2) {
+ max = luaL_checkint(L, 2);
+ lua_pop(L, 1);
+
+ } else {
+ max = NGX_STREAM_LUA_MAX_ARGS;
+ }
+
+ tmp = lua_newuserdata(L, len);
+ ngx_memcpy(tmp, buf, len);
+
+ lua_createtable(L, 0, 4);
+
+ return ngx_stream_lua_parse_args(L, tmp, tmp + len, max);
+}
+
+
+#if (NGX_OPENSSL)
+static int
+ngx_stream_lua_ngx_hmac_sha1(lua_State *L)
+{
+ u_char *sec, *sts;
+ size_t lsec, lsts;
+ unsigned int md_len;
+ unsigned char md[EVP_MAX_MD_SIZE];
+ const EVP_MD *evp_md;
+
+ if (lua_gettop(L) != 2) {
+ return luaL_error(L, "expecting 2 arguments, but got %d",
+ lua_gettop(L));
+ }
+
+ sec = (u_char *) luaL_checklstring(L, 1, &lsec);
+ sts = (u_char *) luaL_checklstring(L, 2, &lsts);
+
+ evp_md = EVP_sha1();
+
+ HMAC(evp_md, sec, lsec, sts, lsts, md, &md_len);
+
+ lua_pushlstring(L, (char *) md, md_len);
+
+ return 1;
+}
+#endif
+
+
+void
+ngx_stream_lua_ffi_md5_bin(const u_char *src, size_t len, u_char *dst)
+{
+ ngx_md5_t md5;
+
+ ngx_md5_init(&md5);
+ ngx_md5_update(&md5, src, len);
+ ngx_md5_final(dst, &md5);
+}
+
+
+void
+ngx_stream_lua_ffi_md5(const u_char *src, size_t len, u_char *dst)
+{
+ ngx_md5_t md5;
+ u_char md5_buf[MD5_DIGEST_LENGTH];
+
+ ngx_md5_init(&md5);
+ ngx_md5_update(&md5, src, len);
+ ngx_md5_final(md5_buf, &md5);
+
+ ngx_hex_dump(dst, md5_buf, sizeof(md5_buf));
+}
+
+
+int
+ngx_stream_lua_ffi_sha1_bin(const u_char *src, size_t len, u_char *dst)
+{
+#if (NGX_HAVE_SHA1)
+ ngx_sha1_t sha;
+
+ ngx_sha1_init(&sha);
+ ngx_sha1_update(&sha, src, len);
+ ngx_sha1_final(dst, &sha);
+
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+
+unsigned int
+ngx_stream_lua_ffi_crc32_short(const u_char *src, size_t len)
+{
+ return ngx_crc32_short((u_char *) src, len);
+}
+
+
+unsigned int
+ngx_stream_lua_ffi_crc32_long(const u_char *src, size_t len)
+{
+ return ngx_crc32_long((u_char *) src, len);
+}
+
+
+size_t
+ngx_stream_lua_ffi_encode_base64(const u_char *src, size_t slen, u_char *dst,
+ int no_padding)
+{
+ ngx_str_t in, out;
+
+ in.data = (u_char *) src;
+ in.len = slen;
+
+ out.data = dst;
+
+ ngx_stream_lua_encode_base64(&out, &in, no_padding);
+
+ return out.len;
+}
+
+
+int
+ngx_stream_lua_ffi_decode_base64(const u_char *src, size_t slen, u_char *dst,
+ size_t *dlen)
+{
+ ngx_int_t rc;
+ ngx_str_t in, out;
+
+ in.data = (u_char *) src;
+ in.len = slen;
+
+ out.data = dst;
+
+ rc = ngx_decode_base64(&out, &in);
+
+ *dlen = out.len;
+
+ return rc == NGX_OK;
+}
+
+
+size_t
+ngx_stream_lua_ffi_unescape_uri(const u_char *src, size_t len, u_char *dst)
+{
+ u_char *p = dst;
+
+ ngx_stream_lua_unescape_uri(&p, (u_char **) &src, len,
+ NGX_UNESCAPE_URI_COMPONENT);
+ return p - dst;
+}
+
+
+size_t
+ngx_stream_lua_ffi_uri_escaped_length(const u_char *src, size_t len,
+ int type)
+{
+ return len + 2 * ngx_stream_lua_escape_uri(NULL, (u_char *) src, len,
+ type);
+}
+
+
+void
+ngx_stream_lua_ffi_escape_uri(const u_char *src, size_t len, u_char *dst,
+ int type)
+{
+ ngx_stream_lua_escape_uri(dst, (u_char *) src, len,
+ type);
+}
+
+
+
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_string.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_string.h
new file mode 100644
index 0000000..ef08788
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_string.h
@@ -0,0 +1,28 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_string.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_STRING_H_INCLUDED_
+#define _NGX_STREAM_LUA_STRING_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+void ngx_stream_lua_inject_string_api(lua_State *L);
+
+
+#endif /* _NGX_STREAM_LUA_STRING_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_time.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_time.c
new file mode 100644
index 0000000..19bfeda
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_time.c
@@ -0,0 +1,104 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_time.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_common.h"
+
+
+double
+ngx_stream_lua_ffi_now(void)
+{
+ ngx_time_t *tp;
+
+ tp = ngx_timeofday();
+
+ return tp->sec + tp->msec / 1000.0;
+}
+
+
+double
+ngx_stream_lua_ffi_req_start_time(ngx_stream_lua_request_t *r)
+{
+ return r->session->start_sec + r->session->start_msec / 1000.0;
+}
+
+
+long
+ngx_stream_lua_ffi_time(void)
+{
+ return (long) ngx_time();
+}
+
+
+long
+ngx_stream_lua_ffi_monotonic_msec(void)
+{
+ return (long) ngx_current_msec;
+}
+
+
+void
+ngx_stream_lua_ffi_update_time(void)
+{
+ ngx_time_update();
+}
+
+
+void
+ngx_stream_lua_ffi_today(u_char *buf)
+{
+ ngx_tm_t tm;
+
+ ngx_gmtime(ngx_time() + ngx_cached_time->gmtoff * 60, &tm);
+
+ ngx_sprintf(buf, "%04d-%02d-%02d", tm.ngx_tm_year, tm.ngx_tm_mon,
+ tm.ngx_tm_mday);
+}
+
+
+void
+ngx_stream_lua_ffi_localtime(u_char *buf)
+{
+ ngx_tm_t tm;
+
+ ngx_gmtime(ngx_time() + ngx_cached_time->gmtoff * 60, &tm);
+
+ ngx_sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d", tm.ngx_tm_year,
+ tm.ngx_tm_mon, tm.ngx_tm_mday, tm.ngx_tm_hour, tm.ngx_tm_min,
+ tm.ngx_tm_sec);
+}
+
+
+void
+ngx_stream_lua_ffi_utctime(u_char *buf)
+{
+ ngx_tm_t tm;
+
+ ngx_gmtime(ngx_time(), &tm);
+
+ ngx_sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d", tm.ngx_tm_year,
+ tm.ngx_tm_mon, tm.ngx_tm_mday, tm.ngx_tm_hour, tm.ngx_tm_min,
+ tm.ngx_tm_sec);
+}
+
+
+
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_timer.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_timer.c
new file mode 100644
index 0000000..64e0313
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_timer.c
@@ -0,0 +1,968 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_timer.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_timer.h"
+#include "ngx_stream_lua_util.h"
+#include "ngx_stream_lua_contentby.h"
+#include "ngx_stream_lua_probe.h"
+
+
+#define NGX_STREAM_LUA_TIMER_ERRBUF_SIZE 128
+
+
+typedef struct {
+ void **main_conf;
+ void **srv_conf;
+
+
+ lua_State *co;
+
+ ngx_pool_t *pool;
+
+ ngx_listening_t *listening;
+ ngx_str_t client_addr_text;
+
+ ngx_stream_lua_main_conf_t *lmcf;
+ ngx_stream_lua_vm_state_t *vm_state;
+
+ int co_ref;
+ unsigned delay:31;
+ unsigned premature:1;
+} ngx_stream_lua_timer_ctx_t;
+
+
+static int ngx_stream_lua_ngx_timer_at(lua_State *L);
+static int ngx_stream_lua_ngx_timer_every(lua_State *L);
+static int ngx_stream_lua_ngx_timer_helper(lua_State *L, int every);
+static int ngx_stream_lua_ngx_timer_running_count(lua_State *L);
+static int ngx_stream_lua_ngx_timer_pending_count(lua_State *L);
+static ngx_int_t ngx_stream_lua_timer_copy(
+ ngx_stream_lua_timer_ctx_t *old_tctx);
+static void ngx_stream_lua_timer_handler(ngx_event_t *ev);
+static u_char *ngx_stream_lua_log_timer_error(ngx_log_t *log, u_char *buf,
+ size_t len);
+static void ngx_stream_lua_abort_pending_timers(ngx_event_t *ev);
+
+
+void
+ngx_stream_lua_inject_timer_api(lua_State *L)
+{
+ lua_createtable(L, 0 /* narr */, 4 /* nrec */); /* ngx.timer. */
+
+ lua_pushcfunction(L, ngx_stream_lua_ngx_timer_at);
+ lua_setfield(L, -2, "at");
+
+ lua_pushcfunction(L, ngx_stream_lua_ngx_timer_every);
+ lua_setfield(L, -2, "every");
+
+ lua_pushcfunction(L, ngx_stream_lua_ngx_timer_running_count);
+ lua_setfield(L, -2, "running_count");
+
+ lua_pushcfunction(L, ngx_stream_lua_ngx_timer_pending_count);
+ lua_setfield(L, -2, "pending_count");
+
+ lua_setfield(L, -2, "timer");
+}
+
+
+static int
+ngx_stream_lua_ngx_timer_running_count(lua_State *L)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_main_conf_t *lmcf;
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request");
+ }
+
+ lmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_lua_module);
+
+ lua_pushnumber(L, lmcf->running_timers);
+
+ return 1;
+}
+
+
+static int
+ngx_stream_lua_ngx_timer_pending_count(lua_State *L)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_main_conf_t *lmcf;
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request");
+ }
+
+ lmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_lua_module);
+
+ lua_pushnumber(L, lmcf->pending_timers);
+
+ return 1;
+}
+
+
+static int
+ngx_stream_lua_ngx_timer_at(lua_State *L)
+{
+ return ngx_stream_lua_ngx_timer_helper(L, 0);
+}
+
+
+/*
+ * TODO: return a timer handler instead which can be passed to
+ * the ngx.timer.cancel method to cancel the timer.
+ */
+static int
+ngx_stream_lua_ngx_timer_every(lua_State *L)
+{
+ return ngx_stream_lua_ngx_timer_helper(L, 1);
+}
+
+
+static int
+ngx_stream_lua_ngx_timer_helper(lua_State *L, int every)
+{
+ int nargs, co_ref;
+ u_char *p;
+ lua_State *vm; /* the main thread */
+ lua_State *co;
+ ngx_msec_t delay;
+ ngx_event_t *ev = NULL;
+ ngx_stream_lua_request_t *r;
+ ngx_connection_t *saved_c = NULL;
+ ngx_stream_lua_ctx_t *ctx;
+#if 0
+ ngx_http_connection_t *hc;
+#endif
+
+ ngx_stream_lua_timer_ctx_t *tctx = NULL;
+ ngx_stream_lua_main_conf_t *lmcf;
+#if 0
+ ngx_http_core_main_conf_t *cmcf;
+#endif
+
+ nargs = lua_gettop(L);
+ if (nargs < 2) {
+ return luaL_error(L, "expecting at least 2 arguments but got %d",
+ nargs);
+ }
+
+ delay = (ngx_msec_t) (luaL_checknumber(L, 1) * 1000);
+
+ if (every && delay == 0) {
+ return luaL_error(L, "delay cannot be zero");
+ }
+
+ luaL_argcheck(L, lua_isfunction(L, 2) && !lua_iscfunction(L, 2), 2,
+ "Lua function expected");
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request");
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+
+ if (ngx_exiting && delay > 0) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "process exiting");
+ return 2;
+ }
+
+ lmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_lua_module);
+
+ if (lmcf->pending_timers >= lmcf->max_pending_timers) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "too many pending timers");
+ return 2;
+ }
+
+ if (lmcf->watcher == NULL) {
+ /* create the watcher fake connection */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "lua creating fake watcher connection");
+
+ if (ngx_cycle->files) {
+ saved_c = ngx_cycle->files[0];
+ }
+
+ lmcf->watcher = ngx_get_connection(0, ngx_cycle->log);
+
+ if (ngx_cycle->files) {
+ ngx_cycle->files[0] = saved_c;
+ }
+
+ if (lmcf->watcher == NULL) {
+ return luaL_error(L, "no memory");
+ }
+
+ /* to work around the -1 check in ngx_worker_process_cycle: */
+ lmcf->watcher->fd = (ngx_socket_t) -2;
+
+ lmcf->watcher->idle = 1;
+ lmcf->watcher->read->handler = ngx_stream_lua_abort_pending_timers;
+ lmcf->watcher->data = lmcf;
+ }
+
+ vm = ngx_stream_lua_get_lua_vm(r, ctx);
+
+ co = lua_newthread(vm);
+
+ /* L stack: time func [args] */
+
+ ngx_stream_lua_probe_user_coroutine_create(r, L, co);
+
+#ifndef OPENRESTY_LUAJIT
+ lua_createtable(co, 0, 0); /* the new globals table */
+
+ /* co stack: global_tb */
+
+ lua_createtable(co, 0, 1); /* the metatable */
+ ngx_stream_lua_get_globals_table(co);
+ lua_setfield(co, -2, "__index");
+ lua_setmetatable(co, -2);
+
+ /* co stack: global_tb */
+
+ ngx_stream_lua_set_globals_table(co);
+#endif
+
+ /* co stack: <empty> */
+
+ dd("stack top: %d", lua_gettop(L));
+
+ lua_xmove(vm, L, 1); /* move coroutine from main thread to L */
+
+ /* L stack: time func [args] thread */
+ /* vm stack: empty */
+
+ lua_pushvalue(L, 2); /* copy entry function to top of L*/
+
+ /* L stack: time func [args] thread func */
+
+ lua_xmove(L, co, 1); /* move entry function from L to co */
+
+ /* L stack: time func [args] thread */
+ /* co stack: func */
+
+#ifndef OPENRESTY_LUAJIT
+ ngx_stream_lua_get_globals_table(co);
+ lua_setfenv(co, -2);
+#endif
+
+ /* co stack: func */
+
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ coroutines_key));
+ lua_rawget(L, LUA_REGISTRYINDEX);
+
+ /* L stack: time func [args] thread coroutines */
+
+ lua_pushvalue(L, -2);
+
+ /* L stack: time func [args] thread coroutines thread */
+
+ co_ref = luaL_ref(L, -2);
+ lua_pop(L, 1);
+
+ /* L stack: time func [args] thread */
+
+ if (nargs > 2) {
+ lua_pop(L, 1); /* L stack: time func [args] */
+ lua_xmove(L, co, nargs - 2); /* L stack: time func */
+
+ /* co stack: func [args] */
+ }
+
+ p = ngx_alloc(sizeof(ngx_event_t) + sizeof(ngx_stream_lua_timer_ctx_t),
+ r->connection->log);
+ if (p == NULL) {
+ goto nomem;
+ }
+
+ ev = (ngx_event_t *) p;
+
+ ngx_memzero(ev, sizeof(ngx_event_t));
+
+ p += sizeof(ngx_event_t);
+
+ tctx = (ngx_stream_lua_timer_ctx_t *) p;
+
+ tctx->delay = every ? delay : 0;
+
+ tctx->premature = 0;
+ tctx->co_ref = co_ref;
+ tctx->co = co;
+
+
+ tctx->main_conf = r->session->main_conf;
+ tctx->srv_conf = r->session->srv_conf;
+
+ tctx->lmcf = lmcf;
+
+ tctx->pool = ngx_create_pool(128, ngx_cycle->log);
+ if (tctx->pool == NULL) {
+ goto nomem;
+ }
+
+ if (r->connection) {
+ tctx->listening = r->connection->listening;
+
+ } else {
+ tctx->listening = NULL;
+ }
+
+ if (r->connection->addr_text.len) {
+ tctx->client_addr_text.data = ngx_palloc(tctx->pool,
+ r->connection->addr_text.len);
+ if (tctx->client_addr_text.data == NULL) {
+ goto nomem;
+ }
+
+ ngx_memcpy(tctx->client_addr_text.data, r->connection->addr_text.data,
+ r->connection->addr_text.len);
+ tctx->client_addr_text.len = r->connection->addr_text.len;
+
+ } else {
+ tctx->client_addr_text.len = 0;
+ tctx->client_addr_text.data = NULL;
+ }
+
+ if (ctx && ctx->vm_state) {
+ tctx->vm_state = ctx->vm_state;
+ tctx->vm_state->count++;
+
+ } else {
+ tctx->vm_state = NULL;
+ }
+
+ ev->handler = ngx_stream_lua_timer_handler;
+ ev->data = tctx;
+ ev->log = ngx_cycle->log;
+
+ lmcf->pending_timers++;
+
+ ngx_add_timer(ev, delay);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "stream created timer (co: %p delay: %M ms)",
+ tctx->co, delay);
+
+ lua_pushinteger(L, 1);
+ return 1;
+
+nomem:
+
+ if (tctx && tctx->pool) {
+ ngx_destroy_pool(tctx->pool);
+ }
+
+ if (ev) {
+ ngx_free(ev);
+ }
+
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ coroutines_key));
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ luaL_unref(L, -1, co_ref);
+
+ return luaL_error(L, "no memory");
+}
+
+
+static ngx_int_t
+ngx_stream_lua_timer_copy(ngx_stream_lua_timer_ctx_t *old_tctx)
+{
+ int nargs, co_ref, i;
+ u_char *p;
+ lua_State *vm; /* the main thread */
+ lua_State *co;
+ lua_State *L;
+ ngx_event_t *ev = NULL;
+
+ ngx_stream_lua_timer_ctx_t *tctx = NULL;
+ ngx_stream_lua_main_conf_t *lmcf;
+
+ /* L stack: func [args] */
+ L = old_tctx->co;
+
+ lmcf = old_tctx->lmcf;
+
+ vm = old_tctx->vm_state ? old_tctx->vm_state->vm : lmcf->lua;
+
+ co = lua_newthread(vm);
+
+#ifndef OPENRESTY_LUAJIT
+ lua_createtable(co, 0, 0); /* the new globals table */
+
+ /* co stack: global_tb */
+
+ lua_createtable(co, 0, 1); /* the metatable */
+ ngx_stream_lua_get_globals_table(co);
+ lua_setfield(co, -2, "__index");
+ lua_setmetatable(co, -2);
+
+ /* co stack: global_tb */
+
+ ngx_stream_lua_set_globals_table(co);
+#endif
+
+ /* co stack: <empty> */
+
+ dd("stack top: %d", lua_gettop(L));
+
+ lua_xmove(vm, L, 1); /* move coroutine from main thread to L */
+
+ /* L stack: func [args] thread */
+ /* vm stack: empty */
+
+ lua_pushvalue(L, 1); /* copy entry function to top of L*/
+
+ /* L stack: func [args] thread func */
+
+ lua_xmove(L, co, 1); /* move entry function from L to co */
+
+ /* L stack: func [args] thread */
+ /* co stack: func */
+
+#ifndef OPENRESTY_LUAJIT
+ ngx_stream_lua_get_globals_table(co);
+ lua_setfenv(co, -2);
+#endif
+
+ /* co stack: func */
+
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ coroutines_key));
+ lua_rawget(L, LUA_REGISTRYINDEX);
+
+ /* L stack: func [args] thread coroutines */
+
+ lua_pushvalue(L, -2);
+
+ /* L stack: func [args] thread coroutines thread */
+
+ co_ref = luaL_ref(L, -2);
+ lua_pop(L, 2);
+
+ /* L stack: func [args] */
+
+ nargs = lua_gettop(L);
+ if (nargs > 1) {
+ for (i = 2; i <= nargs; i++) {
+ lua_pushvalue(L, i);
+ }
+
+ /* L stack: func [args] [args] */
+
+ lua_xmove(L, co, nargs - 1);
+
+ /* L stack: func [args] */
+ /* co stack: func [args] */
+ }
+
+ p = ngx_alloc(sizeof(ngx_event_t) + sizeof(ngx_stream_lua_timer_ctx_t),
+ ngx_cycle->log);
+ if (p == NULL) {
+ goto nomem;
+ }
+
+ ev = (ngx_event_t *) p;
+
+ ngx_memzero(ev, sizeof(ngx_event_t));
+
+ p += sizeof(ngx_event_t);
+
+ tctx = (ngx_stream_lua_timer_ctx_t *) p;
+
+ ngx_memcpy(tctx, old_tctx, sizeof(ngx_stream_lua_timer_ctx_t));
+
+ tctx->co_ref = co_ref;
+ tctx->co = co;
+
+ tctx->pool = ngx_create_pool(128, ngx_cycle->log);
+ if (tctx->pool == NULL) {
+ goto nomem;
+ }
+
+ if (tctx->client_addr_text.len) {
+ tctx->client_addr_text.data = ngx_palloc(tctx->pool,
+ tctx->client_addr_text.len);
+ if (tctx->client_addr_text.data == NULL) {
+ goto nomem;
+ }
+
+ ngx_memcpy(tctx->client_addr_text.data, old_tctx->client_addr_text.data,
+ tctx->client_addr_text.len);
+ }
+
+ if (tctx->vm_state) {
+ tctx->vm_state->count++;
+ }
+
+ ev->handler = ngx_stream_lua_timer_handler;
+ ev->data = tctx;
+ ev->log = ngx_cycle->log;
+
+ lmcf->pending_timers++;
+
+ ngx_add_timer(ev, tctx->delay);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "stream created next timer (co: %p delay: %M ms)",
+ tctx->co, tctx->delay);
+
+ return NGX_OK;
+
+nomem:
+
+ if (tctx && tctx->pool) {
+ ngx_destroy_pool(tctx->pool);
+ }
+
+ if (ev) {
+ ngx_free(ev);
+ }
+
+ /* L stack: func [args] */
+
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ coroutines_key));
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ luaL_unref(L, -1, co_ref);
+
+ /* L stack: func [args] coroutines */
+
+ lua_pop(L, 1);
+
+ return NGX_ERROR;
+}
+
+
+static void
+ngx_stream_lua_timer_handler(ngx_event_t *ev)
+{
+ int n;
+ lua_State *L;
+ ngx_int_t rc;
+ ngx_connection_t *c = NULL;
+ ngx_pool_cleanup_t *pcln;
+
+ ngx_stream_lua_request_t *r = NULL;
+ ngx_stream_lua_cleanup_t *cln;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_timer_ctx_t tctx;
+ ngx_stream_lua_main_conf_t *lmcf;
+
+ ngx_stream_core_srv_conf_t *clcf;
+ ngx_stream_session_t *s;
+
+ lua_Debug ar;
+ u_char *p;
+ u_char errbuf[NGX_STREAM_LUA_TIMER_ERRBUF_SIZE];
+ const char *source;
+ const char *errmsg;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "stream lua ngx.timer expired");
+
+ ngx_memcpy(&tctx, ev->data, sizeof(ngx_stream_lua_timer_ctx_t));
+ ngx_free(ev);
+
+ ngx_stream_lua_assert(tctx.co_ref && tctx.co);
+
+ lmcf = tctx.lmcf;
+
+ lmcf->pending_timers--;
+
+ if (!ngx_exiting && tctx.delay > 0) {
+ rc = ngx_stream_lua_timer_copy(&tctx);
+ if (rc != NGX_OK) {
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+ "failed to create the next timer of delay %ud ms",
+ (unsigned) tctx.delay);
+ }
+ }
+
+ if (lmcf->running_timers >= lmcf->max_running_timers) {
+ p = ngx_snprintf(errbuf, NGX_STREAM_LUA_TIMER_ERRBUF_SIZE - 1,
+ "stream lua: %i lua_max_running_timers are not enough",
+ lmcf->max_running_timers);
+ *p = '\0';
+ errmsg = (const char *) errbuf;
+ goto failed;
+ }
+
+ c = ngx_stream_lua_create_fake_connection(tctx.pool);
+ if (c == NULL) {
+ errmsg = "could not create fake connection";
+ goto failed;
+ }
+
+ c->log->handler = ngx_stream_lua_log_timer_error;
+ c->log->data = c;
+
+ c->listening = tctx.listening;
+ c->addr_text = tctx.client_addr_text;
+
+ s = ngx_stream_lua_create_fake_session(c);
+ if (s == NULL) {
+ errmsg = "could not create fake session";
+ goto failed;
+ }
+
+
+ s->main_conf = tctx.main_conf;
+ s->srv_conf = tctx.srv_conf;
+
+ clcf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module);
+
+#if defined(nginx_version) && nginx_version >= 1009000
+ ngx_set_connection_log(s->connection, clcf->error_log);
+
+#else
+#endif
+
+ dd("lmcf: %p", lmcf);
+
+ ctx = ngx_stream_lua_create_ctx(s);
+ if (ctx == NULL) {
+ errmsg = "could not create ctx";
+ goto failed;
+ }
+
+ r = ctx->request;
+
+ if (tctx.vm_state) {
+ ctx->vm_state = tctx.vm_state;
+
+ pcln = ngx_pool_cleanup_add(r->pool, 0);
+ if (pcln == NULL) {
+ errmsg = "could not add vm cleanup";
+ goto failed;
+ }
+
+ pcln->handler = ngx_stream_lua_cleanup_vm;
+ pcln->data = tctx.vm_state;
+ }
+
+ ctx->cur_co_ctx = &ctx->entry_co_ctx;
+
+ L = ngx_stream_lua_get_lua_vm(r, ctx);
+
+ cln = ngx_stream_lua_cleanup_add(r, 0);
+ if (cln == NULL) {
+ errmsg = "could not add request cleanup";
+ goto failed;
+ }
+
+ cln->handler = ngx_stream_lua_request_cleanup_handler;
+ cln->data = ctx;
+ ctx->cleanup = &cln->handler;
+
+ ctx->entered_content_phase = 1;
+ ctx->context = NGX_STREAM_LUA_CONTEXT_TIMER;
+
+ r->read_event_handler = ngx_stream_lua_block_reading;
+
+ ctx->cur_co_ctx->co_ref = tctx.co_ref;
+ ctx->cur_co_ctx->co = tctx.co;
+ ctx->cur_co_ctx->co_status = NGX_STREAM_LUA_CO_RUNNING;
+
+ dd("r connection: %p, log %p", r->connection, r->connection->log);
+
+ /* save the request in coroutine globals table */
+ ngx_stream_lua_set_req(tctx.co, r);
+
+ ngx_stream_lua_attach_co_ctx_to_L(tctx.co, ctx->cur_co_ctx);
+
+ lmcf->running_timers++;
+
+ lua_pushboolean(tctx.co, tctx.premature);
+
+ n = lua_gettop(tctx.co);
+ if (n > 2) {
+ lua_insert(tctx.co, 2);
+ }
+
+#ifdef NGX_LUA_USE_ASSERT
+ ctx->cur_co_ctx->co_top = 1;
+#endif
+
+ rc = ngx_stream_lua_run_thread(L, r, ctx, n - 1);
+
+ dd("timer lua run thread: %d", (int) rc);
+
+ if (rc == NGX_ERROR || rc >= NGX_OK) {
+ /* do nothing */
+
+ } else if (rc == NGX_AGAIN) {
+ rc = ngx_stream_lua_content_run_posted_threads(L, r, ctx, 0);
+
+ } else if (rc == NGX_DONE) {
+ rc = ngx_stream_lua_content_run_posted_threads(L, r, ctx, 1);
+
+ } else {
+ rc = NGX_OK;
+ }
+
+ ngx_stream_lua_finalize_request(r, rc);
+ return;
+
+failed:
+
+ /* co stack: func [args] */
+ lua_pushvalue(tctx.co, 1);
+ /* co stack: func [args] func */
+ lua_getinfo(tctx.co, ">Sf", &ar);
+
+ source = ar.source;
+
+ if (source == NULL) {
+ source = "(unknown)";
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+ "stream lua failed to run timer with function "
+ "defined at %s:%d: %s",
+ source, ar.linedefined, errmsg);
+
+ lua_pushlightuserdata(tctx.co, ngx_stream_lua_lightudata_mask(
+ coroutines_key));
+ lua_rawget(tctx.co, LUA_REGISTRYINDEX);
+ luaL_unref(tctx.co, -1, tctx.co_ref);
+ lua_settop(tctx.co, 0);
+
+ if (tctx.vm_state) {
+ ngx_stream_lua_cleanup_vm(tctx.vm_state);
+ }
+
+ if (c) {
+ ngx_stream_lua_close_fake_connection(c);
+
+ } else if (tctx.pool) {
+ ngx_destroy_pool(tctx.pool);
+ }
+}
+
+
+static u_char *
+ngx_stream_lua_log_timer_error(ngx_log_t *log, u_char *buf, size_t len)
+{
+ u_char *p;
+ ngx_connection_t *c;
+
+ if (log->action) {
+ p = ngx_snprintf(buf, len, " while %s", log->action);
+ len -= p - buf;
+ buf = p;
+ }
+
+ c = log->data;
+
+ dd("ctx = %p", c);
+
+ p = ngx_snprintf(buf, len, ", context: ngx.timer");
+ len -= p - buf;
+ buf = p;
+
+ if (c != NULL) {
+ if (c->addr_text.len) {
+ p = ngx_snprintf(buf, len, ", client: %V", &c->addr_text);
+ len -= p - buf;
+ buf = p;
+ }
+
+ if (c->listening && c->listening->addr_text.len) {
+ p = ngx_snprintf(buf, len, ", server: %V",
+ &c->listening->addr_text);
+ /* len -= p - buf; */
+ buf = p;
+ }
+ }
+
+ return buf;
+}
+
+
+static void
+ngx_stream_lua_abort_pending_timers(ngx_event_t *ev)
+{
+ ngx_int_t i, n;
+ ngx_event_t **events;
+ ngx_connection_t *c, *saved_c = NULL;
+ ngx_rbtree_node_t *cur, *prev, *next, *sentinel, *temp;
+
+ ngx_stream_lua_timer_ctx_t *tctx;
+ ngx_stream_lua_main_conf_t *lmcf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "lua abort pending timers");
+
+ c = ev->data;
+ lmcf = c->data;
+
+ dd("lua connection fd: %d", (int) c->fd);
+
+ if (!c->close) {
+ return;
+ }
+
+ c->read->closed = 1;
+ c->write->closed = 1;
+
+ /* we temporarily use a valid fd (0) to make ngx_free_connection happy */
+
+ c->fd = 0;
+
+ if (ngx_cycle->files) {
+ saved_c = ngx_cycle->files[0];
+ }
+
+ ngx_free_connection(c);
+
+ c->fd = (ngx_socket_t) -1;
+
+ if (ngx_cycle->files) {
+ ngx_cycle->files[0] = saved_c;
+ }
+
+ if (lmcf->pending_timers == 0) {
+ return;
+ }
+
+ /* expire pending timers immediately */
+
+ sentinel = ngx_event_timer_rbtree.sentinel;
+
+ cur = ngx_event_timer_rbtree.root;
+
+ /* XXX nginx does not guarantee the parent of root is meaningful,
+ * so we temporarily override it to simplify tree traversal. */
+ temp = cur->parent;
+ cur->parent = NULL;
+
+ prev = NULL;
+
+ events = ngx_pcalloc(ngx_cycle->pool,
+ lmcf->pending_timers * sizeof(ngx_event_t *));
+ if (events == NULL) {
+ return;
+ }
+
+ n = 0;
+
+ dd("root: %p, root parent: %p, sentinel: %p", cur, cur->parent, sentinel);
+
+ while (n < lmcf->pending_timers) {
+ if (cur == sentinel || cur == NULL) {
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+ "lua pending timer counter got out of sync: %i",
+ lmcf->pending_timers);
+ break;
+ }
+
+ dd("prev: %p, cur: %p, cur parent: %p, cur left: %p, cur right: %p",
+ prev, cur, cur->parent, cur->left, cur->right);
+
+ if (prev == cur->parent) {
+ /* neither of the children has been accessed yet */
+
+ next = cur->left;
+ if (next == sentinel) {
+ ev = (ngx_event_t *)
+ ((char *) cur - offsetof(ngx_event_t, timer));
+
+ if (ev->handler == ngx_stream_lua_timer_handler) {
+ dd("found node: %p", cur);
+ events[n++] = ev;
+ }
+
+ next = (cur->right != sentinel) ? cur->right : cur->parent;
+ }
+
+ } else if (prev == cur->left) {
+ /* just accessed the left child */
+
+ ev = (ngx_event_t *)
+ ((char *) cur - offsetof(ngx_event_t, timer));
+
+ if (ev->handler == ngx_stream_lua_timer_handler) {
+ dd("found node 2: %p", cur);
+ events[n++] = ev;
+ }
+
+ next = (cur->right != sentinel) ? cur->right : cur->parent;
+
+ } else if (prev == cur->right) {
+ /* already accessed both children */
+ next = cur->parent;
+
+ } else {
+ /* not reachable */
+ next = NULL;
+ }
+
+ prev = cur;
+ cur = next;
+ }
+
+ /* restore the old tree root's parent */
+ ngx_event_timer_rbtree.root->parent = temp;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "stream lua found %i pending timers to be "
+ "aborted prematurely", n);
+
+ for (i = 0; i < n; i++) {
+ ev = events[i];
+
+ ngx_rbtree_delete(&ngx_event_timer_rbtree, &ev->timer);
+
+#if (NGX_DEBUG)
+ ev->timer.left = NULL;
+ ev->timer.right = NULL;
+ ev->timer.parent = NULL;
+#endif
+
+ ev->timer_set = 0;
+
+ ev->timedout = 1;
+
+ tctx = ev->data;
+ tctx->premature = 1;
+
+ dd("calling timer handler prematurely");
+ ev->handler(ev);
+ }
+
+#if 0
+ if (pending_timers) {
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+ "lua pending timer counter got out of sync: %i",
+ pending_timers);
+ }
+#endif
+}
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_timer.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_timer.h
new file mode 100644
index 0000000..3472b5e
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_timer.h
@@ -0,0 +1,28 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_timer.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_TIMER_H_INCLUDED_
+#define _NGX_STREAM_LUA_TIMER_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+void ngx_stream_lua_inject_timer_api(lua_State *L);
+
+
+#endif /* _NGX_STREAM_LUA_TIMER_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_uthread.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_uthread.c
new file mode 100644
index 0000000..8d906de
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_uthread.c
@@ -0,0 +1,290 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_uthread.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_uthread.h"
+#include "ngx_stream_lua_coroutine.h"
+#include "ngx_stream_lua_util.h"
+#include "ngx_stream_lua_probe.h"
+
+
+#if 1
+#undef ngx_stream_lua_probe_info
+#define ngx_stream_lua_probe_info(msg)
+#endif
+
+
+static int ngx_stream_lua_uthread_spawn(lua_State *L);
+static int ngx_stream_lua_uthread_wait(lua_State *L);
+static int ngx_stream_lua_uthread_kill(lua_State *L);
+
+
+void
+ngx_stream_lua_inject_uthread_api(ngx_log_t *log, lua_State *L)
+{
+ /* new thread table */
+ lua_createtable(L, 0 /* narr */, 3 /* nrec */);
+
+ lua_pushcfunction(L, ngx_stream_lua_uthread_spawn);
+ lua_setfield(L, -2, "spawn");
+
+ lua_pushcfunction(L, ngx_stream_lua_uthread_wait);
+ lua_setfield(L, -2, "wait");
+
+ lua_pushcfunction(L, ngx_stream_lua_uthread_kill);
+ lua_setfield(L, -2, "kill");
+
+ lua_setfield(L, -2, "thread");
+}
+
+
+static int
+ngx_stream_lua_uthread_spawn(lua_State *L)
+{
+ int n;
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx = NULL;
+
+ n = lua_gettop(L);
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return luaL_error(L, "no request ctx found");
+ }
+
+ ngx_stream_lua_coroutine_create_helper(L, r, ctx, &coctx);
+
+ /* anchor the newly created coroutine into the Lua registry */
+
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ coroutines_key));
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ lua_pushvalue(L, -2);
+ coctx->co_ref = luaL_ref(L, -2);
+ lua_pop(L, 1);
+
+ if (n > 1) {
+ lua_replace(L, 1);
+ lua_xmove(L, coctx->co, n - 1);
+ }
+
+ coctx->is_uthread = 1;
+ ctx->uthreads++;
+
+ coctx->co_status = NGX_STREAM_LUA_CO_RUNNING;
+ ctx->co_op = NGX_STREAM_LUA_USER_THREAD_RESUME;
+
+ ctx->cur_co_ctx->thread_spawn_yielded = 1;
+
+ if (ngx_stream_lua_post_thread(r, ctx, ctx->cur_co_ctx) != NGX_OK) {
+ return luaL_error(L, "no memory");
+ }
+
+ coctx->parent_co_ctx = ctx->cur_co_ctx;
+ ctx->cur_co_ctx = coctx;
+
+ ngx_stream_lua_attach_co_ctx_to_L(coctx->co, coctx);
+
+ ngx_stream_lua_probe_user_thread_spawn(r, L, coctx->co);
+
+ dd("yielding with arg %s, top=%d, index-1:%s", luaL_typename(L, -1),
+ (int) lua_gettop(L), luaL_typename(L, 1));
+ return lua_yield(L, 1);
+}
+
+
+static int
+ngx_stream_lua_uthread_wait(lua_State *L)
+{
+ int i, nargs, nrets;
+ lua_State *sub_co;
+ ngx_stream_lua_request_t *r;
+
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx, *sub_coctx;
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return luaL_error(L, "no request ctx found");
+ }
+
+ ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_YIELDABLE);
+
+ coctx = ctx->cur_co_ctx;
+
+ nargs = lua_gettop(L);
+
+ for (i = 1; i <= nargs; i++) {
+ sub_co = lua_tothread(L, i);
+
+ luaL_argcheck(L, sub_co, i, "lua thread expected");
+
+ sub_coctx = ngx_stream_lua_get_co_ctx(sub_co, ctx);
+ if (sub_coctx == NULL) {
+ return luaL_error(L, "no co ctx found");
+ }
+
+ if (!sub_coctx->is_uthread) {
+ return luaL_error(L, "attempt to wait on a coroutine that is "
+ "not a user thread");
+ }
+
+ if (sub_coctx->parent_co_ctx != coctx) {
+ return luaL_error(L, "only the parent coroutine can wait on the "
+ "thread");
+ }
+
+ switch (sub_coctx->co_status) {
+ case NGX_STREAM_LUA_CO_ZOMBIE:
+
+ ngx_stream_lua_probe_info("found zombie child");
+
+ nrets = lua_gettop(sub_coctx->co);
+
+ dd("child retval count: %d, %s: %s", (int) nrets,
+ luaL_typename(sub_coctx->co, -1),
+ lua_tostring(sub_coctx->co, -1));
+
+ if (nrets) {
+ lua_xmove(sub_coctx->co, L, nrets);
+ }
+
+#if 1
+ ngx_stream_lua_del_thread(r, L, ctx, sub_coctx);
+ ctx->uthreads--;
+#endif
+
+ return nrets;
+
+ case NGX_STREAM_LUA_CO_DEAD:
+ dd("uthread already waited: %p (parent %p)", sub_coctx,
+ coctx);
+
+ if (i < nargs) {
+ /* just ignore it if it is not the last one */
+ continue;
+ }
+
+ /* being the last one */
+ lua_pushnil(L);
+ lua_pushliteral(L, "already waited or killed");
+ return 2;
+
+ default:
+ dd("uthread %p still alive, status: %d, parent %p", sub_coctx,
+ sub_coctx->co_status, coctx);
+ break;
+ }
+
+ ngx_stream_lua_probe_user_thread_wait(L, sub_coctx->co);
+ sub_coctx->waited_by_parent = 1;
+ }
+
+ return lua_yield(L, 0);
+}
+
+
+static int
+ngx_stream_lua_uthread_kill(lua_State *L)
+{
+ lua_State *sub_co;
+ ngx_stream_lua_request_t *r;
+
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_co_ctx_t *coctx, *sub_coctx;
+
+ r = ngx_stream_lua_get_req(L);
+ if (r == NULL) {
+ return luaL_error(L, "no request found");
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return luaL_error(L, "no request ctx found");
+ }
+
+ ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT
+
+ | NGX_STREAM_LUA_CONTEXT_PREREAD
+ | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO
+ | NGX_STREAM_LUA_CONTEXT_SSL_CERT
+ | NGX_STREAM_LUA_CONTEXT_TIMER);
+
+ coctx = ctx->cur_co_ctx;
+
+ sub_co = lua_tothread(L, 1);
+ luaL_argcheck(L, sub_co, 1, "lua thread expected");
+
+ sub_coctx = ngx_stream_lua_get_co_ctx(sub_co, ctx);
+
+ if (sub_coctx == NULL) {
+ return luaL_error(L, "no co ctx found");
+ }
+
+ if (!sub_coctx->is_uthread) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "not user thread");
+ return 2;
+ }
+
+ if (sub_coctx->parent_co_ctx != coctx) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "killer not parent");
+ return 2;
+ }
+
+
+ switch (sub_coctx->co_status) {
+ case NGX_STREAM_LUA_CO_ZOMBIE:
+ ngx_stream_lua_del_thread(r, L, ctx, sub_coctx);
+ ctx->uthreads--;
+
+ lua_pushnil(L);
+ lua_pushliteral(L, "already terminated");
+ return 2;
+
+ case NGX_STREAM_LUA_CO_DEAD:
+ lua_pushnil(L);
+ lua_pushliteral(L, "already waited or killed");
+ return 2;
+
+ default:
+ ngx_stream_lua_cleanup_pending_operation(sub_coctx);
+ ngx_stream_lua_del_thread(r, L, ctx, sub_coctx);
+ ctx->uthreads--;
+
+ lua_pushinteger(L, 1);
+ return 1;
+ }
+
+ /* not reachable */
+}
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_uthread.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_uthread.h
new file mode 100644
index 0000000..6e8b54e
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_uthread.h
@@ -0,0 +1,44 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_uthread.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_UTHREAD_H_INCLUDED_
+#define _NGX_STREAM_LUA_UTHREAD_H_INCLUDED_
+
+
+#include "ngx_stream_lua_common.h"
+
+
+#define ngx_stream_lua_is_thread(ctx) \
+ ((ctx)->cur_co_ctx->is_uthread || (ctx)->cur_co_ctx == &(ctx)->entry_co_ctx)
+
+
+#define ngx_stream_lua_is_entry_thread(ctx) \
+ ((ctx)->cur_co_ctx == &(ctx)->entry_co_ctx)
+
+
+#define ngx_stream_lua_entry_thread_alive(ctx) \
+ ((ctx)->entry_co_ctx.co_ref != LUA_NOREF)
+
+
+#define ngx_stream_lua_coroutine_alive(coctx) \
+ ((coctx)->co_status != NGX_STREAM_LUA_CO_DEAD \
+ && (coctx)->co_status != NGX_STREAM_LUA_CO_ZOMBIE)
+
+
+void ngx_stream_lua_inject_uthread_api(ngx_log_t *log, lua_State *L);
+
+
+#endif /* _NGX_STREAM_LUA_UTHREAD_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_util.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_util.c
new file mode 100644
index 0000000..e79f18e
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_util.c
@@ -0,0 +1,3680 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_util.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "nginx.h"
+#include "ngx_stream_lua_directive.h"
+#include "ngx_stream_lua_util.h"
+#include "ngx_stream_lua_exception.h"
+#include "ngx_stream_lua_pcrefix.h"
+#include "ngx_stream_lua_args.h"
+#include "ngx_stream_lua_output.h"
+#include "ngx_stream_lua_control.h"
+#include "ngx_stream_lua_log.h"
+#include "ngx_stream_lua_string.h"
+#include "ngx_stream_lua_misc.h"
+#include "ngx_stream_lua_consts.h"
+#include "ngx_stream_lua_shdict.h"
+#include "ngx_stream_lua_coroutine.h"
+#include "ngx_stream_lua_socket_tcp.h"
+#include "ngx_stream_lua_socket_udp.h"
+#include "ngx_stream_lua_sleep.h"
+#include "ngx_stream_lua_probe.h"
+#include "ngx_stream_lua_uthread.h"
+#include "ngx_stream_lua_contentby.h"
+#include "ngx_stream_lua_timer.h"
+#include "ngx_stream_lua_config.h"
+#include "ngx_stream_lua_ssl.h"
+
+
+#include "ngx_stream_lua_phase.h"
+
+
+#if 1
+#undef ngx_stream_lua_probe_info
+#define ngx_stream_lua_probe_info(msg)
+#endif
+
+
+#ifndef NGX_STREAM_LUA_BT_DEPTH
+#define NGX_STREAM_LUA_BT_DEPTH 22
+#endif
+
+
+#ifndef NGX_STREAM_LUA_BT_MAX_COROS
+#define NGX_STREAM_LUA_BT_MAX_COROS 5
+#endif
+
+
+#if (NGX_STREAM_LUA_HAVE_SA_RESTART)
+#define NGX_STREAM_LUA_SA_RESTART_SIGS { \
+ ngx_signal_value(NGX_RECONFIGURE_SIGNAL), \
+ ngx_signal_value(NGX_REOPEN_SIGNAL), \
+ ngx_signal_value(NGX_NOACCEPT_SIGNAL), \
+ ngx_signal_value(NGX_TERMINATE_SIGNAL), \
+ ngx_signal_value(NGX_SHUTDOWN_SIGNAL), \
+ ngx_signal_value(NGX_CHANGEBIN_SIGNAL), \
+ SIGALRM, \
+ SIGINT, \
+ SIGIO, \
+ SIGCHLD, \
+ SIGSYS, \
+ SIGPIPE, \
+ 0 \
+};
+#endif
+
+
+char ngx_stream_lua_code_cache_key;
+char ngx_stream_lua_regex_cache_key;
+char ngx_stream_lua_socket_pool_key;
+char ngx_stream_lua_coroutines_key;
+
+static void ngx_stream_lua_init_registry(lua_State *L, ngx_log_t *log);
+static void ngx_stream_lua_init_globals(lua_State *L, ngx_cycle_t *cycle,
+ ngx_stream_lua_main_conf_t *lmcf, ngx_log_t *log);
+#ifdef OPENRESTY_LUAJIT
+static void ngx_stream_lua_inject_global_write_guard(lua_State *L,
+ ngx_log_t *log);
+#endif
+static void ngx_stream_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);
+static ngx_int_t ngx_stream_lua_handle_exit(lua_State *L,
+ ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx);
+static int ngx_stream_lua_thread_traceback(lua_State *L, lua_State *co,
+ ngx_stream_lua_co_ctx_t *coctx);
+static void ngx_stream_lua_inject_ngx_api(lua_State *L,
+ ngx_stream_lua_main_conf_t *lmcf, ngx_log_t *log);
+static ngx_int_t ngx_stream_lua_output_filter(ngx_stream_lua_request_t *r,
+ ngx_chain_t *in);
+static void ngx_stream_lua_finalize_threads(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx, lua_State *L);
+static ngx_int_t ngx_stream_lua_post_zombie_thread(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_co_ctx_t *parent, ngx_stream_lua_co_ctx_t *thread);
+static void ngx_stream_lua_cleanup_zombie_child_uthreads(
+ ngx_stream_lua_request_t *r, lua_State *L, ngx_stream_lua_ctx_t *ctx,
+ ngx_stream_lua_co_ctx_t *coctx);
+static ngx_int_t ngx_stream_lua_on_abort_resume(ngx_stream_lua_request_t *r);
+static void ngx_stream_lua_close_fake_request(ngx_stream_lua_request_t *r);
+static ngx_int_t ngx_stream_lua_flush_pending_output(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx);
+static ngx_int_t
+ ngx_stream_lua_process_flushing_coroutines(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx);
+static lua_State *ngx_stream_lua_new_state(lua_State *parent_vm,
+ ngx_cycle_t *cycle, ngx_stream_lua_main_conf_t *lmcf, ngx_log_t *log);
+static int ngx_stream_lua_get_raw_phase_context(lua_State *L);
+static int ngx_stream_lua_req_socket(lua_State *L);
+
+
+#ifndef LUA_PATH_SEP
+#define LUA_PATH_SEP ";"
+#endif
+
+
+#if !defined(LUA_DEFAULT_PATH) && (NGX_DEBUG)
+#define LUA_DEFAULT_PATH "../lua-resty-core/lib/?.lua;" \
+ "../lua-resty-lrucache/lib/?.lua"
+#endif
+
+
+#define AUX_MARK "\1"
+
+
+static void
+ngx_stream_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)
+{
+ const char *tmp_path;
+ const char *prefix;
+
+ /* XXX here we use some hack to simplify string manipulation */
+ tmp_path = luaL_gsub(L, path, LUA_PATH_SEP LUA_PATH_SEP,
+ LUA_PATH_SEP AUX_MARK LUA_PATH_SEP);
+
+ lua_pushlstring(L, (char *) cycle->prefix.data, cycle->prefix.len);
+ prefix = lua_tostring(L, -1);
+ tmp_path = luaL_gsub(L, tmp_path, "$prefix", prefix);
+ tmp_path = luaL_gsub(L, tmp_path, "${prefix}", prefix);
+ lua_pop(L, 3);
+
+ dd("tmp_path path: %s", tmp_path);
+
+#if (NGX_DEBUG)
+ tmp_path =
+#else
+ (void)
+#endif
+ luaL_gsub(L, tmp_path, AUX_MARK, default_path);
+
+#if (NGX_DEBUG)
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, log, 0,
+ "lua setting lua package.%s to \"%s\"", fieldname, tmp_path);
+#endif
+
+ lua_remove(L, -2);
+
+ /* fix negative index as there's new data on stack */
+ tab_idx = (tab_idx < 0) ? (tab_idx - 1) : tab_idx;
+ lua_setfield(L, tab_idx, fieldname);
+}
+
+
+#ifndef OPENRESTY_LUAJIT
+/**
+ * Create new table and set _G field to itself.
+ *
+ * After:
+ * | new table | <- top
+ * | ... |
+ * */
+void
+ngx_stream_lua_create_new_globals_table(lua_State *L, int narr, int nrec)
+{
+ lua_createtable(L, narr, nrec + 1);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "_G");
+}
+#endif /* OPENRESTY_LUAJIT */
+
+
+static lua_State *
+ngx_stream_lua_new_state(lua_State *parent_vm, ngx_cycle_t *cycle,
+ ngx_stream_lua_main_conf_t *lmcf, ngx_log_t *log)
+{
+ lua_State *L;
+ const char *old_path;
+ const char *new_path;
+ size_t old_path_len;
+ const char *old_cpath;
+ const char *new_cpath;
+ size_t old_cpath_len;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, log, 0, "lua creating new vm state");
+
+ L = luaL_newstate();
+ if (L == NULL) {
+ return NULL;
+ }
+
+ luaL_openlibs(L);
+
+ lua_getglobal(L, "package");
+
+ if (!lua_istable(L, -1)) {
+ ngx_log_error(NGX_LOG_EMERG, log, 0,
+ "the \"package\" table does not exist");
+ return NULL;
+ }
+
+ if (parent_vm) {
+ lua_getglobal(parent_vm, "package");
+ lua_getfield(parent_vm, -1, "path");
+ old_path = lua_tolstring(parent_vm, -1, &old_path_len);
+ lua_pop(parent_vm, 1);
+
+ lua_pushlstring(L, old_path, old_path_len);
+ lua_setfield(L, -2, "path");
+
+ lua_getfield(parent_vm, -1, "cpath");
+ old_path = lua_tolstring(parent_vm, -1, &old_path_len);
+ lua_pop(parent_vm, 2);
+
+ lua_pushlstring(L, old_path, old_path_len);
+ lua_setfield(L, -2, "cpath");
+
+ } else {
+#ifdef LUA_DEFAULT_PATH
+# define LUA_DEFAULT_PATH_LEN (sizeof(LUA_DEFAULT_PATH) - 1)
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, log, 0,
+ "lua prepending default package.path with %s",
+ LUA_DEFAULT_PATH);
+
+ lua_pushliteral(L, LUA_DEFAULT_PATH ";"); /* package default */
+ lua_getfield(L, -2, "path"); /* package default old */
+ lua_concat(L, 2); /* package new */
+ lua_setfield(L, -2, "path"); /* package */
+#endif
+
+#ifdef LUA_DEFAULT_CPATH
+# define LUA_DEFAULT_CPATH_LEN (sizeof(LUA_DEFAULT_CPATH) - 1)
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, log, 0,
+ "lua prepending default package.cpath with %s",
+ LUA_DEFAULT_CPATH);
+
+ lua_pushliteral(L, LUA_DEFAULT_CPATH ";"); /* package default */
+ lua_getfield(L, -2, "cpath"); /* package default old */
+ old_cpath = lua_tolstring(L, -1, &old_cpath_len);
+ lua_concat(L, 2); /* package new */
+ lua_setfield(L, -2, "cpath"); /* package */
+#endif
+
+ if (lmcf->lua_path.len != 0) {
+ lua_getfield(L, -1, "path"); /* get original package.path */
+ old_path = lua_tolstring(L, -1, &old_path_len);
+
+ dd("old path: %s", old_path);
+
+ lua_pushlstring(L, (char *) lmcf->lua_path.data,
+ lmcf->lua_path.len);
+ new_path = lua_tostring(L, -1);
+
+ ngx_stream_lua_set_path(cycle, L, -3, "path", new_path, old_path,
+ log);
+
+ lua_pop(L, 2);
+ }
+
+ if (lmcf->lua_cpath.len != 0) {
+ lua_getfield(L, -1, "cpath"); /* get original package.cpath */
+ old_cpath = lua_tolstring(L, -1, &old_cpath_len);
+
+ dd("old cpath: %s", old_cpath);
+
+ lua_pushlstring(L, (char *) lmcf->lua_cpath.data,
+ lmcf->lua_cpath.len);
+ new_cpath = lua_tostring(L, -1);
+
+ ngx_stream_lua_set_path(cycle, L, -3, "cpath", new_cpath, old_cpath,
+ log);
+
+
+ lua_pop(L, 2);
+ }
+ }
+
+ lua_pop(L, 1); /* remove the "package" table */
+
+ ngx_stream_lua_init_registry(L, log);
+ ngx_stream_lua_init_globals(L, cycle, lmcf, log);
+
+ return L;
+}
+
+
+lua_State *
+ngx_stream_lua_new_thread(ngx_stream_lua_request_t *r, lua_State *L, int *ref)
+{
+ int base;
+ lua_State *co;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua creating new thread");
+
+ base = lua_gettop(L);
+
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ coroutines_key));
+ lua_rawget(L, LUA_REGISTRYINDEX);
+
+ co = lua_newthread(L);
+
+#ifndef OPENRESTY_LUAJIT
+ /* {{{ inherit coroutine's globals to main thread's globals table
+ * for print() function will try to find tostring() in current
+ * globals table.
+ */
+ /* new globals table for coroutine */
+ ngx_stream_lua_create_new_globals_table(co, 0, 0);
+
+ lua_createtable(co, 0, 1);
+ ngx_stream_lua_get_globals_table(co);
+ lua_setfield(co, -2, "__index");
+ lua_setmetatable(co, -2);
+
+ ngx_stream_lua_set_globals_table(co);
+ /* }}} */
+#endif /* OPENRESTY_LUAJIT */
+
+ *ref = luaL_ref(L, -2);
+
+ if (*ref == LUA_NOREF) {
+ lua_settop(L, base); /* restore main thread stack */
+ return NULL;
+ }
+
+ lua_settop(L, base);
+ return co;
+}
+
+
+void
+ngx_stream_lua_del_thread(ngx_stream_lua_request_t *r, lua_State *L,
+ ngx_stream_lua_ctx_t *ctx, ngx_stream_lua_co_ctx_t *coctx)
+{
+ if (coctx->co_ref == LUA_NOREF) {
+ return;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua deleting light thread");
+
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ coroutines_key));
+ lua_rawget(L, LUA_REGISTRYINDEX);
+
+ ngx_stream_lua_probe_thread_delete(r, coctx->co, ctx);
+
+ luaL_unref(L, -1, coctx->co_ref);
+ coctx->co_ref = LUA_NOREF;
+ coctx->co_status = NGX_STREAM_LUA_CO_DEAD;
+
+ lua_pop(L, 1);
+}
+
+
+u_char *
+ngx_stream_lua_rebase_path(ngx_pool_t *pool, u_char *src, size_t len)
+{
+ u_char *p;
+ ngx_str_t dst;
+
+ dst.data = ngx_palloc(pool, len + 1);
+ if (dst.data == NULL) {
+ return NULL;
+ }
+
+ dst.len = len;
+
+ p = ngx_copy(dst.data, src, len);
+ *p = '\0';
+
+ if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->prefix, &dst)
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ return dst.data;
+}
+
+
+
+
+ngx_int_t
+ngx_stream_lua_send_chain_link(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx, ngx_chain_t *in)
+{
+ if (in == NULL) {
+ ctx->eof = 1;
+
+ return NGX_OK;
+ }
+
+ return ngx_stream_lua_output_filter(r, in);
+}
+
+
+
+
+static ngx_int_t
+ngx_stream_lua_output_filter(ngx_stream_lua_request_t *r, ngx_chain_t *in)
+{
+ ngx_int_t rc;
+ ngx_stream_lua_ctx_t *ctx;
+
+ rc = ngx_stream_top_filter(r->session, in, 1);
+
+ if (rc == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+
+ ngx_chain_update_chains(r->pool,
+ &ctx->free_bufs, &ctx->busy_bufs, &in,
+ (ngx_buf_tag_t) &ngx_stream_lua_module);
+
+ return rc;
+}
+
+
+
+
+static void
+ngx_stream_lua_init_registry(lua_State *L, ngx_log_t *log)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, log, 0,
+ "lua initializing lua registry");
+
+ /* {{{ register a table to anchor lua coroutines reliably:
+ * {([int]ref) = [cort]} */
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ coroutines_key));
+ lua_createtable(L, 0, 32 /* nrec */);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ /* }}} */
+
+ /* create the registry entry for the Lua request ctx data table */
+ lua_pushliteral(L, ngx_stream_lua_ctx_tables_key);
+ lua_createtable(L, 0, 32 /* nrec */);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+
+ /* create the registry entry for the Lua socket connection pool table */
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ socket_pool_key));
+ lua_createtable(L, 0, 8 /* nrec */);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+
+#if (NGX_PCRE)
+ /* create the registry entry for the Lua precompiled regex object cache */
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ regex_cache_key));
+ lua_createtable(L, 0, 16 /* nrec */);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+#endif
+
+ /* {{{ register table to cache user code:
+ * { [(string)cache_key] = <code closure> } */
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ code_cache_key));
+ lua_createtable(L, 0, 8 /* nrec */);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ /* }}} */
+}
+
+
+static void
+ngx_stream_lua_init_globals(lua_State *L, ngx_cycle_t *cycle,
+ ngx_stream_lua_main_conf_t *lmcf, ngx_log_t *log)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, log, 0,
+ "lua initializing lua globals");
+
+
+ ngx_stream_lua_inject_ngx_api(L, lmcf, log);
+}
+
+
+static void
+ngx_stream_lua_inject_ngx_api(lua_State *L, ngx_stream_lua_main_conf_t *lmcf,
+ ngx_log_t *log)
+{
+ lua_createtable(L, 0 /* narr */, 113 /* nrec */); /* ngx.* */
+
+ lua_pushcfunction(L, ngx_stream_lua_get_raw_phase_context);
+ lua_setfield(L, -2, "_phase_ctx");
+
+
+ ngx_stream_lua_inject_core_consts(L);
+
+ ngx_stream_lua_inject_log_api(L);
+ ngx_stream_lua_inject_output_api(L);
+ ngx_stream_lua_inject_string_api(L);
+ ngx_stream_lua_inject_control_api(log, L);
+
+
+ ngx_stream_lua_inject_sleep_api(L);
+ ngx_stream_lua_inject_phase_api(L);
+
+ ngx_stream_lua_inject_req_api(log, L);
+
+
+ ngx_stream_lua_inject_shdict_api(lmcf, L);
+ ngx_stream_lua_inject_socket_tcp_api(log, L);
+ ngx_stream_lua_inject_socket_udp_api(log, L);
+ ngx_stream_lua_inject_uthread_api(log, L);
+ ngx_stream_lua_inject_timer_api(L);
+ ngx_stream_lua_inject_config_api(L);
+
+ lua_getglobal(L, "package"); /* ngx package */
+ lua_getfield(L, -1, "loaded"); /* ngx package loaded */
+ lua_pushvalue(L, -3); /* ngx package loaded ngx */
+ lua_setfield(L, -2, "ngx"); /* ngx package loaded */
+ lua_pop(L, 2);
+
+ lua_setglobal(L, "ngx");
+
+ ngx_stream_lua_inject_coroutine_api(log, L);
+}
+
+#ifdef OPENRESTY_LUAJIT
+static void
+ngx_stream_lua_inject_global_write_guard(lua_State *L, ngx_log_t *log)
+{
+ int rc;
+
+ const char buf[] =
+ "local ngx_log = ngx.log\n"
+ "local ngx_WARN = ngx.WARN\n"
+ "local tostring = tostring\n"
+ "local ngx_get_phase = ngx.get_phase\n"
+ "local traceback = require 'debug'.traceback\n"
+ "local function newindex(table, key, value)\n"
+ "rawset(table, key, value)\n"
+ "local phase = ngx_get_phase()\n"
+ "if phase == 'init_worker' or phase == 'init' then\n"
+ "return\n"
+ "end\n"
+ "ngx_log(ngx_WARN, 'writing a global Lua variable "
+ "(\\'', tostring(key), '\\') which may lead to "
+ "race conditions between concurrent requests, so "
+ "prefer the use of \\'local\\' variables', "
+ "traceback('', 2))\n"
+ "end\n"
+ "setmetatable(_G, { __newindex = newindex })\n"
+ ;
+
+ rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, "=_G write guard");
+
+ if (rc != 0) {
+ ngx_log_error(NGX_LOG_ERR, log, 0,
+ "failed to load Lua code (%i): %s",
+ rc, lua_tostring(L, -1));
+
+ lua_pop(L, 1);
+ return;
+ }
+
+ rc = lua_pcall(L, 0, 0, 0);
+ if (rc != 0) {
+ ngx_log_error(NGX_LOG_ERR, log, 0,
+ "failed to run Lua code (%i): %s",
+ rc, lua_tostring(L, -1));
+ lua_pop(L, 1);
+ }
+}
+#endif
+
+
+void
+ngx_stream_lua_discard_bufs(ngx_pool_t *pool, ngx_chain_t *in)
+{
+ ngx_chain_t *cl;
+
+ for (cl = in; cl; cl = cl->next) {
+ cl->buf->pos = cl->buf->last;
+ cl->buf->file_pos = cl->buf->file_last;
+ }
+}
+
+
+ngx_int_t
+ngx_stream_lua_add_copy_chain(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx, ngx_chain_t ***plast, ngx_chain_t *in,
+ ngx_int_t *eof)
+{
+ ngx_chain_t *cl;
+ size_t len;
+ ngx_buf_t *b;
+
+ len = 0;
+ *eof = 0;
+
+ for (cl = in; cl; cl = cl->next) {
+ if (ngx_buf_in_memory(cl->buf)) {
+ len += cl->buf->last - cl->buf->pos;
+ }
+
+ if (cl->buf->last_in_chain || cl->buf->last_buf) {
+ *eof = 1;
+ }
+ }
+
+ if (len == 0) {
+ return NGX_OK;
+ }
+
+ cl = ngx_stream_lua_chain_get_free_buf(r->connection->log, r->pool,
+ &ctx->free_bufs, len);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ dd("chains get free buf: %d == %d", (int) (cl->buf->end - cl->buf->start),
+ (int) len);
+
+ b = cl->buf;
+
+ while (in) {
+ if (ngx_buf_in_memory(in->buf)) {
+ b->last = ngx_copy(b->last, in->buf->pos,
+ in->buf->last - in->buf->pos);
+ }
+
+ in = in->next;
+ }
+
+ **plast = cl;
+ *plast = &cl->next;
+
+ return NGX_OK;
+}
+
+
+void
+ngx_stream_lua_reset_ctx(ngx_stream_lua_request_t *r, lua_State *L,
+ ngx_stream_lua_ctx_t *ctx)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua reset ctx");
+
+ ngx_stream_lua_finalize_threads(r, ctx, L);
+
+#if 0
+ if (ctx->user_co_ctx) {
+ /* no way to destroy a list but clean up the whole pool */
+ ctx->user_co_ctx = NULL;
+ }
+#endif
+
+ ngx_memzero(&ctx->entry_co_ctx, sizeof(ngx_stream_lua_co_ctx_t));
+
+ ctx->entry_co_ctx.co_ref = LUA_NOREF;
+
+
+ ctx->entered_content_phase = 0;
+
+ ctx->exit_code = 0;
+ ctx->exited = 0;
+ ctx->resume_handler = ngx_stream_lua_wev_handler;
+
+
+ ctx->co_op = 0;
+}
+
+
+
+
+void
+ngx_stream_lua_request_cleanup_handler(void *data)
+{
+ ngx_stream_lua_ctx_t *ctx = data;
+
+ ngx_stream_lua_request_cleanup(ctx, 0 /* forcible */);
+}
+
+
+void
+ngx_stream_lua_request_cleanup(ngx_stream_lua_ctx_t *ctx, int forcible)
+{
+ lua_State *L;
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_main_conf_t *lmcf;
+
+ /* force coroutine handling the request quit */
+ if (ctx == NULL) {
+ dd("ctx is NULL");
+ return;
+ }
+
+ r = ctx->request;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua request cleanup: forcible=%d", forcible);
+
+ if (ctx->cleanup) {
+ *ctx->cleanup = NULL;
+ ctx->cleanup = NULL;
+ }
+
+ lmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_lua_module);
+
+#if 1
+ if (r->connection->fd == (ngx_socket_t) -1) {
+ /* being a fake request */
+
+ if (ctx->context == NGX_STREAM_LUA_CONTEXT_TIMER) {
+ /* being a timer handler */
+ lmcf->running_timers--;
+ }
+ }
+#endif
+
+ L = ngx_stream_lua_get_lua_vm(r, ctx);
+
+ ngx_stream_lua_finalize_threads(r, ctx, L);
+}
+
+
+/*
+ * description:
+ * run a Lua coroutine specified by ctx->cur_co_ctx->co
+ * return value:
+ * NGX_AGAIN: I/O interruption: r->main->count intact
+ * NGX_DONE: I/O interruption: r->main->count already incremented by 1
+ * NGX_ERROR: error
+ * >= 200 HTTP status code
+ */
+ngx_int_t
+ngx_stream_lua_run_thread(lua_State *L, ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx, volatile int nrets)
+{
+ ngx_stream_lua_co_ctx_t *next_coctx, *parent_coctx, *orig_coctx;
+
+ int rv, success = 1;
+ lua_State *next_co;
+ lua_State *old_co;
+ const char *err, *msg, *trace;
+
+
+#if (NGX_PCRE)
+ ngx_pool_t *old_pool = NULL;
+#endif
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua run thread, top:%d", lua_gettop(L));
+
+ /* set Lua VM panic handler */
+ lua_atpanic(L, ngx_stream_lua_atpanic);
+
+ NGX_LUA_EXCEPTION_TRY {
+
+ /*
+ * silence a -Werror=clobbered warning with gcc 5.4
+ * due to above setjmp
+ */
+ err = NULL;
+ msg = NULL;
+ trace = NULL;
+
+ if (ctx->cur_co_ctx->thread_spawn_yielded) {
+ ngx_stream_lua_probe_info("thread spawn yielded");
+
+ ctx->cur_co_ctx->thread_spawn_yielded = 0;
+ nrets = 1;
+ }
+
+ for ( ;; ) {
+
+ dd("ctx: %p, co: %p, co status: %d, co is_wrap: %d",
+ ctx, ctx->cur_co_ctx->co, ctx->cur_co_ctx->co_status,
+ ctx->cur_co_ctx->is_wrap);
+
+#if (NGX_PCRE)
+ /* XXX: work-around to nginx regex subsystem */
+ old_pool = ngx_stream_lua_pcre_malloc_init(r->pool);
+#endif
+
+ orig_coctx = ctx->cur_co_ctx;
+
+#ifdef NGX_LUA_USE_ASSERT
+ dd("%p: saved co top: %d, nrets: %d, true top: %d",
+ orig_coctx->co,
+ (int) orig_coctx->co_top, (int) nrets,
+ (int) lua_gettop(orig_coctx->co));
+#endif
+
+#if DDEBUG
+ if (lua_gettop(orig_coctx->co) > 0) {
+ dd("co top elem: %s", luaL_typename(orig_coctx->co, -1));
+ }
+
+ if (orig_coctx->propagate_error) {
+ dd("co propagate_error: %d", orig_coctx->propagate_error);
+ }
+#endif
+
+ if (orig_coctx->propagate_error) {
+ orig_coctx->propagate_error = 0;
+ goto propagate_error;
+ }
+
+ ngx_stream_lua_assert(orig_coctx->co_top + nrets
+ == lua_gettop(orig_coctx->co));
+
+ rv = lua_resume(orig_coctx->co, nrets);
+
+#if (NGX_PCRE)
+ /* XXX: work-around to nginx regex subsystem */
+ ngx_stream_lua_pcre_malloc_done(old_pool);
+#endif
+
+#if 0
+ /* test the longjmp thing */
+ if (rand() % 2 == 0) {
+ NGX_LUA_EXCEPTION_THROW(1);
+ }
+#endif
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua resume returned %d", rv);
+
+ switch (rv) {
+ case LUA_YIELD:
+ /* yielded, let event handler do the rest job */
+ /* FIXME: add io cmd dispatcher here */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua thread yielded");
+
+#ifdef NGX_LUA_USE_ASSERT
+ dd("%p: saving curr top after yield: %d (co-op: %d)",
+ orig_coctx->co,
+ (int) lua_gettop(orig_coctx->co), (int) ctx->co_op);
+ orig_coctx->co_top = lua_gettop(orig_coctx->co);
+#endif
+
+
+ if (ctx->exited) {
+ return ngx_stream_lua_handle_exit(L, r, ctx);
+ }
+
+
+ /*
+ * check if coroutine.resume or coroutine.yield called
+ * lua_yield()
+ */
+ switch (ctx->co_op) {
+
+ case NGX_STREAM_LUA_USER_CORO_NOP:
+ dd("hit! it is the API yield");
+
+ ngx_stream_lua_assert(lua_gettop(ctx->cur_co_ctx->co) == 0);
+
+ ctx->cur_co_ctx = NULL;
+
+ return NGX_AGAIN;
+
+ case NGX_STREAM_LUA_USER_THREAD_RESUME:
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua user thread resume");
+
+ ctx->co_op = NGX_STREAM_LUA_USER_CORO_NOP;
+ nrets = lua_gettop(ctx->cur_co_ctx->co) - 1;
+ dd("nrets = %d", nrets);
+
+#ifdef NGX_LUA_USE_ASSERT
+ /* ignore the return value (the thread) already pushed */
+ orig_coctx->co_top--;
+#endif
+
+ break;
+
+ case NGX_STREAM_LUA_USER_CORO_RESUME:
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua coroutine: resume");
+
+ /*
+ * the target coroutine lies at the base of the
+ * parent's stack
+ */
+ ctx->co_op = NGX_STREAM_LUA_USER_CORO_NOP;
+
+ old_co = ctx->cur_co_ctx->parent_co_ctx->co;
+
+ nrets = lua_gettop(old_co);
+ if (nrets) {
+ dd("moving %d return values to parent", nrets);
+ lua_xmove(old_co, ctx->cur_co_ctx->co, nrets);
+
+#ifdef NGX_LUA_USE_ASSERT
+ ctx->cur_co_ctx->parent_co_ctx->co_top -= nrets;
+#endif
+ }
+
+ break;
+
+ default:
+ /* ctx->co_op == NGX_STREAM_LUA_USER_CORO_YIELD */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua coroutine: yield");
+
+ ctx->co_op = NGX_STREAM_LUA_USER_CORO_NOP;
+
+ if (ngx_stream_lua_is_thread(ctx)) {
+ ngx_stream_lua_probe_thread_yield(r,
+ ctx->cur_co_ctx->co);
+
+ /* discard any return values from user
+ * coroutine.yield()'s arguments */
+ lua_settop(ctx->cur_co_ctx->co, 0);
+
+#ifdef NGX_LUA_USE_ASSERT
+ ctx->cur_co_ctx->co_top = 0;
+#endif
+
+ ngx_stream_lua_probe_info("set co running");
+ ctx->cur_co_ctx->co_status = NGX_STREAM_LUA_CO_RUNNING;
+
+ if (ctx->posted_threads) {
+ ngx_stream_lua_post_thread(r, ctx, ctx->cur_co_ctx);
+ ctx->cur_co_ctx = NULL;
+ return NGX_AGAIN;
+ }
+
+ /* no pending threads, so resume the thread
+ * immediately */
+
+ nrets = 0;
+ continue;
+ }
+
+ /* being a user coroutine that has a parent */
+
+ nrets = lua_gettop(ctx->cur_co_ctx->co);
+
+ next_coctx = ctx->cur_co_ctx->parent_co_ctx;
+ next_co = next_coctx->co;
+
+ if (nrets) {
+ dd("moving %d return values to next co", nrets);
+ lua_xmove(ctx->cur_co_ctx->co, next_co, nrets);
+#ifdef NGX_LUA_USE_ASSERT
+ ctx->cur_co_ctx->co_top -= nrets;
+#endif
+ }
+
+ if (!ctx->cur_co_ctx->is_wrap) {
+ /* prepare return values for coroutine.resume
+ * (true plus any retvals)
+ */
+ lua_pushboolean(next_co, 1);
+ lua_insert(next_co, 1);
+ nrets++; /* add the true boolean value */
+ }
+
+ ctx->cur_co_ctx = next_coctx;
+
+ break;
+ }
+
+ /* try resuming on the new coroutine again */
+ continue;
+
+ case 0:
+
+ ngx_stream_lua_cleanup_pending_operation(ctx->cur_co_ctx);
+
+ ngx_stream_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 1);
+
+ ctx->cur_co_ctx->co_status = NGX_STREAM_LUA_CO_DEAD;
+
+ if (ctx->cur_co_ctx->zombie_child_threads) {
+ ngx_stream_lua_cleanup_zombie_child_uthreads(r, L, ctx,
+ ctx->cur_co_ctx);
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua light thread ended normally");
+
+ if (ngx_stream_lua_is_entry_thread(ctx)) {
+
+ lua_settop(L, 0);
+
+ ngx_stream_lua_del_thread(r, L, ctx, ctx->cur_co_ctx);
+
+ dd("uthreads: %d", (int) ctx->uthreads);
+
+ if (ctx->uthreads) {
+
+ ctx->cur_co_ctx = NULL;
+ return NGX_AGAIN;
+ }
+
+ /* all user threads terminated already */
+ goto done;
+ }
+
+ if (ctx->cur_co_ctx->is_uthread) {
+ /* being a user thread */
+
+ lua_settop(L, 0);
+
+ parent_coctx = ctx->cur_co_ctx->parent_co_ctx;
+
+ if (ngx_stream_lua_coroutine_alive(parent_coctx)) {
+ if (ctx->cur_co_ctx->waited_by_parent) {
+ ngx_stream_lua_probe_info("parent already waiting");
+ ctx->cur_co_ctx->waited_by_parent = 0;
+ success = 1;
+ goto user_co_done;
+ }
+
+ ngx_stream_lua_probe_info("parent still alive");
+
+ if (ngx_stream_lua_post_zombie_thread(r, parent_coctx,
+ ctx->cur_co_ctx)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ lua_pushboolean(ctx->cur_co_ctx->co, 1);
+ lua_insert(ctx->cur_co_ctx->co, 1);
+
+ ctx->cur_co_ctx->co_status = NGX_STREAM_LUA_CO_ZOMBIE;
+ ctx->cur_co_ctx = NULL;
+ return NGX_AGAIN;
+ }
+
+ ngx_stream_lua_del_thread(r, L, ctx, ctx->cur_co_ctx);
+ ctx->uthreads--;
+
+ if (ctx->uthreads == 0) {
+ if (ngx_stream_lua_entry_thread_alive(ctx)) {
+ ctx->cur_co_ctx = NULL;
+ return NGX_AGAIN;
+ }
+
+ /* all threads terminated already */
+ goto done;
+ }
+
+ /* some other user threads still running */
+ ctx->cur_co_ctx = NULL;
+ return NGX_AGAIN;
+ }
+
+ /* being a user coroutine that has a parent */
+
+ success = 1;
+
+user_co_done:
+
+ nrets = lua_gettop(ctx->cur_co_ctx->co);
+
+ next_coctx = ctx->cur_co_ctx->parent_co_ctx;
+
+ if (next_coctx == NULL) {
+ /* being a light thread */
+ goto no_parent;
+ }
+
+ next_co = next_coctx->co;
+
+ if (nrets) {
+ lua_xmove(ctx->cur_co_ctx->co, next_co, nrets);
+ }
+
+ if (ctx->cur_co_ctx->is_uthread) {
+ ngx_stream_lua_del_thread(r, L, ctx, ctx->cur_co_ctx);
+ ctx->uthreads--;
+ }
+
+ if (!ctx->cur_co_ctx->is_wrap) {
+ /* ended successfully, coroutine.resume returns true plus
+ * any return values
+ */
+ lua_pushboolean(next_co, success);
+ lua_insert(next_co, 1);
+ nrets++;
+ }
+
+ ctx->cur_co_ctx = next_coctx;
+
+ ngx_stream_lua_probe_info("set parent running");
+
+ next_coctx->co_status = NGX_STREAM_LUA_CO_RUNNING;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua coroutine: lua user thread ended normally");
+
+ continue;
+
+ case LUA_ERRRUN:
+ err = "runtime error";
+ break;
+
+ case LUA_ERRSYNTAX:
+ err = "syntax error";
+ break;
+
+ case LUA_ERRMEM:
+ err = "[lua] memory allocation error";
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, err);
+ abort();
+ break;
+
+ case LUA_ERRERR:
+ err = "error handler error";
+ break;
+
+ default:
+ err = "unknown error";
+ break;
+ }
+
+ if (ctx->cur_co_ctx != orig_coctx) {
+ ctx->cur_co_ctx = orig_coctx;
+ }
+
+ ngx_stream_lua_cleanup_pending_operation(ctx->cur_co_ctx);
+
+ ngx_stream_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 0);
+
+ ctx->cur_co_ctx->co_status = NGX_STREAM_LUA_CO_DEAD;
+
+ if (orig_coctx->is_uthread
+ || orig_coctx->is_wrap
+ || ngx_stream_lua_is_entry_thread(ctx))
+ {
+ ngx_stream_lua_thread_traceback(L, orig_coctx->co,
+ orig_coctx);
+ trace = lua_tostring(L, -1);
+
+ if (lua_isstring(orig_coctx->co, -1)) {
+ msg = lua_tostring(orig_coctx->co, -1);
+ dd("user custom error msg: %s", msg);
+
+ } else {
+ msg = "unknown reason";
+ }
+ }
+
+propagate_error:
+
+ if (ctx->cur_co_ctx->is_uthread) {
+ ngx_stream_lua_assert(err != NULL && msg != NULL
+ && trace != NULL);
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "stream lua user thread aborted: %s: %s\n%s",
+ err, msg, trace);
+
+ lua_settop(L, 0);
+
+ parent_coctx = ctx->cur_co_ctx->parent_co_ctx;
+
+ if (ngx_stream_lua_coroutine_alive(parent_coctx)) {
+ if (ctx->cur_co_ctx->waited_by_parent) {
+ ctx->cur_co_ctx->waited_by_parent = 0;
+ success = 0;
+ goto user_co_done;
+ }
+
+ if (ngx_stream_lua_post_zombie_thread(r, parent_coctx,
+ ctx->cur_co_ctx)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ lua_pushboolean(ctx->cur_co_ctx->co, 0);
+ lua_insert(ctx->cur_co_ctx->co, 1);
+
+ ctx->cur_co_ctx->co_status = NGX_STREAM_LUA_CO_ZOMBIE;
+ ctx->cur_co_ctx = NULL;
+ return NGX_AGAIN;
+ }
+
+ ngx_stream_lua_del_thread(r, L, ctx, ctx->cur_co_ctx);
+ ctx->uthreads--;
+
+ if (ctx->uthreads == 0) {
+ if (ngx_stream_lua_entry_thread_alive(ctx)) {
+ ctx->cur_co_ctx = NULL;
+ return NGX_AGAIN;
+ }
+
+ /* all threads terminated already */
+ goto done;
+ }
+
+ /* some other user threads still running */
+ ctx->cur_co_ctx = NULL;
+ return NGX_AGAIN;
+ }
+
+ if (ngx_stream_lua_is_entry_thread(ctx)) {
+ ngx_stream_lua_assert(err != NULL && msg != NULL
+ && trace != NULL);
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "lua entry thread aborted: %s: %s\n%s",
+ err, msg, trace);
+
+ lua_settop(L, 0);
+
+ /* being the entry thread aborted */
+
+
+ ngx_stream_lua_request_cleanup(ctx, 0);
+
+
+ if (ctx->no_abort) {
+ ctx->no_abort = 0;
+ return NGX_ERROR;
+ }
+
+ return NGX_STREAM_INTERNAL_SERVER_ERROR;
+ }
+
+ /* being a user coroutine that has a parent */
+
+ next_coctx = ctx->cur_co_ctx->parent_co_ctx;
+ if (next_coctx == NULL) {
+ goto no_parent;
+ }
+
+ next_co = next_coctx->co;
+
+ ngx_stream_lua_probe_info("set parent running");
+
+ next_coctx->co_status = NGX_STREAM_LUA_CO_RUNNING;
+
+ ctx->cur_co_ctx = next_coctx;
+
+ if (orig_coctx->is_wrap) {
+ /*
+ * coroutine.wrap propagates errors
+ * to its parent coroutine
+ */
+ next_coctx->propagate_error = 1;
+ continue;
+ }
+
+ /*
+ * ended with error, coroutine.resume returns false plus
+ * err msg
+ */
+ lua_pushboolean(next_co, 0);
+ lua_xmove(orig_coctx->co, next_co, 1);
+ nrets = 2;
+
+ /* try resuming on the new coroutine again */
+ continue;
+ }
+
+ } NGX_LUA_EXCEPTION_CATCH {
+ dd("nginx execution restored");
+ }
+
+ return NGX_ERROR;
+
+no_parent:
+
+ lua_settop(L, 0);
+
+ ctx->cur_co_ctx->co_status = NGX_STREAM_LUA_CO_DEAD;
+
+
+ ngx_stream_lua_request_cleanup(ctx, 0);
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "lua handler aborted: "
+ "user coroutine has no parent");
+
+ return NGX_STREAM_INTERNAL_SERVER_ERROR;
+
+done:
+
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_stream_lua_wev_handler(ngx_stream_lua_request_t *r)
+{
+ ngx_int_t rc;
+ ngx_event_t *wev;
+ ngx_connection_t *c;
+
+ ngx_stream_lua_ctx_t *ctx;
+
+ ngx_stream_lua_srv_conf_t *cllscf;
+
+ ngx_stream_lua_socket_tcp_upstream_t *u;
+
+ c = r->connection;
+ wev = c->write;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug3(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "lua run write event handler: timedout:%ud, ready:%ud, "
+ "writing_raw_req_socket:%ud",
+ wev->timedout, wev->ready, ctx->writing_raw_req_socket);
+
+ cllscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module);
+
+ if (wev->timedout && !ctx->writing_raw_req_socket) {
+ if (!wev->delayed) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+ "client timed out");
+ c->timedout = 1;
+
+ goto flush_coros;
+ }
+
+ wev->timedout = 0;
+ wev->delayed = 0;
+
+ if (!wev->ready) {
+ ngx_add_timer(wev, cllscf->send_timeout);
+
+ if (ngx_handle_write_event(wev, cllscf->send_lowat) != NGX_OK) {
+ if (ctx->entered_content_phase) {
+ ngx_stream_lua_finalize_request(r, NGX_ERROR);
+ }
+
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ if (!wev->ready && !wev->timedout) {
+ goto useless;
+ }
+
+ if (ctx->writing_raw_req_socket) {
+ ctx->writing_raw_req_socket = 0;
+
+ u = ctx->downstream;
+ if (u == NULL) {
+ return NGX_ERROR;
+ }
+
+ u->write_event_handler(r, u);
+ return NGX_DONE;
+ }
+
+ if (c->buffered) {
+
+ rc = ngx_stream_lua_flush_pending_output(r, ctx);
+
+ dd("flush pending output returned %d, c->error: %d", (int) rc,
+ c->error);
+
+ if (rc != NGX_ERROR && rc != NGX_OK) {
+ goto useless;
+ }
+
+ /* when rc == NGX_ERROR, c->error must be set */
+ }
+
+flush_coros:
+
+ dd("ctx->flushing_coros: %d", (int) ctx->flushing_coros);
+
+ if (ctx->flushing_coros) {
+ return ngx_stream_lua_process_flushing_coroutines(r, ctx);
+ }
+
+ /* ctx->flushing_coros == 0 */
+
+useless:
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "useless lua write event handler");
+
+ if (ctx->entered_content_phase) {
+ return NGX_OK;
+ }
+
+ return NGX_DONE;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_process_flushing_coroutines(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx)
+{
+ ngx_int_t rc, n;
+ ngx_uint_t i;
+ ngx_list_part_t *part;
+
+ ngx_stream_lua_co_ctx_t *coctx;
+
+ dd("processing flushing coroutines");
+
+ coctx = &ctx->entry_co_ctx;
+ n = ctx->flushing_coros;
+
+ if (coctx->flushing) {
+ coctx->flushing = 0;
+
+ ctx->flushing_coros--;
+ n--;
+ ctx->cur_co_ctx = coctx;
+
+ rc = ngx_stream_lua_flush_resume_helper(r, ctx);
+ if (rc == NGX_ERROR || rc >= NGX_OK) {
+ return rc;
+ }
+
+ /* rc == NGX_DONE */
+ }
+
+ if (n) {
+
+ if (ctx->user_co_ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ part = &ctx->user_co_ctx->part;
+ coctx = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ coctx = part->elts;
+ i = 0;
+ }
+
+ if (coctx[i].flushing) {
+ coctx[i].flushing = 0;
+ ctx->flushing_coros--;
+ n--;
+ ctx->cur_co_ctx = &coctx[i];
+
+ rc = ngx_stream_lua_flush_resume_helper(r, ctx);
+ if (rc == NGX_ERROR || rc >= NGX_OK) {
+ return rc;
+ }
+
+ /* rc == NGX_DONE */
+
+ if (n == 0) {
+ return NGX_DONE;
+ }
+ }
+ }
+ }
+
+ if (n) {
+ return NGX_ERROR;
+ }
+
+ return NGX_DONE;
+}
+
+
+static ngx_int_t
+ngx_stream_lua_flush_pending_output(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx)
+{
+ ngx_int_t rc;
+ ngx_chain_t *cl;
+ ngx_event_t *wev;
+ ngx_connection_t *c;
+
+ ngx_stream_lua_srv_conf_t *cllscf;
+
+ c = r->connection;
+ wev = c->write;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "lua flushing output: buffered 0x%uxd",
+ c->buffered);
+
+ if (ctx->busy_bufs) {
+ /* FIXME since cosockets also share this busy_bufs chain, this condition
+ * might not be strong enough. better use separate busy_bufs chains. */
+ rc = ngx_stream_lua_output_filter(r, NULL);
+
+ } else {
+ cl = ngx_stream_lua_get_flush_chain(r, ctx);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ rc = ngx_stream_lua_output_filter(r, cl);
+ }
+
+ dd("output filter returned %d", (int) rc);
+
+ if (rc == NGX_ERROR || rc > NGX_OK) {
+ return rc;
+ }
+
+ if (c->buffered) {
+
+ cllscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module);
+
+ if (!wev->delayed) {
+ ngx_add_timer(wev, cllscf->send_timeout);
+ }
+
+ if (ngx_handle_write_event(wev, cllscf->send_lowat) != NGX_OK) {
+ if (ctx->entered_content_phase) {
+ ngx_stream_lua_finalize_request(r, NGX_ERROR);
+ }
+
+ return NGX_ERROR;
+ }
+
+ if (ctx->flushing_coros) {
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "lua flush still waiting: buffered 0x%uxd",
+ c->buffered);
+
+ return NGX_DONE;
+ }
+
+ } else {
+#if 1
+ if (wev->timer_set && !wev->delayed) {
+ ngx_del_timer(wev);
+ }
+#endif
+ }
+
+ return NGX_OK;
+}
+
+
+u_char *
+ngx_stream_lua_digest_hex(u_char *dest, const u_char *buf, int buf_len)
+{
+ ngx_md5_t md5;
+ u_char md5_buf[MD5_DIGEST_LENGTH];
+
+ ngx_md5_init(&md5);
+ ngx_md5_update(&md5, buf, buf_len);
+ ngx_md5_final(md5_buf, &md5);
+
+ return ngx_hex_dump(dest, md5_buf, sizeof(md5_buf));
+}
+
+
+void
+ngx_stream_lua_set_multi_value_table(lua_State *L, int index)
+{
+ if (index < 0) {
+ index = lua_gettop(L) + index + 1;
+ }
+
+ lua_pushvalue(L, -2); /* stack: table key value key */
+ lua_rawget(L, index);
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 1); /* stack: table key value */
+ lua_rawset(L, index); /* stack: table */
+
+ } else {
+ if (!lua_istable(L, -1)) {
+ /* just inserted one value */
+ lua_createtable(L, 4, 0);
+ /* stack: table key value value table */
+ lua_insert(L, -2);
+ /* stack: table key value table value */
+ lua_rawseti(L, -2, 1);
+ /* stack: table key value table */
+ lua_insert(L, -2);
+ /* stack: table key table value */
+
+ lua_rawseti(L, -2, 2); /* stack: table key table */
+
+ lua_rawset(L, index); /* stack: table */
+
+ } else {
+ /* stack: table key value table */
+ lua_insert(L, -2); /* stack: table key table value */
+
+ lua_rawseti(L, -2, lua_objlen(L, -2) + 1);
+ /* stack: table key table */
+ lua_pop(L, 2); /* stack: table */
+ }
+ }
+}
+
+
+uintptr_t
+ngx_stream_lua_escape_uri(u_char *dst, u_char *src, size_t size,
+ ngx_uint_t type)
+{
+ ngx_uint_t n;
+ uint32_t *escape;
+ static u_char hex[] = "0123456789ABCDEF";
+
+ /* " ", "#", "%", "?", %00-%1F, %7F-%FF */
+
+ static uint32_t uri[] = {
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+
+ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
+ 0x80000029, /* 1000 0000 0000 0000 0000 0000 0010 1001 */
+
+ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+
+ /* ~}| {zyx wvut srqp onml kjih gfed cba` */
+ 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */
+
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ };
+
+ /* " ", "#", "%", "+", "?", %00-%1F, %7F-%FF */
+
+ static uint32_t args[] = {
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+
+ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
+ 0x80000829, /* 1000 0000 0000 0000 0000 1000 0010 1001 */
+
+ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+
+ /* ~}| {zyx wvut srqp onml kjih gfed cba` */
+ 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */
+
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ };
+
+ /* not ALPHA, DIGIT, "-", ".", "_", "~" */
+
+ static uint32_t uri_component[] = {
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+
+ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
+ 0xfc00987d, /* 1111 1100 0000 0000 1001 1000 0111 1101 */
+
+ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
+ 0x78000001, /* 0111 1000 0000 0000 0000 0000 0000 0001 */
+
+ /* ~}| {zyx wvut srqp onml kjih gfed cba` */
+ 0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */
+
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ };
+
+ /* " ", "#", """, "%", "'", %00-%1F, %7F-%FF */
+
+ static uint32_t html[] = {
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+
+ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
+ 0x000000ad, /* 0000 0000 0000 0000 0000 0000 1010 1101 */
+
+ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+
+ /* ~}| {zyx wvut srqp onml kjih gfed cba` */
+ 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */
+
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ };
+
+ /* " ", """, "%", "'", %00-%1F, %7F-%FF */
+
+ static uint32_t refresh[] = {
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+
+ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
+ 0x00000085, /* 0000 0000 0000 0000 0000 0000 1000 0101 */
+
+ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+
+ /* ~}| {zyx wvut srqp onml kjih gfed cba` */
+ 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */
+
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ };
+
+ /* " ", "%", %00-%1F */
+
+ static uint32_t memcached[] = {
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+
+ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
+ 0x00000021, /* 0000 0000 0000 0000 0000 0000 0010 0001 */
+
+ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+
+ /* ~}| {zyx wvut srqp onml kjih gfed cba` */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+ };
+
+ /* mail_auth is the same as memcached */
+
+ static uint32_t *map[] =
+ { uri, args, uri_component, html, refresh, memcached, memcached };
+
+ escape = map[type];
+
+ if (dst == NULL) {
+
+ /* find the number of the characters to be escaped */
+
+ n = 0;
+
+ while (size) {
+ if (escape[*src >> 5] & (1 << (*src & 0x1f))) {
+ n++;
+ }
+ src++;
+ size--;
+ }
+
+ return (uintptr_t) n;
+ }
+
+ while (size) {
+ if (escape[*src >> 5] & (1 << (*src & 0x1f))) {
+ *dst++ = '%';
+ *dst++ = hex[*src >> 4];
+ *dst++ = hex[*src & 0xf];
+ src++;
+
+ } else {
+ *dst++ = *src++;
+ }
+ size--;
+ }
+
+ return (uintptr_t) dst;
+}
+
+
+/* XXX we also decode '+' to ' ' */
+void
+ngx_stream_lua_unescape_uri(u_char **dst, u_char **src, size_t size,
+ ngx_uint_t type)
+{
+ u_char *d, *s, ch, c, decoded;
+ enum {
+ sw_usual = 0,
+ sw_quoted,
+ sw_quoted_second
+ } state;
+
+ d = *dst;
+ s = *src;
+
+ state = 0;
+ decoded = 0;
+
+ while (size--) {
+
+ ch = *s++;
+
+ switch (state) {
+ case sw_usual:
+ if (ch == '?'
+ && (type & (NGX_UNESCAPE_URI|NGX_UNESCAPE_REDIRECT)))
+ {
+ *d++ = ch;
+ goto done;
+ }
+
+ if (ch == '%') {
+ state = sw_quoted;
+ break;
+ }
+
+ if (ch == '+') {
+ *d++ = ' ';
+ break;
+ }
+
+ *d++ = ch;
+ break;
+
+ case sw_quoted:
+
+ if (ch >= '0' && ch <= '9') {
+ decoded = (u_char) (ch - '0');
+ state = sw_quoted_second;
+ break;
+ }
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'f') {
+ decoded = (u_char) (c - 'a' + 10);
+ state = sw_quoted_second;
+ break;
+ }
+
+ /* the invalid quoted character */
+
+ state = sw_usual;
+
+ *d++ = ch;
+
+ break;
+
+ case sw_quoted_second:
+
+ state = sw_usual;
+
+ if (ch >= '0' && ch <= '9') {
+ ch = (u_char) ((decoded << 4) + ch - '0');
+
+ if (type & NGX_UNESCAPE_REDIRECT) {
+ if (ch > '%' && ch < 0x7f) {
+ *d++ = ch;
+ break;
+ }
+
+ *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1);
+ break;
+ }
+
+ *d++ = ch;
+
+ break;
+ }
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'f') {
+ ch = (u_char) ((decoded << 4) + c - 'a' + 10);
+
+ if (type & NGX_UNESCAPE_URI) {
+ if (ch == '?') {
+ *d++ = ch;
+ goto done;
+ }
+
+ *d++ = ch;
+ break;
+ }
+
+ if (type & NGX_UNESCAPE_REDIRECT) {
+ if (ch == '?') {
+ *d++ = ch;
+ goto done;
+ }
+
+ if (ch > '%' && ch < 0x7f) {
+ *d++ = ch;
+ break;
+ }
+
+ *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1);
+ break;
+ }
+
+ *d++ = ch;
+
+ break;
+ }
+
+ /* the invalid quoted character */
+
+ break;
+ }
+ }
+
+done:
+
+ *dst = d;
+ *src = s;
+}
+
+
+static int
+ngx_stream_lua_req_socket(lua_State *L)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+
+ r = ngx_stream_lua_get_req(L);
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return luaL_error(L, "no ctx found");
+ }
+
+ ngx_stream_lua_check_fake_request2(L, r, ctx);
+
+ switch (r->connection->type) {
+ case SOCK_STREAM:
+ return ngx_stream_lua_req_socket_tcp(L);
+
+ case SOCK_DGRAM:
+ return ngx_stream_lua_req_socket_udp(L);
+ }
+
+ /* shouldn't happen */
+ ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0,
+ "stream unexpected connection type: %d",
+ r->connection->type);
+
+ ngx_stream_lua_assert(0);
+
+ return luaL_error(L, "unexpected connection type");
+}
+
+
+void
+ngx_stream_lua_inject_req_api(ngx_log_t *log, lua_State *L)
+{
+ /* ngx.req table */
+
+ lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* .req */
+
+
+ lua_pushcfunction(L, ngx_stream_lua_req_socket);
+ lua_setfield(L, -2, "socket");
+
+ lua_setfield(L, -2, "req");
+}
+
+
+
+
+static ngx_int_t
+ngx_stream_lua_handle_exit(lua_State *L, ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx)
+{
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua thread aborting request with status %d",
+ ctx->exit_code);
+
+ ngx_stream_lua_cleanup_pending_operation(ctx->cur_co_ctx);
+
+ ngx_stream_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 1);
+
+ ctx->cur_co_ctx->co_status = NGX_STREAM_LUA_CO_DEAD;
+
+
+ ngx_stream_lua_request_cleanup(ctx, 0);
+
+ if (r->connection->fd == (ngx_socket_t) -1) { /* fake request */
+ return ctx->exit_code;
+ }
+
+
+ return ctx->exit_code;
+}
+
+
+void
+ngx_stream_lua_process_args_option(ngx_stream_lua_request_t *r, lua_State *L,
+ int table, ngx_str_t *args)
+{
+ u_char *key;
+ size_t key_len;
+ u_char *value;
+ size_t value_len;
+ size_t len = 0;
+ size_t key_escape = 0;
+ uintptr_t total_escape = 0;
+ int n;
+ int i;
+ u_char *p;
+
+ if (table < 0) {
+ table = lua_gettop(L) + table + 1;
+ }
+
+ n = 0;
+ lua_pushnil(L);
+ while (lua_next(L, table) != 0) {
+ if (lua_type(L, -2) != LUA_TSTRING) {
+ luaL_error(L, "attempt to use a non-string key in the "
+ "\"args\" option table");
+ return;
+ }
+
+ key = (u_char *) lua_tolstring(L, -2, &key_len);
+
+ key_escape = 2 * ngx_stream_lua_escape_uri(NULL, key, key_len,
+ NGX_ESCAPE_URI_COMPONENT);
+ total_escape += key_escape;
+
+ switch (lua_type(L, -1)) {
+ case LUA_TNUMBER:
+ case LUA_TSTRING:
+ value = (u_char *) lua_tolstring(L, -1, &value_len);
+
+ total_escape += 2 * ngx_stream_lua_escape_uri(NULL, value,
+ value_len,
+ NGX_ESCAPE_URI_COMPONENT);
+
+ len += key_len + value_len + (sizeof("=") - 1);
+ n++;
+
+ break;
+
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, -1)) {
+ len += key_len;
+ n++;
+ }
+
+ break;
+
+ case LUA_TTABLE:
+
+ i = 0;
+ lua_pushnil(L);
+ while (lua_next(L, -2) != 0) {
+ if (lua_isboolean(L, -1)) {
+ if (lua_toboolean(L, -1)) {
+ len += key_len;
+
+ } else {
+ lua_pop(L, 1);
+ continue;
+ }
+
+ } else {
+ value = (u_char *) lua_tolstring(L, -1, &value_len);
+
+ if (value == NULL) {
+ luaL_error(L, "attempt to use %s as query arg value",
+ luaL_typename(L, -1));
+ return;
+ }
+
+ total_escape +=
+ 2 * ngx_stream_lua_escape_uri(NULL, value,
+ value_len,
+ NGX_ESCAPE_URI_COMPONENT);
+
+ len += key_len + value_len + (sizeof("=") - 1);
+ }
+
+ if (i++ > 0) {
+ total_escape += key_escape;
+ }
+
+ n++;
+ lua_pop(L, 1);
+ }
+
+ break;
+
+ default:
+ luaL_error(L, "attempt to use %s as query arg value",
+ luaL_typename(L, -1));
+ return;
+ }
+
+ lua_pop(L, 1);
+ }
+
+ len += (size_t) total_escape;
+
+ if (n > 1) {
+ len += (n - 1) * (sizeof("&") - 1);
+ }
+
+ dd("len 1: %d", (int) len);
+
+ if (r) {
+ p = ngx_palloc(r->pool, len);
+ if (p == NULL) {
+ luaL_error(L, "no memory");
+ return;
+ }
+
+ } else {
+ p = lua_newuserdata(L, len);
+ }
+
+ args->data = p;
+ args->len = len;
+
+ i = 0;
+ lua_pushnil(L);
+ while (lua_next(L, table) != 0) {
+ key = (u_char *) lua_tolstring(L, -2, &key_len);
+
+ switch (lua_type(L, -1)) {
+ case LUA_TNUMBER:
+ case LUA_TSTRING:
+
+ if (total_escape) {
+ p = (u_char *) ngx_stream_lua_escape_uri(p, key, key_len,
+ NGX_ESCAPE_URI_COMPONENT);
+
+ } else {
+ dd("shortcut: no escape required");
+
+ p = ngx_copy(p, key, key_len);
+ }
+
+ *p++ = '=';
+
+ value = (u_char *) lua_tolstring(L, -1, &value_len);
+
+ if (total_escape) {
+ p = (u_char *) ngx_stream_lua_escape_uri(p, value, value_len,
+ NGX_ESCAPE_URI_COMPONENT);
+
+ } else {
+ p = ngx_copy(p, value, value_len);
+ }
+
+ if (i != n - 1) {
+ /* not the last pair */
+ *p++ = '&';
+ }
+
+ i++;
+
+ break;
+
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, -1)) {
+ if (total_escape) {
+ p = (u_char *) ngx_stream_lua_escape_uri(p, key, key_len,
+ NGX_ESCAPE_URI_COMPONENT);
+
+ } else {
+ dd("shortcut: no escape required");
+
+ p = ngx_copy(p, key, key_len);
+ }
+
+ if (i != n - 1) {
+ /* not the last pair */
+ *p++ = '&';
+ }
+
+ i++;
+ }
+
+ break;
+
+ case LUA_TTABLE:
+
+ lua_pushnil(L);
+ while (lua_next(L, -2) != 0) {
+
+ if (lua_isboolean(L, -1)) {
+ if (lua_toboolean(L, -1)) {
+ if (total_escape) {
+ p = (u_char *) ngx_stream_lua_escape_uri(p, key,
+ key_len,
+ NGX_ESCAPE_URI_COMPONENT);
+
+ } else {
+ dd("shortcut: no escape required");
+
+ p = ngx_copy(p, key, key_len);
+ }
+
+ } else {
+ lua_pop(L, 1);
+ continue;
+ }
+
+ } else {
+
+ if (total_escape) {
+ p = (u_char *)
+ ngx_stream_lua_escape_uri(p, key,
+ key_len,
+ NGX_ESCAPE_URI_COMPONENT);
+
+ } else {
+ dd("shortcut: no escape required");
+
+ p = ngx_copy(p, key, key_len);
+ }
+
+ *p++ = '=';
+
+ value = (u_char *) lua_tolstring(L, -1, &value_len);
+
+ if (total_escape) {
+ p = (u_char *)
+ ngx_stream_lua_escape_uri(p, value,
+ value_len,
+ NGX_ESCAPE_URI_COMPONENT);
+
+ } else {
+ p = ngx_copy(p, value, value_len);
+ }
+ }
+
+ if (i != n - 1) {
+ /* not the last pair */
+ *p++ = '&';
+ }
+
+ i++;
+ lua_pop(L, 1);
+ }
+
+ break;
+
+ default:
+ luaL_error(L, "should not reach here");
+ return;
+ }
+
+ lua_pop(L, 1);
+ }
+
+ if (p - args->data != (ssize_t) len) {
+ luaL_error(L, "buffer error: %d != %d",
+ (int) (p - args->data), (int) len);
+ return;
+ }
+}
+
+
+
+/* XXX ngx_open_and_stat_file is static in the core. sigh. */
+ngx_int_t
+ngx_stream_lua_open_and_stat_file(u_char *name, ngx_open_file_info_t *of,
+ ngx_log_t *log)
+{
+ ngx_fd_t fd;
+ ngx_file_info_t fi;
+
+ if (of->fd != NGX_INVALID_FILE) {
+
+ if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) {
+ of->failed = ngx_file_info_n;
+ goto failed;
+ }
+
+ if (of->uniq == ngx_file_uniq(&fi)) {
+ goto done;
+ }
+
+ } else if (of->test_dir) {
+
+ if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) {
+ of->failed = ngx_file_info_n;
+ goto failed;
+ }
+
+ if (ngx_is_dir(&fi)) {
+ goto done;
+ }
+ }
+
+ if (!of->log) {
+
+ /*
+ * Use non-blocking open() not to hang on FIFO files, etc.
+ * This flag has no effect on a regular files.
+ */
+
+ fd = ngx_open_file(name, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,
+ NGX_FILE_OPEN, 0);
+
+ } else {
+ fd = ngx_open_file(name, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN,
+ NGX_FILE_DEFAULT_ACCESS);
+ }
+
+ if (fd == NGX_INVALID_FILE) {
+ of->failed = ngx_open_file_n;
+ goto failed;
+ }
+
+ if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_CRIT, log, ngx_errno,
+ ngx_fd_info_n " \"%s\" failed", name);
+
+ if (ngx_close_file(fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", name);
+ }
+
+ of->fd = NGX_INVALID_FILE;
+
+ return NGX_ERROR;
+ }
+
+ if (ngx_is_dir(&fi)) {
+ if (ngx_close_file(fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", name);
+ }
+
+ of->fd = NGX_INVALID_FILE;
+
+ } else {
+ of->fd = fd;
+
+ if (of->directio <= ngx_file_size(&fi)) {
+ if (ngx_directio_on(fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ ngx_directio_on_n " \"%s\" failed", name);
+
+ } else {
+ of->is_directio = 1;
+ }
+ }
+ }
+
+done:
+
+ of->uniq = ngx_file_uniq(&fi);
+ of->mtime = ngx_file_mtime(&fi);
+ of->size = ngx_file_size(&fi);
+ of->fs_size = ngx_file_fs_size(&fi);
+ of->is_dir = ngx_is_dir(&fi);
+ of->is_file = ngx_is_file(&fi);
+ of->is_link = ngx_is_link(&fi);
+ of->is_exec = ngx_is_exec(&fi);
+
+ return NGX_OK;
+
+failed:
+
+ of->fd = NGX_INVALID_FILE;
+ of->err = ngx_errno;
+
+ return NGX_ERROR;
+}
+
+
+ngx_chain_t *
+ngx_stream_lua_chain_get_free_buf(ngx_log_t *log, ngx_pool_t *p,
+ ngx_chain_t **free, size_t len)
+{
+ ngx_buf_t *b;
+ ngx_chain_t *cl;
+ u_char *start, *end;
+
+ const ngx_buf_tag_t tag = (ngx_buf_tag_t) &ngx_stream_lua_module;
+
+ if (*free) {
+ cl = *free;
+ *free = cl->next;
+ cl->next = NULL;
+
+ b = cl->buf;
+ start = b->start;
+ end = b->end;
+ if (start && (size_t) (end - start) >= len) {
+ ngx_log_debug4(NGX_LOG_DEBUG_STREAM, log, 0,
+ "lua reuse free buf memory %O >= %uz, cl:%p, p:%p",
+ (off_t) (end - start), len, cl, start);
+
+ ngx_memzero(b, sizeof(ngx_buf_t));
+
+ b->start = start;
+ b->pos = start;
+ b->last = start;
+ b->end = end;
+ b->tag = tag;
+
+ if (len) {
+ b->temporary = 1;
+ }
+
+ return cl;
+ }
+
+ ngx_log_debug4(NGX_LOG_DEBUG_STREAM, log, 0,
+ "lua reuse free buf chain, but reallocate memory "
+ "because %uz >= %O, cl:%p, p:%p", len,
+ (off_t) (b->end - b->start), cl, b->start);
+
+ if (ngx_buf_in_memory(b) && b->start) {
+ ngx_pfree(p, b->start);
+ }
+
+ ngx_memzero(b, sizeof(ngx_buf_t));
+
+ if (len == 0) {
+ return cl;
+ }
+
+ b->start = ngx_palloc(p, len);
+ if (b->start == NULL) {
+ return NULL;
+ }
+
+ b->end = b->start + len;
+
+ dd("buf start: %p", cl->buf->start);
+
+ b->pos = b->start;
+ b->last = b->start;
+ b->tag = tag;
+ b->temporary = 1;
+
+ return cl;
+ }
+
+ cl = ngx_alloc_chain_link(p);
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, log, 0,
+ "lua allocate new chainlink and new buf of size %uz, cl:%p",
+ len, cl);
+
+ cl->buf = len ? ngx_create_temp_buf(p, len) : ngx_calloc_buf(p);
+ if (cl->buf == NULL) {
+ return NULL;
+ }
+
+ dd("buf start: %p", cl->buf->start);
+
+ cl->buf->tag = tag;
+ cl->next = NULL;
+
+ return cl;
+}
+
+
+static int
+ngx_stream_lua_thread_traceback(lua_State *L, lua_State *co,
+ ngx_stream_lua_co_ctx_t *coctx)
+{
+ int base;
+ int level, coid;
+ lua_Debug ar;
+
+ base = lua_gettop(L);
+ lua_checkstack(L, 3);
+ lua_pushliteral(L, "stack traceback:");
+ coid = 0;
+
+ while (co) {
+
+ if (coid >= NGX_STREAM_LUA_BT_MAX_COROS) {
+ break;
+ }
+
+ lua_checkstack(L, 2);
+ lua_pushfstring(L, "\ncoroutine %d:", coid++);
+
+ level = 0;
+
+ while (lua_getstack(co, level++, &ar)) {
+
+ lua_checkstack(L, 5);
+
+ if (level > NGX_STREAM_LUA_BT_DEPTH) {
+ lua_pushliteral(L, "\n\t...");
+ break;
+ }
+
+ lua_pushliteral(L, "\n\t");
+ lua_getinfo(co, "Snl", &ar);
+ lua_pushfstring(L, "%s:", ar.short_src);
+
+ if (ar.currentline > 0) {
+ lua_pushfstring(L, "%d:", ar.currentline);
+ }
+
+ if (*ar.namewhat != '\0') { /* is there a name? */
+ lua_pushfstring(L, " in function " LUA_QS, ar.name);
+
+ } else {
+ if (*ar.what == 'm') { /* main? */
+ lua_pushliteral(L, " in main chunk");
+
+ } else if (*ar.what == 'C' || *ar.what == 't') {
+ lua_pushliteral(L, " ?"); /* C function or tail call */
+
+ } else {
+ lua_pushfstring(L, " in function <%s:%d>",
+ ar.short_src, ar.linedefined);
+ }
+ }
+ }
+
+ if (lua_gettop(L) - base >= 15) {
+ lua_concat(L, lua_gettop(L) - base);
+ }
+
+ /* check if the coroutine has a parent coroutine*/
+ coctx = coctx->parent_co_ctx;
+ if (!coctx || coctx->co_status == NGX_STREAM_LUA_CO_DEAD) {
+ break;
+ }
+
+ co = coctx->co;
+ }
+
+ lua_concat(L, lua_gettop(L) - base);
+ return 1;
+}
+
+
+int
+ngx_stream_lua_traceback(lua_State *L)
+{
+ if (!lua_isstring(L, 1)) { /* 'message' not a string? */
+ return 1; /* keep it intact */
+ }
+
+ lua_getglobal(L, "debug");
+ if (!lua_istable(L, -1)) {
+ lua_pop(L, 1);
+ return 1;
+ }
+
+ lua_getfield(L, -1, "traceback");
+ if (!lua_isfunction(L, -1)) {
+ lua_pop(L, 2);
+ return 1;
+ }
+
+ lua_pushvalue(L, 1); /* pass error message */
+ lua_pushinteger(L, 2); /* skip this function and traceback */
+ lua_call(L, 2, 1); /* call debug.traceback */
+ return 1;
+}
+
+
+
+
+ngx_stream_lua_co_ctx_t *
+ngx_stream_lua_get_co_ctx(lua_State *L, ngx_stream_lua_ctx_t *ctx)
+{
+#ifdef HAVE_LUA_EXDATA2
+ return (ngx_stream_lua_co_ctx_t *) lua_getexdata2(L);
+#else
+ ngx_uint_t i;
+ ngx_list_part_t *part;
+
+ ngx_stream_lua_co_ctx_t *coctx;
+
+ if (L == ctx->entry_co_ctx.co) {
+ return &ctx->entry_co_ctx;
+ }
+
+ if (ctx->user_co_ctx == NULL) {
+ return NULL;
+ }
+
+ part = &ctx->user_co_ctx->part;
+ coctx = part->elts;
+
+ /* FIXME: we should use rbtree here to prevent O(n) lookup overhead */
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ coctx = part->elts;
+ i = 0;
+ }
+
+ if (coctx[i].co == L) {
+ return &coctx[i];
+ }
+ }
+
+ return NULL;
+#endif
+}
+
+
+ngx_stream_lua_co_ctx_t *
+ngx_stream_lua_create_co_ctx(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx)
+{
+ ngx_stream_lua_co_ctx_t *coctx;
+
+ if (ctx->user_co_ctx == NULL) {
+ ctx->user_co_ctx = ngx_list_create(r->pool, 4,
+ sizeof(ngx_stream_lua_co_ctx_t));
+ if (ctx->user_co_ctx == NULL) {
+ return NULL;
+ }
+ }
+
+ coctx = ngx_list_push(ctx->user_co_ctx);
+ if (coctx == NULL) {
+ return NULL;
+ }
+
+ ngx_memzero(coctx, sizeof(ngx_stream_lua_co_ctx_t));
+
+ coctx->co_ref = LUA_NOREF;
+
+ return coctx;
+}
+
+
+/* this is for callers other than the content handler */
+ngx_int_t
+ngx_stream_lua_run_posted_threads(ngx_connection_t *c, lua_State *L,
+ ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx, ngx_uint_t nreqs)
+{
+ ngx_int_t rc;
+
+ ngx_stream_lua_posted_thread_t *pt;
+
+ for ( ;; ) {
+ if (c->destroyed || c->requests != nreqs) {
+ return NGX_DONE;
+ }
+
+ pt = ctx->posted_threads;
+ if (pt == NULL) {
+ return NGX_DONE;
+ }
+
+ ctx->posted_threads = pt->next;
+
+ ngx_stream_lua_probe_run_posted_thread(r, pt->co_ctx->co,
+ (int) pt->co_ctx->co_status);
+
+ if (pt->co_ctx->co_status != NGX_STREAM_LUA_CO_RUNNING) {
+ continue;
+ }
+
+ ctx->cur_co_ctx = pt->co_ctx;
+
+ rc = ngx_stream_lua_run_thread(L, r, ctx, 0);
+
+ if (rc == NGX_AGAIN) {
+ continue;
+ }
+
+ if (rc == NGX_DONE) {
+ ngx_stream_lua_finalize_request(r, NGX_DONE);
+ continue;
+ }
+
+ /* rc == NGX_ERROR || rc >= NGX_OK */
+
+ if (ctx->entered_content_phase) {
+ ngx_stream_lua_finalize_request(r, rc);
+ }
+
+ return rc;
+ }
+
+ /* impossible to reach here */
+}
+
+
+ngx_int_t
+ngx_stream_lua_post_thread(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx, ngx_stream_lua_co_ctx_t *coctx)
+{
+ ngx_stream_lua_posted_thread_t **p;
+ ngx_stream_lua_posted_thread_t *pt;
+
+ pt = ngx_palloc(r->pool, sizeof(ngx_stream_lua_posted_thread_t));
+ if (pt == NULL) {
+ return NGX_ERROR;
+ }
+
+ pt->co_ctx = coctx;
+ pt->next = NULL;
+
+ for (p = &ctx->posted_threads; *p; p = &(*p)->next) { /* void */ }
+
+ *p = pt;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_stream_lua_finalize_threads(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx, lua_State *L)
+{
+#ifdef NGX_LUA_USE_ASSERT
+ int top;
+#endif
+ int inited = 0, ref;
+ ngx_uint_t i;
+ ngx_list_part_t *part;
+
+ ngx_stream_lua_co_ctx_t *cc, *coctx;
+
+#ifdef NGX_LUA_USE_ASSERT
+ top = lua_gettop(L);
+#endif
+
+#if 1
+ coctx = ctx->on_abort_co_ctx;
+ if (coctx && coctx->co_ref != LUA_NOREF) {
+ if (coctx->co_status != NGX_STREAM_LUA_CO_SUSPENDED) {
+ /* the on_abort thread contributes to the coctx->uthreads
+ * counter only when it actually starts running */
+ ngx_stream_lua_cleanup_pending_operation(coctx);
+ ctx->uthreads--;
+ }
+
+ ngx_stream_lua_probe_thread_delete(r, coctx->co, ctx);
+
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ coroutines_key));
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ inited = 1;
+
+ luaL_unref(L, -1, coctx->co_ref);
+ coctx->co_ref = LUA_NOREF;
+
+ coctx->co_status = NGX_STREAM_LUA_CO_DEAD;
+ ctx->on_abort_co_ctx = NULL;
+ }
+#endif
+
+ if (ctx->user_co_ctx) {
+ part = &ctx->user_co_ctx->part;
+ cc = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ cc = part->elts;
+ i = 0;
+ }
+
+ coctx = &cc[i];
+
+ ref = coctx->co_ref;
+
+ if (ref != LUA_NOREF) {
+ ngx_stream_lua_cleanup_pending_operation(coctx);
+
+ ngx_stream_lua_probe_thread_delete(r, coctx->co, ctx);
+
+ if (!inited) {
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ coroutines_key));
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ inited = 1;
+ }
+
+ ngx_stream_lua_assert(lua_gettop(L) - top == 1);
+
+ luaL_unref(L, -1, ref);
+ coctx->co_ref = LUA_NOREF;
+
+ coctx->co_status = NGX_STREAM_LUA_CO_DEAD;
+ ctx->uthreads--;
+ }
+ }
+
+ ctx->user_co_ctx = NULL;
+ }
+
+ ngx_stream_lua_assert(ctx->uthreads == 0);
+
+ coctx = &ctx->entry_co_ctx;
+
+ ref = coctx->co_ref;
+ if (ref != LUA_NOREF) {
+ ngx_stream_lua_cleanup_pending_operation(coctx);
+
+ ngx_stream_lua_probe_thread_delete(r, coctx->co, ctx);
+
+ if (!inited) {
+ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask(
+ coroutines_key));
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ inited = 1;
+ }
+
+ ngx_stream_lua_assert(lua_gettop(L) - top == 1);
+
+ luaL_unref(L, -1, coctx->co_ref);
+ coctx->co_ref = LUA_NOREF;
+ coctx->co_status = NGX_STREAM_LUA_CO_DEAD;
+ }
+
+ if (inited) {
+ lua_pop(L, 1);
+ }
+}
+
+
+static ngx_int_t
+ngx_stream_lua_post_zombie_thread(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_co_ctx_t *parent, ngx_stream_lua_co_ctx_t *thread)
+{
+ ngx_stream_lua_posted_thread_t **p;
+ ngx_stream_lua_posted_thread_t *pt;
+
+ pt = ngx_palloc(r->pool, sizeof(ngx_stream_lua_posted_thread_t));
+ if (pt == NULL) {
+ return NGX_ERROR;
+ }
+
+ pt->co_ctx = thread;
+ pt->next = NULL;
+
+ for (p = &parent->zombie_child_threads; *p; p = &(*p)->next) { /* void */ }
+
+ *p = pt;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_stream_lua_cleanup_zombie_child_uthreads(ngx_stream_lua_request_t *r,
+ lua_State *L, ngx_stream_lua_ctx_t *ctx, ngx_stream_lua_co_ctx_t *coctx)
+{
+ ngx_stream_lua_posted_thread_t *pt;
+
+ for (pt = coctx->zombie_child_threads; pt; pt = pt->next) {
+ if (pt->co_ctx->co_ref != LUA_NOREF) {
+ ngx_stream_lua_del_thread(r, L, ctx, pt->co_ctx);
+ ctx->uthreads--;
+ }
+ }
+
+ coctx->zombie_child_threads = NULL;
+}
+
+
+ngx_int_t
+ngx_stream_lua_check_broken_connection(ngx_stream_lua_request_t *r,
+ ngx_event_t *ev)
+{
+ int n;
+ char buf[1];
+ ngx_err_t err;
+ ngx_int_t event;
+ ngx_connection_t *c;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ev->log, 0,
+ "stream lua check client, write event:%d", ev->write);
+
+ c = r->connection;
+
+ if (c->error) {
+ if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) {
+
+ event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT;
+
+ if (ngx_del_event(ev, event, 0) != NGX_OK) {
+ return NGX_STREAM_INTERNAL_SERVER_ERROR;
+ }
+ }
+
+ return NGX_ERROR;
+ }
+
+
+
+#if (NGX_HAVE_KQUEUE)
+
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+
+ if (!ev->pending_eof) {
+ return NGX_OK;
+ }
+
+ ev->eof = 1;
+
+ if (ev->kq_errno) {
+ ev->error = 1;
+ }
+
+ ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno,
+ "kevent() reported that client prematurely closed "
+ "connection");
+
+ return NGX_ERROR;
+ }
+
+#endif
+
+ n = recv(c->fd, buf, 1, MSG_PEEK);
+
+ err = ngx_socket_errno;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ev->log, err,
+ "http lua recv(): %d", n);
+
+ if (ev->write && (n >= 0 || err == NGX_EAGAIN)) {
+ return NGX_OK;
+ }
+
+ if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) {
+ dd("event is active");
+
+ event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT;
+
+#if 1
+ if (ngx_del_event(ev, event, 0) != NGX_OK) {
+ return NGX_STREAM_INTERNAL_SERVER_ERROR;
+ }
+#endif
+ }
+
+ dd("HERE %d", (int) n);
+
+ if (n > 0) {
+ return NGX_OK;
+ }
+
+ if (n == -1) {
+ if (err == NGX_EAGAIN) {
+ dd("HERE");
+ return NGX_OK;
+ }
+
+ ev->error = 1;
+
+ } else { /* n == 0 */
+ err = 0;
+ }
+
+ ev->eof = 1;
+
+ ngx_log_error(NGX_LOG_INFO, ev->log, err,
+ "stream client prematurely closed connection");
+
+ return NGX_ERROR;
+}
+
+
+void
+ngx_stream_lua_rd_check_broken_connection(ngx_stream_lua_request_t *r)
+{
+ ngx_int_t rc;
+ ngx_event_t *rev;
+ ngx_stream_lua_ctx_t *ctx;
+
+
+ rc = ngx_stream_lua_check_broken_connection(r, r->connection->read);
+
+ if (rc == NGX_OK) {
+ return;
+ }
+
+ /* rc == NGX_ERROR || rc > NGX_OK */
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return;
+ }
+
+ if (ctx->on_abort_co_ctx == NULL) {
+ r->connection->error = 1;
+ ngx_stream_lua_request_cleanup(ctx, 0);
+ ngx_stream_lua_finalize_request(r, rc);
+ return;
+ }
+
+ if (ctx->on_abort_co_ctx->co_status != NGX_STREAM_LUA_CO_SUSPENDED) {
+
+ /* on_abort already run for the current request handler */
+
+ rev = r->connection->read;
+
+ if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && rev->active) {
+ if (ngx_del_event(rev, NGX_READ_EVENT, 0) != NGX_OK) {
+ ngx_stream_lua_request_cleanup(ctx, 0);
+
+ ngx_stream_lua_finalize_request(r,
+ NGX_STREAM_INTERNAL_SERVER_ERROR);
+ return;
+ }
+ }
+
+ return;
+ }
+
+ ctx->uthreads++;
+ ctx->resume_handler = ngx_stream_lua_on_abort_resume;
+ ctx->on_abort_co_ctx->co_status = NGX_STREAM_LUA_CO_RUNNING;
+ ctx->cur_co_ctx = ctx->on_abort_co_ctx;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua waking up the on_abort callback thread");
+
+ if (ctx->entered_content_phase) {
+ r->write_event_handler = ngx_stream_lua_content_wev_handler;
+
+ } else {
+ r->write_event_handler = ngx_stream_lua_core_run_phases;
+ }
+
+ r->write_event_handler(r);
+}
+
+
+static ngx_int_t
+ngx_stream_lua_on_abort_resume(ngx_stream_lua_request_t *r)
+{
+ lua_State *vm;
+ ngx_int_t rc;
+ ngx_uint_t nreqs;
+ ngx_connection_t *c;
+ ngx_stream_lua_ctx_t *ctx;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ctx->resume_handler = ngx_stream_lua_wev_handler;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua resuming the on_abort callback thread");
+
+#if 0
+ ngx_stream_lua_probe_info("tcp resume");
+#endif
+
+ c = r->connection;
+ vm = ngx_stream_lua_get_lua_vm(r, ctx);
+ nreqs = c->requests;
+
+ rc = ngx_stream_lua_run_thread(vm, r, ctx, 0);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua run thread returned %d", rc);
+
+ if (rc == NGX_AGAIN) {
+ return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs);
+ }
+
+ if (rc == NGX_DONE) {
+ ngx_stream_lua_finalize_request(r, NGX_DONE);
+ return ngx_stream_lua_run_posted_threads(c, vm, r, ctx, nreqs);
+ }
+
+ if (ctx->entered_content_phase) {
+ ngx_stream_lua_finalize_request(r, rc);
+ return NGX_DONE;
+ }
+
+ return rc;
+}
+
+
+
+
+void
+ngx_stream_lua_finalize_request(ngx_stream_lua_request_t *r, ngx_int_t rc)
+{
+ ngx_stream_lua_ctx_t *ctx;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx && ctx->cur_co_ctx) {
+ ngx_stream_lua_cleanup_pending_operation(ctx->cur_co_ctx);
+ }
+
+ if (r->connection->fd != (ngx_socket_t) -1) {
+
+ ngx_stream_lua_finalize_real_request(r, rc);
+
+ return;
+ }
+
+ ngx_stream_lua_finalize_fake_request(r, rc);
+}
+
+
+void
+ngx_stream_lua_finalize_fake_request(ngx_stream_lua_request_t *r, ngx_int_t rc)
+{
+ ngx_connection_t *c;
+
+#if (NGX_STREAM_SSL)
+ ngx_ssl_conn_t *ssl_conn;
+
+ ngx_stream_lua_ssl_ctx_t *cctx;
+#endif
+
+ c = r->connection;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "stream lua finalize fake request: %d", rc);
+
+ if (rc == NGX_DONE) {
+
+
+ return;
+ }
+
+ if (rc == NGX_ERROR || rc >= NGX_STREAM_BAD_REQUEST) {
+
+#if (NGX_STREAM_SSL)
+
+ if (r->connection->ssl) {
+ ssl_conn = r->connection->ssl->connection;
+ if (ssl_conn) {
+ c = ngx_ssl_get_connection(ssl_conn);
+
+ if (c && c->ssl) {
+ cctx = ngx_stream_lua_ssl_get_ctx(c->ssl->connection);
+ if (cctx != NULL) {
+ cctx->exit_code = 0;
+ }
+ }
+ }
+ }
+
+#endif
+
+ ngx_stream_lua_close_fake_request(r);
+ return;
+ }
+
+ if (c->read->timer_set) {
+ ngx_del_timer(c->read);
+ }
+
+ if (c->write->timer_set) {
+ c->write->delayed = 0;
+ ngx_del_timer(c->write);
+ }
+
+ ngx_stream_lua_close_fake_request(r);
+}
+
+
+static void
+ngx_stream_lua_close_fake_request(ngx_stream_lua_request_t *r)
+{
+ ngx_connection_t *c;
+
+
+ c = r->connection;
+
+
+
+ ngx_stream_lua_free_fake_request(r);
+ ngx_stream_lua_close_fake_connection(c);
+}
+
+
+void
+ngx_stream_lua_free_fake_request(ngx_stream_lua_request_t *r)
+{
+ ngx_log_t *log;
+
+ ngx_stream_lua_cleanup_t *cln;
+
+ log = r->connection->log;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, log, 0, "stream lua close fake "
+ "request");
+
+ if (r->pool == NULL) {
+ ngx_log_error(NGX_LOG_ALERT, log, 0, "stream lua fake request "
+ "already closed");
+ return;
+ }
+
+ cln = r->cleanup;
+ r->cleanup = NULL;
+
+ while (cln) {
+ if (cln->handler) {
+ cln->handler(cln->data);
+ }
+
+ cln = cln->next;
+ }
+
+
+ r->connection->destroyed = 1;
+}
+
+
+void
+ngx_stream_lua_close_fake_connection(ngx_connection_t *c)
+{
+ ngx_pool_t *pool;
+ ngx_connection_t *saved_c = NULL;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "stream lua close fake stream connection %p", c);
+
+ c->destroyed = 1;
+
+ pool = c->pool;
+
+ if (c->read->timer_set) {
+ ngx_del_timer(c->read);
+ }
+
+ if (c->write->timer_set) {
+ ngx_del_timer(c->write);
+ }
+
+ c->read->closed = 1;
+ c->write->closed = 1;
+
+ /* we temporarily use a valid fd (0) to make ngx_free_connection happy */
+
+ c->fd = 0;
+
+ if (ngx_cycle->files) {
+ saved_c = ngx_cycle->files[0];
+ }
+
+ ngx_free_connection(c);
+
+ c->fd = (ngx_socket_t) -1;
+
+ if (ngx_cycle->files) {
+ ngx_cycle->files[0] = saved_c;
+ }
+
+ if (pool) {
+ ngx_destroy_pool(pool);
+ }
+}
+
+
+ngx_int_t
+ngx_stream_lua_init_vm(lua_State **new_vm, lua_State *parent_vm,
+ ngx_cycle_t *cycle, ngx_pool_t *pool,
+ ngx_stream_lua_main_conf_t *lmcf, ngx_log_t *log,
+ ngx_pool_cleanup_t **pcln)
+{
+ int rc;
+ lua_State *L;
+ ngx_uint_t i;
+ ngx_pool_cleanup_t *cln;
+
+ ngx_stream_lua_preload_hook_t *hook;
+ ngx_stream_lua_vm_state_t *state;
+
+ cln = ngx_pool_cleanup_add(pool, 0);
+ if (cln == NULL) {
+ return NGX_ERROR;
+ }
+
+ /* create new Lua VM instance */
+ L = ngx_stream_lua_new_state(parent_vm, cycle, lmcf, log);
+ if (L == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, log, 0, "lua initialize the "
+ "global Lua VM %p", L);
+
+ /* register cleanup handler for Lua VM */
+ cln->handler = ngx_stream_lua_cleanup_vm;
+
+ state = ngx_alloc(sizeof(ngx_stream_lua_vm_state_t), log);
+ if (state == NULL) {
+ return NGX_ERROR;
+ }
+ state->vm = L;
+ state->count = 1;
+
+ cln->data = state;
+
+ if (lmcf->vm_cleanup == NULL) {
+ /* this assignment will happen only once,
+ * and also only for the main Lua VM */
+ lmcf->vm_cleanup = cln;
+ }
+
+ if (pcln) {
+ *pcln = cln;
+ }
+
+#ifdef OPENRESTY_LUAJIT
+ /* load FFI library first since cdata needs it */
+ luaopen_ffi(L);
+#endif
+
+ if (lmcf->preload_hooks) {
+
+ /* register the 3rd-party module's preload hooks */
+
+ lua_getglobal(L, "package");
+ lua_getfield(L, -1, "preload");
+
+ hook = lmcf->preload_hooks->elts;
+
+ for (i = 0; i < lmcf->preload_hooks->nelts; i++) {
+
+ ngx_stream_lua_probe_register_preload_package(L,
+ hook[i].package);
+
+ lua_pushcfunction(L, hook[i].loader);
+ lua_setfield(L, -2, (char *) hook[i].package);
+ }
+
+ lua_pop(L, 2);
+ }
+
+ *new_vm = L;
+
+ lua_getglobal(L, "require");
+ lua_pushstring(L, "resty.core");
+
+ rc = lua_pcall(L, 1, 1, 0);
+ if (rc != 0) {
+ return NGX_DECLINED;
+ }
+
+#ifdef OPENRESTY_LUAJIT
+ ngx_stream_lua_inject_global_write_guard(L, log);
+#endif
+
+ return NGX_OK;
+}
+
+
+void
+ngx_stream_lua_cleanup_vm(void *data)
+{
+ lua_State *L;
+ ngx_stream_lua_vm_state_t *state = data;
+
+#if (DDEBUG)
+ if (state) {
+ dd("cleanup VM: c:%d, s:%p", (int) state->count, state->vm);
+ }
+#endif
+
+ if (state) {
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "stream lua decrementing the reference count "
+ "for Lua VM: %i", state->count);
+
+ if (--state->count == 0) {
+ L = state->vm;
+
+ ngx_stream_lua_cleanup_conn_pools(L);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ngx_cycle->log, 0,
+ "stream lua close the global Lua VM %p", L);
+ lua_close(L);
+ ngx_free(state);
+ }
+ }
+}
+
+
+ngx_connection_t *
+ngx_stream_lua_create_fake_connection(ngx_pool_t *pool)
+{
+ ngx_log_t *log;
+ ngx_connection_t *c;
+ ngx_connection_t *saved_c = NULL;
+
+ /* (we temporarily use a valid fd (0) to make ngx_get_connection happy) */
+ if (ngx_cycle->files) {
+ saved_c = ngx_cycle->files[0];
+ }
+
+ c = ngx_get_connection(0, ngx_cycle->log);
+
+ if (ngx_cycle->files) {
+ ngx_cycle->files[0] = saved_c;
+ }
+
+ if (c == NULL) {
+ return NULL;
+ }
+
+ c->fd = (ngx_socket_t) -1;
+ c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
+
+ if (pool) {
+ c->pool = pool;
+
+ } else {
+ c->pool = ngx_create_pool(128, c->log);
+ if (c->pool == NULL) {
+ goto failed;
+ }
+ }
+
+ log = ngx_pcalloc(c->pool, sizeof(ngx_log_t));
+ if (log == NULL) {
+ goto failed;
+ }
+
+ c->log = log;
+ c->log->connection = c->number;
+ c->log->action = NULL;
+ c->log->data = NULL;
+
+ c->log_error = NGX_ERROR_INFO;
+
+#if 0
+ c->buffer = ngx_create_temp_buf(c->pool, 2);
+ if (c->buffer == NULL) {
+ goto failed;
+ }
+
+ c->buffer->start[0] = CR;
+ c->buffer->start[1] = LF;
+#endif
+
+ c->error = 1;
+
+ dd("created fake connection: %p", c);
+
+ return c;
+
+failed:
+
+ ngx_stream_lua_close_fake_connection(c);
+ return NULL;
+}
+
+
+ngx_stream_session_t *
+ngx_stream_lua_create_fake_session(ngx_connection_t *c)
+{
+ ngx_stream_session_t *s;
+
+ s = ngx_pcalloc(c->pool, sizeof(ngx_stream_session_t));
+ if (s == NULL) {
+ return NULL;
+ }
+
+ s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_stream_max_module);
+ if (s->ctx == NULL) {
+ return NULL;
+ }
+
+ s->connection = c;
+
+ c->data = s;
+ s->signature = NGX_STREAM_MODULE;
+
+ dd("created fake session %p", s);
+
+ return s;
+}
+
+
+ngx_stream_lua_request_t *
+ngx_stream_lua_create_fake_request(ngx_stream_session_t *s)
+{
+ ngx_stream_lua_request_t *r;
+
+ r = ngx_pcalloc(s->connection->pool, sizeof(ngx_stream_lua_request_t));
+ if (r == NULL) {
+ return NULL;
+ }
+
+ r->connection = s->connection;
+ r->session = s;
+ r->pool = s->connection->pool;
+
+ return r;
+}
+
+
+ngx_int_t
+ngx_stream_lua_report(ngx_log_t *log, lua_State *L, int status,
+ const char *prefix)
+{
+ const char *msg;
+
+ if (status && !lua_isnil(L, -1)) {
+ msg = lua_tostring(L, -1);
+ if (msg == NULL) {
+ msg = "unknown error";
+ }
+
+ ngx_log_error(NGX_LOG_ERR, log, 0, "%s error: %s", prefix, msg);
+ lua_pop(L, 1);
+ }
+
+ /* force a full garbage-collection cycle */
+ lua_gc(L, LUA_GCCOLLECT, 0);
+
+ return status == 0 ? NGX_OK : NGX_ERROR;
+}
+
+
+int
+ngx_stream_lua_do_call(ngx_log_t *log, lua_State *L)
+{
+ int status, base;
+#if (NGX_PCRE)
+ ngx_pool_t *old_pool;
+#endif
+
+ base = lua_gettop(L); /* function index */
+ lua_pushcfunction(L, ngx_stream_lua_traceback);
+ /* push traceback function */
+ lua_insert(L, base); /* put it under chunk and args */
+
+#if (NGX_PCRE)
+ old_pool = ngx_stream_lua_pcre_malloc_init(ngx_cycle->pool);
+#endif
+
+ status = lua_pcall(L, 0, 0, base);
+
+#if (NGX_PCRE)
+ ngx_stream_lua_pcre_malloc_done(old_pool);
+#endif
+
+ lua_remove(L, base);
+
+ return status;
+}
+
+
+static int
+ngx_stream_lua_get_raw_phase_context(lua_State *L)
+{
+ ngx_stream_lua_request_t *r;
+ ngx_stream_lua_ctx_t *ctx;
+
+#ifdef OPENRESTY_LUAJIT
+ r = lua_getexdata(L);
+#else
+ r = lua_touserdata(L, 1);
+#endif
+
+ if (r == NULL) {
+ return 0;
+ }
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return 0;
+ }
+
+ lua_pushinteger(L, (int) ctx->context);
+ return 1;
+}
+
+
+
+
+void
+ngx_stream_lua_cleanup_free(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_cleanup_pt *cleanup)
+{
+ ngx_stream_lua_cleanup_t **last;
+ ngx_stream_lua_cleanup_t *cln;
+ ngx_stream_lua_ctx_t *ctx;
+
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx == NULL) {
+ return;
+ }
+
+
+ cln = (ngx_stream_lua_cleanup_t *)
+ ((u_char *) cleanup - offsetof(ngx_stream_lua_cleanup_t, handler));
+
+ dd("cln: %p, cln->handler: %p, &cln->handler: %p",
+ cln, cln->handler, &cln->handler);
+
+ last = &r->cleanup;
+
+ while (*last) {
+ if (*last == cln) {
+ *last = cln->next;
+
+ cln->next = ctx->free_cleanup;
+ ctx->free_cleanup = cln;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
+ "lua stream cleanup free: %p", cln);
+
+ return;
+ }
+
+ last = &(*last)->next;
+ }
+}
+
+
+#if (NGX_STREAM_LUA_HAVE_SA_RESTART)
+void
+ngx_stream_lua_set_sa_restart(ngx_log_t *log)
+{
+ int *signo;
+ int sigs[] = NGX_STREAM_LUA_SA_RESTART_SIGS;
+ struct sigaction act;
+
+ for (signo = sigs; *signo != 0; signo++) {
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, log, 0,
+ "setting SA_RESTART for signal %d", *signo);
+
+ if (sigaction(*signo, NULL, &act) != 0) {
+ ngx_log_error(NGX_LOG_WARN, log, ngx_errno, "failed to get "
+ "sigaction for signal %d", *signo);
+ }
+
+ act.sa_flags |= SA_RESTART;
+
+ if (sigaction(*signo, &act, NULL) != 0) {
+ ngx_log_error(NGX_LOG_WARN, log, ngx_errno, "failed to set "
+ "sigaction for signal %d", *signo);
+ }
+ }
+}
+#endif
+
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_util.h b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_util.h
new file mode 100644
index 0000000..3ea872d
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_util.h
@@ -0,0 +1,538 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_util.h.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef _NGX_STREAM_LUA_UTIL_H_INCLUDED_
+#define _NGX_STREAM_LUA_UTIL_H_INCLUDED_
+
+
+#ifdef DDEBUG
+#include "ddebug.h"
+#endif
+
+
+#include "ngx_stream_lua_common.h"
+#include "ngx_stream_lua_ssl.h"
+#include "ngx_stream_lua_api.h"
+
+
+#ifndef NGX_UNESCAPE_URI_COMPONENT
+#define NGX_UNESCAPE_URI_COMPONENT 0
+#endif
+
+
+typedef struct {
+ ngx_stream_lua_ffi_str_t key;
+ ngx_stream_lua_ffi_str_t value;
+} ngx_stream_lua_ffi_table_elt_t;
+
+
+/* char whose address we use as the key in Lua vm registry for
+ * user code cache table */
+extern char ngx_stream_lua_code_cache_key;
+
+
+/* key in Lua vm registry for all the "ngx.ctx" tables */
+#define ngx_stream_lua_ctx_tables_key "ngx_lua_ctx_tables"
+
+
+/* char whose address we use as the key in Lua vm registry for
+ * regex cache table */
+extern char ngx_stream_lua_regex_cache_key;
+
+/* char whose address we use as the key in Lua vm registry for
+ * socket connection pool table */
+extern char ngx_stream_lua_socket_pool_key;
+
+/* char whose address we use as the key for the coroutine parent relationship */
+extern char ngx_stream_lua_coroutine_parents_key;
+
+/* coroutine anchoring table key in Lua VM registry */
+extern char ngx_stream_lua_coroutines_key;
+
+/* key to the metatable for ngx.req.get_headers() and ngx.resp.get_headers() */
+extern char ngx_stream_lua_headers_metatable_key;
+
+
+#ifndef ngx_str_set
+#define ngx_str_set(str, text) \
+ (str)->len = sizeof(text) - 1; (str)->data = (u_char *) text
+#endif
+
+
+#define NGX_STREAM_LUA_CONTEXT_YIELDABLE (NGX_STREAM_LUA_CONTEXT_PREREAD \
+ | NGX_STREAM_LUA_CONTEXT_CONTENT \
+ | NGX_STREAM_LUA_CONTEXT_TIMER \
+ | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO \
+ | NGX_STREAM_LUA_CONTEXT_SSL_CERT)
+
+
+#define ngx_stream_lua_context_name(c) \
+ ((c) == NGX_STREAM_LUA_CONTEXT_CONTENT ? "content_by_lua*" \
+ : (c) == NGX_STREAM_LUA_CONTEXT_LOG ? "log_by_lua*" \
+ : (c) == NGX_STREAM_LUA_CONTEXT_TIMER ? "ngx.timer" \
+ : (c) == NGX_STREAM_LUA_CONTEXT_INIT_WORKER ? "init_worker_by_lua*" \
+ : (c) == NGX_STREAM_LUA_CONTEXT_BALANCER ? "balancer_by_lua*" \
+ : (c) == NGX_STREAM_LUA_CONTEXT_PREREAD ? "preread_by_lua*" \
+ : (c) == NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO ? \
+ "ssl_client_hello_by_lua*" \
+ : (c) == NGX_STREAM_LUA_CONTEXT_SSL_CERT ? "ssl_certificate_by_lua*" \
+ : "(unknown)")
+
+
+#define ngx_stream_lua_check_context(L, ctx, flags) \
+ if (!((ctx)->context & (flags))) { \
+ return luaL_error(L, "API disabled in the context of %s", \
+ ngx_stream_lua_context_name((ctx)->context)); \
+ }
+
+
+static ngx_inline ngx_int_t
+ngx_stream_lua_ffi_check_context(ngx_stream_lua_ctx_t *ctx,
+ unsigned flags, u_char *err, size_t *errlen)
+{
+ if (!(ctx->context & flags)) {
+ *errlen = ngx_snprintf(err, *errlen,
+ "API disabled in the context of %s",
+ ngx_stream_lua_context_name((ctx)->context))
+ - err;
+
+ return NGX_DECLINED;
+ }
+
+ return NGX_OK;
+}
+
+
+#define ngx_stream_lua_check_fake_request(L, r) \
+ if ((r)->connection->fd == (ngx_socket_t) -1) { \
+ return luaL_error(L, "API disabled in the current context"); \
+ }
+
+
+#define ngx_stream_lua_check_fake_request2(L, r, ctx) \
+ if ((r)->connection->fd == (ngx_socket_t) -1) { \
+ return luaL_error(L, "API disabled in the context of %s", \
+ ngx_stream_lua_context_name((ctx) \
+ ->context)); \
+ }
+
+
+#define ngx_stream_lua_ssl_get_ctx(ssl_conn) \
+ SSL_get_ex_data(ssl_conn, ngx_stream_lua_ssl_ctx_index)
+
+
+ngx_int_t ngx_stream_lua_init_vm(lua_State **new_vm, lua_State *parent_vm,
+ ngx_cycle_t *cycle, ngx_pool_t *pool,
+ ngx_stream_lua_main_conf_t *lmcf, ngx_log_t *log,
+ ngx_pool_cleanup_t **pcln);
+
+lua_State *ngx_stream_lua_new_thread(ngx_stream_lua_request_t *r, lua_State *l,
+ int *ref);
+
+u_char *ngx_stream_lua_rebase_path(ngx_pool_t *pool, u_char *src, size_t len);
+
+ngx_int_t ngx_stream_lua_send_header_if_needed(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx);
+
+ngx_int_t ngx_stream_lua_send_chain_link(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx, ngx_chain_t *cl);
+
+void ngx_stream_lua_discard_bufs(ngx_pool_t *pool, ngx_chain_t *in);
+
+ngx_int_t ngx_stream_lua_add_copy_chain(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx, ngx_chain_t ***plast, ngx_chain_t *in,
+ ngx_int_t *eof);
+
+void ngx_stream_lua_reset_ctx(ngx_stream_lua_request_t *r, lua_State *L,
+ ngx_stream_lua_ctx_t *ctx);
+
+void ngx_stream_lua_generic_phase_post_read(ngx_stream_lua_request_t *r);
+
+void ngx_stream_lua_request_cleanup(ngx_stream_lua_ctx_t *ctx, int foricible);
+
+void ngx_stream_lua_request_cleanup_handler(void *data);
+
+ngx_int_t ngx_stream_lua_run_thread(lua_State *L, ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx, volatile int nret);
+
+ngx_int_t ngx_stream_lua_wev_handler(ngx_stream_lua_request_t *r);
+
+u_char *ngx_stream_lua_digest_hex(u_char *dest, const u_char *buf,
+ int buf_len);
+
+void ngx_stream_lua_set_multi_value_table(lua_State *L, int index);
+
+void ngx_stream_lua_unescape_uri(u_char **dst, u_char **src, size_t size,
+ ngx_uint_t type);
+
+uintptr_t ngx_stream_lua_escape_uri(u_char *dst, u_char *src,
+ size_t size, ngx_uint_t type);
+
+void ngx_stream_lua_inject_req_api(ngx_log_t *log, lua_State *L);
+
+void ngx_stream_lua_process_args_option(ngx_stream_lua_request_t *r,
+ lua_State *L, int table, ngx_str_t *args);
+
+ngx_int_t ngx_stream_lua_open_and_stat_file(u_char *name,
+ ngx_open_file_info_t *of, ngx_log_t *log);
+
+ngx_chain_t *ngx_stream_lua_chain_get_free_buf(ngx_log_t *log, ngx_pool_t *p,
+ ngx_chain_t **free, size_t len);
+
+
+static ngx_inline void
+ngx_stream_lua_attach_co_ctx_to_L(lua_State *L, ngx_stream_lua_co_ctx_t *coctx)
+{
+#ifdef HAVE_LUA_EXDATA2
+ lua_setexdata2(L, (void *) coctx);
+#endif
+}
+
+
+#ifndef OPENRESTY_LUAJIT
+void ngx_stream_lua_create_new_globals_table(lua_State *L, int narr, int nrec);
+#endif
+
+int ngx_stream_lua_traceback(lua_State *L);
+
+ngx_stream_lua_co_ctx_t *ngx_stream_lua_get_co_ctx(lua_State *L,
+ ngx_stream_lua_ctx_t *ctx);
+
+ngx_stream_lua_co_ctx_t *ngx_stream_lua_create_co_ctx(
+ ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx);
+
+ngx_int_t ngx_stream_lua_run_posted_threads(ngx_connection_t *c, lua_State *L,
+ ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx, ngx_uint_t nreqs);
+
+ngx_int_t ngx_stream_lua_post_thread(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx, ngx_stream_lua_co_ctx_t *coctx);
+
+void ngx_stream_lua_del_thread(ngx_stream_lua_request_t *r, lua_State *L,
+ ngx_stream_lua_ctx_t *ctx, ngx_stream_lua_co_ctx_t *coctx);
+
+void ngx_stream_lua_rd_check_broken_connection(ngx_stream_lua_request_t *r);
+
+ngx_int_t ngx_stream_lua_test_expect(ngx_stream_lua_request_t *r);
+
+ngx_int_t ngx_stream_lua_check_broken_connection(ngx_stream_lua_request_t *r,
+ ngx_event_t *ev);
+
+void ngx_stream_lua_finalize_request(ngx_stream_lua_request_t *r, ngx_int_t rc);
+
+void ngx_stream_lua_finalize_fake_request(ngx_stream_lua_request_t *r,
+ ngx_int_t rc);
+
+void ngx_stream_lua_close_fake_connection(ngx_connection_t *c);
+
+void ngx_stream_lua_free_fake_request(ngx_stream_lua_request_t *r);
+
+void ngx_stream_lua_release_ngx_ctx_table(ngx_log_t *log, lua_State *L,
+ ngx_stream_lua_ctx_t *ctx);
+
+void ngx_stream_lua_cleanup_vm(void *data);
+
+ngx_connection_t *ngx_stream_lua_create_fake_connection(ngx_pool_t *pool);
+
+ngx_stream_lua_request_t *
+ ngx_stream_lua_create_fake_request(ngx_stream_session_t *s);
+
+ngx_stream_session_t *ngx_stream_lua_create_fake_session(ngx_connection_t *c);
+
+ngx_int_t ngx_stream_lua_report(ngx_log_t *log, lua_State *L, int status,
+ const char *prefix);
+
+int ngx_stream_lua_do_call(ngx_log_t *log, lua_State *L);
+
+
+
+void ngx_stream_lua_cleanup_free(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_cleanup_pt *cleanup);
+
+#if (NGX_STREAM_LUA_HAVE_SA_RESTART)
+void ngx_stream_lua_set_sa_restart(ngx_log_t *log);
+#endif
+
+#define ngx_stream_lua_check_if_abortable(L, ctx) \
+ if ((ctx)->no_abort) { \
+ return luaL_error(L, "attempt to abort with pending subrequests"); \
+ }
+
+
+static ngx_inline void
+ngx_stream_lua_init_ctx(ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx)
+{
+ ngx_memzero(ctx, sizeof(ngx_stream_lua_ctx_t));
+ ctx->ctx_ref = LUA_NOREF;
+ ctx->entry_co_ctx.co_ref = LUA_NOREF;
+ ctx->resume_handler = ngx_stream_lua_wev_handler;
+ ctx->request = r;
+}
+
+
+static ngx_inline ngx_stream_lua_ctx_t *
+ngx_stream_lua_create_ctx(ngx_stream_session_t *r)
+{
+ ngx_int_t rc;
+ lua_State *L = NULL;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_pool_cleanup_t *cln;
+ ngx_stream_lua_loc_conf_t *llcf;
+ ngx_stream_lua_main_conf_t *lmcf;
+
+ ngx_stream_lua_request_t *sreq;
+
+ ctx = ngx_palloc(r->connection->pool, sizeof(ngx_stream_lua_ctx_t));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ sreq = ngx_stream_lua_create_request(r);
+
+ if (sreq == NULL) {
+ return NULL;
+ }
+
+ ngx_stream_lua_init_ctx(sreq, ctx);
+
+ ngx_stream_set_ctx(r, ctx, ngx_stream_lua_module);
+
+ llcf = ngx_stream_get_module_srv_conf(r, ngx_stream_lua_module);
+
+ if (!llcf->enable_code_cache && r->connection->fd != (ngx_socket_t) -1) {
+ lmcf = ngx_stream_get_module_main_conf(r, ngx_stream_lua_module);
+
+#ifdef DDEBUG
+ dd("lmcf: %p", lmcf);
+#endif
+
+ /*
+ * caveats: we need to move the vm cleanup hook to the list end
+ * to ensure it will be executed *after* the request cleanup
+ * hook registered by ngx_stream_lua_create_request to preserve
+ * the correct semantics.
+ */
+
+ rc = ngx_stream_lua_init_vm(&L, lmcf->lua, lmcf->cycle, sreq->pool,
+ lmcf, r->connection->log, &cln);
+
+ while (cln->next != NULL) {
+ cln = cln->next;
+ }
+
+ cln->next = sreq->pool->cleanup;
+
+ cln = sreq->pool->cleanup;
+ sreq->pool->cleanup = cln->next;
+ cln->next = NULL;
+
+ if (rc != NGX_OK) {
+ if (rc == NGX_DECLINED) {
+ ngx_stream_lua_assert(L != NULL);
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "failed to load the 'resty.core' module "
+ "(https://github.com/openresty/lua-resty"
+ "-core); ensure you are using an OpenResty "
+ "release from https://openresty.org/en/"
+ "download.html (reason: %s)",
+ lua_tostring(L, -1));
+
+ } else {
+ /* rc == NGX_ERROR */
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "failed to initialize Lua VM");
+ }
+
+ return NULL;
+ }
+
+ /* rc == NGX_OK */
+
+ ngx_stream_lua_assert(L != NULL);
+
+ if (lmcf->init_handler) {
+ if (lmcf->init_handler(r->connection->log, lmcf, L) != NGX_OK) {
+ /* an error happened */
+ return NULL;
+ }
+ }
+
+ ctx->vm_state = cln->data;
+
+ } else {
+ ctx->vm_state = NULL;
+ }
+
+ return ctx;
+}
+
+
+static ngx_inline lua_State *
+ngx_stream_lua_get_lua_vm(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx)
+{
+ ngx_stream_lua_main_conf_t *lmcf;
+
+ if (ctx == NULL) {
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ }
+
+ if (ctx && ctx->vm_state) {
+ return ctx->vm_state->vm;
+ }
+
+ lmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_lua_module);
+
+#ifdef DDEBUG
+ dd("lmcf->lua: %p", lmcf->lua);
+#endif
+
+ return lmcf->lua;
+}
+
+
+#define ngx_stream_lua_req_key "__ngx_req"
+
+
+static ngx_inline ngx_stream_lua_request_t *
+ngx_stream_lua_get_req(lua_State *L)
+{
+#ifdef OPENRESTY_LUAJIT
+ return lua_getexdata(L);
+#else
+ ngx_stream_lua_request_t *r;
+
+ lua_getglobal(L, ngx_stream_lua_req_key);
+ r = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ return r;
+#endif
+}
+
+
+static ngx_inline void
+ngx_stream_lua_set_req(lua_State *L, ngx_stream_lua_request_t *r)
+{
+#ifdef OPENRESTY_LUAJIT
+ lua_setexdata(L, (void *) r);
+#else
+ lua_pushlightuserdata(L, r);
+ lua_setglobal(L, ngx_stream_lua_req_key);
+#endif
+}
+
+
+static ngx_inline void
+ngx_stream_lua_get_globals_table(lua_State *L)
+{
+ lua_pushvalue(L, LUA_GLOBALSINDEX);
+}
+
+
+static ngx_inline void
+ngx_stream_lua_set_globals_table(lua_State *L)
+{
+ lua_replace(L, LUA_GLOBALSINDEX);
+}
+
+
+#define ngx_stream_lua_hash_literal(s) \
+ ngx_stream_lua_hash_str((u_char *) s, sizeof(s) - 1)
+
+
+static ngx_inline ngx_uint_t
+ngx_stream_lua_hash_str(u_char *src, size_t n)
+{
+ ngx_uint_t key;
+
+ key = 0;
+
+ while (n--) {
+ key = ngx_hash(key, *src);
+ src++;
+ }
+
+ return key;
+}
+
+
+
+
+static ngx_inline void
+ngx_stream_lua_cleanup_pending_operation(ngx_stream_lua_co_ctx_t *coctx)
+{
+ if (coctx->cleanup) {
+ coctx->cleanup(coctx);
+ coctx->cleanup = NULL;
+ }
+}
+
+
+static ngx_inline ngx_chain_t *
+ngx_stream_lua_get_flush_chain(ngx_stream_lua_request_t *r,
+ ngx_stream_lua_ctx_t *ctx)
+{
+ ngx_chain_t *cl;
+
+ cl = ngx_stream_lua_chain_get_free_buf(r->connection->log, r->pool,
+ &ctx->free_bufs, 0);
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ cl->buf->flush = 1;
+
+ return cl;
+}
+
+
+#if defined(nginx_version) && nginx_version < 1011002
+static ngx_inline in_port_t
+ngx_inet_get_port(struct sockaddr *sa)
+{
+ struct sockaddr_in *sin;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+
+ switch (sa->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) sa;
+ return ntohs(sin6->sin6_port);
+#endif
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+ case AF_UNIX:
+ return 0;
+#endif
+
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) sa;
+ return ntohs(sin->sin_port);
+ }
+}
+#endif
+
+
+extern ngx_uint_t ngx_stream_lua_location_hash;
+extern ngx_uint_t ngx_stream_lua_content_length_hash;
+
+
+#endif /* _NGX_STREAM_LUA_UTIL_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_variable.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_variable.c
new file mode 100644
index 0000000..b097109
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_variable.c
@@ -0,0 +1,226 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_variable.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Xiaozhe Wang (chaoslawful)
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#include "ngx_stream_lua_util.h"
+
+
+
+
+int
+ngx_stream_lua_ffi_var_get(ngx_stream_lua_request_t *r, u_char *name_data,
+ size_t name_len, u_char *lowcase_buf, int capture_id, u_char **value,
+ size_t *value_len, char **err)
+{
+ ngx_uint_t hash;
+ ngx_str_t name;
+
+ ngx_stream_session_t *session;
+ ngx_stream_lua_ctx_t *ctx;
+ ngx_stream_lua_ssl_ctx_t *cctx;
+ ngx_stream_variable_value_t *vv;
+
+ if (r == NULL) {
+ *err = "no request object found";
+ return NGX_ERROR;
+ }
+
+ session = r->session;
+ if ((r)->connection->fd == (ngx_socket_t) -1) {
+ ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
+ if (ctx->context & (NGX_STREAM_LUA_CONTEXT_SSL_CERT
+ | NGX_STREAM_LUA_CONTEXT_SSL_CLIENT_HELLO))
+ {
+ cctx = ngx_stream_lua_ssl_get_ctx(r->connection->ssl->connection);
+ session = cctx->connection->data;
+
+ } else {
+ *err = "API disabled in the current context";
+ return NGX_ERROR;
+ }
+ }
+
+ hash = ngx_hash_strlow(lowcase_buf, name_data, name_len);
+
+ name.data = lowcase_buf;
+ name.len = name_len;
+
+ dd("variable name: %.*s", (int) name_len, lowcase_buf);
+
+ vv = ngx_stream_get_variable(session, &name, hash);
+
+ if (vv == NULL || vv->not_found) {
+ return NGX_DECLINED;
+ }
+
+ *value = vv->data;
+ *value_len = vv->len;
+ return NGX_OK;
+}
+
+
+int
+ngx_stream_lua_ffi_var_set(ngx_stream_lua_request_t *r, u_char *name_data,
+ size_t name_len, u_char *lowcase_buf, u_char *value, size_t value_len,
+ u_char *errbuf, size_t *errlen)
+{
+ u_char *p;
+ ngx_uint_t hash;
+
+ ngx_stream_variable_t *v;
+ ngx_stream_variable_value_t *vv;
+ ngx_stream_core_main_conf_t *cmcf;
+
+ if (r == NULL) {
+ *errlen = ngx_snprintf(errbuf, *errlen, "no request object found")
+ - errbuf;
+ return NGX_ERROR;
+ }
+
+ if ((r)->connection->fd == (ngx_socket_t) -1) {
+ *errlen = ngx_snprintf(errbuf, *errlen,
+ "API disabled in the current context")
+ - errbuf;
+ return NGX_ERROR;
+ }
+
+ hash = ngx_hash_strlow(lowcase_buf, name_data, name_len);
+
+ dd("variable name: %.*s", (int) name_len, lowcase_buf);
+
+ /* we fetch the variable itself */
+
+ cmcf = ngx_stream_lua_get_module_main_conf(r, ngx_stream_core_module);
+
+ v = ngx_hash_find(&cmcf->variables_hash, hash, lowcase_buf, name_len);
+
+ if (v) {
+ if (!(v->flags & NGX_STREAM_VAR_CHANGEABLE)) {
+ dd("variable not changeable");
+ *errlen = ngx_snprintf(errbuf, *errlen,
+ "variable \"%*s\" not changeable",
+ name_len, lowcase_buf)
+ - errbuf;
+ return NGX_ERROR;
+ }
+
+ if (v->set_handler) {
+
+ dd("set variables with set_handler");
+
+ if (value != NULL && value_len) {
+ vv = ngx_palloc(r->connection->pool,
+ sizeof(ngx_stream_variable_value_t)
+ + value_len);
+ if (vv == NULL) {
+ goto nomem;
+ }
+
+ p = (u_char *) vv + sizeof(ngx_stream_variable_value_t);
+ ngx_memcpy(p, value, value_len);
+ value = p;
+
+ } else {
+ vv = ngx_palloc(r->connection->pool,
+ sizeof(ngx_stream_variable_value_t));
+ if (vv == NULL) {
+ goto nomem;
+ }
+ }
+
+ if (value == NULL) {
+ vv->valid = 0;
+ vv->not_found = 1;
+ vv->no_cacheable = 0;
+ vv->data = NULL;
+ vv->len = 0;
+
+ } else {
+ vv->valid = 1;
+ vv->not_found = 0;
+ vv->no_cacheable = 0;
+
+ vv->data = value;
+ vv->len = value_len;
+ }
+
+ v->set_handler(r->session, vv, v->data);
+ return NGX_OK;
+ }
+
+ if (v->flags & NGX_STREAM_VAR_INDEXED) {
+ vv = &r->session->variables[v->index];
+
+ dd("set indexed variable");
+
+ if (value == NULL) {
+ vv->valid = 0;
+ vv->not_found = 1;
+ vv->no_cacheable = 0;
+
+ vv->data = NULL;
+ vv->len = 0;
+
+ } else {
+ p = ngx_palloc(r->connection->pool, value_len);
+ if (p == NULL) {
+ goto nomem;
+ }
+
+ ngx_memcpy(p, value, value_len);
+ value = p;
+
+ vv->valid = 1;
+ vv->not_found = 0;
+ vv->no_cacheable = 0;
+
+ vv->data = value;
+ vv->len = value_len;
+ }
+
+ return NGX_OK;
+ }
+
+ *errlen = ngx_snprintf(errbuf, *errlen,
+ "variable \"%*s\" cannot be assigned "
+ "a value", name_len, lowcase_buf)
+ - errbuf;
+ return NGX_ERROR;
+ }
+
+ /* variable not found */
+
+ *errlen = ngx_snprintf(errbuf, *errlen,
+ "variable \"%*s\" not found for writing; "
+ "maybe it is a built-in variable that is not "
+ "changeable or you forgot to use \"set $%*s '';\" "
+ "in the config file to define it first",
+ name_len, lowcase_buf, name_len, lowcase_buf)
+ - errbuf;
+ return NGX_ERROR;
+
+nomem:
+
+ *errlen = ngx_snprintf(errbuf, *errlen, "no memory") - errbuf;
+ return NGX_ERROR;
+}
+
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/ngx_stream_lua-0.0.16/src/ngx_stream_lua_worker.c b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_worker.c
new file mode 100644
index 0000000..e18dd80
--- /dev/null
+++ b/ngx_stream_lua-0.0.16/src/ngx_stream_lua_worker.c
@@ -0,0 +1,177 @@
+
+/*
+ * !!! DO NOT EDIT DIRECTLY !!!
+ * This file was automatically generated from the following template:
+ *
+ * src/subsys/ngx_subsys_lua_worker.c.tt2
+ */
+
+
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#define NGX_PROCESS_PRIVILEGED_AGENT 99
+
+
+int
+ngx_stream_lua_ffi_worker_pid(void)
+{
+ return (int) ngx_pid;
+}
+
+
+#if !(NGX_WIN32)
+int
+ngx_stream_lua_ffi_worker_pids(int *pids, size_t *pids_len)
+{
+ size_t n;
+ ngx_int_t i;
+
+ n = 0;
+ for (i = 0; n < *pids_len && i < NGX_MAX_PROCESSES; i++) {
+ if (i != ngx_process_slot && ngx_processes[i].pid == 0) {
+ break;
+ }
+
+ /* The current process */
+ if (i == ngx_process_slot) {
+ pids[n++] = ngx_pid;
+ }
+
+ if (ngx_processes[i].channel[0] > 0 && ngx_processes[i].pid > 0) {
+ pids[n++] = ngx_processes[i].pid;
+ }
+ }
+
+ if (n == 0) {
+ return NGX_ERROR;
+ }
+
+ *pids_len = n;
+
+ return NGX_OK;
+}
+#endif
+
+
+int
+ngx_stream_lua_ffi_worker_id(void)
+{
+#if defined(nginx_version) && nginx_version >= 1009001
+ if (ngx_process != NGX_PROCESS_WORKER
+ && ngx_process != NGX_PROCESS_SINGLE)
+ {
+ return -1;
+ }
+
+ return (int) ngx_worker;
+#else
+ return -1;
+#endif
+}
+
+
+int
+ngx_stream_lua_ffi_worker_exiting(void)
+{
+ return (int) ngx_exiting;
+}
+
+
+int
+ngx_stream_lua_ffi_worker_count(void)
+{
+ ngx_core_conf_t *ccf;
+
+ ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
+ ngx_core_module);
+
+ return (int) ccf->worker_processes;
+}
+
+
+int
+ngx_stream_lua_ffi_master_pid(void)
+{
+#if defined(nginx_version) && nginx_version >= 1013008
+ if (ngx_process == NGX_PROCESS_SINGLE) {
+ return (int) ngx_pid;
+ }
+
+ return (int) ngx_parent;
+#else
+ return NGX_ERROR;
+#endif
+}
+
+
+int
+ngx_stream_lua_ffi_get_process_type(void)
+{
+ ngx_core_conf_t *ccf;
+
+#if defined(HAVE_PRIVILEGED_PROCESS_PATCH) && !NGX_WIN32
+ if (ngx_process == NGX_PROCESS_HELPER) {
+ if (ngx_is_privileged_agent) {
+ return NGX_PROCESS_PRIVILEGED_AGENT;
+ }
+ }
+#endif
+
+ if (ngx_process == NGX_PROCESS_SINGLE) {
+ ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
+ ngx_core_module);
+
+ if (ccf->master) {
+ return NGX_PROCESS_MASTER;
+ }
+ }
+
+ return ngx_process;
+}
+
+
+#if defined(nginx_version) && nginx_version >= 1019003
+int
+ngx_stream_lua_ffi_enable_privileged_agent(char **err, unsigned int connections)
+#else
+int
+ngx_stream_lua_ffi_enable_privileged_agent(char **err)
+#endif
+{
+#ifdef HAVE_PRIVILEGED_PROCESS_PATCH
+ ngx_core_conf_t *ccf;
+
+ ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
+ ngx_core_module);
+
+ ccf->privileged_agent = 1;
+#if defined(nginx_version) && nginx_version >= 1019003
+ ccf->privileged_agent_connections = connections;
+#endif
+
+ return NGX_OK;
+
+#else
+ *err = "missing privileged agent process patch in the nginx core";
+ return NGX_ERROR;
+#endif
+}
+
+
+void
+ngx_stream_lua_ffi_process_signal_graceful_exit(void)
+{
+ ngx_quit = 1;
+}
+
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */