int assign_server(struct session *s) { struct connection *conn; struct server *conn_slot; struct server *srv, *prev_srv; int err; DPRINTF(stderr,"assign_server : s=%p\n",s); err = SRV_STATUS_INTERNAL; if (unlikely(s->pend_pos || s->flags & SN_ASSIGNED)) goto out_err; prev_srv = objt_server(s->target); conn_slot = s->srv_conn; /* We have to release any connection slot before applying any LB algo, * otherwise we may erroneously end up with no available slot. */ if (conn_slot) sess_change_server(s, NULL); /* We will now try to find the good server and store it into <objt_server(s->target)>. * Note that <objt_server(s->target)> may be NULL in case of dispatch or proxy mode, * as well as if no server is available (check error code). */ srv = NULL; s->target = NULL; conn = objt_conn(s->si[1].end); if (conn && (conn->flags & CO_FL_CONNECTED) && objt_server(conn->target) && __objt_server(conn->target)->proxy == s->be && ((s->txn.flags & TX_PREFER_LAST) || ((s->be->options & PR_O_PREF_LAST) && (!s->be->max_ka_queue || server_has_room(__objt_server(conn->target)) || (__objt_server(conn->target)->nbpend + 1) < s->be->max_ka_queue))) && srv_is_usable(__objt_server(conn->target))) { /* This session was relying on a server in a previous request * and the proxy has "option prefer-last-server" set, so * let's try to reuse the same server. */ srv = __objt_server(conn->target); s->target = &srv->obj_type; } else if (s->be->lbprm.algo & BE_LB_KIND) { /* we must check if we have at least one server available */ if (!s->be->lbprm.tot_weight) { err = SRV_STATUS_NOSRV; goto out; } /* First check whether we need to fetch some data or simply call * the LB lookup function. Only the hashing functions will need * some input data in fact, and will support multiple algorithms. */ switch (s->be->lbprm.algo & BE_LB_LKUP) { case BE_LB_LKUP_RRTREE: srv = fwrr_get_next_server(s->be, prev_srv); break; case BE_LB_LKUP_FSTREE: srv = fas_get_next_server(s->be, prev_srv); break; case BE_LB_LKUP_LCTREE: srv = fwlc_get_next_server(s->be, prev_srv); break; case BE_LB_LKUP_CHTREE: case BE_LB_LKUP_MAP: if ((s->be->lbprm.algo & BE_LB_KIND) == BE_LB_KIND_RR) { if (s->be->lbprm.algo & BE_LB_LKUP_CHTREE) srv = chash_get_next_server(s->be, prev_srv); else srv = map_get_server_rr(s->be, prev_srv); break; } else if ((s->be->lbprm.algo & BE_LB_KIND) != BE_LB_KIND_HI) { /* unknown balancing algorithm */ err = SRV_STATUS_INTERNAL; goto out; } switch (s->be->lbprm.algo & BE_LB_PARM) { case BE_LB_HASH_SRC: conn = objt_conn(s->si[0].end); if (conn && conn->addr.from.ss_family == AF_INET) { srv = get_server_sh(s->be, (void *)&((struct sockaddr_in *)&conn->addr.from)->sin_addr, 4); } else if (conn && conn->addr.from.ss_family == AF_INET6) { srv = get_server_sh(s->be, (void *)&((struct sockaddr_in6 *)&conn->addr.from)->sin6_addr, 16); } else { /* unknown IP family */ err = SRV_STATUS_INTERNAL; goto out; } break; case BE_LB_HASH_URI: /* URI hashing */ if (s->txn.req.msg_state < HTTP_MSG_BODY) break; srv = get_server_uh(s->be, b_ptr(s->req.buf, -http_uri_rewind(&s->txn.req)), s->txn.req.sl.rq.u_l); break; case BE_LB_HASH_PRM: /* URL Parameter hashing */ if (s->txn.req.msg_state < HTTP_MSG_BODY) break; srv = get_server_ph(s->be, b_ptr(s->req.buf, -http_uri_rewind(&s->txn.req)), s->txn.req.sl.rq.u_l); if (!srv && s->txn.meth == HTTP_METH_POST) srv = get_server_ph_post(s); break; case BE_LB_HASH_HDR: /* Header Parameter hashing */ if (s->txn.req.msg_state < HTTP_MSG_BODY) break; srv = get_server_hh(s); break; case BE_LB_HASH_RDP: /* RDP Cookie hashing */ srv = get_server_rch(s); break; default: /* unknown balancing algorithm */ err = SRV_STATUS_INTERNAL; goto out; } /* If the hashing parameter was not found, let's fall * back to round robin on the map. */ if (!srv) { if (s->be->lbprm.algo & BE_LB_LKUP_CHTREE) srv = chash_get_next_server(s->be, prev_srv); else srv = map_get_server_rr(s->be, prev_srv); } /* end of map-based LB */ break; default: /* unknown balancing algorithm */ err = SRV_STATUS_INTERNAL; goto out; } if (!srv) { err = SRV_STATUS_FULL; goto out; } else if (srv != prev_srv) { s->be->be_counters.cum_lbconn++; srv->counters.cum_lbconn++; } s->target = &srv->obj_type; } else if (s->be->options & (PR_O_DISPATCH | PR_O_TRANSP)) { s->target = &s->be->obj_type; } else if ((s->be->options & PR_O_HTTP_PROXY) && (conn = objt_conn(s->si[1].end)) && is_addr(&conn->addr.to)) { /* in proxy mode, we need a valid destination address */ s->target = &s->be->obj_type; } else { err = SRV_STATUS_NOSRV; goto out; } s->flags |= SN_ASSIGNED; err = SRV_STATUS_OK; out: /* Either we take back our connection slot, or we offer it to someone * else if we don't need it anymore. */ if (conn_slot) { if (conn_slot == srv) { sess_change_server(s, srv); } else { if (may_dequeue_tasks(conn_slot, s->be)) process_srv_queue(conn_slot); } } out_err: return err; }
int assign_server(struct session *s) { struct server *conn_slot; int err; #ifdef DEBUG_FULL fprintf(stderr,"assign_server : s=%p\n",s); #endif err = SRV_STATUS_INTERNAL; if (unlikely(s->pend_pos || s->flags & SN_ASSIGNED)) goto out_err; s->prev_srv = s->prev_srv; conn_slot = s->srv_conn; /* We have to release any connection slot before applying any LB algo, * otherwise we may erroneously end up with no available slot. */ if (conn_slot) sess_change_server(s, NULL); /* We will now try to find the good server and store it into <s->srv>. * Note that <s->srv> may be NULL in case of dispatch or proxy mode, * as well as if no server is available (check error code). */ s->srv = NULL; if (s->be->lbprm.algo & BE_LB_KIND) { /* we must check if we have at least one server available */ if (!s->be->lbprm.tot_weight) { err = SRV_STATUS_NOSRV; goto out; } /* First check whether we need to fetch some data or simply call * the LB lookup function. Only the hashing functions will need * some input data in fact, and will support multiple algorithms. */ switch (s->be->lbprm.algo & BE_LB_LKUP) { case BE_LB_LKUP_RRTREE: s->srv = fwrr_get_next_server(s->be, s->prev_srv); break; case BE_LB_LKUP_LCTREE: s->srv = fwlc_get_next_server(s->be, s->prev_srv); break; case BE_LB_LKUP_CHTREE: case BE_LB_LKUP_MAP: if ((s->be->lbprm.algo & BE_LB_KIND) == BE_LB_KIND_RR) { if (s->be->lbprm.algo & BE_LB_LKUP_CHTREE) s->srv = chash_get_next_server(s->be, s->prev_srv); else s->srv = map_get_server_rr(s->be, s->prev_srv); break; } else if ((s->be->lbprm.algo & BE_LB_KIND) != BE_LB_KIND_HI) { /* unknown balancing algorithm */ err = SRV_STATUS_INTERNAL; goto out; } switch (s->be->lbprm.algo & BE_LB_PARM) { case BE_LB_HASH_SRC: if (s->cli_addr.ss_family == AF_INET) s->srv = get_server_sh(s->be, (void *)&((struct sockaddr_in *)&s->cli_addr)->sin_addr, 4); else if (s->cli_addr.ss_family == AF_INET6) s->srv = get_server_sh(s->be, (void *)&((struct sockaddr_in6 *)&s->cli_addr)->sin6_addr, 16); else { /* unknown IP family */ err = SRV_STATUS_INTERNAL; goto out; } break; case BE_LB_HASH_URI: /* URI hashing */ if (s->txn.req.msg_state < HTTP_MSG_BODY) break; s->srv = get_server_uh(s->be, s->txn.req.sol + s->txn.req.sl.rq.u, s->txn.req.sl.rq.u_l); break; case BE_LB_HASH_PRM: /* URL Parameter hashing */ if (s->txn.req.msg_state < HTTP_MSG_BODY) break; s->srv = get_server_ph(s->be, s->txn.req.sol + s->txn.req.sl.rq.u, s->txn.req.sl.rq.u_l); if (!s->srv && s->txn.meth == HTTP_METH_POST) s->srv = get_server_ph_post(s); break; case BE_LB_HASH_HDR: /* Header Parameter hashing */ if (s->txn.req.msg_state < HTTP_MSG_BODY) break; s->srv = get_server_hh(s); break; case BE_LB_HASH_RDP: /* RDP Cookie hashing */ s->srv = get_server_rch(s); break; default: /* unknown balancing algorithm */ err = SRV_STATUS_INTERNAL; goto out; } /* If the hashing parameter was not found, let's fall * back to round robin on the map. */ if (!s->srv) { if (s->be->lbprm.algo & BE_LB_LKUP_CHTREE) s->srv = chash_get_next_server(s->be, s->prev_srv); else s->srv = map_get_server_rr(s->be, s->prev_srv); } /* end of map-based LB */ break; default: /* unknown balancing algorithm */ err = SRV_STATUS_INTERNAL; goto out; } if (!s->srv) { err = SRV_STATUS_FULL; goto out; } else if (s->srv != s->prev_srv) { s->be->counters.cum_lbconn++; s->srv->counters.cum_lbconn++; } } else if (s->be->options & PR_O_HTTP_PROXY) { if (!s->srv_addr.sin_addr.s_addr) { err = SRV_STATUS_NOSRV; goto out; } } else if (!*(int *)&s->be->dispatch_addr.sin_addr && !(s->be->options & PR_O_TRANSP)) { err = SRV_STATUS_NOSRV; goto out; } s->flags |= SN_ASSIGNED; err = SRV_STATUS_OK; out: /* Either we take back our connection slot, or we offer it to someone * else if we don't need it anymore. */ if (conn_slot) { if (conn_slot == s->srv) { sess_change_server(s, s->srv); } else { if (may_dequeue_tasks(conn_slot, s->be)) process_srv_queue(conn_slot); } } out_err: return err; }