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; }
/* This function assigns a server to session <s> if required, and can add the * connection to either the assigned server's queue or to the proxy's queue. * If ->srv_conn is set, the session is first released from the server. * It may also be called with SN_DIRECT and/or SN_ASSIGNED though. It will * be called before any connection and after any retry or redispatch occurs. * * It is not allowed to call this function with a session in a queue. * * Returns : * * SRV_STATUS_OK if everything is OK. * SRV_STATUS_NOSRV if no server is available. objt_server(s->target) = NULL. * SRV_STATUS_QUEUED if the connection has been queued. * SRV_STATUS_FULL if the server(s) is/are saturated and the * connection could not be queued at the server's, * which may be NULL if we queue on the backend. * SRV_STATUS_INTERNAL for other unrecoverable errors. * */ int assign_server_and_queue(struct session *s) { struct pendconn *p; struct server *srv; int err; if (s->pend_pos) return SRV_STATUS_INTERNAL; err = SRV_STATUS_OK; if (!(s->flags & SN_ASSIGNED)) { struct server *prev_srv = objt_server(s->target); err = assign_server(s); if (prev_srv) { /* This session was previously assigned to a server. We have to * update the session's and the server's stats : * - if the server changed : * - set TX_CK_DOWN if txn.flags was TX_CK_VALID * - set SN_REDISP if it was successfully redispatched * - increment srv->redispatches and be->redispatches * - if the server remained the same : update retries. */ if (prev_srv != objt_server(s->target)) { if ((s->txn.flags & TX_CK_MASK) == TX_CK_VALID) { s->txn.flags &= ~TX_CK_MASK; s->txn.flags |= TX_CK_DOWN; } s->flags |= SN_REDISP; prev_srv->counters.redispatches++; s->be->be_counters.redispatches++; } else { prev_srv->counters.retries++; s->be->be_counters.retries++; } } } switch (err) { case SRV_STATUS_OK: /* we have SN_ASSIGNED set */ srv = objt_server(s->target); if (!srv) return SRV_STATUS_OK; /* dispatch or proxy mode */ /* If we already have a connection slot, no need to check any queue */ if (s->srv_conn == srv) return SRV_STATUS_OK; /* OK, this session already has an assigned server, but no * connection slot yet. Either it is a redispatch, or it was * assigned from persistence information (direct mode). */ if ((s->flags & SN_REDIRECTABLE) && srv->rdr_len) { /* server scheduled for redirection, and already assigned. We * don't want to go further nor check the queue. */ sess_change_server(s, srv); /* not really needed in fact */ return SRV_STATUS_OK; } /* We might have to queue this session if the assigned server is full. * We know we have to queue it into the server's queue, so if a maxqueue * is set on the server, we must also check that the server's queue is * not full, in which case we have to return FULL. */ if (srv->maxconn && (srv->nbpend || srv->served >= srv_dynamic_maxconn(srv))) { if (srv->maxqueue > 0 && srv->nbpend >= srv->maxqueue) return SRV_STATUS_FULL; p = pendconn_add(s); if (p) return SRV_STATUS_QUEUED; else return SRV_STATUS_INTERNAL; } /* OK, we can use this server. Let's reserve our place */ sess_change_server(s, srv); return SRV_STATUS_OK; case SRV_STATUS_FULL: /* queue this session into the proxy's queue */ p = pendconn_add(s); if (p) return SRV_STATUS_QUEUED; else return SRV_STATUS_INTERNAL; case SRV_STATUS_NOSRV: return err; case SRV_STATUS_INTERNAL: return err; default: return SRV_STATUS_INTERNAL; } }
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; }