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