1
0
mirror of http://git.haproxy.org/git/haproxy.git/ synced 2025-04-07 01:31:35 +00:00

MEDIUM: mux-h2: start to update stream when sending WU

The rationale here is that we don't absolutely need to update the
stream offset live, there's already the rcvd_s counter to remind
us we've received data. So we can continue to exploit the current
check points for this.

Now we know that rcvd_s indicates the amount of newly received bytes
for the stream since last call to h2c_send_strm_wu() so we can update
our stream offsets within that function. The wu_s counter is set to
the difference between next_adv_ofs and last_adv_ofs, which are
resynchronized once the frame is sent.

If the stream suddenly disappears with unacked data (aborted upload),
the presence of the last update in h2c->wu_s is sufficient to let the
connection ack the data alone, and upon subsequent calls with new
rcvd_s, the received counter will be used to ack, like before. We
don't need to do more anyway since the goal is to let the client
abort ASAP when it gets an RST.

At this point, the stream knows its current rx offset, the computed
max offset and the last advertised one.
This commit is contained in:
Willy Tarreau 2024-08-27 19:33:25 +02:00
parent eb0fe66c61
commit 1cc851d9f2

View File

@ -2637,28 +2637,57 @@ static int h2c_send_conn_wu(struct h2c *h2c)
return ret;
}
/* try to send pending window update for the current dmux stream. It's safe to
/* Recalculate the current stream's rx window based on h2c->rcvd_s, which is
* reset if consumed. The amount of bytes to ACK is the difference between
* next_max_ofs and last_adv_ofs, and is put into h2c->wu_s. For dummy streams,
* rcvd_s is directly transferred to wu_s so that it outlives the stream. The
* function returns non-zero if the resulting wu_s is non-zero, indicating that
* a WU is deserved, otherwise zero.
*/
static int h2c_update_strm_rx_win(struct h2c *h2c)
{
struct h2s *h2s;
h2s = h2c_st_by_id(h2c, h2c->dsi);
if (h2s && h2s->h2c) {
/* real stream */
h2s->curr_rx_ofs += h2c->rcvd_s;
h2s->next_max_ofs += h2c->rcvd_s;
h2c->rcvd_s = 0;
h2c->wu_s = (h2s->next_max_ofs > h2s->last_adv_ofs) ?
(h2s->next_max_ofs - h2s->last_adv_ofs) :
0;
} else {
/* non-existing stream */
h2c->wu_s += h2c->rcvd_s;
h2c->rcvd_s = 0;
}
return !!h2c->wu_s;
}
/* Try to send pending window update for the current dmux stream. It's safe to
* call it with no pending updates. Returns > 0 on success or zero on missing
* room or failure. It may return an error in h2c.
*/
static int h2c_send_strm_wu(struct h2c *h2c)
{
struct h2s *h2s = NULL;
int ret = 1;
TRACE_ENTER(H2_EV_TX_FRAME|H2_EV_TX_WU, h2c->conn);
if (h2c->rcvd_s) {
h2c->wu_s += h2c->rcvd_s;
h2c->rcvd_s = 0;
}
if (h2c->wu_s <= 0)
goto out;
/* send WU for the stream */
ret = h2c_send_window_update(h2c, h2c->dsi, h2c->wu_s);
if (ret > 0)
if (ret > 0) {
h2c->wu_s = 0;
h2s = h2c_st_by_id(h2c, h2c->dsi);
if (h2s)
h2s->last_adv_ofs = h2s->next_max_ofs;
}
out:
TRACE_LEAVE(H2_EV_TX_FRAME|H2_EV_TX_WU, h2c->conn);
return ret;
@ -3734,7 +3763,7 @@ static void h2_process_demux(struct h2c *h2c)
break;
}
if ((h2c->rcvd_s || h2c->wu_s) && h2c->dsi != hdr.sid) {
if (h2c_update_strm_rx_win(h2c) && h2c->dsi != hdr.sid) {
/* changed stream with a pending WU, need to
* send it now.
*/
@ -3996,7 +4025,7 @@ static void h2_process_demux(struct h2c *h2c)
}
}
if ((h2c->rcvd_s || h2c->wu_s) &&
if (h2c_update_strm_rx_win(h2c) &&
!(h2c->flags & (H2_CF_MUX_MFULL | H2_CF_DEM_MROOM))) {
TRACE_PROTO("sending stream WINDOW_UPDATE frame", H2_EV_TX_FRAME|H2_EV_TX_WU, h2c->conn, h2s);
h2c_send_strm_wu(h2c);
@ -4125,7 +4154,7 @@ static int h2_process_mux(struct h2c *h2c)
}
/* start by sending possibly pending window updates */
if ((h2c->rcvd_s || h2c->wu_s) &&
if (h2c_update_strm_rx_win(h2c) &&
!(h2c->flags & (H2_CF_MUX_MFULL | H2_CF_MUX_MALLOC)) &&
h2c_send_strm_wu(h2c) < 0)
goto fail;