#include <types/listener.h>
#include <proto/fd.h>
#include <proto/obj_type.h>
+#include <proto/session.h>
#include <proto/task.h>
extern struct pool_head *pool_head_connection;
if (!LIST_ISEMPTY(&conn->session_list)) {
struct session *sess = conn->owner;
sess->resp_conns--;
- LIST_DEL(&conn->session_list);
+ session_unown_conn(sess, conn);
}
/* By convention we always place a NULL where the ctx points to if the
#include <proto/server.h>
extern struct pool_head *pool_head_session;
+extern struct pool_head *pool_head_sess_srv_list;
+
struct session *session_new(struct proxy *fe, struct listener *li, enum obj_type *origin);
void session_free(struct session *sess);
int session_accept_fd(struct listener *l, int cfd, struct sockaddr_storage *addr);
}
}
-static inline void session_add_conn(struct session *sess, struct connection *conn, void *target)
+/* Remove the connection from the session list, and destroy the srv_list if it's now empty */
+static inline void session_unown_conn(struct session *sess, struct connection *conn)
{
- int avail = -1;
- int i;
-
- sess->resp_conns++;
- for (i = 0; i < MAX_SRV_LIST; i++) {
- if (sess->srv_list[i].target == target) {
- avail = i;
+ struct sess_srv_list *srv_list = NULL;
+ LIST_DEL(&conn->session_list);
+ LIST_INIT(&conn->session_list);
+ list_for_each_entry(srv_list, &sess->srv_list, srv_list) {
+ if (srv_list->target == conn->target) {
+ if (LIST_ISEMPTY(&srv_list->conn_list)) {
+ LIST_DEL(&srv_list->srv_list);
+ pool_free(pool_head_sess_srv_list, srv_list);
+ }
break;
}
- if (LIST_ISEMPTY(&sess->srv_list[i].list) && avail == -1)
- avail = i;
}
- if (avail == -1) {
- struct connection *conn, *conn_back;
- int count = 0;
- /* We have no slot free, let's free the one with the fewer connections */
- for (i = 0; i < MAX_SRV_LIST; i++) {
- int count_list = 0;
- list_for_each_entry(conn, &sess->srv_list[i].list, session_list)
- count_list++;
- if (count == 0 || count_list < count) {
- count = count_list;
- avail = i;
- }
- }
- /* Now unown all the connections */
- list_for_each_entry_safe(conn, conn_back, &sess->srv_list[avail].list, session_list) {
- sess->resp_conns--;
- conn->owner = NULL;
- LIST_DEL(&conn->session_list);
- LIST_INIT(&conn->session_list);
- if (conn->mux)
- conn->mux->destroy(conn);
- }
+}
+
+static inline int session_add_conn(struct session *sess, struct connection *conn, void *target)
+{
+ struct sess_srv_list *srv_list = NULL;
+ int found = 0;
+ list_for_each_entry(srv_list, &sess->srv_list, srv_list) {
+ if (srv_list->target == target) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ /* The session has no connection for the server, create a new entry */
+ srv_list = pool_alloc(pool_head_sess_srv_list);
+ if (!srv_list)
+ return 0;
+ srv_list->target = target;
+ LIST_INIT(&srv_list->conn_list);
+ LIST_ADDQ(&sess->srv_list, &srv_list->srv_list);
}
- sess->srv_list[avail].target = target;
- LIST_ADDQ(&sess->srv_list[avail].list, &conn->session_list);
+ sess->resp_conns++;
+ LIST_ADDQ(&srv_list->conn_list, &conn->session_list);
+ return 1;
}
/* Returns 0 if the session can keep the idle conn, -1 if it was destroyed, or 1 if it was added to the server list */
{
if (sess->resp_conns > sess->fe->max_out_conns) {
/* We can't keep the connection, let's try to add it to the server idle list */
- LIST_DEL(&conn->session_list);
- LIST_INIT(&conn->session_list);
+ session_unown_conn(sess, conn);
conn->owner = NULL;
sess->resp_conns--;
if (!srv_add_to_idle_list(objt_server(conn->target), conn)) {
struct sess_srv_list {
void *target;
- struct list list;
+ struct list conn_list; /* Head of the connections list */
+ struct list srv_list; /* Next element of the server list */
};
#define MAX_SRV_LIST 5
struct task *task; /* handshake timeout processing */
long t_handshake; /* handshake duration, -1 = not completed */
int resp_conns; /* Number of connections we're currently responsible for */
- struct sess_srv_list srv_list[MAX_SRV_LIST]; /* List of servers and the connections the session is currently responsible for */
+ struct list srv_list; /* List of servers and the connections the session is currently responsible for */
};
#endif /* _TYPES_SESSION_H */
struct server *conn_slot;
struct server *srv = NULL, *prev_srv;
int err;
- int i;
DPRINTF(stderr,"assign_server : s=%p\n",s);
if ((s->be->lbprm.algo & BE_LB_KIND) != BE_LB_KIND_HI &&
((s->txn && s->txn->flags & TX_PREFER_LAST) ||
(s->be->options & PR_O_PREF_LAST))) {
- for (i = 0; i < MAX_SRV_LIST; i++) {
- struct server *tmpsrv = objt_server(s->sess->srv_list[i].target);
+ struct sess_srv_list *srv_list;
+ list_for_each_entry(srv_list, &s->sess->srv_list, srv_list) {
+ struct server *tmpsrv = objt_server(srv_list->target);
if (tmpsrv && tmpsrv->proxy == s->be &&
((s->txn && s->txn->flags & TX_PREFER_LAST) ||
server_has_room(tmpsrv) || (
tmpsrv->nbpend + 1 < s->be->max_ka_queue))) &&
srv_currently_usable(tmpsrv)) {
- list_for_each_entry(conn, &s->sess->srv_list[i].list, session_list) {
+ list_for_each_entry(conn, &srv_list->conn_list, session_list) {
if (conn->flags & CO_FL_CONNECTED) {
srv = tmpsrv;
int reuse = 0;
int reuse_orphan = 0;
int err;
- int i;
/* Some, such as http_proxy and the LUA, create their connection and
reuse = 1;
}
} else {
- for (i = 0; i < MAX_SRV_LIST; i++) {
- if (s->sess->srv_list[i].target == s->target) {
- list_for_each_entry(srv_conn, &s->sess->srv_list[i].list,
+ struct sess_srv_list *srv_list;
+ list_for_each_entry(srv_list, &s->sess->srv_list, srv_list) {
+ if (srv_list->target == s->target) {
+ list_for_each_entry(srv_conn, &srv_list->conn_list,
session_list) {
if (conn_xprt_ready(srv_conn) &&
srv_conn->mux && (srv_conn->mux->avail_streams(srv_conn) > 0)) {
break;
}
}
+ break;
}
}
if (reuse == 0) {
srv_conn = NULL;
- for (i = 0; i < MAX_SRV_LIST; i++) {
- if (!LIST_ISEMPTY(&s->sess->srv_list[i].list)) {
- srv_conn = LIST_ELEM(s->sess->srv_list[i].list.n,
- struct connection *, session_list);
- break;
- }
+ if (!LIST_ISEMPTY(&s->sess->srv_list)) {
+ srv_list = LIST_ELEM(s->sess->srv_list.n,
+ struct sess_srv_list *, srv_list);
+ if (!LIST_ISEMPTY(&srv_list->conn_list))
+ srv_conn = LIST_ELEM(srv_list->conn_list.n,
+ struct connection *, session_list);
}
+
}
}
old_conn = srv_conn;
old_conn->mux != NULL &&
(old_conn->mux->avail_streams(old_conn) > 0) &&
(srv_conn->mux->avail_streams(srv_conn) == 1)) {
- LIST_DEL(&old_conn->session_list);
- LIST_INIT(&old_conn->session_list);
+ session_unown_conn(s->sess, old_conn);
old_conn->owner = sess;
- session_add_conn(sess, old_conn, s->target);
- session_check_idle_conn(sess, old_conn);
+ if (!session_add_conn(sess, old_conn, s->target)) {
+ old_conn->owner = NULL;
+ old_conn->mux->destroy(old_conn);
+ } else
+ session_check_idle_conn(sess, old_conn);
}
}
}
}
if (srv_conn && old_conn != srv_conn) {
+ if (srv_conn->owner)
+ session_unown_conn(srv_conn->owner, srv_conn);
srv_conn->owner = s->sess;
- LIST_DEL(&srv_conn->session_list);
- LIST_INIT(&srv_conn->session_list);
- session_add_conn(s->sess, srv_conn, s->target);
+ if (!session_add_conn(s->sess, srv_conn, s->target)) {
+ /* If we failed to attach the connection, detach the
+ * conn_stream, possibly destroying the connection */
+ cs_destroy(srv_cs);
+ srv_conn->owner = NULL;
+ if (!srv_add_to_idle_list(objt_server(srv_conn->target), srv_conn))
+ /* The server doesn't want it, let's kill the connection right away */
+ srv_conn->mux->destroy(srv_conn);
+ srv_conn = NULL;
+
+ }
}
if (!srv_conn)
if (!(h1c->conn->owner)) {
h1c->conn->owner = sess;
- session_add_conn(sess, h1c->conn, h1c->conn->target);
+ if (!session_add_conn(sess, h1c->conn, h1c->conn->target)) {
+ h1c->conn->owner = NULL;
+ if (!srv_add_to_idle_list(objt_server(h1c->conn->target), h1c->conn))
+ /* The server doesn't want it, let's kill the connection right away */
+ h1c->conn->mux->destroy(h1c->conn);
+ else
+ tasklet_wakeup(h1c->wait_event.task);
+ return;
+
+ }
}
if (h1c->conn->owner == sess) {
int ret = session_check_idle_conn(sess, h1c->conn);
(CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH))) {
if (!h2c->conn->owner) {
h2c->conn->owner = sess;
- session_add_conn(sess, h2c->conn, h2c->conn->target);
+ if (!session_add_conn(sess, h2c->conn, h2c->conn->target)) {
+ h2c->conn->owner = NULL;
+ if (eb_is_empty(&h2c->streams_by_id)) {
+ if (!srv_add_to_idle_list(objt_server(h2c->conn->target), h2c->conn))
+ /* The server doesn't want it, let's kill the connection right away */
+ h2c->conn->mux->destroy(h2c->conn);
+ return;
+ }
+ }
}
if (eb_is_empty(&h2c->streams_by_id)) {
if (session_check_idle_conn(h2c->conn->owner, h2c->conn) != 0)
* have released the endpoint and know if it no longer has
* attached streams, and so an idling connection
*/
- session_add_conn(s->sess, srv_conn, s->target);
+ if (!session_add_conn(s->sess, srv_conn, s->target)) {
+ srv_conn->owner = NULL;
+ /* Try to add the connection to the server idle list.
+ * If it fails, as the connection no longer has an
+ * owner, it will be destroy later by
+ * si_release_endpoint(), anyway
+ */
+ srv_add_to_idle_list(objt_server(srv_conn->target), srv_conn);
+ srv_conn = NULL;
+
+ }
}
si_release_endpoint(&s->si[1]);
if (srv_conn && srv_conn->owner == s->sess) {
#include <proto/vars.h>
DECLARE_POOL(pool_head_session, "session", sizeof(struct session));
+DECLARE_POOL(pool_head_sess_srv_list, "session server list",
+ sizeof(struct sess_srv_list));
static int conn_complete_session(struct connection *conn);
static struct task *session_expire_embryonic(struct task *t, void *context, unsigned short state);
struct session *session_new(struct proxy *fe, struct listener *li, enum obj_type *origin)
{
struct session *sess;
- int i;
sess = pool_alloc(pool_head_session);
if (sess) {
proxy_inc_fe_conn_ctr(li, fe);
HA_ATOMIC_ADD(&totalconn, 1);
HA_ATOMIC_ADD(&jobs, 1);
- for (i = 0; i < MAX_SRV_LIST; i++) {
- sess->srv_list[i].target = NULL;
- LIST_INIT(&sess->srv_list[i].list);
- }
+ LIST_INIT(&sess->srv_list);
sess->resp_conns = 0;
}
return sess;
void session_free(struct session *sess)
{
struct connection *conn, *conn_back;
- int i;
+ struct sess_srv_list *srv_list, *srv_list_back;
HA_ATOMIC_SUB(&sess->fe->feconn, 1);
if (sess->listener)
conn = objt_conn(sess->origin);
if (conn != NULL && conn->mux)
conn->mux->destroy(conn);
- for (i = 0; i < MAX_SRV_LIST; i++) {
- int count = 0;
- list_for_each_entry_safe(conn, conn_back, &sess->srv_list[i].list, session_list) {
- count++;
+ list_for_each_entry_safe(srv_list, srv_list_back, &sess->srv_list, srv_list) {
+ list_for_each_entry_safe(conn, conn_back, &srv_list->conn_list, session_list) {
if (conn->mux) {
LIST_DEL(&conn->session_list);
conn_free(conn);
}
}
+ pool_free(pool_head_sess_srv_list, srv_list);
}
pool_free(pool_head_session, sess);
HA_ATOMIC_SUB(&jobs, 1);