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