From 707117276ed252e39c75769a140cbac6e18eb74a Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 2 Jul 2020 16:47:51 +0300 Subject: [PATCH] HTTP/3: close QUIC connection with HTTP/QPACK errors when needed. Previously errors led only to closing streams. To simplify closing QUIC connection from a QUIC stream context, new macro ngx_http_v3_finalize_connection() is introduced. It calls ngx_quic_finalize_connection() for the parent connection. --- src/http/ngx_http_request.c | 12 +- src/http/v3/ngx_http_v3.h | 3 + src/http/v3/ngx_http_v3_parse.c | 239 ++++++++++++++++-------------- src/http/v3/ngx_http_v3_parse.h | 10 ++ src/http/v3/ngx_http_v3_request.c | 18 +++ src/http/v3/ngx_http_v3_streams.c | 58 ++++++-- src/http/v3/ngx_http_v3_tables.c | 22 +-- 7 files changed, 223 insertions(+), 139 deletions(-) diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 89e554bf2..c953386d4 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -416,19 +416,21 @@ static void ngx_http_quic_stream_handler(ngx_connection_t *c) { ngx_event_t *rev; - ngx_connection_t *pc; ngx_http_log_ctx_t *ctx; ngx_http_connection_t *hc; ngx_http_v3_connection_t *h3c; - pc = c->qs->parent; - h3c = pc->data; + h3c = c->qs->parent->data; if (!h3c->settings_sent) { h3c->settings_sent = 1; - /* TODO close QUIC connection on error */ - (void) ngx_http_v3_send_settings(c); + if (ngx_http_v3_send_settings(c) != NGX_OK) { + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, + "could not send settings"); + ngx_http_close_connection(c); + return; + } } if (c->qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) { diff --git a/src/http/v3/ngx_http_v3.h b/src/http/v3/ngx_http_v3.h index d74939fd3..d4ea19aed 100644 --- a/src/http/v3/ngx_http_v3.h +++ b/src/http/v3/ngx_http_v3.h @@ -80,6 +80,9 @@ ((ngx_http_v3_connection_t *) c->qs->parent->data)->hc.conf_ctx, \ module) +#define ngx_http_v3_finalize_connection(c, code, reason) \ + ngx_quic_finalize_connection(c->qs->parent, code, reason) + typedef struct { ngx_quic_tp_t quic; diff --git a/src/http/v3/ngx_http_v3_parse.c b/src/http/v3/ngx_http_v3_parse.c index b50b21618..27484e92a 100644 --- a/src/http/v3/ngx_http_v3_parse.c +++ b/src/http/v3/ngx_http_v3_parse.c @@ -160,7 +160,7 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse headers"); if (ch != NGX_HTTP_V3_FRAME_HEADERS) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; } st->state = sw_length; @@ -183,21 +183,21 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, case sw_prefix: if (st->length-- == 0) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_FRAME_ERROR; } rc = ngx_http_v3_parse_header_block_prefix(c, &st->prefix, ch); - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - - if (rc != NGX_DONE) { + if (rc == NGX_AGAIN) { break; } + if (rc != NGX_DONE) { + return rc; + } + if (st->length == 0) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_FRAME_ERROR; } st->state = sw_verify; @@ -218,24 +218,25 @@ ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, rc = ngx_http_v3_parse_header_rep(c, &st->header_rep, st->prefix.base, ch); + st->length--; - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - - if (--st->length == 0) { - if (rc != NGX_DONE) { - return NGX_ERROR; + if (rc == NGX_AGAIN) { + if (st->length == 0) { + return NGX_HTTP_V3_ERR_FRAME_ERROR; } + break; + } + + if (rc != NGX_DONE) { + return rc; + } + + if (st->length == 0) { goto done; } - if (rc == NGX_DONE) { - return NGX_OK; - } - - break; + return NGX_OK; } return NGX_AGAIN; @@ -259,6 +260,7 @@ ngx_int_t ngx_http_v3_parse_header_block_prefix(ngx_connection_t *c, ngx_http_v3_parse_header_block_prefix_t *st, u_char ch) { + ngx_int_t rc; enum { sw_start = 0, sw_req_insert_count, @@ -308,8 +310,9 @@ ngx_http_v3_parse_header_block_prefix(ngx_connection_t *c, done: - if (ngx_http_v3_decode_insert_count(c, &st->insert_count) != NGX_OK) { - return NGX_ERROR; + rc = ngx_http_v3_decode_insert_count(c, &st->insert_count); + if (rc != NGX_OK) { + return rc; } if (st->sign) { @@ -404,16 +407,10 @@ ngx_http_v3_parse_header_rep(ngx_connection_t *c, rc = NGX_OK; } - if (rc == NGX_ERROR) { - return NGX_ERROR; + if (rc != NGX_DONE) { + return rc; } - if (rc == NGX_AGAIN) { - return NGX_AGAIN; - } - - /* rc == NGX_DONE */ - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header representation done"); @@ -448,7 +445,7 @@ ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st, if (n > v3cf->max_field_size) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client exceeded http3_max_field_size limit"); - return NGX_ERROR; + return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD; } if (st->huffman) { @@ -505,6 +502,7 @@ ngx_int_t ngx_http_v3_parse_header_ri(ngx_connection_t *c, ngx_http_v3_parse_header_t *st, u_char ch) { + ngx_int_t rc; enum { sw_start = 0, sw_index @@ -543,11 +541,10 @@ done: st->index = st->base - st->index - 1; } - if (ngx_http_v3_parse_lookup(c, st->dynamic, st->index, &st->name, - &st->value) - != NGX_OK) - { - return NGX_ERROR; + rc = ngx_http_v3_parse_lookup(c, st->dynamic, st->index, &st->name, + &st->value); + if (rc != NGX_OK) { + return rc; } st->state = sw_start; @@ -614,15 +611,15 @@ ngx_http_v3_parse_header_lri(ngx_connection_t *c, rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - if (rc == NGX_DONE) { st->value = st->literal.value; goto done; } + if (rc != NGX_AGAIN) { + return rc; + } + break; } @@ -639,10 +636,9 @@ done: st->index = st->base - st->index - 1; } - if (ngx_http_v3_parse_lookup(c, st->dynamic, st->index, &st->name, NULL) - != NGX_OK) - { - return NGX_ERROR; + rc = ngx_http_v3_parse_lookup(c, st->dynamic, st->index, &st->name, NULL); + if (rc != NGX_OK) { + return rc; } st->state = sw_start; @@ -693,13 +689,14 @@ ngx_http_v3_parse_header_l(ngx_connection_t *c, rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - if (rc == NGX_DONE) { st->name = st->literal.value; st->state = sw_value_len; + break; + } + + if (rc != NGX_AGAIN) { + return rc; } break; @@ -729,15 +726,15 @@ ngx_http_v3_parse_header_l(ngx_connection_t *c, rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - if (rc == NGX_DONE) { st->value = st->literal.value; goto done; } + if (rc != NGX_AGAIN) { + return rc; + } + break; } @@ -758,6 +755,7 @@ ngx_int_t ngx_http_v3_parse_header_pbi(ngx_connection_t *c, ngx_http_v3_parse_header_t *st, u_char ch) { + ngx_int_t rc; enum { sw_start = 0, sw_index @@ -790,11 +788,10 @@ done: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse header pbi done dynamic[+%ui]", st->index); - if (ngx_http_v3_parse_lookup(c, 1, st->base + st->index, &st->name, - &st->value) - != NGX_OK) - { - return NGX_ERROR; + rc = ngx_http_v3_parse_lookup(c, 1, st->base + st->index, &st->name, + &st->value); + if (rc != NGX_OK) { + return rc; } st->state = sw_start; @@ -861,15 +858,15 @@ ngx_http_v3_parse_header_lpbi(ngx_connection_t *c, rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - if (rc == NGX_DONE) { st->value = st->literal.value; goto done; } + if (rc != NGX_AGAIN) { + return rc; + } + break; } @@ -881,10 +878,9 @@ done: "http3 parse header lpbi done dynamic[+%ui] \"%V\"", st->index, &st->value); - if (ngx_http_v3_parse_lookup(c, 1, st->base + st->index, &st->name, NULL) - != NGX_OK) - { - return NGX_ERROR; + rc = ngx_http_v3_parse_lookup(c, 1, st->base + st->index, &st->name, NULL); + if (rc != NGX_OK) { + return rc; } st->state = sw_start; @@ -899,11 +895,15 @@ ngx_http_v3_parse_lookup(ngx_connection_t *c, ngx_uint_t dynamic, u_char *p; if (!dynamic) { - return ngx_http_v3_lookup_static(c, index, name, value); + if (ngx_http_v3_lookup_static(c, index, name, value) != NGX_OK) { + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; + } + + return NGX_OK; } if (ngx_http_v3_lookup(c, index, name, value) != NGX_OK) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; } if (name) { @@ -940,6 +940,7 @@ ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch) ngx_int_t rc; enum { sw_start = 0, + sw_first_type, sw_type, sw_length, sw_settings, @@ -953,10 +954,11 @@ ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse control"); - st->state = sw_type; + st->state = sw_first_type; /* fall through */ + case sw_first_type: case sw_type: if (ngx_http_v3_parse_varlen_int(c, &st->vlint, ch) != NGX_DONE) { @@ -968,6 +970,12 @@ ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse frame type:%ui", st->type); + if (st->state == sw_first_type + && st->type != NGX_HTTP_V3_FRAME_SETTINGS) + { + return NGX_HTTP_V3_ERR_MISSING_SETTINGS; + } + st->state = sw_length; break; @@ -1008,19 +1016,24 @@ ngx_http_v3_parse_control(ngx_connection_t *c, void *data, u_char ch) rc = ngx_http_v3_parse_settings(c, &st->settings, ch); - if (rc == NGX_ERROR) { - return NGX_ERROR; - } + st->length--; + + if (rc == NGX_AGAIN) { + if (st->length == 0) { + return NGX_HTTP_V3_ERR_SETTINGS_ERROR; + } - if (--st->length > 0) { break; } if (rc != NGX_DONE) { - return NGX_ERROR; + return rc; + } + + if (st->length == 0) { + st->state = sw_type; } - st->state = sw_type; break; case sw_max_push_id: @@ -1085,7 +1098,7 @@ ngx_http_v3_parse_settings(ngx_connection_t *c, } if (ngx_http_v3_set_param(c, st->id, st->vlint.value) != NGX_OK) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_SETTINGS_ERROR; } goto done; @@ -1149,12 +1162,12 @@ ngx_http_v3_parse_encoder(ngx_connection_t *c, void *data, u_char ch) rc = ngx_http_v3_parse_header_inr(c, &st->header, ch); - if (rc == NGX_ERROR) { - return NGX_ERROR; + if (rc == NGX_AGAIN) { + break; } if (rc != NGX_DONE) { - break; + return rc; } goto done; @@ -1163,12 +1176,12 @@ ngx_http_v3_parse_encoder(ngx_connection_t *c, void *data, u_char ch) rc = ngx_http_v3_parse_header_iwnr(c, &st->header, ch); - if (rc == NGX_ERROR) { - return NGX_ERROR; + if (rc == NGX_AGAIN) { + break; } if (rc != NGX_DONE) { - break; + return rc; } goto done; @@ -1179,8 +1192,9 @@ ngx_http_v3_parse_encoder(ngx_connection_t *c, void *data, u_char ch) break; } - if (ngx_http_v3_set_capacity(c, st->pint.value) != NGX_OK) { - return NGX_ERROR; + rc = ngx_http_v3_set_capacity(c, st->pint.value); + if (rc != NGX_OK) { + return rc; } goto done; @@ -1191,8 +1205,9 @@ ngx_http_v3_parse_encoder(ngx_connection_t *c, void *data, u_char ch) break; } - if (ngx_http_v3_duplicate(c, st->pint.value) != NGX_OK) { - return NGX_ERROR; + rc = ngx_http_v3_duplicate(c, st->pint.value); + if (rc != NGX_OK) { + return rc; } goto done; @@ -1269,15 +1284,15 @@ ngx_http_v3_parse_header_inr(ngx_connection_t *c, rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - if (rc == NGX_DONE) { st->value = st->literal.value; goto done; } + if (rc != NGX_AGAIN) { + return rc; + } + break; } @@ -1290,9 +1305,9 @@ done: st->dynamic ? "dynamic" : "static", st->index, &st->value); - if (ngx_http_v3_ref_insert(c, st->dynamic, st->index, &st->value) != NGX_OK) - { - return NGX_ERROR; + rc = ngx_http_v3_ref_insert(c, st->dynamic, st->index, &st->value); + if (rc != NGX_OK) { + return rc; } st->state = sw_start; @@ -1344,13 +1359,14 @@ ngx_http_v3_parse_header_iwnr(ngx_connection_t *c, rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - if (rc == NGX_DONE) { st->name = st->literal.value; st->state = sw_value_len; + break; + } + + if (rc != NGX_AGAIN) { + return rc; } break; @@ -1380,15 +1396,15 @@ ngx_http_v3_parse_header_iwnr(ngx_connection_t *c, rc = ngx_http_v3_parse_literal(c, &st->literal, ch); - if (rc == NGX_ERROR) { - return NGX_ERROR; - } - if (rc == NGX_DONE) { st->value = st->literal.value; goto done; } + if (rc != NGX_AGAIN) { + return rc; + } + break; } @@ -1400,8 +1416,9 @@ done: "http3 parse header iwnr done \"%V\":\"%V\"", &st->name, &st->value); - if (ngx_http_v3_insert(c, &st->name, &st->value) != NGX_OK) { - return NGX_ERROR; + rc = ngx_http_v3_insert(c, &st->name, &st->value); + if (rc != NGX_OK) { + return rc; } st->state = sw_start; @@ -1414,6 +1431,7 @@ ngx_http_v3_parse_decoder(ngx_connection_t *c, void *data, u_char ch) { ngx_http_v3_parse_decoder_t *st = data; + ngx_int_t rc; enum { sw_start = 0, sw_ack_header, @@ -1451,8 +1469,9 @@ ngx_http_v3_parse_decoder(ngx_connection_t *c, void *data, u_char ch) break; } - if (ngx_http_v3_ack_header(c, st->pint.value) != NGX_OK) { - return NGX_ERROR; + rc = ngx_http_v3_ack_header(c, st->pint.value); + if (rc != NGX_OK) { + return rc; } goto done; @@ -1463,8 +1482,9 @@ ngx_http_v3_parse_decoder(ngx_connection_t *c, void *data, u_char ch) break; } - if (ngx_http_v3_cancel_stream(c, st->pint.value) != NGX_OK) { - return NGX_ERROR; + rc = ngx_http_v3_cancel_stream(c, st->pint.value); + if (rc != NGX_OK) { + return rc; } goto done; @@ -1475,8 +1495,9 @@ ngx_http_v3_parse_decoder(ngx_connection_t *c, void *data, u_char ch) break; } - if (ngx_http_v3_inc_insert_count(c, st->pint.value) != NGX_OK) { - return NGX_ERROR; + rc = ngx_http_v3_inc_insert_count(c, st->pint.value); + if (rc != NGX_OK) { + return rc; } goto done; @@ -1521,7 +1542,7 @@ ngx_http_v3_parse_data(ngx_connection_t *c, ngx_http_v3_parse_data_t *st, } if (st->vlint.value != NGX_HTTP_V3_FRAME_DATA) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; } st->state = sw_length; diff --git a/src/http/v3/ngx_http_v3_parse.h b/src/http/v3/ngx_http_v3_parse.h index 17ff6c777..0c0af33b7 100644 --- a/src/http/v3/ngx_http_v3_parse.h +++ b/src/http/v3/ngx_http_v3_parse.h @@ -112,6 +112,16 @@ typedef struct { } ngx_http_v3_parse_data_t; +/* + * Parse functions return codes: + * NGX_DONE - parsing done + * NGX_OK - sub-element done + * NGX_AGAIN - more data expected + * NGX_BUSY - waiting for external event + * NGX_ERROR - internal error + * NGX_HTTP_V3_ERROR_XXX - HTTP/3 or QPACK error + */ + ngx_int_t ngx_http_v3_parse_varlen_int(ngx_connection_t *c, ngx_http_v3_parse_varlen_int_t *st, u_char ch); ngx_int_t ngx_http_v3_parse_prefix_int(ngx_connection_t *c, diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index ae65ba9ea..0ffa8927d 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -66,6 +66,12 @@ ngx_http_v3_parse_request(ngx_http_request_t *r, ngx_buf_t *b) while (b->pos < b->last) { rc = ngx_http_v3_parse_headers(c, st, *b->pos); + if (rc > 0) { + ngx_http_v3_finalize_connection(c, rc, + "could not parse request headers"); + goto failed; + } + if (rc == NGX_ERROR) { goto failed; } @@ -180,6 +186,12 @@ ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b, while (b->pos < b->last) { rc = ngx_http_v3_parse_headers(c, st, *b->pos++); + if (rc > 0) { + ngx_http_v3_finalize_connection(c, rc, + "could not parse request headers"); + return NGX_HTTP_PARSE_INVALID_HEADER; + } + if (rc == NGX_ERROR) { return NGX_HTTP_PARSE_INVALID_HEADER; } @@ -359,6 +371,12 @@ ngx_http_v3_parse_request_body(ngx_http_request_t *r, ngx_buf_t *b, while (b->pos < b->last) { rc = ngx_http_v3_parse_data(c, st, *b->pos++); + if (rc > 0) { + ngx_http_v3_finalize_connection(c, rc, + "could not parse request body"); + goto failed; + } + if (rc == NGX_ERROR) { goto failed; } diff --git a/src/http/v3/ngx_http_v3_streams.c b/src/http/v3/ngx_http_v3_streams.c index 229ce5bf4..8eaa7fde6 100644 --- a/src/http/v3/ngx_http_v3_streams.c +++ b/src/http/v3/ngx_http_v3_streams.c @@ -39,6 +39,8 @@ ngx_http_v3_handle_client_uni_stream(ngx_connection_t *c) us = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_uni_stream_t)); if (us == NULL) { + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, + NULL); ngx_http_v3_close_uni_stream(c); return; } @@ -85,7 +87,7 @@ ngx_http_v3_read_uni_stream_type(ngx_event_t *rev) { u_char ch; ssize_t n; - ngx_int_t index; + ngx_int_t index, rc; ngx_connection_t *c; ngx_http_v3_connection_t *h3c; ngx_http_v3_uni_stream_t *us; @@ -100,12 +102,18 @@ ngx_http_v3_read_uni_stream_type(ngx_event_t *rev) n = c->recv(c, &ch, 1); - if (n == NGX_ERROR) { + if (n == NGX_AGAIN) { + break; + } + + if (n == 0) { + rc = NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR; goto failed; } - if (n == NGX_AGAIN || n != 1) { - break; + if (n != 1) { + rc = NGX_HTTP_V3_ERR_INTERNAL_ERROR; + goto failed; } switch (ch) { @@ -154,6 +162,7 @@ ngx_http_v3_read_uni_stream_type(ngx_event_t *rev) if (index >= 0) { if (h3c->known_streams[index]) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "stream exists"); + rc = NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR; goto failed; } @@ -164,6 +173,7 @@ ngx_http_v3_read_uni_stream_type(ngx_event_t *rev) if (n) { us->data = ngx_pcalloc(c->pool, n); if (us->data == NULL) { + rc = NGX_HTTP_V3_ERR_INTERNAL_ERROR; goto failed; } } @@ -174,6 +184,7 @@ ngx_http_v3_read_uni_stream_type(ngx_event_t *rev) } if (ngx_handle_read_event(rev, 0) != NGX_OK) { + rc = NGX_HTTP_V3_ERR_INTERNAL_ERROR; goto failed; } @@ -181,6 +192,7 @@ ngx_http_v3_read_uni_stream_type(ngx_event_t *rev) failed: + ngx_http_v3_finalize_connection(c, rc, "could not read stream type"); ngx_http_v3_close_uni_stream(c); } @@ -203,10 +215,22 @@ ngx_http_v3_uni_read_handler(ngx_event_t *rev) n = c->recv(c, buf, sizeof(buf)); - if (n == NGX_ERROR || n == 0) { + if (n == NGX_ERROR) { + rc = NGX_HTTP_V3_ERR_INTERNAL_ERROR; goto failed; } + if (n == 0) { + if (us->index >= 0) { + rc = NGX_HTTP_V3_ERR_CLOSED_CRITICAL_STREAM; + goto failed; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read eof"); + ngx_http_v3_close_uni_stream(c); + return; + } + if (n == NGX_AGAIN) { break; } @@ -219,30 +243,34 @@ ngx_http_v3_uni_read_handler(ngx_event_t *rev) rc = us->handler(c, us->data, buf[i]); - if (rc == NGX_ERROR) { + if (rc == NGX_DONE) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 read done"); + ngx_http_v3_close_uni_stream(c); + return; + } + + if (rc > 0) { goto failed; } - if (rc == NGX_DONE) { - goto done; + if (rc != NGX_AGAIN) { + rc = NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR; + goto failed; } - - /* rc == NGX_AGAIN */ } } if (ngx_handle_read_event(rev, 0) != NGX_OK) { + rc = NGX_HTTP_V3_ERR_INTERNAL_ERROR; goto failed; } return; -done: - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read done"); - failed: + ngx_http_v3_finalize_connection(c, rc, "stream error"); ngx_http_v3_close_uni_stream(c); } @@ -257,6 +285,8 @@ ngx_http_v3_dummy_write_handler(ngx_event_t *wev) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 dummy write handler"); if (ngx_handle_write_event(wev, 0) != NGX_OK) { + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, + NULL); ngx_http_v3_close_uni_stream(c); } } diff --git a/src/http/v3/ngx_http_v3_tables.c b/src/http/v3/ngx_http_v3_tables.c index a57eccb9f..446601b01 100644 --- a/src/http/v3/ngx_http_v3_tables.c +++ b/src/http/v3/ngx_http_v3_tables.c @@ -167,7 +167,7 @@ ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, "http3 ref insert dynamic[%ui] \"%V\"", index, value); if (ngx_http_v3_lookup(c, index, &name, NULL) != NGX_OK) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; } } else { @@ -175,7 +175,7 @@ ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, "http3 ref insert static[%ui] \"%V\"", index, value); if (ngx_http_v3_lookup_static(c, index, &name, NULL) != NGX_OK) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; } } @@ -195,7 +195,7 @@ ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) size = ngx_http_v3_table_entry_size(name, value); if (ngx_http_v3_evict(c, size) != NGX_OK) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; } h3c = c->qs->parent->data; @@ -257,14 +257,14 @@ ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) if (capacity > v3cf->max_table_capacity) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client exceeded http3_max_table_capacity limit"); - return NGX_ERROR; + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; } dt = &h3c->table; if (dt->size > capacity) { if (ngx_http_v3_evict(c, dt->size - capacity) != NGX_OK) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; } } @@ -371,13 +371,13 @@ ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index) dt = &h3c->table; if (dt->base + dt->nelts <= index) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; } index = dt->base + dt->nelts - 1 - index; if (ngx_http_v3_lookup(c, index, &name, &value) != NGX_OK) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; } return ngx_http_v3_insert(c, &name, &value); @@ -515,7 +515,7 @@ ngx_http_v3_decode_insert_count(ngx_connection_t *c, ngx_uint_t *insert_count) full_range = 2 * max_entries; if (*insert_count > full_range) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; } max_value = dt->base + dt->nelts + max_entries; @@ -524,14 +524,14 @@ ngx_http_v3_decode_insert_count(ngx_connection_t *c, ngx_uint_t *insert_count) if (req_insert_count > max_value) { if (req_insert_count <= full_range) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; } req_insert_count -= full_range; } if (req_insert_count == 0) { - return NGX_ERROR; + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, @@ -601,7 +601,7 @@ ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count) if (h3c->nblocked == v3cf->max_blocked_streams) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client exceeded http3_max_blocked_streams limit"); - return NGX_ERROR; + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; } h3c->nblocked++;