void
ngx_http_tfs_rc_server_expire(ngx_http_tfs_rc_ctx_t *ctx)
{
    ngx_queue_t             *q, *kp_q;
    ngx_rbtree_node_t       *node;
    ngx_http_tfs_rcs_info_t *rc_info_node;

    if (ngx_queue_empty(&ctx->sh->queue)) {
        return;
    }

    q = ngx_queue_last(&ctx->sh->queue);

    rc_info_node = ngx_queue_data(q, ngx_http_tfs_rcs_info_t, queue);
    kp_q = &rc_info_node->kp_queue;

    ngx_queue_remove(q);
    ngx_queue_remove(kp_q);

    node = (ngx_rbtree_node_t *)
           ((u_char *) rc_info_node - offsetof(ngx_rbtree_node_t, color));

    ngx_rbtree_delete(&ctx->sh->rbtree, node);

    ngx_http_tfs_rc_server_destroy_node(ctx, rc_info_node);
}
static ngx_int_t
ngx_http_tfs_create_info_node(ngx_http_tfs_t *t, ngx_http_tfs_rc_ctx_t *rc_ctx,
    u_char *data, ngx_str_t appkey)
{
    u_char                                   *p;
    size_t                                    n;
    uint32_t                                  len;
    ngx_int_t                                 rc;
    ngx_rbtree_node_t                        *node;
    ngx_http_tfs_rcs_info_t                  *rc_info_node;

    rc_info_node = NULL;

    n = offsetof(ngx_rbtree_node_t, color)
        + sizeof(ngx_http_tfs_rcs_info_t);

    node = ngx_slab_alloc_locked(rc_ctx->shpool, n);
    if (node == NULL) {
        ngx_http_tfs_expire_and_alloc(node, n);
    }

    rc_info_node = (ngx_http_tfs_rcs_info_t *) &node->color;

    node->key = ngx_murmur_hash2(appkey.data, appkey.len);

    rc_info_node->appkey.data = ngx_slab_alloc_locked(rc_ctx->shpool, appkey.len);
    if (rc_info_node->appkey.data == NULL) {
        ngx_http_tfs_rc_server_expire(rc_ctx);
        rc_info_node->appkey.data = ngx_slab_alloc_locked(rc_ctx->shpool, appkey.len);
        if (rc_info_node->appkey.data == NULL) {
            goto login_error;
        }
    }

    ngx_memcpy(rc_info_node->appkey.data, appkey.data, appkey.len);
    rc_info_node->appkey.len = appkey.len;

    /* parse session id */
    len = *((uint32_t *) data);
    p = data + sizeof(uint32_t);
    if (len <= 0) {
        rc_info_node->session_id.len = 0;
        goto login_error;
    }

    rc_info_node->session_id.len = len - 1;
    rc_info_node->session_id.data = ngx_slab_alloc_locked(rc_ctx->shpool, len);
    if (rc_info_node->session_id.data == NULL) {
        ngx_http_tfs_rc_server_expire(rc_ctx);
        rc_info_node->session_id.data = ngx_slab_alloc_locked(rc_ctx->shpool,
                                                              rc_info_node->session_id.len);
        if (rc_info_node->session_id.data == NULL) {
            goto login_error;
        }
    }

    ngx_memcpy(rc_info_node->session_id.data, p, rc_info_node->session_id.len);

    p += rc_info_node->session_id.len + 1;

    /* parse rc info */
    rc = ngx_http_tfs_parse_rc_info(rc_info_node, rc_ctx, p);
    if (rc == NGX_ERROR) {
        goto login_error;
    }

    t->rc_info_node = rc_info_node;
    ngx_rbtree_insert(&rc_ctx->sh->rbtree, node);
    ngx_queue_insert_head(&rc_ctx->sh->queue, &rc_info_node->queue);
    ngx_queue_insert_tail(&rc_ctx->sh->kp_queue, &rc_info_node->kp_queue);

    return NGX_OK;

login_error:

    ngx_http_tfs_rc_server_destroy_node(rc_ctx, rc_info_node);
    t->rc_info_node = NULL;
    return NGX_ERROR;
}
ngx_int_t
ngx_http_tfs_parse_keepalive_message(ngx_http_tfs_t *t)
{
    u_char                             *p, update;
    uint16_t                            type;
    ngx_str_t                           err_msg;
    ngx_int_t                           rc;
    ngx_queue_t                        *q, *queue;
    ngx_rbtree_node_t                  *node;
    ngx_http_tfs_header_t              *header;
    ngx_http_tfs_rc_ctx_t              *rc_ctx;
    ngx_http_tfs_rcs_info_t            *rc_info;
    ngx_http_tfs_peer_connection_t     *tp;

    header = (ngx_http_tfs_header_t *) t->header;
    tp = t->tfs_peer;
    type = header->type;

    switch (type) {
    case NGX_HTTP_TFS_STATUS_MESSAGE:
        ngx_str_set(&err_msg, "keepalive rc");
        return ngx_http_tfs_status_message(&tp->body_buffer, &err_msg, t->log);
    }

    p = tp->body_buffer.pos;
    update = *p;
    p++;

    if (!update) {
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, t->log, 0,
                      "rc keepalive, update flag: %d", update);
    } else {
        ngx_log_error(NGX_LOG_WARN, t->log, 0,
                      "rc keepalive, update flag: %d", update);
    }

    rc_ctx = t->main_conf->rc_ctx;

    queue = &rc_ctx->sh->kp_queue;
    if (ngx_queue_empty(queue)) {
        return NGX_ERROR;
    }

    q = t->curr_ka_queue;
    if (q == NULL) {
        return NGX_ERROR;
    }
    t->curr_ka_queue = ngx_queue_next(q);

    if (update == NGX_HTTP_TFS_NO) {
        return NGX_OK;
    }

    /* FIXME: do not consider rc_info_node being expired, it hardly occurs
     * e.g. a single rc_info_node occupys nearly 2KB space,
     * 10MB for tfs_rcs_zone can hold at least 5000 rc_infos.
     */
    rc_info = ngx_queue_data(q, ngx_http_tfs_rcs_info_t, kp_queue);

    /* update info node */
    /* FIXME: sth terrible may happen here if someone has get the rc_info before lock */
    ngx_shmtx_lock(&rc_ctx->shpool->mutex);
    rc = ngx_http_tfs_update_info_node(t, rc_ctx, rc_info, p);
    /* rc_info has been destroyed, remove from queue and rbtree */
    if (rc == NGX_ERROR) {
        ngx_queue_remove(&rc_info->queue);
        ngx_queue_remove(&rc_info->kp_queue);

        node = (ngx_rbtree_node_t *)
            ((u_char *) rc_info - offsetof(ngx_rbtree_node_t, color));
        ngx_rbtree_delete(&rc_ctx->sh->rbtree, node);

        ngx_http_tfs_rc_server_destroy_node(rc_ctx, rc_info);
    }
    ngx_shmtx_unlock(&rc_ctx->shpool->mutex);

    return rc;
}