Mercurial > hg > nginx
changeset 8925:18d23ed15eef quic
HTTP/3: renamed files.
ngx_http_v3_tables.h and ngx_http_v3_tables.c are renamed to
ngx_http_v3_table.h and ngx_http_v3_table.c to better match HTTP/2 code.
ngx_http_v3_streams.h and ngx_http_v3_streams.c are renamed to
ngx_http_v3_uni.h and ngx_http_v3_uni.c to better match their content.
author | Roman Arutyunyan <arut@nginx.com> |
---|---|
date | Tue, 07 Dec 2021 13:01:28 +0300 |
parents | d6ef13c5fd8e |
children | 3341e4089c6c |
files | auto/modules src/http/v3/ngx_http_v3.h src/http/v3/ngx_http_v3_streams.c src/http/v3/ngx_http_v3_streams.h src/http/v3/ngx_http_v3_table.c src/http/v3/ngx_http_v3_table.h src/http/v3/ngx_http_v3_tables.c src/http/v3/ngx_http_v3_tables.h src/http/v3/ngx_http_v3_uni.c src/http/v3/ngx_http_v3_uni.h |
diffstat | 10 files changed, 1508 insertions(+), 1508 deletions(-) [+] |
line wrap: on
line diff
--- a/auto/modules Mon Dec 06 15:19:54 2021 +0300 +++ b/auto/modules Tue Dec 07 13:01:28 2021 +0300 @@ -448,13 +448,13 @@ ngx_module_deps="src/http/v3/ngx_http_v3.h \ src/http/v3/ngx_http_v3_encode.h \ src/http/v3/ngx_http_v3_parse.h \ - src/http/v3/ngx_http_v3_tables.h \ - src/http/v3/ngx_http_v3_streams.h" + src/http/v3/ngx_http_v3_table.h \ + src/http/v3/ngx_http_v3_uni.h" ngx_module_srcs="src/http/v3/ngx_http_v3.c \ src/http/v3/ngx_http_v3_encode.c \ src/http/v3/ngx_http_v3_parse.c \ - src/http/v3/ngx_http_v3_tables.c \ - src/http/v3/ngx_http_v3_streams.c \ + src/http/v3/ngx_http_v3_table.c \ + src/http/v3/ngx_http_v3_uni.c \ src/http/v3/ngx_http_v3_request.c \ src/http/v3/ngx_http_v3_module.c" ngx_module_libs=
--- a/src/http/v3/ngx_http_v3.h Mon Dec 06 15:19:54 2021 +0300 +++ b/src/http/v3/ngx_http_v3.h Tue Dec 07 13:01:28 2021 +0300 @@ -15,8 +15,8 @@ #include <ngx_http_v3_parse.h> #include <ngx_http_v3_encode.h> -#include <ngx_http_v3_streams.h> -#include <ngx_http_v3_tables.h> +#include <ngx_http_v3_uni.h> +#include <ngx_http_v3_table.h> #define NGX_HTTP_V3_ALPN_PROTO "\x02h3"
--- a/src/http/v3/ngx_http_v3_streams.c Mon Dec 06 15:19:54 2021 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,733 +0,0 @@ - -/* - * Copyright (C) Roman Arutyunyan - * Copyright (C) Nginx, Inc. - */ - - -#include <ngx_config.h> -#include <ngx_core.h> -#include <ngx_http.h> - - -typedef struct { - ngx_http_v3_parse_uni_t parse; - ngx_int_t index; -} ngx_http_v3_uni_stream_t; - - -typedef struct { - ngx_queue_t queue; - uint64_t id; - ngx_connection_t *connection; - ngx_uint_t *npushing; -} ngx_http_v3_push_t; - - -static void ngx_http_v3_close_uni_stream(ngx_connection_t *c); -static void ngx_http_v3_uni_read_handler(ngx_event_t *rev); -static void ngx_http_v3_dummy_write_handler(ngx_event_t *wev); -static void ngx_http_v3_push_cleanup(void *data); -static ngx_connection_t *ngx_http_v3_get_uni_stream(ngx_connection_t *c, - ngx_uint_t type); - - -void -ngx_http_v3_init_uni_stream(ngx_connection_t *c) -{ - uint64_t n; - ngx_http_v3_uni_stream_t *us; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init uni stream"); - - n = c->quic->id >> 2; - - if (n >= NGX_HTTP_V3_MAX_UNI_STREAMS) { - ngx_http_v3_finalize_connection(c, - NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR, - "reached maximum number of uni streams"); - c->data = NULL; - ngx_http_v3_close_uni_stream(c); - return; - } - - c->quic->cancelable = 1; - - 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, - "memory allocation error"); - c->data = NULL; - ngx_http_v3_close_uni_stream(c); - return; - } - - us->index = -1; - - c->data = us; - - c->read->handler = ngx_http_v3_uni_read_handler; - c->write->handler = ngx_http_v3_dummy_write_handler; - - ngx_http_v3_uni_read_handler(c->read); -} - - -static void -ngx_http_v3_close_uni_stream(ngx_connection_t *c) -{ - ngx_pool_t *pool; - ngx_http_v3_session_t *h3c; - ngx_http_v3_uni_stream_t *us; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 close stream"); - - us = c->data; - - if (us && us->index >= 0) { - h3c = ngx_http_v3_get_session(c); - h3c->known_streams[us->index] = NULL; - } - - c->destroyed = 1; - - pool = c->pool; - - ngx_close_connection(c); - - ngx_destroy_pool(pool); -} - - -ngx_int_t -ngx_http_v3_register_uni_stream(ngx_connection_t *c, uint64_t type) -{ - ngx_int_t index; - ngx_http_v3_session_t *h3c; - ngx_http_v3_uni_stream_t *us; - - h3c = ngx_http_v3_get_session(c); - - switch (type) { - - case NGX_HTTP_V3_STREAM_ENCODER: - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 encoder stream"); - index = NGX_HTTP_V3_STREAM_CLIENT_ENCODER; - break; - - case NGX_HTTP_V3_STREAM_DECODER: - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 decoder stream"); - index = NGX_HTTP_V3_STREAM_CLIENT_DECODER; - break; - - case NGX_HTTP_V3_STREAM_CONTROL: - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 control stream"); - index = NGX_HTTP_V3_STREAM_CLIENT_CONTROL; - - break; - - default: - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 stream 0x%02xL", type); - - if (h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_ENCODER] == NULL - || h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_DECODER] == NULL - || h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_CONTROL] == NULL) - { - ngx_log_error(NGX_LOG_INFO, c->log, 0, "missing mandatory stream"); - return NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR; - } - - index = -1; - } - - if (index >= 0) { - if (h3c->known_streams[index]) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, "stream exists"); - return NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR; - } - - h3c->known_streams[index] = c; - - us = c->data; - us->index = index; - } - - return NGX_OK; -} - - -static void -ngx_http_v3_uni_read_handler(ngx_event_t *rev) -{ - u_char buf[128]; - ssize_t n; - ngx_buf_t b; - ngx_int_t rc; - ngx_connection_t *c; - ngx_http_v3_session_t *h3c; - ngx_http_v3_uni_stream_t *us; - - c = rev->data; - us = c->data; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read handler"); - - ngx_memzero(&b, sizeof(ngx_buf_t)); - - while (rev->ready) { - - n = c->recv(c, buf, sizeof(buf)); - - 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; - } - - b.pos = buf; - b.last = buf + n; - - h3c = ngx_http_v3_get_session(c); - h3c->total_bytes += n; - - if (ngx_http_v3_check_flood(c) != NGX_OK) { - ngx_http_v3_close_uni_stream(c); - return; - } - - rc = ngx_http_v3_parse_uni(c, &us->parse, &b); - - 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_AGAIN) { - rc = NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR; - goto failed; - } - } - - if (ngx_handle_read_event(rev, 0) != NGX_OK) { - rc = NGX_HTTP_V3_ERR_INTERNAL_ERROR; - goto failed; - } - - return; - -failed: - - ngx_http_v3_finalize_connection(c, rc, "stream error"); - ngx_http_v3_close_uni_stream(c); -} - - -static void -ngx_http_v3_dummy_write_handler(ngx_event_t *wev) -{ - ngx_connection_t *c; - - c = wev->data; - - 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); - } -} - - -/* XXX async & buffered stream writes */ - -ngx_connection_t * -ngx_http_v3_create_push_stream(ngx_connection_t *c, uint64_t push_id) -{ - u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 2]; - size_t n; - ngx_connection_t *sc; - ngx_pool_cleanup_t *cln; - ngx_http_v3_push_t *push; - ngx_http_v3_session_t *h3c; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 create push stream id:%uL", push_id); - - sc = ngx_quic_open_stream(c, 0); - if (sc == NULL) { - goto failed; - } - - p = buf; - p = (u_char *) ngx_http_v3_encode_varlen_int(p, NGX_HTTP_V3_STREAM_PUSH); - p = (u_char *) ngx_http_v3_encode_varlen_int(p, push_id); - n = p - buf; - - h3c = ngx_http_v3_get_session(c); - h3c->total_bytes += n; - - if (sc->send(sc, buf, n) != (ssize_t) n) { - goto failed; - } - - cln = ngx_pool_cleanup_add(sc->pool, sizeof(ngx_http_v3_push_t)); - if (cln == NULL) { - goto failed; - } - - h3c->npushing++; - - cln->handler = ngx_http_v3_push_cleanup; - - push = cln->data; - push->id = push_id; - push->connection = sc; - push->npushing = &h3c->npushing; - - ngx_queue_insert_tail(&h3c->pushing, &push->queue); - - return sc; - -failed: - - ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to create push stream"); - - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR, - "failed to create push stream"); - if (sc) { - ngx_http_v3_close_uni_stream(sc); - } - - return NULL; -} - - -static void -ngx_http_v3_push_cleanup(void *data) -{ - ngx_http_v3_push_t *push = data; - - ngx_queue_remove(&push->queue); - (*push->npushing)--; -} - - -static ngx_connection_t * -ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type) -{ - u_char buf[NGX_HTTP_V3_VARLEN_INT_LEN]; - size_t n; - ngx_int_t index; - ngx_connection_t *sc; - ngx_http_v3_session_t *h3c; - ngx_http_v3_uni_stream_t *us; - - switch (type) { - case NGX_HTTP_V3_STREAM_ENCODER: - index = NGX_HTTP_V3_STREAM_SERVER_ENCODER; - break; - case NGX_HTTP_V3_STREAM_DECODER: - index = NGX_HTTP_V3_STREAM_SERVER_DECODER; - break; - case NGX_HTTP_V3_STREAM_CONTROL: - index = NGX_HTTP_V3_STREAM_SERVER_CONTROL; - break; - default: - index = -1; - } - - h3c = ngx_http_v3_get_session(c); - - if (index >= 0) { - if (h3c->known_streams[index]) { - return h3c->known_streams[index]; - } - } - - sc = ngx_quic_open_stream(c, 0); - if (sc == NULL) { - goto failed; - } - - sc->quic->cancelable = 1; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 create uni stream, type:%ui", type); - - us = ngx_pcalloc(sc->pool, sizeof(ngx_http_v3_uni_stream_t)); - if (us == NULL) { - goto failed; - } - - us->index = index; - - sc->data = us; - - sc->read->handler = ngx_http_v3_uni_read_handler; - sc->write->handler = ngx_http_v3_dummy_write_handler; - - if (index >= 0) { - h3c->known_streams[index] = sc; - } - - n = (u_char *) ngx_http_v3_encode_varlen_int(buf, type) - buf; - - h3c = ngx_http_v3_get_session(c); - h3c->total_bytes += n; - - if (sc->send(sc, buf, n) != (ssize_t) n) { - goto failed; - } - - return sc; - -failed: - - ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to create server stream"); - - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR, - "failed to create server stream"); - if (sc) { - ngx_http_v3_close_uni_stream(sc); - } - - return NULL; -} - - -ngx_int_t -ngx_http_v3_send_settings(ngx_connection_t *c) -{ - u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 6]; - size_t n; - ngx_connection_t *cc; - ngx_http_v3_session_t *h3c; - ngx_http_v3_srv_conf_t *h3scf; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 send settings"); - - cc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_CONTROL); - if (cc == NULL) { - return NGX_ERROR; - } - - h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); - - n = ngx_http_v3_encode_varlen_int(NULL, - NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY); - n += ngx_http_v3_encode_varlen_int(NULL, h3scf->max_table_capacity); - n += ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_PARAM_BLOCKED_STREAMS); - n += ngx_http_v3_encode_varlen_int(NULL, h3scf->max_blocked_streams); - - p = (u_char *) ngx_http_v3_encode_varlen_int(buf, - NGX_HTTP_V3_FRAME_SETTINGS); - p = (u_char *) ngx_http_v3_encode_varlen_int(p, n); - p = (u_char *) ngx_http_v3_encode_varlen_int(p, - NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY); - p = (u_char *) ngx_http_v3_encode_varlen_int(p, h3scf->max_table_capacity); - p = (u_char *) ngx_http_v3_encode_varlen_int(p, - NGX_HTTP_V3_PARAM_BLOCKED_STREAMS); - p = (u_char *) ngx_http_v3_encode_varlen_int(p, h3scf->max_blocked_streams); - n = p - buf; - - h3c = ngx_http_v3_get_session(c); - h3c->total_bytes += n; - - if (cc->send(cc, buf, n) != (ssize_t) n) { - goto failed; - } - - return NGX_OK; - -failed: - - ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to send settings"); - - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, - "failed to send settings"); - ngx_http_v3_close_uni_stream(cc); - - return NGX_ERROR; -} - - -ngx_int_t -ngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id) -{ - u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 3]; - size_t n; - ngx_connection_t *cc; - ngx_http_v3_session_t *h3c; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 send goaway %uL", id); - - cc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_CONTROL); - if (cc == NULL) { - return NGX_ERROR; - } - - n = ngx_http_v3_encode_varlen_int(NULL, id); - p = (u_char *) ngx_http_v3_encode_varlen_int(buf, NGX_HTTP_V3_FRAME_GOAWAY); - p = (u_char *) ngx_http_v3_encode_varlen_int(p, n); - p = (u_char *) ngx_http_v3_encode_varlen_int(p, id); - n = p - buf; - - h3c = ngx_http_v3_get_session(c); - h3c->total_bytes += n; - - if (cc->send(cc, buf, n) != (ssize_t) n) { - goto failed; - } - - return NGX_OK; - -failed: - - ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to send goaway"); - - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, - "failed to send goaway"); - ngx_http_v3_close_uni_stream(cc); - - return NGX_ERROR; -} - - -ngx_int_t -ngx_http_v3_send_ack_section(ngx_connection_t *c, ngx_uint_t stream_id) -{ - u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; - size_t n; - ngx_connection_t *dc; - ngx_http_v3_session_t *h3c; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 send section acknowledgement %ui", stream_id); - - dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); - if (dc == NULL) { - return NGX_ERROR; - } - - buf[0] = 0x80; - n = (u_char *) ngx_http_v3_encode_prefix_int(buf, stream_id, 7) - buf; - - h3c = ngx_http_v3_get_session(c); - h3c->total_bytes += n; - - if (dc->send(dc, buf, n) != (ssize_t) n) { - goto failed; - } - - return NGX_OK; - -failed: - - ngx_log_error(NGX_LOG_ERR, c->log, 0, - "failed to send section acknowledgement"); - - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, - "failed to send section acknowledgement"); - ngx_http_v3_close_uni_stream(dc); - - return NGX_ERROR; -} - - -ngx_int_t -ngx_http_v3_send_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) -{ - u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; - size_t n; - ngx_connection_t *dc; - ngx_http_v3_session_t *h3c; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 send stream cancellation %ui", stream_id); - - dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); - if (dc == NULL) { - return NGX_ERROR; - } - - buf[0] = 0x40; - n = (u_char *) ngx_http_v3_encode_prefix_int(buf, stream_id, 6) - buf; - - h3c = ngx_http_v3_get_session(c); - h3c->total_bytes += n; - - if (dc->send(dc, buf, n) != (ssize_t) n) { - goto failed; - } - - return NGX_OK; - -failed: - - ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to send stream cancellation"); - - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, - "failed to send stream cancellation"); - ngx_http_v3_close_uni_stream(dc); - - return NGX_ERROR; -} - - -ngx_int_t -ngx_http_v3_send_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) -{ - u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; - size_t n; - ngx_connection_t *dc; - ngx_http_v3_session_t *h3c; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 send insert count increment %ui", inc); - - dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); - if (dc == NULL) { - return NGX_ERROR; - } - - buf[0] = 0; - n = (u_char *) ngx_http_v3_encode_prefix_int(buf, inc, 6) - buf; - - h3c = ngx_http_v3_get_session(c); - h3c->total_bytes += n; - - if (dc->send(dc, buf, n) != (ssize_t) n) { - goto failed; - } - - return NGX_OK; - -failed: - - ngx_log_error(NGX_LOG_ERR, c->log, 0, - "failed to send insert count increment"); - - ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, - "failed to send insert count increment"); - ngx_http_v3_close_uni_stream(dc); - - return NGX_ERROR; -} - - -ngx_int_t -ngx_http_v3_set_max_push_id(ngx_connection_t *c, uint64_t max_push_id) -{ - ngx_http_v3_session_t *h3c; - - h3c = ngx_http_v3_get_session(c); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 MAX_PUSH_ID:%uL", max_push_id); - - if (h3c->max_push_id != (uint64_t) -1 && max_push_id < h3c->max_push_id) { - return NGX_HTTP_V3_ERR_ID_ERROR; - } - - h3c->max_push_id = max_push_id; - - return NGX_OK; -} - - -ngx_int_t -ngx_http_v3_goaway(ngx_connection_t *c, uint64_t push_id) -{ - ngx_http_v3_session_t *h3c; - - h3c = ngx_http_v3_get_session(c); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 GOAWAY:%uL", push_id); - - h3c->goaway_push_id = push_id; - - return NGX_OK; -} - - -ngx_int_t -ngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id) -{ - ngx_queue_t *q; - ngx_http_request_t *r; - ngx_http_v3_push_t *push; - ngx_http_v3_session_t *h3c; - - h3c = ngx_http_v3_get_session(c); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 CANCEL_PUSH:%uL", push_id); - - if (push_id >= h3c->next_push_id) { - return NGX_HTTP_V3_ERR_ID_ERROR; - } - - for (q = ngx_queue_head(&h3c->pushing); - q != ngx_queue_sentinel(&h3c->pushing); - q = ngx_queue_next(&h3c->pushing)) - { - push = (ngx_http_v3_push_t *) q; - - if (push->id != push_id) { - continue; - } - - r = push->connection->data; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http3 cancel push"); - - ngx_http_finalize_request(r, NGX_HTTP_CLOSE); - - break; - } - - return NGX_OK; -} - - -ngx_int_t -ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) -{ - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 cancel stream %ui", stream_id); - - /* we do not use dynamic tables */ - - return NGX_OK; -}
--- a/src/http/v3/ngx_http_v3_streams.h Mon Dec 06 15:19:54 2021 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ - -/* - * Copyright (C) Roman Arutyunyan - * Copyright (C) Nginx, Inc. - */ - - -#ifndef _NGX_HTTP_V3_STREAMS_H_INCLUDED_ -#define _NGX_HTTP_V3_STREAMS_H_INCLUDED_ - - -#include <ngx_config.h> -#include <ngx_core.h> -#include <ngx_http.h> - - -void ngx_http_v3_init_uni_stream(ngx_connection_t *c); -ngx_int_t ngx_http_v3_register_uni_stream(ngx_connection_t *c, uint64_t type); - -ngx_connection_t *ngx_http_v3_create_push_stream(ngx_connection_t *c, - uint64_t push_id); -ngx_int_t ngx_http_v3_set_max_push_id(ngx_connection_t *c, - uint64_t max_push_id); -ngx_int_t ngx_http_v3_goaway(ngx_connection_t *c, uint64_t push_id); -ngx_int_t ngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id); -ngx_int_t ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id); - -ngx_int_t ngx_http_v3_send_settings(ngx_connection_t *c); -ngx_int_t ngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id); -ngx_int_t ngx_http_v3_send_ack_section(ngx_connection_t *c, - ngx_uint_t stream_id); -ngx_int_t ngx_http_v3_send_cancel_stream(ngx_connection_t *c, - ngx_uint_t stream_id); -ngx_int_t ngx_http_v3_send_inc_insert_count(ngx_connection_t *c, - ngx_uint_t inc); - - -#endif /* _NGX_HTTP_V3_STREAMS_H_INCLUDED_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/http/v3/ngx_http_v3_table.c Tue Dec 07 13:01:28 2021 +0300 @@ -0,0 +1,678 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include <ngx_config.h> +#include <ngx_core.h> +#include <ngx_http.h> + + +#define ngx_http_v3_table_entry_size(n, v) ((n)->len + (v)->len + 32) + + +static ngx_int_t ngx_http_v3_evict(ngx_connection_t *c, size_t need); +static void ngx_http_v3_unblock(void *data); +static ngx_int_t ngx_http_v3_new_entry(ngx_connection_t *c); + + +typedef struct { + ngx_queue_t queue; + ngx_connection_t *connection; + ngx_uint_t *nblocked; +} ngx_http_v3_block_t; + + +static ngx_http_v3_field_t ngx_http_v3_static_table[] = { + + { ngx_string(":authority"), ngx_string("") }, + { ngx_string(":path"), ngx_string("/") }, + { ngx_string("age"), ngx_string("0") }, + { ngx_string("content-disposition"), ngx_string("") }, + { ngx_string("content-length"), ngx_string("0") }, + { ngx_string("cookie"), ngx_string("") }, + { ngx_string("date"), ngx_string("") }, + { ngx_string("etag"), ngx_string("") }, + { ngx_string("if-modified-since"), ngx_string("") }, + { ngx_string("if-none-match"), ngx_string("") }, + { ngx_string("last-modified"), ngx_string("") }, + { ngx_string("link"), ngx_string("") }, + { ngx_string("location"), ngx_string("") }, + { ngx_string("referer"), ngx_string("") }, + { ngx_string("set-cookie"), ngx_string("") }, + { ngx_string(":method"), ngx_string("CONNECT") }, + { ngx_string(":method"), ngx_string("DELETE") }, + { ngx_string(":method"), ngx_string("GET") }, + { ngx_string(":method"), ngx_string("HEAD") }, + { ngx_string(":method"), ngx_string("OPTIONS") }, + { ngx_string(":method"), ngx_string("POST") }, + { ngx_string(":method"), ngx_string("PUT") }, + { ngx_string(":scheme"), ngx_string("http") }, + { ngx_string(":scheme"), ngx_string("https") }, + { ngx_string(":status"), ngx_string("103") }, + { ngx_string(":status"), ngx_string("200") }, + { ngx_string(":status"), ngx_string("304") }, + { ngx_string(":status"), ngx_string("404") }, + { ngx_string(":status"), ngx_string("503") }, + { ngx_string("accept"), ngx_string("*/*") }, + { ngx_string("accept"), + ngx_string("application/dns-message") }, + { ngx_string("accept-encoding"), ngx_string("gzip, deflate, br") }, + { ngx_string("accept-ranges"), ngx_string("bytes") }, + { ngx_string("access-control-allow-headers"), + ngx_string("cache-control") }, + { ngx_string("access-control-allow-headers"), + ngx_string("content-type") }, + { ngx_string("access-control-allow-origin"), + ngx_string("*") }, + { ngx_string("cache-control"), ngx_string("max-age=0") }, + { ngx_string("cache-control"), ngx_string("max-age=2592000") }, + { ngx_string("cache-control"), ngx_string("max-age=604800") }, + { ngx_string("cache-control"), ngx_string("no-cache") }, + { ngx_string("cache-control"), ngx_string("no-store") }, + { ngx_string("cache-control"), + ngx_string("public, max-age=31536000") }, + { ngx_string("content-encoding"), ngx_string("br") }, + { ngx_string("content-encoding"), ngx_string("gzip") }, + { ngx_string("content-type"), + ngx_string("application/dns-message") }, + { ngx_string("content-type"), + ngx_string("application/javascript") }, + { ngx_string("content-type"), ngx_string("application/json") }, + { ngx_string("content-type"), + ngx_string("application/x-www-form-urlencoded") }, + { ngx_string("content-type"), ngx_string("image/gif") }, + { ngx_string("content-type"), ngx_string("image/jpeg") }, + { ngx_string("content-type"), ngx_string("image/png") }, + { ngx_string("content-type"), ngx_string("text/css") }, + { ngx_string("content-type"), + ngx_string("text/html;charset=utf-8") }, + { ngx_string("content-type"), ngx_string("text/plain") }, + { ngx_string("content-type"), + ngx_string("text/plain;charset=utf-8") }, + { ngx_string("range"), ngx_string("bytes=0-") }, + { ngx_string("strict-transport-security"), + ngx_string("max-age=31536000") }, + { ngx_string("strict-transport-security"), + ngx_string("max-age=31536000;includesubdomains") }, + { ngx_string("strict-transport-security"), + ngx_string("max-age=31536000;includesubdomains;preload") }, + { ngx_string("vary"), ngx_string("accept-encoding") }, + { ngx_string("vary"), ngx_string("origin") }, + { ngx_string("x-content-type-options"), + ngx_string("nosniff") }, + { ngx_string("x-xss-protection"), ngx_string("1;mode=block") }, + { ngx_string(":status"), ngx_string("100") }, + { ngx_string(":status"), ngx_string("204") }, + { ngx_string(":status"), ngx_string("206") }, + { ngx_string(":status"), ngx_string("302") }, + { ngx_string(":status"), ngx_string("400") }, + { ngx_string(":status"), ngx_string("403") }, + { ngx_string(":status"), ngx_string("421") }, + { ngx_string(":status"), ngx_string("425") }, + { ngx_string(":status"), ngx_string("500") }, + { ngx_string("accept-language"), ngx_string("") }, + { ngx_string("access-control-allow-credentials"), + ngx_string("FALSE") }, + { ngx_string("access-control-allow-credentials"), + ngx_string("TRUE") }, + { ngx_string("access-control-allow-headers"), + ngx_string("*") }, + { ngx_string("access-control-allow-methods"), + ngx_string("get") }, + { ngx_string("access-control-allow-methods"), + ngx_string("get, post, options") }, + { ngx_string("access-control-allow-methods"), + ngx_string("options") }, + { ngx_string("access-control-expose-headers"), + ngx_string("content-length") }, + { ngx_string("access-control-request-headers"), + ngx_string("content-type") }, + { ngx_string("access-control-request-method"), + ngx_string("get") }, + { ngx_string("access-control-request-method"), + ngx_string("post") }, + { ngx_string("alt-svc"), ngx_string("clear") }, + { ngx_string("authorization"), ngx_string("") }, + { ngx_string("content-security-policy"), + ngx_string("script-src 'none';object-src 'none';base-uri 'none'") }, + { ngx_string("early-data"), ngx_string("1") }, + { ngx_string("expect-ct"), ngx_string("") }, + { ngx_string("forwarded"), ngx_string("") }, + { ngx_string("if-range"), ngx_string("") }, + { ngx_string("origin"), ngx_string("") }, + { ngx_string("purpose"), ngx_string("prefetch") }, + { ngx_string("server"), ngx_string("") }, + { ngx_string("timing-allow-origin"), ngx_string("*") }, + { ngx_string("upgrade-insecure-requests"), + ngx_string("1") }, + { ngx_string("user-agent"), ngx_string("") }, + { ngx_string("x-forwarded-for"), ngx_string("") }, + { ngx_string("x-frame-options"), ngx_string("deny") }, + { ngx_string("x-frame-options"), ngx_string("sameorigin") } +}; + + +ngx_int_t +ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, + ngx_uint_t index, ngx_str_t *value) +{ + ngx_str_t name; + ngx_http_v3_session_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + if (dynamic) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 ref insert dynamic[%ui] \"%V\"", index, value); + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + if (dt->base + dt->nelts <= index) { + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + + index = dt->base + dt->nelts - 1 - index; + + if (ngx_http_v3_lookup(c, index, &name, NULL) != NGX_OK) { + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + + } else { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 ref insert static[%ui] \"%V\"", index, value); + + if (ngx_http_v3_lookup_static(c, index, &name, NULL) != NGX_OK) { + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + } + + return ngx_http_v3_insert(c, &name, value); +} + + +ngx_int_t +ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) +{ + u_char *p; + size_t size; + ngx_http_v3_field_t *field; + ngx_http_v3_session_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + size = ngx_http_v3_table_entry_size(name, value); + + if (ngx_http_v3_evict(c, size) != NGX_OK) { + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 insert [%ui] \"%V\":\"%V\", size:%uz", + dt->base + dt->nelts, name, value, size); + + p = ngx_alloc(sizeof(ngx_http_v3_field_t) + name->len + value->len, + c->log); + if (p == NULL) { + return NGX_ERROR; + } + + field = (ngx_http_v3_field_t *) p; + + field->name.data = p + sizeof(ngx_http_v3_field_t); + field->name.len = name->len; + field->value.data = ngx_cpymem(field->name.data, name->data, name->len); + field->value.len = value->len; + ngx_memcpy(field->value.data, value->data, value->len); + + dt->elts[dt->nelts++] = field; + dt->size += size; + + /* TODO increment can be sent less often */ + + if (ngx_http_v3_send_inc_insert_count(c, 1) != NGX_OK) { + return NGX_ERROR; + } + + if (ngx_http_v3_new_entry(c) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) +{ + ngx_uint_t max, prev_max; + ngx_http_v3_field_t **elts; + ngx_http_v3_session_t *h3c; + ngx_http_v3_srv_conf_t *h3scf; + ngx_http_v3_dynamic_table_t *dt; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 set capacity %ui", capacity); + + h3c = ngx_http_v3_get_session(c); + h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); + + if (capacity > h3scf->max_table_capacity) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client exceeded http3_max_table_capacity limit"); + 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_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + } + + max = capacity / 32; + prev_max = dt->capacity / 32; + + if (max > prev_max) { + elts = ngx_alloc(max * sizeof(void *), c->log); + if (elts == NULL) { + return NGX_ERROR; + } + + if (dt->elts) { + ngx_memcpy(elts, dt->elts, dt->nelts * sizeof(void *)); + ngx_free(dt->elts); + } + + dt->elts = elts; + } + + dt->capacity = capacity; + + return NGX_OK; +} + + +void +ngx_http_v3_cleanup_table(ngx_http_v3_session_t *h3c) +{ + ngx_uint_t n; + ngx_http_v3_dynamic_table_t *dt; + + dt = &h3c->table; + + if (dt->elts == NULL) { + return; + } + + for (n = 0; n < dt->nelts; n++) { + ngx_free(dt->elts[n]); + } + + ngx_free(dt->elts); +} + + +static ngx_int_t +ngx_http_v3_evict(ngx_connection_t *c, size_t need) +{ + size_t size, target; + ngx_uint_t n; + ngx_http_v3_field_t *field; + ngx_http_v3_session_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + if (need > dt->capacity) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "not enough dynamic table capacity"); + return NGX_ERROR; + } + + target = dt->capacity - need; + n = 0; + + while (dt->size > target) { + field = dt->elts[n++]; + size = ngx_http_v3_table_entry_size(&field->name, &field->value); + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 evict [%ui] \"%V\":\"%V\" size:%uz", + dt->base, &field->name, &field->value, size); + + ngx_free(field); + dt->size -= size; + } + + if (n) { + dt->nelts -= n; + dt->base += n; + ngx_memmove(dt->elts, &dt->elts[n], dt->nelts * sizeof(void *)); + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index) +{ + ngx_str_t name, value; + ngx_http_v3_session_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 duplicate %ui", index); + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + if (dt->base + dt->nelts <= index) { + 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_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + + return ngx_http_v3_insert(c, &name, &value); +} + + +ngx_int_t +ngx_http_v3_ack_section(ngx_connection_t *c, ngx_uint_t stream_id) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 ack section %ui", stream_id); + + /* we do not use dynamic tables */ + + return NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR; +} + + +ngx_int_t +ngx_http_v3_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 increment insert count %ui", inc); + + /* we do not use dynamic tables */ + + return NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR; +} + + +ngx_int_t +ngx_http_v3_lookup_static(ngx_connection_t *c, ngx_uint_t index, + ngx_str_t *name, ngx_str_t *value) +{ + ngx_uint_t nelts; + ngx_http_v3_field_t *field; + + nelts = sizeof(ngx_http_v3_static_table) + / sizeof(ngx_http_v3_static_table[0]); + + if (index >= nelts) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 static[%ui] lookup out of bounds: %ui", + index, nelts); + return NGX_ERROR; + } + + field = &ngx_http_v3_static_table[index]; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 static[%ui] lookup \"%V\":\"%V\"", + index, &field->name, &field->value); + + if (name) { + *name = field->name; + } + + if (value) { + *value = field->value; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_lookup(ngx_connection_t *c, ngx_uint_t index, ngx_str_t *name, + ngx_str_t *value) +{ + ngx_http_v3_field_t *field; + ngx_http_v3_session_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + if (index < dt->base || index - dt->base >= dt->nelts) { + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 dynamic[%ui] lookup out of bounds: [%ui,%ui]", + index, dt->base, dt->base + dt->nelts); + return NGX_ERROR; + } + + field = dt->elts[index - dt->base]; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 dynamic[%ui] lookup \"%V\":\"%V\"", + index, &field->name, &field->value); + + if (name) { + *name = field->name; + } + + if (value) { + *value = field->value; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_decode_insert_count(ngx_connection_t *c, ngx_uint_t *insert_count) +{ + ngx_uint_t max_entries, full_range, max_value, + max_wrapped, req_insert_count; + ngx_http_v3_srv_conf_t *h3scf; + ngx_http_v3_session_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + /* QPACK 4.5.1.1. Required Insert Count */ + + if (*insert_count == 0) { + return NGX_OK; + } + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); + + max_entries = h3scf->max_table_capacity / 32; + full_range = 2 * max_entries; + + if (*insert_count > full_range) { + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; + } + + max_value = dt->base + dt->nelts + max_entries; + max_wrapped = (max_value / full_range) * full_range; + req_insert_count = max_wrapped + *insert_count - 1; + + if (req_insert_count > max_value) { + if (req_insert_count <= full_range) { + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; + } + + req_insert_count -= full_range; + } + + if (req_insert_count == 0) { + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 decode insert_count %ui -> %ui", + *insert_count, req_insert_count); + + *insert_count = req_insert_count; + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count) +{ + size_t n; + ngx_pool_cleanup_t *cln; + ngx_http_v3_block_t *block; + ngx_http_v3_session_t *h3c; + ngx_http_v3_srv_conf_t *h3scf; + ngx_http_v3_dynamic_table_t *dt; + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + n = dt->base + dt->nelts; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 check insert count req:%ui, have:%ui", + insert_count, n); + + if (n >= insert_count) { + return NGX_OK; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 block stream"); + + block = NULL; + + for (cln = c->pool->cleanup; cln; cln = cln->next) { + if (cln->handler == ngx_http_v3_unblock) { + block = cln->data; + break; + } + } + + if (block == NULL) { + cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_http_v3_block_t)); + if (cln == NULL) { + return NGX_ERROR; + } + + cln->handler = ngx_http_v3_unblock; + + block = cln->data; + block->queue.prev = NULL; + block->connection = c; + block->nblocked = &h3c->nblocked; + } + + if (block->queue.prev == NULL) { + h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); + + if (h3c->nblocked == h3scf->max_blocked_streams) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client exceeded http3_max_blocked_streams limit"); + + ngx_http_v3_finalize_connection(c, + NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED, + "too many blocked streams"); + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; + } + + h3c->nblocked++; + ngx_queue_insert_tail(&h3c->blocked, &block->queue); + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 blocked:%ui", h3c->nblocked); + + return NGX_BUSY; +} + + +static void +ngx_http_v3_unblock(void *data) +{ + ngx_http_v3_block_t *block = data; + + if (block->queue.prev) { + ngx_queue_remove(&block->queue); + block->queue.prev = NULL; + (*block->nblocked)--; + } +} + + +static ngx_int_t +ngx_http_v3_new_entry(ngx_connection_t *c) +{ + ngx_queue_t *q; + ngx_connection_t *bc; + ngx_http_v3_block_t *block; + ngx_http_v3_session_t *h3c; + + h3c = ngx_http_v3_get_session(c); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 new dynamic entry, blocked:%ui", h3c->nblocked); + + while (!ngx_queue_empty(&h3c->blocked)) { + q = ngx_queue_head(&h3c->blocked); + block = (ngx_http_v3_block_t *) q; + bc = block->connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, bc->log, 0, "http3 unblock stream"); + + ngx_http_v3_unblock(block); + ngx_post_event(bc->read, &ngx_posted_events); + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_set_param(ngx_connection_t *c, uint64_t id, uint64_t value) +{ + switch (id) { + + case NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY: + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 param QPACK_MAX_TABLE_CAPACITY:%uL", value); + break; + + case NGX_HTTP_V3_PARAM_MAX_HEADER_LIST_SIZE: + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 param SETTINGS_MAX_HEADER_LIST_SIZE:%uL", value); + break; + + case NGX_HTTP_V3_PARAM_BLOCKED_STREAMS: + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 param QPACK_BLOCKED_STREAMS:%uL", value); + break; + + default: + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 param #%uL:%uL", id, value); + } + + return NGX_OK; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/http/v3/ngx_http_v3_table.h Tue Dec 07 13:01:28 2021 +0300 @@ -0,0 +1,53 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_HTTP_V3_TABLE_H_INCLUDED_ +#define _NGX_HTTP_V3_TABLE_H_INCLUDED_ + + +#include <ngx_config.h> +#include <ngx_core.h> +#include <ngx_http.h> + + +typedef struct { + ngx_str_t name; + ngx_str_t value; +} ngx_http_v3_field_t; + + +typedef struct { + ngx_http_v3_field_t **elts; + ngx_uint_t nelts; + ngx_uint_t base; + size_t size; + size_t capacity; +} ngx_http_v3_dynamic_table_t; + + +void ngx_http_v3_cleanup_table(ngx_http_v3_session_t *h3c); +ngx_int_t ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, + ngx_uint_t index, ngx_str_t *value); +ngx_int_t ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, + ngx_str_t *value); +ngx_int_t ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity); +ngx_int_t ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index); +ngx_int_t ngx_http_v3_ack_section(ngx_connection_t *c, ngx_uint_t stream_id); +ngx_int_t ngx_http_v3_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc); +ngx_int_t ngx_http_v3_lookup_static(ngx_connection_t *c, ngx_uint_t index, + ngx_str_t *name, ngx_str_t *value); +ngx_int_t ngx_http_v3_lookup(ngx_connection_t *c, ngx_uint_t index, + ngx_str_t *name, ngx_str_t *value); +ngx_int_t ngx_http_v3_decode_insert_count(ngx_connection_t *c, + ngx_uint_t *insert_count); +ngx_int_t ngx_http_v3_check_insert_count(ngx_connection_t *c, + ngx_uint_t insert_count); +ngx_int_t ngx_http_v3_set_param(ngx_connection_t *c, uint64_t id, + uint64_t value); + + +#endif /* _NGX_HTTP_V3_TABLE_H_INCLUDED_ */
--- a/src/http/v3/ngx_http_v3_tables.c Mon Dec 06 15:19:54 2021 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,678 +0,0 @@ - -/* - * Copyright (C) Roman Arutyunyan - * Copyright (C) Nginx, Inc. - */ - - -#include <ngx_config.h> -#include <ngx_core.h> -#include <ngx_http.h> - - -#define ngx_http_v3_table_entry_size(n, v) ((n)->len + (v)->len + 32) - - -static ngx_int_t ngx_http_v3_evict(ngx_connection_t *c, size_t need); -static void ngx_http_v3_unblock(void *data); -static ngx_int_t ngx_http_v3_new_entry(ngx_connection_t *c); - - -typedef struct { - ngx_queue_t queue; - ngx_connection_t *connection; - ngx_uint_t *nblocked; -} ngx_http_v3_block_t; - - -static ngx_http_v3_field_t ngx_http_v3_static_table[] = { - - { ngx_string(":authority"), ngx_string("") }, - { ngx_string(":path"), ngx_string("/") }, - { ngx_string("age"), ngx_string("0") }, - { ngx_string("content-disposition"), ngx_string("") }, - { ngx_string("content-length"), ngx_string("0") }, - { ngx_string("cookie"), ngx_string("") }, - { ngx_string("date"), ngx_string("") }, - { ngx_string("etag"), ngx_string("") }, - { ngx_string("if-modified-since"), ngx_string("") }, - { ngx_string("if-none-match"), ngx_string("") }, - { ngx_string("last-modified"), ngx_string("") }, - { ngx_string("link"), ngx_string("") }, - { ngx_string("location"), ngx_string("") }, - { ngx_string("referer"), ngx_string("") }, - { ngx_string("set-cookie"), ngx_string("") }, - { ngx_string(":method"), ngx_string("CONNECT") }, - { ngx_string(":method"), ngx_string("DELETE") }, - { ngx_string(":method"), ngx_string("GET") }, - { ngx_string(":method"), ngx_string("HEAD") }, - { ngx_string(":method"), ngx_string("OPTIONS") }, - { ngx_string(":method"), ngx_string("POST") }, - { ngx_string(":method"), ngx_string("PUT") }, - { ngx_string(":scheme"), ngx_string("http") }, - { ngx_string(":scheme"), ngx_string("https") }, - { ngx_string(":status"), ngx_string("103") }, - { ngx_string(":status"), ngx_string("200") }, - { ngx_string(":status"), ngx_string("304") }, - { ngx_string(":status"), ngx_string("404") }, - { ngx_string(":status"), ngx_string("503") }, - { ngx_string("accept"), ngx_string("*/*") }, - { ngx_string("accept"), - ngx_string("application/dns-message") }, - { ngx_string("accept-encoding"), ngx_string("gzip, deflate, br") }, - { ngx_string("accept-ranges"), ngx_string("bytes") }, - { ngx_string("access-control-allow-headers"), - ngx_string("cache-control") }, - { ngx_string("access-control-allow-headers"), - ngx_string("content-type") }, - { ngx_string("access-control-allow-origin"), - ngx_string("*") }, - { ngx_string("cache-control"), ngx_string("max-age=0") }, - { ngx_string("cache-control"), ngx_string("max-age=2592000") }, - { ngx_string("cache-control"), ngx_string("max-age=604800") }, - { ngx_string("cache-control"), ngx_string("no-cache") }, - { ngx_string("cache-control"), ngx_string("no-store") }, - { ngx_string("cache-control"), - ngx_string("public, max-age=31536000") }, - { ngx_string("content-encoding"), ngx_string("br") }, - { ngx_string("content-encoding"), ngx_string("gzip") }, - { ngx_string("content-type"), - ngx_string("application/dns-message") }, - { ngx_string("content-type"), - ngx_string("application/javascript") }, - { ngx_string("content-type"), ngx_string("application/json") }, - { ngx_string("content-type"), - ngx_string("application/x-www-form-urlencoded") }, - { ngx_string("content-type"), ngx_string("image/gif") }, - { ngx_string("content-type"), ngx_string("image/jpeg") }, - { ngx_string("content-type"), ngx_string("image/png") }, - { ngx_string("content-type"), ngx_string("text/css") }, - { ngx_string("content-type"), - ngx_string("text/html;charset=utf-8") }, - { ngx_string("content-type"), ngx_string("text/plain") }, - { ngx_string("content-type"), - ngx_string("text/plain;charset=utf-8") }, - { ngx_string("range"), ngx_string("bytes=0-") }, - { ngx_string("strict-transport-security"), - ngx_string("max-age=31536000") }, - { ngx_string("strict-transport-security"), - ngx_string("max-age=31536000;includesubdomains") }, - { ngx_string("strict-transport-security"), - ngx_string("max-age=31536000;includesubdomains;preload") }, - { ngx_string("vary"), ngx_string("accept-encoding") }, - { ngx_string("vary"), ngx_string("origin") }, - { ngx_string("x-content-type-options"), - ngx_string("nosniff") }, - { ngx_string("x-xss-protection"), ngx_string("1;mode=block") }, - { ngx_string(":status"), ngx_string("100") }, - { ngx_string(":status"), ngx_string("204") }, - { ngx_string(":status"), ngx_string("206") }, - { ngx_string(":status"), ngx_string("302") }, - { ngx_string(":status"), ngx_string("400") }, - { ngx_string(":status"), ngx_string("403") }, - { ngx_string(":status"), ngx_string("421") }, - { ngx_string(":status"), ngx_string("425") }, - { ngx_string(":status"), ngx_string("500") }, - { ngx_string("accept-language"), ngx_string("") }, - { ngx_string("access-control-allow-credentials"), - ngx_string("FALSE") }, - { ngx_string("access-control-allow-credentials"), - ngx_string("TRUE") }, - { ngx_string("access-control-allow-headers"), - ngx_string("*") }, - { ngx_string("access-control-allow-methods"), - ngx_string("get") }, - { ngx_string("access-control-allow-methods"), - ngx_string("get, post, options") }, - { ngx_string("access-control-allow-methods"), - ngx_string("options") }, - { ngx_string("access-control-expose-headers"), - ngx_string("content-length") }, - { ngx_string("access-control-request-headers"), - ngx_string("content-type") }, - { ngx_string("access-control-request-method"), - ngx_string("get") }, - { ngx_string("access-control-request-method"), - ngx_string("post") }, - { ngx_string("alt-svc"), ngx_string("clear") }, - { ngx_string("authorization"), ngx_string("") }, - { ngx_string("content-security-policy"), - ngx_string("script-src 'none';object-src 'none';base-uri 'none'") }, - { ngx_string("early-data"), ngx_string("1") }, - { ngx_string("expect-ct"), ngx_string("") }, - { ngx_string("forwarded"), ngx_string("") }, - { ngx_string("if-range"), ngx_string("") }, - { ngx_string("origin"), ngx_string("") }, - { ngx_string("purpose"), ngx_string("prefetch") }, - { ngx_string("server"), ngx_string("") }, - { ngx_string("timing-allow-origin"), ngx_string("*") }, - { ngx_string("upgrade-insecure-requests"), - ngx_string("1") }, - { ngx_string("user-agent"), ngx_string("") }, - { ngx_string("x-forwarded-for"), ngx_string("") }, - { ngx_string("x-frame-options"), ngx_string("deny") }, - { ngx_string("x-frame-options"), ngx_string("sameorigin") } -}; - - -ngx_int_t -ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, - ngx_uint_t index, ngx_str_t *value) -{ - ngx_str_t name; - ngx_http_v3_session_t *h3c; - ngx_http_v3_dynamic_table_t *dt; - - if (dynamic) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 ref insert dynamic[%ui] \"%V\"", index, value); - - h3c = ngx_http_v3_get_session(c); - dt = &h3c->table; - - if (dt->base + dt->nelts <= index) { - return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; - } - - index = dt->base + dt->nelts - 1 - index; - - if (ngx_http_v3_lookup(c, index, &name, NULL) != NGX_OK) { - return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; - } - - } else { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 ref insert static[%ui] \"%V\"", index, value); - - if (ngx_http_v3_lookup_static(c, index, &name, NULL) != NGX_OK) { - return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; - } - } - - return ngx_http_v3_insert(c, &name, value); -} - - -ngx_int_t -ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) -{ - u_char *p; - size_t size; - ngx_http_v3_field_t *field; - ngx_http_v3_session_t *h3c; - ngx_http_v3_dynamic_table_t *dt; - - size = ngx_http_v3_table_entry_size(name, value); - - if (ngx_http_v3_evict(c, size) != NGX_OK) { - return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; - } - - h3c = ngx_http_v3_get_session(c); - dt = &h3c->table; - - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 insert [%ui] \"%V\":\"%V\", size:%uz", - dt->base + dt->nelts, name, value, size); - - p = ngx_alloc(sizeof(ngx_http_v3_field_t) + name->len + value->len, - c->log); - if (p == NULL) { - return NGX_ERROR; - } - - field = (ngx_http_v3_field_t *) p; - - field->name.data = p + sizeof(ngx_http_v3_field_t); - field->name.len = name->len; - field->value.data = ngx_cpymem(field->name.data, name->data, name->len); - field->value.len = value->len; - ngx_memcpy(field->value.data, value->data, value->len); - - dt->elts[dt->nelts++] = field; - dt->size += size; - - /* TODO increment can be sent less often */ - - if (ngx_http_v3_send_inc_insert_count(c, 1) != NGX_OK) { - return NGX_ERROR; - } - - if (ngx_http_v3_new_entry(c) != NGX_OK) { - return NGX_ERROR; - } - - return NGX_OK; -} - - -ngx_int_t -ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) -{ - ngx_uint_t max, prev_max; - ngx_http_v3_field_t **elts; - ngx_http_v3_session_t *h3c; - ngx_http_v3_srv_conf_t *h3scf; - ngx_http_v3_dynamic_table_t *dt; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 set capacity %ui", capacity); - - h3c = ngx_http_v3_get_session(c); - h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); - - if (capacity > h3scf->max_table_capacity) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client exceeded http3_max_table_capacity limit"); - 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_HTTP_V3_ERR_ENCODER_STREAM_ERROR; - } - } - - max = capacity / 32; - prev_max = dt->capacity / 32; - - if (max > prev_max) { - elts = ngx_alloc(max * sizeof(void *), c->log); - if (elts == NULL) { - return NGX_ERROR; - } - - if (dt->elts) { - ngx_memcpy(elts, dt->elts, dt->nelts * sizeof(void *)); - ngx_free(dt->elts); - } - - dt->elts = elts; - } - - dt->capacity = capacity; - - return NGX_OK; -} - - -void -ngx_http_v3_cleanup_table(ngx_http_v3_session_t *h3c) -{ - ngx_uint_t n; - ngx_http_v3_dynamic_table_t *dt; - - dt = &h3c->table; - - if (dt->elts == NULL) { - return; - } - - for (n = 0; n < dt->nelts; n++) { - ngx_free(dt->elts[n]); - } - - ngx_free(dt->elts); -} - - -static ngx_int_t -ngx_http_v3_evict(ngx_connection_t *c, size_t need) -{ - size_t size, target; - ngx_uint_t n; - ngx_http_v3_field_t *field; - ngx_http_v3_session_t *h3c; - ngx_http_v3_dynamic_table_t *dt; - - h3c = ngx_http_v3_get_session(c); - dt = &h3c->table; - - if (need > dt->capacity) { - ngx_log_error(NGX_LOG_ERR, c->log, 0, - "not enough dynamic table capacity"); - return NGX_ERROR; - } - - target = dt->capacity - need; - n = 0; - - while (dt->size > target) { - field = dt->elts[n++]; - size = ngx_http_v3_table_entry_size(&field->name, &field->value); - - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 evict [%ui] \"%V\":\"%V\" size:%uz", - dt->base, &field->name, &field->value, size); - - ngx_free(field); - dt->size -= size; - } - - if (n) { - dt->nelts -= n; - dt->base += n; - ngx_memmove(dt->elts, &dt->elts[n], dt->nelts * sizeof(void *)); - } - - return NGX_OK; -} - - -ngx_int_t -ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index) -{ - ngx_str_t name, value; - ngx_http_v3_session_t *h3c; - ngx_http_v3_dynamic_table_t *dt; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 duplicate %ui", index); - - h3c = ngx_http_v3_get_session(c); - dt = &h3c->table; - - if (dt->base + dt->nelts <= index) { - 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_HTTP_V3_ERR_ENCODER_STREAM_ERROR; - } - - return ngx_http_v3_insert(c, &name, &value); -} - - -ngx_int_t -ngx_http_v3_ack_section(ngx_connection_t *c, ngx_uint_t stream_id) -{ - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 ack section %ui", stream_id); - - /* we do not use dynamic tables */ - - return NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR; -} - - -ngx_int_t -ngx_http_v3_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) -{ - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 increment insert count %ui", inc); - - /* we do not use dynamic tables */ - - return NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR; -} - - -ngx_int_t -ngx_http_v3_lookup_static(ngx_connection_t *c, ngx_uint_t index, - ngx_str_t *name, ngx_str_t *value) -{ - ngx_uint_t nelts; - ngx_http_v3_field_t *field; - - nelts = sizeof(ngx_http_v3_static_table) - / sizeof(ngx_http_v3_static_table[0]); - - if (index >= nelts) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 static[%ui] lookup out of bounds: %ui", - index, nelts); - return NGX_ERROR; - } - - field = &ngx_http_v3_static_table[index]; - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 static[%ui] lookup \"%V\":\"%V\"", - index, &field->name, &field->value); - - if (name) { - *name = field->name; - } - - if (value) { - *value = field->value; - } - - return NGX_OK; -} - - -ngx_int_t -ngx_http_v3_lookup(ngx_connection_t *c, ngx_uint_t index, ngx_str_t *name, - ngx_str_t *value) -{ - ngx_http_v3_field_t *field; - ngx_http_v3_session_t *h3c; - ngx_http_v3_dynamic_table_t *dt; - - h3c = ngx_http_v3_get_session(c); - dt = &h3c->table; - - if (index < dt->base || index - dt->base >= dt->nelts) { - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 dynamic[%ui] lookup out of bounds: [%ui,%ui]", - index, dt->base, dt->base + dt->nelts); - return NGX_ERROR; - } - - field = dt->elts[index - dt->base]; - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 dynamic[%ui] lookup \"%V\":\"%V\"", - index, &field->name, &field->value); - - if (name) { - *name = field->name; - } - - if (value) { - *value = field->value; - } - - return NGX_OK; -} - - -ngx_int_t -ngx_http_v3_decode_insert_count(ngx_connection_t *c, ngx_uint_t *insert_count) -{ - ngx_uint_t max_entries, full_range, max_value, - max_wrapped, req_insert_count; - ngx_http_v3_srv_conf_t *h3scf; - ngx_http_v3_session_t *h3c; - ngx_http_v3_dynamic_table_t *dt; - - /* QPACK 4.5.1.1. Required Insert Count */ - - if (*insert_count == 0) { - return NGX_OK; - } - - h3c = ngx_http_v3_get_session(c); - dt = &h3c->table; - - h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); - - max_entries = h3scf->max_table_capacity / 32; - full_range = 2 * max_entries; - - if (*insert_count > full_range) { - return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; - } - - max_value = dt->base + dt->nelts + max_entries; - max_wrapped = (max_value / full_range) * full_range; - req_insert_count = max_wrapped + *insert_count - 1; - - if (req_insert_count > max_value) { - if (req_insert_count <= full_range) { - return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; - } - - req_insert_count -= full_range; - } - - if (req_insert_count == 0) { - return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; - } - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 decode insert_count %ui -> %ui", - *insert_count, req_insert_count); - - *insert_count = req_insert_count; - - return NGX_OK; -} - - -ngx_int_t -ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count) -{ - size_t n; - ngx_pool_cleanup_t *cln; - ngx_http_v3_block_t *block; - ngx_http_v3_session_t *h3c; - ngx_http_v3_srv_conf_t *h3scf; - ngx_http_v3_dynamic_table_t *dt; - - h3c = ngx_http_v3_get_session(c); - dt = &h3c->table; - - n = dt->base + dt->nelts; - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 check insert count req:%ui, have:%ui", - insert_count, n); - - if (n >= insert_count) { - return NGX_OK; - } - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 block stream"); - - block = NULL; - - for (cln = c->pool->cleanup; cln; cln = cln->next) { - if (cln->handler == ngx_http_v3_unblock) { - block = cln->data; - break; - } - } - - if (block == NULL) { - cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_http_v3_block_t)); - if (cln == NULL) { - return NGX_ERROR; - } - - cln->handler = ngx_http_v3_unblock; - - block = cln->data; - block->queue.prev = NULL; - block->connection = c; - block->nblocked = &h3c->nblocked; - } - - if (block->queue.prev == NULL) { - h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); - - if (h3c->nblocked == h3scf->max_blocked_streams) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client exceeded http3_max_blocked_streams limit"); - - ngx_http_v3_finalize_connection(c, - NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED, - "too many blocked streams"); - return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; - } - - h3c->nblocked++; - ngx_queue_insert_tail(&h3c->blocked, &block->queue); - } - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 blocked:%ui", h3c->nblocked); - - return NGX_BUSY; -} - - -static void -ngx_http_v3_unblock(void *data) -{ - ngx_http_v3_block_t *block = data; - - if (block->queue.prev) { - ngx_queue_remove(&block->queue); - block->queue.prev = NULL; - (*block->nblocked)--; - } -} - - -static ngx_int_t -ngx_http_v3_new_entry(ngx_connection_t *c) -{ - ngx_queue_t *q; - ngx_connection_t *bc; - ngx_http_v3_block_t *block; - ngx_http_v3_session_t *h3c; - - h3c = ngx_http_v3_get_session(c); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 new dynamic entry, blocked:%ui", h3c->nblocked); - - while (!ngx_queue_empty(&h3c->blocked)) { - q = ngx_queue_head(&h3c->blocked); - block = (ngx_http_v3_block_t *) q; - bc = block->connection; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, bc->log, 0, "http3 unblock stream"); - - ngx_http_v3_unblock(block); - ngx_post_event(bc->read, &ngx_posted_events); - } - - return NGX_OK; -} - - -ngx_int_t -ngx_http_v3_set_param(ngx_connection_t *c, uint64_t id, uint64_t value) -{ - switch (id) { - - case NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY: - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 param QPACK_MAX_TABLE_CAPACITY:%uL", value); - break; - - case NGX_HTTP_V3_PARAM_MAX_HEADER_LIST_SIZE: - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 param SETTINGS_MAX_HEADER_LIST_SIZE:%uL", value); - break; - - case NGX_HTTP_V3_PARAM_BLOCKED_STREAMS: - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 param QPACK_BLOCKED_STREAMS:%uL", value); - break; - - default: - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http3 param #%uL:%uL", id, value); - } - - return NGX_OK; -}
--- a/src/http/v3/ngx_http_v3_tables.h Mon Dec 06 15:19:54 2021 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ - -/* - * Copyright (C) Roman Arutyunyan - * Copyright (C) Nginx, Inc. - */ - - -#ifndef _NGX_HTTP_V3_TABLES_H_INCLUDED_ -#define _NGX_HTTP_V3_TABLES_H_INCLUDED_ - - -#include <ngx_config.h> -#include <ngx_core.h> -#include <ngx_http.h> - - -typedef struct { - ngx_str_t name; - ngx_str_t value; -} ngx_http_v3_field_t; - - -typedef struct { - ngx_http_v3_field_t **elts; - ngx_uint_t nelts; - ngx_uint_t base; - size_t size; - size_t capacity; -} ngx_http_v3_dynamic_table_t; - - -void ngx_http_v3_cleanup_table(ngx_http_v3_session_t *h3c); -ngx_int_t ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, - ngx_uint_t index, ngx_str_t *value); -ngx_int_t ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, - ngx_str_t *value); -ngx_int_t ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity); -ngx_int_t ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index); -ngx_int_t ngx_http_v3_ack_section(ngx_connection_t *c, ngx_uint_t stream_id); -ngx_int_t ngx_http_v3_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc); -ngx_int_t ngx_http_v3_lookup_static(ngx_connection_t *c, ngx_uint_t index, - ngx_str_t *name, ngx_str_t *value); -ngx_int_t ngx_http_v3_lookup(ngx_connection_t *c, ngx_uint_t index, - ngx_str_t *name, ngx_str_t *value); -ngx_int_t ngx_http_v3_decode_insert_count(ngx_connection_t *c, - ngx_uint_t *insert_count); -ngx_int_t ngx_http_v3_check_insert_count(ngx_connection_t *c, - ngx_uint_t insert_count); -ngx_int_t ngx_http_v3_set_param(ngx_connection_t *c, uint64_t id, - uint64_t value); - - -#endif /* _NGX_HTTP_V3_TABLES_H_INCLUDED_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/http/v3/ngx_http_v3_uni.c Tue Dec 07 13:01:28 2021 +0300 @@ -0,0 +1,733 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include <ngx_config.h> +#include <ngx_core.h> +#include <ngx_http.h> + + +typedef struct { + ngx_http_v3_parse_uni_t parse; + ngx_int_t index; +} ngx_http_v3_uni_stream_t; + + +typedef struct { + ngx_queue_t queue; + uint64_t id; + ngx_connection_t *connection; + ngx_uint_t *npushing; +} ngx_http_v3_push_t; + + +static void ngx_http_v3_close_uni_stream(ngx_connection_t *c); +static void ngx_http_v3_uni_read_handler(ngx_event_t *rev); +static void ngx_http_v3_dummy_write_handler(ngx_event_t *wev); +static void ngx_http_v3_push_cleanup(void *data); +static ngx_connection_t *ngx_http_v3_get_uni_stream(ngx_connection_t *c, + ngx_uint_t type); + + +void +ngx_http_v3_init_uni_stream(ngx_connection_t *c) +{ + uint64_t n; + ngx_http_v3_uni_stream_t *us; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init uni stream"); + + n = c->quic->id >> 2; + + if (n >= NGX_HTTP_V3_MAX_UNI_STREAMS) { + ngx_http_v3_finalize_connection(c, + NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR, + "reached maximum number of uni streams"); + c->data = NULL; + ngx_http_v3_close_uni_stream(c); + return; + } + + c->quic->cancelable = 1; + + 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, + "memory allocation error"); + c->data = NULL; + ngx_http_v3_close_uni_stream(c); + return; + } + + us->index = -1; + + c->data = us; + + c->read->handler = ngx_http_v3_uni_read_handler; + c->write->handler = ngx_http_v3_dummy_write_handler; + + ngx_http_v3_uni_read_handler(c->read); +} + + +static void +ngx_http_v3_close_uni_stream(ngx_connection_t *c) +{ + ngx_pool_t *pool; + ngx_http_v3_session_t *h3c; + ngx_http_v3_uni_stream_t *us; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 close stream"); + + us = c->data; + + if (us && us->index >= 0) { + h3c = ngx_http_v3_get_session(c); + h3c->known_streams[us->index] = NULL; + } + + c->destroyed = 1; + + pool = c->pool; + + ngx_close_connection(c); + + ngx_destroy_pool(pool); +} + + +ngx_int_t +ngx_http_v3_register_uni_stream(ngx_connection_t *c, uint64_t type) +{ + ngx_int_t index; + ngx_http_v3_session_t *h3c; + ngx_http_v3_uni_stream_t *us; + + h3c = ngx_http_v3_get_session(c); + + switch (type) { + + case NGX_HTTP_V3_STREAM_ENCODER: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 encoder stream"); + index = NGX_HTTP_V3_STREAM_CLIENT_ENCODER; + break; + + case NGX_HTTP_V3_STREAM_DECODER: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 decoder stream"); + index = NGX_HTTP_V3_STREAM_CLIENT_DECODER; + break; + + case NGX_HTTP_V3_STREAM_CONTROL: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 control stream"); + index = NGX_HTTP_V3_STREAM_CLIENT_CONTROL; + + break; + + default: + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 stream 0x%02xL", type); + + if (h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_ENCODER] == NULL + || h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_DECODER] == NULL + || h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_CONTROL] == NULL) + { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "missing mandatory stream"); + return NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR; + } + + index = -1; + } + + if (index >= 0) { + if (h3c->known_streams[index]) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "stream exists"); + return NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR; + } + + h3c->known_streams[index] = c; + + us = c->data; + us->index = index; + } + + return NGX_OK; +} + + +static void +ngx_http_v3_uni_read_handler(ngx_event_t *rev) +{ + u_char buf[128]; + ssize_t n; + ngx_buf_t b; + ngx_int_t rc; + ngx_connection_t *c; + ngx_http_v3_session_t *h3c; + ngx_http_v3_uni_stream_t *us; + + c = rev->data; + us = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read handler"); + + ngx_memzero(&b, sizeof(ngx_buf_t)); + + while (rev->ready) { + + n = c->recv(c, buf, sizeof(buf)); + + 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; + } + + b.pos = buf; + b.last = buf + n; + + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + + if (ngx_http_v3_check_flood(c) != NGX_OK) { + ngx_http_v3_close_uni_stream(c); + return; + } + + rc = ngx_http_v3_parse_uni(c, &us->parse, &b); + + 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_AGAIN) { + rc = NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR; + goto failed; + } + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + rc = NGX_HTTP_V3_ERR_INTERNAL_ERROR; + goto failed; + } + + return; + +failed: + + ngx_http_v3_finalize_connection(c, rc, "stream error"); + ngx_http_v3_close_uni_stream(c); +} + + +static void +ngx_http_v3_dummy_write_handler(ngx_event_t *wev) +{ + ngx_connection_t *c; + + c = wev->data; + + 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); + } +} + + +/* XXX async & buffered stream writes */ + +ngx_connection_t * +ngx_http_v3_create_push_stream(ngx_connection_t *c, uint64_t push_id) +{ + u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 2]; + size_t n; + ngx_connection_t *sc; + ngx_pool_cleanup_t *cln; + ngx_http_v3_push_t *push; + ngx_http_v3_session_t *h3c; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 create push stream id:%uL", push_id); + + sc = ngx_quic_open_stream(c, 0); + if (sc == NULL) { + goto failed; + } + + p = buf; + p = (u_char *) ngx_http_v3_encode_varlen_int(p, NGX_HTTP_V3_STREAM_PUSH); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, push_id); + n = p - buf; + + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + + if (sc->send(sc, buf, n) != (ssize_t) n) { + goto failed; + } + + cln = ngx_pool_cleanup_add(sc->pool, sizeof(ngx_http_v3_push_t)); + if (cln == NULL) { + goto failed; + } + + h3c->npushing++; + + cln->handler = ngx_http_v3_push_cleanup; + + push = cln->data; + push->id = push_id; + push->connection = sc; + push->npushing = &h3c->npushing; + + ngx_queue_insert_tail(&h3c->pushing, &push->queue); + + return sc; + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to create push stream"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR, + "failed to create push stream"); + if (sc) { + ngx_http_v3_close_uni_stream(sc); + } + + return NULL; +} + + +static void +ngx_http_v3_push_cleanup(void *data) +{ + ngx_http_v3_push_t *push = data; + + ngx_queue_remove(&push->queue); + (*push->npushing)--; +} + + +static ngx_connection_t * +ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type) +{ + u_char buf[NGX_HTTP_V3_VARLEN_INT_LEN]; + size_t n; + ngx_int_t index; + ngx_connection_t *sc; + ngx_http_v3_session_t *h3c; + ngx_http_v3_uni_stream_t *us; + + switch (type) { + case NGX_HTTP_V3_STREAM_ENCODER: + index = NGX_HTTP_V3_STREAM_SERVER_ENCODER; + break; + case NGX_HTTP_V3_STREAM_DECODER: + index = NGX_HTTP_V3_STREAM_SERVER_DECODER; + break; + case NGX_HTTP_V3_STREAM_CONTROL: + index = NGX_HTTP_V3_STREAM_SERVER_CONTROL; + break; + default: + index = -1; + } + + h3c = ngx_http_v3_get_session(c); + + if (index >= 0) { + if (h3c->known_streams[index]) { + return h3c->known_streams[index]; + } + } + + sc = ngx_quic_open_stream(c, 0); + if (sc == NULL) { + goto failed; + } + + sc->quic->cancelable = 1; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 create uni stream, type:%ui", type); + + us = ngx_pcalloc(sc->pool, sizeof(ngx_http_v3_uni_stream_t)); + if (us == NULL) { + goto failed; + } + + us->index = index; + + sc->data = us; + + sc->read->handler = ngx_http_v3_uni_read_handler; + sc->write->handler = ngx_http_v3_dummy_write_handler; + + if (index >= 0) { + h3c->known_streams[index] = sc; + } + + n = (u_char *) ngx_http_v3_encode_varlen_int(buf, type) - buf; + + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + + if (sc->send(sc, buf, n) != (ssize_t) n) { + goto failed; + } + + return sc; + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to create server stream"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR, + "failed to create server stream"); + if (sc) { + ngx_http_v3_close_uni_stream(sc); + } + + return NULL; +} + + +ngx_int_t +ngx_http_v3_send_settings(ngx_connection_t *c) +{ + u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 6]; + size_t n; + ngx_connection_t *cc; + ngx_http_v3_session_t *h3c; + ngx_http_v3_srv_conf_t *h3scf; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 send settings"); + + cc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_CONTROL); + if (cc == NULL) { + return NGX_ERROR; + } + + h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); + + n = ngx_http_v3_encode_varlen_int(NULL, + NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY); + n += ngx_http_v3_encode_varlen_int(NULL, h3scf->max_table_capacity); + n += ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_PARAM_BLOCKED_STREAMS); + n += ngx_http_v3_encode_varlen_int(NULL, h3scf->max_blocked_streams); + + p = (u_char *) ngx_http_v3_encode_varlen_int(buf, + NGX_HTTP_V3_FRAME_SETTINGS); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, n); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, + NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, h3scf->max_table_capacity); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, + NGX_HTTP_V3_PARAM_BLOCKED_STREAMS); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, h3scf->max_blocked_streams); + n = p - buf; + + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + + if (cc->send(cc, buf, n) != (ssize_t) n) { + goto failed; + } + + return NGX_OK; + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to send settings"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, + "failed to send settings"); + ngx_http_v3_close_uni_stream(cc); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id) +{ + u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 3]; + size_t n; + ngx_connection_t *cc; + ngx_http_v3_session_t *h3c; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 send goaway %uL", id); + + cc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_CONTROL); + if (cc == NULL) { + return NGX_ERROR; + } + + n = ngx_http_v3_encode_varlen_int(NULL, id); + p = (u_char *) ngx_http_v3_encode_varlen_int(buf, NGX_HTTP_V3_FRAME_GOAWAY); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, n); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, id); + n = p - buf; + + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + + if (cc->send(cc, buf, n) != (ssize_t) n) { + goto failed; + } + + return NGX_OK; + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to send goaway"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, + "failed to send goaway"); + ngx_http_v3_close_uni_stream(cc); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_http_v3_send_ack_section(ngx_connection_t *c, ngx_uint_t stream_id) +{ + u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; + size_t n; + ngx_connection_t *dc; + ngx_http_v3_session_t *h3c; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 send section acknowledgement %ui", stream_id); + + dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); + if (dc == NULL) { + return NGX_ERROR; + } + + buf[0] = 0x80; + n = (u_char *) ngx_http_v3_encode_prefix_int(buf, stream_id, 7) - buf; + + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + + if (dc->send(dc, buf, n) != (ssize_t) n) { + goto failed; + } + + return NGX_OK; + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "failed to send section acknowledgement"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, + "failed to send section acknowledgement"); + ngx_http_v3_close_uni_stream(dc); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_http_v3_send_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) +{ + u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; + size_t n; + ngx_connection_t *dc; + ngx_http_v3_session_t *h3c; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 send stream cancellation %ui", stream_id); + + dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); + if (dc == NULL) { + return NGX_ERROR; + } + + buf[0] = 0x40; + n = (u_char *) ngx_http_v3_encode_prefix_int(buf, stream_id, 6) - buf; + + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + + if (dc->send(dc, buf, n) != (ssize_t) n) { + goto failed; + } + + return NGX_OK; + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to send stream cancellation"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, + "failed to send stream cancellation"); + ngx_http_v3_close_uni_stream(dc); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_http_v3_send_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) +{ + u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; + size_t n; + ngx_connection_t *dc; + ngx_http_v3_session_t *h3c; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 send insert count increment %ui", inc); + + dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); + if (dc == NULL) { + return NGX_ERROR; + } + + buf[0] = 0; + n = (u_char *) ngx_http_v3_encode_prefix_int(buf, inc, 6) - buf; + + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + + if (dc->send(dc, buf, n) != (ssize_t) n) { + goto failed; + } + + return NGX_OK; + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "failed to send insert count increment"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, + "failed to send insert count increment"); + ngx_http_v3_close_uni_stream(dc); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_http_v3_set_max_push_id(ngx_connection_t *c, uint64_t max_push_id) +{ + ngx_http_v3_session_t *h3c; + + h3c = ngx_http_v3_get_session(c); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 MAX_PUSH_ID:%uL", max_push_id); + + if (h3c->max_push_id != (uint64_t) -1 && max_push_id < h3c->max_push_id) { + return NGX_HTTP_V3_ERR_ID_ERROR; + } + + h3c->max_push_id = max_push_id; + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_goaway(ngx_connection_t *c, uint64_t push_id) +{ + ngx_http_v3_session_t *h3c; + + h3c = ngx_http_v3_get_session(c); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 GOAWAY:%uL", push_id); + + h3c->goaway_push_id = push_id; + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id) +{ + ngx_queue_t *q; + ngx_http_request_t *r; + ngx_http_v3_push_t *push; + ngx_http_v3_session_t *h3c; + + h3c = ngx_http_v3_get_session(c); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 CANCEL_PUSH:%uL", push_id); + + if (push_id >= h3c->next_push_id) { + return NGX_HTTP_V3_ERR_ID_ERROR; + } + + for (q = ngx_queue_head(&h3c->pushing); + q != ngx_queue_sentinel(&h3c->pushing); + q = ngx_queue_next(&h3c->pushing)) + { + push = (ngx_http_v3_push_t *) q; + + if (push->id != push_id) { + continue; + } + + r = push->connection->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 cancel push"); + + ngx_http_finalize_request(r, NGX_HTTP_CLOSE); + + break; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 cancel stream %ui", stream_id); + + /* we do not use dynamic tables */ + + return NGX_OK; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/http/v3/ngx_http_v3_uni.h Tue Dec 07 13:01:28 2021 +0300 @@ -0,0 +1,38 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_HTTP_V3_UNI_H_INCLUDED_ +#define _NGX_HTTP_V3_UNI_H_INCLUDED_ + + +#include <ngx_config.h> +#include <ngx_core.h> +#include <ngx_http.h> + + +void ngx_http_v3_init_uni_stream(ngx_connection_t *c); +ngx_int_t ngx_http_v3_register_uni_stream(ngx_connection_t *c, uint64_t type); + +ngx_connection_t *ngx_http_v3_create_push_stream(ngx_connection_t *c, + uint64_t push_id); +ngx_int_t ngx_http_v3_set_max_push_id(ngx_connection_t *c, + uint64_t max_push_id); +ngx_int_t ngx_http_v3_goaway(ngx_connection_t *c, uint64_t push_id); +ngx_int_t ngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id); +ngx_int_t ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id); + +ngx_int_t ngx_http_v3_send_settings(ngx_connection_t *c); +ngx_int_t ngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id); +ngx_int_t ngx_http_v3_send_ack_section(ngx_connection_t *c, + ngx_uint_t stream_id); +ngx_int_t ngx_http_v3_send_cancel_stream(ngx_connection_t *c, + ngx_uint_t stream_id); +ngx_int_t ngx_http_v3_send_inc_insert_count(ngx_connection_t *c, + ngx_uint_t inc); + + +#endif /* _NGX_HTTP_V3_UNI_H_INCLUDED_ */