diff --git a/README.md b/README.md index d92d7c9..9fcc750 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,7 @@ ngx.req.socket receive receiveuntil + send ngx.say/ngx.print ------------ diff --git a/config b/config index 9546490..654b673 100644 --- a/config +++ b/config @@ -168,5 +168,11 @@ NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_tcp.c \ $ngx_addon_dir/src/ngx_tcp_lua_exception.c \ $ngx_addon_dir/src/ngx_tcp_lua_output.c \ $ngx_addon_dir/src/ngx_tcp_lua_socket.c \ - $ngx_addon_dir/src/ngx_tcp_lua_log.c" + $ngx_addon_dir/src/ngx_tcp_lua_log.c \ + $ngx_addon_dir/src/ngx_tcp_lua_time.c \ + $ngx_addon_dir/src/ngx_tcp_lua_shdict.c \ + $ngx_addon_dir/src/ngx_tcp_variables.c \ + $ngx_addon_dir/src/ngx_tcp_lua_variable.c \ + $ngx_addon_dir/src/ngx_tcp_lua_string.c " + diff --git a/src/log b/src/log new file mode 100644 index 0000000..0e3ac73 --- /dev/null +++ b/src/log @@ -0,0 +1,106 @@ +ngx_tcp_access.c: if (s->connection->sockaddr->sa_family != AF_INET) { +ngx_tcp_access.c: sin = (struct sockaddr_in *) s->connection->sockaddr; +ngx_tcp_access.c: ngx_log_debug3(NGX_LOG_DEBUG, s->connection->log, 0, +ngx_tcp_access.c: ngx_log_error(NGX_LOG_NOTICE, s->connection->log, 0, +ngx_tcp_lua_log.c: if (s && s->connection && s->connection->log) { +ngx_tcp_lua_log.c: if (s && s->connection && s->connection->log) { +ngx_tcp_lua_log.c: if (level > s->connection->log->log_level) { +ngx_tcp_lua_log.c: ngx_log_error(level, s->connection->log, 0, "%s%s", ident, buf); +ngx_tcp_lua_module.c: ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, +ngx_tcp_lua_module.c: ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, +ngx_tcp_lua_module.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, +ngx_tcp_lua_output.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_output.c: chain = s->connection->send_chain(s->connection, cl, 0); +ngx_tcp_lua_output.c: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: chain = s->connection->send_chain(s->connection, cl, 0); +ngx_tcp_lua_socket.c: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: pc->log = s->connection->log; +ngx_tcp_lua_socket.c: s->connection->single_connection = 0; +ngx_tcp_lua_socket.c: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug4(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: c->sendfile &= s->connection->sendfile; +ngx_tcp_lua_socket.c: c->log = s->connection->log; +ngx_tcp_lua_socket.c: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_tcp_lua_chains_get_free_buf(s->connection->log, s->pool, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, u->session->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, u->session->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, u->session->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug3(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: cl = ngx_tcp_lua_chains_get_free_buf(s->connection->log, s->pool, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_tcp_lua_socket_free_pool(r->connection->log, spool); +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: s->connection->log); +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_tcp_lua_chains_get_free_buf(s->connection->log, s->pool, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: pc->log = s->connection->log; +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_socket.c: cl = ngx_tcp_lua_chains_get_free_buf(s->connection->log, s->pool, +ngx_tcp_lua_socket.c: new_cl = ngx_tcp_lua_chains_get_free_buf(s->connection->log, s->pool, +ngx_tcp_lua_util.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_util.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_util.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_util.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_util.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_util.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_util.c: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_util.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_util.c: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_lua_util.c: ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, +ngx_tcp_lua_util.c: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, +ngx_tcp_session.c: if (setsockopt(s->connection->fd, SOL_SOCKET, SO_KEEPALIVE, +ngx_tcp_session.c: ngx_log_error(NGX_LOG_ALERT, s->connection->log, ngx_socket_errno, +ngx_tcp_session.c: if (setsockopt(s->connection->fd, IPPROTO_TCP, TCP_NODELAY, +ngx_tcp_session.c: ngx_log_error(NGX_LOG_ALERT, s->connection->log, ngx_socket_errno, +ngx_tcp_session.c: s->connection->tcp_nodelay = NGX_TCP_NODELAY_SET; +ngx_tcp_session.c: ngx_tcp_send(s->connection->write); +ngx_tcp_session.c: ngx_log_debug1(NGX_LOG_DEBUG_EVENT, s->connection->log, 0, +ngx_tcp_session.c: ngx_log_debug0(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, +ngx_tcp_session.c: && s->connection->read->active) +ngx_tcp_session.c: if (ngx_del_event(s->connection->read, NGX_READ_EVENT, 0) != NGX_OK) { diff --git a/src/ngx_tcp.c b/src/ngx_tcp.c index fd10c03..be7faac 100644 --- a/src/ngx_tcp.c +++ b/src/ngx_tcp.c @@ -3,6 +3,7 @@ #include #include #include "ngx_tcp.h" +#include "ngx_tcp_variables.h" static char *ngx_tcp_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -151,6 +152,21 @@ ngx_tcp_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) pcf = *cf; cf->ctx = ctx; + + for (m = 0; ngx_modules[m]; m++) { + if (ngx_modules[m]->type != NGX_TCP_MODULE) { + continue; + } + + module = ngx_modules[m]->ctx; + + if (module->preconfiguration) { + if (module->preconfiguration(cf) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + } + cf->module_type = NGX_TCP_MODULE; cf->cmd_type = NGX_TCP_MAIN_CONF; rv = ngx_conf_parse(cf, NULL); @@ -217,6 +233,9 @@ ngx_tcp_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } } + if (ngx_tcp_variables_init_vars(cf) != NGX_OK) { + return NGX_CONF_ERROR; + } /* * tcp{}'s cf->ctx was needed while the configuration merging * and in postconfiguration process @@ -610,8 +629,11 @@ static ngx_listening_t * ngx_tcp_add_listening(ngx_conf_t *cf, ngx_tcp_conf_addr_t *addr) { ngx_listening_t *ls; - //ngx_tcp_core_srv_conf_t *cscf; + ngx_tcp_core_srv_conf_t *cscf; + //cscf=ngx_tcp_conf_get_module_srv_conf(cf, ngx_tcp_core_module); + cscf=ngx_tcp_get_module_srv_conf(addr->ctx,ngx_tcp_core_module); + ls = ngx_create_listening(cf, addr->sockaddr, addr->socklen); if (ls == NULL) { return NULL; @@ -624,7 +646,8 @@ ngx_tcp_add_listening(ngx_conf_t *cf, ngx_tcp_conf_addr_t *addr) ls->pool_size = 256; //cscf->connection_pool_size; ls->post_accept_timeout = 10000; //cscf->client_header_timeout; - ls->logp = &cf->cycle->new_log; + ls->logp = cscf->error_log; + /*ls->logp = &cf->cycle->new_log;*/ ls->log.data = &ls->addr_text; ls->log.handler = ngx_accept_log_error; diff --git a/src/ngx_tcp.h b/src/ngx_tcp.h index d2acaf8..1dd410f 100755 --- a/src/ngx_tcp.h +++ b/src/ngx_tcp.h @@ -135,6 +135,11 @@ typedef struct { ngx_array_t servers; /* ngx_tcp_core_srv_conf_t */ ngx_array_t listen; /* ngx_tcp_listen_t */ ngx_array_t virtual_servers; /* ngx_tcp_virtual_server_t */ + ngx_uint_t variables_hash_max_size; + ngx_uint_t variables_hash_bucket_size; + ngx_hash_t variables_hash; + ngx_array_t variables; /* ngx_http_variable_t */ + ngx_hash_keys_arrays_t *variables_keys; } ngx_tcp_core_main_conf_t; typedef struct { @@ -182,14 +187,18 @@ struct ngx_tcp_core_srv_conf_s { /*ACL rules*/ ngx_array_t *rules; + ngx_log_t *error_log; + ngx_tcp_log_srv_conf_t *access_log; /* server ctx */ ngx_tcp_conf_ctx_t *ctx; - + off_t directio_alignment; /* directio_alignment */ size_t client_body_buffer_size; /* client_body_buffer_size */ + + }; @@ -220,6 +229,7 @@ struct ngx_tcp_protocol_s { typedef struct { ngx_tcp_protocol_t *protocol; + ngx_int_t (*preconfiguration)(ngx_conf_t *cf); ngx_int_t (*postconfiguration)(ngx_conf_t *cf); void *(*create_main_conf)(ngx_conf_t *cf); diff --git a/src/ngx_tcp_core_module.c b/src/ngx_tcp_core_module.c index 69a140d..b887534 100755 --- a/src/ngx_tcp_core_module.c +++ b/src/ngx_tcp_core_module.c @@ -4,8 +4,11 @@ #include #include #include "ngx_tcp.h" +#include "ngx_tcp_variables.h" +static ngx_int_t ngx_tcp_core_preconfiguration(ngx_conf_t *cf); +static char *ngx_tcp_core_init_main_conf(ngx_conf_t *cf, void *conf) ; static void *ngx_tcp_core_create_main_conf(ngx_conf_t *cf); static void *ngx_tcp_core_create_srv_conf(ngx_conf_t *cf); static char *ngx_tcp_core_merge_srv_conf(ngx_conf_t *cf, void *parent, @@ -26,9 +29,24 @@ static char *ngx_tcp_access_rule(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_tcp_log_set_access_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_tcp_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static ngx_command_t ngx_tcp_core_commands[] = { + { ngx_string("variables_hash_max_size"), + NGX_TCP_MAIN_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_TCP_MAIN_CONF_OFFSET, + offsetof(ngx_tcp_core_main_conf_t, variables_hash_max_size), + NULL }, + + { ngx_string("variables_hash_bucket_size"), + NGX_TCP_MAIN_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_TCP_MAIN_CONF_OFFSET, + offsetof(ngx_tcp_core_main_conf_t, variables_hash_bucket_size), + NULL }, { ngx_string("server"), NGX_TCP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_MULTI|NGX_CONF_NOARGS, ngx_tcp_core_server, @@ -131,6 +149,13 @@ static ngx_command_t ngx_tcp_core_commands[] = { 0, NULL }, + { ngx_string("error_log"), + NGX_TCP_MAIN_CONF|NGX_TCP_SRV_CONF|NGX_CONF_1MORE, + ngx_tcp_core_error_log, + NGX_TCP_SRV_CONF_OFFSET, + 0, + NULL }, + { ngx_string("directio_alignment"), NGX_TCP_MAIN_CONF|NGX_TCP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_off_slot, @@ -151,10 +176,11 @@ static ngx_command_t ngx_tcp_core_commands[] = { static ngx_tcp_module_t ngx_tcp_core_module_ctx = { NULL, /* protocol */ - NULL, + ngx_tcp_core_preconfiguration, /*preconfiguration*/ + NULL, /*postconfiguration*/ ngx_tcp_core_create_main_conf, /* create main configuration */ - NULL, /* init main configuration */ + ngx_tcp_core_init_main_conf, /* init main configuration */ ngx_tcp_core_create_srv_conf, /* create server configuration */ ngx_tcp_core_merge_srv_conf /* merge server configuration */ @@ -179,6 +205,12 @@ ngx_module_t ngx_tcp_core_module = { static ngx_str_t ngx_tcp_access_log = ngx_string("logs/tcp_access.log"); +static ngx_int_t +ngx_tcp_core_preconfiguration(ngx_conf_t *cf) +{ + //return NGX_OK; + return ngx_tcp_variables_add_core_vars(cf); +} static void * ngx_tcp_core_create_main_conf(ngx_conf_t *cf) @@ -209,10 +241,31 @@ ngx_tcp_core_create_main_conf(ngx_conf_t *cf) return NULL; } + cmcf->variables_hash_max_size = NGX_CONF_UNSET_UINT; + cmcf->variables_hash_bucket_size = NGX_CONF_UNSET_UINT; return cmcf; } +static char * +ngx_tcp_core_init_main_conf(ngx_conf_t *cf,void *conf) +{ + ngx_tcp_core_main_conf_t *cmcf; + + cmcf = ngx_tcp_conf_get_module_main_conf(cf, ngx_tcp_core_module); + + if (cmcf->variables_hash_max_size == NGX_CONF_UNSET_UINT) { + cmcf->variables_hash_max_size = 512; + } + + if (cmcf->variables_hash_bucket_size == NGX_CONF_UNSET_UINT) { + cmcf->variables_hash_bucket_size = 64; + } + + cmcf->variables_hash_bucket_size = ngx_align(cmcf->variables_hash_bucket_size, ngx_cacheline_size); + return NGX_CONF_OK; + +} static void * ngx_tcp_core_create_srv_conf(ngx_conf_t *cf) @@ -230,7 +283,6 @@ ngx_tcp_core_create_srv_conf(ngx_conf_t *cf) * * cscf->protocol = NULL; */ - if (ngx_array_init(&cscf->server_names, cf->pool, 4, sizeof(ngx_tcp_server_name_t)) != NGX_OK) @@ -329,6 +381,15 @@ ngx_tcp_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) conf->rules = prev->rules; } + if (conf->error_log == NULL) { + if (prev->error_log) { + conf->error_log = prev->error_log; + } else { + conf->error_log = &cf->cycle->new_log; + } + } + + if (lscf->open_file_cache == NGX_CONF_UNSET_PTR) { lscf->open_file_cache = plscf->open_file_cache; @@ -890,3 +951,37 @@ ngx_tcp_log_set_access_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_OK; } + + +static char * +ngx_tcp_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_tcp_core_srv_conf_t *clcf = conf; + + ngx_str_t *value, name; + + if (clcf->error_log) { + return "is duplicate"; + } + + value = cf->args->elts; + + if (ngx_strcmp(value[1].data, "stderr") == 0) { + ngx_str_null(&name); + + } else { + name = value[1]; + } + + clcf->error_log = ngx_log_create(cf->cycle, &name); + if (clcf->error_log == NULL) { + return NGX_CONF_ERROR; + } + + if (cf->args->nelts == 2) { + clcf->error_log->log_level = NGX_LOG_ERR; + return NGX_CONF_OK; + } + + return ngx_log_set_levels(cf, clcf->error_log); +} diff --git a/src/ngx_tcp_lua_common.h b/src/ngx_tcp_lua_common.h index 9782037..cac7372 100755 --- a/src/ngx_tcp_lua_common.h +++ b/src/ngx_tcp_lua_common.h @@ -36,14 +36,27 @@ #define NGX_TCP_LUA_FILE_KEY_LEN \ (NGX_TCP_LUA_FILE_TAG_LEN + 2 * MD5_DIGEST_LENGTH) -typedef struct ngx_tcp_lua_main_conf_s { +typedef struct ngx_tcp_lua_main_conf_s ngx_tcp_lua_main_conf_t; + +typedef ngx_int_t (*ngx_tcp_lua_conf_handler_pt)(ngx_log_t *log, + ngx_tcp_lua_main_conf_t *lmcf, lua_State *L); + +struct ngx_tcp_lua_main_conf_s { lua_State *lua; ngx_str_t lua_path; ngx_str_t lua_cpath; + ngx_tcp_lua_conf_handler_pt init_handler; + ngx_pool_t *pool; -} ngx_tcp_lua_main_conf_t; + + ngx_array_t *shm_zones; /* of ngx_shm_zone_t* */ + + ngx_uint_t shm_zones_inited; + + unsigned requires_shm:1; +} ; typedef struct ngx_tcp_lua_srv_conf_s { @@ -67,7 +80,6 @@ typedef struct ngx_tcp_lua_srv_conf_s { } ngx_tcp_lua_srv_conf_t; -typedef void (*ngx_tcp_cleanup_pt)(void *data); typedef struct ngx_tcp_lua_ctx_s { @@ -90,6 +102,7 @@ typedef struct ngx_tcp_lua_ctx_s { unsigned socket_busy:1; unsigned socket_ready:1; + unsigned exited:1; } ngx_tcp_lua_ctx_t; #define DDEBUG 0 diff --git a/src/ngx_tcp_lua_module.c b/src/ngx_tcp_lua_module.c index 8c0eca9..4c23d73 100755 --- a/src/ngx_tcp_lua_module.c +++ b/src/ngx_tcp_lua_module.c @@ -1,7 +1,9 @@ +//#include "lua_module/ngx_http_lua_directive.h" #include "ngx_tcp_lua_common.h" #include "ngx_tcp_lua_cache.h" #include "ngx_tcp_lua_util.h" +#include "ngx_tcp_lua_shdict.h" static void ngx_tcp_lua_init_session(ngx_tcp_session_t *s); @@ -26,6 +28,8 @@ void ngx_tcp_lua_reset_ctx(ngx_tcp_session_t *r, lua_State *L, ngx_tcp_lua_ctx_t static char *ngx_tcp_lua_lowat_check(ngx_conf_t *cf, void *post, void *data); char *ngx_tcp_lua_code_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char * ngx_tcp_lua_shared_dict(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + static ngx_tcp_protocol_t ngx_tcp_lua_protocol = { @@ -46,6 +50,12 @@ static ngx_conf_post_t ngx_tcp_lua_lowat_post = static ngx_command_t ngx_tcp_lua_commands[] = { + { ngx_string("lua_shared_dict"), + NGX_TCP_MAIN_CONF|NGX_CONF_TAKE2, + ngx_tcp_lua_shared_dict, + 0, + 0, + NULL }, { ngx_string("lua_package_cpath"), NGX_TCP_MAIN_CONF|NGX_CONF_TAKE1, ngx_tcp_lua_package_cpath, @@ -134,6 +144,7 @@ static ngx_command_t ngx_tcp_lua_commands[] = { static ngx_tcp_module_t ngx_tcp_lua_module_ctx = { &ngx_tcp_lua_protocol, /* protocol */ + NULL, /* preconfiguration */ ngx_tcp_lua_init, /* postconfiguration */ ngx_tcp_lua_create_main_conf, /* create main configuration */ @@ -174,8 +185,8 @@ ngx_tcp_lua_init_session(ngx_tcp_session_t *s) c = s->connection; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "tcp lua init and load src"); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "tcp lua init and load src"); lscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_lua_module); lmcf = ngx_tcp_get_module_main_conf(s, ngx_tcp_lua_module); L = lmcf->lua; @@ -194,6 +205,7 @@ ngx_tcp_lua_init_session(ngx_tcp_session_t *s) ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "failed to load Lua inlined code: %s", err); + ngx_tcp_finalize_session(s); return; } } else { @@ -202,6 +214,7 @@ ngx_tcp_lua_init_session(ngx_tcp_session_t *s) lscf->lua_src.len); if (script_path == NULL) { + ngx_tcp_finalize_session(s); return; } @@ -216,6 +229,7 @@ ngx_tcp_lua_init_session(ngx_tcp_session_t *s) ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to load Lua code: %s", err); + ngx_tcp_finalize_session(s); return; } } @@ -228,7 +242,7 @@ ngx_tcp_lua_init_session(ngx_tcp_session_t *s) rc = ngx_tcp_lua_process_by_chunk(L, s); - if (rc == NGX_DONE || rc == NGX_OK) { + if (rc == NGX_DONE || rc == NGX_OK || rc == NGX_ERROR) { ngx_tcp_finalize_session(s); return; } @@ -354,6 +368,7 @@ ngx_tcp_lua_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_uint_value(conf->pool_size, prev->pool_size, 30); + return NGX_CONF_OK; } @@ -581,8 +596,9 @@ ngx_tcp_lua_process_by_chunk(lua_State *L, ngx_tcp_session_t *s) dd("setting new ctx, ctx = %p", ctx); - ctx->cc_ref = LUA_NOREF; + ctx->cc_ref = LUA_NOREF; ctx->ctx_ref = LUA_NOREF; + ctx->exited = 0; ngx_tcp_set_ctx(s, ctx, ngx_tcp_lua_module); @@ -702,3 +718,91 @@ ngx_tcp_lua_code_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +char * +ngx_tcp_lua_shared_dict(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_tcp_lua_main_conf_t *lmcf = conf; + + ngx_str_t *value, name; + ngx_shm_zone_t *zone; + ngx_shm_zone_t **zp; + ngx_tcp_lua_shdict_ctx_t *ctx; + ssize_t size; + + if (lmcf->shm_zones == NULL) { + lmcf->shm_zones = ngx_palloc(cf->pool, sizeof(ngx_array_t)); + if (lmcf->shm_zones == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid shm_zones "); + return NGX_CONF_ERROR; + } + + ngx_array_init(lmcf->shm_zones, cf->pool, 2, sizeof(ngx_shm_zone_t *)); + } + + 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]); + /*ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "shared dict name \"%d\" %d size \"%V\"",name.data,lmcf->shm_zones->nelts ,&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_tcp_lua_shdict_ctx_t)); + if (ctx == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "alloc ctx error"); + return NGX_CONF_ERROR; + } + + ctx->name = name; + ctx->main_conf = lmcf; + ctx->log = &cf->cycle->new_log; + + zone = ngx_shared_memory_add(cf, &name, (size_t) size, + &ngx_tcp_lua_module); + if (zone == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "add shm_zones error"); + 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_tcp_lua_shdict_init_zone; + zone->data = ctx; + + zp = ngx_array_push(lmcf->shm_zones); + if (zp == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "push shm_zones error"); + return NGX_CONF_ERROR; + } + + *zp = zone; + + lmcf->requires_shm = 1; + + return NGX_CONF_OK; +} diff --git a/src/ngx_tcp_lua_output.c b/src/ngx_tcp_lua_output.c index bc3b678..151e8df 100755 --- a/src/ngx_tcp_lua_output.c +++ b/src/ngx_tcp_lua_output.c @@ -5,14 +5,13 @@ static int ngx_tcp_lua_ngx_print(lua_State *L); -static int ngx_tcp_lua_ngx_echo(lua_State *L, unsigned newline); static int ngx_tcp_lua_ngx_print(lua_State *L) { dd("calling lua print"); - return ngx_tcp_lua_ngx_echo(L, 0); + return ngx_tcp_lua_ngx_echo(L, 0,1); } @@ -20,12 +19,12 @@ static int ngx_tcp_lua_ngx_say(lua_State *L) { dd("calling"); - return ngx_tcp_lua_ngx_echo(L, 1); + return ngx_tcp_lua_ngx_echo(L, 1,1); } -static int -ngx_tcp_lua_ngx_echo(lua_State *L, unsigned newline) +int +ngx_tcp_lua_ngx_echo(lua_State *L, unsigned newline,unsigned start) { ngx_tcp_session_t *s; ngx_tcp_lua_ctx_t *ctx; @@ -63,7 +62,7 @@ ngx_tcp_lua_ngx_echo(lua_State *L, unsigned newline) nargs = lua_gettop(L); size = 0; - for (i = 1; i <= nargs; i++) { + for (i = start; i <= nargs; i++) { type = lua_type(L, i); @@ -131,7 +130,7 @@ ngx_tcp_lua_ngx_echo(lua_State *L, unsigned newline) return luaL_error(L, "out of memory"); } - for (i = 1; i <= nargs; i++) { + for (i = start; i <= nargs; i++) { type = lua_type(L, i); switch (type) { case LUA_TNUMBER: @@ -208,6 +207,32 @@ ngx_tcp_lua_ngx_echo(lua_State *L, unsigned newline) return 0; } +int +ngx_tcp_lua_ngx_exit(lua_State *L) +{ + ngx_tcp_session_t *s; + ngx_tcp_lua_ctx_t *ctx; + //ngx_buf_tag_t tag; + + lua_pushlightuserdata(L, &ngx_tcp_lua_request_key); + lua_rawget(L, LUA_GLOBALSINDEX); + s = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (s == NULL) { + return luaL_error(L, "no request object found"); + } + + ctx = ngx_tcp_get_module_ctx(s, ngx_tcp_lua_module); + + if (ctx == NULL) { + return luaL_error(L, "no request ctx found"); + } + ctx->exited = 1; + + + return lua_yield(L,0); +} void ngx_tcp_lua_inject_output_api(lua_State *L) @@ -218,6 +243,9 @@ ngx_tcp_lua_inject_output_api(lua_State *L) lua_pushcfunction(L, ngx_tcp_lua_ngx_print); lua_setfield(L, -2, "print"); + lua_pushcfunction(L, ngx_tcp_lua_ngx_exit); + lua_setfield(L, -2, "exit"); + } @@ -405,3 +433,4 @@ ngx_tcp_lua_copy_str_in_table(lua_State *L, u_char *dst) } + diff --git a/src/ngx_tcp_lua_output.h b/src/ngx_tcp_lua_output.h index 48d3dd3..00376f1 100644 --- a/src/ngx_tcp_lua_output.h +++ b/src/ngx_tcp_lua_output.h @@ -6,6 +6,7 @@ void ngx_tcp_lua_inject_output_api(lua_State *L); size_t ngx_tcp_lua_calc_strlen_in_table(lua_State *L, int arg_i, unsigned strict); u_char *ngx_tcp_lua_copy_str_in_table(lua_State *L, u_char *dst); +int ngx_tcp_lua_ngx_echo(lua_State *L, unsigned newline,unsigned start); #endif diff --git a/src/ngx_tcp_lua_shdict.c b/src/ngx_tcp_lua_shdict.c new file mode 100644 index 0000000..9fe83ae --- /dev/null +++ b/src/ngx_tcp_lua_shdict.c @@ -0,0 +1,1191 @@ +#ifndef DDEBUG +#define DDEBUG 0 +#endif +//#include "ddebug.h" + + +#include "ngx_tcp_lua_shdict.h" + +typedef struct { + uint8_t type; + + union { + int b; /* boolean */ + lua_Number n; /* number */ + ngx_str_t s; /* string */ + } value; + +} ngx_tcp_lua_value_t; + +static int ngx_tcp_lua_shdict_set(lua_State *L); +static int ngx_tcp_lua_shdict_get(lua_State *L); +static int ngx_tcp_lua_shdict_expire(ngx_tcp_lua_shdict_ctx_t *ctx, + ngx_uint_t n); +static ngx_int_t ngx_tcp_lua_shdict_lookup(ngx_shm_zone_t *shm_zone, + ngx_uint_t hash, u_char *kdata, size_t klen, + ngx_tcp_lua_shdict_node_t **sdp); +static int ngx_tcp_lua_shdict_set_helper(lua_State *L, int flags); +static int ngx_tcp_lua_shdict_add(lua_State *L); +static int ngx_tcp_lua_shdict_replace(lua_State *L); +static int ngx_tcp_lua_shdict_incr(lua_State *L); +static int ngx_tcp_lua_shdict_delete(lua_State *L); +static int ngx_tcp_lua_shdict_flush_all(lua_State *L); +static int ngx_tcp_lua_shdict_flush_expired(lua_State *L); + + +#define NGX_TCP_LUA_SHDICT_ADD 0x0001 +#define NGX_TCP_LUA_SHDICT_REPLACE 0x0002 + + +ngx_int_t +ngx_tcp_lua_shdict_init_zone(ngx_shm_zone_t *shm_zone, void *data) +{ + ngx_tcp_lua_shdict_ctx_t *octx = data; + + size_t len; + ngx_tcp_lua_shdict_ctx_t *ctx; + ngx_tcp_lua_main_conf_t *lmcf; + + dd("init zone"); + + ctx = shm_zone->data; + + if (octx) { + ctx->sh = octx->sh; + ctx->shpool = octx->shpool; + + goto done; + } + + ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + + if (shm_zone->shm.exists) { + ctx->sh = ctx->shpool->data; + + goto done; + } + + ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_tcp_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_tcp_lua_shdict_rbtree_insert_value); + + ngx_queue_init(&ctx->sh->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); + +done: + dd("get lmcf"); + + lmcf = ctx->main_conf; + + dd("lmcf->lua: %p", lmcf->lua); + + lmcf->shm_zones_inited++; + + /*if (lmcf->shm_zones_inited == lmcf->shm_zones->nelts && lmcf->init_handler) + { + if (lmcf->init_handler(ctx->log, lmcf, lmcf->lua) != 0) { + * an error happened * / + return NGX_ERROR; + } + }*/ + + return NGX_OK; +} + + +void +ngx_tcp_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_tcp_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_tcp_lua_shdict_node_t *) &node->color; + sdnt = (ngx_tcp_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_tcp_lua_shdict_lookup(ngx_shm_zone_t *shm_zone, ngx_uint_t hash, + u_char *kdata, size_t klen, ngx_tcp_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_tcp_lua_shdict_ctx_t *ctx; + ngx_tcp_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_tcp_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->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_tcp_lua_shdict_expire(ngx_tcp_lua_shdict_ctx_t *ctx, ngx_uint_t n) +{ + ngx_time_t *tp; + uint64_t now; + ngx_queue_t *q; + int64_t ms; + ngx_rbtree_node_t *node; + ngx_tcp_lua_shdict_node_t *sd; + int freed = 0; + + 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->queue)) { + return freed; + } + + q = ngx_queue_last(&ctx->sh->queue); + + sd = ngx_queue_data(q, ngx_tcp_lua_shdict_node_t, queue); + + if (n++ != 0) { + + if (sd->expires == 0) { + return freed; + } + + ms = sd->expires - now; + if (ms > 0) { + return freed; + } + } + + 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_tcp_lua_inject_shdict_api(ngx_tcp_lua_main_conf_t *lmcf, lua_State *L) +{ + ngx_tcp_lua_shdict_ctx_t *ctx; + ngx_uint_t i; + ngx_shm_zone_t **zone; + + //ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0, " init shm api %d\n",lmcf->shm_zones->nelts); + + if (lmcf->shm_zones != NULL) { + lua_createtable(L, 0, lmcf->shm_zones->nelts /* nrec */); + /* ngx.shared */ + + lua_createtable(L, 0 /* narr */, 9 /* nrec */); /* shared mt */ + + lua_pushcfunction(L, ngx_tcp_lua_shdict_get); + lua_setfield(L, -2, "get"); + + lua_pushcfunction(L, ngx_tcp_lua_shdict_set); + lua_setfield(L, -2, "set"); + + lua_pushcfunction(L, ngx_tcp_lua_shdict_add); + lua_setfield(L, -2, "add"); + + lua_pushcfunction(L, ngx_tcp_lua_shdict_replace); + lua_setfield(L, -2, "replace"); + + lua_pushcfunction(L, ngx_tcp_lua_shdict_incr); + lua_setfield(L, -2, "incr"); + + lua_pushcfunction(L, ngx_tcp_lua_shdict_delete); + lua_setfield(L, -2, "delete"); + + lua_pushcfunction(L, ngx_tcp_lua_shdict_flush_all); + lua_setfield(L, -2, "flush_all"); + + lua_pushcfunction(L, ngx_tcp_lua_shdict_flush_expired); + lua_setfield(L, -2, "flush_expired"); + + lua_pushvalue(L, -1); /* shared mt mt */ + lua_setfield(L, -2, "__index"); /* shared mt */ + + zone = lmcf->shm_zones->elts; + + for (i = 0; i < lmcf->shm_zones->nelts; i++) { + + ctx = zone[i]->data; + + //ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0," set shm %d %s\n", ctx->name.data,(char*)ctx->name.data); + + lua_pushlstring(L, (char *) ctx->name.data, ctx->name.len); + /* shared mt key */ + + lua_pushlightuserdata(L, zone[i]); /* shared mt key ud */ + 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 int +ngx_tcp_lua_shdict_get(lua_State *L) +{ + int n; + ngx_str_t name; + ngx_str_t key; + uint32_t hash; + ngx_int_t rc; + ngx_tcp_lua_shdict_ctx_t *ctx; + ngx_tcp_lua_shdict_node_t *sd; + ngx_str_t value; + int value_type; + lua_Number num; + u_char c; + ngx_shm_zone_t *zone; + uint32_t user_flags = 0; + + n = lua_gettop(L); + + if (n != 2) { + return luaL_error(L, "expecting exactly two arguments, " + "but only seen %d", n); + } + + luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); + + zone = lua_touserdata(L, 1); + if (zone == NULL) { + return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer"); + } + + ctx = zone->data; + + name = ctx->name; + + key.data = (u_char *) luaL_checklstring(L, 2, &key.len); + + if (key.len == 0) { + lua_pushnil(L); + return 1; + } + + if (key.len > 65535) { + return luaL_error(L, + "the key argument is more than 65535 bytes: \"%s\"", + key.data); + } + + hash = ngx_crc32_short(key.data, key.len); + +#if (NGX_DEBUG) + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->log, 0, + "fetching key \"%V\" in shared dict \"%V\"", &key, &name); +#endif /* NGX_DEBUG */ + + ngx_shmtx_lock(&ctx->shpool->mutex); + +#if 1 + ngx_tcp_lua_shdict_expire(ctx, 1); +#endif + + rc = ngx_tcp_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); + + dd("shdict lookup returns %d", (int) rc); + + if (rc == NGX_DECLINED || rc == NGX_DONE) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + lua_pushnil(L); + return 1; + } + + /* rc == NGX_OK */ + + 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; + + switch (value_type) { + case LUA_TSTRING: + + lua_pushlstring(L, (char *) value.data, value.len); + break; + + case LUA_TNUMBER: + + if (value.len != sizeof(lua_Number)) { + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return luaL_error(L, "bad lua number value size found for key %s " + "in shared_dict %s: %lu", key.data, name.data, + (unsigned long) value.len); + } + + num = *(lua_Number *) value.data; + + lua_pushnumber(L, num); + break; + + case LUA_TBOOLEAN: + + if (value.len != sizeof(u_char)) { + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return luaL_error(L, "bad lua boolean value size found for key %s " + "in shared_dict %s: %lu", key.data, name.data, + (unsigned long) value.len); + } + + c = *value.data; + + lua_pushboolean(L, c ? 1 : 0); + break; + + default: + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return luaL_error(L, "bad value type found for key %s in " + "shared_dict %s: %d", key.data, name.data, + value_type); + } + + user_flags = sd->user_flags; + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + if (user_flags) { + lua_pushinteger(L, (lua_Integer) user_flags); + return 2; + } + + return 1; +} + + +static int +ngx_tcp_lua_shdict_delete(lua_State *L) +{ + int n; + + n = lua_gettop(L); + + if (n != 2) { + return luaL_error(L, "expecting 2 arguments, " + "but only seen %d", n); + } + + lua_pushnil(L); + + return ngx_tcp_lua_shdict_set_helper(L, 0); +} + + +static int +ngx_tcp_lua_shdict_flush_all(lua_State *L) +{ + ngx_queue_t *q; + ngx_tcp_lua_shdict_node_t *sd; + int n; + ngx_tcp_lua_shdict_ctx_t *ctx; + ngx_shm_zone_t *zone; + + n = lua_gettop(L); + + if (n != 1) { + return luaL_error(L, "expecting 1 argument, " + "but seen %d", n); + } + + luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); + + zone = lua_touserdata(L, 1); + if (zone == NULL) { + return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer"); + } + + ctx = zone->data; + + ngx_shmtx_lock(&ctx->shpool->mutex); + + for (q = ngx_queue_head(&ctx->sh->queue); + q != ngx_queue_sentinel(&ctx->sh->queue); + q = ngx_queue_next(q)) + { + sd = ngx_queue_data(q, ngx_tcp_lua_shdict_node_t, queue); + sd->expires = 1; + } + + ngx_tcp_lua_shdict_expire(ctx, 0); + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return 0; +} + + +static int +ngx_tcp_lua_shdict_flush_expired(lua_State *L) +{ + ngx_queue_t *q, *prev; + ngx_tcp_lua_shdict_node_t *sd; + ngx_tcp_lua_shdict_ctx_t *ctx; + 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; + + 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_TLIGHTUSERDATA); + + zone = lua_touserdata(L, 1); + if (zone == NULL) { + return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer"); + } + + if (n == 2) { + attempts = luaL_checknumber(L, 2); + } + + ctx = zone->data; + + ngx_shmtx_lock(&ctx->shpool->mutex); + + if (ngx_queue_empty(&ctx->sh->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->queue); + + while (q != ngx_queue_sentinel(&ctx->sh->queue)) { + prev = ngx_queue_prev(q); + + sd = ngx_queue_data(q, ngx_tcp_lua_shdict_node_t, queue); + + if (sd->expires != 0 && sd->expires <= now) { + 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; +} + + +static int +ngx_tcp_lua_shdict_add(lua_State *L) +{ + return ngx_tcp_lua_shdict_set_helper(L, NGX_TCP_LUA_SHDICT_ADD); +} + + +static int +ngx_tcp_lua_shdict_replace(lua_State *L) +{ + return ngx_tcp_lua_shdict_set_helper(L, NGX_TCP_LUA_SHDICT_REPLACE); +} + + +static int +ngx_tcp_lua_shdict_set(lua_State *L) +{ + return ngx_tcp_lua_shdict_set_helper(L, 0); +} + + +static int +ngx_tcp_lua_shdict_set_helper(lua_State *L, int flags) +{ + int i, n; + ngx_str_t name; + ngx_str_t key; + uint32_t hash; + ngx_int_t rc; + ngx_tcp_lua_shdict_ctx_t *ctx; + ngx_tcp_lua_shdict_node_t *sd; + ngx_str_t value; + int value_type; + lua_Number num; + u_char c; + lua_Number exptime = 0; + u_char *p; + ngx_rbtree_node_t *node; + ngx_time_t *tp; + ngx_shm_zone_t *zone; + int forcible = 0; + /* indicates whether to foricibly override other + * valid entries */ + int32_t user_flags = 0; + + n = lua_gettop(L); + + if (n != 3 && n != 4 && n != 5) { + return luaL_error(L, "expecting 3, 4 or 5 arguments, " + "but only seen %d", n); + } + + luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); + + zone = lua_touserdata(L, 1); + if (zone == NULL) { + return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer"); + } + + ctx = zone->data; + + name = ctx->name; + + key.data = (u_char *) luaL_checklstring(L, 2, &key.len); + + if (key.len == 0) { + return luaL_error(L, "attempt to use empty keys"); + } + + if (key.len > 65535) { + return luaL_error(L, + "the key argument is more than 65535 bytes: %d", + (int) key.len); + } + + hash = ngx_crc32_short(key.data, key.len); + + value_type = lua_type(L, 3); + + switch (value_type) { + case LUA_TSTRING: + value.data = (u_char *) lua_tolstring(L, 3, &value.len); + break; + + case LUA_TNUMBER: + value.len = sizeof(lua_Number); + num = lua_tonumber(L, 3); + value.data = (u_char *) # + break; + + case LUA_TBOOLEAN: + value.len = sizeof(u_char); + c = lua_toboolean(L, 3) ? 1 : 0; + value.data = &c; + break; + + case LUA_TNIL: + if (flags & (NGX_TCP_LUA_SHDICT_ADD|NGX_TCP_LUA_SHDICT_REPLACE)) { + return luaL_error(L, "attempt to add or replace nil values"); + } + + value.len = 0; + value.data = NULL; + break; + + default: + return luaL_error(L, "unsupported value type for key \"%s\" in " + "shared_dict \"%s\": %s", key.data, name.data, + lua_typename(L, value_type)); + } + + if (n >= 4) { + exptime = luaL_checknumber(L, 4); + if (exptime < 0) { + exptime = 0; + } + } + + if (n == 5) { + user_flags = (uint32_t) luaL_checkinteger(L, 5); + } + + dd("looking up key %s in shared dict %s", key.data, name.data); + + ngx_shmtx_lock(&ctx->shpool->mutex); + +#if 1 + ngx_tcp_lua_shdict_expire(ctx, 1); +#endif + + rc = ngx_tcp_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); + + dd("shdict lookup returned %d", (int) rc); + + if (flags & NGX_TCP_LUA_SHDICT_REPLACE) { + + if (rc == NGX_DECLINED || rc == NGX_DONE) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushboolean(L, 0); + lua_pushliteral(L, "not found"); + lua_pushboolean(L, forcible); + return 3; + } + + /* rc == NGX_OK */ + + goto replace; + } + + if (flags & NGX_TCP_LUA_SHDICT_ADD) { + + if (rc == NGX_OK) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushboolean(L, 0); + lua_pushliteral(L, "exists"); + lua_pushboolean(L, forcible); + return 3; + } + + 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 (value.data && value.len == (size_t) sd->value_len) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, 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->queue, &sd->queue); + + sd->key_len = key.len; + + if (exptime > 0) { + tp = ngx_timeofday(); + sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + + exptime * 1000; + + } else { + sd->expires = 0; + } + + sd->user_flags = user_flags; + + sd->value_len = (uint32_t) value.len; + + dd("setting value type to %d", value_type); + + sd->value_type = value_type; + + p = ngx_copy(sd->data, key.data, key.len); + ngx_memcpy(p, value.data, value.len); + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushboolean(L, 1); + lua_pushnil(L); + lua_pushboolean(L, forcible); + return 3; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, + "lua shared dict set: found old entry bug value size NOT matched, " + "removing it first"); + +remove: + 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 (value.data == NULL) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushboolean(L, 1); + lua_pushnil(L); + lua_pushboolean(L, 0); + return 3; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, + "lua shared dict set: creating a new entry"); + + n = offsetof(ngx_rbtree_node_t, color) + + offsetof(ngx_tcp_lua_shdict_node_t, data) + + key.len + + value.len; + + node = ngx_slab_alloc_locked(ctx->shpool, n); + + if (node == NULL) { + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0, + "lua shared dict set: overriding non-expired items due to memory " + "shortage for entry \"%V\"", &name); + + for (i = 0; i < 30; i++) { + if (ngx_tcp_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); + + lua_pushboolean(L, 0); + lua_pushliteral(L, "no memory"); + lua_pushboolean(L, forcible); + return 3; + } + +allocated: + sd = (ngx_tcp_lua_shdict_node_t *) &node->color; + + node->key = hash; + sd->key_len = key.len; + + if (exptime > 0) { + tp = ngx_timeofday(); + sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + + exptime * 1000; + + } else { + sd->expires = 0; + } + + sd->user_flags = user_flags; + + sd->value_len = (uint32_t) value.len; + + dd("setting value type to %d", value_type); + + sd->value_type = value_type; + + p = ngx_copy(sd->data, key.data, key.len); + ngx_memcpy(p, value.data, value.len); + + ngx_rbtree_insert(&ctx->sh->rbtree, node); + + ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushboolean(L, 1); + lua_pushnil(L); + lua_pushboolean(L, forcible); + return 3; +} + + +static int +ngx_tcp_lua_shdict_incr(lua_State *L) +{ + int n; + ngx_str_t key; + uint32_t hash; + ngx_int_t rc; + ngx_tcp_lua_shdict_ctx_t *ctx; + ngx_tcp_lua_shdict_node_t *sd; + lua_Number num; + u_char *p; + ngx_shm_zone_t *zone; + lua_Number value; + + n = lua_gettop(L); + + if (n != 3) { + return luaL_error(L, "expecting 3 arguments, " + "but only seen %d", n); + } + + luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); + + zone = lua_touserdata(L, 1); + if (zone == NULL) { + return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer"); + } + + ctx = zone->data; + + key.data = (u_char *) luaL_checklstring(L, 2, &key.len); + + if (key.len == 0) { + return luaL_error(L, "attempt to use empty keys"); + } + + if (key.len > 65535) { + return luaL_error(L, + "the key argument is more than 65535 bytes: %d", + (int) key.len); + } + + hash = ngx_crc32_short(key.data, key.len); + + value = luaL_checknumber(L, 3); + + dd("looking up key %.*s in shared dict %.*s", (int) key.len, key.data, + (int) ctx->name.len, ctx->name.data); + + ngx_shmtx_lock(&ctx->shpool->mutex); + +#if 1 + ngx_tcp_lua_shdict_expire(ctx, 1); +#endif + + rc = ngx_tcp_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); + lua_pushliteral(L, "not found"); + return 2; + } + + /* rc == NGX_OK */ + + if (sd->value_type != LUA_TNUMBER || sd->value_len != sizeof(lua_Number)) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushnil(L); + lua_pushliteral(L, "not a number"); + return 2; + } + + ngx_queue_remove(&sd->queue); + ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); + + dd("setting value type to %d", (int) sd->value_type); + + p = sd->data + key.len; + + num = *(lua_Number *) p; + + num += value; + + ngx_memcpy(p, (lua_Number *) &num, sizeof(lua_Number)); + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushnumber(L, num); + lua_pushnil(L); + return 2; +} + + +ngx_int_t +ngx_tcp_lua_shared_dict_get(ngx_shm_zone_t *zone, u_char *key_data, + size_t key_len, ngx_tcp_lua_value_t*value) +{ + u_char *data; + size_t len; + uint32_t hash; + ngx_int_t rc; + ngx_tcp_lua_shdict_ctx_t *ctx; + ngx_tcp_lua_shdict_node_t *sd; + + if (zone == NULL) { + return NGX_ERROR; + } + + hash = ngx_crc32_short(key_data, key_len); + + ctx = zone->data; + + ngx_shmtx_lock(&ctx->shpool->mutex); + + rc = ngx_tcp_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); + + return rc; + } + + /* rc == NGX_OK */ + + value->type = sd->value_type; + + dd("type: %d", (int) value->type); + + data = sd->data + sd->key_len; + len = (size_t) sd->value_len; + + switch (value->type) { + case LUA_TSTRING: + + if (value->value.s.data == NULL || value->value.s.len == 0) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "no string buffer " + "initialized"); + return NGX_ERROR; + } + + if (len > value->value.s.len) { + len = value->value.s.len; + + } else { + value->value.s.len = len; + } + + ngx_memcpy(value->value.s.data, data, len); + break; + + case LUA_TNUMBER: + + if (len != sizeof(lua_Number)) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "bad lua number " + "value size found for key %*s: %lu", key_len, + key_data, (unsigned long) len); + + ngx_shmtx_unlock(&ctx->shpool->mutex); + return NGX_ERROR; + } + + ngx_memcpy(&value->value.b, data, len); + break; + + case LUA_TBOOLEAN: + + if (len != sizeof(u_char)) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "bad lua boolean " + "value size found for key %*s: %lu", key_len, + + key_data, (unsigned long) len); + + ngx_shmtx_unlock(&ctx->shpool->mutex); + return NGX_ERROR; + } + + value->value.b = *data; + break; + + default: + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "bad lua value type " + "found for key %*s: %d", key_len, key_data, + (int) value->type); + + ngx_shmtx_unlock(&ctx->shpool->mutex); + return NGX_ERROR; + } + + ngx_shmtx_unlock(&ctx->shpool->mutex); + return NGX_OK; +} + + +ngx_shm_zone_t * +ngx_tcp_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; + + 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) + { + return zone; + } + } + + return NULL; +} + diff --git a/src/ngx_tcp_lua_shdict.h b/src/ngx_tcp_lua_shdict.h new file mode 100644 index 0000000..cad0154 --- /dev/null +++ b/src/ngx_tcp_lua_shdict.h @@ -0,0 +1,47 @@ +#ifndef NGX_TCP_LUA_SHDICT_H +#define NGX_TCP_LUA_SHDICT_H +//from lua_ngx_http 7.2 + +#include "ngx_tcp_lua_common.h" + + +typedef struct { + u_char color; + u_char dummy; + u_short key_len; + ngx_queue_t queue; + uint64_t expires; + uint8_t value_type; + uint32_t value_len; + uint32_t user_flags; + u_char data[1]; +} ngx_tcp_lua_shdict_node_t; + + +typedef struct { + ngx_rbtree_t rbtree; + ngx_rbtree_node_t sentinel; + ngx_queue_t queue; +} ngx_tcp_lua_shdict_shctx_t; + + +typedef struct { + ngx_tcp_lua_shdict_shctx_t *sh; + ngx_slab_pool_t *shpool; + ngx_str_t name; + ngx_tcp_lua_main_conf_t *main_conf; + ngx_log_t *log; +} ngx_tcp_lua_shdict_ctx_t; + + +ngx_int_t ngx_tcp_lua_shdict_init_zone(ngx_shm_zone_t *shm_zone, void *data); + +void ngx_tcp_lua_shdict_rbtree_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); + +void ngx_tcp_lua_inject_shdict_api(ngx_tcp_lua_main_conf_t *lmcf, + lua_State *L); + + +#endif /* NGX_HTTP_LUA_SHDICT_H */ + diff --git a/src/ngx_tcp_lua_socket.c b/src/ngx_tcp_lua_socket.c index 7c93ef2..b43d00f 100755 --- a/src/ngx_tcp_lua_socket.c +++ b/src/ngx_tcp_lua_socket.c @@ -12,6 +12,7 @@ #define NGX_TCP_LUA_SOCKET_FT_BUFTOOSMALL 0x0010 #define NGX_TCP_LUA_SOCKET_FT_NOMEM 0x0020 +extern int ngx_tcp_lua_ngx_echo(lua_State *L, unsigned newline,unsigned start); enum { SOCKET_CTX_INDEX = 1, SOCKET_TIMEOUT_INDEX = 2, @@ -37,10 +38,8 @@ static ngx_int_t ngx_tcp_lua_socket_read_all(void *data, ssize_t bytes); static ngx_int_t ngx_tcp_lua_socket_read_line(void *data, ssize_t bytes); static ngx_int_t ngx_tcp_lua_socket_read(ngx_tcp_session_t *s, ngx_tcp_lua_socket_upstream_t *u); -static int -ngx_tcp_lua_socket_tcp_send(lua_State *L); -static int -ngx_tcp_lua_socket_tcp_send_retval_handler(ngx_tcp_session_t *s, +static int ngx_tcp_lua_socket_tcp_send(lua_State *L); +static int ngx_tcp_lua_socket_tcp_send_retval_handler(ngx_tcp_session_t *s, ngx_tcp_lua_socket_upstream_t *u, lua_State *L); static int ngx_tcp_lua_socket_tcp_close(lua_State *L); static int ngx_tcp_lua_socket_tcp_setoption(lua_State *L); @@ -73,6 +72,7 @@ static ngx_int_t ngx_tcp_lua_socket_compile_pattern(u_char *data, size_t len, static ngx_int_t ngx_tcp_lua_socket_read_until(void *data, ssize_t bytes); static int ngx_tcp_lua_socket_cleanup_compiled_pattern(lua_State *L); static int ngx_tcp_lua_req_socket(lua_State *L); +static int ngx_tcp_lua_req_socket_tcp_send(lua_State *L); static void ngx_tcp_lua_req_socket_rev_handler(ngx_tcp_session_t *s); static int ngx_tcp_lua_socket_downstream_destroy(lua_State *L); static void ngx_tcp_lua_req_socket_cleanup(void *data); @@ -97,6 +97,16 @@ static int ngx_tcp_lua_socket_upstream_destroy(lua_State *L); static ngx_int_t ngx_tcp_lua_get_keepalive_peer(ngx_tcp_session_t *s, lua_State *L, int key_index, ngx_tcp_lua_socket_upstream_t *u); +static int chain_size(ngx_chain_t *cl) +{ + int size=0; + while(cl!=NULL){ + size++; + cl=cl->next; + } + return size; +} + void ngx_tcp_lua_inject_socket_api(ngx_log_t *log, lua_State *L) @@ -137,6 +147,10 @@ ngx_tcp_lua_inject_socket_api(ngx_log_t *log, lua_State *L) lua_pushcfunction(L, ngx_tcp_lua_socket_tcp_receiveuntil); lua_setfield(L, -2, "receiveuntil"); + //lua_pushcfunction(L, ngx_tcp_lua_req_socket_tcp_send); + lua_pushcfunction(L, ngx_tcp_lua_socket_tcp_send); + lua_setfield(L, -2, "send"); + lua_pushcfunction(L, ngx_tcp_lua_socket_tcp_settimeout); lua_setfield(L, -2, "settimeout"); /* ngx socket mt */ @@ -196,6 +210,131 @@ ngx_tcp_lua_inject_req_socket_api(lua_State *L) } +static int +ngx_tcp_lua_req_socket_tcp_send(lua_State *L) +{ + ngx_tcp_session_t *s; + ngx_tcp_lua_ctx_t *ctx; + const u_char *p; + size_t size; + ngx_buf_t *b; + ngx_chain_t *cl, *chain; + int type; + const char *msg; + //ngx_buf_tag_t tag; + if (lua_gettop(L) != 2) { + return luaL_error(L, "expecting 2 arguments (including the object), " + "but got %d", lua_gettop(L)); + } + + lua_pushlightuserdata(L, &ngx_tcp_lua_request_key); + lua_rawget(L, LUA_GLOBALSINDEX); + s = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (s == NULL) { + return luaL_error(L, "no request object found"); + } + + ctx = ngx_tcp_get_module_ctx(s, ngx_tcp_lua_module); + + if (ctx == NULL) { + return luaL_error(L, "no request ctx found"); + } + +/* + if (ctx->eof) { + return luaL_error(L, "seen eof already"); + } +*/ + size = 0; + type = lua_type(L, 2); + switch (type) { + case LUA_TNUMBER: + case LUA_TSTRING: + lua_tolstring(L, 2, &size); + break; + + case LUA_TTABLE: + size = ngx_tcp_lua_calc_strlen_in_table(L, 2, 1 /* strict */); + 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 (size == 0) { + /* do nothing for empty strings */ + return 0; + } + cl=ngx_tcp_lua_chains_get_free_buf(s->connection->log, s->pool, + &ctx->free_recv_bufs, + size, + (ngx_buf_tag_t) + &ngx_tcp_lua_module); + if (cl == NULL) { + return luaL_error(L, "out of memory"); + } + b=cl->buf; + /*b = ngx_create_temp_buf(s->pool, size); + if (b == NULL) { + return luaL_error(L, "out of memory"); + }*/ + + switch (type) { + case LUA_TNUMBER: + case LUA_TSTRING: + p = (u_char *) lua_tolstring(L, -1, &size); + b->last = ngx_copy(b->last, (u_char *) p, size); + break; + + case LUA_TTABLE: + b->last = ngx_tcp_lua_copy_str_in_table(L, b->last); + break; + default: + return luaL_error(L, "impossible to reach here"); + } + +#if 0 + if (b->last != b->end) { + return luaL_error(L, "buffer error: %p != %p", b->last, b->end); + } +#endif + +#if 0 + cl = ngx_alloc_chain_link(s->pool); + if (cl == NULL) { + return luaL_error(L, "out of memory"); + } + + + cl->next = NULL; + cl->buf = b; +#endif + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, "lua send response" ); + + chain = s->connection->send_chain(s->connection, cl, 0); + + if(chain==cl){ + size=size - ( cl->buf->last - cl->buf->pos ); + } + /* free buf */ + cl->next=ctx->free_recv_bufs; + ctx->free_recv_bufs=cl; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, + "tcp_lua write chain %p", chain); + + lua_pushinteger(L, size); + return 1; +} + + static int ngx_tcp_lua_socket_tcp(lua_State *L) { @@ -250,13 +389,16 @@ ngx_tcp_lua_socket_tcp_connect(lua_State *L) ngx_tcp_lua_srv_conf_t *lscf; ngx_peer_connection_t *pc; int timeout; + unsigned custom_pool; + int key_index; + const char *msg; ngx_tcp_lua_socket_upstream_t *u; n = lua_gettop(L); - if (n != 2 && n != 3) { - return luaL_error(L, "ngx.socket connect: expecting 2 or 3 arguments " - "(including the object), but seen %d", n); + 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); } lua_pushlightuserdata(L, &ngx_tcp_lua_request_key); @@ -277,15 +419,48 @@ ngx_tcp_lua_socket_tcp_connect(lua_State *L) p = (u_char *) luaL_checklstring(L, 2, &len); - host.data = ngx_palloc(s->pool, len + 1); + host.data = p;//ngx_palloc(s->pool, len + 1); if (host.data == NULL) { return luaL_error(L, "out of memory"); } host.len = len; - ngx_memcpy(host.data, p, len); - host.data[len] = '\0'; + /*ngx_memcpy(host.data, p, len); + host.data[len] = '\0';*/ + + key_index = 2; + custom_pool = 0; + + if (lua_type(L, n) == LUA_TTABLE) { + + /* found the last optional option table */ + + lua_getfield(L, n, "pool"); + + switch (lua_type(L, -1)) { + case LUA_TNUMBER: + lua_tostring(L, -1); + + case LUA_TSTRING: + custom_pool = 1; + + lua_pushvalue(L, -1); + lua_rawseti(L, 1, SOCKET_KEY_INDEX); + + key_index = n + 1; + + break; + + default: + msg = lua_pushfstring(L, "bad \"pool\" option type: %s", + luaL_typename(L, -1)); + luaL_argerror(L, n, msg); + break; + } + + n--; + } if (n == 3) { port = luaL_checkinteger(L, 3); @@ -296,9 +471,11 @@ ngx_tcp_lua_socket_tcp_connect(lua_State *L) return 2; } - lua_pushliteral(L, ":"); - lua_insert(L, 3); - lua_concat(L, 3); + if (!custom_pool) { + lua_pushliteral(L, ":"); + lua_insert(L, 3); + lua_concat(L, 3); + } dd("socket key: %s", lua_tostring(L, -1)); @@ -306,16 +483,24 @@ ngx_tcp_lua_socket_tcp_connect(lua_State *L) port = 0; } - /* the key's index is 2 */ + if (!custom_pool) { + /* the key's index is 2 */ - lua_pushvalue(L, -1); - lua_rawseti(L, 1, SOCKET_KEY_INDEX); + 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->waiting) { + lua_pushnil(L); + lua_pushliteral(L, "socket busy"); + return 2; + } + if (u->is_downstream) { return luaL_error(L, "attempt to re-connect a request socket"); } @@ -377,7 +562,7 @@ ngx_tcp_lua_socket_tcp_connect(lua_State *L) s->connection->single_connection = 0; - rc = ngx_tcp_lua_get_keepalive_peer(s, L, 2, u); + rc = ngx_tcp_lua_get_keepalive_peer(s, L, key_index, u); if (rc == NGX_OK) { lua_pushinteger(L, 1); @@ -1002,7 +1187,7 @@ ngx_tcp_lua_socket_tcp_receive(lua_State *L) u->buffer = *u->buf_in->buf; } - dd("tcp receive: buf_in: %p, bufs_in: %p", u->buf_in, u->bufs_in); + dd("tcp receive:u:%p buf_in: %p, bufs_in: %p",u, u->buf_in, u->bufs_in); u->waiting = 0; @@ -1340,6 +1525,30 @@ ngx_tcp_lua_socket_read(ngx_tcp_session_t *s, return rc; } +static void +ngx_tcp_lua_socket_write_handler(ngx_tcp_session_t *s) +{ + ngx_tcp_lua_ctx_t *ctx; + ngx_tcp_lua_socket_upstream_t *u; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, + "lua request socket write event handler"); + + ctx = ngx_tcp_get_module_ctx(s, ngx_tcp_lua_module); + if (ctx == NULL) { + return; + } + + u = ctx->data; + + if (u && u->write_event_handler) { + u->write_event_handler(s, u); + } + if (!ctx->socket_busy && ctx->socket_ready) { + ngx_tcp_lua_wev_handler(s); + } + +} static int ngx_tcp_lua_socket_tcp_send(lua_State *L) @@ -1386,9 +1595,6 @@ ngx_tcp_lua_socket_tcp_send(lua_State *L) return 2; } - if (u->is_downstream) { - return luaL_error(L, "attempt to write to request sockets"); - } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, "lua socket send timeout: %M", u->send_timeout); @@ -1412,6 +1618,11 @@ ngx_tcp_lua_socket_tcp_send(lua_State *L) return luaL_argerror(L, 2, msg); } + if (u->is_downstream) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, + "downstream write" ); + } + ctx = ngx_tcp_get_module_ctx(s, ngx_tcp_lua_module); cl = ngx_tcp_lua_chains_get_free_buf(s->connection->log, s->pool, @@ -1439,6 +1650,8 @@ ngx_tcp_lua_socket_tcp_send(lua_State *L) default: return luaL_error(L, "impossible to reach here"); } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP,s->connection->log, 0,"request chain size %d\n",chain_size(u->request_bufs)); + u->request_bufs = cl; @@ -1481,7 +1694,7 @@ ngx_tcp_lua_socket_tcp_send(lua_State *L) /* rc == NGX_AGAIN */ /* set s->write_event_handler to go on session process */ - s->write_event_handler = ngx_tcp_lua_wev_handler; + s->write_event_handler = ngx_tcp_lua_socket_write_handler; u->waiting = 1; u->prepare_retvals = ngx_tcp_lua_socket_tcp_send_retval_handler; @@ -1738,6 +1951,32 @@ ngx_tcp_lua_socket_send_handler(ngx_tcp_session_t *s, } } +static ngx_int_t ngx_tcp_send_chain(ngx_connection_t *c, + ngx_buf_t *b) +{ + ngx_int_t n; + for (;;) { + n = c->send(c, b->pos, b->last - b->pos); + + if (n < 0) { + /* NGX_ERROR || NGX_AGAIN */ + break; + } + + b->pos += n; + if (b->pos == b->last) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "lua tcp socket sent all the data"); + return NGX_OK; + } + /* keep sending more data */ + } + if(n==NGX_ERROR){ + return NGX_ERROR; + } + + return NGX_AGAIN; +} static ngx_int_t ngx_tcp_lua_socket_send(ngx_tcp_session_t *s, @@ -1749,12 +1988,12 @@ ngx_tcp_lua_socket_send(ngx_tcp_session_t *s, c = u->peer.connection; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, - "lua socket send data: %p", u); + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, + "lua socket send data: %p connect:%p log:%p ", u,c,c->log); dd("lua connection log: %p", c->log); - rc = ngx_output_chain(&u->output, u->request_sent ? NULL : u->request_bufs); + rc = ngx_tcp_send_chain(c,u->request_bufs->buf);//ngx_output_chain(&u->output, u->request_sent ? NULL : u->request_bufs); dd("output chain returned: %d", (int) rc); @@ -1770,6 +2009,8 @@ ngx_tcp_lua_socket_send(ngx_tcp_session_t *s, } if (rc == NGX_AGAIN) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "lua socket again "); u->write_event_handler = ngx_tcp_lua_socket_send_handler; u->read_event_handler = ngx_tcp_lua_socket_dummy_handler; @@ -1803,6 +2044,8 @@ ngx_tcp_lua_socket_send(ngx_tcp_session_t *s, &ctx->free_bufs, &ctx->busy_bufs, &u->request_bufs, (ngx_buf_tag_t) &ngx_tcp_lua_module); + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "chain size: free:%d busy:%d request:%d", chain_size(&ctx->free_bufs),chain_size(&ctx->busy_bufs) ,chain_size(u->request_bufs)); u->request_sent = 0; u->write_event_handler = ngx_tcp_lua_socket_dummy_handler; @@ -2081,7 +2324,13 @@ ngx_tcp_lua_socket_test_connect(ngx_connection_t *c) } if (err) { - (void) ngx_connection_error(c, err, "connect() failed"); + char errstr[255]; + char * addr=""; + if(c->sockaddr){ + addr= inet_ntoa(((struct sockaddr_in*)(c->sockaddr))->sin_addr); + } + snprintf(errstr,255,"connect() %s failed",addr); + (void) ngx_connection_error(c, err, errstr); return err; } } @@ -2639,6 +2888,17 @@ ngx_tcp_lua_req_socket(lua_State *L) s = lua_touserdata(L, -1); lua_pop(L, 1); + lua_pushlightuserdata(L, &ngx_tcp_lua_request_socket_key); + lua_rawget(L, LUA_GLOBALSINDEX); + if(!lua_isnil(L,-1)){ + lua_settop(L, 1); + lua_pushnil(L); + return 2; + }else{ + lua_pop(L,1); + } + + ctx = ngx_tcp_get_module_ctx(s, ngx_tcp_lua_module); if (ctx == NULL) { return luaL_error(L, "no ctx found"); @@ -2697,6 +2957,10 @@ ngx_tcp_lua_req_socket(lua_State *L) c = s->connection; pc->connection = c; + u->writer.out = NULL; + u->writer.last = &u->writer.out; + u->writer.connection = c; + u->writer.limit = 0; ctx->data = u; @@ -2706,12 +2970,15 @@ ngx_tcp_lua_req_socket(lua_State *L) ngx_del_timer(c->read); } + lua_pushlightuserdata(L, &ngx_tcp_lua_request_socket_key); + lua_pushvalue (L,1); + lua_rawset(L, LUA_GLOBALSINDEX); + lua_settop(L, 1); lua_pushnil(L); return 2; } - static void ngx_tcp_lua_req_socket_rev_handler(ngx_tcp_session_t *s) { @@ -3368,6 +3635,8 @@ ngx_tcp_lua_socket_push_input_data(ngx_tcp_session_t *s, ngx_pfree(s->pool, p); done: + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, + "push_input done ll:%p nbufs:%d free:%p",ll,nbufs,ctx->free_recv_bufs); if (nbufs > 1 && ll) { dd("recycle buffers: %d", (int) (nbufs - 1)); diff --git a/src/ngx_tcp_lua_string.c b/src/ngx_tcp_lua_string.c new file mode 100644 index 0000000..a04ae37 --- /dev/null +++ b/src/ngx_tcp_lua_string.c @@ -0,0 +1,947 @@ +#ifndef DDEBUG +#define DDEBUG 0 +#endif +//#include "ddebug.h" + + +#include "ngx_tcp_lua_string.h" +#include "ngx_tcp_lua_util.h" +//#include "ngx_tcp_lua_args.h" +#include "ngx_crc32.h" + +#if NGX_HAVE_SHA1 +#include "ngx_sha1.h" +#endif + +#include "ngx_md5.h" + +#if (NGX_OPENSSL) +#include +#include +#endif + + +#ifndef SHA_DIGEST_LENGTH +#define SHA_DIGEST_LENGTH 20 +#endif + + +static uintptr_t ngx_tcp_lua_ngx_escape_sql_str(u_char *dst, u_char *src, + size_t size); +static int ngx_tcp_lua_ngx_escape_uri(lua_State *L); +static int ngx_tcp_lua_ngx_unescape_uri(lua_State *L); +static int ngx_tcp_lua_ngx_quote_sql_str(lua_State *L); +static int ngx_tcp_lua_ngx_md5(lua_State *L); +static int ngx_tcp_lua_ngx_md5_bin(lua_State *L); + +#if (NGX_HAVE_SHA1) +static int ngx_tcp_lua_ngx_sha1_bin(lua_State *L); +#endif + +static int ngx_tcp_lua_ngx_decode_base64(lua_State *L); +static int ngx_tcp_lua_ngx_encode_base64(lua_State *L); +static int ngx_tcp_lua_ngx_crc32_short(lua_State *L); +static int ngx_tcp_lua_ngx_crc32_long(lua_State *L); +//static int ngx_tcp_lua_ngx_encode_args(lua_State *L); +//static int ngx_tcp_lua_ngx_decode_args(lua_State *L); +#if (NGX_OPENSSL) +static int ngx_tcp_lua_ngx_hmac_sha1(lua_State *L); +#endif + + +void +ngx_tcp_lua_inject_string_api(lua_State *L) +{ + lua_pushcfunction(L, ngx_tcp_lua_ngx_escape_uri); + lua_setfield(L, -2, "escape_uri"); + + lua_pushcfunction(L, ngx_tcp_lua_ngx_unescape_uri); + lua_setfield(L, -2, "unescape_uri"); + + /*lua_pushcfunction(L, ngx_tcp_lua_ngx_encode_args); + lua_setfield(L, -2, "encode_args"); + + lua_pushcfunction(L, ngx_tcp_lua_ngx_decode_args); + lua_setfield(L, -2, "decode_args");*/ + + lua_pushcfunction(L, ngx_tcp_lua_ngx_quote_sql_str); + lua_setfield(L, -2, "quote_sql_str"); + + lua_pushcfunction(L, ngx_tcp_lua_ngx_decode_base64); + lua_setfield(L, -2, "decode_base64"); + + lua_pushcfunction(L, ngx_tcp_lua_ngx_encode_base64); + lua_setfield(L, -2, "encode_base64"); + + lua_pushcfunction(L, ngx_tcp_lua_ngx_md5_bin); + lua_setfield(L, -2, "md5_bin"); + + lua_pushcfunction(L, ngx_tcp_lua_ngx_md5); + lua_setfield(L, -2, "md5"); + +#if (NGX_HAVE_SHA1) + lua_pushcfunction(L, ngx_tcp_lua_ngx_sha1_bin); + lua_setfield(L, -2, "sha1_bin"); +#endif + + lua_pushcfunction(L, ngx_tcp_lua_ngx_crc32_short); + lua_setfield(L, -2, "crc32_short"); + + lua_pushcfunction(L, ngx_tcp_lua_ngx_crc32_long); + lua_setfield(L, -2, "crc32_long"); + +#if (NGX_OPENSSL) + lua_pushcfunction(L, ngx_tcp_lua_ngx_hmac_sha1); + lua_setfield(L, -2, "hmac_sha1"); +#endif +} + + +uintptr_t +ngx_tcp_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 /.-, +*)( '&%$ #"! */ + 0xfc00886d, /* 1111 1100 0000 0000 1000 1000 0110 1101 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x78000000, /* 0111 1000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0xa8000000, /* 1010 1000 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 */ + }; + + /* " ", "#", """, "%", "'", %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, 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_tcp_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_tcp_lua_ngx_escape_uri(lua_State *L) +{ + ngx_tcp_session_t *r; + size_t len, dlen; + uintptr_t escape; + u_char *src, *dst; + + lua_pushlightuserdata(L, &ngx_tcp_lua_request_key); + lua_rawget(L, LUA_GLOBALSINDEX); + r = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (r == NULL) { + return luaL_error(L, "no request object found"); + } + + if (lua_gettop(L) != 1) { + return luaL_error(L, "expecting one argument"); + } + + src = (u_char *) luaL_checklstring(L, 1, &len); + + if (len == 0) { + lua_pushlstring(L, NULL, 0); + return 1; + } + + escape = 2 * ngx_tcp_lua_escape_uri(NULL, src, len, NGX_ESCAPE_URI); + + dlen = escape + len; + + dst = ngx_palloc(r->pool, dlen); + if (dst == NULL) { + return luaL_error(L, "memory allocation error"); + } + + if (escape == 0) { + ngx_memcpy(dst, src, len); + + } else { + ngx_tcp_lua_escape_uri(dst, src, len, NGX_ESCAPE_URI); + } + + lua_pushlstring(L, (char *) dst, dlen); + + return 1; +} + + +static int +ngx_tcp_lua_ngx_unescape_uri(lua_State *L) +{ + ngx_tcp_session_t *r; + size_t len, dlen; + u_char *p; + u_char *src, *dst; + + lua_pushlightuserdata(L, &ngx_tcp_lua_request_key); + lua_rawget(L, LUA_GLOBALSINDEX); + r = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (r == NULL) { + return luaL_error(L, "no request object found"); + } + + if (lua_gettop(L) != 1) { + return luaL_error(L, "expecting one argument"); + } + + src = (u_char *) luaL_checklstring(L, 1, &len); + + /* the unescaped string can only be smaller */ + dlen = len; + + p = ngx_palloc(r->pool, dlen); + if (p == NULL) { + return luaL_error(L, "memory allocation error"); + } + + dst = p; + + ngx_tcp_lua_unescape_uri(&dst, &src, len, NGX_UNESCAPE_URI_COMPONENT); + + lua_pushlstring(L, (char *) p, dst - p); + + return 1; +} + + +static int +ngx_tcp_lua_ngx_quote_sql_str(lua_State *L) +{ + ngx_tcp_session_t *r; + size_t len, dlen, escape; + u_char *p; + u_char *src, *dst; + + lua_pushlightuserdata(L, &ngx_tcp_lua_request_key); + lua_rawget(L, LUA_GLOBALSINDEX); + r = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (r == NULL) { + return luaL_error(L, "no request object found"); + } + + 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_tcp_lua_ngx_escape_sql_str(NULL, src, len); + + dlen = sizeof("''") - 1 + len + escape; + + p = ngx_palloc(r->pool, dlen); + if (p == NULL) { + return luaL_error(L, "out of memory"); + } + + dst = p; + + *p++ = '\''; + + if (escape == 0) { + p = ngx_copy(p, src, len); + + } else { + p = (u_char *) ngx_tcp_lua_ngx_escape_sql_str(p, src, len); + } + + *p++ = '\''; + + if (p != dst + dlen) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "ngx.quote_sql_str: buffer error"); + return NGX_ERROR; + } + + lua_pushlstring(L, (char *) dst, p - dst); + + return 1; +} + + +static uintptr_t +ngx_tcp_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 '\r': + case '\n': + case '\\': + case '\'': + case '"': + case '\032': + n++; + break; + default: + break; + } + } + src++; + size--; + } + + return (uintptr_t) n; + } + + while (size) { + if ((*src & 0x80) == 0) { + switch (*src) { + case '\r': + *dst++ = '\\'; + *dst++ = 'r'; + break; + + case '\n': + *dst++ = '\\'; + *dst++ = 'n'; + break; + + case '\\': + *dst++ = '\\'; + *dst++ = '\\'; + break; + + case '\'': + *dst++ = '\\'; + *dst++ = '\''; + break; + + case '"': + *dst++ = '\\'; + *dst++ = '"'; + break; + + case '\032': + *dst++ = '\\'; + *dst++ = *src; + break; + + default: + *dst++ = *src; + break; + } + } else { + *dst++ = *src; + } + src++; + size--; + } /* while (size) */ + + return (uintptr_t) dst; +} + + +static int +ngx_tcp_lua_ngx_md5(lua_State *L) +{ + u_char *src; + size_t slen; + + ngx_md5_t md5; + u_char md5_buf[MD5_DIGEST_LENGTH]; + u_char hex_buf[2 * sizeof(md5_buf)]; + + if (lua_gettop(L) != 1) { + return luaL_error(L, "expecting one argument"); + } + + if (strcmp(luaL_typename(L, 1), (char *) "nil") == 0) { + src = (u_char *) ""; + slen = 0; + + } else { + src = (u_char *) luaL_checklstring(L, 1, &slen); + } + + ngx_md5_init(&md5); + ngx_md5_update(&md5, src, slen); + ngx_md5_final(md5_buf, &md5); + + ngx_hex_dump(hex_buf, md5_buf, sizeof(md5_buf)); + + lua_pushlstring(L, (char *) hex_buf, sizeof(hex_buf)); + + return 1; +} + + +static int +ngx_tcp_lua_ngx_md5_bin(lua_State *L) +{ + u_char *src; + size_t slen; + + ngx_md5_t md5; + u_char md5_buf[MD5_DIGEST_LENGTH]; + + if (lua_gettop(L) != 1) { + return luaL_error(L, "expecting one argument"); + } + + if (lua_isnil(L, 1)) { + src = (u_char *) ""; + slen = 0; + + } else { + src = (u_char *) luaL_checklstring(L, 1, &slen); + } + + dd("slen: %d", (int) slen); + + ngx_md5_init(&md5); + ngx_md5_update(&md5, src, slen); + ngx_md5_final(md5_buf, &md5); + + lua_pushlstring(L, (char *) md5_buf, sizeof(md5_buf)); + + return 1; +} + + +#if (NGX_HAVE_SHA1) +static int +ngx_tcp_lua_ngx_sha1_bin(lua_State *L) +{ + u_char *src; + size_t slen; + + ngx_sha1_t sha; + u_char sha_buf[SHA_DIGEST_LENGTH]; + + if (lua_gettop(L) != 1) { + return luaL_error(L, "expecting one argument"); + } + + if (lua_isnil(L, 1)) { + src = (u_char *) ""; + slen = 0; + + } else { + src = (u_char *) luaL_checklstring(L, 1, &slen); + } + + dd("slen: %d", (int) slen); + + ngx_sha1_init(&sha); + ngx_sha1_update(&sha, src, slen); + ngx_sha1_final(sha_buf, &sha); + + lua_pushlstring(L, (char *) sha_buf, sizeof(sha_buf)); + + return 1; +} +#endif + + +static int +ngx_tcp_lua_ngx_decode_base64(lua_State *L) +{ + ngx_tcp_session_t *r; + ngx_str_t p, src; + + lua_pushlightuserdata(L, &ngx_tcp_lua_request_key); + lua_rawget(L, LUA_GLOBALSINDEX); + r = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (r == NULL) { + return luaL_error(L, "no request object found"); + } + + if (lua_gettop(L) != 1) { + return luaL_error(L, "expecting one argument"); + } + + if (strcmp(luaL_typename(L, 1), (char *) "nil") == 0) { + src.data = (u_char *) ""; + src.len = 0; + + } else { + src.data = (u_char *) luaL_checklstring(L, 1, &src.len); + } + + p.len = ngx_base64_decoded_length(src.len); + + p.data = ngx_palloc(r->pool, p.len); + if (p.data == NULL) { + return NGX_ERROR; + } + + if (ngx_decode_base64(&p, &src) == NGX_OK) { + lua_pushlstring(L, (char *) p.data, p.len); + + } else { + lua_pushnil(L); + } + + ngx_pfree(r->pool, p.data); + + return 1; +} + + +static int +ngx_tcp_lua_ngx_encode_base64(lua_State *L) +{ + ngx_tcp_session_t *r; + ngx_str_t p, src; + + lua_pushlightuserdata(L, &ngx_tcp_lua_request_key); + lua_rawget(L, LUA_GLOBALSINDEX); + r = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (r == NULL) { + return luaL_error(L, "no request object found"); + } + + if (lua_gettop(L) != 1) { + return luaL_error(L, "expecting one argument"); + } + + if (strcmp(luaL_typename(L, 1), (char *) "nil") == 0) { + src.data = (u_char *) ""; + src.len = 0; + + } else { + src.data = (u_char *) luaL_checklstring(L, 1, &src.len); + } + + p.len = ngx_base64_encoded_length(src.len); + + p.data = ngx_palloc(r->pool, p.len); + if (p.data == NULL) { + return NGX_ERROR; + } + + ngx_encode_base64(&p, &src); + + lua_pushlstring(L, (char *) p.data, p.len); + + ngx_pfree(r->pool, p.data); + + return 1; +} + + +static int +ngx_tcp_lua_ngx_crc32_short(lua_State *L) +{ + u_char *p; + size_t len; + + if (lua_gettop(L) != 1) { + return luaL_error(L, "expecting one argument, but got %d", + lua_gettop(L)); + } + + p = (u_char *) luaL_checklstring(L, 1, &len); + + lua_pushnumber(L, (lua_Number) ngx_crc32_short(p, len)); + return 1; +} + + +static int +ngx_tcp_lua_ngx_crc32_long(lua_State *L) +{ + u_char *p; + size_t len; + + if (lua_gettop(L) != 1) { + return luaL_error(L, "expecting one argument, but got %d", + lua_gettop(L)); + } + + p = (u_char *) luaL_checklstring(L, 1, &len); + + lua_pushnumber(L, (lua_Number) ngx_crc32_long(p, len)); + return 1; +} + + +/*static int +ngx_tcp_lua_ngx_encode_args(lua_State *L) { + ngx_tcp_session_t *r; + ngx_str_t args; + + if (lua_gettop(L) != 1) { + return luaL_error(L, "expecting 1 argument but seen %d", + lua_gettop(L)); + } + + lua_pushlightuserdata(L, &ngx_tcp_lua_request_key); + lua_rawget(L, LUA_GLOBALSINDEX); + r = lua_touserdata(L, -1); + lua_pop(L, 1); + + luaL_checktype(L, 1, LUA_TTABLE); + + ngx_http_lua_process_args_option(r, L, 1, &args); + + lua_pushlstring(L, (char *) args.data, args.len); + + ngx_pfree(r->pool, args.data); + + return 1; +}*/ + +/*static int +ngx_tcp_lua_ngx_decode_args(lua_State *L) { + ngx_tcp_session_t *r; + u_char *buf; + u_char *last; + 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_HTTP_LUA_MAX_ARGS; + } + + lua_pushlightuserdata(L, &ngx_tcp_lua_request_key); + lua_rawget(L, LUA_GLOBALSINDEX); + r = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (r == NULL) { + return luaL_error(L, "no request object found"); + } + + lua_createtable(L, 0, 4); + + last = buf + len; + + return ngx_http_lua_parse_args(r, L, buf, last, max); +}*/ + + +#if (NGX_OPENSSL) + +static int +ngx_tcp_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 one argument, 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 + diff --git a/src/ngx_tcp_lua_string.h b/src/ngx_tcp_lua_string.h new file mode 100644 index 0000000..8804186 --- /dev/null +++ b/src/ngx_tcp_lua_string.h @@ -0,0 +1,12 @@ +#ifndef NGX_TCP_LUA_STRING_H +#define NGX_TCP_LUA_STRING_H + + +#include "ngx_tcp_lua_common.h" +#define NGX_UNESCAPE_URI_COMPONENT 0 + +void ngx_tcp_lua_inject_string_api(lua_State *L); + + +#endif /* NGX_TCP_LUA_STRING_H */ + diff --git a/src/ngx_tcp_lua_time.c b/src/ngx_tcp_lua_time.c new file mode 100644 index 0000000..e05ec8a --- /dev/null +++ b/src/ngx_tcp_lua_time.c @@ -0,0 +1,217 @@ +#ifndef DDEBUG +#define DDEBUG 0 +#endif +//#include "ddebug.h" +#include "ngx_tcp_lua_time.h" + +extern u_char *ngx_http_time(u_char *buf, time_t t); +extern time_t ngx_http_parse_time(u_char *value, size_t len); +static int ngx_tcp_lua_ngx_today(lua_State *L); +static int ngx_tcp_lua_ngx_time(lua_State *L); +static int ngx_tcp_lua_ngx_now(lua_State *L); +static int ngx_tcp_lua_ngx_localtime(lua_State *L); +static int ngx_tcp_lua_ngx_utctime(lua_State *L); +static int ngx_tcp_lua_ngx_cookie_time(lua_State *L); +static int ngx_tcp_lua_ngx_http_time(lua_State *L); +static int ngx_tcp_lua_ngx_parse_http_time(lua_State *L); +static int ngx_tcp_lua_ngx_update_time(lua_State *L); + + +static int +ngx_tcp_lua_ngx_today(lua_State *L) +{ + time_t now; + ngx_tm_t tm; + u_char buf[sizeof("2010-11-19") - 1]; + + now = ngx_time(); + ngx_gmtime(now + ngx_cached_time->gmtoff * 60, &tm); + + ngx_sprintf(buf, "%04d-%02d-%02d", tm.ngx_tm_year, tm.ngx_tm_mon, + tm.ngx_tm_mday); + + lua_pushlstring(L, (char *) buf, sizeof(buf)); + + return 1; +} + + +static int +ngx_tcp_lua_ngx_localtime(lua_State *L) +{ + ngx_tm_t tm; + + u_char buf[sizeof("2010-11-19 20:56:31") - 1]; + + 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); + + lua_pushlstring(L, (char *) buf, sizeof(buf)); + + return 1; +} + + +static int +ngx_tcp_lua_ngx_time(lua_State *L) +{ + lua_pushnumber(L, (lua_Number) ngx_time()); + + return 1; +} + + +static int +ngx_tcp_lua_ngx_now(lua_State *L) +{ + ngx_time_t *tp; + + tp = ngx_timeofday(); + + lua_pushnumber(L, (lua_Number) (tp->sec + tp->msec / 1000.0L)); + + return 1; +} + + +static int +ngx_tcp_lua_ngx_update_time(lua_State *L) +{ + ngx_time_update(); + return 0; +} + + +static int +ngx_tcp_lua_ngx_utctime(lua_State *L) +{ + ngx_tm_t tm; + + u_char buf[sizeof("2010-11-19 20:56:31") - 1]; + + 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); + + lua_pushlstring(L, (char *) buf, sizeof(buf)); + + return 1; +} + + +static int +ngx_tcp_lua_ngx_cookie_time(lua_State *L) +{ + time_t t; + u_char *p; + + u_char buf[sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1]; + + if (lua_gettop(L) != 1) { + return luaL_error(L, "expecting one argument"); + } + + t = (time_t) luaL_checknumber(L, 1); + + p = buf; + p = ngx_http_cookie_time(p, t); + + lua_pushlstring(L, (char *) buf, p - buf); + + return 1; +} + + +static int +ngx_tcp_lua_ngx_http_time(lua_State *L) +{ + time_t t; + u_char *p; + + u_char buf[sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1]; + + if (lua_gettop(L) != 1) { + return luaL_error(L, "expecting one argument"); + } + + t = (time_t) luaL_checknumber(L, 1); + + p = buf; + p = ngx_http_time(p, t); + + lua_pushlstring(L, (char *) buf, p - buf); + + return 1; +} + + +static int +ngx_tcp_lua_ngx_parse_http_time(lua_State *L) +{ + u_char *p; + size_t len; + time_t time; + + if (lua_gettop(L) != 1) { + return luaL_error(L, "expecting one argument"); + } + + p = (u_char *) luaL_checklstring(L, 1, &len); + + time = ngx_http_parse_time(p, len); + if (time == NGX_ERROR) { + lua_pushnil(L); + return 1; + } + + lua_pushnumber(L, (lua_Number) time); + + return 1; +} + + +void +ngx_tcp_lua_inject_time_api(lua_State *L) +{ + lua_pushcfunction(L, ngx_tcp_lua_ngx_utctime); + lua_setfield(L, -2, "utctime"); + + lua_pushcfunction(L, ngx_tcp_lua_ngx_time); + lua_setfield(L, -2, "get_now_ts"); /* deprecated */ + + lua_pushcfunction(L, ngx_tcp_lua_ngx_localtime); + lua_setfield(L, -2, "get_now"); /* deprecated */ + + lua_pushcfunction(L, ngx_tcp_lua_ngx_localtime); + lua_setfield(L, -2, "localtime"); + + lua_pushcfunction(L, ngx_tcp_lua_ngx_time); + lua_setfield(L, -2, "time"); + + lua_pushcfunction(L, ngx_tcp_lua_ngx_now); + lua_setfield(L, -2, "now"); + + lua_pushcfunction(L, ngx_tcp_lua_ngx_update_time); + lua_setfield(L, -2, "update_time"); + + lua_pushcfunction(L, ngx_tcp_lua_ngx_today); + lua_setfield(L, -2, "get_today"); /* deprecated */ + + lua_pushcfunction(L, ngx_tcp_lua_ngx_today); + lua_setfield(L, -2, "today"); + + lua_pushcfunction(L, ngx_tcp_lua_ngx_cookie_time); + lua_setfield(L, -2, "cookie_time"); + + lua_pushcfunction(L, ngx_tcp_lua_ngx_http_time); + lua_setfield(L, -2, "http_time"); + + lua_pushcfunction(L, ngx_tcp_lua_ngx_parse_http_time); + lua_setfield(L, -2, "parse_http_time"); +} + diff --git a/src/ngx_tcp_lua_time.h b/src/ngx_tcp_lua_time.h new file mode 100644 index 0000000..819a298 --- /dev/null +++ b/src/ngx_tcp_lua_time.h @@ -0,0 +1,12 @@ +#ifndef NGX_TCP_LUA_TIME_H +#define NGX_TCP_LUA_TIME_H + + +#include "ngx_tcp_lua_common.h" + + +void ngx_tcp_lua_inject_time_api(lua_State *L); + + +#endif /* NGX_HTTP_LUA_TIME_H */ + diff --git a/src/ngx_tcp_lua_util.c b/src/ngx_tcp_lua_util.c index 22949e9..185f94a 100755 --- a/src/ngx_tcp_lua_util.c +++ b/src/ngx_tcp_lua_util.c @@ -1,10 +1,15 @@ +//tcp only #include "ngx_md5.h" #include "ngx_tcp_lua_util.h" #include "ngx_tcp_lua_output.h" #include "ngx_tcp_lua_socket.h" #include "ngx_tcp_lua_exception.h" #include "ngx_tcp_lua_log.h" +#include "ngx_tcp_lua_string.h" +#include "ngx_tcp_lua_time.h" +#include "ngx_tcp_lua_shdict.h" +#include "ngx_tcp_lua_variable.h" char ngx_tcp_lua_code_cache_key; @@ -12,6 +17,7 @@ char ngx_tcp_lua_ctx_tables_key; char ngx_tcp_lua_regex_cache_key; char ngx_tcp_lua_socket_pool_key; char ngx_tcp_lua_request_key; +char ngx_tcp_lua_request_socket_key; /* coroutine anchoring table key in Lua vm registry */ @@ -24,7 +30,7 @@ static char ngx_tcp_lua_coroutines_key; #define AUX_MARK "\1" void ngx_tcp_lua_create_new_global_table(lua_State *L, int narr, int nrec); -static void ngx_tcp_lua_inject_ngx_api(ngx_conf_t *cf, lua_State *L); +static void ngx_tcp_lua_inject_ngx_api(ngx_conf_t *cf, lua_State *L,ngx_tcp_lua_main_conf_t *lmcf); void ngx_tcp_lua_inject_core_consts(lua_State *L); @@ -92,7 +98,7 @@ ngx_tcp_lua_init_registry(ngx_conf_t *cf, lua_State *L) static void -ngx_tcp_lua_init_globals(ngx_conf_t *cf, lua_State *L) +ngx_tcp_lua_init_globals(ngx_conf_t *cf, lua_State *L,ngx_tcp_lua_main_conf_t *lmcf) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0, "lua initializing lua globals"); @@ -103,13 +109,14 @@ ngx_tcp_lua_init_globals(ngx_conf_t *cf, lua_State *L) /* }}} */ - ngx_tcp_lua_inject_ngx_api(cf, L); + ngx_tcp_lua_inject_ngx_api(cf, L, lmcf); } static void -ngx_tcp_lua_inject_ngx_api(ngx_conf_t *cf, lua_State *L) +ngx_tcp_lua_inject_ngx_api(ngx_conf_t *cf, lua_State *L,ngx_tcp_lua_main_conf_t *lmcf) { + lua_createtable(L, 0 /* narr */, 89 /* nrec */); /* ngx.* */ ngx_tcp_lua_inject_core_consts(L); @@ -117,11 +124,19 @@ ngx_tcp_lua_inject_ngx_api(ngx_conf_t *cf, lua_State *L) ngx_tcp_lua_inject_log_api(L); ngx_tcp_lua_inject_output_api(L); + + ngx_tcp_lua_inject_string_api(L); ngx_tcp_lua_inject_req_socket_api(L); ngx_tcp_lua_inject_socket_api(cf->log, L); + ngx_tcp_lua_inject_time_api(L); + + ngx_tcp_lua_inject_shdict_api(lmcf, L); + + ngx_tcp_lua_inject_variable_api(L); + lua_getglobal(L, "package"); /* ngx package */ lua_getfield(L, -1, "loaded"); /* ngx package loaded */ lua_pushvalue(L, -3); /* ngx package loaded ngx */ @@ -211,7 +226,7 @@ ngx_tcp_lua_new_state(ngx_conf_t *cf, ngx_tcp_lua_main_conf_t *lmcf) lua_remove(L, -1); /* remove the "package" table */ ngx_tcp_lua_init_registry(cf, L); - ngx_tcp_lua_init_globals(cf, L); + ngx_tcp_lua_init_globals(cf, L, lmcf); return L; } @@ -384,9 +399,11 @@ ngx_tcp_lua_run_thread(lua_State *L, ngx_tcp_session_t *s, ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, "lua thread yielded"); + if(ctx->exited != 1){ - lua_settop(cc, 0); - return NGX_AGAIN; + lua_settop(cc, 0); + return NGX_AGAIN; + } case 0: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, @@ -510,7 +527,7 @@ ngx_tcp_lua_wev_handler(ngx_tcp_session_t *s) return; } - if (rc == NGX_DONE || rc == NGX_OK) { + if (rc == NGX_DONE || rc == NGX_OK || rc==NGX_ERROR) { ngx_tcp_finalize_session(s); return; } diff --git a/src/ngx_tcp_lua_util.h b/src/ngx_tcp_lua_util.h index efe3669..67d32d8 100644 --- a/src/ngx_tcp_lua_util.h +++ b/src/ngx_tcp_lua_util.h @@ -28,6 +28,8 @@ extern char ngx_tcp_lua_request_key; extern char ngx_tcp_lua_cf_log_key; +/* char whose address we'll use as key for the nginx request socket pointer */ +extern char ngx_tcp_lua_request_socket_key; lua_State *ngx_tcp_lua_new_state(ngx_conf_t *cf, ngx_tcp_lua_main_conf_t *lmcf); lua_State *ngx_tcp_lua_new_thread(ngx_tcp_session_t *s, lua_State *L, int *ref); diff --git a/src/ngx_tcp_lua_variable.c b/src/ngx_tcp_lua_variable.c new file mode 100644 index 0000000..8aa00c8 --- /dev/null +++ b/src/ngx_tcp_lua_variable.c @@ -0,0 +1,282 @@ +#ifndef DDEBUG +#define DDEBUG 0 +#endif + +#include "ngx_tcp_lua_variable.h" +#include "ngx_tcp_lua_util.h" +#include "ngx_tcp_variables.h" + + +static int ngx_tcp_lua_var_get(lua_State *L); +static int ngx_tcp_lua_var_set(lua_State *L); + + +void +ngx_tcp_lua_inject_variable_api(lua_State *L) +{ + /* register reference maps */ + lua_newtable(L); /* ngx.var */ + + lua_createtable(L, 0, 2 /* nrec */); /* metatable for .var */ + lua_pushcfunction(L, ngx_tcp_lua_var_get); + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, ngx_tcp_lua_var_set); + lua_setfield(L, -2, "__newindex"); + lua_setmetatable(L, -2); + + lua_setfield(L, -2, "var"); +} + + +/** + * Get nginx internal variables content + * + * @retval Always return a string or nil on Lua stack. Return nil when failed + * to get content, and actual content string when found the specified variable. + * @seealso ngx_tcp_lua_var_set + * */ +static int +ngx_tcp_lua_var_get(lua_State *L) +{ + ngx_tcp_session_t *s; + u_char *p, *lowcase; + size_t len; + ngx_uint_t hash; + ngx_str_t name; + ngx_tcp_variable_value_t *vv; +/* +#if (NGX_PCRE) + u_char *val; + ngx_uint_t n; + LUA_NUMBER index; + int *cap; +#endif +*/ + + lua_pushlightuserdata(L, &ngx_tcp_lua_request_key); + lua_rawget(L, LUA_GLOBALSINDEX); + s = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (s == NULL) { + return luaL_error(L, "no request object found"); + } + +/*#if (NGX_PCRE)*/ +#if 0 + if (lua_type(L, -1) == LUA_TNUMBER) { + /* it is a regex capturing variable */ + + index = lua_tonumber(L, -1); + + if (index <= 0) { + lua_pushnil(L); + return 1; + } + + n = (ngx_uint_t) index * 2; + + /*dd("n = %d, ncaptures = %d", (int) n, (int) s->ncaptures);*/ + + if (s->captures == NULL || s->captures_data == NULL || + n >= s->ncaptures) + { + lua_pushnil(L); + return 1; + } + + /* n >= 0 && n < s->ncaptures */ + + cap = s->captures; + + p = s->captures_data; + + val = &p[cap[n]]; + + lua_pushlstring(L, (const char *) val, (size_t) (cap[n + 1] - cap[n])); + + return 1; + } +#endif + + p = (u_char *) luaL_checklstring(L, -1, &len); + + lowcase = ngx_palloc(s->pool, len); + if (lowcase == NULL) { + return luaL_error(L, "memory allocation error"); + } + + hash = ngx_hash_strlow(lowcase, p, len); + + name.len = len; + name.data = lowcase; + vv = ngx_tcp_get_variable(s, &name, hash); + + if (vv == NULL || vv->not_found) { + lua_pushnil(L); + return 1; + } + + lua_pushlstring(L, (const char *) vv->data, (size_t) vv->len); + + return 1; +} + + +/** + * Set nginx internal variable content + * + * @retval Always return a boolean on Lua stack. Return true when variable + * content was modified successfully, false otherwise. + * @seealso ngx_tcp_lua_var_get + * */ +static int +ngx_tcp_lua_var_set(lua_State *L) +{ + ngx_tcp_variable_t *v; + ngx_tcp_variable_value_t *vv; + ngx_tcp_core_main_conf_t *cmcf; + u_char *p, *lowcase, *val; + size_t len; + ngx_str_t name; + ngx_uint_t hash; + ngx_tcp_session_t *s; + int value_type; + const char *msg; + + lua_pushlightuserdata(L, &ngx_tcp_lua_request_key); + lua_rawget(L, LUA_GLOBALSINDEX); + s = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (s == NULL) { + return luaL_error(L, "no request object found"); + } + + /* we skip the first argument that is the table */ + + /* we read the variable name */ + + p = (u_char *) luaL_checklstring(L, 2, &len); + + lowcase = ngx_palloc(s->pool, len + 1); + if (lowcase == NULL) { + return luaL_error(L, "memory allocation error"); + } + + lowcase[len] = '\0'; + + hash = ngx_hash_strlow(lowcase, p, len); + + name.len = len; + name.data = lowcase; + + /* we read the variable new value */ + + value_type = lua_type(L, 3); + switch (value_type) { + case LUA_TNUMBER: + case LUA_TSTRING: + p = (u_char *) luaL_checklstring(L, 3, &len); + + val = ngx_palloc(s->pool, len); + if (val == NULL) { + return luaL_error(L, "memory allocation erorr"); + } + + ngx_memcpy(val, p, len); + + break; + + case LUA_TNIL: + /* undef the variable */ + + val = NULL; + len = 0; + + break; + + default: + msg = lua_pushfstring(L, "string, number, or nil expected, " + "but got %s", lua_typename(L, value_type)); + return luaL_argerror(L, 1, msg); + } + + /* we fetch the variable itself */ + + cmcf = ngx_tcp_get_module_main_conf(s, ngx_tcp_core_module); + + v = ngx_hash_find(&cmcf->variables_hash, hash, name.data, name.len); + + if (v) { + if (!(v->flags & NGX_TCP_VAR_CHANGEABLE)) { + return luaL_error(L, "variable \"%s\" not changeable", lowcase); + } + + if (v->set_handler) { + + dd("set variables with set_handler"); + + vv = ngx_palloc(s->pool, sizeof(ngx_tcp_variable_value_t)); + if (vv == NULL) { + return luaL_error(L, "out of memory"); + } + + if (value_type == LUA_TNIL) { + 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 = val; + vv->len = len; + } + + v->set_handler(s, vv, v->data); + + return 0; + } + + /*if (v->flags & NGX_TCP_VAR_INDEXED) { + vv = &s->variables[v->index]; + + dd("set indexed variable"); + + if (value_type == LUA_TNIL) { + 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 = val; + vv->len = len; + } + + return 0; + }*/ + + return luaL_error(L, "variable \"%s\" cannot be assigned a value", + lowcase); + } + + /* variable not found */ + + return luaL_error(L, "varaible \"%s\" not found for writing; " + "maybe it is a built-in variable that is not changeable " + "or you sould have used \"set $%s '';\" earlier " + "in the config file", lowcase, lowcase); +} + diff --git a/src/ngx_tcp_lua_variable.h b/src/ngx_tcp_lua_variable.h new file mode 100644 index 0000000..d5717bc --- /dev/null +++ b/src/ngx_tcp_lua_variable.h @@ -0,0 +1,12 @@ +#ifndef NGX_TCP_LUA_VARIABLE_H +#define NGX_TCP_LUA_VARIABLE_H + + +#include "ngx_tcp_lua_common.h" + + +void ngx_tcp_lua_inject_variable_api(lua_State *L); + + +#endif /* NGX_HTTP_LUA_VARIABLE_H */ + diff --git a/src/ngx_tcp_session.h b/src/ngx_tcp_session.h index a422881..8bc994d 100755 --- a/src/ngx_tcp_session.h +++ b/src/ngx_tcp_session.h @@ -48,7 +48,6 @@ struct ngx_tcp_session_s { typedef void (*ngx_tcp_cleanup_pt)(void *data); - struct ngx_tcp_cleanup_s { ngx_tcp_cleanup_pt handler; void *data; diff --git a/src/ngx_tcp_variables.c b/src/ngx_tcp_variables.c new file mode 100644 index 0000000..a30c52f --- /dev/null +++ b/src/ngx_tcp_variables.c @@ -0,0 +1,1008 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include "ngx_tcp.h" +#include "ngx_tcp_variables.h" +/* +static ngx_int_t ngx_tcp_variable_unknown_header_in(ngx_tcp_session_t *s, + ngx_tcp_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_tcp_variable_unknown_header_out(ngx_tcp_session_t *s, + ngx_tcp_variable_value_t *v, uintptr_t data); +*/ +#if (NGX_HAVE_TCP_INFO) +static ngx_int_t ngx_tcp_variable_tcpinfo(ngx_tcp_session_t *s, + ngx_tcp_variable_value_t *v, uintptr_t data); +#endif + +static ngx_int_t ngx_tcp_variable_binary_remote_addr(ngx_tcp_session_t *s, + ngx_tcp_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_tcp_variable_remote_addr(ngx_tcp_session_t *s, + ngx_tcp_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_tcp_variable_remote_port(ngx_tcp_session_t *s, + ngx_tcp_variable_value_t *v, uintptr_t data); + + +static ngx_int_t ngx_tcp_variable_nginx_version(ngx_tcp_session_t *s, + ngx_tcp_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_tcp_variable_hostname(ngx_tcp_session_t *s, + ngx_tcp_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_tcp_variable_pid(ngx_tcp_session_t *s, + ngx_tcp_variable_value_t *v, uintptr_t data); + +/* + * TODO: + * Apache CGI: AUTH_TYPE, PATH_INFO (null), PATH_TRANSLATED + * REMOTE_HOST (null), REMOTE_IDENT (null), + * SERVER_SOFTWARE + * + * Apache SSI: DOCUMENT_NAME, LAST_MODIFIED, USER_NAME (file owner) + */ + +/* + * the $tcp_host, $tcp_user_agent, $tcp_referer, $tcp_via, + * and $tcp_x_forwarded_for variables may be handled by generic + * ngx_tcp_variable_unknown_header_in(), but for performance reasons + * they are handled using dedicated entries + */ + +static ngx_tcp_variable_t ngx_tcp_core_variables[] = { + + { ngx_string("binary_remote_addr"), NULL, + ngx_tcp_variable_binary_remote_addr, 0, 0, 0 }, + + { ngx_string("remote_addr"), NULL, ngx_tcp_variable_remote_addr, 0, 0, 0 }, + + { ngx_string("remote_port"), NULL, ngx_tcp_variable_remote_port, 0, 0, 0 }, + + { ngx_string("nginx_version"), NULL, ngx_tcp_variable_nginx_version, + 0, 0, 0 }, + + { ngx_string("hostname"), NULL, ngx_tcp_variable_hostname, + 0, 0, 0 }, + + { ngx_string("pid"), NULL, ngx_tcp_variable_pid, + 0, 0, 0 }, + +#if (NGX_HAVE_TCP_INFO) + { ngx_string("tcpinfo_rtt"), NULL, ngx_tcp_variable_tcpinfo, + 0, NGX_TCP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("tcpinfo_rttvar"), NULL, ngx_tcp_variable_tcpinfo, + 1, NGX_TCP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("tcpinfo_snd_cwnd"), NULL, ngx_tcp_variable_tcpinfo, + 2, NGX_TCP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("tcpinfo_rcv_space"), NULL, ngx_tcp_variable_tcpinfo, + 3, NGX_TCP_VAR_NOCACHEABLE, 0 }, +#endif + + { ngx_null_string, NULL, NULL, 0, 0, 0 } +}; + + +ngx_tcp_variable_value_t ngx_tcp_variable_null_value = + ngx_tcp_variable(""); +ngx_tcp_variable_value_t ngx_tcp_variable_true_value = + ngx_tcp_variable("1"); + + +ngx_tcp_variable_t * +ngx_tcp_add_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags) +{ + ngx_int_t rc; + ngx_uint_t i; + ngx_hash_key_t *key; + ngx_tcp_variable_t *v; + ngx_tcp_core_main_conf_t *cmcf; + + cmcf = ngx_tcp_conf_get_module_main_conf(cf, ngx_tcp_core_module); + + key = cmcf->variables_keys->keys.elts; + for (i = 0; i < cmcf->variables_keys->keys.nelts; i++) { + if (name->len != key[i].key.len + || ngx_strncasecmp(name->data, key[i].key.data, name->len) != 0) + { + continue; + } + + v = key[i].value; + + if (!(v->flags & NGX_TCP_VAR_CHANGEABLE)) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the duplicate \"%V\" variable", name); + return NULL; + } + + return v; + } + + v = ngx_palloc(cf->pool, sizeof(ngx_tcp_variable_t)); + if (v == NULL) { + return NULL; + } + + v->name.len = name->len; + v->name.data = ngx_pnalloc(cf->pool, name->len); + if (v->name.data == NULL) { + return NULL; + } + + ngx_strlow(v->name.data, name->data, name->len); + + v->set_handler = NULL; + v->get_handler = NULL; + v->data = 0; + v->flags = flags; + v->index = 0; + + rc = ngx_hash_add_key(cmcf->variables_keys, &v->name, v, 0); + + if (rc == NGX_ERROR) { + return NULL; + } + + if (rc == NGX_BUSY) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "conflicting variable name \"%V\"", name); + return NULL; + } + + return v; +} + + +ngx_int_t +ngx_tcp_get_variable_index(ngx_conf_t *cf, ngx_str_t *name) +{ + ngx_uint_t i; + ngx_tcp_variable_t *v; + ngx_tcp_core_main_conf_t *cmcf; + + cmcf = ngx_tcp_conf_get_module_main_conf(cf, ngx_tcp_core_module); + + v = cmcf->variables.elts; + + if (v == NULL) { + if (ngx_array_init(&cmcf->variables, cf->pool, 4, + sizeof(ngx_tcp_variable_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + } else { + for (i = 0; i < cmcf->variables.nelts; i++) { + if (name->len != v[i].name.len + || ngx_strncasecmp(name->data, v[i].name.data, name->len) != 0) + { + continue; + } + + return i; + } + } + + v = ngx_array_push(&cmcf->variables); + if (v == NULL) { + return NGX_ERROR; + } + + v->name.len = name->len; + v->name.data = ngx_pnalloc(cf->pool, name->len); + if (v->name.data == NULL) { + return NGX_ERROR; + } + + ngx_strlow(v->name.data, name->data, name->len); + + v->set_handler = NULL; + v->get_handler = NULL; + v->data = 0; + v->flags = 0; + v->index = cmcf->variables.nelts - 1; + + return v->index; +} + + +/*ngx_tcp_variable_value_t * +ngx_tcp_get_indexed_variable(ngx_tcp_session_t *s, ngx_uint_t index) +{ + ngx_tcp_variable_t *v; + ngx_tcp_core_main_conf_t *cmcf; + + cmcf = ngx_tcp_get_module_main_conf(s, ngx_tcp_core_module); + + if (cmcf->variables.nelts <= index) { + ngx_log_error(NGX_LOG_ALERT, s->connection->log, 0, + "unknown variable index: %d", index); + return NULL; + } + + if (s->variables[index].not_found || s->variables[index].valid) { + return &s->variables[index]; + } + + v = cmcf->variables.elts; + + if (v[index].get_handler(r, &s->variables[index], v[index].data) + == NGX_OK) + { + if (v[index].flags & NGX_TCP_VAR_NOCACHEABLE) { + s->variables[index].no_cacheable = 1; + } + + return &s->variables[index]; + } + + s->variables[index].valid = 0; + s->variables[index].not_found = 1; + + return NULL; +}*/ + + +/*ngx_tcp_variable_value_t * +ngx_tcp_get_flushed_variable(ngx_tcp_session_t *s, ngx_uint_t index) +{ + ngx_tcp_variable_value_t *v; + + v = &s->variables[index]; + + if (v->valid || v->not_found) { + if (!v->no_cacheable) { + return v; + } + + v->valid = 0; + v->not_found = 0; + } + + return ngx_tcp_get_indexed_variable(r, index); +}*/ + + +ngx_tcp_variable_value_t * +ngx_tcp_get_variable(ngx_tcp_session_t *s, ngx_str_t *name, ngx_uint_t key) +{ + ngx_tcp_variable_t *v; + ngx_tcp_variable_value_t *vv; + ngx_tcp_core_main_conf_t *cmcf; + + cmcf = ngx_tcp_get_module_main_conf(s, ngx_tcp_core_module); + + v = ngx_hash_find(&cmcf->variables_hash, key, name->data, name->len); + + if (v) { + if (v->flags & NGX_TCP_VAR_INDEXED) { + return NULL;//ngx_tcp_get_flushed_variable(r, v->index); + + } else { + + vv = ngx_palloc(s->pool, sizeof(ngx_tcp_variable_value_t)); + + if (vv && v->get_handler(s, vv, v->data) == NGX_OK) { + return vv; + } + + return NULL; + } + } + + vv = ngx_palloc(s->pool, sizeof(ngx_tcp_variable_value_t)); + if (vv == NULL) { + return NULL; + } + + /*if (ngx_strncmp(name->data, "tcp_", 5) == 0) { + + if (ngx_tcp_variable_unknown_header_in(r, vv, (uintptr_t) name) + == NGX_OK) + { + return vv; + } + + return NULL; + } + + if (ngx_strncmp(name->data, "sent_tcp_", 10) == 0) { + + if (ngx_tcp_variable_unknown_header_out(r, vv, (uintptr_t) name) + == NGX_OK) + { + return vv; + } + + return NULL; + } + + if (ngx_strncmp(name->data, "upstream_tcp_", 14) == 0) { + + if (ngx_tcp_upstream_header_variable(r, vv, (uintptr_t) name) + == NGX_OK) + { + return vv; + } + + return NULL; + }*/ + + + vv->not_found = 1; + + return vv; +} + + + + + + + + + + + + + + +#if 0 +static ngx_int_t +ngx_tcp_variable_unknown_header_in(ngx_tcp_session_t *s, + ngx_tcp_variable_value_t *v, uintptr_t data) +{ + return ngx_tcp_variable_unknown_header(v, (ngx_str_t *) data, + &s->headers_in.headers.part, + sizeof("tcp_") - 1); +} + + +static ngx_int_t +ngx_tcp_variable_unknown_header_out(ngx_tcp_session_t *s, + ngx_tcp_variable_value_t *v, uintptr_t data) +{ + return ngx_tcp_variable_unknown_header(v, (ngx_str_t *) data, + &s->headers_out.headers.part, + sizeof("sent_tcp_") - 1); +} + + +ngx_int_t +ngx_tcp_variable_unknown_header(ngx_tcp_variable_value_t *v, ngx_str_t *var, + ngx_list_part_t *part, size_t prefix) +{ + u_char ch; + ngx_uint_t i, n; + ngx_table_elt_t *header; + + header = part->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + for (n = 0; n + prefix < var->len && n < header[i].key.len; n++) { + ch = header[i].key.data[n]; + + if (ch >= 'A' && ch <= 'Z') { + ch |= 0x20; + + } else if (ch == '-') { + ch = '_'; + } + + if (var->data[n + prefix] != ch) { + break; + } + } + + if (n + prefix == var->len && n == header[i].key.len) { + v->len = header[i].value.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = header[i].value.data; + + return NGX_OK; + } + } + + v->not_found = 1; + + return NGX_OK; +} +#endif + + + + + +#if (NGX_HAVE_TCP_INFO) + +static ngx_int_t +ngx_tcp_variable_tcpinfo(ngx_tcp_session_t *s, ngx_tcp_variable_value_t *v, + uintptr_t data) +{ + struct tcp_info ti; + socklen_t len; + uint32_t value; + + len = sizeof(struct tcp_info); + if (getsockopt(s->connection->fd, IPPROTO_TCP, TCP_INFO, &ti, &len) == -1) { + v->not_found = 1; + return NGX_OK; + } + + v->data = ngx_pnalloc(s->pool, NGX_INT32_LEN); + if (v->data == NULL) { + return NGX_ERROR; + } + + switch (data) { + case 0: + value = ti.tcpi_rtt; + break; + + case 1: + value = ti.tcpi_rttvar; + break; + + case 2: + value = ti.tcpi_snd_cwnd; + break; + + case 3: + value = ti.tcpi_rcv_space; + break; + + /* suppress warning */ + default: + value = 0; + break; + } + + v->len = ngx_sprintf(v->data, "%uD", value) - v->data; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; +} + +#endif + + + +static ngx_int_t +ngx_tcp_variable_binary_remote_addr(ngx_tcp_session_t *s, + ngx_tcp_variable_value_t *v, uintptr_t data) +{ + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + + switch (s->connection->sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) s->connection->sockaddr; + + v->len = sizeof(struct in6_addr); + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = sin6->sin6_addr.s6_addr; + + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) s->connection->sockaddr; + + v->len = sizeof(in_addr_t); + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) &sin->sin_addr; + + break; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_tcp_variable_remote_addr(ngx_tcp_session_t *s, + ngx_tcp_variable_value_t *v, uintptr_t data) +{ + v->len = s->connection->addr_text.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = s->connection->addr_text.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_tcp_variable_remote_port(ngx_tcp_session_t *s, + ngx_tcp_variable_value_t *v, uintptr_t data) +{ + ngx_uint_t port; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + + v->len = 0; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + v->data = ngx_pnalloc(s->pool, sizeof("65535") - 1); + if (v->data == NULL) { + return NGX_ERROR; + } + + switch (s->connection->sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) s->connection->sockaddr; + port = ntohs(sin6->sin6_port); + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) s->connection->sockaddr; + port = ntohs(sin->sin_port); + break; + } + + if (port > 0 && port < 65536) { + v->len = ngx_sprintf(v->data, "%ui", port) - v->data; + } + + return NGX_OK; +} + +static ngx_int_t +ngx_tcp_variable_nginx_version(ngx_tcp_session_t *s, + ngx_tcp_variable_value_t *v, uintptr_t data) +{ + v->len = sizeof(NGINX_VERSION) - 1; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) NGINX_VERSION; + + return NGX_OK; +} + + +static ngx_int_t +ngx_tcp_variable_hostname(ngx_tcp_session_t *s, + ngx_tcp_variable_value_t *v, uintptr_t data) +{ + v->len = ngx_cycle->hostname.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = ngx_cycle->hostname.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_tcp_variable_pid(ngx_tcp_session_t *s, + ngx_tcp_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + p = ngx_pnalloc(s->pool, NGX_INT64_LEN); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = ngx_sprintf(p, "%P", ngx_pid) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + +#if 0 +void * +ngx_tcp_map_find(ngx_tcp_session_t *s, ngx_tcp_map_t *map, ngx_str_t *match) +{ + void *value; + u_char *low; + size_t len; + ngx_uint_t key; + + len = match->len; + + if (len) { + low = ngx_pnalloc(s->pool, len); + if (low == NULL) { + return NULL; + } + + } else { + low = NULL; + } + + key = ngx_hash_strlow(low, match->data, len); + + value = ngx_hash_find_combined(&map->hash, key, low, len); + if (value) { + return value; + } + +#if (NGX_PCRE) + + if (len && map->nregex) { + ngx_int_t n; + ngx_uint_t i; + ngx_tcp_map_regex_t *reg; + + reg = map->regex; + + for (i = 0; i < map->nregex; i++) { + + n = ngx_tcp_regex_exec(r, reg[i].regex, match); + + if (n == NGX_OK) { + return reg[i].value; + } + + if (n == NGX_DECLINED) { + continue; + } + + /* NGX_ERROR */ + + return NULL; + } + } + +#endif + + return NULL; +} +#endif + +#if 0 +#if (NGX_PCRE) + +static ngx_int_t +ngx_tcp_variable_not_found(ngx_tcp_session_t *s, ngx_tcp_variable_value_t *v, + uintptr_t data) +{ + v->not_found = 1; + return NGX_OK; +} + + +ngx_tcp_regex_t * +ngx_tcp_regex_compile(ngx_conf_t *cf, ngx_regex_compile_t *rc) +{ + u_char *p; + size_t size; + ngx_str_t name; + ngx_uint_t i, n; + ngx_tcp_variable_t *v; + ngx_tcp_regex_t *re; + ngx_tcp_regex_variable_t *rv; + ngx_tcp_core_main_conf_t *cmcf; + + rc->pool = cf->pool; + + if (ngx_regex_compile(rc) != NGX_OK) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc->err); + return NULL; + } + + re = ngx_pcalloc(cf->pool, sizeof(ngx_tcp_regex_t)); + if (re == NULL) { + return NULL; + } + + re->regex = rc->regex; + re->ncaptures = rc->captures; + + cmcf = ngx_tcp_conf_get_module_main_conf(cf, ngx_tcp_core_module); + cmcf->ncaptures = ngx_max(cmcf->ncaptures, re->ncaptures); + + n = (ngx_uint_t) rc->named_captures; + + if (n == 0) { + return re; + } + + rv = ngx_palloc(rc->pool, n * sizeof(ngx_tcp_regex_variable_t)); + if (rv == NULL) { + return NULL; + } + + re->variables = rv; + re->nvariables = n; + re->name = rc->pattern; + + size = rc->name_size; + p = rc->names; + + for (i = 0; i < n; i++) { + rv[i].capture = 2 * ((p[0] << 8) + p[1]); + + name.data = &p[2]; + name.len = ngx_strlen(name.data); + + v = ngx_tcp_add_variable(cf, &name, NGX_TCP_VAR_CHANGEABLE); + if (v == NULL) { + return NULL; + } + + rv[i].index = ngx_tcp_get_variable_index(cf, &name); + if (rv[i].index == NGX_ERROR) { + return NULL; + } + + v->get_handler = ngx_tcp_variable_not_found; + + p += size; + } + + return re; +} + + +ngx_int_t +ngx_tcp_regex_exec(ngx_tcp_session_t *s, ngx_tcp_regex_t *re, ngx_str_t *s) +{ + ngx_int_t rc, index; + ngx_uint_t i, n, len; + ngx_tcp_variable_value_t *vv; + ngx_tcp_core_main_conf_t *cmcf; + + cmcf = ngx_tcp_get_module_main_conf(s, ngx_tcp_core_module); + + if (re->ncaptures) { + len = cmcf->ncaptures; + + if (r->captures == NULL) { + r->captures = ngx_palloc(r->pool, len * sizeof(int)); + if (r->captures == NULL) { + return NGX_ERROR; + } + } + + } else { + len = 0; + } + + rc = ngx_regex_exec(re->regex, s, r->captures, len); + + if (rc == NGX_REGEX_NO_MATCHED) { + return NGX_DECLINED; + } + + if (rc < 0) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"", + rc, s, &re->name); + return NGX_ERROR; + } + + for (i = 0; i < re->nvariables; i++) { + + n = re->variables[i].capture; + index = re->variables[i].index; + vv = &r->variables[index]; + + vv->len = r->captures[n + 1] - r->captures[n]; + vv->valid = 1; + vv->no_cacheable = 0; + vv->not_found = 0; + vv->data = &s->data[r->captures[n]]; + +#if (NGX_DEBUG) + { + ngx_tcp_variable_t *v; + + v = cmcf->variables.elts; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "tcp regex set $%V to \"%*s\"", + &v[index].name, vv->len, vv->data); + } +#endif + } + + r->ncaptures = rc * 2; + r->captures_data = s->data; + + return NGX_OK; +} + +#endif + + + + +#endif + +ngx_int_t +ngx_tcp_variables_add_core_vars(ngx_conf_t *cf) +{ + ngx_int_t rc; + ngx_tcp_variable_t *cv, *v; + ngx_tcp_core_main_conf_t *cmcf; + + cmcf = ngx_tcp_conf_get_module_main_conf(cf, ngx_tcp_core_module); + + cmcf->variables_keys = ngx_pcalloc(cf->temp_pool, + sizeof(ngx_hash_keys_arrays_t)); + if (cmcf->variables_keys == NULL) { + return NGX_ERROR; + } + + cmcf->variables_keys->pool = cf->pool; + cmcf->variables_keys->temp_pool = cf->pool; + + if (ngx_hash_keys_array_init(cmcf->variables_keys, NGX_HASH_SMALL) + != NGX_OK) + { + return NGX_ERROR; + } + + for (cv = ngx_tcp_core_variables; cv->name.len; cv++) { + v = ngx_palloc(cf->pool, sizeof(ngx_tcp_variable_t)); + if (v == NULL) { + return NGX_ERROR; + } + + *v = *cv; + + rc = ngx_hash_add_key(cmcf->variables_keys, &v->name, v, + NGX_HASH_READONLY_KEY); + + if (rc == NGX_OK) { + continue; + } + + if (rc == NGX_BUSY) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "conflicting variable name \"%V\"", &v->name); + } + + return NGX_ERROR; + } + + return NGX_OK; +} +ngx_int_t +ngx_tcp_variables_init_vars(ngx_conf_t *cf) +{ + ngx_uint_t i, n; + ngx_hash_key_t *key; + ngx_hash_init_t hash; + ngx_tcp_variable_t *v, *av; + ngx_tcp_core_main_conf_t *cmcf; + + /* set the handlers for the indexed tcp variables */ + + cmcf = ngx_tcp_conf_get_module_main_conf(cf, ngx_tcp_core_module); + + v = cmcf->variables.elts; + key = cmcf->variables_keys->keys.elts; + + for (i = 0; i < cmcf->variables.nelts; i++) { + + for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) { + + av = key[n].value; + + if (av->get_handler + && v[i].name.len == key[n].key.len + && ngx_strncmp(v[i].name.data, key[n].key.data, v[i].name.len) + == 0) + { + v[i].get_handler = av->get_handler; + v[i].data = av->data; + + av->flags |= NGX_TCP_VAR_INDEXED; + v[i].flags = av->flags; + + av->index = i; + + goto next; + } + } + + /*if (ngx_strncmp(v[i].name.data, "tcp_", 5) == 0) { + v[i].get_handler = ngx_tcp_variable_unknown_header_in; + v[i].data = (uintptr_t) &v[i].name; + + continue; + } + + if (ngx_strncmp(v[i].name.data, "sent_tcp_", 10) == 0) { + v[i].get_handler = ngx_tcp_variable_unknown_header_out; + v[i].data = (uintptr_t) &v[i].name; + + continue; + } + + if (ngx_strncmp(v[i].name.data, "upstream_tcp_", 14) == 0) { + v[i].get_handler = ngx_tcp_upstream_header_variable; + v[i].data = (uintptr_t) &v[i].name; + v[i].flags = NGX_TCP_VAR_NOCACHEABLE; + + continue; + }*/ + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "unknown \"%V\" variable", &v[i].name); + + return NGX_ERROR; + + next: + continue; + } + + + for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) { + av = key[n].value; + + if (av->flags & NGX_TCP_VAR_NOHASH) { + key[n].key.data = NULL; + } + } + + + hash.hash = &cmcf->variables_hash; + hash.key = ngx_hash_key; + hash.max_size = cmcf->variables_hash_max_size; + hash.bucket_size = cmcf->variables_hash_bucket_size; + hash.name = "variables_hash"; + hash.pool = cf->pool; + hash.temp_pool = NULL; + + if (ngx_hash_init(&hash, cmcf->variables_keys->keys.elts, + cmcf->variables_keys->keys.nelts) + != NGX_OK) + { + return NGX_ERROR; + } + + cmcf->variables_keys = NULL; + + return NGX_OK; +} diff --git a/src/ngx_tcp_variables.h b/src/ngx_tcp_variables.h new file mode 100644 index 0000000..7a05e9d --- /dev/null +++ b/src/ngx_tcp_variables.h @@ -0,0 +1,120 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_TCP_VARIABLES_H_INCLUDED_ +#define _NGX_TCP_VARIABLES_H_INCLUDED_ + + +#include +#include +#include "ngx_tcp.h" + + +typedef ngx_variable_value_t ngx_tcp_variable_value_t; + +#define ngx_tcp_variable(v) { sizeof(v) - 1, 1, 0, 0, 0, (u_char *) v } + +typedef struct ngx_tcp_variable_s ngx_tcp_variable_t; + +typedef void (*ngx_tcp_set_variable_pt) (ngx_tcp_session_t *s, + ngx_tcp_variable_value_t *v, uintptr_t data); +typedef ngx_int_t (*ngx_tcp_get_variable_pt) (ngx_tcp_session_t *s, + ngx_tcp_variable_value_t *v, uintptr_t data); + + +#define NGX_TCP_VAR_CHANGEABLE 1 +#define NGX_TCP_VAR_NOCACHEABLE 2 +#define NGX_TCP_VAR_INDEXED 4 +#define NGX_TCP_VAR_NOHASH 8 + + +struct ngx_tcp_variable_s { + ngx_str_t name; /* must be first to build the hash */ + ngx_tcp_set_variable_pt set_handler; + ngx_tcp_get_variable_pt get_handler; + uintptr_t data; + ngx_uint_t flags; + ngx_uint_t index; +}; + + +ngx_tcp_variable_t *ngx_tcp_add_variable(ngx_conf_t *cf, ngx_str_t *name, + ngx_uint_t flags); + +ngx_int_t ngx_tcp_get_variable_index(ngx_conf_t *cf, ngx_str_t *name); + +ngx_tcp_variable_value_t *ngx_tcp_get_indexed_variable(ngx_tcp_session_t *s, + ngx_uint_t index); + +ngx_tcp_variable_value_t *ngx_tcp_get_flushed_variable(ngx_tcp_session_t *s, + ngx_uint_t index); + + +ngx_tcp_variable_value_t *ngx_tcp_get_variable(ngx_tcp_session_t *s, + ngx_str_t *name, ngx_uint_t key); + +/*ngx_int_t ngx_tcp_variable_unknown_header(ngx_tcp_variable_value_t *v, + ngx_str_t *var, ngx_list_part_t *part, size_t prefix);*/ + + +#define ngx_tcp_clear_variable(r, index) r->variables0[index].text.data = NULL; + +#if 0 +#if (NGX_PCRE) + +typedef struct { + ngx_uint_t capture; + ngx_int_t index; +} ngx_tcp_regex_variable_t; + + +typedef struct { + ngx_regex_t *regex; + ngx_uint_t ncaptures; + ngx_tcp_regex_variable_t *variables; + ngx_uint_t nvariables; + ngx_str_t name; +} ngx_tcp_regex_t; + + +typedef struct { + ngx_tcp_regex_t *regex; + void *value; +} ngx_tcp_map_regex_t; + + +ngx_tcp_regex_t *ngx_tcp_regex_compile(ngx_conf_t *cf, + ngx_regex_compile_t *rc); +ngx_int_t ngx_tcp_regex_exec(ngx_tcp_session_t *s, ngx_tcp_regex_t *re, + ngx_str_t *s); + +#endif + + +typedef struct { + ngx_hash_combined_t hash; +#if (NGX_PCRE) + ngx_tcp_map_regex_t *regex; + ngx_uint_t nregex; +#endif +} ngx_tcp_map_t; + + +void *ngx_tcp_map_find(ngx_tcp_session_t *s, ngx_tcp_map_t *map, + ngx_str_t *match); + + + + + +#endif +extern ngx_tcp_variable_value_t ngx_tcp_variable_null_value; +extern ngx_tcp_variable_value_t ngx_tcp_variable_true_value; + +ngx_int_t ngx_tcp_variables_add_core_vars(ngx_conf_t *cf); +ngx_int_t ngx_tcp_variables_init_vars(ngx_conf_t *cf); +#endif /* _NGX_HTTP_VARIABLES_H_INCLUDED_ */ diff --git a/test/lib/Test/Nginx/Util.pm b/test/lib/Test/Nginx/Util.pm index 822da2e..9776970 100644 --- a/test/lib/Test/Nginx/Util.pm +++ b/test/lib/Test/Nginx/Util.pm @@ -53,7 +53,7 @@ our $Timeout = $ENV{TEST_NGINX_TIMEOUT} || 3; our $CheckLeak = $ENV{TEST_NGINX_CHECK_LEAK} || 0; -our $ServerAddr = 'localhost'; +our $ServerAddr = '127.0.0.1'; our $StapOutFileHandle; @@ -637,7 +637,7 @@ $http_config server { listen $ServerPort; - server_name 'localhost'; + server_name '127.0.0.1'; client_max_body_size 30M; #client_body_buffer_size 4k; diff --git a/test/t/0000-req_receive.t b/test/t/0000-req_receive.t index 50290bb..180b128 100644 --- a/test/t/0000-req_receive.t +++ b/test/t/0000-req_receive.t @@ -66,3 +66,45 @@ tcp { foofoofoofoo --- raw_response: foofoofoofoo + +=== TEST 4: receive() +--- config +--- main_config +tcp { + server { + listen 1980; + process_by_lua ' + local sock = ngx.req.socket() + local re = sock:receive() + if re == nil then + ngx.print("error") + end + ngx.print(ngx.var.remote_addr) + '; + } +} +--- raw_request +foooooo +--- raw_response: 127.0.0.1 + + +=== TEST 5: receive() +--- config +--- main_config +tcp { + server { + listen 1980; + process_by_lua ' + local sock = ngx.req.socket() + local re = sock:receive() + if re == nil then + ngx.print("error") + end + sock:send(re); + '; + } +} +--- raw_request +foooooo +--- raw_response: foooooo + diff --git a/test/t/0005-req_var.t b/test/t/0005-req_var.t new file mode 100644 index 0000000..d175253 --- /dev/null +++ b/test/t/0005-req_var.t @@ -0,0 +1,29 @@ +use Test::Nginx::Socket; +repeat_each(1); +plan tests => 1 * repeat_each() * blocks(); + +run_tests(); + +__DATA__ + +=== TEST 1: receive() +--- config +--- main_config +tcp { + server { + listen 1980; + error_log /root/nginx/ngx-moomin-bundle/nginx-tcp-lua-module/test/req_var.log; + process_by_lua ' + local sock = ngx.req.socket() + local re = sock:receive() + if re == nil then + ngx.print("error") + end + ngx.print(ngx.var.remote_addr) + '; + } +} +--- raw_request +foooooo +--- raw_response: 127.0.0.1 +