int srv_redispatch_connect(struct session *s) { struct server *srv; int conn_err; /* We know that we don't have any connection pending, so we will * try to get a new one, and wait in this state if it's queued */ redispatch: conn_err = assign_server_and_queue(s); srv = objt_server(s->target); switch (conn_err) { case SRV_STATUS_OK: break; case SRV_STATUS_FULL: /* The server has reached its maxqueue limit. Either PR_O_REDISP is set * and we can redispatch to another server, or it is not and we return * 503. This only makes sense in DIRECT mode however, because normal LB * algorithms would never select such a server, and hash algorithms * would bring us on the same server again. Note that s->target is set * in this case. */ if (((s->flags & (SN_DIRECT|SN_FORCE_PRST)) == SN_DIRECT) && (s->be->options & PR_O_REDISP)) { s->flags &= ~(SN_DIRECT | SN_ASSIGNED | SN_ADDR_SET); goto redispatch; } if (!s->si[1].err_type) { s->si[1].err_type = SI_ET_QUEUE_ERR; } srv->counters.failed_conns++; s->be->be_counters.failed_conns++; return 1; case SRV_STATUS_NOSRV: /* note: it is guaranteed that srv == NULL here */ if (!s->si[1].err_type) { s->si[1].err_type = SI_ET_CONN_ERR; } s->be->be_counters.failed_conns++; return 1; case SRV_STATUS_QUEUED: s->si[1].exp = tick_add_ifset(now_ms, s->be->timeout.queue); s->si[1].state = SI_ST_QUE; /* do nothing else and do not wake any other session up */ return 1; case SRV_STATUS_INTERNAL: default: if (!s->si[1].err_type) { s->si[1].err_type = SI_ET_CONN_OTHER; } if (srv) srv_inc_sess_ctr(srv); if (srv) srv_set_sess_last(srv); if (srv) srv->counters.failed_conns++; s->be->be_counters.failed_conns++; /* release other sessions waiting for this server */ if (may_dequeue_tasks(srv, s->be)) process_srv_queue(srv); return 1; } /* if we get here, it's because we got SRV_STATUS_OK, which also * means that the connection has not been queued. */ return 0; }
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; }