When default "connection" value is set, a dedicated socket will be allocated
by every QUIC connections. This option is the preferred one to achieve the
best performance with a large QUIC traffic. This is also the only way to
- ensure soft-stop is conducted properly without data loss for QUIC
- connections. However, this relies on some advanced features from the UDP
+ ensure soft-stop is conducted properly without data loss for QUIC connections
+ and cases of transient errors during sendto() operation are handled
+ efficiently. However, this relies on some advanced features from the UDP
network stack. If your platform is deemed not compatible, haproxy will
automatically switch to "listener" mode on startup.
*
* This function returns 1 for success. On error, there is several behavior
* depending on underlying sendto() error :
- * - for a fatal error, 0 is returned and connection is killed.
- * - a transient error is assimilated to a success case with 1 returned.
+ * - for an unrecoverable error, 0 is returned and connection is killed.
+ * - a transient error is handled differently if connection has its owned
+ * socket. If this is the case, 0 is returned and socket is subscribed on the
+ * poller. The other case is assimilated to a success case with 1 returned.
* Remaining data are purged from the buffer and will eventually be detected
* as lost which gives the opportunity to retry sending.
*/
if (!skip_sendto) {
int ret = qc_snd_buf(qc, &tmpbuf, tmpbuf.data, 0);
if (ret < 0) {
+ TRACE_ERROR("sendto fatal error", QUIC_EV_CONN_SPPKTS, qc);
qc_kill_conn(qc);
b_del(buf, buf->data);
goto leave;
}
else if (!ret) {
+ /* Connection owned socket : poller will wake us up when transient error is cleared. */
+ if (qc_test_fd(qc)) {
+ TRACE_ERROR("sendto error, subscribe to poller", QUIC_EV_CONN_SPPKTS, qc);
+ goto leave;
+ }
+
+ /* No connection owned-socket : rely on retransmission to retry sending. */
skip_sendto = 1;
TRACE_ERROR("sendto error, simulate sending for the rest of data", QUIC_EV_CONN_SPPKTS, qc);
}
break;
if (!qc_send_ppkts(buf, qc->xprt_ctx)) {
- qc_txb_release(qc);
+ if (qc->flags & QUIC_FL_CONN_TO_KILL)
+ qc_txb_release(qc);
goto err;
}
}
}
if (ret && !qc_send_ppkts(buf, qc->xprt_ctx)) {
- qc_txb_release(qc);
+ if (qc->flags & QUIC_FL_CONN_TO_KILL)
+ qc_txb_release(qc);
goto out;
}
}
if (ret && !qc_send_ppkts(buf, qc->xprt_ctx)) {
- qc_txb_release(qc);
+ if (qc->flags & QUIC_FL_CONN_TO_KILL)
+ qc_txb_release(qc);
goto out;
}
TRACE_ENTER(QUIC_EV_CONN_RCV, qc);
- tasklet_wakeup_after(NULL, qc->wait_event.tasklet);
- fd_stop_recv(fd);
+ if (fd_send_active(fd) && fd_send_ready(fd)) {
+ TRACE_DEVEL("send ready", QUIC_EV_CONN_RCV, qc);
+ fd_stop_send(fd);
+ tasklet_wakeup_after(NULL, qc->wait_event.tasklet);
+ }
+
+ if (fd_recv_ready(fd)) {
+ tasklet_wakeup_after(NULL, qc->wait_event.tasklet);
+ fd_stop_recv(fd);
+ }
TRACE_LEAVE(QUIC_EV_CONN_RCV, qc);
}
do {
if (qc_test_fd(qc)) {
+ if (!fd_send_ready(qc->fd))
+ return 0;
+
ret = send(qc->fd, b_peek(buf, b_head_ofs(buf)), sz,
MSG_DONTWAIT | MSG_NOSIGNAL);
}
HA_ATOMIC_INC(&prx_counters->sendto_err);
/* transient error */
+ fd_want_send(qc->fd);
+ fd_cant_send(qc->fd);
TRACE_PRINTF(TRACE_LEVEL_USER, QUIC_EV_CONN_SPPKTS, qc, 0, 0, 0,
"UDP send failure errno=%d (%s)", errno, strerror(errno));
return 0;