static ngx_int_t
ngx_http_upstream_init_zeromq_peer(ngx_http_request_t *r,
    ngx_http_upstream_srv_conf_t *us)
{
    ngx_http_upstream_zeromq_peer_data_t  *zp;

    zp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_zeromq_peer_data_t));
    if (zp == NULL) {
        return NGX_ERROR;
    }

    zp ->request = r;
    zp ->zmq = NULL;
    ngx_http_set_ctx(r, NULL, ngx_zeromq_module);

    r->upstream->peer.data = &zp->rrp;

    if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
        return NGX_ERROR;
    }

    r->upstream->peer.get = ngx_http_upstream_get_zeromq_peer;
    r->upstream->peer.free = ngx_http_upstream_free_zeromq_peer;

    return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_init_least_conn_peer(ngx_http_request_t *r,
    ngx_http_upstream_srv_conf_t *us)
{
    ngx_http_upstream_lc_peer_data_t     *lcp;
    ngx_http_upstream_least_conn_conf_t  *lcf;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "init least conn peer");

    lcf = ngx_http_conf_upstream_srv_conf(us,
                                          ngx_http_upstream_least_conn_module);

    lcp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_lc_peer_data_t));
    if (lcp == NULL) {
        return NGX_ERROR;
    }

    lcp->conns = lcf->conns;

    r->upstream->peer.data = &lcp->rrp;

    if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
        return NGX_ERROR;
    }

    r->upstream->peer.get = ngx_http_upstream_get_least_conn_peer;
    r->upstream->peer.free = ngx_http_upstream_free_least_conn_peer;

    lcp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;
    lcp->free_rr_peer = ngx_http_upstream_free_round_robin_peer;

    return NGX_OK;
}
static ngx_int_t
ngx_http_lua_balancer_init_peer(ngx_http_request_t *r,
    ngx_http_upstream_srv_conf_t *us)
{
    ngx_http_lua_srv_conf_t            *bcf;
    ngx_http_lua_balancer_peer_data_t  *bp;

    bp = ngx_pcalloc(r->pool, sizeof(ngx_http_lua_balancer_peer_data_t));
    if (bp == NULL) {
        return NGX_ERROR;
    }

    r->upstream->peer.data = &bp->rrp;

    if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
        return NGX_ERROR;
    }

    r->upstream->peer.get = ngx_http_lua_balancer_get_peer;
    r->upstream->peer.free = ngx_http_lua_balancer_free_peer;

#if (NGX_HTTP_SSL)
    r->upstream->peer.set_session = ngx_http_lua_balancer_set_session;
    r->upstream->peer.save_session = ngx_http_lua_balancer_save_session;
#endif

    bcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_lua_module);

    bp->conf = bcf;
    bp->request = r;

    return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_session_sticky_init_peer(ngx_http_request_t *r,
    ngx_http_upstream_srv_conf_t *us)
{
    ngx_int_t                          rc;
    ngx_http_ss_ctx_t                 *ctx;
    ngx_http_upstream_ss_srv_conf_t   *sscf;
    ngx_http_upstream_ss_peer_data_t  *sspd;

    sspd = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_ss_peer_data_t));
    if (sspd == NULL) {
        return NGX_ERROR;
    }

    r->upstream->peer.data = &sspd->rrp;
    rc = ngx_http_upstream_init_round_robin_peer(r, us);
    if (rc != NGX_OK) {
        return rc;
    }

    sscf = ngx_http_conf_upstream_srv_conf(us,
                                    ngx_http_upstream_session_sticky_module);
    ctx = ngx_http_get_module_ctx(r, ngx_http_upstream_session_sticky_module);
    if (ctx == NULL) {
        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_ss_ctx_t));
        if (ctx == NULL) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "session sticky ctx allocated failed");
            return NGX_ERROR;
        }

        ctx->sscf = sscf;

        ngx_http_set_ctx(r, ctx, ngx_http_upstream_session_sticky_module);

        rc = ngx_http_session_sticky_get_cookie(r);
        if (rc != NGX_OK) {
            return rc;
        }

    } else {
        if (ctx->sscf != sscf) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                "different sscf with header_handler");
        }
    }

    sspd->r = r;
    sspd->sscf = sscf;
    sspd->get_rr_peer = ngx_http_upstream_get_round_robin_peer;

    r->upstream->peer.data = sspd;
    r->upstream->peer.get = ngx_http_upstream_session_sticky_get_peer;

    return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_init_ip_hash_peer(ngx_http_request_t *r,
    ngx_http_upstream_srv_conf_t *us)
{
    struct sockaddr_in                     *sin;
#if (NGX_HAVE_INET6)
    struct sockaddr_in6                    *sin6;
#endif
    ngx_http_upstream_ip_hash_peer_data_t  *iphp;

    iphp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_ip_hash_peer_data_t));
    if (iphp == NULL) {
        return NGX_ERROR;
    }

    r->upstream->peer.data = &iphp->rrp;

    if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
        return NGX_ERROR;
    }

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

    switch (r->connection->sockaddr->sa_family) {

    case AF_INET:
        sin = (struct sockaddr_in *) r->connection->sockaddr;
        iphp->addr = (u_char *) &sin->sin_addr.s_addr;
        iphp->addrlen = 3;
        break;

#if (NGX_HAVE_INET6)
    case AF_INET6:
        sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;
        iphp->addr = (u_char *) &sin6->sin6_addr.s6_addr;
        iphp->addrlen = 16;
        break;
#endif

    default:
        iphp->addr = ngx_http_upstream_ip_hash_pseudo_addr;
        iphp->addrlen = 3;
    }

    iphp->hash = 89;
    iphp->tries = 0;
    iphp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;

    return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_init_least_conn_peer(ngx_http_request_t *r,
    ngx_http_upstream_srv_conf_t *us)
{
    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "init least conn peer");

    if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
        return NGX_ERROR;
    }

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

    return NGX_OK;
}
static ngx_int_t ngx_http_upstream_init_q_chash_peer(ngx_http_request_t *r, ngx_http_upstream_srv_conf_t *us)
{
    ngx_int_t                                       rc;
    ngx_http_upstream_q_chash_srv_conf_t            *uchscf;
    ngx_http_upstream_q_chash_peer_data_t           *qchp;
    ngx_http_upstream_q_chash_ring                  *q_chash_ring;
    ngx_str_t                                       evaluated_key_to_hash;

    uchscf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_q_chash_module);
    if (uchscf == NULL) {
        return NGX_ERROR;
    }

    q_chash_ring = uchscf->q_chash_ring;

    qchp = ngx_pcalloc(r->pool, sizeof(*qchp));
    if(qchp == NULL)
        return NGX_ERROR;
    r->upstream->peer.data = &qchp->rrp;

    qchp->q_chash_ring = q_chash_ring;
    qchp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;
    qchp->tries = 0;
    qchp->ignore = 0;
    qchp->rr_mode = 0;

    rc = ngx_http_upstream_init_round_robin_peer(r, us);
    if(rc != NGX_OK)
        return NGX_ERROR;

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

    // calculate the vnode_index
    if(q_chash_ring->nr_valid_peers > 1) {
        if (ngx_http_script_run(r, &evaluated_key_to_hash, uchscf->lengths->elts, 0, uchscf->values->elts) == NULL)
            return NGX_ERROR;

        qchp->point = (uint32_t)ngx_crc32_long(evaluated_key_to_hash.data, evaluated_key_to_hash.len);
        qchp->vnode_index = q_chash_find(q_chash_ring, qchp->point);

        ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "q_chash key %V, point %uD, vnode_index %ui", &evaluated_key_to_hash, qchp->point, qchp->vnode_index);
    }

    return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_init_consistent_hash_peer(ngx_http_request_t *r,
        ngx_http_upstream_srv_conf_t *us)
{
    ngx_str_t                                          evaluated_key_to_hash;
    ngx_http_upstream_consistent_hash_srv_conf_t      *uchscf;
    ngx_http_upstream_consistent_hash_peer_data_t     *uchpd;

    uchscf = ngx_http_conf_upstream_srv_conf(us,
            ngx_http_upstream_consistent_hash_module);
    if (uchscf == NULL) {
        return NGX_ERROR;
    }

    uchpd = ngx_pcalloc(r->pool, 
            sizeof(ngx_http_upstream_consistent_hash_peer_data_t));
    if (uchpd == NULL) {
        return NGX_ERROR;
    }

    r->upstream->peer.data = &uchpd->rrp;

    if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
        return NGX_ERROR;
    }

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

    uchpd->buckets = uchscf->data;
    uchpd->tries = 0;

    if (ngx_http_script_run(r, &evaluated_key_to_hash, 
                uchscf->lengths->elts, 0, uchscf->values->elts) == NULL)
    {
        return NGX_ERROR;
    }

    uchpd->point = 
        ngx_crc32_long(evaluated_key_to_hash.data, evaluated_key_to_hash.len);

    uchpd->get_rr_peer = ngx_http_upstream_get_round_robin_peer;

    return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_init_ip_hash_peer(ngx_http_request_t *r,
    ngx_http_upstream_srv_conf_t *us)
{
    u_char                                 *p;
    struct sockaddr_in                     *sin;
    ngx_http_upstream_ip_hash_peer_data_t  *iphp;

    iphp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_ip_hash_peer_data_t));
    if (iphp == NULL) {
        return NGX_ERROR;
    }

    r->upstream->peer.data = &iphp->rrp;

    if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
        return NGX_ERROR;
    }

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

    /* AF_INET only */

    if (r->connection->sockaddr->sa_family == AF_INET) {

        sin = (struct sockaddr_in *) r->connection->sockaddr;
        p = (u_char *) &sin->sin_addr.s_addr;
        iphp->addr[0] = p[0];
        iphp->addr[1] = p[1];
        iphp->addr[2] = p[2];

    } else {
        iphp->addr[0] = 0;
        iphp->addr[1] = 0;
        iphp->addr[2] = 0;
    }

    iphp->hash = 89;
    iphp->tries = 0;
    iphp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;

    return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_init_cookie_hash_peer(ngx_http_request_t *r,
                                        ngx_http_upstream_srv_conf_t *us)
{
    ngx_table_elt_t** cookies;
    ngx_http_upstream_cookie_hash_peer_data_t  *ckhp;

    ngx_uint_t i;

    ckhp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_cookie_hash_peer_data_t));
    if (ckhp == NULL) {
        return NGX_ERROR;
    }
    ngx_memset(ckhp->sess_id, 0, sizeof(ckhp->sess_id));


    r->upstream->peer.data = &ckhp->rrp;

    if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
        return NGX_ERROR;
    }
    r->upstream->peer.get = ngx_http_upstream_get_cookie_hash_peer;

    cookies = r->headers_in.cookies.elts;
    for(i=0; i<r->headers_in.cookies.nelts; i++) {
        if (0 == ngx_strcmp((u_char*)"Cookie", cookies[i]->key.data)) {
            if(0 == get_session_id(r, (char*)cookies[i]->value.data, "PHPSESSID", (char*)ckhp->sess_id)) {
                if (strlen((char*)ckhp->sess_id)>0) {
                    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "session id: %s", ckhp->sess_id);
                    break;
                }
            }
        }
    }

    ckhp->hash = 89;
    ckhp->tries = 0;
    ckhp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;

    return NGX_OK;
}
ngx_int_t
ngx_http_upstream_init_idempotent_peer(ngx_http_request_t *r,
    ngx_http_upstream_srv_conf_t *us)
{
    ngx_http_upstream_idempotent_peer_data_t  *iphp;

    iphp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_idempotent_peer_data_t));
    if (iphp == NULL) {
        return NGX_ERROR;
    }

    iphp->method = r->method;
    r->upstream->peer.data = &iphp->rrp;

    if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
        return NGX_ERROR;
    }

    r->upstream->peer.free = ngx_http_upstream_free_idempotent_peer;

    return NGX_OK;
}
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;
}
/*
 * function called by the upstream module when it inits each peer
 * it's called once per request
 */
static ngx_int_t
ngx_http_init_sticky_peer(ngx_http_request_t *r, ngx_http_upstream_srv_conf_t *us)
{
    ngx_http_sticky_peer_data_t  *iphp;
    ngx_str_t                     route;
    ngx_uint_t                    i;
    ngx_int_t                     n;

    /* alloc custom sticky struct */
    iphp = ngx_palloc(r->pool, sizeof(ngx_http_sticky_peer_data_t));
    if (iphp == NULL) {
        return NGX_ERROR;
    }

    /* attach it to the request upstream data */
    r->upstream->peer.data = &iphp->rrp;

    /* call the rr module on which the sticky is based on */
    if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
        return NGX_ERROR;
    }

    /* set the callback to select the next peer to use */
    r->upstream->peer.get = ngx_http_get_sticky_peer;

    /* init the custom sticky struct */
    iphp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;
    iphp->selected_peer = -1;
    iphp->no_fallback = 0;
    iphp->sticky_conf = ngx_http_conf_upstream_srv_conf(us,
                                                        ngx_http_sticky_module);
    iphp->request = r;

    /* check weather a cookie is present or not and save it */
    if (ngx_http_parse_multi_header_lines(&r->headers_in.cookies,
                                          &iphp->sticky_conf->cookie_name,
                                          &route) != NGX_DECLINED)
    {
        /* a route cookie has been found. Let's give it a try */
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "[sticky/init_sticky_peer] got cookie route=%V, "
                       "let's try to find a matching peer", &route);

        /* hash or hmac, just compare digest */
        if (iphp->sticky_conf->hash || iphp->sticky_conf->hmac) {

            /* search the digest found in the cookie in the peer digest list */
            for (i = 0; i < iphp->rrp.peers->number; i++) {

                /* ensure the both len are equal and > 0 */
                if (iphp->sticky_conf->peers[i].digest.len != route.len
                    || route.len <= 0) {

                    continue;
                }

                if (!ngx_strncmp(iphp->sticky_conf->peers[i].digest.data,
                                 route.data, route.len)) {

                    /* we found a match */
                    iphp->selected_peer = i;
                    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                                   "[sticky/init_sticky_peer] the route "
                                   "\"%V\"matches peer at index %ui",
                                   &route, i);
                    return NGX_OK;
                }
            }

        } else {

            /* 
             * switch back to index, just convert to integer and ensure it
             * corresponds to a valid peer
             */
            n = ngx_atoi(route.data, route.len);
            if (n == NGX_ERROR) {
                ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
                              "[sticky/init_sticky_peer] unable to convert "
                              "the route \"%V\" to an integer value", &route);
            } else if (n >= 0 && n < (ngx_int_t) iphp->rrp.peers->number) {
                /* found one */
                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                               "[sticky/init_sticky_peer] the route \"%V\" "
                               "matches peer at index %i", &route, n);
                iphp->selected_peer = n;
                return NGX_OK;
            }
        }

        /* nothing was found, just continue with rr */
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "[sticky/init_sticky_peer] the route \"%V\""
                       "does not match any peer. Just ignoring it ...", &route);
        return NGX_OK;
    }

    /* nothing found */
    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "[sticky/init_sticky_peer] route cookie not found");

    return NGX_OK; /* return OK, in order to continue */
}