mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-09-22 22:31:28 +02:00
MEDIUM: stream-int: split the shutr/shutw functions between applet and conn
These functions induce a lot of ifs everywhere because they consider two different cases, one which is where the connection exists and has a file descriptor, and the other one which is the default case where at most an applet has to be notified. Let's have them in si_ops and automatically decide which one to use. The connection shutdown sequence has been slightly simplified, and we now clear the flags at the end. Also we remove SHUTR_NOW after a shutw with nolinger, as it's cleaner not to keep it.
This commit is contained in:
parent
fac4bd1492
commit
8b3d7dfd7c
@ -36,8 +36,6 @@ int stream_int_check_timeouts(struct stream_interface *si);
|
||||
void stream_int_report_error(struct stream_interface *si);
|
||||
void stream_int_retnclose(struct stream_interface *si, const struct chunk *msg);
|
||||
int conn_si_send_proxy(struct connection *conn, unsigned int flag);
|
||||
int stream_int_shutr(struct stream_interface *si);
|
||||
int stream_int_shutw(struct stream_interface *si);
|
||||
void stream_sock_read0(struct stream_interface *si);
|
||||
|
||||
extern struct si_ops si_embedded_ops;
|
||||
@ -74,14 +72,14 @@ static inline void si_prepare_embedded(struct stream_interface *si)
|
||||
/* Sends a shutr to the connection using the data layer */
|
||||
static inline void si_shutr(struct stream_interface *si)
|
||||
{
|
||||
if (stream_int_shutr(si))
|
||||
if (si->ops->shutr(si))
|
||||
conn_data_stop_recv(si->conn);
|
||||
}
|
||||
|
||||
/* Sends a shutw to the connection using the data layer */
|
||||
static inline void si_shutw(struct stream_interface *si)
|
||||
{
|
||||
if (stream_int_shutw(si))
|
||||
if (si->ops->shutw(si))
|
||||
conn_data_stop_send(si->conn);
|
||||
}
|
||||
|
||||
|
@ -85,6 +85,8 @@ struct si_ops {
|
||||
void (*update)(struct stream_interface *); /* I/O update function */
|
||||
void (*chk_rcv)(struct stream_interface *); /* chk_rcv function */
|
||||
void (*chk_snd)(struct stream_interface *); /* chk_snd function */
|
||||
int (*shutr)(struct stream_interface *); /* shut read function */
|
||||
int (*shutw)(struct stream_interface *); /* shut write function */
|
||||
};
|
||||
|
||||
/* A stream interface has 3 parts :
|
||||
|
@ -37,9 +37,13 @@
|
||||
|
||||
/* socket functions used when running a stream interface as a task */
|
||||
static void stream_int_update_embedded(struct stream_interface *si);
|
||||
static int stream_int_shutr(struct stream_interface *si);
|
||||
static int stream_int_shutw(struct stream_interface *si);
|
||||
static void stream_int_chk_rcv(struct stream_interface *si);
|
||||
static void stream_int_chk_snd(struct stream_interface *si);
|
||||
static void stream_int_update_conn(struct stream_interface *si);
|
||||
static int stream_int_shutr_conn(struct stream_interface *si);
|
||||
static int stream_int_shutw_conn(struct stream_interface *si);
|
||||
static void stream_int_chk_rcv_conn(struct stream_interface *si);
|
||||
static void stream_int_chk_snd_conn(struct stream_interface *si);
|
||||
static void si_conn_recv_cb(struct connection *conn);
|
||||
@ -51,6 +55,8 @@ struct si_ops si_embedded_ops = {
|
||||
.update = stream_int_update_embedded,
|
||||
.chk_rcv = stream_int_chk_rcv,
|
||||
.chk_snd = stream_int_chk_snd,
|
||||
.shutr = stream_int_shutr,
|
||||
.shutw = stream_int_shutw,
|
||||
};
|
||||
|
||||
/* stream-interface operations for connections */
|
||||
@ -58,6 +64,8 @@ struct si_ops si_conn_ops = {
|
||||
.update = stream_int_update_conn,
|
||||
.chk_rcv = stream_int_chk_rcv_conn,
|
||||
.chk_snd = stream_int_chk_snd_conn,
|
||||
.shutr = stream_int_shutr_conn,
|
||||
.shutw = stream_int_shutw_conn,
|
||||
};
|
||||
|
||||
struct data_cb si_conn_cb = {
|
||||
@ -200,22 +208,16 @@ static void stream_int_update_embedded(struct stream_interface *si)
|
||||
}
|
||||
|
||||
/*
|
||||
* This function performs a shutdown-read on a stream interface in a connected
|
||||
* or init state (it does nothing for other states). It either shuts the read
|
||||
* side or marks itself as closed. The buffer flags are updated to reflect the
|
||||
* new state. If the stream interface has SI_FL_NOHALF, we also forward the
|
||||
* close to the write side. If a control layer is defined, then it is supposed
|
||||
* to be a socket layer and file descriptors are then shutdown or closed
|
||||
* accordingly. If no control layer is defined, then the SI is supposed to be
|
||||
* an embedded one and the owner task is woken up if it exists. The function
|
||||
* does not disable polling on the FD by itself, it returns non-zero instead
|
||||
* if the caller needs to do so (except when the FD is deleted where this is
|
||||
* implicit).
|
||||
* This function performs a shutdown-read on a stream interface attached to an
|
||||
* applet in a connected or init state (it does nothing for other states). It
|
||||
* either shuts the read side or marks itself as closed. The buffer flags are
|
||||
* updated to reflect the new state. If the stream interface has SI_FL_NOHALF,
|
||||
* we also forward the close to the write side. The owner task is woken up if
|
||||
* it exists. No control layer is supposed to be defined so this function
|
||||
* always returns zero.
|
||||
*/
|
||||
int stream_int_shutr(struct stream_interface *si)
|
||||
static int stream_int_shutr(struct stream_interface *si)
|
||||
{
|
||||
struct connection *conn = si->conn;
|
||||
|
||||
si->ib->flags &= ~CF_SHUTR_NOW;
|
||||
if (si->ib->flags & CF_SHUTR)
|
||||
return 0;
|
||||
@ -227,7 +229,6 @@ int stream_int_shutr(struct stream_interface *si)
|
||||
return 0;
|
||||
|
||||
if (si->ob->flags & CF_SHUTW) {
|
||||
conn_full_close(si->conn);
|
||||
si->state = SI_ST_DIS;
|
||||
si->exp = TICK_ETERNITY;
|
||||
|
||||
@ -238,33 +239,24 @@ int stream_int_shutr(struct stream_interface *si)
|
||||
/* we want to immediately forward this close to the write side */
|
||||
return stream_int_shutw(si);
|
||||
}
|
||||
else if (conn->ctrl) {
|
||||
/* we want the caller to disable polling on this FD */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* note that if the task exists, it must unregister itself once it runs */
|
||||
if (!conn->ctrl && !(si->flags & SI_FL_DONT_WAKE) && si->owner)
|
||||
if (!(si->flags & SI_FL_DONT_WAKE) && si->owner)
|
||||
task_wakeup(si->owner, TASK_WOKEN_IO);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function performs a shutdown-write on a stream interface in a connected or
|
||||
* init state (it does nothing for other states). It either shuts the write side
|
||||
* or marks itself as closed. The buffer flags are updated to reflect the new state.
|
||||
* It does also close everything if the SI was marked as being in error state. If
|
||||
* there is a data-layer shutdown, it is called. If a control layer is defined, then
|
||||
* it is supposed to be a socket layer and file descriptors are then shutdown or
|
||||
* closed accordingly. If no control layer is defined, then the SI is supposed to
|
||||
* be an embedded one and the owner task is woken up if it exists. The function
|
||||
* does not disable polling on the FD by itself, it returns non-zero instead if
|
||||
* the caller needs to do so (except when the FD is deleted where this is implicit).
|
||||
* This function performs a shutdown-write on a stream interface attached to an
|
||||
* applet in a connected or init state (it does nothing for other states). It
|
||||
* either shuts the write side or marks itself as closed. The buffer flags are
|
||||
* updated to reflect the new state. It does also close everything if the SI
|
||||
* was marked as being in error state. The owner task is woken up if it exists.
|
||||
* No control layer is supposed to be defined so this function always returns
|
||||
* zero.
|
||||
*/
|
||||
int stream_int_shutw(struct stream_interface *si)
|
||||
static int stream_int_shutw(struct stream_interface *si)
|
||||
{
|
||||
struct connection *conn = si->conn;
|
||||
|
||||
si->ob->flags &= ~CF_SHUTW_NOW;
|
||||
if (si->ob->flags & CF_SHUTW)
|
||||
return 0;
|
||||
@ -280,66 +272,30 @@ int stream_int_shutw(struct stream_interface *si)
|
||||
* However, if SI_FL_NOLINGER is explicitly set, we know there is
|
||||
* no risk so we close both sides immediately.
|
||||
*/
|
||||
if (si->flags & SI_FL_ERR) {
|
||||
/* quick close, the socket is already shut. Remove pending flags. */
|
||||
si->flags &= ~SI_FL_NOLINGER;
|
||||
} else if (si->flags & SI_FL_NOLINGER) {
|
||||
si->flags &= ~SI_FL_NOLINGER;
|
||||
if (conn->ctrl) {
|
||||
setsockopt(si->conn->t.sock.fd, SOL_SOCKET, SO_LINGER,
|
||||
(struct linger *) &nolinger, sizeof(struct linger));
|
||||
}
|
||||
/* unclean data-layer shutdown */
|
||||
if (conn->xprt && conn->xprt->shutw)
|
||||
conn->xprt->shutw(conn, 0);
|
||||
} else {
|
||||
/* clean data-layer shutdown */
|
||||
if (conn->xprt && conn->xprt->shutw)
|
||||
conn->xprt->shutw(conn, 1);
|
||||
|
||||
/* If the stream interface is configured to disable half-open
|
||||
* connections, we'll skip the shutdown(), but only if the
|
||||
* read size is already closed. Otherwise we can't support
|
||||
* closed write with pending read (eg: abortonclose while
|
||||
* waiting for the server).
|
||||
*/
|
||||
if (!(si->flags & SI_FL_NOHALF) || !(si->ib->flags & (CF_SHUTR|CF_DONT_READ))) {
|
||||
/* We shutdown transport layer */
|
||||
if (conn->ctrl)
|
||||
shutdown(si->conn->t.sock.fd, SHUT_WR);
|
||||
|
||||
if (!(si->ib->flags & (CF_SHUTR|CF_DONT_READ))) {
|
||||
/* OK just a shutw, but we want the caller
|
||||
* to disable polling on this FD if exists.
|
||||
*/
|
||||
return !!conn->ctrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(si->flags & (SI_FL_ERR | SI_FL_NOLINGER)) &&
|
||||
!(si->ib->flags & (CF_SHUTR|CF_DONT_READ)))
|
||||
return 0;
|
||||
|
||||
/* fall through */
|
||||
case SI_ST_CON:
|
||||
/* we may have to close a pending connection, and mark the
|
||||
* response buffer as shutr
|
||||
*/
|
||||
conn_full_close(si->conn);
|
||||
/* fall through */
|
||||
case SI_ST_CER:
|
||||
case SI_ST_QUE:
|
||||
case SI_ST_TAR:
|
||||
/* Note that none of these states may happen with applets */
|
||||
si->state = SI_ST_DIS;
|
||||
|
||||
if (si->release)
|
||||
si->release(si);
|
||||
default:
|
||||
si->flags &= ~SI_FL_WAIT_ROOM;
|
||||
si->flags &= ~(SI_FL_WAIT_ROOM | SI_FL_NOLINGER);
|
||||
si->ib->flags &= ~CF_SHUTR_NOW;
|
||||
si->ib->flags |= CF_SHUTR;
|
||||
si->ib->rex = TICK_ETERNITY;
|
||||
si->exp = TICK_ETERNITY;
|
||||
}
|
||||
|
||||
/* note that if the task exists, it must unregister itself once it runs */
|
||||
if (!conn->ctrl && !(si->flags & SI_FL_DONT_WAKE) && si->owner)
|
||||
if (!(si->flags & SI_FL_DONT_WAKE) && si->owner)
|
||||
task_wakeup(si->owner, TASK_WOKEN_IO);
|
||||
return 0;
|
||||
}
|
||||
@ -530,7 +486,7 @@ static int si_conn_wake_cb(struct connection *conn)
|
||||
if (channel_is_empty(si->ob)) {
|
||||
if (((si->ob->flags & (CF_SHUTW|CF_SHUTW_NOW)) == CF_SHUTW_NOW) &&
|
||||
(si->state == SI_ST_EST))
|
||||
stream_int_shutw(si);
|
||||
stream_int_shutw_conn(si);
|
||||
__conn_data_stop_send(conn);
|
||||
si->ob->wex = TICK_ETERNITY;
|
||||
}
|
||||
@ -756,6 +712,147 @@ void stream_int_update_conn(struct stream_interface *si)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function performs a shutdown-read on a stream interface attached to
|
||||
* a connection in a connected or init state (it does nothing for other
|
||||
* states). It either shuts the read side or marks itself as closed. The buffer
|
||||
* flags are updated to reflect the new state. If the stream interface has
|
||||
* SI_FL_NOHALF, we also forward the close to the write side. If a control
|
||||
* layer is defined, then it is supposed to be a socket layer and file
|
||||
* descriptors are then shutdown or closed accordingly. The function does not
|
||||
* disable polling on the FD by itself, it returns non-zero instead if the
|
||||
* caller needs to do so (except when the FD is deleted where this is
|
||||
* implicit).
|
||||
*/
|
||||
static int stream_int_shutr_conn(struct stream_interface *si)
|
||||
{
|
||||
struct connection *conn = si->conn;
|
||||
|
||||
si->ib->flags &= ~CF_SHUTR_NOW;
|
||||
if (si->ib->flags & CF_SHUTR)
|
||||
return 0;
|
||||
si->ib->flags |= CF_SHUTR;
|
||||
si->ib->rex = TICK_ETERNITY;
|
||||
si->flags &= ~SI_FL_WAIT_ROOM;
|
||||
|
||||
if (si->state != SI_ST_EST && si->state != SI_ST_CON)
|
||||
return 0;
|
||||
|
||||
if (si->ob->flags & CF_SHUTW) {
|
||||
conn_full_close(si->conn);
|
||||
si->state = SI_ST_DIS;
|
||||
si->exp = TICK_ETERNITY;
|
||||
|
||||
if (si->release)
|
||||
si->release(si);
|
||||
}
|
||||
else if (si->flags & SI_FL_NOHALF) {
|
||||
/* we want to immediately forward this close to the write side */
|
||||
return stream_int_shutw_conn(si);
|
||||
}
|
||||
else if (conn->ctrl) {
|
||||
/* we want the caller to disable polling on this FD */
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function performs a shutdown-write on a stream interface attached to
|
||||
* a connection in a connected or init state (it does nothing for other
|
||||
* states). It either shuts the write side or marks itself as closed. The
|
||||
* buffer flags are updated to reflect the new state. It does also close
|
||||
* everything if the SI was marked as being in error state. If there is a
|
||||
* data-layer shutdown, it is called. If a control layer is defined, then it is
|
||||
* supposed to be a socket layer and file descriptors are then shutdown or
|
||||
* closed accordingly. The function does not disable polling on the FD by
|
||||
* itself, it returns non-zero instead if the caller needs to do so (except
|
||||
* when the FD is deleted where this is implicit). Note: at the moment, we
|
||||
* continue to check conn->ctrl eventhough we *know* it is valid. This will
|
||||
* help selecting the proper shutdown() and setsockopt() calls if/when we
|
||||
* implement remote sockets later.
|
||||
*/
|
||||
static int stream_int_shutw_conn(struct stream_interface *si)
|
||||
{
|
||||
struct connection *conn = si->conn;
|
||||
|
||||
si->ob->flags &= ~CF_SHUTW_NOW;
|
||||
if (si->ob->flags & CF_SHUTW)
|
||||
return 0;
|
||||
si->ob->flags |= CF_SHUTW;
|
||||
si->ob->wex = TICK_ETERNITY;
|
||||
si->flags &= ~SI_FL_WAIT_DATA;
|
||||
|
||||
switch (si->state) {
|
||||
case SI_ST_EST:
|
||||
/* we have to shut before closing, otherwise some short messages
|
||||
* may never leave the system, especially when there are remaining
|
||||
* unread data in the socket input buffer, or when nolinger is set.
|
||||
* However, if SI_FL_NOLINGER is explicitly set, we know there is
|
||||
* no risk so we close both sides immediately.
|
||||
*/
|
||||
if (si->flags & SI_FL_ERR) {
|
||||
/* quick close, the socket is alredy shut anyway */
|
||||
}
|
||||
else if (si->flags & SI_FL_NOLINGER) {
|
||||
if (conn->ctrl) {
|
||||
setsockopt(si->conn->t.sock.fd, SOL_SOCKET, SO_LINGER,
|
||||
(struct linger *) &nolinger, sizeof(struct linger));
|
||||
}
|
||||
/* unclean data-layer shutdown */
|
||||
if (conn->xprt && conn->xprt->shutw)
|
||||
conn->xprt->shutw(conn, 0);
|
||||
}
|
||||
else {
|
||||
/* clean data-layer shutdown */
|
||||
if (conn->xprt && conn->xprt->shutw)
|
||||
conn->xprt->shutw(conn, 1);
|
||||
|
||||
/* If the stream interface is configured to disable half-open
|
||||
* connections, we'll skip the shutdown(), but only if the
|
||||
* read size is already closed. Otherwise we can't support
|
||||
* closed write with pending read (eg: abortonclose while
|
||||
* waiting for the server).
|
||||
*/
|
||||
if (!(si->flags & SI_FL_NOHALF) || !(si->ib->flags & (CF_SHUTR|CF_DONT_READ))) {
|
||||
/* We shutdown transport layer */
|
||||
if (conn->ctrl)
|
||||
shutdown(si->conn->t.sock.fd, SHUT_WR);
|
||||
|
||||
if (!(si->ib->flags & (CF_SHUTR|CF_DONT_READ))) {
|
||||
/* OK just a shutw, but we want the caller
|
||||
* to disable polling on this FD if exists.
|
||||
*/
|
||||
return !!conn->ctrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* fall through */
|
||||
case SI_ST_CON:
|
||||
/* we may have to close a pending connection, and mark the
|
||||
* response buffer as shutr
|
||||
*/
|
||||
conn_full_close(si->conn);
|
||||
/* fall through */
|
||||
case SI_ST_CER:
|
||||
case SI_ST_QUE:
|
||||
case SI_ST_TAR:
|
||||
si->state = SI_ST_DIS;
|
||||
|
||||
if (si->release)
|
||||
si->release(si);
|
||||
default:
|
||||
si->flags &= ~(SI_FL_WAIT_ROOM | SI_FL_NOLINGER);
|
||||
si->ib->flags &= ~CF_SHUTR_NOW;
|
||||
si->ib->flags |= CF_SHUTR;
|
||||
si->ib->rex = TICK_ETERNITY;
|
||||
si->exp = TICK_ETERNITY;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function is used for inter-stream-interface calls. It is called by the
|
||||
* consumer to inform the producer side that it may be interested in checking
|
||||
* for free space in the buffer. Note that it intentionally does not update
|
||||
|
Loading…
x
Reference in New Issue
Block a user