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,
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);
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);
}
+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)
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;
#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
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 */
};
ngx_array_t *cookies;
+ ngx_http_priority_state_t priority;
+
ngx_pool_t *pool;
unsigned initialized:1;