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