• 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 int stream_update_dep_on_detach_item(nghttp2_stream *stream) {
469   if (nghttp2_pq_empty(&stream->obq)) {
470     stream_obq_remove(stream);
471   }
472 
473   validate_tree(stream);
474 
475   return 0;
476 }
477 
nghttp2_stream_attach_item(nghttp2_stream * stream,nghttp2_outbound_item * item)478 int nghttp2_stream_attach_item(nghttp2_stream *stream,
479                                nghttp2_outbound_item *item) {
480   int rv;
481 
482   assert((stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0);
483   assert(stream->item == NULL);
484 
485   DEBUGF("stream: stream=%d attach item=%p\n", stream->stream_id, item);
486 
487   stream->item = item;
488 
489   if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
490     return 0;
491   }
492 
493   rv = stream_update_dep_on_attach_item(stream);
494   if (rv != 0) {
495     /* This may relave stream->queued == 1, but stream->item == NULL.
496        But only consequence of this error is fatal one, and session
497        destruction.  In that execution path, these inconsistency does
498        not matter. */
499     stream->item = NULL;
500     return rv;
501   }
502 
503   return 0;
504 }
505 
nghttp2_stream_detach_item(nghttp2_stream * stream)506 int nghttp2_stream_detach_item(nghttp2_stream *stream) {
507   DEBUGF("stream: stream=%d detach item=%p\n", stream->stream_id, stream->item);
508 
509   stream->item = NULL;
510   stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_DEFERRED_ALL);
511 
512   if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
513     return 0;
514   }
515 
516   return stream_update_dep_on_detach_item(stream);
517 }
518 
nghttp2_stream_defer_item(nghttp2_stream * stream,uint8_t flags)519 int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags) {
520   assert(stream->item);
521 
522   DEBUGF("stream: stream=%d defer item=%p cause=%02x\n", stream->stream_id,
523          stream->item, flags);
524 
525   stream->flags |= flags;
526 
527   if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
528     return 0;
529   }
530 
531   return stream_update_dep_on_detach_item(stream);
532 }
533 
nghttp2_stream_resume_deferred_item(nghttp2_stream * stream,uint8_t flags)534 int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags) {
535   assert(stream->item);
536 
537   DEBUGF("stream: stream=%d resume item=%p flags=%02x\n", stream->stream_id,
538          stream->item, flags);
539 
540   stream->flags = (uint8_t)(stream->flags & ~flags);
541 
542   if (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) {
543     return 0;
544   }
545 
546   if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
547     return 0;
548   }
549 
550   return stream_update_dep_on_attach_item(stream);
551 }
552 
nghttp2_stream_check_deferred_item(nghttp2_stream * stream)553 int nghttp2_stream_check_deferred_item(nghttp2_stream *stream) {
554   return stream->item && (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL);
555 }
556 
nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream * stream)557 int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream) {
558   return stream->item &&
559          (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
560 }
561 
update_initial_window_size(int32_t * window_size_ptr,int32_t new_initial_window_size,int32_t old_initial_window_size)562 static int update_initial_window_size(int32_t *window_size_ptr,
563                                       int32_t new_initial_window_size,
564                                       int32_t old_initial_window_size) {
565   int64_t new_window_size = (int64_t)(*window_size_ptr) +
566                             new_initial_window_size - old_initial_window_size;
567   if (INT32_MIN > new_window_size ||
568       new_window_size > NGHTTP2_MAX_WINDOW_SIZE) {
569     return -1;
570   }
571   *window_size_ptr = (int32_t)new_window_size;
572   return 0;
573 }
574 
nghttp2_stream_update_remote_initial_window_size(nghttp2_stream * stream,int32_t new_initial_window_size,int32_t old_initial_window_size)575 int nghttp2_stream_update_remote_initial_window_size(
576     nghttp2_stream *stream, int32_t new_initial_window_size,
577     int32_t old_initial_window_size) {
578   return update_initial_window_size(&stream->remote_window_size,
579                                     new_initial_window_size,
580                                     old_initial_window_size);
581 }
582 
nghttp2_stream_update_local_initial_window_size(nghttp2_stream * stream,int32_t new_initial_window_size,int32_t old_initial_window_size)583 int nghttp2_stream_update_local_initial_window_size(
584     nghttp2_stream *stream, int32_t new_initial_window_size,
585     int32_t old_initial_window_size) {
586   return update_initial_window_size(&stream->local_window_size,
587                                     new_initial_window_size,
588                                     old_initial_window_size);
589 }
590 
nghttp2_stream_promise_fulfilled(nghttp2_stream * stream)591 void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream) {
592   stream->state = NGHTTP2_STREAM_OPENED;
593   stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_PUSH);
594 }
595 
nghttp2_stream_dep_find_ancestor(nghttp2_stream * stream,nghttp2_stream * target)596 int nghttp2_stream_dep_find_ancestor(nghttp2_stream *stream,
597                                      nghttp2_stream *target) {
598   for (; stream; stream = stream->dep_prev) {
599     if (stream == target) {
600       return 1;
601     }
602   }
603   return 0;
604 }
605 
nghttp2_stream_dep_insert(nghttp2_stream * dep_stream,nghttp2_stream * stream)606 int nghttp2_stream_dep_insert(nghttp2_stream *dep_stream,
607                               nghttp2_stream *stream) {
608   nghttp2_stream *si;
609   int rv;
610 
611   DEBUGF("stream: dep_insert dep_stream(%p)=%d, stream(%p)=%d\n", dep_stream,
612          dep_stream->stream_id, stream, stream->stream_id);
613 
614   stream->sum_dep_weight = dep_stream->sum_dep_weight;
615   dep_stream->sum_dep_weight = stream->weight;
616 
617   if (dep_stream->dep_next) {
618     for (si = dep_stream->dep_next; si; si = si->sib_next) {
619       si->dep_prev = stream;
620       if (si->queued) {
621         rv = stream_obq_move(stream, dep_stream, si);
622         if (rv != 0) {
623           return rv;
624         }
625       }
626     }
627 
628     if (stream_subtree_active(stream)) {
629       rv = stream_obq_push(dep_stream, stream);
630       if (rv != 0) {
631         return rv;
632       }
633     }
634 
635     stream->dep_next = dep_stream->dep_next;
636   }
637 
638   dep_stream->dep_next = stream;
639   stream->dep_prev = dep_stream;
640 
641   validate_tree(stream);
642 
643   return 0;
644 }
645 
set_dep_prev(nghttp2_stream * stream,nghttp2_stream * dep)646 static void set_dep_prev(nghttp2_stream *stream, nghttp2_stream *dep) {
647   for (; stream; stream = stream->sib_next) {
648     stream->dep_prev = dep;
649   }
650 }
651 
link_dep(nghttp2_stream * dep_stream,nghttp2_stream * stream)652 static void link_dep(nghttp2_stream *dep_stream, nghttp2_stream *stream) {
653   dep_stream->dep_next = stream;
654   if (stream) {
655     stream->dep_prev = dep_stream;
656   }
657 }
658 
link_sib(nghttp2_stream * a,nghttp2_stream * b)659 static void link_sib(nghttp2_stream *a, nghttp2_stream *b) {
660   a->sib_next = b;
661   if (b) {
662     b->sib_prev = a;
663   }
664 }
665 
insert_link_dep(nghttp2_stream * dep_stream,nghttp2_stream * stream)666 static void insert_link_dep(nghttp2_stream *dep_stream,
667                             nghttp2_stream *stream) {
668   nghttp2_stream *sib_next;
669 
670   assert(stream->sib_prev == NULL);
671 
672   sib_next = dep_stream->dep_next;
673 
674   link_sib(stream, sib_next);
675 
676   link_dep(dep_stream, stream);
677 }
678 
unlink_sib(nghttp2_stream * stream)679 static void unlink_sib(nghttp2_stream *stream) {
680   nghttp2_stream *prev, *next, *dep_next;
681 
682   prev = stream->sib_prev;
683   dep_next = stream->dep_next;
684 
685   assert(prev);
686 
687   if (dep_next) {
688     /*
689      *  prev--stream(--sib_next--...)
690      *         |
691      *        dep_next
692      */
693 
694     link_sib(prev, dep_next);
695 
696     set_dep_prev(dep_next, stream->dep_prev);
697 
698     if (stream->sib_next) {
699       link_sib(stream_last_sib(dep_next), stream->sib_next);
700     }
701   } else {
702     /*
703      *  prev--stream(--sib_next--...)
704      */
705     next = stream->sib_next;
706 
707     prev->sib_next = next;
708 
709     if (next) {
710       next->sib_prev = prev;
711     }
712   }
713 }
714 
unlink_dep(nghttp2_stream * stream)715 static void unlink_dep(nghttp2_stream *stream) {
716   nghttp2_stream *prev, *next, *dep_next;
717 
718   prev = stream->dep_prev;
719   dep_next = stream->dep_next;
720 
721   assert(prev);
722 
723   if (dep_next) {
724     /*
725      * prev
726      *   |
727      * stream(--sib_next--...)
728      *   |
729      * dep_next
730      */
731     link_dep(prev, dep_next);
732 
733     set_dep_prev(dep_next, stream->dep_prev);
734 
735     if (stream->sib_next) {
736       link_sib(stream_last_sib(dep_next), stream->sib_next);
737     }
738 
739   } else if (stream->sib_next) {
740     /*
741      * prev
742      *   |
743      * stream--sib_next
744      */
745     next = stream->sib_next;
746 
747     next->sib_prev = NULL;
748 
749     link_dep(prev, next);
750   } else {
751     prev->dep_next = NULL;
752   }
753 }
754 
nghttp2_stream_dep_add(nghttp2_stream * dep_stream,nghttp2_stream * stream)755 void nghttp2_stream_dep_add(nghttp2_stream *dep_stream,
756                             nghttp2_stream *stream) {
757   DEBUGF("stream: dep_add dep_stream(%p)=%d, stream(%p)=%d\n", dep_stream,
758          dep_stream->stream_id, stream, stream->stream_id);
759 
760   dep_stream->sum_dep_weight += stream->weight;
761 
762   if (dep_stream->dep_next == NULL) {
763     link_dep(dep_stream, stream);
764   } else {
765     insert_link_dep(dep_stream, stream);
766   }
767 
768   validate_tree(stream);
769 }
770 
nghttp2_stream_dep_remove(nghttp2_stream * stream)771 int nghttp2_stream_dep_remove(nghttp2_stream *stream) {
772   nghttp2_stream *dep_prev, *si;
773   int32_t sum_dep_weight_delta;
774   int rv;
775 
776   DEBUGF("stream: dep_remove stream(%p)=%d\n", stream, stream->stream_id);
777 
778   /* Distribute weight of |stream| to direct descendants */
779   sum_dep_weight_delta = -stream->weight;
780 
781   for (si = stream->dep_next; si; si = si->sib_next) {
782     si->weight = nghttp2_stream_dep_distributed_weight(stream, si->weight);
783 
784     sum_dep_weight_delta += si->weight;
785 
786     if (si->queued) {
787       rv = stream_obq_move(stream->dep_prev, stream, si);
788       if (rv != 0) {
789         return rv;
790       }
791     }
792   }
793 
794   assert(stream->dep_prev);
795 
796   dep_prev = stream->dep_prev;
797 
798   dep_prev->sum_dep_weight += sum_dep_weight_delta;
799 
800   if (stream->queued) {
801     stream_obq_remove(stream);
802   }
803 
804   if (stream->sib_prev) {
805     unlink_sib(stream);
806   } else {
807     unlink_dep(stream);
808   }
809 
810   stream->sum_dep_weight = 0;
811 
812   stream->dep_prev = NULL;
813   stream->dep_next = NULL;
814   stream->sib_prev = NULL;
815   stream->sib_next = NULL;
816 
817   validate_tree(dep_prev);
818 
819   return 0;
820 }
821 
nghttp2_stream_dep_insert_subtree(nghttp2_stream * dep_stream,nghttp2_stream * stream)822 int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream,
823                                       nghttp2_stream *stream) {
824   nghttp2_stream *last_sib;
825   nghttp2_stream *dep_next;
826   nghttp2_stream *si;
827   int rv;
828 
829   DEBUGF("stream: dep_insert_subtree dep_stream(%p)=%d stream(%p)=%d\n",
830          dep_stream, dep_stream->stream_id, stream, stream->stream_id);
831 
832   stream->sum_dep_weight += dep_stream->sum_dep_weight;
833   dep_stream->sum_dep_weight = stream->weight;
834 
835   if (dep_stream->dep_next) {
836     dep_next = dep_stream->dep_next;
837 
838     link_dep(dep_stream, stream);
839 
840     if (stream->dep_next) {
841       last_sib = stream_last_sib(stream->dep_next);
842 
843       link_sib(last_sib, dep_next);
844     } else {
845       link_dep(stream, dep_next);
846     }
847 
848     for (si = dep_next; si; si = si->sib_next) {
849       si->dep_prev = stream;
850       if (si->queued) {
851         rv = stream_obq_move(stream, dep_stream, si);
852         if (rv != 0) {
853           return rv;
854         }
855       }
856     }
857   } else {
858     link_dep(dep_stream, stream);
859   }
860 
861   if (stream_subtree_active(stream)) {
862     rv = stream_obq_push(dep_stream, stream);
863     if (rv != 0) {
864       return rv;
865     }
866   }
867 
868   validate_tree(dep_stream);
869 
870   return 0;
871 }
872 
nghttp2_stream_dep_add_subtree(nghttp2_stream * dep_stream,nghttp2_stream * stream)873 int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream,
874                                    nghttp2_stream *stream) {
875   int rv;
876 
877   DEBUGF("stream: dep_add_subtree dep_stream(%p)=%d stream(%p)=%d\n",
878          dep_stream, dep_stream->stream_id, stream, stream->stream_id);
879 
880   dep_stream->sum_dep_weight += stream->weight;
881 
882   if (dep_stream->dep_next) {
883     insert_link_dep(dep_stream, stream);
884   } else {
885     link_dep(dep_stream, stream);
886   }
887 
888   if (stream_subtree_active(stream)) {
889     rv = stream_obq_push(dep_stream, stream);
890     if (rv != 0) {
891       return rv;
892     }
893   }
894 
895   validate_tree(dep_stream);
896 
897   return 0;
898 }
899 
nghttp2_stream_dep_remove_subtree(nghttp2_stream * stream)900 void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream) {
901   nghttp2_stream *next, *dep_prev;
902 
903   DEBUGF("stream: dep_remove_subtree stream(%p)=%d\n", stream,
904          stream->stream_id);
905 
906   assert(stream->dep_prev);
907 
908   dep_prev = stream->dep_prev;
909 
910   if (stream->sib_prev) {
911     link_sib(stream->sib_prev, stream->sib_next);
912   } else {
913     next = stream->sib_next;
914 
915     link_dep(dep_prev, next);
916 
917     if (next) {
918       next->sib_prev = NULL;
919     }
920   }
921 
922   dep_prev->sum_dep_weight -= stream->weight;
923 
924   if (stream->queued) {
925     stream_obq_remove(stream);
926   }
927 
928   validate_tree(dep_prev);
929 
930   stream->sib_prev = NULL;
931   stream->sib_next = NULL;
932   stream->dep_prev = NULL;
933 }
934 
nghttp2_stream_in_dep_tree(nghttp2_stream * stream)935 int nghttp2_stream_in_dep_tree(nghttp2_stream *stream) {
936   return stream->dep_prev || stream->dep_next || stream->sib_prev ||
937          stream->sib_next;
938 }
939 
940 nghttp2_outbound_item *
nghttp2_stream_next_outbound_item(nghttp2_stream * stream)941 nghttp2_stream_next_outbound_item(nghttp2_stream *stream) {
942   nghttp2_pq_entry *ent;
943   nghttp2_stream *si;
944 
945   for (;;) {
946     if (stream_active(stream)) {
947       /* Update ascendant's descendant_last_cycle here, so that we can
948          assure that new stream is scheduled based on it. */
949       for (si = stream; si->dep_prev; si = si->dep_prev) {
950         si->dep_prev->descendant_last_cycle = si->cycle;
951       }
952       return stream->item;
953     }
954     ent = nghttp2_pq_top(&stream->obq);
955     if (!ent) {
956       return NULL;
957     }
958     stream = nghttp2_struct_of(ent, nghttp2_stream, pq_entry);
959   }
960 }
961 
nghttp2_stream_get_state(nghttp2_stream * stream)962 nghttp2_stream_proto_state nghttp2_stream_get_state(nghttp2_stream *stream) {
963   if (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) {
964     return NGHTTP2_STREAM_STATE_CLOSED;
965   }
966 
967   if (stream->flags & NGHTTP2_STREAM_FLAG_PUSH) {
968     if (stream->shut_flags & NGHTTP2_SHUT_RD) {
969       return NGHTTP2_STREAM_STATE_RESERVED_LOCAL;
970     }
971 
972     if (stream->shut_flags & NGHTTP2_SHUT_WR) {
973       return NGHTTP2_STREAM_STATE_RESERVED_REMOTE;
974     }
975   }
976 
977   if (stream->shut_flags & NGHTTP2_SHUT_RD) {
978     return NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE;
979   }
980 
981   if (stream->shut_flags & NGHTTP2_SHUT_WR) {
982     return NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL;
983   }
984 
985   if (stream->state == NGHTTP2_STREAM_IDLE) {
986     return NGHTTP2_STREAM_STATE_IDLE;
987   }
988 
989   return NGHTTP2_STREAM_STATE_OPEN;
990 }
991 
nghttp2_stream_get_parent(nghttp2_stream * stream)992 nghttp2_stream *nghttp2_stream_get_parent(nghttp2_stream *stream) {
993   return stream->dep_prev;
994 }
995 
nghttp2_stream_get_next_sibling(nghttp2_stream * stream)996 nghttp2_stream *nghttp2_stream_get_next_sibling(nghttp2_stream *stream) {
997   return stream->sib_next;
998 }
999 
nghttp2_stream_get_previous_sibling(nghttp2_stream * stream)1000 nghttp2_stream *nghttp2_stream_get_previous_sibling(nghttp2_stream *stream) {
1001   return stream->sib_prev;
1002 }
1003 
nghttp2_stream_get_first_child(nghttp2_stream * stream)1004 nghttp2_stream *nghttp2_stream_get_first_child(nghttp2_stream *stream) {
1005   return stream->dep_next;
1006 }
1007 
nghttp2_stream_get_weight(nghttp2_stream * stream)1008 int32_t nghttp2_stream_get_weight(nghttp2_stream *stream) {
1009   return stream->weight;
1010 }
1011 
nghttp2_stream_get_sum_dependency_weight(nghttp2_stream * stream)1012 int32_t nghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream) {
1013   return stream->sum_dep_weight;
1014 }
1015 
nghttp2_stream_get_stream_id(nghttp2_stream * stream)1016 int32_t nghttp2_stream_get_stream_id(nghttp2_stream *stream) {
1017   return stream->stream_id;
1018 }
1019