mirror of
https://github.com/nginx/nginx.git
synced 2026-10-21 11:31:24 +00:00
HTTP/2: handle PRIORITY_UPDATE frames
Implement the RFC 9218 PRIORITY_UPDATE frame (type 0x10). The frame is sent on stream 0 and carries a prioritized stream id and a Priority Field Value; it is validated for framing errors and dispatched from ngx_http_v2_state_head(). A PRIORITY_UPDATE conveys a complete set of parameters (RFC 9218, Section 7): omitted parameters reset to their defaults, so the value replaces any earlier signal. A frame for a stream that already exists is applied at once. A frame for a not-yet-created stream is buffered on its node until the HEADERS frame arrives; the node is parked in h2c->closed and, while the RFC 7540 tree is still present, attached to the root as is done for a PRIORITY frame with a zero dependency. The number of buffered signals is bounded by the node reuse budget. A malformed value is logged and ignored, leaving the current priority untouched. A value that does not fit the state buffer while the frame is fragmented is skipped. The signals are only collected here; the scheduler still uses the RFC 7540 dependency tree until the switch to RFC 9218. Assisted-by: opencode:claude-opus-4.8
This commit is contained in:
parent
b5bd4b3487
commit
2b6c2de41d
2 changed files with 159 additions and 10 deletions
|
|
@ -85,6 +85,8 @@ static u_char *ngx_http_v2_handle_continuation(ngx_http_v2_connection_t *h2c,
|
|||
u_char *pos, u_char *end, ngx_http_v2_handler_pt handler);
|
||||
static u_char *ngx_http_v2_state_priority(ngx_http_v2_connection_t *h2c,
|
||||
u_char *pos, u_char *end);
|
||||
static u_char *ngx_http_v2_state_priority_update(ngx_http_v2_connection_t *h2c,
|
||||
u_char *pos, u_char *end);
|
||||
static u_char *ngx_http_v2_state_rst_stream(ngx_http_v2_connection_t *h2c,
|
||||
u_char *pos, u_char *end);
|
||||
static u_char *ngx_http_v2_state_settings(ngx_http_v2_connection_t *h2c,
|
||||
|
|
@ -907,6 +909,11 @@ ngx_http_v2_state_head(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)
|
|||
type, h2c->state.flags, h2c->state.length, h2c->state.sid);
|
||||
|
||||
if (type >= NGX_HTTP_V2_FRAME_STATES) {
|
||||
|
||||
if (type == NGX_HTTP_V2_PRIORITY_UPDATE_FRAME) {
|
||||
return ngx_http_v2_state_priority_update(h2c, pos, end);
|
||||
}
|
||||
|
||||
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
|
||||
"client sent frame with unknown type %ui", type);
|
||||
return ngx_http_v2_state_skip(h2c, pos, end);
|
||||
|
|
@ -1342,6 +1349,20 @@ ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c, u_char *pos,
|
|||
ngx_http_v2_set_dependency(h2c, node, depend, excl);
|
||||
}
|
||||
|
||||
/* a PRIORITY_UPDATE may have arrived before HEADERS */
|
||||
|
||||
if (node->priority_set) {
|
||||
stream->priority.client = node->priority;
|
||||
node->priority_set = 0;
|
||||
|
||||
ngx_http_priority_state_update(&stream->priority);
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
|
||||
"http2 buffered priority: u=%ud i=%ud",
|
||||
stream->priority.effective.urgency,
|
||||
stream->priority.effective.incremental);
|
||||
}
|
||||
|
||||
clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx,
|
||||
ngx_http_core_module);
|
||||
|
||||
|
|
@ -2066,6 +2087,127 @@ ngx_http_v2_state_priority(ngx_http_v2_connection_t *h2c, u_char *pos,
|
|||
}
|
||||
|
||||
|
||||
static u_char *
|
||||
ngx_http_v2_state_priority_update(ngx_http_v2_connection_t *h2c, u_char *pos,
|
||||
u_char *end)
|
||||
{
|
||||
size_t len;
|
||||
ngx_str_t value;
|
||||
ngx_uint_t sid;
|
||||
ngx_http_v2_node_t *node;
|
||||
ngx_http_priority_t priority;
|
||||
ngx_http_v2_stream_t *stream;
|
||||
|
||||
if (h2c->state.sid != 0) {
|
||||
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
|
||||
"client sent PRIORITY_UPDATE frame with incorrect "
|
||||
"identifier");
|
||||
|
||||
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
|
||||
}
|
||||
|
||||
if (h2c->state.length < 4) {
|
||||
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
|
||||
"client sent PRIORITY_UPDATE frame with incorrect "
|
||||
"length %uz", h2c->state.length);
|
||||
|
||||
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
|
||||
}
|
||||
|
||||
len = h2c->state.length;
|
||||
|
||||
if ((size_t) (end - pos) < len) {
|
||||
|
||||
if (len > NGX_HTTP_V2_STATE_BUFFER_SIZE) {
|
||||
return ngx_http_v2_state_skip(h2c, pos, end);
|
||||
}
|
||||
|
||||
return ngx_http_v2_state_save(h2c, pos, end,
|
||||
ngx_http_v2_state_priority_update);
|
||||
}
|
||||
|
||||
sid = ngx_http_v2_parse_sid(pos);
|
||||
|
||||
if (sid % 2 == 0) {
|
||||
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
|
||||
"client sent PRIORITY_UPDATE frame with incorrect "
|
||||
"prioritized stream %ui", sid);
|
||||
|
||||
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
|
||||
}
|
||||
|
||||
value.data = pos + 4;
|
||||
value.len = len - 4;
|
||||
|
||||
pos += len;
|
||||
|
||||
if (ngx_http_priority_parse(&value, &priority) != NGX_OK) {
|
||||
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
|
||||
"client sent PRIORITY_UPDATE frame with malformed value "
|
||||
"for stream %ui", sid);
|
||||
|
||||
return ngx_http_v2_state_complete(h2c, pos, end);
|
||||
}
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
|
||||
"http2 PRIORITY_UPDATE frame sid:%ui u=%ud i=%ud",
|
||||
sid, priority.urgency, priority.incremental);
|
||||
|
||||
/*
|
||||
* A PRIORITY_UPDATE conveys a complete set of parameters (RFC 9218,
|
||||
* Section 7): omitted parameters reset to their defaults, so the result
|
||||
* replaces any earlier signal rather than merging with it.
|
||||
*/
|
||||
|
||||
node = ngx_http_v2_get_node_by_id(h2c, sid, sid > h2c->last_sid);
|
||||
|
||||
if (sid > h2c->last_sid) {
|
||||
/*
|
||||
* The stream is not created yet; park the node in h2c->closed holding
|
||||
* the buffered priority. While the RFC 7540 tree is still present a
|
||||
* parked node has to be part of it, so attach it to the root as is
|
||||
* done for a PRIORITY frame with a zero dependency.
|
||||
*
|
||||
* RFC 9218, Section 7.1, bounds idle prioritized streams together with
|
||||
* active ones and requires a connection error on overflow; here idle
|
||||
* signals are bounded on their own by the node reuse budget and the
|
||||
* oldest is dropped instead, which is deliberately more lenient.
|
||||
*/
|
||||
|
||||
if (node == NULL) {
|
||||
return ngx_http_v2_connection_error(h2c,
|
||||
NGX_HTTP_V2_INTERNAL_ERROR);
|
||||
}
|
||||
|
||||
if (node->parent == NULL) {
|
||||
h2c->closed_nodes++;
|
||||
|
||||
} else {
|
||||
ngx_queue_remove(&node->reuse);
|
||||
}
|
||||
|
||||
ngx_queue_insert_tail(&h2c->closed, &node->reuse);
|
||||
|
||||
if (node->parent == NULL) {
|
||||
/* a node in the closed queue is expected to be in the tree */
|
||||
|
||||
node->weight = NGX_HTTP_V2_DEFAULT_WEIGHT;
|
||||
ngx_http_v2_set_dependency(h2c, node, 0, 0);
|
||||
}
|
||||
|
||||
node->priority = priority;
|
||||
node->priority_set = 1;
|
||||
|
||||
} else if (node && node->stream) {
|
||||
stream = node->stream;
|
||||
stream->priority.client = priority;
|
||||
ngx_http_priority_state_update(&stream->priority);
|
||||
}
|
||||
|
||||
return ngx_http_v2_state_complete(h2c, pos, end);
|
||||
}
|
||||
|
||||
|
||||
static u_char *
|
||||
ngx_http_v2_state_rst_stream(ngx_http_v2_connection_t *h2c, u_char *pos,
|
||||
u_char *end)
|
||||
|
|
@ -3106,6 +3248,8 @@ ngx_http_v2_create_stream(ngx_http_v2_connection_t *h2c)
|
|||
stream->request = r;
|
||||
stream->connection = h2c;
|
||||
|
||||
ngx_http_priority_state_init(&stream->priority);
|
||||
|
||||
h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module);
|
||||
|
||||
stream->send_window = h2c->init_window;
|
||||
|
|
|
|||
|
|
@ -27,16 +27,17 @@
|
|||
#define NGX_HTTP_V2_FRAME_HEADER_SIZE 9
|
||||
|
||||
/* frame types */
|
||||
#define NGX_HTTP_V2_DATA_FRAME 0x0
|
||||
#define NGX_HTTP_V2_HEADERS_FRAME 0x1
|
||||
#define NGX_HTTP_V2_PRIORITY_FRAME 0x2
|
||||
#define NGX_HTTP_V2_RST_STREAM_FRAME 0x3
|
||||
#define NGX_HTTP_V2_SETTINGS_FRAME 0x4
|
||||
#define NGX_HTTP_V2_PUSH_PROMISE_FRAME 0x5
|
||||
#define NGX_HTTP_V2_PING_FRAME 0x6
|
||||
#define NGX_HTTP_V2_GOAWAY_FRAME 0x7
|
||||
#define NGX_HTTP_V2_WINDOW_UPDATE_FRAME 0x8
|
||||
#define NGX_HTTP_V2_CONTINUATION_FRAME 0x9
|
||||
#define NGX_HTTP_V2_DATA_FRAME 0x00
|
||||
#define NGX_HTTP_V2_HEADERS_FRAME 0x01
|
||||
#define NGX_HTTP_V2_PRIORITY_FRAME 0x02
|
||||
#define NGX_HTTP_V2_RST_STREAM_FRAME 0x03
|
||||
#define NGX_HTTP_V2_SETTINGS_FRAME 0x04
|
||||
#define NGX_HTTP_V2_PUSH_PROMISE_FRAME 0x05
|
||||
#define NGX_HTTP_V2_PING_FRAME 0x06
|
||||
#define NGX_HTTP_V2_GOAWAY_FRAME 0x07
|
||||
#define NGX_HTTP_V2_WINDOW_UPDATE_FRAME 0x08
|
||||
#define NGX_HTTP_V2_CONTINUATION_FRAME 0x09
|
||||
#define NGX_HTTP_V2_PRIORITY_UPDATE_FRAME 0x10
|
||||
|
||||
/* frame flags */
|
||||
#define NGX_HTTP_V2_NO_FLAG 0x00
|
||||
|
|
@ -183,6 +184,8 @@ struct ngx_http_v2_node_s {
|
|||
ngx_uint_t weight;
|
||||
double rel_weight;
|
||||
ngx_http_v2_stream_t *stream;
|
||||
ngx_http_priority_t priority;
|
||||
ngx_uint_t priority_set; /* unsigned priority_set:1 */
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -212,6 +215,8 @@ struct ngx_http_v2_stream_s {
|
|||
|
||||
ngx_array_t *cookies;
|
||||
|
||||
ngx_http_priority_state_t priority;
|
||||
|
||||
ngx_pool_t *pool;
|
||||
|
||||
unsigned initialized:1;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue