diff --git a/include/haproxy/mux_quic.h b/include/haproxy/mux_quic.h index 233a210a6..b91ef57c7 100644 --- a/include/haproxy/mux_quic.h +++ b/include/haproxy/mux_quic.h @@ -24,7 +24,7 @@ void qcs_notify_recv(struct qcs *qcs); void qcs_notify_send(struct qcs *qcs); int qcc_recv(struct qcc *qcc, uint64_t id, uint64_t len, uint64_t offset, - char fin, char *data, struct qcs **out_qcs); + char fin, char *data, struct qcs **out_qcs, size_t *done); int qcc_recv_max_data(struct qcc *qcc, uint64_t max); int qcc_recv_max_stream_data(struct qcc *qcc, uint64_t id, uint64_t max); int qcc_decode_qcs(struct qcc *qcc, struct qcs *qcs); diff --git a/src/mux_quic.c b/src/mux_quic.c index 46681968e..17adf4661 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -333,19 +333,23 @@ struct qcs *qcc_get_qcs(struct qcc *qcc, uint64_t id) * . In case of success, the caller can immediatly call qcc_decode_qcs * to process the frame content. * - * Returns 0 on success. On errors, two codes are present. - * - 1 is returned if the frame cannot be decoded and must be discarded. - * - 2 is returned if the stream cannot decode at the moment the frame. The - * frame should be buffered to be handled later. + * Returns a code indicating how the frame was handled. + * - 0: frame received completly and can be dropped. + * - 1: frame not received but can be dropped. + * - 2: frame cannot be handled, either partially or not at all. + * indicated the number of bytes handled. The rest should be buffered. */ int qcc_recv(struct qcc *qcc, uint64_t id, uint64_t len, uint64_t offset, - char fin, char *data, struct qcs **out_qcs) + char fin, char *data, struct qcs **out_qcs, size_t *done) { struct qcs *qcs; size_t total, diff; TRACE_ENTER(QMUX_EV_QCC_RECV, qcc->conn); + *out_qcs = NULL; + *done = 0; + qcs = qcc_get_qcs(qcc, id); if (!qcs) { TRACE_DEVEL("leaving on stream not found", QMUX_EV_QCC_RECV|QMUX_EV_QCC_NQCS, qcc->conn, NULL, &id); @@ -367,7 +371,7 @@ int qcc_recv(struct qcc *qcc, uint64_t id, uint64_t len, uint64_t offset, /* TODO initial max-stream-data overflow. Implement FLOW_CONTROL_ERROR emission. */ BUG_ON(offset + len > qcs->rx.msd); - if (!qc_get_buf(qcs, &qcs->rx.buf)) { + if (!qc_get_buf(qcs, &qcs->rx.buf) || b_full(&qcs->rx.buf)) { /* TODO should mark qcs as full */ return 2; } @@ -375,27 +379,24 @@ int qcc_recv(struct qcc *qcc, uint64_t id, uint64_t len, uint64_t offset, TRACE_DEVEL("newly received offset", QMUX_EV_QCC_RECV|QMUX_EV_QCS_RECV, qcc->conn, qcs); diff = qcs->rx.offset - offset; - /* TODO do not partially copy a frame if not enough size left. Maybe - * this can be optimized. - */ - if (len > b_room(&qcs->rx.buf)) { - /* TODO handle STREAM frames larger than RX buffer. */ - BUG_ON(len > b_size(&qcs->rx.buf)); - return 2; - } - len -= diff; data += diff; - total = b_putblk(&qcs->rx.buf, data, len); - /* TODO handle partial copy of a STREAM frame. */ - BUG_ON(len != total); + /* TODO handle STREAM frames larger than RX buffer. */ + BUG_ON(len > b_size(&qcs->rx.buf)); + total = b_putblk(&qcs->rx.buf, data, len); qcs->rx.offset += total; + *done = total; /* TODO initial max-stream-data reached. Implement MAX_STREAM_DATA emission. */ BUG_ON(qcs->rx.offset == qcs->rx.msd); + if (total < len) { + TRACE_DEVEL("leaving on partially received offset", QMUX_EV_QCC_RECV|QMUX_EV_QCS_RECV, qcc->conn, qcs); + return 2; + } + if (fin) qcs->flags |= QC_SF_FIN_RECV; diff --git a/src/xprt_quic.c b/src/xprt_quic.c index 5691f63d9..773b62b0d 100644 --- a/src/xprt_quic.c +++ b/src/xprt_quic.c @@ -2139,11 +2139,12 @@ static int qc_handle_bidi_strm_frm(struct quic_rx_packet *pkt, struct quic_rx_strm_frm *frm; struct eb64_node *frm_node; struct qcs *qcs = NULL; + size_t done; int ret; ret = qcc_recv(qc->qcc, strm_frm->id, strm_frm->len, strm_frm->offset.key, strm_frm->fin, - (char *)strm_frm->data, &qcs); + (char *)strm_frm->data, &qcs, &done); /* invalid or already received frame */ if (ret == 1) @@ -2160,13 +2161,22 @@ static int qc_handle_bidi_strm_frm(struct quic_rx_packet *pkt, return 0; } + if (done) { + BUG_ON(done >= frm->len); /* must never happen */ + frm->len -= done; + frm->data += done; + frm->offset_node.key += done; + } + eb64_insert(&qcs->rx.frms, &frm->offset_node); quic_rx_packet_refinc(pkt); - return 1; + /* interrupt only if frame was not received at all. */ + if (!done) + return 1; } - /* Frame correctly received by the mux. + /* Frame received (partially or not) by the mux. * If there is buffered frame for next offset, it may be possible to * receive them now. */ @@ -2177,13 +2187,23 @@ static int qc_handle_bidi_strm_frm(struct quic_rx_packet *pkt, ret = qcc_recv(qc->qcc, qcs->id, frm->len, frm->offset_node.key, frm->fin, - (char *)frm->data, &qcs); + (char *)frm->data, &qcs, &done); - /* interrupt the parsing if the frame cannot be handled for the - * moment only by the MUX. + /* interrupt the parsing if the frame cannot be handled + * entirely for the moment only. */ - if (ret == 2) + if (ret == 2) { + if (done) { + BUG_ON(done >= frm->len); /* must never happen */ + frm->len -= done; + frm->data += done; + + eb64_delete(&frm->offset_node); + frm->offset_node.key += done; + eb64_insert(&qcs->rx.frms, &frm->offset_node); + } break; + } /* Remove a newly received frame or an invalid one. */ frm_node = eb64_next(frm_node);