static void 
ngx_tcp_check_finish_handler(ngx_event_t *event) 
{
    if (ngx_tcp_check_need_exit()) {
        return;
    }
}
static void 
ngx_tcp_check_connect_handler(ngx_event_t *event) 
{
    ngx_int_t                      rc;
    ngx_connection_t              *c;
    ngx_tcp_check_peer_conf_t     *peer_conf;
    ngx_tcp_upstream_srv_conf_t   *uscf;

    if (ngx_tcp_check_need_exit()) {
        return;
    }

    peer_conf = event->data;
    uscf = peer_conf->conf;

    ngx_memzero(&peer_conf->pc, sizeof(ngx_peer_connection_t));

    peer_conf->pc.sockaddr = peer_conf->peer->sockaddr;
    peer_conf->pc.socklen = peer_conf->peer->socklen;
    peer_conf->pc.name = &peer_conf->peer->name;

    peer_conf->pc.get = ngx_event_get_peer;
    peer_conf->pc.log = event->log;
    peer_conf->pc.log_error = NGX_ERROR_ERR; 

    peer_conf->pc.cached = 0;
    peer_conf->pc.connection = NULL;

    rc = ngx_event_connect_peer(&peer_conf->pc);

    if (rc == NGX_ERROR || rc == NGX_DECLINED) {
        ngx_tcp_check_status_update(peer_conf, 0);
        return;
    }

    /*NGX_OK or NGX_AGAIN*/
    c = peer_conf->pc.connection;
    c->data = peer_conf;
    c->log = peer_conf->pc.log;
    c->sendfile = 0;
    c->read->log = c->log;
    c->write->log = c->log;
    c->pool = peer_conf->pool;

    peer_conf->state = NGX_TCP_CHECK_CONNECT_DONE;

    c->write->handler = peer_conf->send_handler;
    c->read->handler = peer_conf->recv_handler;

    ngx_add_timer(&peer_conf->check_timeout_ev, uscf->check_timeout);

    /* The kqueue's loop interface need it. */
    if (rc == NGX_OK) {
        c->write->handler(c->write);
    }
}
static void 
ngx_tcp_check_timeout_handler(ngx_event_t *event) 
{
    ngx_tcp_check_peer_conf_t     *peer_conf;
    
    if (ngx_tcp_check_need_exit()) {
        return;
    }

    peer_conf = event->data;

    ngx_log_error(NGX_LOG_ERR, event->log, 0,
            "check time out with peer: %V ", &peer_conf->peer->name);

    ngx_tcp_check_status_update(peer_conf, 0);
    ngx_tcp_check_clean_event(peer_conf);
}
static void 
ngx_tcp_check_begin_handler(ngx_event_t *event) 
{
    ngx_tcp_check_peer_conf_t     *peer_conf;
    ngx_tcp_upstream_srv_conf_t   *uscf;

    if (ngx_tcp_check_need_exit()) {
        return;
    }

    peer_conf = event->data;
    uscf = peer_conf->conf;

    ngx_add_timer(event, uscf->check_interval/2);

    /* This process are processing the event now. */
    if (peer_conf->shm->owner == ngx_pid) {
        return;
    }

    ngx_log_debug4(NGX_LOG_DEBUG_TCP, event->log, 0, 
                   "tcp check begin handler index:%ud, owner: %d, "
                   "ngx_pid: %ud, time:%d", 
                   peer_conf->index, peer_conf->shm->owner, ngx_pid, 
                   (ngx_current_msec - peer_conf->shm->access_time));

    ngx_spinlock(&peer_conf->shm->lock, ngx_pid, 1024);

    if (((ngx_current_msec - peer_conf->shm->access_time) >= uscf->check_interval) && 
            peer_conf->shm->owner == NGX_INVALID_PID)
    {
        peer_conf->shm->owner = ngx_pid;
    }

    ngx_spinlock_unlock(&peer_conf->shm->lock);

    if (peer_conf->shm->owner == ngx_pid) {
        ngx_tcp_check_connect_handler(event);
    }
}
static void 
ngx_tcp_check_peek_handler(ngx_event_t *event) 
{
    char                           buf[1];
    ngx_int_t                      n;
    ngx_err_t                      err;
    ngx_connection_t              *c;
    ngx_tcp_check_peer_conf_t     *peer_conf;

    if (ngx_tcp_check_need_exit()) {
        return;
    }

    c = event->data;
    peer_conf = c->data;

    n = recv(c->fd, buf, 1, MSG_PEEK);

    err = ngx_socket_errno;

    ngx_log_debug2(NGX_LOG_DEBUG_TCP, c->log, err, 
            "tcp check upstream recv(): %d, fd: %d",
            n, c->fd);

    if (n >= 0 || err == NGX_EAGAIN) {
        ngx_tcp_check_status_update(peer_conf, 1);
    }
    else {
        c->error = 1;
        ngx_tcp_check_status_update(peer_conf, 0);
    }

    ngx_tcp_check_clean_event(peer_conf);

    /*dummy*/
    ngx_tcp_check_finish_handler(event);
}
static void 
ngx_tcp_check_recv_handler(ngx_event_t *event) 
{
    ssize_t                        size, n, rc;
    u_char                        *new_buf;
    ngx_connection_t              *c;
    ngx_tcp_check_peer_conf_t     *peer_conf;
    ngx_tcp_check_ctx             *ctx;

    if (ngx_tcp_check_need_exit()) {
        return;
    }

    c = event->data;
    peer_conf = c->data;

    if (peer_conf->state != NGX_TCP_CHECK_SEND_DONE) {

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

        return;
    }

    ctx = peer_conf->check_data;

    if (ctx->recv.start == NULL) {
        /* 2048, is it enough? */
        ctx->recv.start = ngx_palloc(c->pool, ngx_pagesize/2);
        if (ctx->recv.start == NULL) {
            goto check_recv_fail;
        }

        ctx->recv.last = ctx->recv.pos = ctx->recv.start;
        ctx->recv.end = ctx->recv.start + ngx_pagesize/2;
    }

    while (1) {
        n = ctx->recv.end - ctx->recv.last;
        /*Not enough buffer? Enlarge twice*/
        if (n == 0) {
            size = ctx->recv.end - ctx->recv.start;
            new_buf = ngx_palloc(c->pool, size * 2);
            if (new_buf == NULL) {
                goto check_recv_fail;
            }

            ngx_memcpy(new_buf, ctx->recv.start, size);

            ctx->recv.pos = ctx->recv.start = new_buf;
            ctx->recv.last = new_buf + size;
            ctx->recv.end = new_buf + size * 2;

            n = ctx->recv.end - ctx->recv.last;
        }

        size = c->recv(c, ctx->recv.last, n);

#if (NGX_DEBUG)
        ngx_err_t                      err;

        err = (size >= 0) ? 0 : ngx_socket_errno;
        ngx_log_debug2(NGX_LOG_DEBUG_TCP, c->log, err, 
                "tcp check recv size: %d, peer: %V", size, &peer_conf->peer->name);
#endif

        if (size > 0) {
            ctx->recv.last += size;
            continue;
        } else if (size == 0 || size == NGX_AGAIN) {
            break;
        }
        else {
            c->error = 1;
            goto check_recv_fail;
        }
    }

    rc = peer_conf->parse(peer_conf); 

    ngx_log_debug2(NGX_LOG_DEBUG_TCP, c->log, 0, 
            "tcp check parse rc: %d, peer: %V", rc, &peer_conf->peer->name);

    switch (rc) {
        case NGX_AGAIN:
            return;
        case NGX_ERROR:
            ngx_log_error(NGX_LOG_ERR, event->log, 0,
                    "check protocol %s error with peer: %V ", 
                    peer_conf->conf->check_type_conf->name, &peer_conf->peer->name);
            ngx_tcp_check_status_update(peer_conf, 0);
            break;
        case NGX_OK:
        default:
            ngx_tcp_check_status_update(peer_conf, 1);
    }

    peer_conf->state = NGX_TCP_CHECK_RECV_DONE;
    ngx_tcp_check_clean_event(peer_conf);
    return;

check_recv_fail:
    ngx_tcp_check_status_update(peer_conf, 0);
    ngx_tcp_check_clean_event(peer_conf);
    return;
}
static void 
ngx_tcp_check_send_handler(ngx_event_t *event) 
{
    ssize_t                        size;
    ngx_connection_t              *c;
    ngx_tcp_check_ctx             *ctx;
    ngx_tcp_check_peer_conf_t     *peer_conf;

    if (ngx_tcp_check_need_exit()) {
        return;
    }

    c = event->data;
    peer_conf = c->data;

    if (c->pool == NULL) {
        ngx_log_error(NGX_LOG_ERR, event->log, 0,
                "check pool NULL with peer: %V ", &peer_conf->peer->name);

        goto check_send_fail;
    }

    if (peer_conf->state != NGX_TCP_CHECK_CONNECT_DONE) {
        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
                goto check_send_fail;
        }

        return;
    }

    if (peer_conf->check_data == NULL) {

        peer_conf->check_data = ngx_pcalloc(c->pool, sizeof(ngx_tcp_check_ctx));
        if (peer_conf->check_data == NULL) {
            goto check_send_fail;
        }

        if (peer_conf->init == NULL || peer_conf->init(peer_conf) != NGX_OK) {

            ngx_log_error(NGX_LOG_ERR, event->log, 0,
                    "check init error with peer: %V ", &peer_conf->peer->name);

            goto check_send_fail;
        }
    }

    ctx = peer_conf->check_data;

    while (ctx->send.pos < ctx->send.last) {

        size = c->send(c, ctx->send.pos, ctx->send.last - ctx->send.pos);

#if (NGX_DEBUG)
        ngx_err_t                      err;

        err = (size >=0) ? 0 : ngx_socket_errno;
        ngx_log_debug2(NGX_LOG_DEBUG_TCP, c->log, err, 
                "tcp check send size: %d, total: %d", size, ctx->send.last - ctx->send.pos);
#endif

        if (size >= 0) {
            ctx->send.pos += size;
        }
        else if (size == NGX_AGAIN) {
            return;
        }
        else {
            c->error = 1;
            goto check_send_fail;
        }
    }

    if (ctx->send.pos == ctx->send.last) {
        ngx_log_debug0(NGX_LOG_DEBUG_TCP, c->log, 0, "tcp check send done.");
        peer_conf->state = NGX_TCP_CHECK_SEND_DONE;
    }

    return;

check_send_fail:
    ngx_tcp_check_status_update(peer_conf, 0);
    ngx_tcp_check_clean_event(peer_conf);
    return;
}