mirror of
https://github.com/nginx/nginx.git
synced 2024-11-27 15:39:01 +08:00
QUIC: congestion control in ngx_quic_frame_sendto().
Previously ngx_quic_frame_sendto() ignored congestion control and did not contribute to in_flight counter. Now congestion control window is checked unless ignore_congestion flag is set. Also, in_flight counter is incremented and the frame is stored in ctx->sent queue if it's ack-eliciting. This behavior is now similar to ngx_quic_output_packet().
This commit is contained in:
parent
0c0f340554
commit
ccca701dc6
@ -593,6 +593,7 @@ ngx_quic_resend_frames(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case NGX_QUIC_FT_PING:
|
case NGX_QUIC_FT_PING:
|
||||||
|
case NGX_QUIC_FT_PATH_CHALLENGE:
|
||||||
case NGX_QUIC_FT_PATH_RESPONSE:
|
case NGX_QUIC_FT_PATH_RESPONSE:
|
||||||
case NGX_QUIC_FT_CONNECTION_CLOSE:
|
case NGX_QUIC_FT_CONNECTION_CLOSE:
|
||||||
ngx_quic_free_frame(c, f);
|
ngx_quic_free_frame(c, f);
|
||||||
@ -824,11 +825,11 @@ void ngx_quic_lost_handler(ngx_event_t *ev)
|
|||||||
void
|
void
|
||||||
ngx_quic_pto_handler(ngx_event_t *ev)
|
ngx_quic_pto_handler(ngx_event_t *ev)
|
||||||
{
|
{
|
||||||
ngx_uint_t i;
|
ngx_uint_t i, n;
|
||||||
ngx_msec_t now;
|
ngx_msec_t now;
|
||||||
ngx_queue_t *q;
|
ngx_queue_t *q;
|
||||||
ngx_connection_t *c;
|
ngx_connection_t *c;
|
||||||
ngx_quic_frame_t *f, frame;
|
ngx_quic_frame_t *f;
|
||||||
ngx_quic_send_ctx_t *ctx;
|
ngx_quic_send_ctx_t *ctx;
|
||||||
ngx_quic_connection_t *qc;
|
ngx_quic_connection_t *qc;
|
||||||
|
|
||||||
@ -865,16 +866,20 @@ ngx_quic_pto_handler(ngx_event_t *ev)
|
|||||||
"quic pto %s pto_count:%ui",
|
"quic pto %s pto_count:%ui",
|
||||||
ngx_quic_level_name(ctx->level), qc->pto_count);
|
ngx_quic_level_name(ctx->level), qc->pto_count);
|
||||||
|
|
||||||
ngx_memzero(&frame, sizeof(ngx_quic_frame_t));
|
for (n = 0; n < 2; n++) {
|
||||||
|
|
||||||
frame.level = ctx->level;
|
f = ngx_quic_alloc_frame(c);
|
||||||
frame.type = NGX_QUIC_FT_PING;
|
if (f == NULL) {
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
if (ngx_quic_frame_sendto(c, &frame, 0, qc->path) != NGX_OK
|
f->level = ctx->level;
|
||||||
|| ngx_quic_frame_sendto(c, &frame, 0, qc->path) != NGX_OK)
|
f->type = NGX_QUIC_FT_PING;
|
||||||
{
|
f->ignore_congestion = 1;
|
||||||
ngx_quic_close_connection(c, NGX_ERROR);
|
|
||||||
return;
|
if (ngx_quic_frame_sendto(c, f, 0, qc->path) == NGX_ERROR) {
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -883,6 +888,13 @@ ngx_quic_pto_handler(ngx_event_t *ev)
|
|||||||
ngx_quic_set_lost_timer(c);
|
ngx_quic_set_lost_timer(c);
|
||||||
|
|
||||||
ngx_quic_connstate_dbg(c);
|
ngx_quic_connstate_dbg(c);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
failed:
|
||||||
|
|
||||||
|
ngx_quic_close_connection(c, NGX_ERROR);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ ngx_quic_handle_path_challenge_frame(ngx_connection_t *c,
|
|||||||
ngx_quic_header_t *pkt, ngx_quic_path_challenge_frame_t *f)
|
ngx_quic_header_t *pkt, ngx_quic_path_challenge_frame_t *f)
|
||||||
{
|
{
|
||||||
size_t min;
|
size_t min;
|
||||||
ngx_quic_frame_t frame, *fp;
|
ngx_quic_frame_t *fp;
|
||||||
ngx_quic_connection_t *qc;
|
ngx_quic_connection_t *qc;
|
||||||
|
|
||||||
if (pkt->level != ssl_encryption_application || pkt->path_challenged) {
|
if (pkt->level != ssl_encryption_application || pkt->path_challenged) {
|
||||||
@ -50,11 +50,14 @@ ngx_quic_handle_path_challenge_frame(ngx_connection_t *c,
|
|||||||
|
|
||||||
qc = ngx_quic_get_connection(c);
|
qc = ngx_quic_get_connection(c);
|
||||||
|
|
||||||
ngx_memzero(&frame, sizeof(ngx_quic_frame_t));
|
fp = ngx_quic_alloc_frame(c);
|
||||||
|
if (fp == NULL) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
frame.level = ssl_encryption_application;
|
fp->level = ssl_encryption_application;
|
||||||
frame.type = NGX_QUIC_FT_PATH_RESPONSE;
|
fp->type = NGX_QUIC_FT_PATH_RESPONSE;
|
||||||
frame.u.path_response = *f;
|
fp->u.path_response = *f;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* RFC 9000, 8.2.2. Path Validation Responses
|
* RFC 9000, 8.2.2. Path Validation Responses
|
||||||
@ -73,7 +76,7 @@ ngx_quic_handle_path_challenge_frame(ngx_connection_t *c,
|
|||||||
|
|
||||||
min = (ngx_quic_path_limit(c, pkt->path, 1200) < 1200) ? 0 : 1200;
|
min = (ngx_quic_path_limit(c, pkt->path, 1200) < 1200) ? 0 : 1200;
|
||||||
|
|
||||||
if (ngx_quic_frame_sendto(c, &frame, min, pkt->path) == NGX_ERROR) {
|
if (ngx_quic_frame_sendto(c, fp, min, pkt->path) == NGX_ERROR) {
|
||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -546,22 +549,25 @@ ngx_quic_validate_path(ngx_connection_t *c, ngx_quic_path_t *path)
|
|||||||
static ngx_int_t
|
static ngx_int_t
|
||||||
ngx_quic_send_path_challenge(ngx_connection_t *c, ngx_quic_path_t *path)
|
ngx_quic_send_path_challenge(ngx_connection_t *c, ngx_quic_path_t *path)
|
||||||
{
|
{
|
||||||
size_t min;
|
size_t min;
|
||||||
ngx_uint_t n;
|
ngx_uint_t n;
|
||||||
ngx_quic_frame_t frame;
|
ngx_quic_frame_t *frame;
|
||||||
|
|
||||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||||
"quic path seq:%uL send path_challenge tries:%ui",
|
"quic path seq:%uL send path_challenge tries:%ui",
|
||||||
path->seqnum, path->tries);
|
path->seqnum, path->tries);
|
||||||
|
|
||||||
ngx_memzero(&frame, sizeof(ngx_quic_frame_t));
|
|
||||||
|
|
||||||
frame.level = ssl_encryption_application;
|
|
||||||
frame.type = NGX_QUIC_FT_PATH_CHALLENGE;
|
|
||||||
|
|
||||||
for (n = 0; n < 2; n++) {
|
for (n = 0; n < 2; n++) {
|
||||||
|
|
||||||
ngx_memcpy(frame.u.path_challenge.data, path->challenge[n], 8);
|
frame = ngx_quic_alloc_frame(c);
|
||||||
|
if (frame == NULL) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame->level = ssl_encryption_application;
|
||||||
|
frame->type = NGX_QUIC_FT_PATH_CHALLENGE;
|
||||||
|
|
||||||
|
ngx_memcpy(frame->u.path_challenge.data, path->challenge[n], 8);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* RFC 9000, 8.2.1. Initiating Path Validation
|
* RFC 9000, 8.2.1. Initiating Path Validation
|
||||||
@ -574,7 +580,7 @@ ngx_quic_send_path_challenge(ngx_connection_t *c, ngx_quic_path_t *path)
|
|||||||
|
|
||||||
min = (ngx_quic_path_limit(c, path, 1200) < 1200) ? 0 : 1200;
|
min = (ngx_quic_path_limit(c, path, 1200) < 1200) ? 0 : 1200;
|
||||||
|
|
||||||
if (ngx_quic_frame_sendto(c, &frame, min, path) == NGX_ERROR) {
|
if (ngx_quic_frame_sendto(c, frame, min, path) == NGX_ERROR) {
|
||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -883,14 +889,17 @@ ngx_quic_send_path_mtu_probe(ngx_connection_t *c, ngx_quic_path_t *path)
|
|||||||
size_t mtu;
|
size_t mtu;
|
||||||
ngx_int_t rc;
|
ngx_int_t rc;
|
||||||
ngx_uint_t log_error;
|
ngx_uint_t log_error;
|
||||||
ngx_quic_frame_t frame;
|
ngx_quic_frame_t *frame;
|
||||||
ngx_quic_send_ctx_t *ctx;
|
ngx_quic_send_ctx_t *ctx;
|
||||||
ngx_quic_connection_t *qc;
|
ngx_quic_connection_t *qc;
|
||||||
|
|
||||||
ngx_memzero(&frame, sizeof(ngx_quic_frame_t));
|
frame = ngx_quic_alloc_frame(c);
|
||||||
|
if (frame == NULL) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
frame.level = ssl_encryption_application;
|
frame->level = ssl_encryption_application;
|
||||||
frame.type = NGX_QUIC_FT_PING;
|
frame->type = NGX_QUIC_FT_PING;
|
||||||
|
|
||||||
qc = ngx_quic_get_connection(c);
|
qc = ngx_quic_get_connection(c);
|
||||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
|
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
|
||||||
@ -907,7 +916,7 @@ ngx_quic_send_path_mtu_probe(ngx_connection_t *c, ngx_quic_path_t *path)
|
|||||||
mtu = path->mtu;
|
mtu = path->mtu;
|
||||||
path->mtu = path->mtud;
|
path->mtu = path->mtud;
|
||||||
|
|
||||||
rc = ngx_quic_frame_sendto(c, &frame, path->mtud, path);
|
rc = ngx_quic_frame_sendto(c, frame, path->mtud, path);
|
||||||
|
|
||||||
path->mtu = mtu;
|
path->mtu = mtu;
|
||||||
c->log_error = log_error;
|
c->log_error = log_error;
|
||||||
|
@ -844,7 +844,7 @@ ngx_quic_send_stateless_reset(ngx_connection_t *c, ngx_quic_conf_t *conf,
|
|||||||
ngx_int_t
|
ngx_int_t
|
||||||
ngx_quic_send_cc(ngx_connection_t *c)
|
ngx_quic_send_cc(ngx_connection_t *c)
|
||||||
{
|
{
|
||||||
ngx_quic_frame_t frame;
|
ngx_quic_frame_t *frame;
|
||||||
ngx_quic_connection_t *qc;
|
ngx_quic_connection_t *qc;
|
||||||
|
|
||||||
qc = ngx_quic_get_connection(c);
|
qc = ngx_quic_get_connection(c);
|
||||||
@ -860,22 +860,27 @@ ngx_quic_send_cc(ngx_connection_t *c)
|
|||||||
return NGX_OK;
|
return NGX_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngx_memzero(&frame, sizeof(ngx_quic_frame_t));
|
frame = ngx_quic_alloc_frame(c);
|
||||||
|
if (frame == NULL) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
frame.level = qc->error_level;
|
frame->level = qc->error_level;
|
||||||
frame.type = qc->error_app ? NGX_QUIC_FT_CONNECTION_CLOSE_APP
|
frame->type = qc->error_app ? NGX_QUIC_FT_CONNECTION_CLOSE_APP
|
||||||
: NGX_QUIC_FT_CONNECTION_CLOSE;
|
: NGX_QUIC_FT_CONNECTION_CLOSE;
|
||||||
frame.u.close.error_code = qc->error;
|
frame->u.close.error_code = qc->error;
|
||||||
frame.u.close.frame_type = qc->error_ftype;
|
frame->u.close.frame_type = qc->error_ftype;
|
||||||
|
|
||||||
if (qc->error_reason) {
|
if (qc->error_reason) {
|
||||||
frame.u.close.reason.len = ngx_strlen(qc->error_reason);
|
frame->u.close.reason.len = ngx_strlen(qc->error_reason);
|
||||||
frame.u.close.reason.data = (u_char *) qc->error_reason;
|
frame->u.close.reason.data = (u_char *) qc->error_reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
frame->ignore_congestion = 1;
|
||||||
|
|
||||||
qc->last_cc = ngx_current_msec;
|
qc->last_cc = ngx_current_msec;
|
||||||
|
|
||||||
return ngx_quic_frame_sendto(c, &frame, 0, qc->path);
|
return ngx_quic_frame_sendto(c, frame, 0, qc->path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1184,22 +1189,32 @@ ngx_quic_frame_sendto(ngx_connection_t *c, ngx_quic_frame_t *frame,
|
|||||||
size_t max, max_payload, min_payload, pad;
|
size_t max, max_payload, min_payload, pad;
|
||||||
ssize_t len, sent;
|
ssize_t len, sent;
|
||||||
ngx_str_t res;
|
ngx_str_t res;
|
||||||
|
ngx_msec_t now;
|
||||||
ngx_quic_header_t pkt;
|
ngx_quic_header_t pkt;
|
||||||
ngx_quic_send_ctx_t *ctx;
|
ngx_quic_send_ctx_t *ctx;
|
||||||
|
ngx_quic_congestion_t *cg;
|
||||||
ngx_quic_connection_t *qc;
|
ngx_quic_connection_t *qc;
|
||||||
|
|
||||||
static u_char src[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
|
static u_char src[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
|
||||||
static u_char dst[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
|
static u_char dst[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
|
||||||
|
|
||||||
qc = ngx_quic_get_connection(c);
|
qc = ngx_quic_get_connection(c);
|
||||||
|
cg = &qc->congestion;
|
||||||
ctx = ngx_quic_get_send_ctx(qc, frame->level);
|
ctx = ngx_quic_get_send_ctx(qc, frame->level);
|
||||||
|
|
||||||
|
now = ngx_current_msec;
|
||||||
|
|
||||||
max = ngx_quic_path_limit(c, path, path->mtu);
|
max = ngx_quic_path_limit(c, path, path->mtu);
|
||||||
|
|
||||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||||
"quic sendto %s packet max:%uz min:%uz",
|
"quic sendto %s packet max:%uz min:%uz",
|
||||||
ngx_quic_level_name(ctx->level), max, min);
|
ngx_quic_level_name(ctx->level), max, min);
|
||||||
|
|
||||||
|
if (cg->in_flight >= cg->window && !frame->ignore_congestion) {
|
||||||
|
ngx_quic_free_frame(c, frame);
|
||||||
|
return NGX_AGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
ngx_quic_init_packet(c, ctx, &pkt, path);
|
ngx_quic_init_packet(c, ctx, &pkt, path);
|
||||||
|
|
||||||
min_payload = ngx_quic_payload_size(&pkt, min);
|
min_payload = ngx_quic_payload_size(&pkt, min);
|
||||||
@ -1210,6 +1225,7 @@ ngx_quic_frame_sendto(ngx_connection_t *c, ngx_quic_frame_t *frame,
|
|||||||
min_payload = ngx_max(min_payload, pad);
|
min_payload = ngx_max(min_payload, pad);
|
||||||
|
|
||||||
if (min_payload > max_payload) {
|
if (min_payload > max_payload) {
|
||||||
|
ngx_quic_free_frame(c, frame);
|
||||||
return NGX_AGAIN;
|
return NGX_AGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1221,11 +1237,13 @@ ngx_quic_frame_sendto(ngx_connection_t *c, ngx_quic_frame_t *frame,
|
|||||||
|
|
||||||
len = ngx_quic_create_frame(NULL, frame);
|
len = ngx_quic_create_frame(NULL, frame);
|
||||||
if ((size_t) len > max_payload) {
|
if ((size_t) len > max_payload) {
|
||||||
|
ngx_quic_free_frame(c, frame);
|
||||||
return NGX_AGAIN;
|
return NGX_AGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
len = ngx_quic_create_frame(src, frame);
|
len = ngx_quic_create_frame(src, frame);
|
||||||
if (len == -1) {
|
if (len == -1) {
|
||||||
|
ngx_quic_free_frame(c, frame);
|
||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1242,18 +1260,45 @@ ngx_quic_frame_sendto(ngx_connection_t *c, ngx_quic_frame_t *frame,
|
|||||||
ngx_quic_log_packet(c->log, &pkt);
|
ngx_quic_log_packet(c->log, &pkt);
|
||||||
|
|
||||||
if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) {
|
if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) {
|
||||||
|
ngx_quic_free_frame(c, frame);
|
||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
frame->pnum = ctx->pnum;
|
||||||
|
frame->first = now;
|
||||||
|
frame->last = now;
|
||||||
|
frame->plen = res.len;
|
||||||
|
|
||||||
ctx->pnum++;
|
ctx->pnum++;
|
||||||
|
|
||||||
sent = ngx_quic_send(c, res.data, res.len, path->sockaddr, path->socklen);
|
sent = ngx_quic_send(c, res.data, res.len, path->sockaddr, path->socklen);
|
||||||
if (sent < 0) {
|
if (sent < 0) {
|
||||||
|
ngx_quic_free_frame(c, frame);
|
||||||
return sent;
|
return sent;
|
||||||
}
|
}
|
||||||
|
|
||||||
path->sent += sent;
|
path->sent += sent;
|
||||||
|
|
||||||
|
if (frame->need_ack && !qc->closing) {
|
||||||
|
ngx_queue_insert_tail(&ctx->sent, &frame->queue);
|
||||||
|
|
||||||
|
cg->in_flight += frame->plen;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ngx_quic_free_frame(c, frame);
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||||
|
"quic congestion send if:%uz", cg->in_flight);
|
||||||
|
|
||||||
|
if (!qc->send_timer_set) {
|
||||||
|
qc->send_timer_set = 1;
|
||||||
|
ngx_add_timer(c->read, qc->tp.max_idle_timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_quic_set_lost_timer(c);
|
||||||
|
|
||||||
return NGX_OK;
|
return NGX_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,6 +271,7 @@ struct ngx_quic_frame_s {
|
|||||||
ssize_t len;
|
ssize_t len;
|
||||||
unsigned need_ack:1;
|
unsigned need_ack:1;
|
||||||
unsigned pkt_need_ack:1;
|
unsigned pkt_need_ack:1;
|
||||||
|
unsigned ignore_congestion:1;
|
||||||
|
|
||||||
ngx_chain_t *data;
|
ngx_chain_t *data;
|
||||||
union {
|
union {
|
||||||
|
Loading…
Reference in New Issue
Block a user