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