ngx_int_t hustdb_ha_post_peer(
    hustdb_ha_check_parameter_t check_parameter,
    ngx_str_t * backend_uri,
    ngx_http_request_t *r)
{
    hustdb_ha_peer_ctx_t * ctx = ngx_http_get_addon_module_ctx(r);
    if (!ctx)
    {
        if (check_parameter && !check_parameter(backend_uri, r))
        {
            return NGX_ERROR;
        }
        return __post_peer(backend_uri, r);
    }

    if (ctx->keys)
    {
        if (!hustdb_ha_add_keys_to_header(ctx->keys, r))
        {
            return ngx_http_send_response_imp(NGX_HTTP_NOT_FOUND, NULL, r);
        }
    }
    return (NGX_HTTP_OK == r->headers_out.status) ? ngx_http_send_response_imp(
        r->headers_out.status, &ctx->base.response, r) : ngx_http_send_response_imp(
            NGX_HTTP_NOT_FOUND, NULL, r);
}
ngx_int_t hustdb_ha_write_handler(
    uint8_t method,
    ngx_bool_t support_post_only,
    ngx_bool_t key_in_body,
    ngx_bool_t has_tb,
    ngx_bool_t check_body_len,
    ngx_str_t * backend_uri,
    ngx_http_request_t *r,
    hustdb_ha_start_write_t start_write)
{
    ngx_http_hustdb_ha_write_ctx_t * ctx = ngx_http_get_addon_module_ctx(r);
    if (!ctx)
    {
        return start_write(support_post_only, key_in_body, has_tb, check_body_len, backend_uri, r);
    }
    if (STATE_WRITE_MASTER1 == ctx->state)
    {
        return __on_write_master1_complete(method, has_tb, r, ctx);
    }
    else if (STATE_WRITE_MASTER2 == ctx->state)
    {
        return __on_write_master2_complete(method, has_tb, r, ctx);
    }
    return NGX_ERROR;
}
static void __post_body_handler(ngx_http_request_t * r)
{
    hustmq_ha_put_ctx_t * ctx = ngx_http_get_addon_module_ctx(r);
    --r->main->count;
    ngx_http_gen_subrequest(ctx->base.backend_uri, r, NULL,
        &ctx->base, ngx_http_post_subrequest_handler);
}
static void __post_handler(ngx_http_request_t *r)
{
    --r->main->count;

    ngx_http_hustdb_ha_write_ctx_t * ctx = ngx_http_get_addon_module_ctx(r);

    ngx_bool_t rc = true;
    if (ctx->key_in_body)
    {
        if (!__init_write_ctx_by_body(r, ctx))
        {
            rc = false;
        }
    }
    else
    {
        if (ctx->check_body_len)
        {
            rc = hustdb_ha_check_body(r);
        }
    }

    if (!rc)
    {
        hustdb_ha_send_response(NGX_HTTP_NOT_FOUND, NULL, NULL, r);
        return;
    }

    ngx_http_gen_subrequest(
        ctx->base.base.backend_uri,
        r,
        ctx->base.peer->peer,
        &ctx->base.base,
        hustdb_ha_on_subrequest_complete);
}
ngx_int_t hustdb_ha_read_handler(
    ngx_bool_t read_body,
    ngx_bool_t key_in_body,
    hustdb_ha_check_parameter_t check_parameter,
    ngx_str_t * backend_uri,
    ngx_http_request_t *r)
{
	hustdb_ha_ctx_t * ctx = ngx_http_get_addon_module_ctx(r);
	if (!ctx)
	{
	    if (read_body && !(r->method & NGX_HTTP_POST))
	    {
	        return NGX_HTTP_NOT_ALLOWED;
	    }
	    if (check_parameter && !check_parameter(backend_uri, r))
        {
            return NGX_ERROR;
        }
	    return read_body ? __post_read(key_in_body, backend_uri, r) : __start_read(backend_uri, r);
	}
	if (NGX_HTTP_OK != r->headers_out.status)
	{
		ctx->peer = ngx_http_get_next_peer(ctx->peer);
		return ctx->peer ? ngx_http_run_subrequest(
				r, &ctx->base, ctx->peer->peer) : hustdb_ha_send_response(
						NGX_HTTP_NOT_FOUND, NULL, NULL, r);
	}
	return hustdb_ha_send_response(NGX_HTTP_OK, ctx->version, &ctx->base.response, r);
}
ngx_int_t hustmq_ha_put_handler(ngx_str_t * backend_uri, ngx_http_request_t *r)
{
	hustmq_ha_put_ctx_t * ctx = ngx_http_get_addon_module_ctx(r);
	if (!ctx)
	{
		return __first_put_handler(backend_uri, r);
	}
	if (NGX_HTTP_OK != r->headers_out.status)
	{
		return (++ctx->count < ctx->peer_count) ? ngx_http_run_subrequest(r, &ctx->base, NULL) : NGX_ERROR;
	}
	return ngx_http_send_response_imp(NGX_HTTP_OK, &ctx->base.response, r);
}
ngx_int_t hustdb_ha_zwrite_handler(
    uint8_t method,
    ngx_str_t * backend_uri,
    ngx_http_request_t *r)
{
    ngx_http_hustdb_ha_write_ctx_t * ctx = ngx_http_get_addon_module_ctx(r);
    if (!ctx)
    {
        return __start_zwrite(backend_uri, r);
    }
    if (STATE_WRITE_MASTER1 == ctx->state)
    {
        return __on_write_master1_complete(method, true, r, ctx);
    }
    else if (STATE_WRITE_MASTER2 == ctx->state)
    {
        return __on_write_master2_complete(method, true, r, ctx);
    }
    return NGX_ERROR;
}
static void __post_body_handler(ngx_http_request_t *r)
{
    --r->main->count;

    ngx_http_hustdb_ha_write_ctx_t * ctx = ngx_http_get_addon_module_ctx(r);

    ctx->base.key = hustdb_ha_get_key(r);
    if (!ctx->base.key)
    {
        hustdb_ha_send_response(NGX_HTTP_NOT_FOUND, NULL, NULL, r);
        return;
    }

    ngx_http_gen_subrequest(
        ctx->base.base.backend_uri,
        r,
        ctx->base.peer->peer,
        &ctx->base.base,
        hustdb_ha_on_subrequest_complete);
}
ngx_int_t hustdb_ha_loop_handler(
    hustdb_ha_check_parameter_t check_parameter,
    ngx_str_t * backend_uri,
    ngx_http_request_t *r)
{
    hustdb_ha_loop_ctx_t * ctx = ngx_http_get_addon_module_ctx(r);
    if (!ctx)
    {
        return __first_loop(check_parameter, backend_uri, r);
    }
    if (NGX_HTTP_OK != r->headers_out.status)
    {
        return ngx_http_send_response_imp(NGX_HTTP_NOT_FOUND, NULL, r);
    }
    ctx->peer = ngx_http_next_peer(ctx->peer);
    if (ctx->peer)
    {
        return ngx_http_run_subrequest(r, &ctx->base, ctx->peer);
    }
    return ngx_http_send_response_imp(NGX_HTTP_OK, &ctx->base.response, r);
}
static void __post_handler(ngx_http_request_t *r)
{
    --r->main->count;
    hustdb_ha_ctx_t * ctx = ngx_http_get_addon_module_ctx(r);

    ngx_http_subrequest_peer_t * peer = __get_readable_peer(ctx->key_in_body, r);
    if (!peer)
    {
        hustdb_ha_send_response(NGX_HTTP_NOT_FOUND, NULL, NULL, r);
    }
    else
    {
        ctx->peer = peer;

        ngx_http_gen_subrequest(
            ctx->base.backend_uri,
            r,
            peer->peer,
            &ctx->base,
            hustdb_ha_on_subrequest_complete);
    }
}
static ngx_int_t __init_peer(ngx_http_request_t * r, ngx_http_upstream_srv_conf_t * us)
{
    if (!r || !r->parent)
    {
        return NGX_ERROR;
    }

    ngx_http_peer_selector_peer_data_t  *peer_data = ngx_palloc(r->pool, sizeof(ngx_http_peer_selector_peer_data_t));
    if (!peer_data)
    {
        return NGX_ERROR;
    }
    peer_data->peer = ngx_http_get_addon_module_ctx(r);
    r->upstream->peer.data = &peer_data->rrp;
    if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK)
    {
        return NGX_ERROR;
    }

    r->upstream->peer.get = __get_peer;

    return NGX_OK;
}