static ngx_tcp_virtual_server_t * 
ngx_tcp_websocket_find_virtual_server(ngx_tcp_session_t *s, 
    ngx_tcp_websocket_ctx_t *ctx)
{
    ngx_uint_t                 hash, i;
    ngx_tcp_core_main_conf_t  *cmcf;
    ngx_tcp_virtual_server_t  *vs;

    cmcf = ngx_tcp_get_module_main_conf(s, ngx_tcp_core_module);

    if (ctx->host.len == 0) {
        return NULL;
    }

    hash = ngx_hash_key(ctx->host.data, ctx->host.len);

    vs = cmcf->virtual_servers.elts;
    for (i = 0; i < cmcf->virtual_servers.nelts; i++) {

        if (vs[i].hash != hash) {
            continue;
        }

        if ((vs[i].name.len != ctx->host.len)
                || ngx_memcmp(vs[i].name.data, ctx->host.data,
                              ctx->host.len) != 0){
            continue;
        }

        return &vs[i];
    }

    return NULL;
}
static ngx_int_t
ngx_tcp_cmd_tran_handler(ngx_tcp_ctx_t *ctx, const u_char *pkg, int pkg_len)
{
    ngx_tcp_cmd_pkghead_t       *pkghead;
    ngx_tcp_cmd_pkgtran_t       *pkgtran;
    uint32_t                     pkg_size;
    ngx_tcp_cmd_session_t       *s;
    ngx_connection_t            *c;
    ngx_tcp_cmd_session_t       *dest_s;
    ngx_tcp_core_main_conf_t    *cmcf;
    socketfd_info_t             *socketfd_info;

    s = ctx->ngx_tcp_session;
    c = s->parent.connection;
    if (ngx_rstrncmp((u_char *)"unix:", c->addr_text.data, sizeof("unix:") - 1) != 0) {
        ngx_log_error(NGX_LOG_ERR, c->log, 0, 
            "ngx_tcp_cmd_tran_handler|cli=%V\n", &c->addr_text);
        return NGX_OK;
    }
    cmcf = ngx_tcp_get_module_main_conf(((ngx_tcp_session_t *)s), 
                                            ngx_tcp_core_module);
    pkghead = (ngx_tcp_cmd_pkghead_t *)(pkg);
    pkgtran = (ngx_tcp_cmd_pkgtran_t *)(pkghead + 1);
    pkg_size = pkgtran->data_size;
    socketfd_info = cmcf->socketfd_shm->info->socketfd_info + pkgtran->dest_fd;
    dest_s = (ngx_tcp_cmd_session_t *)(socketfd_info->tag);
    if (dest_s == NULL) {
        ngx_log_error(NGX_LOG_ERR, s->parent.connection->log, 0, 
            "ngx_tcp_cmd_tran_handler|dest_s=NULL|dest_fd=%d|dest_pid=%d\n",
                pkgtran->dest_fd, pkgtran->dest_pid);
        return NGX_OK;
    }
    if (dest_s->parent.connection == NULL || pkgtran->dest_fd <= 2) {
        ngx_log_error(NGX_LOG_ERR, s->parent.connection->log, 0, 
            "ngx_tcp_cmd_tran_handler|dest_c=%p|dest_fd=%d\n",
                dest_s->parent.connection, pkgtran->dest_fd);
        return NGX_OK;
    }
    if (ngx_pid != pkgtran->dest_pid 
      || ngx_process_slot != socketfd_info->listening_unix_info_i
      || pkgtran->dest_fd != dest_s->parent.connection->fd) {
        ngx_log_error(NGX_LOG_ERR, s->parent.connection->log, 0, 
            "ngx_tcp_cmd_tran_handler|conn fd=%d|dest_fd=%d"
            "|ngx_process_slot=%d|listening_unix_info_i=%d"
            "|dest_pid=%d|ngx_pid=%d\n", 
                 dest_s->parent.connection->fd, pkgtran->dest_fd, 
                 ngx_process_slot, socketfd_info->listening_unix_info_i,
                 pkgtran->dest_pid, ngx_pid);
        return NGX_OK;
    }
    
    ngx_tcp_send_data(&dest_s->parent.tcp_ctx, pkgtran->data, pkg_size);

    return NGX_OK;
}
void 
ngx_tcp_lua_req_resume(ngx_tcp_session_t *s) 
{
    int                                  nret = 0;
    ngx_int_t                            rc;
    ngx_connection_t                    *c;
    ngx_tcp_lua_ctx_t                   *ctx;
    ngx_tcp_lua_main_conf_t             *lmcf;

    c = s->connection;

    ctx = s->ctx;

    if (ctx->prepare_retvals) {
    
        nret = ctx->prepare_retvals(s,ctx->co);
        ctx->prepare_retvals = NULL;
        
    }
    
    lmcf = ngx_tcp_get_module_main_conf(s, ngx_tcp_lua_module);

    dd("about to run thread for %p ", s);

    rc = ngx_tcp_lua_run_thread(lmcf->lua, s, ctx, nret);

    ngx_log_debug1(NGX_LOG_DEBUG_TCP, c->log, 0,
            "lua run thread returned %d", rc);

    if (rc == NGX_ERROR || rc == NGX_OK) {
        ngx_tcp_lua_close_session(s);
        return;
    }
    
    //NGX_AGAIN
    return;
}
static ngx_int_t 
ngx_tcp_process_init(ngx_cycle_t *cycle)
{
    ngx_int_t                   rc;
    ngx_tcp_core_main_conf_t   *cmcf;
    ngx_tcp_conf_ctx_t         *ctx;
    ngx_listening_t            *ls;
    ngx_connection_t           *c;
    ngx_event_t                *rev;
    ngx_tcp_port_t             *mport;
    ngx_tcp_in_addr_t          *addrs;

    rc = ngx_tcp_instruct_unix_listen(cycle);
    if (NGX_OK != rc) {
        return rc;
    }
    ctx = (ngx_tcp_conf_ctx_t *)ngx_get_conf(cycle->conf_ctx, ngx_tcp_module);
    cmcf = ngx_tcp_get_module_main_conf(ctx, ngx_tcp_core_module);
    ls = cmcf->ls;
    rc = ngx_tcp_open_listening_socket(ls);
    if (NGX_OK != rc) {
        return rc;
    }
    mport = ngx_palloc(cycle->pool, sizeof(ngx_tcp_port_t));
    if (mport == NULL) {
        return NGX_ERROR;
    }
    mport->naddrs = 1;
    mport->addrs = ngx_pcalloc(cycle->pool,
                               mport->naddrs * sizeof(ngx_tcp_in_addr_t));
    if (mport->addrs == NULL) {
        return NGX_ERROR;
    }
    addrs = mport->addrs;
    addrs->conf.ctx = ctx;
    addrs->conf.addr_text= cmcf->unix_url;
    ls->servers = mport;

    if (cmcf->error_log == NULL) {
        ls->logp = cycle->log;
    } else {
        ls->logp = cmcf->error_log;
    }
    ls->log = *(ls->logp);
    ls->log.data = &ls->addr_text;
    ls->log.handler = ngx_accept_log_error;

    c = ngx_get_connection(ls->fd, cycle->log);
    // c->log = &ls->log;
    c->log = ls->logp;
    c->listening = ls;
    ls->connection = c;

    rev = c->read;
    rev->log = c->log;
    rev->accept = 1;

#if (NGX_HAVE_DEFERRED_ACCEPT)
    rev->deferred_accept = ls->deferred_accept;
#endif

    rev->handler = ngx_event_accept;

    if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
        if (ngx_add_conn(c) == NGX_ERROR) {
            return NGX_ERROR;
        }

    } else {
        if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
            return NGX_ERROR;
        }
    }

    return NGX_OK;
}
static ngx_int_t
ngx_tcp_instruct_unix_listen(ngx_cycle_t *cycle)
{
    ngx_tcp_core_main_conf_t   *cmcf;
    ngx_tcp_conf_ctx_t         *ctx;

    unix_listening_info_t      *unix_info;
    size_t                      len;
    ngx_listening_t            *ls;
    ngx_url_t                   u;
    struct sockaddr            *sa;
    u_char                      text[NGX_SOCKADDR_STRLEN + 1];

    ctx = (ngx_tcp_conf_ctx_t *)ngx_get_conf(cycle->conf_ctx, ngx_tcp_module);
    cmcf = ngx_tcp_get_module_main_conf(ctx, ngx_tcp_core_module);

    ngx_memzero(&u, sizeof(ngx_url_t));
    ngx_conf_full_name(cycle, &cmcf->unix_url, 0);
    /* ngx_pid to string len is less than 21 - strlen("unix:") */
    u.url.data = ngx_pcalloc(cycle->pool, cmcf->unix_url.len + 21);
    ngx_sprintf(u.url.data, "%s%V%d", "unix:", &cmcf->unix_url, ngx_pid);
    u.url.len = ngx_strlen(u.url.data);
    u.listen = 1;
    cmcf->unix_url = u.url;

    ngx_log_error(NGX_LOG_INFO, cycle->log, 0, 
            "ngx_tcp_instruct_unix_listen|unix_url=%V,len=%d", 
                  &u.url, u.url.len);

    if (ngx_parse_url(cycle->pool, &u) != NGX_OK) {
        if (u.err) {
            ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
                               "%s in \"%V\" of the \"listen\" directive",
                               u.err, &u.url);
        }

        return NGX_ERROR;
    }

    unix_info = cmcf->socketfd_shm->info->listening_unix_info + ngx_process_slot;
    ngx_memzero(unix_info, sizeof(unix_listening_info_t));
    ngx_memcpy(unix_info, u.url.data, u.url.len);

    ls = ngx_pcalloc(cycle->pool, sizeof(ngx_listening_t));
    if (ls == NULL) {
        goto failed;
    }
    sa = ngx_pcalloc(cycle->pool, u.socklen);
    if (sa == NULL) {
        goto failed;
    }

    ngx_memcpy(sa, u.sockaddr, u.socklen);

    ls->sockaddr = sa;
    ls->socklen = u.socklen;

    len = ngx_sock_ntop(sa, text, NGX_SOCKADDR_STRLEN, 1);
    ls->addr_text.len = len;

    ls->addr_text_max_len = NGX_UNIX_ADDRSTRLEN;
    len++;
    ls->addr_text.data = ngx_pcalloc(cycle->pool, len);
    if (ls->addr_text.data == NULL) {
        goto failed;
    }
    ngx_memcpy(ls->addr_text.data, text, len);

    ls->fd = (ngx_socket_t) -1;
    ls->type = SOCK_STREAM;

    ls->backlog = NGX_LISTEN_BACKLOG;
    ls->rcvbuf = -1;
    ls->sndbuf = -1;

#if (NGX_HAVE_SETFIB)
    ls->setfib = -1;
#endif

    ls->addr_ntop = 1;
    ls->handler = ngx_tcp_init_connection;
    ls->pool_size = 8192;

    ls->logp = &cycle->new_log;
    ls->log.data = &ls->addr_text;
    ls->log.handler = ngx_accept_log_error;

    cmcf->ls = ls;

    return NGX_OK;

failed:
    return NGX_ERROR;
}
void
ngx_tcp_upstream_init(ngx_tcp_session_t *s)
{
    ngx_str_t                      *host;
    ngx_uint_t                      i;
    ngx_connection_t               *c;
    ngx_tcp_cleanup_t              *cln;
    ngx_resolver_ctx_t             *ctx, temp;
    ngx_tcp_upstream_t             *u;
    ngx_tcp_core_srv_conf_t        *cscf;
    ngx_tcp_upstream_srv_conf_t    *uscf, **uscfp;
    ngx_tcp_upstream_main_conf_t   *umcf;

    c = s->connection;

    cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module);

    ngx_log_debug1(NGX_LOG_DEBUG_TCP, c->log, 0,
            "tcp init upstream, client timer: %d", c->read->timer_set);

    if (c->read->timer_set) {
        ngx_del_timer(c->read);
    }

    u = s->upstream;

    cln = ngx_tcp_cleanup_add(s, 0);

    cln->handler = ngx_tcp_upstream_cleanup;
    cln->data = s;
    u->cleanup = &cln->handler;

    if (u->resolved == NULL) {

        uscf = u->conf->upstream;

    } else {

        /*TODO: support variable in the proxy_pass*/
        if (u->resolved->sockaddr) {

            if (ngx_tcp_upstream_create_round_robin_peer(s, u->resolved)
                    != NGX_OK)
            {
                ngx_tcp_finalize_session(s);
                return;
            }

            ngx_tcp_upstream_connect(s, u);

            return;
        }

        host = &u->resolved->host;

        umcf = ngx_tcp_get_module_main_conf(s, ngx_tcp_upstream_module);

        uscfp = umcf->upstreams.elts;

        for (i = 0; i < umcf->upstreams.nelts; i++) {

            uscf = uscfp[i];

            if (uscf->host.len == host->len
                    && ((uscf->port == 0 && u->resolved->no_port)
                        || uscf->port == u->resolved->port)
                    && ngx_memcmp(uscf->host.data, host->data, host->len) == 0)
            {
                goto found;
            }
        }

        temp.name = *host;

        ctx = ngx_resolve_start(cscf->resolver, &temp);
        if (ctx == NULL) {
            ngx_tcp_finalize_session(s);
            return;
        }

        if (ctx == NGX_NO_RESOLVER) {
            ngx_log_error(NGX_LOG_ERR, c->log, 0,
                    "no resolver defined to resolve %V", host);
            ngx_tcp_finalize_session(s);
            return;
        }

        ctx->name = *host;
        ctx->type = NGX_RESOLVE_A;
        ctx->handler = ngx_tcp_upstream_resolve_handler;
        ctx->data = s;
        ctx->timeout = cscf->resolver_timeout;

        u->resolved->ctx = ctx;

        if (ngx_resolve_name(ctx) != NGX_OK) {
            u->resolved->ctx = NULL;
            ngx_tcp_finalize_session(s);
            return;
        }

        return;
    }

found:

    if (uscf->peer.init(s, uscf) != NGX_OK) {
        ngx_tcp_finalize_session(s);
        return;
    }

    ngx_tcp_upstream_connect(s, u);
}
static void 
ngx_tcp_lua_init_session(ngx_tcp_session_t *s) 
{
    ngx_connection_t            *c;
    ngx_tcp_lua_srv_conf_t          *lscf;
    ngx_tcp_lua_main_conf_t     *lmcf;
    
    lua_State                       *L;
    ngx_int_t                        rc;
    u_char                          *script_path;
    char                            *err;

    c = s->connection;


    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;

    if (lscf->lua_src_inline) {
        /*  load Lua inline script (w/ cache) sp = 1 */
        rc = ngx_tcp_lua_cache_loadbuffer(L, lscf->lua_src.data,
                lscf->lua_src.len, lscf->lua_src_key,
                "process_by_lua", &err, lscf->enable_code_cache ? 1 : 0); 
        
        if (rc != NGX_OK) {
            if (err == NULL) {
                err = "unknown error";
            }
        
            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 {
        /*  load Lua script file (w/ cache)        sp = 1 */
        script_path = ngx_tcp_lua_rebase_path(s->pool, lscf->lua_src.data,
                lscf->lua_src.len);
        
        if (script_path == NULL) {
            ngx_tcp_finalize_session(s);
            return;
        }
        
        rc = ngx_tcp_lua_cache_loadfile(L, script_path, lscf->lua_src_key,
                &err, lscf->enable_code_cache ? 1 : 0);

        if (rc != NGX_OK) {
            if (err == NULL) {
                err = "unknown error";
            }

            ngx_log_error(NGX_LOG_ERR, c->log, 0,
                          "failed to load Lua code: %s", err);

            ngx_tcp_finalize_session(s);
            return;
        }
    }
    
    /*  make sure we have a valid code chunk */
    assert(lua_isfunction(L, -1));

    s->write_event_handler= ngx_tcp_lua_dummy_write_handler;
    s->read_event_handler= ngx_tcp_lua_dummy_read_handler;
    
    rc = ngx_tcp_lua_process_by_chunk(L, s);
    
    if (rc == NGX_DONE || rc == NGX_OK || rc == NGX_ERROR) {
        ngx_tcp_finalize_session(s);
        return;
    }
}
/**
 * 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);
}
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;
}
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;
}