mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-06 23:27:04 +02:00
MEDIUM: connection: extract the send_proxy callback from proto_tcp
This handshake handler must be independant, so move it away from proto_tcp. It has a dedicated connection flag. It is tested before I/O handlers and automatically removes the CO_FL_WAIT_L4_CONN flag upon success. It also sets the BF_WRITE_NULL flag on the stream interface and stops the SI timeout. However it does not perform the task_wakeup(), and relies on the data handler to do so for now. The SI wakeup will have to be moved elsewhere anyway.
This commit is contained in:
parent
61ace1b2ca
commit
2c6be84b3a
@ -25,6 +25,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include <common/config.h>
|
#include <common/config.h>
|
||||||
|
#include <types/session.h>
|
||||||
#include <types/stream_interface.h>
|
#include <types/stream_interface.h>
|
||||||
|
|
||||||
|
|
||||||
@ -32,6 +33,7 @@
|
|||||||
int stream_int_check_timeouts(struct stream_interface *si);
|
int stream_int_check_timeouts(struct stream_interface *si);
|
||||||
void stream_int_report_error(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);
|
void stream_int_retnclose(struct stream_interface *si, const struct chunk *msg);
|
||||||
|
int conn_si_send_proxy(struct connection *conn, unsigned int flag);
|
||||||
|
|
||||||
extern struct sock_ops stream_int_embedded;
|
extern struct sock_ops stream_int_embedded;
|
||||||
extern struct sock_ops stream_int_task;
|
extern struct sock_ops stream_int_task;
|
||||||
|
@ -36,6 +36,8 @@ enum {
|
|||||||
CO_FL_NONE = 0x00000000,
|
CO_FL_NONE = 0x00000000,
|
||||||
CO_FL_ERROR = 0x00000001, /* a fatal error was reported */
|
CO_FL_ERROR = 0x00000001, /* a fatal error was reported */
|
||||||
CO_FL_WAIT_L4_CONN = 0x00000002, /* waiting for L4 to be connected */
|
CO_FL_WAIT_L4_CONN = 0x00000002, /* waiting for L4 to be connected */
|
||||||
|
/* flags below are used for connection handshakes */
|
||||||
|
CO_FL_SI_SEND_PROXY = 0x00000004, /* send a valid PROXY protocol header */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* This structure describes a connection with its methods and data.
|
/* This structure describes a connection with its methods and data.
|
||||||
|
@ -14,7 +14,8 @@
|
|||||||
#include <common/config.h>
|
#include <common/config.h>
|
||||||
|
|
||||||
#include <types/connection.h>
|
#include <types/connection.h>
|
||||||
#include <types/stream_interface.h>
|
|
||||||
|
#include <proto/stream_interface.h>
|
||||||
|
|
||||||
/* I/O callback for fd-based connections. It calls the read/write handlers
|
/* 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
|
* provided by the connection's sock_ops, which must be valid. It returns
|
||||||
@ -26,22 +27,26 @@ int conn_fd_handler(int fd)
|
|||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (!conn)
|
if (!conn)
|
||||||
return ret;
|
goto leave;
|
||||||
|
|
||||||
if (conn->flags & CO_FL_ERROR)
|
if (conn->flags & CO_FL_ERROR)
|
||||||
return ret;
|
goto leave;
|
||||||
|
|
||||||
|
if (conn->flags & CO_FL_SI_SEND_PROXY)
|
||||||
|
if ((ret = conn_si_send_proxy(conn, CO_FL_SI_SEND_PROXY)))
|
||||||
|
goto leave;
|
||||||
|
|
||||||
if (fdtab[fd].ev & (FD_POLL_IN | FD_POLL_HUP | FD_POLL_ERR))
|
if (fdtab[fd].ev & (FD_POLL_IN | FD_POLL_HUP | FD_POLL_ERR))
|
||||||
if (!conn->data->read(fd))
|
if (!conn->data->read(fd))
|
||||||
ret |= FD_WAIT_READ;
|
ret |= FD_WAIT_READ;
|
||||||
|
|
||||||
if (conn->flags & CO_FL_ERROR)
|
if (conn->flags & CO_FL_ERROR)
|
||||||
return ret;
|
goto leave;
|
||||||
|
|
||||||
if (fdtab[fd].ev & (FD_POLL_OUT | FD_POLL_ERR))
|
if (fdtab[fd].ev & (FD_POLL_OUT | FD_POLL_ERR))
|
||||||
if (!conn->data->write(fd))
|
if (!conn->data->write(fd))
|
||||||
ret |= FD_WAIT_WRITE;
|
ret |= FD_WAIT_WRITE;
|
||||||
|
leave:
|
||||||
/* remove the events before leaving */
|
/* remove the events before leaving */
|
||||||
fdtab[fd].ev &= ~(FD_POLL_IN | FD_POLL_OUT | FD_POLL_HUP | FD_POLL_ERR);
|
fdtab[fd].ev &= ~(FD_POLL_IN | FD_POLL_OUT | FD_POLL_HUP | FD_POLL_ERR);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
#include <proto/arg.h>
|
#include <proto/arg.h>
|
||||||
#include <proto/buffers.h>
|
#include <proto/buffers.h>
|
||||||
#include <proto/connection.h>
|
#include <proto/connection.h>
|
||||||
#include <proto/frontend.h>
|
//#include <proto/frontend.h>
|
||||||
#include <proto/log.h>
|
#include <proto/log.h>
|
||||||
#include <proto/port_range.h>
|
#include <proto/port_range.h>
|
||||||
#include <proto/protocols.h>
|
#include <proto/protocols.h>
|
||||||
@ -470,18 +470,20 @@ int tcp_connect_server(struct stream_interface *si)
|
|||||||
fdtab[fd].flags = FD_FL_TCP | FD_FL_TCP_NODELAY;
|
fdtab[fd].flags = FD_FL_TCP | FD_FL_TCP_NODELAY;
|
||||||
si->conn.flags = CO_FL_WAIT_L4_CONN; /* connection in progress */
|
si->conn.flags = CO_FL_WAIT_L4_CONN; /* connection in progress */
|
||||||
|
|
||||||
/* If we have nothing to send, we want to confirm that the TCP
|
/* Prepare to send a few handshakes related to the on-wire protocol.
|
||||||
|
* If we have nothing to send, we want to confirm that the TCP
|
||||||
* connection is established before doing so, so we use our own write
|
* connection is established before doing so, so we use our own write
|
||||||
* callback then switch to the sock layer.
|
* callback then switch to the sock layer.
|
||||||
*/
|
*/
|
||||||
if ((si->ob->flags & BF_OUT_EMPTY) || si->send_proxy_ofs) {
|
fdtab[fd].cb[DIR_RD].f = NULL;
|
||||||
|
fdtab[fd].cb[DIR_WR].f = NULL;
|
||||||
|
|
||||||
|
if (si->send_proxy_ofs)
|
||||||
|
si->conn.flags |= CO_FL_SI_SEND_PROXY;
|
||||||
|
else if (si->ob->flags & BF_OUT_EMPTY) {
|
||||||
fdtab[fd].cb[DIR_RD].f = tcp_connect_read;
|
fdtab[fd].cb[DIR_RD].f = tcp_connect_read;
|
||||||
fdtab[fd].cb[DIR_WR].f = tcp_connect_write;
|
fdtab[fd].cb[DIR_WR].f = tcp_connect_write;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
fdtab[fd].cb[DIR_RD].f = NULL;
|
|
||||||
fdtab[fd].cb[DIR_WR].f = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
fdtab[fd].iocb = conn_fd_handler;
|
fdtab[fd].iocb = conn_fd_handler;
|
||||||
fd_insert(fd);
|
fd_insert(fd);
|
||||||
@ -551,64 +553,23 @@ static int tcp_connect_write(int fd)
|
|||||||
if (b->flags & BF_SHUTW)
|
if (b->flags & BF_SHUTW)
|
||||||
goto out_wakeup;
|
goto out_wakeup;
|
||||||
|
|
||||||
/* If we have a PROXY line to send, we'll use this to validate the
|
/* We have no data to send to check the connection, and
|
||||||
* connection, in which case the connection is validated only once
|
* getsockopt() will not inform us whether the connection
|
||||||
* we've sent the whole proxy line. Otherwise we use connect().
|
* is still pending. So we'll reuse connect() to check the
|
||||||
|
* state of the socket. This has the advantage of giving us
|
||||||
|
* the following info :
|
||||||
|
* - error
|
||||||
|
* - connecting (EALREADY, EINPROGRESS)
|
||||||
|
* - connected (EISCONN, 0)
|
||||||
*/
|
*/
|
||||||
if (si->send_proxy_ofs) {
|
if ((connect(fd, conn->peeraddr, conn->peerlen) < 0)) {
|
||||||
int ret;
|
if (errno == EALREADY || errno == EINPROGRESS)
|
||||||
|
|
||||||
/* The target server expects a PROXY line to be sent first.
|
|
||||||
* If the send_proxy_ofs is negative, it corresponds to the
|
|
||||||
* offset to start sending from then end of the proxy string
|
|
||||||
* (which is recomputed every time since it's constant). If
|
|
||||||
* it is positive, it means we have to send from the start.
|
|
||||||
*/
|
|
||||||
ret = make_proxy_line(trash, trashlen, &b->prod->addr.from, &b->prod->addr.to);
|
|
||||||
if (!ret)
|
|
||||||
goto out_error;
|
|
||||||
|
|
||||||
if (si->send_proxy_ofs > 0)
|
|
||||||
si->send_proxy_ofs = -ret; /* first call */
|
|
||||||
|
|
||||||
/* we have to send trash from (ret+sp for -sp bytes) */
|
|
||||||
ret = send(fd, trash + ret + si->send_proxy_ofs, -si->send_proxy_ofs,
|
|
||||||
(b->flags & BF_OUT_EMPTY) ? 0 : MSG_MORE);
|
|
||||||
|
|
||||||
if (ret == 0)
|
|
||||||
goto out_ignore;
|
goto out_ignore;
|
||||||
|
|
||||||
if (ret < 0) {
|
if (errno && errno != EISCONN)
|
||||||
if (errno == EAGAIN)
|
|
||||||
goto out_ignore;
|
|
||||||
goto out_error;
|
goto out_error;
|
||||||
}
|
|
||||||
|
|
||||||
si->send_proxy_ofs += ret; /* becomes zero once complete */
|
/* otherwise we're connected */
|
||||||
if (si->send_proxy_ofs != 0)
|
|
||||||
goto out_ignore;
|
|
||||||
|
|
||||||
/* OK we've sent the whole line, we're connected */
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* We have no data to send to check the connection, and
|
|
||||||
* getsockopt() will not inform us whether the connection
|
|
||||||
* is still pending. So we'll reuse connect() to check the
|
|
||||||
* state of the socket. This has the advantage of giving us
|
|
||||||
* the following info :
|
|
||||||
* - error
|
|
||||||
* - connecting (EALREADY, EINPROGRESS)
|
|
||||||
* - connected (EISCONN, 0)
|
|
||||||
*/
|
|
||||||
if ((connect(fd, conn->peeraddr, conn->peerlen) < 0)) {
|
|
||||||
if (errno == EALREADY || errno == EINPROGRESS)
|
|
||||||
goto out_ignore;
|
|
||||||
|
|
||||||
if (errno && errno != EISCONN)
|
|
||||||
goto out_error;
|
|
||||||
|
|
||||||
/* otherwise we're connected */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* OK we just need to indicate that we got a connection
|
/* OK we just need to indicate that we got a connection
|
||||||
@ -629,7 +590,6 @@ static int tcp_connect_write(int fd)
|
|||||||
task_wakeup(si->owner, TASK_WOKEN_IO);
|
task_wakeup(si->owner, TASK_WOKEN_IO);
|
||||||
|
|
||||||
out_ignore:
|
out_ignore:
|
||||||
fdtab[fd].ev &= ~FD_POLL_OUT;
|
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
out_error:
|
out_error:
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
#include <proto/buffers.h>
|
#include <proto/buffers.h>
|
||||||
#include <proto/fd.h>
|
#include <proto/fd.h>
|
||||||
|
#include <proto/frontend.h>
|
||||||
#include <proto/sock_raw.h>
|
#include <proto/sock_raw.h>
|
||||||
#include <proto/stream_interface.h>
|
#include <proto/stream_interface.h>
|
||||||
#include <proto/task.h>
|
#include <proto/task.h>
|
||||||
@ -401,6 +402,91 @@ void stream_int_unregister_handler(struct stream_interface *si)
|
|||||||
clear_target(&si->target);
|
clear_target(&si->target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This callback is used to send a valid PROXY protocol line to a socket being
|
||||||
|
* established. It returns a combination of FD_WAIT_* if it wants some polling
|
||||||
|
* before being called again, otherwise it returns zero and removes itself from
|
||||||
|
* the connection's flags (the bit is provided in <flag> by the caller).
|
||||||
|
*/
|
||||||
|
int conn_si_send_proxy(struct connection *conn, unsigned int flag)
|
||||||
|
{
|
||||||
|
int fd = conn->t.sock.fd;
|
||||||
|
struct stream_interface *si = container_of(conn, struct stream_interface, conn);
|
||||||
|
struct buffer *b = si->ob;
|
||||||
|
|
||||||
|
/* we might have been called just after an asynchronous shutw */
|
||||||
|
if (b->flags & BF_SHUTW)
|
||||||
|
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().
|
||||||
|
*/
|
||||||
|
if (si->send_proxy_ofs) {
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* The target server expects a PROXY line to be sent first.
|
||||||
|
* If the send_proxy_ofs is negative, it corresponds to the
|
||||||
|
* offset to start sending from then end of the proxy string
|
||||||
|
* (which is recomputed every time since it's constant). If
|
||||||
|
* it is positive, it means we have to send from the start.
|
||||||
|
*/
|
||||||
|
ret = make_proxy_line(trash, trashlen, &b->prod->addr.from, &b->prod->addr.to);
|
||||||
|
if (!ret)
|
||||||
|
goto out_error;
|
||||||
|
|
||||||
|
if (si->send_proxy_ofs > 0)
|
||||||
|
si->send_proxy_ofs = -ret; /* first call */
|
||||||
|
|
||||||
|
/* we have to send trash from (ret+sp for -sp bytes) */
|
||||||
|
ret = send(fd, trash + ret + si->send_proxy_ofs, -si->send_proxy_ofs,
|
||||||
|
(b->flags & BF_OUT_EMPTY) ? 0 : MSG_MORE);
|
||||||
|
|
||||||
|
if (ret == 0)
|
||||||
|
goto out_wait;
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
goto out_wait;
|
||||||
|
goto out_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
si->send_proxy_ofs += ret; /* becomes zero once complete */
|
||||||
|
if (si->send_proxy_ofs != 0)
|
||||||
|
goto out_wait;
|
||||||
|
|
||||||
|
/* OK we've sent the whole line, we're connected */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The FD is ready now, simply return and let the connection handler
|
||||||
|
* notify upper layers if needed.
|
||||||
|
*/
|
||||||
|
if (conn->flags & CO_FL_WAIT_L4_CONN)
|
||||||
|
conn->flags &= ~CO_FL_WAIT_L4_CONN;
|
||||||
|
b->flags |= BF_WRITE_NULL;
|
||||||
|
si->exp = TICK_ETERNITY;
|
||||||
|
|
||||||
|
out_leave:
|
||||||
|
conn->flags &= ~flag;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out_error:
|
||||||
|
/* Write error on the file descriptor. We mark the FD as STERROR so
|
||||||
|
* that we don't use it anymore. The error is reported to the stream
|
||||||
|
* interface which will take proper action. We must not perturbate the
|
||||||
|
* buffer because the stream interface wants to ensure transparent
|
||||||
|
* connection retries.
|
||||||
|
*/
|
||||||
|
|
||||||
|
conn->flags |= CO_FL_ERROR;
|
||||||
|
fdtab[fd].ev &= ~FD_POLL_STICKY;
|
||||||
|
EV_FD_REM(fd);
|
||||||
|
goto out_leave;
|
||||||
|
|
||||||
|
out_wait:
|
||||||
|
return FD_WAIT_WRITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Local variables:
|
* Local variables:
|
||||||
* c-indent-level: 8
|
* c-indent-level: 8
|
||||||
|
Loading…
Reference in New Issue
Block a user