diff --git a/haproxy.c b/haproxy.c index 5b96681712..10cb6e5d8b 100644 --- a/haproxy.c +++ b/haproxy.c @@ -405,6 +405,8 @@ int strlcpy2(char *dst, const char *src, int size) { /* various other session flags, bits values 0x400000 and above */ #define SN_MONITOR 0x00400000 /* this session comes from a monitoring system */ +#define SN_ASSIGNED 0x00800000 /* no need to assign a server to this session */ +#define SN_ADDR_SET 0x01000000 /* this session's server address has been set */ /* different possible states for the client side */ @@ -448,6 +450,13 @@ int strlcpy2(char *dst, const char *src, int size) { #define SRV_BIND_SRC 8 /* this server uses a specific source address */ #define SRV_CHECKED 16 /* this server needs to be checked */ +/* function which act on servers need to return various errors */ +#define SRV_STATUS_OK 0 /* everything is OK. */ +#define SRV_STATUS_INTERNAL 1 /* other unrecoverable errors. */ +#define SRV_STATUS_NOSRV 2 /* no server is available */ +#define SRV_STATUS_FULL 3 /* the/all server(s) are saturated */ +#define SRV_STATUS_QUEUED 4 /* the/all server(s) are saturated but the connection was queued */ + /* what to do when a header matches a regex */ #define ACT_ALLOW 0 /* allow the request */ #define ACT_REPLACE 1 /* replace the matching header */ @@ -622,6 +631,8 @@ struct proxy { int srvtimeout; /* server I/O timeout (in milliseconds) */ int contimeout; /* connect timeout (in milliseconds) */ char *id; /* proxy id */ + struct list pendconns; /* pending connections with no server assigned yet */ + int nbpend; /* number of pending connections with no server assigned yet */ int nbconn; /* # of active sessions */ unsigned int cum_conn; /* cumulated number of processed sessions */ int maxconn; /* max # of active sessions */ @@ -1781,57 +1792,94 @@ struct task *task_queue(struct task *task) { /*********************************************************************/ /* - * returns the first pending connection of server or NULL if none. + * Detaches pending connection

, decreases the pending count, and frees + * the pending connection. The connection might have been queued to a specific + * server as well as to the proxy. The session also gets marked unqueued. */ -static inline struct pendconn *pendconn_peek(struct server *s) { +static void pendconn_free(struct pendconn *p) { + LIST_DEL(&p->list); + p->sess->pend_pos = NULL; + if (p->srv) + p->srv->nbpend--; + else + p->sess->proxy->nbpend--; + pool_free(pendconn, p); +} + +/* Returns the first pending connection for server , which may be NULL if + * nothing is pending. + */ +static inline struct pendconn *pendconn_from_srv(struct server *s) { if (!s->nbpend) return NULL; return LIST_ELEM(s->pendconns.n, struct pendconn *, list); } -/* - * Detaches pending connection

, decreases the pending count, and frees - * the pending connection. +/* Returns the first pending connection for proxy , which may be NULL if + * nothing is pending. */ -static inline void pendconn_free(struct pendconn *p) { - LIST_DEL(&p->list); - p->sess->pend_pos = NULL; - p->srv->nbpend--; - pool_free(pendconn, p); +static inline struct pendconn *pendconn_from_px(struct proxy *px) { + if (!px->nbpend) + return NULL; + + return LIST_ELEM(px->pendconns.n, struct pendconn *, list); } -/* detaches the first pending connection for server and returns its - * associated session. If no pending connection is found, NULL is returned. +/* Detaches the next pending connection for either current session's server or + * current session's proxy, and returns its associated session. If no pending + * connection is found, NULL is returned. Note that cur->srv cannot be NULL. */ -static inline struct session *pendconn_get(struct server *s) { +static struct session *pendconn_get_next_sess(struct session *cur) { struct pendconn *p; struct session *sess; - p = pendconn_peek(s); - if (!p) - return NULL; + p = pendconn_from_srv(cur->srv); + if (!p) { + p = pendconn_from_px(cur->proxy); + if (!p) + return NULL; + p->sess->srv = cur->srv; + } sess = p->sess; pendconn_free(p); return sess; } -/* adds the session to the pending connection list of server . - * All counters and back pointers are updated accordingly. Returns NULL if - * no memory is available, otherwise the pendconn itself. +/* Checks if other sessions are waiting for the same server, and wakes the + * first one up. Note that cur->srv cannot be NULL. */ -static struct pendconn *pendconn_add(struct server *srv, struct session *sess) { +void offer_connection_slot(struct session *cur) { + struct session *sess; + + sess = pendconn_get_next_sess(cur); + if (sess == NULL) + return; + task_wakeup(&rq, sess->task); +} + +/* Adds the session to the pending connection list of server ->srv + * or to the one of ->proxy if srv is NULL. All counters and back pointers + * are updated accordingly. Returns NULL if no memory is available, otherwise the + * pendconn itself. + */ +static struct pendconn *pendconn_add(struct session *sess) { struct pendconn *p; p = pool_alloc(pendconn); if (!p) return NULL; - LIST_ADDQ(&srv->pendconns, &p->list); - p->sess = sess; - p->srv = srv; sess->pend_pos = p; - srv->nbpend++; + p->sess = sess; + p->srv = sess->srv; + if (sess->srv) { + LIST_ADDQ(&sess->srv->pendconns, &p->list); + sess->srv->nbpend++; + } else { + LIST_ADDQ(&sess->proxy->pendconns, &p->list); + sess->proxy->nbpend++; + } return p; } @@ -1860,7 +1908,7 @@ static int get_original_dst(int fd, struct sockaddr_in *sa, socklen_t *salen) { /* * frees the context associated to a session. It must have been removed first. */ -static inline void session_free(struct session *s) { +static void session_free(struct session *s) { if (s->pend_pos) pendconn_free(s->pend_pos); if (s->req) @@ -2053,60 +2101,99 @@ static inline struct server *get_server_sh(struct proxy *px, char *addr, int len /* - * This function initiates a connection to the current server (s->srv) if (s->direct) - * is set, or to the dispatch server if (s->direct) is 0. - * It can return one of : - * - SN_ERR_NONE if everything's OK - * - SN_ERR_SRVTO if there are no more servers - * - SN_ERR_SRVCL if the connection was refused by the server - * - SN_ERR_PRXCOND if the connection has been limited by the proxy (maxconn) - * - SN_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...) - * - SN_ERR_INTERNAL for any other purely internal errors - * Additionnally, in the case of SN_ERR_RESOURCE, an emergency log will be emitted. + * This function marks the session as 'assigned' in direct or dispatch modes, + * or tries to assign one in balance mode, according to the algorithm. It does + * nothing if the session had already been assigned a server. + * + * It may return : + * SRV_STATUS_OK if everything is OK. + * SRV_STATUS_NOSRV if no server is available + * SRV_STATUS_FULL if all servers are saturated + * SRV_STATUS_INTERNAL for other unrecoverable errors. + * + * Upon successful return, the session flag SN_ASSIGNED to indicate that it does + * not need to be called anymore. This usually means that s->srv can be trusted + * in balance and direct modes. This flag is not cleared, so it's to the caller + * to clear it if required (eg: redispatch). + * */ -int connect_server(struct session *s) { - int fd; +int assign_server(struct session *s) { #ifdef DEBUG_FULL - fprintf(stderr,"connect_server : s=%p\n",s); + fprintf(stderr,"assign_server : s=%p\n",s); #endif - if (s->flags & SN_DIRECT) { /* srv cannot be null */ - s->srv_addr = s->srv->addr; + if (s->pend_pos) + return SRV_STATUS_INTERNAL; + + if (!(s->flags & SN_ASSIGNED)) { + if ((s->proxy->options & PR_O_BALANCE) && !(s->flags & SN_DIRECT)) { + if (!s->proxy->srv_act && !s->proxy->srv_bck) + return SRV_STATUS_NOSRV; + + if (s->proxy->options & PR_O_BALANCE_RR) { + s->srv = get_server_rr_with_conns(s->proxy); + if (!s->srv) + return SRV_STATUS_FULL; + } + else if (s->proxy->options & PR_O_BALANCE_SH) { + int len; + + if (s->cli_addr.ss_family == AF_INET) + len = 4; + else if (s->cli_addr.ss_family == AF_INET6) + len = 16; + else /* unknown IP family */ + return SRV_STATUS_INTERNAL; + + s->srv = get_server_sh(s->proxy, + (void *)&((struct sockaddr_in *)&s->cli_addr)->sin_addr, + len); + } + else /* unknown balancing algorithm */ + return SRV_STATUS_INTERNAL; + } + s->flags |= SN_ASSIGNED; } - else if (s->proxy->options & PR_O_BALANCE) { - /* Ensure that srv will not be NULL */ - if (!s->proxy->srv_act && !s->proxy->srv_bck) - return SN_ERR_SRVTO; + return SRV_STATUS_OK; +} - if (s->proxy->options & PR_O_BALANCE_RR) { - struct server *srv; +/* + * This function assigns a server address to a session, and sets SN_ADDR_SET. + * The address is taken from the currently assigned server, or from the + * dispatch or transparent address. + * + * It may return : + * SRV_STATUS_OK if everything is OK. + * SRV_STATUS_INTERNAL for other unrecoverable errors. + * + * Upon successful return, the session flag SN_ADDR_SET is set. This flag is + * not cleared, so it's to the caller to clear it if required. + * + */ +int assign_server_address(struct session *s) { +#ifdef DEBUG_FULL + fprintf(stderr,"assign_server_address : s=%p\n",s); +#endif - srv = get_server_rr_with_conns(s->proxy); - if (!srv) - srv = get_server_rr(s->proxy); - s->srv_addr = srv->addr; - s->srv = srv; + if (s->flags & SN_DIRECT || s->proxy->options & PR_O_BALANCE) { + /* A server is necessarily known for this session */ + if (!(s->flags & SN_ASSIGNED)) + return SRV_STATUS_INTERNAL; + + s->srv_addr = s->srv->addr; + + /* if this server remaps proxied ports, we'll use + * the port the client connected to with an offset. */ + if (s->srv->state & SRV_MAPPORTS) { + struct sockaddr_in sockname; + socklen_t namelen = sizeof(sockname); + + if (!(s->proxy->options & PR_O_TRANSP) || + get_original_dst(s->cli_fd, (struct sockaddr_in *)&sockname, &namelen) == -1) + getsockname(s->cli_fd, (struct sockaddr *)&sockname, &namelen); + s->srv_addr.sin_port = htons(ntohs(s->srv_addr.sin_port) + ntohs(sockname.sin_port)); } - else if (s->proxy->options & PR_O_BALANCE_SH) { - struct server *srv; - int len; - - if (s->cli_addr.ss_family == AF_INET) - len = 4; - else if (s->cli_addr.ss_family == AF_INET6) - len = 16; - else /* unknown IP family */ - return SN_ERR_INTERNAL; - - srv = get_server_sh(s->proxy, - (void *)&((struct sockaddr_in *)&s->cli_addr)->sin_addr, - len); - s->srv_addr = srv->addr; - s->srv = srv; - } - else /* unknown balancing algorithm */ - return SN_ERR_INTERNAL; } else if (*(int *)&s->proxy->dispatch_addr.sin_addr) { /* connect to the defined dispatch addr */ @@ -2118,20 +2205,101 @@ int connect_server(struct session *s) { if (get_original_dst(s->cli_fd, &s->srv_addr, &salen) == -1) { qfprintf(stderr, "Cannot get original server address.\n"); - return SN_ERR_INTERNAL; + return SRV_STATUS_INTERNAL; } } - /* if this server remaps proxied ports, we'll use - * the port the client connected to with an offset. */ - if (s->srv != NULL && s->srv->state & SRV_MAPPORTS) { - struct sockaddr_in sockname; - socklen_t namelen = sizeof(sockname); + s->flags |= SN_ADDR_SET; + return SRV_STATUS_OK; +} - if (!(s->proxy->options & PR_O_TRANSP) || - get_original_dst(s->cli_fd, (struct sockaddr_in *)&sockname, &namelen) == -1) - getsockname(s->cli_fd, (struct sockaddr *)&sockname, &namelen); - s->srv_addr.sin_port = htons(ntohs(s->srv_addr.sin_port) + ntohs(sockname.sin_port)); +/* This function assigns a server to session if required, and can add the + * connection to either the assigned server's queue or to the proxy's queue. + * + * Returns : + * + * SRV_STATUS_OK if everything is OK. + * SRV_STATUS_NOSRV if no server is available + * 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. + * SRV_STATUS_INTERNAL for other unrecoverable errors. + * + */ +int assign_server_and_queue(struct session *s) { + struct pendconn *p; + int err; + + if (s->pend_pos) + return SRV_STATUS_INTERNAL; + + if (s->flags & SN_ASSIGNED) { + /* a server does not need to be assigned, perhaps because we're in + * direct mode, or in dispatch or transparent modes where the server + * is not needed. + */ + if (s->srv && + s->srv->maxconn && s->srv->cur_sess >= s->srv->maxconn) { + p = pendconn_add(s); + if (p) + return SRV_STATUS_QUEUED; + else + return SRV_STATUS_FULL; + } + return SRV_STATUS_OK; + } + + /* a server needs to be assigned */ + err = assign_server(s); + switch (err) { + case SRV_STATUS_OK: + /* in balance mode, we might have servers with connection limits */ + if (s->srv != NULL && + s->srv->maxconn && s->srv->cur_sess >= s->srv->maxconn) { + p = pendconn_add(s); + if (p) + return SRV_STATUS_QUEUED; + else + return SRV_STATUS_FULL; + } + 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_FULL; + + case SRV_STATUS_NOSRV: + case SRV_STATUS_INTERNAL: + return err; + default: + return SRV_STATUS_INTERNAL; + } +} + + +/* + * This function initiates a connection to the server assigned to this session + * (s->srv, s->srv_addr). It will assign a server if none is assigned yet. + * It can return one of : + * - SN_ERR_NONE if everything's OK + * - SN_ERR_SRVTO if there are no more servers + * - SN_ERR_SRVCL if the connection was refused by the server + * - SN_ERR_PRXCOND if the connection has been limited by the proxy (maxconn) + * - SN_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...) + * - SN_ERR_INTERNAL for any other purely internal errors + * Additionnally, in the case of SN_ERR_RESOURCE, an emergency log will be emitted. + */ +int connect_server(struct session *s) { + int fd, err; + + if (!(s->flags & SN_ADDR_SET)) { + err = assign_server_address(s); + if (err != SRV_STATUS_OK) + return SN_ERR_INTERNAL; } if ((fd = s->srv_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { @@ -3624,7 +3792,7 @@ int process_cli(struct session *t) { if (srv->state & SRV_RUNNING || t->proxy->options & PR_O_PERSIST) { /* we found the server and it's usable */ t->flags &= ~SN_CK_MASK; - t->flags |= SN_CK_VALID | SN_DIRECT; + t->flags |= SN_CK_VALID | SN_DIRECT | SN_ASSIGNED; t->srv = srv; break; } else { @@ -3879,7 +4047,7 @@ int process_cli(struct session *t) { if (srv->state & SRV_RUNNING || t->proxy->options & PR_O_PERSIST) { /* we found the server and it's usable */ t->flags &= ~SN_CK_MASK; - t->flags |= SN_CK_VALID | SN_DIRECT; + t->flags |= SN_CK_VALID | SN_DIRECT | SN_ASSIGNED; t->srv = srv; break; } else { @@ -3976,7 +4144,7 @@ int process_cli(struct session *t) { if (srv->state & SRV_RUNNING || t->proxy->options & PR_O_PERSIST) { /* we found the server and it's usable */ t->flags &= ~SN_CK_MASK; - t->flags |= SN_CK_VALID | SN_DIRECT; + t->flags |= SN_CK_VALID | SN_DIRECT | SN_ASSIGNED; t->srv = srv; break; } else { @@ -4312,6 +4480,150 @@ int process_cli(struct session *t) { return 0; } +/* This function turns the server state into the SV_STCLOSE, and sets + * indicators accordingly. Note that if is 0, no message is + * returned. + */ +void srv_close_with_err(struct session *t, int err, int finst, int status, int msglen, char *msg) { + t->srv_state = SV_STCLOSE; + if (status > 0) { + t->logs.status = status; + if (t->proxy->mode == PR_MODE_HTTP) + client_return(t, msglen, msg); + } + if (!(t->flags & SN_ERR_MASK)) + t->flags |= err; + if (!(t->flags & SN_FINST_MASK)) + t->flags |= finst; +} + +/* + * This function checks the retry count during the connect() job. + * It updates the session's srv_state and retries, so that the caller knows + * what it has to do. It uses the last connection error to set the log when + * it expires. It returns 1 when it has expired, and 0 otherwise. + */ +int srv_count_retry_down(struct session *t, int conn_err) { + /* we are in front of a retryable error */ + t->conn_retries--; + if (t->conn_retries < 0) { + /* if not retryable anymore, let's abort */ + tv_eternity(&t->cnexpire); + srv_close_with_err(t, conn_err, SN_FINST_C, + 503, t->proxy->errmsg.len503, t->proxy->errmsg.msg503); + + /* We used to have a free connection slot. Since we'll never use it, + * we have to pass it on to another session. + */ + if (t->srv) + offer_connection_slot(t); + return 1; + } + return 0; +} + +/* + * This function performs the retryable part of the connect() job. + * It updates the session's srv_state and retries, so that the caller knows + * what it has to do. It returns 1 when it breaks out of the loop, or 0 if + * it needs to redispatch. + */ +int srv_retryable_connect(struct session *t) { + int conn_err; + + /* This loop ensures that we stop before the last retry in case of a + * redispatchable server. + */ + do { + /* initiate a connection to the server */ + conn_err = connect_server(t); + switch (conn_err) { + + case SN_ERR_NONE: + //fprintf(stderr,"0: c=%d, s=%d\n", c, s); + t->srv_state = SV_STCONN; + return 1; + + case SN_ERR_INTERNAL: + tv_eternity(&t->cnexpire); + srv_close_with_err(t, SN_ERR_INTERNAL, SN_FINST_C, + 500, t->proxy->errmsg.len500, t->proxy->errmsg.msg500); + /* release other sessions waiting for this server */ + if (t->srv) + offer_connection_slot(t); + return 1; + } + /* ensure that we have enough retries left */ + if (srv_count_retry_down(t, conn_err)) + return 1; + } while (t->srv == NULL || t->conn_retries > 0 || !(t->proxy->options & PR_O_REDISP)); + + /* We're on our last chance, and the REDISP option was specified. + * We will ignore cookie and force to balance or use the dispatcher. + */ + t->flags &= ~(SN_DIRECT | SN_ASSIGNED | SN_ADDR_SET); + t->srv = NULL; /* it's left to the dispatcher to choose a server */ + if ((t->flags & SN_CK_MASK) == SN_CK_VALID) { + t->flags &= ~SN_CK_MASK; + t->flags |= SN_CK_DOWN; + } + return 0; +} + +/* This function performs the "redispatch" part of a connection attempt. It + * will assign a server if required, queue the connection if required, and + * handle errors that might arise at this level. It can change the server + * state. It will return 1 if it encounters an error, switches the server + * state, or has to queue a connection. Otherwise, it will return 0 indicating + * that the connection is ready to use. + */ + +int srv_redispatch_connect(struct session *t) { + 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 + */ + conn_err = assign_server_and_queue(t); + switch (conn_err) { + case SRV_STATUS_OK: + break; + + case SRV_STATUS_NOSRV: + tv_eternity(&t->cnexpire); + srv_close_with_err(t, SN_ERR_SRVTO, SN_FINST_C, + 503, t->proxy->errmsg.len503, t->proxy->errmsg.msg503); + + /* FIXME-20060501: we should not need this once we flush every session + * when the last server goes down. + */ + /* release other sessions waiting for this server */ + if (t->srv) + offer_connection_slot(t); + return 1; + + case SRV_STATUS_QUEUED: + t->srv_state = SV_STIDLE; + /* do nothing else and do not wake any other session up */ + return 1; + + case SRV_STATUS_FULL: + case SRV_STATUS_INTERNAL: + default: + tv_eternity(&t->cnexpire); + srv_close_with_err(t, SN_ERR_INTERNAL, SN_FINST_C, + 500, t->proxy->errmsg.len500, t->proxy->errmsg.msg500); + /* release other sessions waiting for this server */ + if (t->srv) + offer_connection_slot(t); + 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; +} + /* * manages the server FSM and its socket. It returns 1 if a state has changed @@ -4340,51 +4652,37 @@ int process_srv(struct session *t) { c == CL_STSHUTW || (c == CL_STSHUTR && t->req->l == 0)) { /* give up */ tv_eternity(&t->cnexpire); - t->srv_state = SV_STCLOSE; - if (!(t->flags & SN_ERR_MASK)) - t->flags |= SN_ERR_CLICL; - if (!(t->flags & SN_FINST_MASK)) - t->flags |= SN_FINST_C; + srv_close_with_err(t, SN_ERR_CLICL, SN_FINST_C, 0, 0, NULL); + + /* it might be possible that we have been granted an access to the + * server while waiting for a free slot. Since we'll never use it, + * we have to pass it on to another session. + */ + if (t->srv) + offer_connection_slot(t); return 1; } - else { /* go to SV_STCONN */ - /* initiate a connection to the server */ - conn_err = connect_server(t); - if (conn_err == SN_ERR_NONE) { - //fprintf(stderr,"0: c=%d, s=%d\n", c, s); - t->srv_state = SV_STCONN; - } - else { /* try again */ - while (t->conn_retries-- > 0) { - if ((t->proxy->options & PR_O_REDISP) && (t->conn_retries == 0)) { - t->flags &= ~SN_DIRECT; /* ignore cookie and force to use the dispatcher */ - t->srv = NULL; /* it's left to the dispatcher to choose a server */ - if ((t->flags & SN_CK_MASK) == SN_CK_VALID) { - t->flags &= ~SN_CK_MASK; - t->flags |= SN_CK_DOWN; - } - } + else { + /* Right now, we will need to create a connection to the server. + * We might already have tried, and got a connection pending, in + * which case we will not do anything till it's pending. It's up + * to any other session to release it and wake us up again. + */ + if (t->pend_pos) + return 0; - conn_err = connect_server(t); - if (conn_err == SN_ERR_NONE) { - t->srv_state = SV_STCONN; - break; - } - } - if (t->conn_retries < 0) { - /* if conn_retries < 0 or other error, let's abort */ - tv_eternity(&t->cnexpire); - t->srv_state = SV_STCLOSE; - t->logs.status = 503; - if (t->proxy->mode == PR_MODE_HTTP) - client_return(t, t->proxy->errmsg.len503, t->proxy->errmsg.msg503); - if (!(t->flags & SN_ERR_MASK)) - t->flags |= conn_err; /* report the precise connect() error */ - if (!(t->flags & SN_FINST_MASK)) - t->flags |= SN_FINST_C; - } - } - return 1; + do { + /* first, get a connection */ + if (srv_redispatch_connect(t)) + return t->srv_state != SV_STIDLE; + + /* try to (re-)connect to the server, and fail if we expire the + * number of retries. + */ + if (srv_retryable_connect(t)) + return t->srv_state != SV_STIDLE; + + } while (1); } } else if (s == SV_STCONN) { /* connection in progress */ @@ -4393,44 +4691,35 @@ int process_srv(struct session *t) { return 0; /* nothing changed */ } else if (t->res_sw == RES_SILENT || t->res_sw == RES_ERROR) { + /* timeout, asynchronous connect error or first write error */ //fprintf(stderr,"2: c=%d, s=%d\n", c, s); - /* timeout, connect error or first write error */ - //FD_CLR(t->srv_fd, StaticWriteEvent); + fd_delete(t->srv_fd); if (t->srv) t->srv->cur_sess--; - //close(t->srv_fd); - t->conn_retries--; - if (t->conn_retries >= 0) { - if ((t->proxy->options & PR_O_REDISP) && (t->conn_retries == 0)) { - t->flags &= ~SN_DIRECT; /* ignore cookie and force to use the dispatcher */ - t->srv = NULL; /* it's left to the dispatcher to choose a server */ - if ((t->flags & SN_CK_MASK) == SN_CK_VALID) { - t->flags &= ~SN_CK_MASK; - t->flags |= SN_CK_DOWN; - } - } - conn_err = connect_server(t); - if (conn_err == SN_ERR_NONE) - return 0; /* no state changed */ - } - else if (t->res_sw == RES_SILENT) + + if (t->res_sw == RES_SILENT) conn_err = SN_ERR_SRVTO; // it was a connect timeout. else - conn_err = SN_ERR_SRVCL; // it was a connect error. + conn_err = SN_ERR_SRVCL; // it was an asynchronous connect error. - /* if conn_retries < 0 or other error, let's abort */ - tv_eternity(&t->cnexpire); - t->srv_state = SV_STCLOSE; - t->logs.status = 503; - if (t->proxy->mode == PR_MODE_HTTP) - client_return(t, t->proxy->errmsg.len503, t->proxy->errmsg.msg503); - if (!(t->flags & SN_ERR_MASK)) - t->flags |= conn_err; - if (!(t->flags & SN_FINST_MASK)) - t->flags |= SN_FINST_C; - /* TODO : check if there are pending connections on this server */ - return 1; + /* ensure that we have enough retries left */ + if (srv_count_retry_down(t, conn_err)) + return 1; + + do { + /* Now we will try to either reconnect to the same server or + * connect to another server. If the connection gets queued + * because all servers are saturated, then we will go back to + * the SV_STIDLE state. + */ + if (srv_retryable_connect(t)) + return t->srv_state != SV_STCONN; + + /* we need to redispatch the connection to another server */ + if (srv_redispatch_connect(t)) + return t->srv_state != SV_STCONN; + } while (1); } else { /* no error or write 0 */ t->logs.t_connect = tv_diff(&t->logs.tv_accept, &now); @@ -4521,7 +4810,12 @@ int process_srv(struct session *t) { Alert("Blocking cacheable cookie in response from instance %s, server %s.\n", t->proxy->id, t->srv->id); send_log(t->proxy, LOG_ALERT, "Blocking cacheable cookie in response from instance %s, server %s.\n", t->proxy->id, t->srv->id); - /* TODO : check if there are pending connections on this server */ + /* We used to have a free connection slot. Since we'll never use it, + * we have to pass it on to another session. + */ + if (t->srv) + offer_connection_slot(t); + return 1; } } @@ -4540,7 +4834,12 @@ int process_srv(struct session *t) { t->flags |= SN_ERR_PRXCOND; if (!(t->flags & SN_FINST_MASK)) t->flags |= SN_FINST_H; - /* TODO : check if there are pending connections on this server */ + /* We used to have a free connection slot. Since we'll never use it, + * we have to pass it on to another session. + */ + if (t->srv) + offer_connection_slot(t); + return 1; } @@ -4974,7 +5273,12 @@ int process_srv(struct session *t) { t->flags |= SN_ERR_SRVCL; if (!(t->flags & SN_FINST_MASK)) t->flags |= SN_FINST_H; - /* TODO : check if there are pending connections on this server */ + /* We used to have a free connection slot. Since we'll never use it, + * we have to pass it on to another session. + */ + if (t->srv) + offer_connection_slot(t); + return 1; } /* end of client write or end of server read. @@ -5004,7 +5308,12 @@ int process_srv(struct session *t) { t->flags |= SN_ERR_SRVTO; if (!(t->flags & SN_FINST_MASK)) t->flags |= SN_FINST_H; - /* TODO : check if there are pending connections on this server */ + /* We used to have a free connection slot. Since we'll never use it, + * we have to pass it on to another session. + */ + if (t->srv) + offer_connection_slot(t); + return 1; } /* last client read and buffer empty */ @@ -5099,7 +5408,12 @@ int process_srv(struct session *t) { t->flags |= SN_ERR_SRVCL; if (!(t->flags & SN_FINST_MASK)) t->flags |= SN_FINST_D; - /* TODO : check if there are pending connections on this server */ + /* We used to have a free connection slot. Since we'll never use it, + * we have to pass it on to another session. + */ + if (t->srv) + offer_connection_slot(t); + return 1; } /* last read, or end of client write */ @@ -5208,7 +5522,12 @@ int process_srv(struct session *t) { t->flags |= SN_ERR_SRVCL; if (!(t->flags & SN_FINST_MASK)) t->flags |= SN_FINST_D; - /* TODO : check if there are pending connections on this server */ + /* We used to have a free connection slot. Since we'll never use it, + * we have to pass it on to another session. + */ + if (t->srv) + offer_connection_slot(t); + return 1; } else if ((c == CL_STSHUTR || c == CL_STCLOSE) && (req->l == 0)) { @@ -5219,7 +5538,12 @@ int process_srv(struct session *t) { t->srv->cur_sess--; //close(t->srv_fd); t->srv_state = SV_STCLOSE; - /* TODO : check if there are pending connections on this server */ + /* We used to have a free connection slot. Since we'll never use it, + * we have to pass it on to another session. + */ + if (t->srv) + offer_connection_slot(t); + return 1; } else if (tv_cmp2_ms(&t->swexpire, &now) <= 0) { @@ -5234,7 +5558,12 @@ int process_srv(struct session *t) { t->flags |= SN_ERR_SRVTO; if (!(t->flags & SN_FINST_MASK)) t->flags |= SN_FINST_D; - /* TODO : check if there are pending connections on this server */ + /* We used to have a free connection slot. Since we'll never use it, + * we have to pass it on to another session. + */ + if (t->srv) + offer_connection_slot(t); + return 1; } else if (req->l == 0) { @@ -5271,7 +5600,12 @@ int process_srv(struct session *t) { t->flags |= SN_ERR_SRVCL; if (!(t->flags & SN_FINST_MASK)) t->flags |= SN_FINST_D; - /* TODO : check if there are pending connections on this server */ + /* We used to have a free connection slot. Since we'll never use it, + * we have to pass it on to another session. + */ + if (t->srv) + offer_connection_slot(t); + return 1; } else if (t->res_sr == RES_NULL || c == CL_STSHUTW || c == CL_STCLOSE) { @@ -5282,7 +5616,12 @@ int process_srv(struct session *t) { t->srv->cur_sess--; //close(t->srv_fd); t->srv_state = SV_STCLOSE; - /* TODO : check if there are pending connections on this server */ + /* We used to have a free connection slot. Since we'll never use it, + * we have to pass it on to another session. + */ + if (t->srv) + offer_connection_slot(t); + return 1; } else if (tv_cmp2_ms(&t->srexpire, &now) <= 0) { @@ -5297,7 +5636,12 @@ int process_srv(struct session *t) { t->flags |= SN_ERR_SRVTO; if (!(t->flags & SN_FINST_MASK)) t->flags |= SN_FINST_D; - /* TODO : check if there are pending connections on this server */ + /* We used to have a free connection slot. Since we'll never use it, + * we have to pass it on to another session. + */ + if (t->srv) + offer_connection_slot(t); + return 1; } else if (rep->l == BUFSIZE) { /* no room to read more data */ @@ -6666,8 +7010,11 @@ int cfg_parse_listen(char *file, int linenum, char **args) { Alert("parsing [%s:%d] : out of memory.\n", file, linenum); return -1; } + curproxy->next = proxy; proxy = curproxy; + LIST_INIT(&curproxy->pendconns); + curproxy->id = strdup(args[1]); /* parse the listener address if any */