]> git.kaiwu.me - nginx.git/commitdiff
HTTP/3: optimize encoder stream memory usage
authorRoman Arutyunyan <arut@nginx.com>
Fri, 10 Apr 2026 17:42:18 +0000 (21:42 +0400)
committerRoman Arutyunyan <arutyunyan.roman@gmail.com>
Thu, 16 Apr 2026 15:47:46 +0000 (19:47 +0400)
Previously, the encoder stream allocated each new inserted field in the
connection pool.  This memory was not freed until the end of the connection.
Now a special insert buffer is used for all inserts.

src/http/v3/ngx_http_v3_parse.c
src/http/v3/ngx_http_v3_parse.h
src/http/v3/ngx_http_v3_table.c
src/http/v3/ngx_http_v3_table.h

index bcbf0dbe1208d8ee92d001b8a22ab005ad32440e..1ba08c7910574364aac5ece222757899d7f7ef14 100644 (file)
@@ -633,9 +633,23 @@ ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st,
                 st->huffstate = 0;
             }
 
-            st->last = ngx_pnalloc(c->pool, n + 1);
-            if (st->last == NULL) {
-                return NGX_ERROR;
+            if (st->buf) {
+                if ((size_t) (st->buf->end - st->buf->last) < n + 1) {
+                    ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                                  "not enough dynamic table capacity");
+
+                    st->last = NULL;
+                    return NGX_ERROR;
+                }
+
+                st->last = st->buf->last;
+                st->buf->last += n + 1;
+
+            } else {
+                st->last = ngx_pnalloc(c->pool, n + 1);
+                if (st->last == NULL) {
+                    return NGX_ERROR;
+                }
             }
 
             st->value.data = st->last;
@@ -1486,6 +1500,11 @@ ngx_http_v3_parse_field_inr(ngx_connection_t *c,
 
             ch = *b->pos;
 
+            st->literal.buf = ngx_http_v3_get_insert_buffer(c);
+            if (st->literal.buf == NULL) {
+                return NGX_ERROR;
+            }
+
             st->dynamic = (ch & 0x40) ? 0 : 1;
             st->state = sw_name_index;
 
@@ -1590,6 +1609,11 @@ ngx_http_v3_parse_field_iln(ngx_connection_t *c,
 
             ch = *b->pos;
 
+            st->literal.buf = ngx_http_v3_get_insert_buffer(c);
+            if (st->literal.buf == NULL) {
+                return NGX_ERROR;
+            }
+
             st->literal.huffman = (ch & 0x20) ? 1 : 0;
             st->state = sw_name_len;
 
index ba004db5df4305d2ec8e2cdd45b31066497d4970..c7e1e1ebb9aa12a3081dc66de84564d07b111b75 100644 (file)
@@ -51,6 +51,7 @@ typedef struct {
     ngx_str_t                       value;
     u_char                         *last;
     u_char                          huffstate;
+    ngx_buf_t                      *buf;
 } ngx_http_v3_parse_literal_t;
 
 
index 428e7326b91918de6f275461cefade7da5855652..3b0b7b309f869e7f1216cdd8d53ac93cd3744ac6 100644 (file)
@@ -155,6 +155,28 @@ static ngx_http_v3_field_t  ngx_http_v3_static_table[] = {
 };
 
 
+ngx_buf_t *
+ngx_http_v3_get_insert_buffer(ngx_connection_t *c)
+{
+    ngx_http_v3_session_t        *h3c;
+    ngx_http_v3_dynamic_table_t  *dt;
+
+    h3c = ngx_http_v3_get_session(c);
+    dt = &h3c->table;
+
+    if (dt->insert_buffer == NULL) {
+        dt->insert_buffer = ngx_create_temp_buf(c->pool, dt->capacity);
+        if (dt->insert_buffer == NULL) {
+            return NULL;
+        }
+    }
+
+    dt->insert_buffer->last = dt->insert_buffer->pos;
+
+    return dt->insert_buffer;
+}
+
+
 ngx_int_t
 ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic,
     ngx_uint_t index, ngx_str_t *value)
index 1c2fb17b9c4f5c413822be1b35aee604ed9d476a..6644723d1e216ec4e1ee52a0858ca4b86ba25880 100644 (file)
@@ -29,11 +29,13 @@ typedef struct {
     uint64_t                      insert_count;
     uint64_t                      ack_insert_count;
     ngx_event_t                   send_insert_count;
+    ngx_buf_t                    *insert_buffer;
 } ngx_http_v3_dynamic_table_t;
 
 
 void ngx_http_v3_inc_insert_count_handler(ngx_event_t *ev);
 void ngx_http_v3_cleanup_table(ngx_http_v3_session_t *h3c);
+ngx_buf_t *ngx_http_v3_get_insert_buffer(ngx_connection_t *c);
 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,