1 /*
2 * nghttp2 - HTTP/2 C Library
3 *
4 * Copyright (c) 2012 Tatsuhiro Tsujikawa
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25 #include "nghttp2_stream.h"
26
27 #include <assert.h>
28 #include <stdio.h>
29
30 #include "nghttp2_session.h"
31 #include "nghttp2_helper.h"
32 #include "nghttp2_debug.h"
33 #include "nghttp2_frame.h"
34
35 /* Maximum distance between any two stream's cycle in the same
36 priority queue. Imagine stream A's cycle is A, and stream B's
37 cycle is B, and A < B. The cycle is unsigned 32 bit integer, it
38 may get overflow. Because of how we calculate the next cycle
39 value, if B - A is less than or equals to
40 NGHTTP2_MAX_CYCLE_DISTANCE, A and B are in the same scale, in other
41 words, B is really greater than or equal to A. Otherwise, A is a
42 result of overflow, and it is actually A > B if we consider that
43 fact. */
44 #define NGHTTP2_MAX_CYCLE_DISTANCE \
45 ((uint64_t)NGHTTP2_MAX_FRAME_SIZE_MAX * 256 + 255)
46
stream_less(const void * lhsx,const void * rhsx)47 static int stream_less(const void *lhsx, const void *rhsx) {
48 const nghttp2_stream *lhs, *rhs;
49
50 lhs = nghttp2_struct_of(lhsx, nghttp2_stream, pq_entry);
51 rhs = nghttp2_struct_of(rhsx, nghttp2_stream, pq_entry);
52
53 if (lhs->cycle == rhs->cycle) {
54 return lhs->seq < rhs->seq;
55 }
56
57 return rhs->cycle - lhs->cycle <= NGHTTP2_MAX_CYCLE_DISTANCE;
58 }
59
nghttp2_stream_init(nghttp2_stream * stream,int32_t stream_id,uint8_t flags,nghttp2_stream_state initial_state,int32_t weight,int32_t remote_initial_window_size,int32_t local_initial_window_size,void * stream_user_data,nghttp2_mem * mem)60 void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
61 uint8_t flags, nghttp2_stream_state initial_state,
62 int32_t weight, int32_t remote_initial_window_size,
63 int32_t local_initial_window_size,
64 void *stream_user_data, nghttp2_mem *mem) {
65 nghttp2_pq_init(&stream->obq, stream_less, mem);
66
67 stream->stream_id = stream_id;
68 stream->flags = flags;
69 stream->state = initial_state;
70 stream->shut_flags = NGHTTP2_SHUT_NONE;
71 stream->stream_user_data = stream_user_data;
72 stream->item = NULL;
73 stream->remote_window_size = remote_initial_window_size;
74 stream->local_window_size = local_initial_window_size;
75 stream->recv_window_size = 0;
76 stream->consumed_size = 0;
77 stream->recv_reduction = 0;
78 stream->window_update_queued = 0;
79
80 stream->dep_prev = NULL;
81 stream->dep_next = NULL;
82 stream->sib_prev = NULL;
83 stream->sib_next = NULL;
84
85 stream->closed_prev = NULL;
86 stream->closed_next = NULL;
87
88 stream->weight = weight;
89 stream->sum_dep_weight = 0;
90
91 stream->http_flags = NGHTTP2_HTTP_FLAG_NONE;
92 stream->content_length = -1;
93 stream->recv_content_length = 0;
94 stream->status_code = -1;
95
96 stream->queued = 0;
97 stream->descendant_last_cycle = 0;
98 stream->cycle = 0;
99 stream->pending_penalty = 0;
100 stream->descendant_next_seq = 0;
101 stream->seq = 0;
102 stream->last_writelen = 0;
103
104 stream->extpri = stream->http_extpri = NGHTTP2_EXTPRI_DEFAULT_URGENCY;
105 }
106
nghttp2_stream_free(nghttp2_stream * stream)107 void nghttp2_stream_free(nghttp2_stream *stream) {
108 nghttp2_pq_free(&stream->obq);
109 /* We don't free stream->item. If it is assigned to aob, then
110 active_outbound_item_reset() will delete it. Otherwise,
111 nghttp2_stream_close() or session_del() will delete it. */
112 }
113
nghttp2_stream_shutdown(nghttp2_stream * stream,nghttp2_shut_flag flag)114 void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag) {
115 stream->shut_flags = (uint8_t)(stream->shut_flags | flag);
116 }
117
118 /*
119 * Returns nonzero if |stream| is active. This function does not take
120 * into account its descendants.
121 */
stream_active(nghttp2_stream * stream)122 static int stream_active(nghttp2_stream *stream) {
123 return stream->item &&
124 (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0;
125 }
126
127 /*
128 * Returns nonzero if |stream| or one of its descendants is active
129 */
stream_subtree_active(nghttp2_stream * stream)130 static int stream_subtree_active(nghttp2_stream *stream) {
131 return stream_active(stream) || !nghttp2_pq_empty(&stream->obq);
132 }
133
134 /*
135 * Returns next cycle for |stream|.
136 */
stream_next_cycle(nghttp2_stream * stream,uint64_t last_cycle)137 static void stream_next_cycle(nghttp2_stream *stream, uint64_t last_cycle) {
138 uint64_t penalty;
139
140 penalty = (uint64_t)stream->last_writelen * NGHTTP2_MAX_WEIGHT +
141 stream->pending_penalty;
142
143 stream->cycle = last_cycle + penalty / (uint32_t)stream->weight;
144 stream->pending_penalty = (uint32_t)(penalty % (uint32_t)stream->weight);
145 }
146
stream_obq_push(nghttp2_stream * dep_stream,nghttp2_stream * stream)147 static int stream_obq_push(nghttp2_stream *dep_stream, nghttp2_stream *stream) {
148 int rv;
149
150 for (; dep_stream && !stream->queued;
151 stream = dep_stream, dep_stream = dep_stream->dep_prev) {
152 stream_next_cycle(stream, dep_stream->descendant_last_cycle);
153 stream->seq = dep_stream->descendant_next_seq++;
154
155 DEBUGF("stream: stream=%d obq push cycle=%lu\n", stream->stream_id,
156 stream->cycle);
157
158 DEBUGF("stream: push stream %d to stream %d\n", stream->stream_id,
159 dep_stream->stream_id);
160
161 rv = nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry);
162 if (rv != 0) {
163 return rv;
164 }
165 stream->queued = 1;
166 }
167
168 return 0;
169 }
170
171 /*
172 * Removes |stream| from parent's obq. If removal of |stream| makes
173 * parent's obq empty, and parent is not active, then parent is also
174 * removed. This process is repeated recursively.
175 */
stream_obq_remove(nghttp2_stream * stream)176 static void stream_obq_remove(nghttp2_stream *stream) {
177 nghttp2_stream *dep_stream;
178
179 dep_stream = stream->dep_prev;
180
181 if (!stream->queued) {
182 return;
183 }
184
185 for (; dep_stream; stream = dep_stream, dep_stream = dep_stream->dep_prev) {
186 DEBUGF("stream: remove stream %d from stream %d\n", stream->stream_id,
187 dep_stream->stream_id);
188
189 nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry);
190
191 assert(stream->queued);
192
193 stream->queued = 0;
194 stream->cycle = 0;
195 stream->pending_penalty = 0;
196 stream->descendant_last_cycle = 0;
197 stream->last_writelen = 0;
198
199 if (stream_subtree_active(dep_stream)) {
200 return;
201 }
202 }
203 }
204
205 /*
206 * Moves |stream| from |src|'s obq to |dest|'s obq. Removal from
207 * |src|'s obq is just done calling nghttp2_pq_remove(), so it does
208 * not recursively remove |src| and ancestors, like
209 * stream_obq_remove().
210 */
stream_obq_move(nghttp2_stream * dest,nghttp2_stream * src,nghttp2_stream * stream)211 static int stream_obq_move(nghttp2_stream *dest, nghttp2_stream *src,
212 nghttp2_stream *stream) {
213 if (!stream->queued) {
214 return 0;
215 }
216
217 DEBUGF("stream: remove stream %d from stream %d (move)\n", stream->stream_id,
218 src->stream_id);
219
220 nghttp2_pq_remove(&src->obq, &stream->pq_entry);
221 stream->queued = 0;
222
223 return stream_obq_push(dest, stream);
224 }
225
nghttp2_stream_reschedule(nghttp2_stream * stream)226 void nghttp2_stream_reschedule(nghttp2_stream *stream) {
227 nghttp2_stream *dep_stream;
228
229 assert(stream->queued);
230
231 dep_stream = stream->dep_prev;
232
233 for (; dep_stream; stream = dep_stream, dep_stream = dep_stream->dep_prev) {
234 nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry);
235
236 stream_next_cycle(stream, dep_stream->descendant_last_cycle);
237 stream->seq = dep_stream->descendant_next_seq++;
238
239 nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry);
240
241 DEBUGF("stream: stream=%d obq resched cycle=%lu\n", stream->stream_id,
242 stream->cycle);
243
244 dep_stream->last_writelen = stream->last_writelen;
245 }
246 }
247
nghttp2_stream_change_weight(nghttp2_stream * stream,int32_t weight)248 void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight) {
249 nghttp2_stream *dep_stream;
250 uint64_t last_cycle;
251 int32_t old_weight;
252 uint64_t wlen_penalty;
253
254 if (stream->weight == weight) {
255 return;
256 }
257
258 old_weight = stream->weight;
259 stream->weight = weight;
260
261 dep_stream = stream->dep_prev;
262
263 if (!dep_stream) {
264 return;
265 }
266
267 dep_stream->sum_dep_weight += weight - old_weight;
268
269 if (!stream->queued) {
270 return;
271 }
272
273 nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry);
274
275 wlen_penalty = (uint64_t)stream->last_writelen * NGHTTP2_MAX_WEIGHT;
276
277 /* Compute old stream->pending_penalty we used to calculate
278 stream->cycle */
279 stream->pending_penalty =
280 (uint32_t)((stream->pending_penalty + (uint32_t)old_weight -
281 (wlen_penalty % (uint32_t)old_weight)) %
282 (uint32_t)old_weight);
283
284 last_cycle = stream->cycle -
285 (wlen_penalty + stream->pending_penalty) / (uint32_t)old_weight;
286
287 /* Now we have old stream->pending_penalty and new stream->weight in
288 place */
289 stream_next_cycle(stream, last_cycle);
290
291 if (dep_stream->descendant_last_cycle - stream->cycle <=
292 NGHTTP2_MAX_CYCLE_DISTANCE) {
293 stream->cycle = dep_stream->descendant_last_cycle;
294 }
295
296 /* Continue to use same stream->seq */
297
298 nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry);
299
300 DEBUGF("stream: stream=%d obq resched cycle=%lu\n", stream->stream_id,
301 stream->cycle);
302 }
303
stream_last_sib(nghttp2_stream * stream)304 static nghttp2_stream *stream_last_sib(nghttp2_stream *stream) {
305 for (; stream->sib_next; stream = stream->sib_next)
306 ;
307
308 return stream;
309 }
310
nghttp2_stream_dep_distributed_weight(nghttp2_stream * stream,int32_t weight)311 int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream,
312 int32_t weight) {
313 weight = stream->weight * weight / stream->sum_dep_weight;
314
315 return nghttp2_max(1, weight);
316 }
317
318 #ifdef STREAM_DEP_DEBUG
319
ensure_inactive(nghttp2_stream * stream)320 static void ensure_inactive(nghttp2_stream *stream) {
321 nghttp2_stream *si;
322
323 if (stream->queued) {
324 fprintf(stderr, "stream(%p)=%d, stream->queued = 1; want 0\n", stream,
325 stream->stream_id);
326 assert(0);
327 }
328
329 if (stream_active(stream)) {
330 fprintf(stderr, "stream(%p)=%d, stream_active(stream) = 1; want 0\n",
331 stream, stream->stream_id);
332 assert(0);
333 }
334
335 if (!nghttp2_pq_empty(&stream->obq)) {
336 fprintf(stderr, "stream(%p)=%d, nghttp2_pq_size() = %zu; want 0\n", stream,
337 stream->stream_id, nghttp2_pq_size(&stream->obq));
338 assert(0);
339 }
340
341 for (si = stream->dep_next; si; si = si->sib_next) {
342 ensure_inactive(si);
343 }
344 }
345
check_queued(nghttp2_stream * stream)346 static void check_queued(nghttp2_stream *stream) {
347 nghttp2_stream *si;
348 int queued;
349
350 if (stream->queued) {
351 if (!stream_subtree_active(stream)) {
352 fprintf(stderr,
353 "stream(%p)=%d, stream->queued == 1, but "
354 "stream_active() == %d and nghttp2_pq_size(&stream->obq) = %zu\n",
355 stream, stream->stream_id, stream_active(stream),
356 nghttp2_pq_size(&stream->obq));
357 assert(0);
358 }
359 if (!stream_active(stream)) {
360 queued = 0;
361 for (si = stream->dep_next; si; si = si->sib_next) {
362 if (si->queued) {
363 ++queued;
364 }
365 }
366 if (queued == 0) {
367 fprintf(stderr,
368 "stream(%p)=%d, stream->queued == 1, and "
369 "!stream_active(), but no descendants is queued\n",
370 stream, stream->stream_id);
371 assert(0);
372 }
373 }
374
375 for (si = stream->dep_next; si; si = si->sib_next) {
376 check_queued(si);
377 }
378 } else {
379 if (stream_active(stream) || !nghttp2_pq_empty(&stream->obq)) {
380 fprintf(stderr,
381 "stream(%p) = %d, stream->queued == 0, but "
382 "stream_active(stream) == %d and "
383 "nghttp2_pq_size(&stream->obq) = %zu\n",
384 stream, stream->stream_id, stream_active(stream),
385 nghttp2_pq_size(&stream->obq));
386 assert(0);
387 }
388 for (si = stream->dep_next; si; si = si->sib_next) {
389 ensure_inactive(si);
390 }
391 }
392 }
393
check_sum_dep(nghttp2_stream * stream)394 static void check_sum_dep(nghttp2_stream *stream) {
395 nghttp2_stream *si;
396 int32_t n = 0;
397 for (si = stream->dep_next; si; si = si->sib_next) {
398 n += si->weight;
399 }
400 if (n != stream->sum_dep_weight) {
401 fprintf(stderr, "stream(%p)=%d, sum_dep_weight = %d; want %d\n", stream,
402 stream->stream_id, n, stream->sum_dep_weight);
403 assert(0);
404 }
405 for (si = stream->dep_next; si; si = si->sib_next) {
406 check_sum_dep(si);
407 }
408 }
409
check_dep_prev(nghttp2_stream * stream)410 static void check_dep_prev(nghttp2_stream *stream) {
411 nghttp2_stream *si;
412 for (si = stream->dep_next; si; si = si->sib_next) {
413 if (si->dep_prev != stream) {
414 fprintf(stderr, "si->dep_prev = %p; want %p\n", si->dep_prev, stream);
415 assert(0);
416 }
417 check_dep_prev(si);
418 }
419 }
420
421 #endif /* STREAM_DEP_DEBUG */
422
423 #ifdef STREAM_DEP_DEBUG
validate_tree(nghttp2_stream * stream)424 static void validate_tree(nghttp2_stream *stream) {
425 nghttp2_stream *si;
426
427 if (!stream) {
428 return;
429 }
430
431 for (; stream->dep_prev; stream = stream->dep_prev)
432 ;
433
434 assert(stream->stream_id == 0);
435 assert(!stream->queued);
436
437 fprintf(stderr, "checking...\n");
438 if (nghttp2_pq_empty(&stream->obq)) {
439 fprintf(stderr, "root obq empty\n");
440 for (si = stream->dep_next; si; si = si->sib_next) {
441 ensure_inactive(si);
442 }
443 } else {
444 for (si = stream->dep_next; si; si = si->sib_next) {
445 check_queued(si);
446 }
447 }
448
449 check_sum_dep(stream);
450 check_dep_prev(stream);
451 }
452 #else /* !STREAM_DEP_DEBUG */
validate_tree(nghttp2_stream * stream)453 static void validate_tree(nghttp2_stream *stream) { (void)stream; }
454 #endif /* !STREAM_DEP_DEBUG*/
455
stream_update_dep_on_attach_item(nghttp2_stream * stream)456 static int stream_update_dep_on_attach_item(nghttp2_stream *stream) {
457 int rv;
458
459 rv = stream_obq_push(stream->dep_prev, stream);
460 if (rv != 0) {
461 return rv;
462 }
463
464 validate_tree(stream);
465 return 0;
466 }
467
stream_update_dep_on_detach_item(nghttp2_stream * stream)468 static int stream_update_dep_on_detach_item(nghttp2_stream *stream) {
469 if (nghttp2_pq_empty(&stream->obq)) {
470 stream_obq_remove(stream);
471 }
472
473 validate_tree(stream);
474
475 return 0;
476 }
477
nghttp2_stream_attach_item(nghttp2_stream * stream,nghttp2_outbound_item * item)478 int nghttp2_stream_attach_item(nghttp2_stream *stream,
479 nghttp2_outbound_item *item) {
480 int rv;
481
482 assert((stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0);
483 assert(stream->item == NULL);
484
485 DEBUGF("stream: stream=%d attach item=%p\n", stream->stream_id, item);
486
487 stream->item = item;
488
489 if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
490 return 0;
491 }
492
493 rv = stream_update_dep_on_attach_item(stream);
494 if (rv != 0) {
495 /* This may relave stream->queued == 1, but stream->item == NULL.
496 But only consequence of this error is fatal one, and session
497 destruction. In that execution path, these inconsistency does
498 not matter. */
499 stream->item = NULL;
500 return rv;
501 }
502
503 return 0;
504 }
505
nghttp2_stream_detach_item(nghttp2_stream * stream)506 int nghttp2_stream_detach_item(nghttp2_stream *stream) {
507 DEBUGF("stream: stream=%d detach item=%p\n", stream->stream_id, stream->item);
508
509 stream->item = NULL;
510 stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_DEFERRED_ALL);
511
512 if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
513 return 0;
514 }
515
516 return stream_update_dep_on_detach_item(stream);
517 }
518
nghttp2_stream_defer_item(nghttp2_stream * stream,uint8_t flags)519 int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags) {
520 assert(stream->item);
521
522 DEBUGF("stream: stream=%d defer item=%p cause=%02x\n", stream->stream_id,
523 stream->item, flags);
524
525 stream->flags |= flags;
526
527 if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
528 return 0;
529 }
530
531 return stream_update_dep_on_detach_item(stream);
532 }
533
nghttp2_stream_resume_deferred_item(nghttp2_stream * stream,uint8_t flags)534 int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags) {
535 assert(stream->item);
536
537 DEBUGF("stream: stream=%d resume item=%p flags=%02x\n", stream->stream_id,
538 stream->item, flags);
539
540 stream->flags = (uint8_t)(stream->flags & ~flags);
541
542 if (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) {
543 return 0;
544 }
545
546 if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
547 return 0;
548 }
549
550 return stream_update_dep_on_attach_item(stream);
551 }
552
nghttp2_stream_check_deferred_item(nghttp2_stream * stream)553 int nghttp2_stream_check_deferred_item(nghttp2_stream *stream) {
554 return stream->item && (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL);
555 }
556
nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream * stream)557 int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream) {
558 return stream->item &&
559 (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
560 }
561
update_initial_window_size(int32_t * window_size_ptr,int32_t new_initial_window_size,int32_t old_initial_window_size)562 static int update_initial_window_size(int32_t *window_size_ptr,
563 int32_t new_initial_window_size,
564 int32_t old_initial_window_size) {
565 int64_t new_window_size = (int64_t)(*window_size_ptr) +
566 new_initial_window_size - old_initial_window_size;
567 if (INT32_MIN > new_window_size ||
568 new_window_size > NGHTTP2_MAX_WINDOW_SIZE) {
569 return -1;
570 }
571 *window_size_ptr = (int32_t)new_window_size;
572 return 0;
573 }
574
nghttp2_stream_update_remote_initial_window_size(nghttp2_stream * stream,int32_t new_initial_window_size,int32_t old_initial_window_size)575 int nghttp2_stream_update_remote_initial_window_size(
576 nghttp2_stream *stream, int32_t new_initial_window_size,
577 int32_t old_initial_window_size) {
578 return update_initial_window_size(&stream->remote_window_size,
579 new_initial_window_size,
580 old_initial_window_size);
581 }
582
nghttp2_stream_update_local_initial_window_size(nghttp2_stream * stream,int32_t new_initial_window_size,int32_t old_initial_window_size)583 int nghttp2_stream_update_local_initial_window_size(
584 nghttp2_stream *stream, int32_t new_initial_window_size,
585 int32_t old_initial_window_size) {
586 return update_initial_window_size(&stream->local_window_size,
587 new_initial_window_size,
588 old_initial_window_size);
589 }
590
nghttp2_stream_promise_fulfilled(nghttp2_stream * stream)591 void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream) {
592 stream->state = NGHTTP2_STREAM_OPENED;
593 stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_PUSH);
594 }
595
nghttp2_stream_dep_find_ancestor(nghttp2_stream * stream,nghttp2_stream * target)596 int nghttp2_stream_dep_find_ancestor(nghttp2_stream *stream,
597 nghttp2_stream *target) {
598 for (; stream; stream = stream->dep_prev) {
599 if (stream == target) {
600 return 1;
601 }
602 }
603 return 0;
604 }
605
nghttp2_stream_dep_insert(nghttp2_stream * dep_stream,nghttp2_stream * stream)606 int nghttp2_stream_dep_insert(nghttp2_stream *dep_stream,
607 nghttp2_stream *stream) {
608 nghttp2_stream *si;
609 int rv;
610
611 DEBUGF("stream: dep_insert dep_stream(%p)=%d, stream(%p)=%d\n", dep_stream,
612 dep_stream->stream_id, stream, stream->stream_id);
613
614 stream->sum_dep_weight = dep_stream->sum_dep_weight;
615 dep_stream->sum_dep_weight = stream->weight;
616
617 if (dep_stream->dep_next) {
618 for (si = dep_stream->dep_next; si; si = si->sib_next) {
619 si->dep_prev = stream;
620 if (si->queued) {
621 rv = stream_obq_move(stream, dep_stream, si);
622 if (rv != 0) {
623 return rv;
624 }
625 }
626 }
627
628 if (stream_subtree_active(stream)) {
629 rv = stream_obq_push(dep_stream, stream);
630 if (rv != 0) {
631 return rv;
632 }
633 }
634
635 stream->dep_next = dep_stream->dep_next;
636 }
637
638 dep_stream->dep_next = stream;
639 stream->dep_prev = dep_stream;
640
641 validate_tree(stream);
642
643 return 0;
644 }
645
set_dep_prev(nghttp2_stream * stream,nghttp2_stream * dep)646 static void set_dep_prev(nghttp2_stream *stream, nghttp2_stream *dep) {
647 for (; stream; stream = stream->sib_next) {
648 stream->dep_prev = dep;
649 }
650 }
651
link_dep(nghttp2_stream * dep_stream,nghttp2_stream * stream)652 static void link_dep(nghttp2_stream *dep_stream, nghttp2_stream *stream) {
653 dep_stream->dep_next = stream;
654 if (stream) {
655 stream->dep_prev = dep_stream;
656 }
657 }
658
link_sib(nghttp2_stream * a,nghttp2_stream * b)659 static void link_sib(nghttp2_stream *a, nghttp2_stream *b) {
660 a->sib_next = b;
661 if (b) {
662 b->sib_prev = a;
663 }
664 }
665
insert_link_dep(nghttp2_stream * dep_stream,nghttp2_stream * stream)666 static void insert_link_dep(nghttp2_stream *dep_stream,
667 nghttp2_stream *stream) {
668 nghttp2_stream *sib_next;
669
670 assert(stream->sib_prev == NULL);
671
672 sib_next = dep_stream->dep_next;
673
674 link_sib(stream, sib_next);
675
676 link_dep(dep_stream, stream);
677 }
678
unlink_sib(nghttp2_stream * stream)679 static void unlink_sib(nghttp2_stream *stream) {
680 nghttp2_stream *prev, *next, *dep_next;
681
682 prev = stream->sib_prev;
683 dep_next = stream->dep_next;
684
685 assert(prev);
686
687 if (dep_next) {
688 /*
689 * prev--stream(--sib_next--...)
690 * |
691 * dep_next
692 */
693
694 link_sib(prev, dep_next);
695
696 set_dep_prev(dep_next, stream->dep_prev);
697
698 if (stream->sib_next) {
699 link_sib(stream_last_sib(dep_next), stream->sib_next);
700 }
701 } else {
702 /*
703 * prev--stream(--sib_next--...)
704 */
705 next = stream->sib_next;
706
707 prev->sib_next = next;
708
709 if (next) {
710 next->sib_prev = prev;
711 }
712 }
713 }
714
unlink_dep(nghttp2_stream * stream)715 static void unlink_dep(nghttp2_stream *stream) {
716 nghttp2_stream *prev, *next, *dep_next;
717
718 prev = stream->dep_prev;
719 dep_next = stream->dep_next;
720
721 assert(prev);
722
723 if (dep_next) {
724 /*
725 * prev
726 * |
727 * stream(--sib_next--...)
728 * |
729 * dep_next
730 */
731 link_dep(prev, dep_next);
732
733 set_dep_prev(dep_next, stream->dep_prev);
734
735 if (stream->sib_next) {
736 link_sib(stream_last_sib(dep_next), stream->sib_next);
737 }
738
739 } else if (stream->sib_next) {
740 /*
741 * prev
742 * |
743 * stream--sib_next
744 */
745 next = stream->sib_next;
746
747 next->sib_prev = NULL;
748
749 link_dep(prev, next);
750 } else {
751 prev->dep_next = NULL;
752 }
753 }
754
nghttp2_stream_dep_add(nghttp2_stream * dep_stream,nghttp2_stream * stream)755 void nghttp2_stream_dep_add(nghttp2_stream *dep_stream,
756 nghttp2_stream *stream) {
757 DEBUGF("stream: dep_add dep_stream(%p)=%d, stream(%p)=%d\n", dep_stream,
758 dep_stream->stream_id, stream, stream->stream_id);
759
760 dep_stream->sum_dep_weight += stream->weight;
761
762 if (dep_stream->dep_next == NULL) {
763 link_dep(dep_stream, stream);
764 } else {
765 insert_link_dep(dep_stream, stream);
766 }
767
768 validate_tree(stream);
769 }
770
nghttp2_stream_dep_remove(nghttp2_stream * stream)771 int nghttp2_stream_dep_remove(nghttp2_stream *stream) {
772 nghttp2_stream *dep_prev, *si;
773 int32_t sum_dep_weight_delta;
774 int rv;
775
776 DEBUGF("stream: dep_remove stream(%p)=%d\n", stream, stream->stream_id);
777
778 /* Distribute weight of |stream| to direct descendants */
779 sum_dep_weight_delta = -stream->weight;
780
781 for (si = stream->dep_next; si; si = si->sib_next) {
782 si->weight = nghttp2_stream_dep_distributed_weight(stream, si->weight);
783
784 sum_dep_weight_delta += si->weight;
785
786 if (si->queued) {
787 rv = stream_obq_move(stream->dep_prev, stream, si);
788 if (rv != 0) {
789 return rv;
790 }
791 }
792 }
793
794 assert(stream->dep_prev);
795
796 dep_prev = stream->dep_prev;
797
798 dep_prev->sum_dep_weight += sum_dep_weight_delta;
799
800 if (stream->queued) {
801 stream_obq_remove(stream);
802 }
803
804 if (stream->sib_prev) {
805 unlink_sib(stream);
806 } else {
807 unlink_dep(stream);
808 }
809
810 stream->sum_dep_weight = 0;
811
812 stream->dep_prev = NULL;
813 stream->dep_next = NULL;
814 stream->sib_prev = NULL;
815 stream->sib_next = NULL;
816
817 validate_tree(dep_prev);
818
819 return 0;
820 }
821
nghttp2_stream_dep_insert_subtree(nghttp2_stream * dep_stream,nghttp2_stream * stream)822 int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream,
823 nghttp2_stream *stream) {
824 nghttp2_stream *last_sib;
825 nghttp2_stream *dep_next;
826 nghttp2_stream *si;
827 int rv;
828
829 DEBUGF("stream: dep_insert_subtree dep_stream(%p)=%d stream(%p)=%d\n",
830 dep_stream, dep_stream->stream_id, stream, stream->stream_id);
831
832 stream->sum_dep_weight += dep_stream->sum_dep_weight;
833 dep_stream->sum_dep_weight = stream->weight;
834
835 if (dep_stream->dep_next) {
836 dep_next = dep_stream->dep_next;
837
838 link_dep(dep_stream, stream);
839
840 if (stream->dep_next) {
841 last_sib = stream_last_sib(stream->dep_next);
842
843 link_sib(last_sib, dep_next);
844 } else {
845 link_dep(stream, dep_next);
846 }
847
848 for (si = dep_next; si; si = si->sib_next) {
849 si->dep_prev = stream;
850 if (si->queued) {
851 rv = stream_obq_move(stream, dep_stream, si);
852 if (rv != 0) {
853 return rv;
854 }
855 }
856 }
857 } else {
858 link_dep(dep_stream, stream);
859 }
860
861 if (stream_subtree_active(stream)) {
862 rv = stream_obq_push(dep_stream, stream);
863 if (rv != 0) {
864 return rv;
865 }
866 }
867
868 validate_tree(dep_stream);
869
870 return 0;
871 }
872
nghttp2_stream_dep_add_subtree(nghttp2_stream * dep_stream,nghttp2_stream * stream)873 int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream,
874 nghttp2_stream *stream) {
875 int rv;
876
877 DEBUGF("stream: dep_add_subtree dep_stream(%p)=%d stream(%p)=%d\n",
878 dep_stream, dep_stream->stream_id, stream, stream->stream_id);
879
880 dep_stream->sum_dep_weight += stream->weight;
881
882 if (dep_stream->dep_next) {
883 insert_link_dep(dep_stream, stream);
884 } else {
885 link_dep(dep_stream, stream);
886 }
887
888 if (stream_subtree_active(stream)) {
889 rv = stream_obq_push(dep_stream, stream);
890 if (rv != 0) {
891 return rv;
892 }
893 }
894
895 validate_tree(dep_stream);
896
897 return 0;
898 }
899
nghttp2_stream_dep_remove_subtree(nghttp2_stream * stream)900 void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream) {
901 nghttp2_stream *next, *dep_prev;
902
903 DEBUGF("stream: dep_remove_subtree stream(%p)=%d\n", stream,
904 stream->stream_id);
905
906 assert(stream->dep_prev);
907
908 dep_prev = stream->dep_prev;
909
910 if (stream->sib_prev) {
911 link_sib(stream->sib_prev, stream->sib_next);
912 } else {
913 next = stream->sib_next;
914
915 link_dep(dep_prev, next);
916
917 if (next) {
918 next->sib_prev = NULL;
919 }
920 }
921
922 dep_prev->sum_dep_weight -= stream->weight;
923
924 if (stream->queued) {
925 stream_obq_remove(stream);
926 }
927
928 validate_tree(dep_prev);
929
930 stream->sib_prev = NULL;
931 stream->sib_next = NULL;
932 stream->dep_prev = NULL;
933 }
934
nghttp2_stream_in_dep_tree(nghttp2_stream * stream)935 int nghttp2_stream_in_dep_tree(nghttp2_stream *stream) {
936 return stream->dep_prev || stream->dep_next || stream->sib_prev ||
937 stream->sib_next;
938 }
939
940 nghttp2_outbound_item *
nghttp2_stream_next_outbound_item(nghttp2_stream * stream)941 nghttp2_stream_next_outbound_item(nghttp2_stream *stream) {
942 nghttp2_pq_entry *ent;
943 nghttp2_stream *si;
944
945 for (;;) {
946 if (stream_active(stream)) {
947 /* Update ascendant's descendant_last_cycle here, so that we can
948 assure that new stream is scheduled based on it. */
949 for (si = stream; si->dep_prev; si = si->dep_prev) {
950 si->dep_prev->descendant_last_cycle = si->cycle;
951 }
952 return stream->item;
953 }
954 ent = nghttp2_pq_top(&stream->obq);
955 if (!ent) {
956 return NULL;
957 }
958 stream = nghttp2_struct_of(ent, nghttp2_stream, pq_entry);
959 }
960 }
961
nghttp2_stream_get_state(nghttp2_stream * stream)962 nghttp2_stream_proto_state nghttp2_stream_get_state(nghttp2_stream *stream) {
963 if (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) {
964 return NGHTTP2_STREAM_STATE_CLOSED;
965 }
966
967 if (stream->flags & NGHTTP2_STREAM_FLAG_PUSH) {
968 if (stream->shut_flags & NGHTTP2_SHUT_RD) {
969 return NGHTTP2_STREAM_STATE_RESERVED_LOCAL;
970 }
971
972 if (stream->shut_flags & NGHTTP2_SHUT_WR) {
973 return NGHTTP2_STREAM_STATE_RESERVED_REMOTE;
974 }
975 }
976
977 if (stream->shut_flags & NGHTTP2_SHUT_RD) {
978 return NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE;
979 }
980
981 if (stream->shut_flags & NGHTTP2_SHUT_WR) {
982 return NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL;
983 }
984
985 if (stream->state == NGHTTP2_STREAM_IDLE) {
986 return NGHTTP2_STREAM_STATE_IDLE;
987 }
988
989 return NGHTTP2_STREAM_STATE_OPEN;
990 }
991
nghttp2_stream_get_parent(nghttp2_stream * stream)992 nghttp2_stream *nghttp2_stream_get_parent(nghttp2_stream *stream) {
993 return stream->dep_prev;
994 }
995
nghttp2_stream_get_next_sibling(nghttp2_stream * stream)996 nghttp2_stream *nghttp2_stream_get_next_sibling(nghttp2_stream *stream) {
997 return stream->sib_next;
998 }
999
nghttp2_stream_get_previous_sibling(nghttp2_stream * stream)1000 nghttp2_stream *nghttp2_stream_get_previous_sibling(nghttp2_stream *stream) {
1001 return stream->sib_prev;
1002 }
1003
nghttp2_stream_get_first_child(nghttp2_stream * stream)1004 nghttp2_stream *nghttp2_stream_get_first_child(nghttp2_stream *stream) {
1005 return stream->dep_next;
1006 }
1007
nghttp2_stream_get_weight(nghttp2_stream * stream)1008 int32_t nghttp2_stream_get_weight(nghttp2_stream *stream) {
1009 return stream->weight;
1010 }
1011
nghttp2_stream_get_sum_dependency_weight(nghttp2_stream * stream)1012 int32_t nghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream) {
1013 return stream->sum_dep_weight;
1014 }
1015
nghttp2_stream_get_stream_id(nghttp2_stream * stream)1016 int32_t nghttp2_stream_get_stream_id(nghttp2_stream *stream) {
1017 return stream->stream_id;
1018 }
1019