static void 
ngx_tcp_proxy_init_session(ngx_tcp_session_t *s) 
{
    ngx_connection_t         *c;
    ngx_tcp_proxy_conf_t     *pcf;
    ngx_tcp_core_srv_conf_t  *cscf;

    c = s->connection;

    ngx_log_debug0(NGX_LOG_DEBUG_TCP, c->log, 0, "tcp proxy init session");

    cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module);

    pcf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_proxy_module);

    s->buffer = ngx_create_temp_buf(s->connection->pool, pcf->buffer_size);
    if (s->buffer == NULL) {
        ngx_tcp_finalize_session(s);
        return;
    }

    s->out.len = 0;

    c->write->handler = ngx_tcp_proxy_dummy_write_handler;
    c->read->handler = ngx_tcp_proxy_dummy_read_handler;

    ngx_add_timer(c->read, cscf->timeout);

    ngx_tcp_proxy_init_upstream(c, s);

    return;
}
static void 
ngx_tcp_websocket_init_session(ngx_tcp_session_t *s) 
{
    ngx_connection_t             *c;
    ngx_tcp_websocket_ctx_t      *wctx;
    ngx_tcp_core_srv_conf_t      *cscf;
    ngx_tcp_websocket_conf_t     *wcf;

    c = s->connection;

    ngx_log_debug0(NGX_LOG_DEBUG_TCP, c->log, 0, "tcp websocket init session");

    cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module);

    wcf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_websocket_module);

    wctx = ngx_pcalloc(s->connection->pool, sizeof(ngx_tcp_websocket_ctx_t));
    if (wctx == NULL) {
        ngx_tcp_finalize_session(s);
        return;
    }
    ngx_tcp_set_ctx(s, wctx, ngx_tcp_websocket_module);

    s->out.len = 0;

    s->buffer = ngx_create_temp_buf(s->connection->pool, wcf->buffer_size);
    if (s->buffer == NULL) {
        ngx_tcp_finalize_session(s);
        return;
    }

    c->write->handler = ngx_tcp_websocket_dummy_write_handler;
    c->read->handler = ngx_tcp_websocket_init_protocol;

    ngx_add_timer(c->read, cscf->timeout);

    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
        ngx_tcp_finalize_session(s);
    }

#if (NGX_TCP_SSL)

    /*
     * The ssl connection with client may not trigger the read event again,
     * So I trigger it in this function.
     * */
    if (c->ssl) {
        ngx_tcp_websocket_init_protocol(c->read);
        return;
    }

#endif

    if (c->read->ready) {
        ngx_tcp_websocket_init_protocol(c->read);
    }

    return;
}
static void
ngx_tcp_ssl_handshake_handler(ngx_connection_t *c)
{
    ngx_tcp_session_t        *s;
    ngx_tcp_core_srv_conf_t  *cscf;

    if (c->ssl->handshaked) {

        s = c->data;

        if (s->starttls) {
            cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module);

            c->read->handler = cscf->protocol->init_protocol;
            c->write->handler = ngx_tcp_send;

            cscf->protocol->init_protocol(c->read);

            return;
        }

        c->read->ready = 0;

        ngx_tcp_init_session(c);
        return;
    }

    ngx_tcp_close_connection(c);
}
static void
ngx_tcp_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c)
{
    ngx_tcp_session_t        *s;
    ngx_tcp_core_srv_conf_t  *cscf;

    if (ngx_ssl_create_connection(ssl, c, NGX_SSL_BUFFER) == NGX_ERROR) {
        ngx_tcp_close_connection(c);
        return;
    }

    if (ngx_ssl_handshake(c) == NGX_AGAIN) {

        s = c->data;

        cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module);

        ngx_add_timer(c->read, cscf->timeout);

        c->ssl->handler = ngx_tcp_ssl_handshake_handler;

        return;
    }

    ngx_tcp_ssl_handshake_handler(c);
}
static void 
ngx_tcp_set_session_socket(ngx_tcp_session_t *s) 
{
    int                       keepalive;
    int                       tcp_nodelay;
    ngx_tcp_core_srv_conf_t  *cscf;

    cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module);

    if (cscf->so_keepalive) {
        keepalive = 1;

        if (setsockopt(s->connection->fd, SOL_SOCKET, SO_KEEPALIVE,
                       (const void *) &keepalive, sizeof(int)) == -1)
        {
            ngx_log_error(NGX_LOG_ALERT, s->connection->log, ngx_socket_errno,
                          "setsockopt(SO_KEEPALIVE) failed");
        }
    }

    if (cscf->tcp_nodelay) {
        tcp_nodelay = 1;
        if (setsockopt(s->connection->fd, IPPROTO_TCP, TCP_NODELAY,
                       (const void *) &tcp_nodelay, sizeof(int))
            == -1)
        {
            ngx_log_error(NGX_LOG_ALERT, s->connection->log, ngx_socket_errno,
                          "setsockopt(TCP_NODELAY) failed");
        }

        s->connection->tcp_nodelay = NGX_TCP_NODELAY_SET;
    }
}
void
ngx_tcp_lua_check_client_abort_handler(ngx_tcp_session_t *s)
{
    ngx_connection_t       *c;
    ngx_tcp_lua_ctx_t      *ctx;
    ngx_tcp_lua_srv_conf_t *lscf;
    
    c = s->connection;
    ctx = s->ctx;
    lscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_lua_module);

    if (!lscf->check_client_abort) {
        return;
    }

    ngx_log_debug0(NGX_LOG_DEBUG_TCP, c->log, 0,
                   "lua socket check client abort handler");

    if (ctx->socket_invalid) {
        return;
    }
    
    ngx_tcp_test_reading(s);
    
    if (c->error) {
        ctx->ft_type |= NGX_TCP_LUA_REQ_FT_ERROR;
        ctx->socket_errno = ngx_socket_errno;
        ctx->socket_invalid = 1;
        
        ngx_tcp_lua_close_session(s);
    }
}
static void
ngx_tcp_process_session(ngx_event_t *rev)
{
    ngx_connection_t          *c;
    ngx_tcp_session_t         *s;
    ngx_tcp_core_srv_conf_t   *cscf;

    c = rev->data;
    s = c->data;

    cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module);

    /* process the ACL */
    if (ngx_tcp_access_handler(s) == NGX_ERROR) {
        ngx_tcp_finalize_session(s);
        return;
    }

    if (cscf->protocol == NULL || cscf->protocol->init_session == NULL) {
        dd("protocol not initialized");
        ngx_tcp_finalize_session(s);
        return;
    }

    c->read->handler= ngx_tcp_session_handler;
    c->write->handler= ngx_tcp_session_handler;
    s->read_event_handler = cscf->protocol->init_session;
    
    rev->handler(rev);
}
void
ngx_tcp_send(ngx_event_t *wev)
{
    ngx_int_t                  n;
    ngx_connection_t          *c;
    ngx_tcp_session_t         *s;
    ngx_tcp_core_srv_conf_t   *cscf;

    c = wev->data;
    s = c->data;

    if (wev->timedout) {
        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
        c->timedout = 1;
        ngx_tcp_close_connection(c);
        return;
    }

    if (s->out.len == 0) {
        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
            ngx_tcp_close_connection(c);
        }

        return;
    }

    n = c->send(c, s->out.data, s->out.len);
    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, wev->log, 0, "nginx tcp send:%d", n);

    if (n > 0) {
        s->out.len -= n;

        if (wev->timer_set) {
            ngx_del_timer(wev);
        }

        if (s->quit) {
            ngx_tcp_close_connection(c);
            return;
        }

        return;
    }

    if (n == NGX_ERROR) {
        ngx_tcp_close_connection(c);
        return;
    }

    /* n == NGX_AGAIN */

    cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module);

    ngx_add_timer(c->write, cscf->timeout);

    if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
        ngx_tcp_close_connection(c);
        return;
    }
}
void
ngx_tcp_send(ngx_event_t *wev)
{
    ngx_int_t                  rc;
    ngx_connection_t          *c;
    ngx_tcp_session_t         *s;
    ngx_tcp_core_srv_conf_t   *cscf;

    c = wev->data;
    s = c->data;

    if (wev->timedout) {
        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "%s|%d|%s|client timed out",__FILE__, __LINE__, __FUNCTION__);
        c->timedout = 1;
        ngx_tcp_close_connection(c);
        return;
    }

    if (s->output_buffer_chain == NULL) {
        return;
    }

   // rc = s->output_ctx->output_filter(s->output_ctx->filter_ctx, 
                                      //s->output_buffer_chain);
    rc = s->output_ctx->output_filter(s);
    ngx_chain_update_chains(s->output_ctx->pool, 
                            &s->output_ctx->free, &s->output_ctx->busy, 
                            &s->output_buffer_chain, s->output_ctx->tag);
    s->output_buffer_chain = NULL;

    if (rc == NGX_OK || rc == NGX_DONE) {
        if (wev->timer_set) {
            ngx_del_timer(wev);
        }
        return;
    }

    if (rc == NGX_ERROR) {
        ngx_log_error(NGX_LOG_ERR, c->log, 0, 
            "ngx_tcp_send|client=%V\n", &c->addr_text);
        ngx_tcp_close_connection(c);
        return;
    }

    /* rc == NGX_AGAIN */
    /*
    ngx_log_error(NGX_LOG_INFO, c->log, 0, 
                  "ngx_tcp_send|NGX_AGAIN|client=%V\n", &c->addr_text);
     */

    cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module);

    ngx_add_timer(c->write, cscf->timeout);

    if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
        ngx_tcp_close_connection(c);
        return;
    }
}
void
ngx_tcp_session_internal_server_error(ngx_tcp_session_t *s)
{
    ngx_tcp_core_srv_conf_t  *cscf;

    cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module);

    s->out = cscf->protocol->internal_server_error;
    s->quit = 1;

    ngx_tcp_send(s->connection->write);
}
static  void
ngx_tcp_proxy_init_upstream(ngx_connection_t *c, ngx_tcp_session_t *s)
{
    ngx_tcp_upstream_t       *u;
    ngx_tcp_proxy_ctx_t      *p;
    ngx_tcp_proxy_conf_t     *pcf;

    s->connection->log->action = "ngx_tcp_proxy_init_upstream";

    pcf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_proxy_module);
    if (pcf->upstream.upstream == NULL) {
        ngx_tcp_finalize_session(s);
        return;
    }

    p = ngx_pcalloc(s->connection->pool, sizeof(ngx_tcp_proxy_ctx_t));
    if (p == NULL) {
        ngx_tcp_finalize_session(s);
        return;
    }

    ngx_tcp_set_ctx(s, p, ngx_tcp_proxy_module);

    if (ngx_tcp_upstream_create(s) != NGX_OK) {
        ngx_tcp_finalize_session(s);
        return;
    }

    u = s->upstream;

    u->conf = &pcf->upstream;

    u->write_event_handler = ngx_tcp_upstream_init_proxy_handler;
    u->read_event_handler = ngx_tcp_upstream_init_proxy_handler;

    p->upstream = &u->peer;

    p->buffer = ngx_create_temp_buf(s->connection->pool, pcf->buffer_size);
    if (p->buffer == NULL) {
        ngx_tcp_finalize_session(s);
        return;
    }

    ngx_tcp_upstream_init(s);

    return;
}
void
ngx_tcp_starttls_handler(ngx_event_t *rev)
{
    ngx_connection_t    *c;
    ngx_tcp_session_t   *s;
    ngx_tcp_ssl_conf_t  *sslcf;

    c = rev->data;
    s = c->data;
    s->starttls = 1;

    c->log->action = "in starttls state";

    sslcf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_ssl_module);

    ngx_tcp_ssl_init_connection(&sslcf->ssl, c);
}
ngx_int_t
ngx_tcp_starttls_only(ngx_tcp_session_t *s, ngx_connection_t *c)
{
    ngx_tcp_ssl_conf_t  *sslcf;

    if (c->ssl) {
        return 0;
    }

    sslcf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_ssl_module);

    if (sslcf->starttls == NGX_TCP_STARTTLS_ONLY) {
        return 1;
    }

    return 0;
}
void
ngx_tcp_close_connection(ngx_connection_t *c)
{
    ngx_pool_t           *pool;
    ngx_tcp_session_t    *s;

    ngx_log_error(NGX_LOG_NOTICE, c->log, 0, 
        "ngx_tcp_close_connection|client=%V|fd=%d\n", 
            &c->addr_text, c->fd);

    s = c->data;
    if (s != NULL) {
        ngx_tcp_core_srv_conf_t  *cscf;
    
        cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module);
        if (cscf->protocol != NULL && cscf->protocol->finit_session != NULL) {
            cscf->protocol->finit_session(s);
        }
    }

#if (NGX_TCP_SSL)

    if (c->ssl) {
        if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
            c->ssl->handler = ngx_tcp_close_connection;
            return;
        }
    }

#endif

#if (NGX_STAT_STUB)
    (void) ngx_atomic_fetch_add(ngx_stat_active, -1);
#endif

    c->destroyed = 1;

    pool = c->pool;

    ngx_close_connection(c);

    ngx_destroy_pool(pool);
}
static void
ngx_tcp_init_session(ngx_connection_t *c)
{
    ngx_tcp_session_t        *s;
    ngx_tcp_core_srv_conf_t  *cscf;

    s = c->data;

    cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module);

    s->protocol = cscf->protocol->type;

    s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_tcp_max_module);
    if (s->ctx == NULL) {
        ngx_tcp_session_internal_server_error(s);
        return;
    }

    c->write->handler = ngx_tcp_send;

    cscf->protocol->init_session(s, c);
}
static ngx_tcp_path_upstream_t *
ngx_tcp_websocket_find_path_upstream(ngx_tcp_session_t *s,
    ngx_tcp_websocket_ctx_t *ctx)
{
    ngx_uint_t                    i;
    ngx_tcp_path_upstream_t      *pu;
    ngx_tcp_websocket_conf_t     *wcf;

    wcf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_websocket_module);

    pu = wcf->path_upstreams.elts;
    for (i = 0; i < wcf->path_upstreams.nelts; i++) {
        if ((pu[i].path.len != ctx->path.len)
                || ngx_memcmp(pu[i].path.data, ctx->path.data,
                              ctx->path.len) != 0) {
            continue;
        }

        return &pu[i];
    }

    return NULL;
}
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;
    }
}
ngx_int_t
ngx_tcp_log_handler(ngx_tcp_session_t *s)
{
    u_char                   *line, *p;
    size_t                    len;
    ngx_uint_t                l;
    ngx_connection_t         *c;
    ngx_tcp_log_t            *log;
    ngx_open_file_t          *file;
 #if (nginx_version) >= 1003010
    ngx_tcp_log_buf_t        *buffer;
#endif
   ngx_tcp_log_srv_conf_t    *lscf;
    ngx_tcp_core_srv_conf_t  *cscf;

    ngx_log_debug0(NGX_LOG_DEBUG_TCP, s->connection->log, 0,
                   "tcp access log handler");

    cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module);
    lscf = cscf->access_log;

    if (lscf->off) {
        return NGX_OK;
    }

    c = s->connection;
    log = lscf->logs->elts;
    for (l = 0; l < lscf->logs->nelts; l++) {

        if (ngx_time() == log[l].disk_full_time) {

            /*
             * on FreeBSD writing to a full filesystem with enabled softupdates
             * may block process for much longer time than writing to non-full
             * filesystem, so we skip writing to a log for one second
             */

            continue;
        }

        len = 0;

        /* Calculate the length */
        len += sizeof("1970/09/28 12:00:00");   /* log time */
        len += NGX_INT64_LEN + 2;               /* [ngx_pid] */
        len += c->addr_text.len + 1;            /* client address */
        len += s->addr_text->len + 1;           /* this session address */
        len += sizeof("1970/09/28 12:00:00");   /* accept time */
        len += sizeof("255.255.255.255:65536"); /* upstream address */
        len += NGX_OFF_T_LEN + 1;               /* read bytes from client */
        len += NGX_OFF_T_LEN + 1;               /* write bytes to client */
        len += NGX_LINEFEED_SIZE;

        file = log[l].file;

#if (nginx_version) >= 1003010
        if (file && file->data) {

            buffer = file->data;

            if (len > (size_t) (buffer->last - buffer->pos)) {

                ngx_tcp_log_write(s, &log[l], buffer->start,
                                  buffer->pos - buffer->start);

                buffer->pos = buffer->start;
            }

            if (len <= (size_t) (buffer->last - buffer->pos)) {

                p = buffer->pos;

                p = ngx_tcp_log_fill(s, p);

                buffer->pos = p;

                continue;
            }
        }
#else
        if (file && file->buffer) {

            if (len > (size_t) (file->last - file->pos)) {

                ngx_tcp_log_write(s, &log[l], file->buffer,
                                  file->pos - file->buffer);

                file->pos = file->buffer;
            }

            if (len <= (size_t) (file->last - file->pos)) {

                p = file->pos;

                p = ngx_tcp_log_fill(s, p);

                file->pos = p;

                continue;
            }
        }
#endif

        line = ngx_pnalloc(s->pool, len);
        if (line == NULL) {
            return NGX_ERROR;
        }

        p = line;

        p = ngx_tcp_log_fill(s, p);

        ngx_tcp_log_write(s, &log[l], line, p - line);
    }

    return NGX_OK;
}
static void
ngx_tcp_upstream_connect(ngx_tcp_session_t *s, ngx_tcp_upstream_t *u) 
{
    int                       tcp_nodelay;
    ngx_int_t                 rc;
    ngx_connection_t         *c;
    ngx_tcp_core_srv_conf_t  *cscf;

    s->connection->log->action = "connecting to upstream";

    cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module);

    rc = ngx_event_connect_peer(&u->peer);

    ngx_log_debug1(NGX_LOG_DEBUG_TCP, s->connection->log, 0, "tcp upstream connect: %d", rc);

    if (rc != NGX_OK && rc != NGX_AGAIN) {

        ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, 
                "upstream servers error or busy!");

        ngx_tcp_upstream_finalize_session(s, u, 0);

        return;
    }

    /* rc == NGX_OK or rc == NGX_AGAIN */

    if (u->peer.check_index != NGX_INVALID_INDEX) {
        ngx_tcp_check_get_peer(u->peer.check_index);
    }

    c = u->peer.connection;

    c->data = s;
    c->pool = s->connection->pool;
    c->log = s->connection->log;
    c->read->log = c->log;
    c->write->log = c->log;

    c->write->handler = ngx_tcp_upstream_handler;
    c->read->handler = ngx_tcp_upstream_handler;

    if (cscf->tcp_nodelay) {
        tcp_nodelay = 1;

        if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,
                    (const void *) &tcp_nodelay, sizeof(int)) == -1)
        {
            ngx_connection_error(c, ngx_socket_errno,
                    "setsockopt(TCP_NODELAY) failed");
            ngx_tcp_upstream_finalize_session(s, u, 0);
            return;
        }

        c->tcp_nodelay = NGX_TCP_NODELAY_SET;
    }

    if (rc == NGX_AGAIN) {
        /*connect busy*/
        ngx_add_timer(c->write, u->conf->connect_timeout);
        return;
    }
    else {
        ngx_add_timer(c->read, u->conf->read_timeout);
        ngx_add_timer(c->write, u->conf->send_timeout);

        c->write->handler(c->write);
    }
}
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_init_session(ngx_event_t *rev)
{
    ngx_time_t                *tp;
    ngx_uint_t                 i;
    ngx_connection_t          *c;
    struct sockaddr_in        *sin;
    ngx_tcp_port_t            *port;
    ngx_tcp_in_addr_t         *addr;
    ngx_tcp_log_ctx_t         *ctx;
    ngx_tcp_addr_conf_t       *addr_conf;
    ngx_tcp_session_t         *s;
#if (NGX_HAVE_INET6)
    struct sockaddr_in6        *sin6;
    ngx_tcp_in6_addr_t        *addr6;
#endif
    ngx_tcp_core_srv_conf_t  *cscf;

/*
#if (NGX_STAT_STUB)
    (void) ngx_atomic_fetch_add(ngx_stat_reading, -1);
#endif
*/
    c = rev->data;
    s = c->data;
    if (s == NULL) {
        s = ngx_pcalloc(c->pool, sizeof(ngx_tcp_session_t));
        if (s == NULL) {
            ngx_tcp_close_connection(c);
            return;
        }
        c->data = s;
    }

    ctx = c->log->data;
    ctx->session= s;
    ctx->client= &c->addr_text;

    c->requests++;

    s->connection = c;
    s->signature = NGX_TCP_MODULE;

    /* find the server configuration for the address:port */

    port = c->listening->servers;

    if (port->naddrs > 1) {

        /*
         * there are several addresses on this port and one of them
         * is an "*:port" wildcard so getsockname() in ngx_tcp_server_addr()
         * is required to determine a server address
         */

        if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
            ngx_tcp_close_connection(c);
            return;
        }

        switch (c->local_sockaddr->sa_family) {

#if (NGX_HAVE_INET6)
        case AF_INET6:
            sin6 = (struct sockaddr_in6 *) c->local_sockaddr;

            addr6 = port->addrs;

            /* the last address is "*" */

            for (i = 0; i < port->naddrs - 1; i++) {
                if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {
                    break;
                }
            }

            addr_conf = &addr6[i].conf;

            break;
#endif

        default: /* AF_INET */
            sin = (struct sockaddr_in *) c->local_sockaddr;

            addr = port->addrs;

            /* the last address is "*" */

            for (i = 0; i < port->naddrs - 1; i++) {
                if (addr[i].addr == sin->sin_addr.s_addr) {
                    break;
                }
            }

            addr_conf = &addr[i].conf;

            break;
        }

    } else {

        switch (c->local_sockaddr->sa_family) {

#if (NGX_HAVE_INET6)
        case AF_INET6:
            addr6 = port->addrs;
            addr_conf = &addr6[0].conf;
            break;
#endif

        default: /* AF_INET */
            addr = port->addrs;
            addr_conf = &addr[0].conf;
            break;
        }
    }

    if (addr_conf->default_ctx) {
        s->main_conf = addr_conf->default_ctx->main_conf;
        s->srv_conf = addr_conf->default_ctx->srv_conf;
    }
    else {
        s->main_conf = addr_conf->ctx->main_conf;
        s->srv_conf = addr_conf->ctx->srv_conf;
    }
    
    s->addr_text = &addr_conf->addr_text;

    if (rev->timedout) {
        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");

        ngx_tcp_close_connection(c);
        return;
    }

    rev->handler = ngx_tcp_process_session;
    //s->read_event_handler = ngx_tcp_block_reading;
/*
#if (NGX_HTTP_SSL)

    {
    ngx_tcp_ssl_srv_conf_t  *sscf;

    sscf = ngx_tcp_get_module_srv_conf(r, ngx_tcp_ssl_module);
    if (sscf->enable || addr_conf->ssl) {

        if (c->ssl == NULL) {

            c->log->action = "SSL handshaking";

            if (addr_conf->ssl && sscf->ssl.ctx == NULL) {
                ngx_log_error(NGX_LOG_ERR, c->log, 0,
                              "no \"ssl_certificate\" is defined "
                              "in server listening on SSL port");
                ngx_tcp_close_connection(c);
                return;
            }

            if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER)
                != NGX_OK)
            {
                ngx_tcp_close_connection(c);
                return;
            }

            rev->handler = ngx_tcp_ssl_handshake;
        }

        r->main_filter_need_in_memory = 1;
    }
    }

#endif
*/

    s->pool = c->pool;

    cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module);
    if (cscf == NULL) {
        ngx_tcp_finalize_session(s);
        return;
    }

    s->ctx = ngx_pcalloc(s->pool, sizeof(void *) * ngx_tcp_max_module);
    if (s->ctx == NULL) {
        ngx_tcp_finalize_session(s);
        return;
    }

    tp = ngx_timeofday();
    s->start_sec = tp->sec;
    s->start_msec = tp->msec;

    s->bytes_read = 0;
    s->bytes_write = 0;

    ngx_tcp_set_session_socket(s);
/*
#if (NGX_STAT_STUB)
    (void) ngx_atomic_fetch_add(ngx_stat_reading, 1);
    r->stat_reading = 1;
    (void) ngx_atomic_fetch_add(ngx_stat_requests, 1);
#endif
*/
    rev->handler(rev);

}
static ngx_int_t
ngx_tcp_lua_req_read(ngx_tcp_session_t *s, ngx_tcp_lua_ctx_t *ctx)
{
    ngx_connection_t            *c;
    ngx_tcp_core_srv_conf_t     *cscf;
    ngx_buf_t                   *b;
    ngx_event_t                 *rev;
    size_t                       size;
    ssize_t                      n;

    c = s->connection;
    rev = c->read;

    if (rev->timedout) {
        ngx_log_error(NGX_LOG_ERR, c->log, 0,
                      "lua socket read timed out");
        ctx->ft_type |= NGX_TCP_LUA_REQ_FT_TIMEOUT;
        return NGX_ERROR;
    }
    
    b = ctx->buf_in;
    
    size = ctx->length - (b->last - b->pos);
    if (size <= 0) {
        //log
        return NGX_OK;
    }
    
    n = c->recv(c, b->last, size);

    ngx_log_debug1(NGX_LOG_DEBUG_TCP, c->log, 0,
                "lua socket recv returned %d",(int) n);

    if (n == NGX_AGAIN) {
        dd("socket recv busy");
        if (!rev->timer_set) {
            cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module);
            ngx_add_timer(rev, cscf->read_timeout);
        }
        
        if (ngx_handle_read_event(rev, 0) != NGX_OK) {
            ctx->ft_type |= NGX_TCP_LUA_REQ_FT_ERROR;
            ctx->socket_invalid = 1;
            ctx->socket_errno = ngx_socket_errno;
            return NGX_ERROR;
        }

        return NGX_AGAIN;
    }

    if (n == 0) {
        ctx->ft_type |= NGX_TCP_LUA_REQ_FT_CLOSED;
        ctx->socket_invalid = 1;
        //ctx->socket_errno = ngx_socket_errno;
        
        ngx_log_debug0(NGX_LOG_DEBUG_TCP, s->connection->log, 0,
                    "lua socket closed");

        return NGX_ERROR;
    }

    if (n == NGX_ERROR) {
        ctx->ft_type |= NGX_TCP_LUA_REQ_FT_ERROR;
        ctx->socket_invalid = 1;
        ctx->socket_errno = ngx_socket_errno;
        
        return NGX_ERROR;
    }
    
    b->last += n;
    
    if ((size_t)(b->last - b->pos) >= ctx->bytes_atleast) {
        return NGX_OK;
    }
    
    return NGX_AGAIN;
}
static void 
ngx_tcp_upstream_init_proxy_handler(ngx_tcp_session_t *s, ngx_tcp_upstream_t *u)
{
    ngx_connection_t         *c;
    ngx_tcp_proxy_ctx_t      *pctx;
    ngx_tcp_proxy_conf_t     *pcf;

    c = s->connection;
    c->log->action = "ngx_tcp_upstream_init_proxy_handler";

    ngx_log_debug0(NGX_LOG_DEBUG_TCP, s->connection->log, 0,
                   "tcp proxy upstream init proxy");

    pcf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_proxy_module);

    pctx = ngx_tcp_get_module_ctx(s, ngx_tcp_proxy_module);

    if (pcf == NULL || pctx == NULL) {
        ngx_tcp_finalize_session(s);
        return;
    }

    pctx->upstream = &s->upstream->peer;

    c = pctx->upstream->connection;
    if (c->read->timedout || c->write->timedout) {
        ngx_tcp_upstream_next(s, u, NGX_TCP_UPSTREAM_FT_TIMEOUT);
        return;
    }

    if (ngx_tcp_upstream_check_broken_connection(s) != NGX_OK){
        ngx_tcp_upstream_next(s, u, NGX_TCP_UPSTREAM_FT_ERROR);
        return;
    }

    s->connection->read->handler = ngx_tcp_proxy_handler;
    s->connection->write->handler = ngx_tcp_proxy_handler;

    c->read->handler = ngx_tcp_proxy_handler;
    c->write->handler = ngx_tcp_proxy_handler;

    ngx_add_timer(c->read, pcf->upstream.read_timeout);
    ngx_add_timer(c->write, pcf->upstream.send_timeout);

    if (ngx_handle_read_event(s->connection->read, 0) != NGX_OK) {
        ngx_tcp_finalize_session(s);
        return;
    }

#if (NGX_TCP_SSL)

    /* 
     * The ssl connection with client may not trigger the read event again,
     * So I trigger it in this function.
     * */
    if (s->connection->ssl) {
        ngx_tcp_proxy_handler(s->connection->read); 
    }

#endif

    return;
}
static void
ngx_tcp_proxy_handler(ngx_event_t *ev) 
{
    char                     *action, *recv_action, *send_action;
    off_t                    *read_bytes, *write_bytes;
    size_t                    size;
    ssize_t                   n;
    ngx_buf_t                *b;
    ngx_err_t                 err;
    ngx_uint_t                do_write, first_read;
    ngx_connection_t         *c, *src, *dst;
    ngx_tcp_session_t        *s;
    ngx_tcp_proxy_conf_t     *pcf;
    ngx_tcp_proxy_ctx_t      *pctx;
    ngx_tcp_core_srv_conf_t  *cscf;

    c = ev->data;
    s = c->data;

    cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module);

    if (ev->timedout) {
        c->log->action = "proxying";

        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "proxy timed out");
        c->timedout = 1;

        ngx_tcp_finalize_session(s);
        return;
    }

    pctx = ngx_tcp_get_module_ctx(s, ngx_tcp_proxy_module);

    if (pctx == NULL) {
        ngx_tcp_finalize_session(s);
        return;
    }

    read_bytes = NULL;
    write_bytes = NULL;

    if (c == s->connection) {
        if (ev->write) {
            recv_action = "client write: proxying and reading from upstream";
            send_action = "client write: proxying and sending to client";
            src = pctx->upstream->connection;
            dst = c;
            b = pctx->buffer;
            write_bytes = &s->bytes_write;
        } else {
            recv_action = "client read: proxying and reading from client";
            send_action = "client read: proxying and sending to upstream";
            src = c;
            dst = pctx->upstream->connection;
            b = s->buffer;
            read_bytes = &s->bytes_read;
        }

    } else {
        if (ev->write) {
            recv_action = "upstream write: proxying and reading from client";
            send_action = "upstream write: proxying and sending to upstream";
            src = s->connection;
            dst = c;
            b = s->buffer;
            read_bytes = &s->bytes_read;
        } else {
            recv_action = "upstream read: proxying and reading from upstream";
            send_action = "upstream read: proxying and sending to client";
            src = c;
            dst = s->connection;
            b = pctx->buffer;
            write_bytes = &s->bytes_write;
        }
    }

    do_write = ev->write ? 1 : 0;

#if (NGX_TCP_SSL)
    /* SSL Need this */
    if (s->connection->ssl) {
        first_read = 1;
    }
#else
    first_read = 0;
#endif

    ngx_log_debug4(NGX_LOG_DEBUG_TCP, ev->log, 0,
                   "tcp proxy handler: %d, #%d > #%d, time:%ui",
                   do_write, src->fd, dst->fd, ngx_current_msec);

    for ( ;; ) {

        if (do_write) {

            size = b->last - b->pos;

            if (size && dst->write->ready) {
                c->log->action = send_action;

                n = dst->send(dst, b->pos, size);
                err = ngx_socket_errno;

                ngx_log_debug1(NGX_LOG_DEBUG_TCP, ev->log, 0,
                               "tcp proxy handler send:%d", n);

                if (n == NGX_ERROR) {
                    ngx_log_error(NGX_LOG_ERR, c->log, err, "proxy send error");

                    ngx_tcp_finalize_session(s);
                    return;
                }

                if (n > 0) {
                    b->pos += n;

                    if (write_bytes) {
                        *write_bytes += n;
                    }

                    if (b->pos == b->last) {
                        b->pos = b->start;
                        b->last = b->start;
                    }
                }
            }
        }

        size = b->end - b->last;

        if (size) {
            if (src->read->ready || first_read) { 

                first_read = 0;
                c->log->action = recv_action;

                n = src->recv(src, b->last, size);
                err = ngx_socket_errno;

                ngx_log_debug1(NGX_LOG_DEBUG_TCP, ev->log, 0,
                               "tcp proxy handler recv:%d", n);

                if (n == NGX_AGAIN || n == 0) {
                    break;
                }

                if (n > 0) {
                    do_write = 1;
                    b->last += n;

                    if (read_bytes) {
                        *read_bytes += n;
                    }

                    continue;
                }

                if (n == NGX_ERROR) {
                    src->read->eof = 1;
                }
            }
        }

        break;
    }

    c->log->action = "nginx tcp proxying";

    if ((s->connection->read->eof && s->buffer->pos == s->buffer->last)
            || (pctx->upstream->connection->read->eof
                && pctx->buffer->pos == pctx->buffer->last)
            || (s->connection->read->eof
                && pctx->upstream->connection->read->eof))
    {
        action = c->log->action;
        c->log->action = NULL;
        ngx_log_error(NGX_LOG_DEBUG, c->log, 0, "proxied session done");
        c->log->action = action;

        ngx_tcp_finalize_session(s);
        return;
    }

    if (ngx_handle_write_event(dst->write, 0) != NGX_OK) {
        ngx_tcp_finalize_session(s);
        return;
    }

    if (ngx_handle_read_event(dst->read, 0) != NGX_OK) {
        ngx_tcp_finalize_session(s);
        return;
    }

    if (ngx_handle_write_event(src->write, 0) != NGX_OK) {
        ngx_tcp_finalize_session(s);
        return;
    }

    if (ngx_handle_read_event(src->read, 0) != NGX_OK) {
        ngx_tcp_finalize_session(s);
        return;
    }

    pcf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_proxy_module);

    if (c == s->connection) {
        ngx_add_timer(c->read, cscf->timeout);
    }

    if (c == pctx->upstream->connection) {
        if (ev->write) {
            ngx_add_timer(c->write, pcf->upstream.send_timeout);
        } else {
            ngx_add_timer(c->read, pcf->upstream.read_timeout);
        }
    }

    return;
}
static int 
ngx_tcp_lua_ngx_wait_next_request(lua_State *L)
{
    ngx_tcp_session_t                  *s;
    ngx_connection_t                   *c;
    int                                 n;
    ngx_tcp_core_srv_conf_t            *cscf;
    ngx_tcp_lua_ctx_t                  *ctx;

    n = lua_gettop(L);
    if (n != 0) {
        return luaL_error(L, "expecting 0 arguments");
    }

    s = ngx_tcp_lua_get_session(L);
    if (s == NULL) { //init_by_lua no session
        return luaL_error(L, "no session found");
    }

    c = s->connection;

    ngx_tcp_lua_finalize_light_session(s);

    ngx_log_debug0(NGX_LOG_DEBUG_TCP, c->log, 0,
                   "lua req calling wait_next_request() method");

    ctx = s->ctx;

    if (ctx->socket_invalid) {
        c->close = 1;
        goto yield;
    }
    
    ngx_tcp_test_reading(s);
    
    if (c->error) {
        c->close = 1;
        goto yield;
    }
    
    if (c->read->ready) {
        if(ngx_tcp_lua_init_light_session(s) != NGX_OK){
            c->close = 1;
            goto yield;
        }

        c->log->action = "continue with new session";
        
        return 0;
    }


yield:
    if (c->close) {
        ctx->exited = 1;
        return lua_yield(L, 0);
    }

    cscf = ngx_tcp_get_module_srv_conf(s,ngx_tcp_core_module);

    c->log->action = "lua_req_keepalive";
    c->idle = 1;
    ngx_reusable_connection(c, 1);

    s->write_event_handler = ngx_tcp_session_empty_handler;
    s->read_event_handler = ngx_tcp_lua_req_keepalive_handler;

    if (!c->read->timer_set && cscf->keepalive_timeout != NGX_CONF_UNSET_MSEC) {
        ngx_add_timer(c->read, cscf->keepalive_timeout);
    }

    //lua_pushliteral(L, "closed");
    return lua_yield(L, 0);
}
static void 
ngx_tcp_websocket_parse_protocol(ngx_event_t *ev)
{
    u_char                       *new_buf;
    ssize_t                       size, n;
    ngx_int_t                     rc;
    ngx_connection_t             *c;
    ngx_tcp_session_t            *s;
    ngx_tcp_websocket_ctx_t      *wctx;
    ngx_tcp_websocket_conf_t     *wcf;

    c = ev->data;
    s = c->data;

    wcf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_websocket_module);

    wctx = ngx_tcp_get_module_ctx(s, ngx_tcp_websocket_module);

    while (1) {
        n = s->buffer->end - s->buffer->last;
        /*Not enough buffer? Enlarge twice*/
        if (n == 0) {
            size = s->buffer->end - s->buffer->start;

            if ((size_t)size > wcf->buffer_size << 3) {

                ngx_log_error(NGX_LOG_ERR, ev->log, 0,
                              "too large websocket handshake packet "
                              "error with client: %V #%d",
                              &c->addr_text, c->fd);

                ngx_tcp_finalize_session(s);
                return;
            }

            new_buf = ngx_palloc(c->pool, size * 2);
            if (new_buf == NULL) {
                goto websocket_recv_fail;
            }

            ngx_memcpy(new_buf, s->buffer->start, size);
            
            n = s->buffer->pos - s->buffer->start;
            s->buffer->start = new_buf;
            s->buffer->pos = new_buf + n;
            s->buffer->last = new_buf + size;
            s->buffer->end = new_buf + size * 2;

            n = s->buffer->end - s->buffer->last;
        }

        size = c->recv(c, s->buffer->last, n);

#if (NGX_DEBUG)
        ngx_err_t                      err;

        if (size >= 0 || size == NGX_AGAIN) {
            err = 0;

        } else {
            err = ngx_socket_errno;

        }

        ngx_log_debug3(NGX_LOG_DEBUG_TCP, ev->log, err,
                       "tcp websocket recv size: %d, client: %V #%d",
                       size, &c->addr_text, c->fd);
#endif

        if (size > 0) {
            s->buffer->last += size;
            continue;

        } else if (size == 0 || size == NGX_AGAIN) {
            break;

        } else {
            c->error = 1;
            goto websocket_recv_fail;
        }
    }

    rc = websocket_http_request_parser_execute(wctx->parser);

    ngx_log_debug2(NGX_LOG_DEBUG_TCP, c->log, 0, 
                   "tcp websocket parse rc: %d, fd: %d", rc, c->fd);

    switch (rc) {

    case NGX_AGAIN:
        return;

    case NGX_ERROR:
        goto websocket_recv_fail;

    case NGX_OK:
        /* pass through */

    default:
        ngx_tcp_websocket_init_upstream(c, s);
    }

    return;

websocket_recv_fail:

    ngx_log_error(NGX_LOG_ERR, ev->log, 0,
                  "recv websocket handshake packet error with client: %V #%d",
                  &c->addr_text, c->fd);

    ngx_tcp_finalize_session(s);
}
void
ngx_tcp_init_connection(ngx_connection_t *c)
{
    ngx_uint_t                i;
    ngx_tcp_port_t           *port;
    struct sockaddr          *sa;
    struct sockaddr_in       *sin;
    ngx_tcp_log_ctx_t        *ctx;
    ngx_tcp_in_addr_t        *addr;
    ngx_tcp_session_t        *s;
    ngx_tcp_addr_conf_t      *addr_conf;
    ngx_tcp_core_srv_conf_t  *cscf;
#if (NGX_HAVE_INET6)
    struct sockaddr_in6   *sin6;
    ngx_tcp_in6_addr_t   *addr6;
#endif


    /* find the server configuration for the address:port */

    port = c->listening->servers;

    if (port && port->naddrs > 1) {

        /*
         * There are several addresses on this port and one of them
         * is the "*:port" wildcard so getsockname() is needed to determine
         * the server address.
         *
         * AcceptEx() already gave this address.
         */

        if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
            ngx_tcp_close_connection(c);
            return;
        }

        sa = c->local_sockaddr;

        switch (sa->sa_family) {

#if (NGX_HAVE_INET6)
        case AF_INET6:
            sin6 = (struct sockaddr_in6 *) sa;

            addr6 = port->addrs;

            /* the last address is "*" */

            for (i = 0; i < port->naddrs - 1; i++) {
                if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {
                    break;
                }
            }

            addr_conf = &addr6[i].conf;

            break;
#endif

        default: /* AF_INET */
            sin = (struct sockaddr_in *) sa;

            addr = port->addrs;

            /* the last address is "*" */

            for (i = 0; i < port->naddrs - 1; i++) {
                if (addr[i].addr == sin->sin_addr.s_addr) {
                    break;
                }
            }

            addr_conf = &addr[i].conf;

            break;
        }

    } else {
        switch (c->local_sockaddr->sa_family) {

#if (NGX_HAVE_INET6)
        case AF_INET6:
            addr6 = port->addrs;
            addr_conf = &addr6[0].conf;
            break;
#endif

        case AF_INET:
            addr = port->addrs;
            addr_conf = &addr[0].conf;
            break;

        default: /* AF_UNIX */
            addr = port->addrs;
            addr_conf = &addr[0].conf;
            break;
        }
    }

    cscf = ngx_tcp_get_module_srv_conf(addr_conf->ctx, 
                                           ngx_tcp_core_module);
    s = cscf->protocol->create_session(c);
    if (s == NULL) {
        ngx_tcp_close_connection(c);
        return;
    }

    s->main_conf = addr_conf->ctx->main_conf;
    s->srv_conf = addr_conf->ctx->srv_conf;

    s->addr_text = &addr_conf->addr_text;

    c->data = s;
    s->connection = c;

    ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%ui client %V connected to %V",
                  c->number, &c->addr_text, s->addr_text);

    ctx = ngx_palloc(c->pool, sizeof(ngx_tcp_log_ctx_t));
    if (ctx == NULL) {
        ngx_tcp_close_connection(c);
        return;
    }

    ctx->client = &c->addr_text;
    ctx->session = s;

    c->log->connection = c->number;
    c->log->handler = ngx_tcp_log_error_msg;
    c->log->data = ctx;
    c->log->action = "sending client greeting line";

    c->log_error = NGX_ERROR_INFO;

#if (NGX_TCP_SSL)
    {
    ngx_tcp_ssl_conf_t  *sslcf;

    sslcf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_ssl_module);

    if (sslcf->enable) {
        c->log->action = "SSL handshaking";

        ngx_tcp_ssl_init_connection(&sslcf->ssl, c);
        return;
    }

    if (addr_conf->ssl) {

        c->log->action = "SSL handshaking";

        if (sslcf->ssl.ctx == NULL) {
            ngx_log_error(NGX_LOG_ERR, c->log, 0,
                          "no \"ssl_certificate\" is defined "
                          "in server listening on SSL port");
            ngx_tcp_close_connection(c);
            return;
        }

        ngx_tcp_ssl_init_connection(&sslcf->ssl, c);
        return;
    }

    }
#endif

    ngx_tcp_init_session(c);
}
static  void
ngx_tcp_websocket_init_upstream(ngx_connection_t *c, ngx_tcp_session_t *s) 
{
    ngx_tcp_upstream_t           *u;
    ngx_tcp_path_upstream_t      *pu;     
    ngx_tcp_upstream_conf_t      *ucf;
    ngx_tcp_websocket_ctx_t      *wctx;
    ngx_tcp_websocket_conf_t     *wcf;
    ngx_tcp_virtual_server_t     *vs;

    s->connection->log->action = "ngx_tcp_websocket_init_upstream";

    wctx = ngx_tcp_get_module_ctx(s, ngx_tcp_websocket_module);

    vs = ngx_tcp_websocket_find_virtual_server(s, wctx);

    if (vs) {
        s->main_conf = vs->ctx->main_conf;
        s->srv_conf = vs->ctx->srv_conf;
    }

    wcf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_websocket_module);
    if (wcf->upstream.upstream == NULL) {
        ngx_tcp_finalize_session(s);
        return;
    }

    ngx_log_debug3(NGX_LOG_DEBUG_TCP, s->connection->log, 0, 
                   "tcp websocket init upstream, scheme: \"%V\" "
                   "path: \"%V\", host: \"%V\"",
                   &wcf->scheme, &wctx->path, &wctx->host);

    c->write->handler = ngx_tcp_websocket_dummy_write_handler;
    c->read->handler = ngx_tcp_websocket_dummy_read_handler;

    if (ngx_tcp_upstream_create(s) != NGX_OK) {
        ngx_tcp_finalize_session(s);
        return;
    }

    u = s->upstream;

    u->conf = &wcf->upstream;

    pu = ngx_tcp_websocket_find_path_upstream(s, wctx);

    if (pu) {
        ucf = ngx_palloc(s->pool, sizeof(ngx_tcp_upstream_conf_t));
        if (ucf == NULL) {
            ngx_tcp_finalize_session(s);
            return;
        }

        ngx_memcpy(ucf, &wcf->upstream, sizeof(ngx_tcp_upstream_conf_t));

        ucf->upstream = pu->upstream;

        u->conf = ucf;
    }

    u->write_event_handler = ngx_tcp_upstream_websocket_proxy_init_handler;
    u->read_event_handler = ngx_tcp_upstream_websocket_proxy_init_handler;

    wctx->upstream = &u->peer;

    wctx->buffer = ngx_create_temp_buf(s->connection->pool, 
                                       (s->buffer->end - s->buffer->start));
    if (wctx->buffer == NULL) {
        ngx_tcp_finalize_session(s);
        return;
    }

    /* move back to the start position, send the handshake 
     * packet to backend server */
    s->buffer->pos = s->buffer->start;
    s->connection->read->ready = 1;

    ngx_tcp_upstream_init(s);

    return;
}
static void 
ngx_tcp_upstream_websocket_proxy_init_handler(ngx_tcp_session_t *s,
    ngx_tcp_upstream_t *u)
{
    ngx_connection_t             *c;
    ngx_tcp_websocket_ctx_t      *wctx;
    ngx_tcp_websocket_conf_t     *wcf;

    c = s->connection;
    c->log->action = "ngx_tcp_upstream_websocket_proxy_init_handler";

    ngx_log_debug0(NGX_LOG_DEBUG_TCP, s->connection->log, 
                   0, "tcp upstream websocket proxy init handler");

    wcf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_websocket_module);

    wctx = ngx_tcp_get_module_ctx(s, ngx_tcp_websocket_module);

    if (wcf == NULL || wctx == NULL) {
        ngx_tcp_finalize_session(s);
        return;
    }

    wctx->upstream = &s->upstream->peer;

    c = wctx->upstream->connection;
    if (c->read->timedout || c->write->timedout) {
        ngx_tcp_upstream_next(s, u, NGX_TCP_UPSTREAM_FT_TIMEOUT);
        return;
    }

    if (ngx_tcp_upstream_check_broken_connection(s) != NGX_OK){
        ngx_tcp_upstream_next(s, u, NGX_TCP_UPSTREAM_FT_ERROR);
        return;
    }

    s->connection->read->handler = ngx_tcp_websocket_proxy_handler;
    s->connection->write->handler = ngx_tcp_websocket_proxy_handler;

    c->read->handler = ngx_tcp_websocket_proxy_handler;
    c->write->handler = ngx_tcp_websocket_proxy_handler;

    ngx_add_timer(c->read, wcf->upstream.read_timeout);
    ngx_add_timer(c->write, wcf->upstream.send_timeout);

#if (NGX_TCP_SSL)

    if (s->connection->ssl) {
        ngx_tcp_websocket_proxy_handler(s->connection->read); 
        return;
    }

#endif

    if (ngx_handle_read_event(s->connection->read, 0) != NGX_OK) {
        ngx_tcp_finalize_session(s);
        return;
    }

    ngx_tcp_websocket_proxy_handler(s->connection->read);

    return;
}