int conn_recv_proxy(struct connection *conn, int flag);
int make_proxy_line(char *buf, int buf_len, struct sockaddr_storage *src, struct sockaddr_storage *dst);
-/* calls the init() function of the transport layer if any.
+/* Calls the init() function of the transport layer if any and if not done yet,
+ * and sets the CO_FL_XPRT_READY flag to indicate it was properly initialized.
* Returns <0 in case of error.
*/
static inline int conn_xprt_init(struct connection *conn)
{
- if (conn->xprt && conn->xprt->init)
- return conn->xprt->init(conn);
- return 0;
+ int ret = 0;
+
+ if (!(conn->flags & CO_FL_XPRT_READY) && conn->xprt && conn->xprt->init)
+ ret = conn->xprt->init(conn);
+
+ if (ret >= 0)
+ conn->flags |= CO_FL_XPRT_READY;
+
+ return ret;
}
-/* Calls the close() function of the transport layer if any, and always unsets
- * the transport layer. However this is not done if the CO_FL_XPRT_TRACKED flag
- * is set, which allows logs to take data from the transport layer very late if
- * needed.
+/* Calls the close() function of the transport layer if any and if not done
+ * yet, and clears the CO_FL_XPRT_READY flag. However this is not done if the
+ * CO_FL_XPRT_TRACKED flag is set, which allows logs to take data from the
+ * transport layer very late if needed.
*/
static inline void conn_xprt_close(struct connection *conn)
{
- if (conn->xprt && !(conn->flags & CO_FL_XPRT_TRACKED)) {
- if (conn->xprt->close)
+ if ((conn->flags & (CO_FL_XPRT_READY|CO_FL_XPRT_TRACKED)) == CO_FL_XPRT_READY) {
+ if (conn->xprt && conn->xprt->close)
conn->xprt->close(conn);
- conn->xprt = NULL;
+ conn->flags &= ~CO_FL_XPRT_READY;
+ }
+}
+
+/* Initializes the connection's control layer which essentially consists in
+ * registering the file descriptor for polling and setting the CO_FL_CTRL_READY
+ * flag.
+ */
+static inline void conn_ctrl_init(struct connection *conn)
+{
+ if (!(conn->flags & CO_FL_CTRL_READY)) {
+ int fd = conn->t.sock.fd;
+
+ fd_insert(fd);
+ fdtab[fd].owner = conn;
+ fdtab[fd].iocb = conn_fd_handler;
+ conn->flags |= CO_FL_CTRL_READY;
+ }
+}
+
+/* Deletes the FD if the transport layer is already gone. Once done,
+ * it then removes the CO_FL_CTRL_READY flag.
+ */
+static inline void conn_ctrl_close(struct connection *conn)
+{
+ if ((conn->flags & (CO_FL_XPRT_READY|CO_FL_CTRL_READY)) == CO_FL_CTRL_READY) {
+ fd_delete(conn->t.sock.fd);
+ conn->flags &= ~CO_FL_CTRL_READY;
}
}
*/
static inline void conn_full_close(struct connection *conn)
{
- if (conn->xprt && !(conn->flags & CO_FL_XPRT_TRACKED)) {
- if (conn->xprt->close)
- conn->xprt->close(conn);
- if (conn->ctrl)
- fd_delete(conn->t.sock.fd);
- conn->xprt = NULL;
- }
+ conn_xprt_close(conn);
+ conn_ctrl_close(conn);
+}
+
+/* Force to close the connection whatever the tracking state. This is mainly
+ * used on the error path where the tracking does not make sense.
+ */
+static inline void conn_force_close(struct connection *conn)
+{
+ if ((conn->flags & CO_FL_XPRT_READY) && conn->xprt && conn->xprt->close)
+ conn->xprt->close(conn);
+
+ if (conn->flags & CO_FL_CTRL_READY)
+ fd_delete(conn->t.sock.fd);
+
+ conn->flags &= ~(CO_FL_XPRT_READY|CO_FL_CTRL_READY);
}
/* Update polling on connection <c>'s file descriptor depending on its current
{
conn->flags &= ~(CO_FL_WAIT_ROOM | CO_FL_WAIT_RD | CO_FL_WAIT_DATA | CO_FL_WAIT_WR);
- if (conn->ctrl) {
+ if ((conn->flags & CO_FL_CTRL_READY) && conn->ctrl) {
unsigned int flags = conn->flags & ~(CO_FL_CURR_RD_ENA | CO_FL_CURR_WR_ENA);
if (fd_ev_is_set(conn->t.sock.fd, DIR_RD))
if (conn->flags & CO_FL_ADDR_FROM_SET)
return;
- if (!conn->ctrl || !conn->ctrl->get_src)
+ if (!(conn->flags & CO_FL_CTRL_READY) || !conn->ctrl || !conn->ctrl->get_src)
return;
if (conn->ctrl->get_src(conn->t.sock.fd, (struct sockaddr *)&conn->addr.from,
if (conn->flags & CO_FL_ADDR_TO_SET)
return;
- if (!conn->ctrl || !conn->ctrl->get_dst)
+ if (!(conn->flags & CO_FL_CTRL_READY) || !conn->ctrl || !conn->ctrl->get_dst)
return;
if (conn->ctrl->get_dst(conn->t.sock.fd, (struct sockaddr *)&conn->addr.to,
CO_FL_CURR_WR_ENA = 0x00000040, /* sending is currently desired */
CO_FL_WAIT_WR = 0x00000080, /* sending needs to poll first */
+ /* These flags indicate whether the Control and Transport layers are initialized */
+ CO_FL_CTRL_READY = 0x00000100, /* FD was registered, fd_delete() needed */
+ CO_FL_XPRT_READY = 0x00000200, /* xprt_init() done, xprt_close() needed */
+
/* These flags are used by data layers to indicate they had to stop
* sending data because a buffer was empty (WAIT_DATA) or stop receiving
* data because a buffer was full (WAIT_ROOM). The connection handler
*/
CO_FL_POLL_SOCK = CO_FL_HANDSHAKE | CO_FL_WAIT_L4_CONN | CO_FL_WAIT_L6_CONN,
+ /* unused : 0x10000000, 0x20000000, 0x40000000 */
+
/* This last flag indicates that the transport layer is used (for instance
* by logs) and must not be cleared yet. The last call to conn_xprt_close()
* must be done after clearing this flag.
enum obj_type obj_type; /* differentiates connection from applet context */
const struct protocol *ctrl; /* operations at the socket layer */
const struct xprt_ops *xprt; /* operations at the transport layer */
- const struct data_cb *data; /* data layer callbacks */
+ const struct data_cb *data; /* data layer callbacks. Must be set before xprt->init() */
unsigned int flags; /* CO_FL_* */
int xprt_st; /* transport layer state, initialized to zero */
void *xprt_ctx; /* general purpose pointer, initialized to NULL */
if (conn->flags & CO_FL_ERROR && ((errno && errno != EAGAIN) || !conn->ctrl))
return 1;
- if (!conn->ctrl)
+ if (!(conn->flags & CO_FL_CTRL_READY) || !conn->ctrl)
return 0;
if (getsockopt(conn->t.sock.fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == 0)
/* We're here because nobody wants to handle the error, so we
* sure want to abort the hard way.
*/
- if (conn->ctrl && !(conn->flags & CO_FL_SOCK_RD_SH)) {
+ if ((conn->flags & CO_FL_CTRL_READY) && !(conn->flags & CO_FL_SOCK_RD_SH)) {
if (conn->flags & CO_FL_WAIT_RD || !conn->ctrl->drain || !conn->ctrl->drain(conn->t.sock.fd))
setsockopt(conn->t.sock.fd, SOL_SOCKET, SO_LINGER,
(struct linger *) &nolinger, sizeof(struct linger));
}
- conn_full_close(conn);
+ conn_force_close(conn);
}
return 0;
}
* as a failed response coupled with "observe layer7" caused the
* server state to be suddenly changed.
*/
- if (conn->ctrl && !(conn->flags & CO_FL_SOCK_RD_SH)) {
+ if ((conn->flags & CO_FL_CTRL_READY) && !(conn->flags & CO_FL_SOCK_RD_SH)) {
if (conn->flags & CO_FL_WAIT_RD || !conn->ctrl->drain || !conn->ctrl->drain(conn->t.sock.fd))
setsockopt(conn->t.sock.fd, SOL_SOCKET, SO_LINGER,
(struct linger *) &nolinger, sizeof(struct linger));
}
- conn_full_close(conn);
+ conn_force_close(conn);
}
if (check->result & SRV_CHK_FAILED) /* a failure or timeout detected */
{
unsigned int f = c->flags;
+ if (!(c->flags & CO_FL_CTRL_READY))
+ return;
+
/* update read status if needed */
if (unlikely((f & (CO_FL_DATA_RD_ENA|CO_FL_WAIT_RD)) == (CO_FL_DATA_RD_ENA|CO_FL_WAIT_RD))) {
fd_poll_recv(c->t.sock.fd);
{
unsigned int f = c->flags;
+ if (!(c->flags & CO_FL_CTRL_READY))
+ return;
+
/* update read status if needed */
if (unlikely((f & (CO_FL_SOCK_RD_ENA|CO_FL_WAIT_RD)) == (CO_FL_SOCK_RD_ENA|CO_FL_WAIT_RD))) {
fd_poll_recv(c->t.sock.fd);
if (conn->flags & CO_FL_SOCK_RD_SH)
goto fail;
+ if (!(conn->flags & CO_FL_CTRL_READY))
+ goto fail;
+
do {
trash.len = recv(conn->t.sock.fd, trash.str, trash.size, MSG_PEEK);
if (trash.len < 0) {
if (conn->flags & CO_FL_SOCK_WR_SH)
goto out_error;
+ if (!(conn->flags & CO_FL_CTRL_READY))
+ goto out_error;
+
/* The target server expects a PROXY line to be sent first. Retrieving
* local or remote addresses may fail until the connection is established.
*/
static inline const char *get_conn_ctrl_name(const struct connection *conn)
{
- if (!conn->ctrl)
+ if (!(conn->flags & CO_FL_CTRL_READY) || !conn->ctrl)
return "NONE";
return conn->ctrl->name;
}
{
static char ptr[17];
- if (!conn->xprt)
+ if (!(conn->flags & CO_FL_XPRT_READY) || !conn->xprt)
return "NONE";
if (conn->xprt == &raw_sock)
req->flags |= CF_READ_DONTWAIT; /* try to get back here ASAP */
s->rep->flags &= ~CF_EXPECT_MORE; /* speed up sending a previous response */
#ifdef TCP_QUICKACK
- if (s->listener->options & LI_O_NOQUICKACK && req->buf->i && objt_conn(s->req->prod->end)) {
+ if (s->listener->options & LI_O_NOQUICKACK && req->buf->i && objt_conn(s->req->prod->end) && (__objt_conn(s->req->prod->end)->flags & CO_FL_CTRL_READY)) {
/* We need more data, we have to re-enable quick-ack in case we
* previously disabled it, otherwise we might cause the client
* to delay next data.
break;
case HTTP_REQ_ACT_SET_TOS:
- if ((cli_conn = objt_conn(s->req->prod->end)))
+ if ((cli_conn = objt_conn(s->req->prod->end)) && (cli_conn->flags & CO_FL_CTRL_READY))
inet_set_tos(cli_conn->t.sock.fd, cli_conn->addr.from, rule->arg.tos);
break;
case HTTP_REQ_ACT_SET_MARK:
#ifdef SO_MARK
- if ((cli_conn = objt_conn(s->req->prod->end)))
+ if ((cli_conn = objt_conn(s->req->prod->end)) && (cli_conn->flags & CO_FL_CTRL_READY))
setsockopt(cli_conn->t.sock.fd, SOL_SOCKET, SO_MARK, &rule->arg.mark, sizeof(rule->arg.mark));
#endif
break;
break;
case HTTP_RES_ACT_SET_TOS:
- if ((cli_conn = objt_conn(s->req->prod->end)))
+ if ((cli_conn = objt_conn(s->req->prod->end)) && (cli_conn->flags & CO_FL_CTRL_READY))
inet_set_tos(cli_conn->t.sock.fd, cli_conn->addr.from, rule->arg.tos);
break;
case HTTP_RES_ACT_SET_MARK:
#ifdef SO_MARK
- if ((cli_conn = objt_conn(s->req->prod->end)))
+ if ((cli_conn = objt_conn(s->req->prod->end)) && (cli_conn->flags & CO_FL_CTRL_READY))
setsockopt(cli_conn->t.sock.fd, SOL_SOCKET, SO_MARK, &rule->arg.mark, sizeof(rule->arg.mark));
#endif
break;
* the client to delay further data.
*/
if ((s->listener->options & LI_O_NOQUICKACK) &&
- cli_conn &&
+ cli_conn && (cli_conn->flags & CO_FL_CTRL_READY) &&
((msg->flags & HTTP_MSGF_TE_CHNK) ||
(msg->body_len > req->buf->i - txn->req.eoh - 2)))
setsockopt(cli_conn->t.sock.fd, IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one));
}
}
- fdtab[fd].owner = conn;
conn->flags = CO_FL_WAIT_L4_CONN; /* connection in progress */
conn->flags |= CO_FL_ADDR_TO_SET;
- fdtab[fd].iocb = conn_fd_handler;
- fd_insert(fd);
+ conn_ctrl_init(conn); /* registers the FD */
conn_sock_want_send(conn); /* for connect status */
if (conn_xprt_init(conn) < 0) {
- fd_delete(fd);
+ conn_force_close(conn);
return SN_ERR_RESOURCE;
}
if (conn->flags & CO_FL_ERROR)
return 0;
+ if (!(conn->flags & CO_FL_CTRL_READY))
+ return 0;
+
if (!(conn->flags & CO_FL_WAIT_L4_CONN))
return 1; /* strange we were called while ready */
int ret;
int retval = 0;
+
+ if (!(conn->flags & CO_FL_CTRL_READY))
+ return 0;
+
errno = 0;
/* Under Linux, if FD_POLL_HUP is set, we have reached the end.
{
int ret, done;
+ if (!(conn->flags & CO_FL_CTRL_READY))
+ return 0;
+
done = 0;
while (pipe->data) {
ret = splice(pipe->cons, NULL, conn->t.sock.fd, NULL, pipe->data,
int ret, done = 0;
int try = count;
+ if (!(conn->flags & CO_FL_CTRL_READY))
+ return 0;
+
errno = 0;
if (unlikely(!(fdtab[conn->t.sock.fd].ev & FD_POLL_IN))) {
{
int ret, try, done, send_flag;
+ if (!(conn->flags & CO_FL_CTRL_READY))
+ return 0;
+
done = 0;
/* send the largest possible block. For this we perform only one call
* to send() unless the buffer wraps and we exactly fill the first hunk,
conn_assign(cli_conn, &sess_conn_cb, l->proto, l->xprt, s);
/* finish initialization of the accepted file descriptor */
- fd_insert(cfd);
- fdtab[cfd].owner = cli_conn;
- fdtab[cfd].iocb = conn_fd_handler;
+ conn_ctrl_init(cli_conn);
+
conn_data_want_recv(cli_conn);
if (conn_xprt_init(cli_conn) < 0)
goto out_free_task;
session_store_counters(s);
pool_free2(pool2_connection, s->si[1].conn);
out_fail_conn1:
+ s->si[0].conn->flags &= ~CO_FL_XPRT_TRACKED;
+ conn_xprt_close(s->si[0].conn);
pool_free2(pool2_connection, s->si[0].conn);
out_fail_conn0:
pool_free2(pool2_session, s);
}
/* kill the connection now */
- conn_full_close(conn);
+ conn_force_close(conn);
s->fe->feconn--;
session_store_counters(s);
http_end_txn(s);
/* ensure the client-side transport layer is destroyed */
- if (cli_conn) {
- cli_conn->flags &= ~CO_FL_XPRT_TRACKED;
- conn_full_close(cli_conn);
- }
+ if (cli_conn)
+ conn_force_close(cli_conn);
for (i = 0; i < s->store_count; i++) {
if (!s->store[i].ts)
si->exp = TICK_ETERNITY;
si->state = SI_ST_CER;
- srv_conn->flags &= ~CO_FL_XPRT_TRACKED;
- conn_full_close(srv_conn);
+ conn_force_close(srv_conn);
if (si->err_type)
return 0;
if (conn->xprt_ctx)
return 0;
+ if (!(conn->flags & CO_FL_CTRL_READY))
+ return 0;
+
if (global.maxsslconn && sslconns >= global.maxsslconn) {
conn->err_code = CO_ER_SSL_TOO_MANY;
return -1;
{
int ret;
+ if (!(conn->flags & CO_FL_CTRL_READY))
+ return 0;
+
if (!conn->xprt_ctx)
goto out_error;
* TCP sockets. We first try to drain possibly pending
* data to avoid this as much as possible.
*/
- if (conn->ctrl && conn->ctrl->drain)
+ if ((conn->flags & CO_FL_CTRL_READY) && conn->ctrl && conn->ctrl->drain)
conn->ctrl->drain(conn->t.sock.fd);
if (!conn->err_code)
conn->err_code = CO_ER_SSL_HANDSHAKE;
* TCP sockets. We first try to drain possibly pending
* data to avoid this as much as possible.
*/
- if (conn->ctrl && conn->ctrl->drain)
+ if ((conn->flags & CO_FL_CTRL_READY) && conn->ctrl && conn->ctrl->drain)
conn->ctrl->drain(conn->t.sock.fd);
if (!conn->err_code)
conn->err_code = CO_ER_SSL_HANDSHAKE;
if (conn->flags & CO_FL_SOCK_WR_SH)
goto out_error;
+ if (!(conn->flags & CO_FL_CTRL_READY))
+ goto out_error;
+
/* If we have a PROXY line to send, we'll use this to validate the
* connection, in which case the connection is validated only once
* we've sent the whole proxy line. Otherwise we use connect().
/* quick close, the socket is alredy shut anyway */
}
else if (si->flags & SI_FL_NOLINGER) {
- if (conn->ctrl) {
+ if ((conn->flags & CO_FL_CTRL_READY) && conn->ctrl) {
setsockopt(conn->t.sock.fd, SOL_SOCKET, SO_LINGER,
(struct linger *) &nolinger, sizeof(struct linger));
}
*/
if (!(si->flags & SI_FL_NOHALF) || !(si->ib->flags & (CF_SHUTR|CF_DONT_READ))) {
/* We shutdown transport layer */
- if (conn->ctrl)
+ if ((conn->flags & CO_FL_CTRL_READY) && conn->ctrl)
shutdown(conn->t.sock.fd, SHUT_WR);
if (!(si->ib->flags & (CF_SHUTR|CF_DONT_READ))) {
/* Before calling the data-level operations, we have to prepare
* the polling flags to ensure we properly detect changes.
*/
- if (conn->ctrl)
+ if ((conn->flags & CO_FL_CTRL_READY) && conn->ctrl)
fd_want_send(conn->t.sock.fd);
conn_refresh_polling_flags(conn);
si_conn_send(conn);
- if (conn->flags & CO_FL_ERROR) {
+ if ((conn->flags & CO_FL_CTRL_READY) && (conn->flags & CO_FL_ERROR)) {
/* Write error on the file descriptor */
fd_stop_both(conn->t.sock.fd);
__conn_data_stop_both(conn);
/* we want to immediately forward this close to the write side */
if (si->flags & SI_FL_NOLINGER) {
si->flags &= ~SI_FL_NOLINGER;
- setsockopt(conn->t.sock.fd, SOL_SOCKET, SO_LINGER,
- (struct linger *) &nolinger, sizeof(struct linger));
+ if (conn->flags & CO_FL_CTRL_READY)
+ setsockopt(conn->t.sock.fd, SOL_SOCKET, SO_LINGER,
+ (struct linger *) &nolinger, sizeof(struct linger));
}
/* force flag on ssl to keep session in cache */
if (conn->xprt->shutw)