diff options
Diffstat (limited to 'ngx_stream_lua-0.0.16/src')
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 *) # + 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: */ |