[PATCH] QUIC: stream lingering
Vladimir Homutov
vl at nginx.com
Fri Feb 4 13:56:23 UTC 2022
On Tue, Feb 01, 2022 at 04:39:59PM +0300, Roman Arutyunyan wrote:
> # HG changeset patch
> # User Roman Arutyunyan <arut at nginx.com>
> # Date 1643722727 -10800
> # Tue Feb 01 16:38:47 2022 +0300
> # Branch quic
> # Node ID db31ae16c1f2050be9c9f6b1f117ab6725b97dd4
> # Parent 308ac307b3e6952ef0c5ccf10cc82904c59fa4c3
> QUIC: stream lingering.
>
> Now ngx_quic_stream_t is decoupled from ngx_connection_t in a way that it
> can persist after connection is closed by application. During this period,
> server is expecting stream final size from client for correct flow control.
> Also, buffered output is sent to client as more flow control credit is granted.
>
[..]
> +static ngx_int_t
> +ngx_quic_stream_flush(ngx_quic_stream_t *qs)
> +{
> + size_t limit, len;
> + ngx_uint_t last;
> + ngx_chain_t *out, *cl;
> + ngx_quic_frame_t *frame;
> + ngx_connection_t *pc;
> + ngx_quic_connection_t *qc;
> +
> + if (qs->send_state != NGX_QUIC_STREAM_SEND_SEND) {
> + return NGX_OK;
> + }
> +
> + pc = qs->parent;
> + qc = ngx_quic_get_connection(pc);
> +
> + limit = ngx_quic_max_stream_flow(qs);
> + last = 0;
> +
> + out = ngx_quic_read_chain(pc, &qs->out, limit);
> + if (out == NGX_CHAIN_ERROR) {
> + return NGX_ERROR;
> + }
> +
> + len = 0;
> + last = 0;
this assignment looks duplicate.
[..]
> +static ngx_int_t
> +ngx_quic_close_stream(ngx_quic_stream_t *qs)
> +{
> ngx_connection_t *pc;
> ngx_quic_frame_t *frame;
> - ngx_quic_stream_t *qs;
> ngx_quic_connection_t *qc;
>
> - qs = c->quic;
> pc = qs->parent;
> qc = ngx_quic_get_connection(pc);
>
> - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
> - "quic stream id:0x%xL cleanup", qs->id);
> + if (!qc->closing) {
> + if (qs->recv_state == NGX_QUIC_STREAM_RECV_RECV
> + || qs->send_state == NGX_QUIC_STREAM_SEND_READY
> + || qs->send_state == NGX_QUIC_STREAM_SEND_SEND)
> + {
so basically this are the states where we need to wait for FIN?
and thus avoid closing till we get it.
I would add a comment here.
[..]
> + if (qs->connection == NULL) {
> + return ngx_quic_close_stream(qs);
> + }
> +
> ngx_quic_set_event(qs->connection->write);
this pattern - check connection, close if NULL and set event seem to
repeat. Maybe it's worth to try to put this check/action into
ngx_quic_set_event somehow ? we could instead have
set_read_event/set_write_event maybe.
> +static ngx_int_t
> +ngx_quic_stream_flush(ngx_quic_stream_t *qs)
> +
[..]
> + if (len == 0 && !last) {
> + return NGX_OK;
> + }
> +
> + frame = ngx_quic_alloc_frame(pc);
> + if (frame == NULL) {
> + return NGX_ERROR;
> + }
> +
> + frame = ngx_quic_alloc_frame(pc);
> + if (frame == NULL) {
> + return NGX_ERROR;
> + }
one more dup here.
Overal, it looks good, but the testing revealed another issue: with big
buffer sizes we run into issue of too long chains in ngx_quic_write_chain().
As discussed, this certainly needs optimization - probably adding some
pointer to the end to facilitate appending, or something else.
More information about the nginx-devel
mailing list