ngx_chain_t *
ngx_http_tfs_root_server_create_message(ngx_pool_t *pool)
{
    ngx_buf_t                  *b;
    ngx_chain_t                *cl;
    ngx_http_tfs_rs_request_t  *req;

    b = ngx_create_temp_buf(pool, sizeof(ngx_http_tfs_rs_request_t));
    if (b == NULL) {
        return NULL;
    }

    req = (ngx_http_tfs_rs_request_t *) b->pos;
    req->header.flag = NGX_HTTP_TFS_PACKET_FLAG;
    req->header.len = sizeof(uint8_t);
    req->header.type = NGX_HTTP_TFS_REQ_RT_GET_TABLE_MESSAGE;
    req->header.version = NGX_HTTP_TFS_PACKET_VERSION;
    req->header.crc = ngx_http_tfs_crc(NGX_HTTP_TFS_PACKET_FLAG,
                                       (const char *) (&req->header + 1),
                                       req->header.len);
    req->header.id = ngx_http_tfs_generate_packet_id();

    b->last += sizeof(ngx_http_tfs_rs_request_t);

    cl = ngx_alloc_chain_link(pool);
    if (cl == NULL) {
        return NULL;
    }

    cl->buf = b;
    cl->next = NULL;

    return cl;
}
static ngx_chain_t *
ngx_http_tfs_create_ls_message(ngx_http_tfs_t *t)
{
    size_t                                  size;
    u_char                                 *p;
    ngx_buf_t                              *b;
    ngx_chain_t                            *cl;
    ngx_http_tfs_restful_ctx_t             *r_ctx;
    ngx_http_tfs_ms_ls_msg_header_t        *req;

    r_ctx = &t->r_ctx;

    size = sizeof(ngx_http_tfs_ms_ls_msg_header_t) +
        /* file path */
        t->last_file_path.len +
        /* '/0' */
        1 +
        /* file type */
        sizeof(uint8_t) +
        /* version */
        sizeof(uint64_t);

    b = ngx_create_temp_buf(t->pool, size);
    if (b == NULL) {
        return NULL;
    }

    req = (ngx_http_tfs_ms_ls_msg_header_t *) b->pos;
    req->header.type = NGX_HTTP_TFS_LS_FILEPATH_MESSAGE;
    req->header.len = size - sizeof(ngx_http_tfs_header_t);
    req->header.flag = NGX_HTTP_TFS_PACKET_FLAG;
    req->header.version = NGX_HTTP_TFS_PACKET_VERSION;
    req->header.id = ngx_http_tfs_generate_packet_id();
    req->app_id = r_ctx->app_id;
    req->user_id = r_ctx->user_id;
    req->file_len = t->last_file_path.len + 1;
    req->pid = t->last_file_pid;
    p = ngx_cpymem(req->file_path, t->last_file_path.data, t->last_file_path.len + 1);

    *p = t->last_file_type;
    p += sizeof(uint8_t);

    *((uint64_t *)p) = t->loc_conf->meta_server_table.version;
    p += sizeof(uint64_t);

    req->header.crc = ngx_http_tfs_crc(NGX_HTTP_TFS_PACKET_FLAG,
                                       (const char *) (&req->header + 1),
                                       req->header.len);
    b->last += size;

    cl = ngx_alloc_chain_link(t->pool);
    if (cl == NULL) {
        return NULL;
    }

    cl->buf = b;
    cl->next = NULL;

    return cl;
}
ngx_chain_t *
ngx_http_tfs_create_login_message(ngx_http_tfs_t *t)
{
    ngx_buf_t                                 *b;
    ngx_chain_t                               *cl;
    struct sockaddr_in                        *addr;
    ngx_http_tfs_rcs_login_msg_header_t       *req;

    b = ngx_create_temp_buf(t->pool, sizeof(ngx_http_tfs_rcs_login_msg_header_t) +
                            sizeof(uint64_t) + t->r_ctx.appkey.len + 1);
    if (b == NULL) {
        return NULL;
    }

    req = (ngx_http_tfs_rcs_login_msg_header_t *) b->pos;
    req->header.flag = NGX_HTTP_TFS_PACKET_FLAG;
    req->header.len = sizeof(uint64_t) + t->r_ctx.appkey.len + sizeof(uint32_t) + 1;
    req->header.type = NGX_HTTP_TFS_REQ_RC_LOGIN_MESSAGE;
    req->header.version = NGX_HTTP_TFS_PACKET_VERSION;
    req->header.id = ngx_http_tfs_generate_packet_id();

    req->appkey_len = t->r_ctx.appkey.len + 1;

    b->last += sizeof(ngx_http_tfs_rcs_login_msg_header_t);

    /* app key */
    ngx_memcpy(b->last, t->r_ctx.appkey.data, t->r_ctx.appkey.len);
    b->last += t->r_ctx.appkey.len;
    *(b->last) = '\0';
    b->last += 1;

    /* app ip */
    addr = &(t->srv_conf->local_addr);
    ngx_memcpy(b->last, &(addr->sin_addr.s_addr), sizeof(uint64_t));
    b->last += sizeof(uint64_t);

    req->header.crc = ngx_http_tfs_crc(NGX_HTTP_TFS_PACKET_FLAG,
                                       (const char *) (&req->header + 1),
                                       req->header.len);

    cl = ngx_alloc_chain_link(t->pool);
    if (cl == NULL) {
        return NULL;
    }

    cl->buf = b;
    cl->next = NULL;

    return cl;
}
static ngx_chain_t *
ngx_http_tfs_create_action_message(ngx_http_tfs_t *t, ngx_str_t *file_path_s, ngx_str_t *file_path_d)
{
    size_t                                      size;
    u_char                                     *p;
    ngx_buf_t                                  *b;
    ngx_chain_t                                *cl;
    ngx_http_tfs_restful_ctx_t                 *r_ctx;
    ngx_http_tfs_ms_base_msg_header_t          *req;

    r_ctx = &t->r_ctx;

    size = sizeof(ngx_http_tfs_ms_base_msg_header_t) +
        /* file path */
        file_path_s->len +
        /* version */
        sizeof(uint64_t) +
        /* new file path len */
        sizeof(uint32_t) +
        /* '/0' */
        1 +
        /* action */
        sizeof(uint8_t);

    if (file_path_d != NULL && file_path_d->data != NULL) {
        size += file_path_d->len + 1;
    }

    b = ngx_create_temp_buf(t->pool, size);
    if (b == NULL) {
        return NULL;
    }

    req = (ngx_http_tfs_ms_base_msg_header_t *) b->pos;
    req->header.type = NGX_HTTP_TFS_FILEPATH_ACTION_MESSAGE;
    req->header.len = size - sizeof(ngx_http_tfs_header_t);
    req->header.flag = NGX_HTTP_TFS_PACKET_FLAG;
    req->header.version = NGX_HTTP_TFS_PACKET_VERSION;
    req->header.id = ngx_http_tfs_generate_packet_id();
    req->app_id = r_ctx->app_id;
    req->user_id = r_ctx->user_id;
    req->file_len = file_path_s->len + 1;
    p = ngx_cpymem(req->file_path_s, file_path_s->data, file_path_s->len + 1);

    *((uint64_t *)p) = t->loc_conf->meta_server_table.version;
    p += sizeof(uint64_t);

    if (file_path_d != NULL && file_path_d->data != NULL) {
        /* new file path */
        *((uint32_t *)p) = file_path_d->len + 1;
        p += sizeof(uint32_t);
        p = ngx_cpymem(p, file_path_d->data, file_path_d->len + 1);

    } else {
        *((uint32_t *)p) = 0;
        p += sizeof(uint32_t);
    }

    /* start body */
    *p = r_ctx->action.code;

    req->header.crc = ngx_http_tfs_crc(NGX_HTTP_TFS_PACKET_FLAG,
                                       (const char *) (&req->header + 1),
                                       req->header.len);
    b->last += size;

    cl = ngx_alloc_chain_link(t->pool);
    if (cl == NULL) {
        return NULL;
    }

    cl->buf = b;
    cl->next = NULL;

    return cl;
}
static ngx_chain_t *
ngx_http_tfs_create_read_meta_message(ngx_http_tfs_t *t, int64_t req_offset, uint64_t req_size)
{
    u_char                                 *p;
    size_t                                  size, max_frag_count, req_frag_count;
    ngx_buf_t                              *b;
    ngx_chain_t                            *cl;
    ngx_http_tfs_restful_ctx_t             *r_ctx;
    ngx_http_tfs_ms_base_msg_header_t      *req;

    r_ctx = &t->r_ctx;

    size = sizeof(ngx_http_tfs_ms_base_msg_header_t) +
        /* file */
        r_ctx->file_path_s.len +
        /* \0 */
        1 +
        /* version */
        sizeof(uint64_t) +
        /* offset */
        sizeof(uint64_t) +
        /* size */
        sizeof(uint64_t);

    b = ngx_create_temp_buf(t->pool, size);
    if (b == NULL) {
        return NULL;
    }

    req = (ngx_http_tfs_ms_base_msg_header_t *) b->pos;
    req->header.type = NGX_HTTP_TFS_READ_FILEPATH_MESSAGE;
    req->header.len = size - sizeof(ngx_http_tfs_header_t);
    req->header.flag = NGX_HTTP_TFS_PACKET_FLAG;
    req->header.version = NGX_HTTP_TFS_PACKET_VERSION;
    req->header.id = ngx_http_tfs_generate_packet_id();
    req->app_id = r_ctx->app_id;
    req->user_id = r_ctx->user_id;
    req->file_len = r_ctx->file_path_s.len + 1;
    p = ngx_cpymem(req->file_path_s, r_ctx->file_path_s.data, r_ctx->file_path_s.len + 1);

    *((uint64_t *)p) = t->loc_conf->meta_server_table.version;
    p += sizeof(uint64_t);

    *((uint64_t *) p) = req_offset;
    p += sizeof(uint64_t);

    max_frag_count = (t->main_conf->body_buffer_size - sizeof(ngx_http_tfs_ms_read_response_t))
        / sizeof(ngx_http_tfs_meta_frag_meta_info_t);
    req_frag_count = req_size / (NGX_HTTP_TFS_MAX_FRAGMENT_SIZE);

    ngx_log_error(NGX_LOG_INFO, t->log, 0 ,"max_frag_count: %uz, req_frag_count: %uz, data size: %uz",
                  max_frag_count, req_frag_count, req_size);

    if (req_frag_count > max_frag_count) {
        *((uint64_t *) p) = (max_frag_count - 1) * NGX_HTTP_TFS_MAX_FRAGMENT_SIZE;
        p += sizeof(uint64_t);
        t->has_split_frag = NGX_HTTP_TFS_YES;

    } else {
        *((uint64_t *) p) = req_size;
        p += sizeof(uint64_t);
        t->has_split_frag = NGX_HTTP_TFS_NO;
    }

    req->header.crc = ngx_http_tfs_crc(NGX_HTTP_TFS_PACKET_FLAG,
                                       (const char *) (&req->header + 1),
                                       req->header.len);

    b->last += size;

    cl = ngx_alloc_chain_link(t->pool);
    if (cl == NULL) {
        return NULL;
    }

    cl->buf = b;
    cl->next = NULL;

    return cl;
}
static ngx_chain_t *
ngx_http_tfs_create_write_meta_message(ngx_http_tfs_t *t)
{
    u_char                                 *p;
    size_t                                  size, frag_size;
    ngx_buf_t                              *b;
    ngx_int_t                               need_write_frag_count, i;
    ngx_chain_t                            *cl;
    ngx_http_tfs_restful_ctx_t             *r_ctx;
    ngx_http_tfs_segment_data_t            *segment_data;
    ngx_http_tfs_meta_frag_info_t          *wfi;
    ngx_http_tfs_ms_base_msg_header_t      *req;

    r_ctx = &t->r_ctx;
    need_write_frag_count = t->file.segment_index - t->file.last_write_segment_index ;
    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, t->log, 0 ,"last_write_segment_index: %uD, segment_index: %uD",
                  t->file.last_write_segment_index, t->file.segment_index);

    frag_size = sizeof(ngx_http_tfs_meta_frag_info_t) +
        sizeof(ngx_http_tfs_meta_frag_meta_info_t) * need_write_frag_count;

    size = sizeof(ngx_http_tfs_ms_base_msg_header_t) +
        r_ctx->file_path_s.len + 1 +
        /* version */
        sizeof(uint64_t) +
        frag_size;

    b = ngx_create_temp_buf(t->pool, size);
    if (b == NULL) {
        return NULL;
    }

    req = (ngx_http_tfs_ms_base_msg_header_t *) b->pos;
    req->header.type = NGX_HTTP_TFS_WRITE_FILEPATH_MESSAGE;
    req->header.flag = NGX_HTTP_TFS_PACKET_FLAG;
    req->header.version = NGX_HTTP_TFS_PACKET_VERSION;
    req->header.id = ngx_http_tfs_generate_packet_id();
    req->app_id = r_ctx->app_id;
    req->user_id = r_ctx->user_id;
    req->file_len = r_ctx->file_path_s.len + 1;
    p = ngx_cpymem(req->file_path_s, r_ctx->file_path_s.data, r_ctx->file_path_s.len + 1);

    *((uint64_t *)p) = t->loc_conf->meta_server_table.version;

    wfi = (ngx_http_tfs_meta_frag_info_t*)(p + sizeof(uint64_t));
    wfi->cluster_id = t->file.cluster_id;
    wfi->frag_count = need_write_frag_count;
    segment_data = &t->file.segment_data[t->file.last_write_segment_index];
    for (i = 0; i < need_write_frag_count; i++) {
#if (NGX_DEBUG)
        ngx_http_tfs_dump_segment_data(segment_data, t->log);
#endif
        wfi->frag_meta[i].block_id = segment_data->segment_info.block_id;
        wfi->frag_meta[i].file_id = segment_data->segment_info.file_id;
        wfi->frag_meta[i].offset = segment_data->segment_info.offset;
        wfi->frag_meta[i].size = segment_data->segment_info.size;
        segment_data++;
    }
    t->file.last_write_segment_index += need_write_frag_count;

    b->last += size;

    req->header.len = size - sizeof(ngx_http_tfs_header_t);
    req->header.crc = ngx_http_tfs_crc(NGX_HTTP_TFS_PACKET_FLAG,
                                       (const char *) (&req->header + 1),
                                       size - sizeof(ngx_http_tfs_header_t));

    cl = ngx_alloc_chain_link(t->pool);
    if (cl == NULL) {
        return NULL;
    }

    cl->buf = b;
    cl->next = NULL;

    return cl;
}
ngx_chain_t *
ngx_http_tfs_create_keepalive_message(ngx_http_tfs_t *t)
{
    u_char                         *p;
    ssize_t                         size, base_size;
    ngx_buf_t                      *b;
    ngx_queue_t                    *q, *queue;
    ngx_chain_t                    *cl, **ll;
    ngx_http_tfs_rc_ctx_t          *rc_ctx;
    ngx_http_tfs_header_t          *header;
    ngx_http_tfs_rcs_info_t        *rc_info;

    rc_ctx = t->main_conf->rc_ctx;
    ll = NULL;
    cl = NULL;

    base_size = sizeof(ngx_http_tfs_header_t)
        /* session id and client version len */
        + sizeof(uint32_t) * 2
        /* client version */
        + sizeof(NGX_HTTP_TFS_CLIENT_VERSION)
        /* cache_size cache_time modify_time */
        + sizeof(uint64_t) * 3
        /* is_logout */
        + sizeof(uint8_t)
        /* stat info */
        + sizeof(uint32_t)
        + sizeof(uint64_t)
        /* last_report_time */
        + sizeof(uint64_t);

    queue = &rc_ctx->sh->kp_queue;
    if (ngx_queue_empty(queue)) {
        ngx_shmtx_unlock(&rc_ctx->shpool->mutex);
        return NULL;
    }

    q = t->curr_ka_queue;
    if (q == NULL) {
        q = ngx_queue_head(queue);
        t->curr_ka_queue = q;
    }

    rc_info = ngx_queue_data(q, ngx_http_tfs_rcs_info_t, kp_queue);

    ngx_log_error(NGX_LOG_INFO, t->log, 0,
        "will do keepalive for appkey: %V", &rc_info->appkey);

    size = base_size + rc_info->session_id.len + 1;
    b = ngx_create_temp_buf(t->pool, size);
    if (b == NULL) {
        goto keepalive_create_error;
    }

    header = (ngx_http_tfs_header_t *) b->pos;
    header->flag = NGX_HTTP_TFS_PACKET_FLAG;
    header->len = size - sizeof(ngx_http_tfs_header_t);
    header->type = NGX_HTTP_TFS_REQ_RC_KEEPALIVE_MESSAGE;
    header->version = NGX_HTTP_TFS_PACKET_VERSION;
    header->id = ngx_http_tfs_generate_packet_id();

    p = (u_char *)(header + 1);

    /* include '\0' */
    *((uint32_t *) p) = rc_info->session_id.len + 1;
    p += sizeof(uint32_t);

    p = ngx_cpymem(p, rc_info->session_id.data, rc_info->session_id.len);
    *p = '\0';
    p += sizeof(uint8_t);

    *((uint32_t *) p) = sizeof(NGX_HTTP_TFS_CLIENT_VERSION);
    p += sizeof(uint32_t);

    p = ngx_cpymem(p, NGX_HTTP_TFS_CLIENT_VERSION, sizeof(NGX_HTTP_TFS_CLIENT_VERSION));

    ngx_memzero(p, sizeof(uint64_t) * 4 + sizeof(uint32_t) + sizeof(uint8_t));

    /* cache_size cache_time */
    p += sizeof(uint64_t) * 2;

    *((uint64_t *) p) = rc_info->modify_time;

    /* modify_time is_logout stat_info */
    p += sizeof(uint64_t) * 3 + sizeof(uint32_t) + sizeof(uint8_t);

    header->crc = ngx_http_tfs_crc(NGX_HTTP_TFS_PACKET_FLAG,
        (const char *) (header + 1), header->len);

    b->last += size;

    if (ll == NULL) {
        cl = ngx_alloc_chain_link(t->pool);
        if (cl == NULL) {
            goto keepalive_create_error;
        }

        cl->next = NULL;
        ll = &cl->next;
        cl->buf = b;

    } else {
        *ll = ngx_alloc_chain_link(t->pool);
        if (*ll == NULL) {
            goto keepalive_create_error;
        }

        (*ll)->next = NULL;
        (*ll)->buf = b;
        ll = &((*ll)->next);
    }

    return cl;

keepalive_create_error:
    return NULL;
}