list[hdr].n = ist("");
+ start:
if (!(res = qcc_get_stream_txbuf(qcs))) {
TRACE_ERROR("cannot allocate Tx buffer", H3_EV_TX_FRAME|H3_EV_TX_HDR, qcs->qcc->conn, qcs);
h3c->err = H3_INTERNAL_ERROR;
/* At least 9 bytes to store frame type + length as a varint max size */
if (b_room(res) < 9) {
- /* TODO */
TRACE_STATE("not enough room for trailers frame", H3_EV_TX_FRAME|H3_EV_TX_HDR, qcs->qcc->conn, qcs);
- ABORT_NOW();
+ if (qcc_release_stream_txbuf(qcs))
+ goto end;
+
+ /* Buffer released, restart processing. */
+ goto start;
}
/* Force buffer realignment as size required to encode headers is unknown. */
headers_buf = b_make(b_peek(res, b_data(res) + 9), b_contig_space(res) - 9, 0, 0);
if (qpack_encode_field_section_line(&headers_buf)) {
- /* TODO */
TRACE_STATE("not enough room for trailers section line", H3_EV_TX_FRAME|H3_EV_TX_HDR, qcs->qcc->conn, qcs);
- ABORT_NOW();
+ if (qcc_release_stream_txbuf(qcs))
+ goto end;
+
+ /* Buffer released, restart processing. */
+ goto start;
}
tail = b_tail(&headers_buf);
}
if (qpack_encode_header(&headers_buf, list[hdr].n, list[hdr].v)) {
- /* TODO */
TRACE_STATE("not enough room for all trailers", H3_EV_TX_FRAME|H3_EV_TX_HDR, qcs->qcc->conn, qcs);
- ABORT_NOW();
+ if (qcc_release_stream_txbuf(qcs))
+ goto end;
+
+ /* Buffer released, restart processing. */
+ goto start;
}
}
/* TODO buffer can be realign only if no data waiting for ACK. */
outbuf = b_make(b_tail(res), b_contig_space(res), 0, 0);
+ /* Not enough room for headers and at least one data byte, try to
+ * release the current buffer and allocate a new one. If not possible,
+ * stconn layer will subscribe on SEND.
+ */
if (b_size(&outbuf) <= hsize) {
- /* TODO */
TRACE_STATE("not enough room for data frame", H3_EV_TX_FRAME|H3_EV_TX_DATA, qcs->qcc->conn, qcs);
- ABORT_NOW();
+ if (qcc_release_stream_txbuf(qcs))
+ goto end;
+
+ /* Buffer released, restart processing. */
+ goto new_frame;
}
if (b_size(&outbuf) < hsize + fsize)
htx = htx_from_buf(buf);
- while (count && !htx_is_empty(htx) && !h3c->err) {
+ while (count && !htx_is_empty(htx) && qcc_stream_can_send(qcs) &&
+ !h3c->err) {
idx = htx_get_head(htx);
blk = htx_get_blk(htx, idx);
TRACE_ENTER(H3_EV_STRM_SEND, qcs->qcc->conn, qcs);
+ start:
if (!(res = qcc_get_stream_txbuf(qcs))) {
qcs->sd->iobuf.flags |= IOBUF_FL_NO_FF;
goto end;
* on SEND.
*/
if (b_contig_space(res) <= hsize) {
- /* TODO */
- qcs->sd->iobuf.flags |= IOBUF_FL_FF_BLOCKED;
- goto end;
+ if (qcc_release_stream_txbuf(qcs)) {
+ qcs->sd->iobuf.flags |= IOBUF_FL_FF_BLOCKED;
+ goto end;
+ }
+
+ /* Buffer released, restart processing. */
+ goto start;
}
/* Cannot forward more than available room in output buffer */
htx = htx_from_buf(buf);
- while (count && !htx_is_empty(htx)) {
+ while (count && !htx_is_empty(htx) && qcc_stream_can_send(qcs)) {
/* Not implemented : QUIC on backend side */
idx = htx_get_head(htx);
blk = htx_get_blk(htx, idx);
fsize = b_contig_space(res);
if (!fsize) {
- /* TODO */
- ABORT_NOW();
+ /* Release buf and restart parsing if sending still possible. */
+ qcc_release_stream_txbuf(qcs);
+ continue;
}
b_putblk(res, htx_get_blk_ptr(htx, blk), fsize);
int ret = 0;
struct buffer *res;
+ start:
res = qcc_get_stream_txbuf(qcs);
if (!res) {
qcs->sd->iobuf.flags |= IOBUF_FL_FF_BLOCKED;
}
if (!b_room(res)) {
- /* TODO */
- ABORT_NOW();
+ if (qcc_release_stream_txbuf(qcs)) {
+ qcs->sd->iobuf.flags |= IOBUF_FL_FF_BLOCKED;
+ goto end;
+ }
+
+ goto start;
}
/* No header required for HTTP/0.9, no need to reserve an offset. */
return b_data(out) - diff;
}
+/* Release the current <qcs> Tx buffer. This is useful if space left is not
+ * enough anymore. A new instance can then be allocated to continue sending.
+ *
+ * This operation fails if there is not yet sent bytes in the buffer. In this
+ * case, stream layer should interrupt sending until further notification.
+ *
+ * Returns 0 if buffer is released and a new one can be allocated or non-zero
+ * if there is still remaining data.
+ */
+int qcc_release_stream_txbuf(struct qcs *qcs)
+{
+ const uint64_t bytes = qcs_prep_bytes(qcs);
+
+ /* Cannot release buffer if prepared data is not fully sent. */
+ if (bytes) {
+ qcs->flags |= QC_SF_BLK_MROOM;
+ return 1;
+ }
+
+ qc_stream_buf_release(qcs->stream);
+ return 0;
+}
+
+/* Returns true if stream layer can proceed to emission via <qcs>. */
+int qcc_stream_can_send(const struct qcs *qcs)
+{
+ return !(qcs->flags & QC_SF_BLK_MROOM);
+}
+
/* Wakes up every streams of <qcc> which are currently waiting for sending but
* are blocked on connection flow control.
*/
QMUX_EV_QCS_SEND, qcc->conn, qcs);
}
/* Release buffer if everything sent and buf is full or stream is waiting for room. */
- if (!qcs_prep_bytes(qcs) && (b_full(&qcs->stream->buf->buf))) {
+ if (!qcs_prep_bytes(qcs) &&
+ (b_full(&qcs->stream->buf->buf) || qcs->flags & QC_SF_BLK_MROOM)) {
qc_stream_buf_release(qcs->stream);
+ qcs->flags &= ~QC_SF_BLK_MROOM;
+ qcs_notify_send(qcs);
}
/* Add measurement for send rate. This is done at the MUX layer