Mercurial > hg > nginx-quic
comparison src/http/v3/ngx_http_v3_tables.c @ 7951:c9538aef3211 quic
HTTP/3: refactored dynamic table implementation.
Previously dynamic table was not functional because of zero limit on its size
set by default. Now the following changes enable it:
- new directives to set SETTINGS_QPACK_MAX_TABLE_CAPACITY and
SETTINGS_QPACK_BLOCKED_STREAMS
- send settings with SETTINGS_QPACK_MAX_TABLE_CAPACITY and
SETTINGS_QPACK_BLOCKED_STREAMS to the client
- send Insert Count Increment to the client
- send Header Acknowledgement to the client
- evict old dynamic table entries on overflow
- decode Required Insert Count from client
- block stream if Required Insert Count is not reached
author | Roman Arutyunyan <arut@nginx.com> |
---|---|
date | Thu, 02 Jul 2020 15:34:05 +0300 |
parents | 26cb2f3259b1 |
children | 72f9ff4e0a88 |
comparison
equal
deleted
inserted
replaced
7950:b0e81f49d7c0 | 7951:c9538aef3211 |
---|---|
8 #include <ngx_config.h> | 8 #include <ngx_config.h> |
9 #include <ngx_core.h> | 9 #include <ngx_core.h> |
10 #include <ngx_http.h> | 10 #include <ngx_http.h> |
11 | 11 |
12 | 12 |
13 static ngx_array_t *ngx_http_v3_get_dynamic_table(ngx_connection_t *c); | 13 #define ngx_http_v3_table_entry_size(n, v) ((n)->len + (v)->len + 32) |
14 | |
15 | |
16 static ngx_int_t ngx_http_v3_evict(ngx_connection_t *c, size_t need); | |
17 static void ngx_http_v3_cleanup_table(void *data); | |
18 static void ngx_http_v3_unblock(void *data); | |
14 static ngx_int_t ngx_http_v3_new_header(ngx_connection_t *c); | 19 static ngx_int_t ngx_http_v3_new_header(ngx_connection_t *c); |
20 | |
21 | |
22 typedef struct { | |
23 ngx_queue_t queue; | |
24 ngx_connection_t *connection; | |
25 ngx_uint_t *nblocked; | |
26 } ngx_http_v3_block_t; | |
15 | 27 |
16 | 28 |
17 static ngx_http_v3_header_t ngx_http_v3_static_table[] = { | 29 static ngx_http_v3_header_t ngx_http_v3_static_table[] = { |
18 | 30 |
19 { ngx_string(":authority"), ngx_string("") }, | 31 { ngx_string(":authority"), ngx_string("") }, |
146 | 158 |
147 ngx_int_t | 159 ngx_int_t |
148 ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, | 160 ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, |
149 ngx_uint_t index, ngx_str_t *value) | 161 ngx_uint_t index, ngx_str_t *value) |
150 { | 162 { |
151 ngx_array_t *dt; | 163 ngx_str_t name; |
152 ngx_connection_t *pc; | 164 |
153 ngx_http_v3_header_t *ref, *h; | 165 if (dynamic) { |
154 | 166 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, |
155 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, | 167 "http3 ref insert dynamic[%ui] \"%V\"", index, value); |
156 "http3 ref insert %s[$ui] \"%V\"", | 168 |
157 dynamic ? "dynamic" : "static", index, value); | 169 if (ngx_http_v3_lookup(c, index, &name, NULL) != NGX_OK) { |
158 | 170 return NGX_ERROR; |
159 pc = c->qs->parent; | 171 } |
160 | 172 |
161 ref = ngx_http_v3_lookup_table(c, dynamic, index); | 173 } else { |
162 if (ref == NULL) { | 174 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, |
163 return NGX_ERROR; | 175 "http3 ref insert static[%ui] \"%V\"", index, value); |
164 } | 176 |
165 | 177 if (ngx_http_v3_lookup_static(c, index, &name, NULL) != NGX_OK) { |
166 dt = ngx_http_v3_get_dynamic_table(c); | 178 return NGX_ERROR; |
167 if (dt == NULL) { | 179 } |
168 return NGX_ERROR; | 180 } |
169 } | 181 |
170 | 182 return ngx_http_v3_insert(c, &name, value); |
171 h = ngx_array_push(dt); | 183 } |
172 if (h == NULL) { | 184 |
173 return NGX_ERROR; | 185 |
174 } | 186 ngx_int_t |
175 | 187 ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) |
176 h->name = ref->name; | 188 { |
177 | 189 u_char *p; |
178 h->value.data = ngx_pstrdup(pc->pool, value); | 190 size_t size; |
179 if (h->value.data == NULL) { | 191 ngx_http_v3_header_t *h; |
180 h->value.len = 0; | 192 ngx_http_v3_connection_t *h3c; |
181 return NGX_ERROR; | 193 ngx_http_v3_dynamic_table_t *dt; |
182 } | 194 |
183 | 195 size = ngx_http_v3_table_entry_size(name, value); |
196 | |
197 if (ngx_http_v3_evict(c, size) != NGX_OK) { | |
198 return NGX_ERROR; | |
199 } | |
200 | |
201 h3c = c->qs->parent->data; | |
202 dt = &h3c->table; | |
203 | |
204 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
205 "http3 insert [%ui] \"%V\":\"%V\", size:%uz", | |
206 dt->base + dt->nelts, name, value, size); | |
207 | |
208 p = ngx_alloc(sizeof(ngx_http_v3_header_t) + name->len + value->len, | |
209 c->log); | |
210 if (p == NULL) { | |
211 return NGX_ERROR; | |
212 } | |
213 | |
214 h = (ngx_http_v3_header_t *) p; | |
215 | |
216 h->name.data = p + sizeof(ngx_http_v3_header_t); | |
217 h->name.len = name->len; | |
218 h->value.data = ngx_cpymem(h->name.data, name->data, name->len); | |
184 h->value.len = value->len; | 219 h->value.len = value->len; |
220 ngx_memcpy(h->value.data, value->data, value->len); | |
221 | |
222 dt->elts[dt->nelts++] = h; | |
223 dt->size += size; | |
224 | |
225 /* TODO increment can be sent less often */ | |
226 | |
227 if (ngx_http_v3_client_inc_insert_count(c, 1) != NGX_OK) { | |
228 return NGX_ERROR; | |
229 } | |
185 | 230 |
186 if (ngx_http_v3_new_header(c) != NGX_OK) { | 231 if (ngx_http_v3_new_header(c) != NGX_OK) { |
187 return NGX_ERROR; | 232 return NGX_ERROR; |
188 } | 233 } |
189 | 234 |
190 return NGX_OK; | 235 return NGX_OK; |
191 } | 236 } |
192 | 237 |
193 | 238 |
194 ngx_int_t | 239 ngx_int_t |
195 ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, | |
196 ngx_str_t *value) | |
197 { | |
198 ngx_array_t *dt; | |
199 ngx_connection_t *pc; | |
200 ngx_http_v3_header_t *h; | |
201 | |
202 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
203 "http3 insert \"%V\":\"%V\"", name, value); | |
204 | |
205 pc = c->qs->parent; | |
206 | |
207 dt = ngx_http_v3_get_dynamic_table(c); | |
208 if (dt == NULL) { | |
209 return NGX_ERROR; | |
210 } | |
211 | |
212 h = ngx_array_push(dt); | |
213 if (h == NULL) { | |
214 return NGX_ERROR; | |
215 } | |
216 | |
217 h->name.data = ngx_pstrdup(pc->pool, name); | |
218 if (h->name.data == NULL) { | |
219 h->name.len = 0; | |
220 h->value.len = 0; | |
221 return NGX_ERROR; | |
222 } | |
223 | |
224 h->name.len = name->len; | |
225 | |
226 h->value.data = ngx_pstrdup(pc->pool, value); | |
227 if (h->value.data == NULL) { | |
228 h->value.len = 0; | |
229 return NGX_ERROR; | |
230 } | |
231 | |
232 h->value.len = value->len; | |
233 | |
234 if (ngx_http_v3_new_header(c) != NGX_OK) { | |
235 return NGX_ERROR; | |
236 } | |
237 | |
238 return NGX_OK; | |
239 } | |
240 | |
241 | |
242 ngx_int_t | |
243 ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) | 240 ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) |
244 { | 241 { |
242 ngx_uint_t max, prev_max; | |
243 ngx_connection_t *pc; | |
244 ngx_pool_cleanup_t *cln; | |
245 ngx_http_v3_header_t **elts; | |
246 ngx_http_v3_srv_conf_t *v3cf; | |
247 ngx_http_v3_connection_t *h3c; | |
248 ngx_http_v3_dynamic_table_t *dt; | |
249 | |
245 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, | 250 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, |
246 "http3 set capacity %ui", capacity); | 251 "http3 set capacity %ui", capacity); |
247 | 252 |
248 /* XXX ignore capacity */ | 253 pc = c->qs->parent; |
254 h3c = pc->data; | |
255 v3cf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, ngx_http_v3_module); | |
256 | |
257 if (capacity > v3cf->max_table_capacity) { | |
258 ngx_log_error(NGX_LOG_INFO, c->log, 0, | |
259 "client exceeded http3_max_table_capacity limit"); | |
260 return NGX_ERROR; | |
261 } | |
262 | |
263 dt = &h3c->table; | |
264 | |
265 if (dt->size > capacity) { | |
266 if (ngx_http_v3_evict(c, dt->size - capacity) != NGX_OK) { | |
267 return NGX_ERROR; | |
268 } | |
269 } | |
270 | |
271 max = capacity / 32; | |
272 prev_max = dt->capacity / 32; | |
273 | |
274 if (max > prev_max) { | |
275 elts = ngx_alloc(max * sizeof(void *), c->log); | |
276 if (elts == NULL) { | |
277 return NGX_ERROR; | |
278 } | |
279 | |
280 if (dt->elts == NULL) { | |
281 cln = ngx_pool_cleanup_add(pc->pool, 0); | |
282 if (cln == NULL) { | |
283 return NGX_ERROR; | |
284 } | |
285 | |
286 cln->handler = ngx_http_v3_cleanup_table; | |
287 cln->data = dt; | |
288 | |
289 } else { | |
290 ngx_memcpy(elts, dt->elts, dt->nelts * sizeof(void *)); | |
291 ngx_free(dt->elts); | |
292 } | |
293 | |
294 dt->elts = elts; | |
295 } | |
296 | |
297 dt->capacity = capacity; | |
298 | |
299 return NGX_OK; | |
300 } | |
301 | |
302 | |
303 static void | |
304 ngx_http_v3_cleanup_table(void *data) | |
305 { | |
306 ngx_http_v3_dynamic_table_t *dt = data; | |
307 | |
308 ngx_uint_t n; | |
309 | |
310 for (n = 0; n < dt->nelts; n++) { | |
311 ngx_free(dt->elts[n]); | |
312 } | |
313 | |
314 ngx_free(dt->elts); | |
315 } | |
316 | |
317 | |
318 static ngx_int_t | |
319 ngx_http_v3_evict(ngx_connection_t *c, size_t need) | |
320 { | |
321 size_t size, target; | |
322 ngx_uint_t n; | |
323 ngx_http_v3_header_t *h; | |
324 ngx_http_v3_connection_t *h3c; | |
325 ngx_http_v3_dynamic_table_t *dt; | |
326 | |
327 h3c = c->qs->parent->data; | |
328 dt = &h3c->table; | |
329 | |
330 if (need > dt->capacity) { | |
331 ngx_log_error(NGX_LOG_ERR, c->log, 0, | |
332 "not enough dynamic table capacity"); | |
333 return NGX_ERROR; | |
334 } | |
335 | |
336 target = dt->capacity - need; | |
337 n = 0; | |
338 | |
339 while (dt->size > target) { | |
340 h = dt->elts[n++]; | |
341 size = ngx_http_v3_table_entry_size(&h->name, &h->value); | |
342 | |
343 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
344 "http3 evict [%ui] \"%V\":\"%V\" size:%uz", | |
345 dt->base, &h->name, &h->value, size); | |
346 | |
347 ngx_free(h); | |
348 dt->size -= size; | |
349 } | |
350 | |
351 if (n) { | |
352 dt->nelts -= n; | |
353 dt->base += n; | |
354 ngx_memmove(dt->elts, &dt->elts[n], dt->nelts * sizeof(void *)); | |
355 } | |
249 | 356 |
250 return NGX_OK; | 357 return NGX_OK; |
251 } | 358 } |
252 | 359 |
253 | 360 |
254 ngx_int_t | 361 ngx_int_t |
255 ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index) | 362 ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index) |
256 { | 363 { |
257 ngx_array_t *dt; | 364 ngx_str_t name, value; |
258 ngx_http_v3_header_t *ref, *h; | 365 ngx_http_v3_connection_t *h3c; |
366 ngx_http_v3_dynamic_table_t *dt; | |
259 | 367 |
260 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 duplicate %ui", index); | 368 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 duplicate %ui", index); |
261 | 369 |
262 ref = ngx_http_v3_lookup_table(c, 1, index); | 370 h3c = c->qs->parent->data; |
263 if (ref == NULL) { | 371 dt = &h3c->table; |
264 return NGX_ERROR; | 372 |
265 } | 373 if (dt->base + dt->nelts <= index) { |
266 | 374 return NGX_ERROR; |
267 dt = ngx_http_v3_get_dynamic_table(c); | 375 } |
268 if (dt == NULL) { | 376 |
269 return NGX_ERROR; | 377 index = dt->base + dt->nelts - 1 - index; |
270 } | 378 |
271 | 379 if (ngx_http_v3_lookup(c, index, &name, &value) != NGX_OK) { |
272 h = ngx_array_push(dt); | 380 return NGX_ERROR; |
273 if (h == NULL) { | 381 } |
274 return NGX_ERROR; | 382 |
275 } | 383 return ngx_http_v3_insert(c, &name, &value); |
276 | |
277 *h = *ref; | |
278 | |
279 if (ngx_http_v3_new_header(c) != NGX_OK) { | |
280 return NGX_ERROR; | |
281 } | |
282 | |
283 return NGX_OK; | |
284 } | 384 } |
285 | 385 |
286 | 386 |
287 ngx_int_t | 387 ngx_int_t |
288 ngx_http_v3_ack_header(ngx_connection_t *c, ngx_uint_t stream_id) | 388 ngx_http_v3_ack_header(ngx_connection_t *c, ngx_uint_t stream_id) |
318 | 418 |
319 return NGX_OK; | 419 return NGX_OK; |
320 } | 420 } |
321 | 421 |
322 | 422 |
323 static ngx_array_t * | 423 ngx_int_t |
324 ngx_http_v3_get_dynamic_table(ngx_connection_t *c) | 424 ngx_http_v3_lookup_static(ngx_connection_t *c, ngx_uint_t index, |
325 { | 425 ngx_str_t *name, ngx_str_t *value) |
326 ngx_connection_t *pc; | 426 { |
327 ngx_http_v3_connection_t *h3c; | 427 ngx_uint_t nelts; |
428 ngx_http_v3_header_t *h; | |
429 | |
430 nelts = sizeof(ngx_http_v3_static_table) | |
431 / sizeof(ngx_http_v3_static_table[0]); | |
432 | |
433 if (index >= nelts) { | |
434 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
435 "http3 static[%ui] lookup out of bounds: %ui", | |
436 index, nelts); | |
437 return NGX_ERROR; | |
438 } | |
439 | |
440 h = &ngx_http_v3_static_table[index]; | |
441 | |
442 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
443 "http3 static[%ui] lookup \"%V\":\"%V\"", | |
444 index, &h->name, &h->value); | |
445 | |
446 if (name) { | |
447 *name = h->name; | |
448 } | |
449 | |
450 if (value) { | |
451 *value = h->value; | |
452 } | |
453 | |
454 return NGX_OK; | |
455 } | |
456 | |
457 | |
458 ngx_int_t | |
459 ngx_http_v3_lookup(ngx_connection_t *c, ngx_uint_t index, ngx_str_t *name, | |
460 ngx_str_t *value) | |
461 { | |
462 ngx_http_v3_header_t *h; | |
463 ngx_http_v3_connection_t *h3c; | |
464 ngx_http_v3_dynamic_table_t *dt; | |
465 | |
466 h3c = c->qs->parent->data; | |
467 dt = &h3c->table; | |
468 | |
469 if (index < dt->base || index - dt->base >= dt->nelts) { | |
470 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
471 "http3 dynamic[%ui] lookup out of bounds: [%ui,%ui]", | |
472 index, dt->base, dt->base + dt->nelts); | |
473 return NGX_ERROR; | |
474 } | |
475 | |
476 h = dt->elts[index - dt->base]; | |
477 | |
478 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
479 "http3 dynamic[%ui] lookup \"%V\":\"%V\"", | |
480 index, &h->name, &h->value); | |
481 | |
482 if (name) { | |
483 *name = h->name; | |
484 } | |
485 | |
486 if (value) { | |
487 *value = h->value; | |
488 } | |
489 | |
490 return NGX_OK; | |
491 } | |
492 | |
493 | |
494 ngx_int_t | |
495 ngx_http_v3_decode_insert_count(ngx_connection_t *c, ngx_uint_t *insert_count) | |
496 { | |
497 ngx_uint_t max_entries, full_range, max_value, | |
498 max_wrapped, req_insert_count; | |
499 ngx_http_v3_srv_conf_t *v3cf; | |
500 ngx_http_v3_connection_t *h3c; | |
501 ngx_http_v3_dynamic_table_t *dt; | |
502 | |
503 /* QPACK 4.5.1.1. Required Insert Count */ | |
504 | |
505 if (*insert_count == 0) { | |
506 return NGX_OK; | |
507 } | |
508 | |
509 h3c = c->qs->parent->data; | |
510 dt = &h3c->table; | |
511 | |
512 v3cf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, ngx_http_v3_module); | |
513 | |
514 max_entries = v3cf->max_table_capacity / 32; | |
515 full_range = 2 * max_entries; | |
516 | |
517 if (*insert_count > full_range) { | |
518 return NGX_ERROR; | |
519 } | |
520 | |
521 max_value = dt->base + dt->nelts + max_entries; | |
522 max_wrapped = (max_value / full_range) * full_range; | |
523 req_insert_count = max_wrapped + *insert_count - 1; | |
524 | |
525 if (req_insert_count > max_value) { | |
526 if (req_insert_count <= full_range) { | |
527 return NGX_ERROR; | |
528 } | |
529 | |
530 req_insert_count -= full_range; | |
531 } | |
532 | |
533 if (req_insert_count == 0) { | |
534 return NGX_ERROR; | |
535 } | |
536 | |
537 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
538 "http3 decode insert_count %ui -> %ui", | |
539 *insert_count, req_insert_count); | |
540 | |
541 *insert_count = req_insert_count; | |
542 | |
543 return NGX_OK; | |
544 } | |
545 | |
546 | |
547 ngx_int_t | |
548 ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count) | |
549 { | |
550 size_t n; | |
551 ngx_connection_t *pc; | |
552 ngx_pool_cleanup_t *cln; | |
553 ngx_http_v3_block_t *block; | |
554 ngx_http_v3_srv_conf_t *v3cf; | |
555 ngx_http_v3_connection_t *h3c; | |
556 ngx_http_v3_dynamic_table_t *dt; | |
328 | 557 |
329 pc = c->qs->parent; | 558 pc = c->qs->parent; |
330 h3c = pc->data; | 559 h3c = pc->data; |
331 | 560 dt = &h3c->table; |
332 if (h3c->dynamic) { | 561 |
333 return h3c->dynamic; | 562 n = dt->base + dt->nelts; |
334 } | |
335 | |
336 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 create dynamic table"); | |
337 | |
338 h3c->dynamic = ngx_array_create(pc->pool, 1, sizeof(ngx_http_v3_header_t)); | |
339 | |
340 return h3c->dynamic; | |
341 } | |
342 | |
343 | |
344 ngx_http_v3_header_t * | |
345 ngx_http_v3_lookup_table(ngx_connection_t *c, ngx_uint_t dynamic, | |
346 ngx_uint_t index) | |
347 { | |
348 ngx_uint_t nelts; | |
349 ngx_array_t *dt; | |
350 ngx_http_v3_header_t *table; | |
351 | |
352 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 lookup %s[%ui]", | |
353 dynamic ? "dynamic" : "static", index); | |
354 | |
355 if (dynamic) { | |
356 dt = ngx_http_v3_get_dynamic_table(c); | |
357 if (dt == NULL) { | |
358 return NULL; | |
359 } | |
360 | |
361 table = dt->elts; | |
362 nelts = dt->nelts; | |
363 | |
364 } else { | |
365 table = ngx_http_v3_static_table; | |
366 nelts = sizeof(ngx_http_v3_static_table) | |
367 / sizeof(ngx_http_v3_static_table[0]); | |
368 } | |
369 | |
370 if (index >= nelts) { | |
371 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
372 "http3 lookup out of bounds: %ui", nelts); | |
373 return NULL; | |
374 } | |
375 | |
376 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 lookup \"%V\":\"%V\"", | |
377 &table[index].name, &table[index].value); | |
378 | |
379 return &table[index]; | |
380 } | |
381 | |
382 | |
383 ngx_int_t | |
384 ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count) | |
385 { | |
386 size_t n; | |
387 ngx_http_v3_connection_t *h3c; | |
388 | |
389 h3c = c->qs->parent->data; | |
390 n = h3c->dynamic ? h3c->dynamic->nelts : 0; | |
391 | 563 |
392 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, | 564 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, |
393 "http3 check insert count %ui/%ui", insert_count, n); | 565 "http3 check insert count req:%ui, have:%ui", |
394 | 566 insert_count, n); |
395 if (n < insert_count) { | 567 |
396 /* XXX how to get notified? */ | 568 if (n >= insert_count) { |
397 /* XXX wake all streams on any arrival to the encoder stream? */ | 569 return NGX_OK; |
398 return NGX_AGAIN; | 570 } |
399 } | 571 |
400 | 572 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 block stream"); |
401 return NGX_OK; | 573 |
574 block = NULL; | |
575 | |
576 for (cln = c->pool->cleanup; cln; cln = cln->next) { | |
577 if (cln->handler == ngx_http_v3_unblock) { | |
578 block = cln->data; | |
579 break; | |
580 } | |
581 } | |
582 | |
583 if (block == NULL) { | |
584 cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_http_v3_block_t)); | |
585 if (cln == NULL) { | |
586 return NGX_ERROR; | |
587 } | |
588 | |
589 cln->handler = ngx_http_v3_unblock; | |
590 | |
591 block = cln->data; | |
592 block->queue.prev = NULL; | |
593 block->connection = c; | |
594 block->nblocked = &h3c->nblocked; | |
595 } | |
596 | |
597 if (block->queue.prev == NULL) { | |
598 v3cf = ngx_http_get_module_srv_conf(h3c->hc.conf_ctx, | |
599 ngx_http_v3_module); | |
600 | |
601 if (h3c->nblocked == v3cf->max_blocked_streams) { | |
602 ngx_log_error(NGX_LOG_INFO, c->log, 0, | |
603 "client exceeded http3_max_blocked_streams limit"); | |
604 return NGX_ERROR; | |
605 } | |
606 | |
607 h3c->nblocked++; | |
608 ngx_queue_insert_tail(&h3c->blocked, &block->queue); | |
609 } | |
610 | |
611 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
612 "http3 blocked:%ui", h3c->nblocked); | |
613 | |
614 return NGX_BUSY; | |
615 } | |
616 | |
617 | |
618 static void | |
619 ngx_http_v3_unblock(void *data) | |
620 { | |
621 ngx_http_v3_block_t *block = data; | |
622 | |
623 if (block->queue.prev) { | |
624 ngx_queue_remove(&block->queue); | |
625 block->queue.prev = NULL; | |
626 (*block->nblocked)--; | |
627 } | |
402 } | 628 } |
403 | 629 |
404 | 630 |
405 static ngx_int_t | 631 static ngx_int_t |
406 ngx_http_v3_new_header(ngx_connection_t *c) | 632 ngx_http_v3_new_header(ngx_connection_t *c) |
407 { | 633 { |
408 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 new dynamic header"); | 634 ngx_queue_t *q; |
409 | 635 ngx_connection_t *bc; |
410 /* XXX report all waiting streams of a new header */ | 636 ngx_http_v3_block_t *block; |
637 ngx_http_v3_connection_t *h3c; | |
638 | |
639 h3c = c->qs->parent->data; | |
640 | |
641 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
642 "http3 new dynamic header, blocked:%ui", h3c->nblocked); | |
643 | |
644 while (!ngx_queue_empty(&h3c->blocked)) { | |
645 q = ngx_queue_head(&h3c->blocked); | |
646 block = (ngx_http_v3_block_t *) q; | |
647 bc = block->connection; | |
648 | |
649 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, bc->log, 0, "http3 unblock stream"); | |
650 | |
651 ngx_http_v3_unblock(block); | |
652 ngx_post_event(bc->read, &ngx_posted_events); | |
653 } | |
411 | 654 |
412 return NGX_OK; | 655 return NGX_OK; |
413 } | 656 } |
414 | 657 |
415 | 658 |