mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-01-12 08:49:29 +00:00
2542b53b19
When an incoming connection request is accepted, a connection structure is needed to store its state. However we don't want to fully initialize a session until the data layer is about to be ready. As long as the connection is physically stored into the session, it's not easy to split both allocations. As such, we only initialize the minimum requirements of a session, which results in what we call an embryonic session. Then once the data layer is ready, we can complete the function's initialization. Doing so avoids buffers allocation and ensures that a session only sees ready connections. The frontend's client timeout is used as the handshake timeout. It is likely that another timeout will be used in the future.
144 lines
4.8 KiB
C
144 lines
4.8 KiB
C
/*
|
|
* Connection management functions
|
|
*
|
|
* Copyright 2000-2012 Willy Tarreau <w@1wt.eu>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version
|
|
* 2 of the License, or (at your option) any later version.
|
|
*
|
|
*/
|
|
|
|
#include <common/compat.h>
|
|
#include <common/config.h>
|
|
|
|
#include <proto/connection.h>
|
|
#include <proto/proto_tcp.h>
|
|
#include <proto/session.h>
|
|
#include <proto/stream_interface.h>
|
|
|
|
/* I/O callback for fd-based connections. It calls the read/write handlers
|
|
* provided by the connection's sock_ops, which must be valid. It returns 0.
|
|
*/
|
|
int conn_fd_handler(int fd)
|
|
{
|
|
struct connection *conn = fdtab[fd].owner;
|
|
|
|
if (unlikely(!conn))
|
|
return 0;
|
|
|
|
process_handshake:
|
|
/* The handshake callbacks are called in sequence. If either of them is
|
|
* missing something, it must enable the required polling at the socket
|
|
* layer of the connection. Polling state is not guaranteed when entering
|
|
* these handlers, so any handshake handler which does not complete its
|
|
* work must explicitly disable events it's not interested in.
|
|
*/
|
|
while (unlikely(conn->flags & CO_FL_HANDSHAKE)) {
|
|
if (unlikely(conn->flags & CO_FL_ERROR))
|
|
goto leave;
|
|
|
|
if (conn->flags & CO_FL_SI_SEND_PROXY)
|
|
if (!conn_si_send_proxy(conn, CO_FL_SI_SEND_PROXY))
|
|
goto leave;
|
|
}
|
|
|
|
/* Once we're purely in the data phase, we disable handshake polling */
|
|
if (!(conn->flags & CO_FL_POLL_SOCK))
|
|
__conn_sock_stop_both(conn);
|
|
|
|
/* Maybe we need to finish initializing an incoming session. The
|
|
* function may fail and cause the connection to be destroyed, thus
|
|
* we must not use it anymore and should immediately leave instead.
|
|
*/
|
|
if ((conn->flags & CO_FL_INIT_SESS) &&
|
|
conn_session_initialize(conn, CO_FL_INIT_SESS) < 0)
|
|
return 0;
|
|
|
|
if (fdtab[fd].ev & (FD_POLL_IN | FD_POLL_HUP | FD_POLL_ERR))
|
|
conn->app_cb->recv(conn);
|
|
|
|
if (unlikely(conn->flags & CO_FL_ERROR))
|
|
goto leave;
|
|
|
|
/* It may happen during the data phase that a handshake is
|
|
* enabled again (eg: SSL)
|
|
*/
|
|
if (unlikely(conn->flags & CO_FL_HANDSHAKE))
|
|
goto process_handshake;
|
|
|
|
if (fdtab[fd].ev & (FD_POLL_OUT | FD_POLL_ERR))
|
|
conn->app_cb->send(conn);
|
|
|
|
if (unlikely(conn->flags & CO_FL_ERROR))
|
|
goto leave;
|
|
|
|
/* It may happen during the data phase that a handshake is
|
|
* enabled again (eg: SSL)
|
|
*/
|
|
if (unlikely(conn->flags & CO_FL_HANDSHAKE))
|
|
goto process_handshake;
|
|
|
|
if (unlikely(conn->flags & CO_FL_WAIT_L4_CONN)) {
|
|
/* still waiting for a connection to establish and no data to
|
|
* send in order to probe it ? Then let's retry the connect().
|
|
*/
|
|
if (!tcp_connect_probe(conn))
|
|
goto leave;
|
|
}
|
|
|
|
leave:
|
|
/* we may need to release the connection which is an embryonic session */
|
|
if ((conn->flags & (CO_FL_ERROR|CO_FL_INIT_SESS)) == (CO_FL_ERROR|CO_FL_INIT_SESS)) {
|
|
conn->flags |= CO_FL_ERROR;
|
|
conn_session_complete(conn, CO_FL_INIT_SESS);
|
|
return 0;
|
|
}
|
|
|
|
if (conn->flags & CO_FL_NOTIFY_SI)
|
|
conn_notify_si(conn);
|
|
|
|
/* Last check, verify if the connection just established */
|
|
if (unlikely(!(conn->flags & (CO_FL_WAIT_L4_CONN | CO_FL_WAIT_L6_CONN | CO_FL_CONNECTED))))
|
|
conn->flags |= CO_FL_CONNECTED;
|
|
|
|
/* remove the events before leaving */
|
|
fdtab[fd].ev &= ~(FD_POLL_IN | FD_POLL_OUT | FD_POLL_HUP | FD_POLL_ERR);
|
|
|
|
/* commit polling changes */
|
|
conn_cond_update_polling(conn);
|
|
return 0;
|
|
}
|
|
|
|
/* set polling depending on the change between the CURR part of the
|
|
* flags and the new flags in connection C. The connection flags are
|
|
* updated with the new flags at the end of the operation. Only the bits
|
|
* relevant to CO_FL_CURR_* from <flags> are considered.
|
|
*/
|
|
void conn_set_polling(struct connection *c, unsigned int new)
|
|
{
|
|
unsigned int old = c->flags; /* for CO_FL_CURR_* */
|
|
|
|
/* update read status if needed */
|
|
if ((old & (CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL)) != (CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL) &&
|
|
(new & (CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL)) == (CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL))
|
|
fd_poll_recv(c->t.sock.fd);
|
|
else if (!(old & CO_FL_CURR_RD_ENA) && (new & CO_FL_CURR_RD_ENA))
|
|
fd_want_recv(c->t.sock.fd);
|
|
else if ((old & CO_FL_CURR_RD_ENA) && !(new & CO_FL_CURR_RD_ENA))
|
|
fd_stop_recv(c->t.sock.fd);
|
|
|
|
/* update write status if needed */
|
|
if ((old & (CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL)) != (CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL) &&
|
|
(new & (CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL)) == (CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL))
|
|
fd_poll_send(c->t.sock.fd);
|
|
else if (!(old & CO_FL_CURR_WR_ENA) && (new & CO_FL_CURR_WR_ENA))
|
|
fd_want_send(c->t.sock.fd);
|
|
else if ((old & CO_FL_CURR_WR_ENA) && !(new & CO_FL_CURR_WR_ENA))
|
|
fd_stop_send(c->t.sock.fd);
|
|
|
|
c->flags &= ~(CO_FL_CURR_WR_POL|CO_FL_CURR_WR_ENA|CO_FL_CURR_RD_POL|CO_FL_CURR_RD_ENA);
|
|
c->flags |= new & (CO_FL_CURR_WR_POL|CO_FL_CURR_WR_ENA|CO_FL_CURR_RD_POL|CO_FL_CURR_RD_ENA);
|
|
}
|