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