Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Utilities/cmnghttp2/lib/nghttp2_stream.c
Line
Count
Source
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
0
  ((uint64_t)NGHTTP2_MAX_FRAME_SIZE_MAX * 256 + 255)
46
47
0
static int stream_less(const void *lhsx, const void *rhsx) {
48
0
  const nghttp2_stream *lhs, *rhs;
49
50
0
  lhs = nghttp2_struct_of(lhsx, nghttp2_stream, pq_entry);
51
0
  rhs = nghttp2_struct_of(rhsx, nghttp2_stream, pq_entry);
52
53
0
  if (lhs->cycle == rhs->cycle) {
54
0
    return lhs->seq < rhs->seq;
55
0
  }
56
57
0
  return rhs->cycle - lhs->cycle <= NGHTTP2_MAX_CYCLE_DISTANCE;
58
0
}
59
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
0
                         void *stream_user_data, nghttp2_mem *mem) {
65
0
  nghttp2_pq_init(&stream->obq, stream_less, mem);
66
67
0
  stream->stream_id = stream_id;
68
0
  stream->flags = flags;
69
0
  stream->state = initial_state;
70
0
  stream->shut_flags = NGHTTP2_SHUT_NONE;
71
0
  stream->stream_user_data = stream_user_data;
72
0
  stream->item = NULL;
73
0
  stream->remote_window_size = remote_initial_window_size;
74
0
  stream->local_window_size = local_initial_window_size;
75
0
  stream->recv_window_size = 0;
76
0
  stream->consumed_size = 0;
77
0
  stream->recv_reduction = 0;
78
0
  stream->window_update_queued = 0;
79
80
0
  stream->dep_prev = NULL;
81
0
  stream->dep_next = NULL;
82
0
  stream->sib_prev = NULL;
83
0
  stream->sib_next = NULL;
84
85
0
  stream->closed_prev = NULL;
86
0
  stream->closed_next = NULL;
87
88
0
  stream->weight = weight;
89
0
  stream->sum_dep_weight = 0;
90
91
0
  stream->http_flags = NGHTTP2_HTTP_FLAG_NONE;
92
0
  stream->content_length = -1;
93
0
  stream->recv_content_length = 0;
94
0
  stream->status_code = -1;
95
96
0
  stream->queued = 0;
97
0
  stream->descendant_last_cycle = 0;
98
0
  stream->cycle = 0;
99
0
  stream->pending_penalty = 0;
100
0
  stream->descendant_next_seq = 0;
101
0
  stream->seq = 0;
102
0
  stream->last_writelen = 0;
103
104
0
  stream->extpri = stream->http_extpri = NGHTTP2_EXTPRI_DEFAULT_URGENCY;
105
0
}
106
107
0
void nghttp2_stream_free(nghttp2_stream *stream) {
108
0
  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
0
}
113
114
0
void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag) {
115
0
  stream->shut_flags = (uint8_t)(stream->shut_flags | flag);
116
0
}
117
118
/*
119
 * Returns nonzero if |stream| is active.  This function does not take
120
 * into account its descendants.
121
 */
122
0
static int stream_active(nghttp2_stream *stream) {
123
0
  return stream->item &&
124
0
         (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0;
125
0
}
126
127
/*
128
 * Returns nonzero if |stream| or one of its descendants is active
129
 */
130
0
static int stream_subtree_active(nghttp2_stream *stream) {
131
0
  return stream_active(stream) || !nghttp2_pq_empty(&stream->obq);
132
0
}
133
134
/*
135
 * Returns next cycle for |stream|.
136
 */
137
0
static void stream_next_cycle(nghttp2_stream *stream, uint64_t last_cycle) {
138
0
  uint64_t penalty;
139
140
0
  penalty = (uint64_t)stream->last_writelen * NGHTTP2_MAX_WEIGHT +
141
0
            stream->pending_penalty;
142
143
0
  stream->cycle = last_cycle + penalty / (uint32_t)stream->weight;
144
0
  stream->pending_penalty = (uint32_t)(penalty % (uint32_t)stream->weight);
145
0
}
146
147
0
static int stream_obq_push(nghttp2_stream *dep_stream, nghttp2_stream *stream) {
148
0
  int rv;
149
150
0
  for (; dep_stream && !stream->queued;
151
0
       stream = dep_stream, dep_stream = dep_stream->dep_prev) {
152
0
    stream_next_cycle(stream, dep_stream->descendant_last_cycle);
153
0
    stream->seq = dep_stream->descendant_next_seq++;
154
155
0
    DEBUGF("stream: stream=%d obq push cycle=%lu\n", stream->stream_id,
156
0
           stream->cycle);
157
158
0
    DEBUGF("stream: push stream %d to stream %d\n", stream->stream_id,
159
0
           dep_stream->stream_id);
160
161
0
    rv = nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry);
162
0
    if (rv != 0) {
163
0
      return rv;
164
0
    }
165
0
    stream->queued = 1;
166
0
  }
167
168
0
  return 0;
169
0
}
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
 */
176
0
static void stream_obq_remove(nghttp2_stream *stream) {
177
0
  nghttp2_stream *dep_stream;
178
179
0
  dep_stream = stream->dep_prev;
180
181
0
  if (!stream->queued) {
182
0
    return;
183
0
  }
184
185
0
  for (; dep_stream; stream = dep_stream, dep_stream = dep_stream->dep_prev) {
186
0
    DEBUGF("stream: remove stream %d from stream %d\n", stream->stream_id,
187
0
           dep_stream->stream_id);
188
189
0
    nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry);
190
191
0
    assert(stream->queued);
192
193
0
    stream->queued = 0;
194
0
    stream->cycle = 0;
195
0
    stream->pending_penalty = 0;
196
0
    stream->descendant_last_cycle = 0;
197
0
    stream->last_writelen = 0;
198
199
0
    if (stream_subtree_active(dep_stream)) {
200
0
      return;
201
0
    }
202
0
  }
203
0
}
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
 */
211
static int stream_obq_move(nghttp2_stream *dest, nghttp2_stream *src,
212
0
                           nghttp2_stream *stream) {
213
0
  if (!stream->queued) {
214
0
    return 0;
215
0
  }
216
217
0
  DEBUGF("stream: remove stream %d from stream %d (move)\n", stream->stream_id,
218
0
         src->stream_id);
219
220
0
  nghttp2_pq_remove(&src->obq, &stream->pq_entry);
221
0
  stream->queued = 0;
222
223
0
  return stream_obq_push(dest, stream);
224
0
}
225
226
0
void nghttp2_stream_reschedule(nghttp2_stream *stream) {
227
0
  nghttp2_stream *dep_stream;
228
229
0
  assert(stream->queued);
230
231
0
  dep_stream = stream->dep_prev;
232
233
0
  for (; dep_stream; stream = dep_stream, dep_stream = dep_stream->dep_prev) {
234
0
    nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry);
235
236
0
    stream_next_cycle(stream, dep_stream->descendant_last_cycle);
237
0
    stream->seq = dep_stream->descendant_next_seq++;
238
239
0
    nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry);
240
241
0
    DEBUGF("stream: stream=%d obq resched cycle=%lu\n", stream->stream_id,
242
0
           stream->cycle);
243
244
0
    dep_stream->last_writelen = stream->last_writelen;
245
0
  }
246
0
}
247
248
0
void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight) {
249
0
  nghttp2_stream *dep_stream;
250
0
  uint64_t last_cycle;
251
0
  int32_t old_weight;
252
0
  uint64_t wlen_penalty;
253
254
0
  if (stream->weight == weight) {
255
0
    return;
256
0
  }
257
258
0
  old_weight = stream->weight;
259
0
  stream->weight = weight;
260
261
0
  dep_stream = stream->dep_prev;
262
263
0
  if (!dep_stream) {
264
0
    return;
265
0
  }
266
267
0
  dep_stream->sum_dep_weight += weight - old_weight;
268
269
0
  if (!stream->queued) {
270
0
    return;
271
0
  }
272
273
0
  nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry);
274
275
0
  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
0
  stream->pending_penalty =
280
0
      (uint32_t)((stream->pending_penalty + (uint32_t)old_weight -
281
0
                  (wlen_penalty % (uint32_t)old_weight)) %
282
0
                 (uint32_t)old_weight);
283
284
0
  last_cycle = stream->cycle -
285
0
               (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
0
  stream_next_cycle(stream, last_cycle);
290
291
0
  if (dep_stream->descendant_last_cycle - stream->cycle <=
292
0
      NGHTTP2_MAX_CYCLE_DISTANCE) {
293
0
    stream->cycle = dep_stream->descendant_last_cycle;
294
0
  }
295
296
  /* Continue to use same stream->seq */
297
298
0
  nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry);
299
300
0
  DEBUGF("stream: stream=%d obq resched cycle=%lu\n", stream->stream_id,
301
0
         stream->cycle);
302
0
}
303
304
0
static nghttp2_stream *stream_last_sib(nghttp2_stream *stream) {
305
0
  for (; stream->sib_next; stream = stream->sib_next)
306
0
    ;
307
308
0
  return stream;
309
0
}
310
311
int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream,
312
0
                                              int32_t weight) {
313
0
  weight = stream->weight * weight / stream->sum_dep_weight;
314
315
0
  return nghttp2_max(1, weight);
316
0
}
317
318
#ifdef STREAM_DEP_DEBUG
319
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
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
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
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
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 */
453
0
static void validate_tree(nghttp2_stream *stream) { (void)stream; }
454
#endif /* !STREAM_DEP_DEBUG*/
455
456
0
static int stream_update_dep_on_attach_item(nghttp2_stream *stream) {
457
0
  int rv;
458
459
0
  rv = stream_obq_push(stream->dep_prev, stream);
460
0
  if (rv != 0) {
461
0
    return rv;
462
0
  }
463
464
0
  validate_tree(stream);
465
0
  return 0;
466
0
}
467
468
0
static int stream_update_dep_on_detach_item(nghttp2_stream *stream) {
469
0
  if (nghttp2_pq_empty(&stream->obq)) {
470
0
    stream_obq_remove(stream);
471
0
  }
472
473
0
  validate_tree(stream);
474
475
0
  return 0;
476
0
}
477
478
int nghttp2_stream_attach_item(nghttp2_stream *stream,
479
0
                               nghttp2_outbound_item *item) {
480
0
  int rv;
481
482
0
  assert((stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0);
483
0
  assert(stream->item == NULL);
484
485
0
  DEBUGF("stream: stream=%d attach item=%p\n", stream->stream_id, item);
486
487
0
  stream->item = item;
488
489
0
  if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
490
0
    return 0;
491
0
  }
492
493
0
  rv = stream_update_dep_on_attach_item(stream);
494
0
  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
0
    stream->item = NULL;
500
0
    return rv;
501
0
  }
502
503
0
  return 0;
504
0
}
505
506
0
int nghttp2_stream_detach_item(nghttp2_stream *stream) {
507
0
  DEBUGF("stream: stream=%d detach item=%p\n", stream->stream_id, stream->item);
508
509
0
  stream->item = NULL;
510
0
  stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_DEFERRED_ALL);
511
512
0
  if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
513
0
    return 0;
514
0
  }
515
516
0
  return stream_update_dep_on_detach_item(stream);
517
0
}
518
519
0
int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags) {
520
0
  assert(stream->item);
521
522
0
  DEBUGF("stream: stream=%d defer item=%p cause=%02x\n", stream->stream_id,
523
0
         stream->item, flags);
524
525
0
  stream->flags |= flags;
526
527
0
  if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
528
0
    return 0;
529
0
  }
530
531
0
  return stream_update_dep_on_detach_item(stream);
532
0
}
533
534
0
int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags) {
535
0
  assert(stream->item);
536
537
0
  DEBUGF("stream: stream=%d resume item=%p flags=%02x\n", stream->stream_id,
538
0
         stream->item, flags);
539
540
0
  stream->flags = (uint8_t)(stream->flags & ~flags);
541
542
0
  if (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) {
543
0
    return 0;
544
0
  }
545
546
0
  if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
547
0
    return 0;
548
0
  }
549
550
0
  return stream_update_dep_on_attach_item(stream);
551
0
}
552
553
0
int nghttp2_stream_check_deferred_item(nghttp2_stream *stream) {
554
0
  return stream->item && (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL);
555
0
}
556
557
0
int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream) {
558
0
  return stream->item &&
559
0
         (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
560
0
}
561
562
static int update_initial_window_size(int32_t *window_size_ptr,
563
                                      int32_t new_initial_window_size,
564
0
                                      int32_t old_initial_window_size) {
565
0
  int64_t new_window_size = (int64_t)(*window_size_ptr) +
566
0
                            new_initial_window_size - old_initial_window_size;
567
0
  if (INT32_MIN > new_window_size ||
568
0
      new_window_size > NGHTTP2_MAX_WINDOW_SIZE) {
569
0
    return -1;
570
0
  }
571
0
  *window_size_ptr = (int32_t)new_window_size;
572
0
  return 0;
573
0
}
574
575
int nghttp2_stream_update_remote_initial_window_size(
576
    nghttp2_stream *stream, int32_t new_initial_window_size,
577
0
    int32_t old_initial_window_size) {
578
0
  return update_initial_window_size(&stream->remote_window_size,
579
0
                                    new_initial_window_size,
580
0
                                    old_initial_window_size);
581
0
}
582
583
int nghttp2_stream_update_local_initial_window_size(
584
    nghttp2_stream *stream, int32_t new_initial_window_size,
585
0
    int32_t old_initial_window_size) {
586
0
  return update_initial_window_size(&stream->local_window_size,
587
0
                                    new_initial_window_size,
588
0
                                    old_initial_window_size);
589
0
}
590
591
0
void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream) {
592
0
  stream->state = NGHTTP2_STREAM_OPENED;
593
0
  stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_PUSH);
594
0
}
595
596
int nghttp2_stream_dep_find_ancestor(nghttp2_stream *stream,
597
0
                                     nghttp2_stream *target) {
598
0
  for (; stream; stream = stream->dep_prev) {
599
0
    if (stream == target) {
600
0
      return 1;
601
0
    }
602
0
  }
603
0
  return 0;
604
0
}
605
606
int nghttp2_stream_dep_insert(nghttp2_stream *dep_stream,
607
0
                              nghttp2_stream *stream) {
608
0
  nghttp2_stream *si;
609
0
  int rv;
610
611
0
  DEBUGF("stream: dep_insert dep_stream(%p)=%d, stream(%p)=%d\n", dep_stream,
612
0
         dep_stream->stream_id, stream, stream->stream_id);
613
614
0
  stream->sum_dep_weight = dep_stream->sum_dep_weight;
615
0
  dep_stream->sum_dep_weight = stream->weight;
616
617
0
  if (dep_stream->dep_next) {
618
0
    for (si = dep_stream->dep_next; si; si = si->sib_next) {
619
0
      si->dep_prev = stream;
620
0
      if (si->queued) {
621
0
        rv = stream_obq_move(stream, dep_stream, si);
622
0
        if (rv != 0) {
623
0
          return rv;
624
0
        }
625
0
      }
626
0
    }
627
628
0
    if (stream_subtree_active(stream)) {
629
0
      rv = stream_obq_push(dep_stream, stream);
630
0
      if (rv != 0) {
631
0
        return rv;
632
0
      }
633
0
    }
634
635
0
    stream->dep_next = dep_stream->dep_next;
636
0
  }
637
638
0
  dep_stream->dep_next = stream;
639
0
  stream->dep_prev = dep_stream;
640
641
0
  validate_tree(stream);
642
643
0
  return 0;
644
0
}
645
646
0
static void set_dep_prev(nghttp2_stream *stream, nghttp2_stream *dep) {
647
0
  for (; stream; stream = stream->sib_next) {
648
0
    stream->dep_prev = dep;
649
0
  }
650
0
}
651
652
0
static void link_dep(nghttp2_stream *dep_stream, nghttp2_stream *stream) {
653
0
  dep_stream->dep_next = stream;
654
0
  if (stream) {
655
0
    stream->dep_prev = dep_stream;
656
0
  }
657
0
}
658
659
0
static void link_sib(nghttp2_stream *a, nghttp2_stream *b) {
660
0
  a->sib_next = b;
661
0
  if (b) {
662
0
    b->sib_prev = a;
663
0
  }
664
0
}
665
666
static void insert_link_dep(nghttp2_stream *dep_stream,
667
0
                            nghttp2_stream *stream) {
668
0
  nghttp2_stream *sib_next;
669
670
0
  assert(stream->sib_prev == NULL);
671
672
0
  sib_next = dep_stream->dep_next;
673
674
0
  link_sib(stream, sib_next);
675
676
0
  link_dep(dep_stream, stream);
677
0
}
678
679
0
static void unlink_sib(nghttp2_stream *stream) {
680
0
  nghttp2_stream *prev, *next, *dep_next;
681
682
0
  prev = stream->sib_prev;
683
0
  dep_next = stream->dep_next;
684
685
0
  assert(prev);
686
687
0
  if (dep_next) {
688
    /*
689
     *  prev--stream(--sib_next--...)
690
     *         |
691
     *        dep_next
692
     */
693
694
0
    link_sib(prev, dep_next);
695
696
0
    set_dep_prev(dep_next, stream->dep_prev);
697
698
0
    if (stream->sib_next) {
699
0
      link_sib(stream_last_sib(dep_next), stream->sib_next);
700
0
    }
701
0
  } else {
702
    /*
703
     *  prev--stream(--sib_next--...)
704
     */
705
0
    next = stream->sib_next;
706
707
0
    prev->sib_next = next;
708
709
0
    if (next) {
710
0
      next->sib_prev = prev;
711
0
    }
712
0
  }
713
0
}
714
715
0
static void unlink_dep(nghttp2_stream *stream) {
716
0
  nghttp2_stream *prev, *next, *dep_next;
717
718
0
  prev = stream->dep_prev;
719
0
  dep_next = stream->dep_next;
720
721
0
  assert(prev);
722
723
0
  if (dep_next) {
724
    /*
725
     * prev
726
     *   |
727
     * stream(--sib_next--...)
728
     *   |
729
     * dep_next
730
     */
731
0
    link_dep(prev, dep_next);
732
733
0
    set_dep_prev(dep_next, stream->dep_prev);
734
735
0
    if (stream->sib_next) {
736
0
      link_sib(stream_last_sib(dep_next), stream->sib_next);
737
0
    }
738
739
0
  } else if (stream->sib_next) {
740
    /*
741
     * prev
742
     *   |
743
     * stream--sib_next
744
     */
745
0
    next = stream->sib_next;
746
747
0
    next->sib_prev = NULL;
748
749
0
    link_dep(prev, next);
750
0
  } else {
751
0
    prev->dep_next = NULL;
752
0
  }
753
0
}
754
755
void nghttp2_stream_dep_add(nghttp2_stream *dep_stream,
756
0
                            nghttp2_stream *stream) {
757
0
  DEBUGF("stream: dep_add dep_stream(%p)=%d, stream(%p)=%d\n", dep_stream,
758
0
         dep_stream->stream_id, stream, stream->stream_id);
759
760
0
  dep_stream->sum_dep_weight += stream->weight;
761
762
0
  if (dep_stream->dep_next == NULL) {
763
0
    link_dep(dep_stream, stream);
764
0
  } else {
765
0
    insert_link_dep(dep_stream, stream);
766
0
  }
767
768
0
  validate_tree(stream);
769
0
}
770
771
0
int nghttp2_stream_dep_remove(nghttp2_stream *stream) {
772
0
  nghttp2_stream *dep_prev, *si;
773
0
  int32_t sum_dep_weight_delta;
774
0
  int rv;
775
776
0
  DEBUGF("stream: dep_remove stream(%p)=%d\n", stream, stream->stream_id);
777
778
  /* Distribute weight of |stream| to direct descendants */
779
0
  sum_dep_weight_delta = -stream->weight;
780
781
0
  for (si = stream->dep_next; si; si = si->sib_next) {
782
0
    si->weight = nghttp2_stream_dep_distributed_weight(stream, si->weight);
783
784
0
    sum_dep_weight_delta += si->weight;
785
786
0
    if (si->queued) {
787
0
      rv = stream_obq_move(stream->dep_prev, stream, si);
788
0
      if (rv != 0) {
789
0
        return rv;
790
0
      }
791
0
    }
792
0
  }
793
794
0
  assert(stream->dep_prev);
795
796
0
  dep_prev = stream->dep_prev;
797
798
0
  dep_prev->sum_dep_weight += sum_dep_weight_delta;
799
800
0
  if (stream->queued) {
801
0
    stream_obq_remove(stream);
802
0
  }
803
804
0
  if (stream->sib_prev) {
805
0
    unlink_sib(stream);
806
0
  } else {
807
0
    unlink_dep(stream);
808
0
  }
809
810
0
  stream->sum_dep_weight = 0;
811
812
0
  stream->dep_prev = NULL;
813
0
  stream->dep_next = NULL;
814
0
  stream->sib_prev = NULL;
815
0
  stream->sib_next = NULL;
816
817
0
  validate_tree(dep_prev);
818
819
0
  return 0;
820
0
}
821
822
int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream,
823
0
                                      nghttp2_stream *stream) {
824
0
  nghttp2_stream *last_sib;
825
0
  nghttp2_stream *dep_next;
826
0
  nghttp2_stream *si;
827
0
  int rv;
828
829
0
  DEBUGF("stream: dep_insert_subtree dep_stream(%p)=%d stream(%p)=%d\n",
830
0
         dep_stream, dep_stream->stream_id, stream, stream->stream_id);
831
832
0
  stream->sum_dep_weight += dep_stream->sum_dep_weight;
833
0
  dep_stream->sum_dep_weight = stream->weight;
834
835
0
  if (dep_stream->dep_next) {
836
0
    dep_next = dep_stream->dep_next;
837
838
0
    link_dep(dep_stream, stream);
839
840
0
    if (stream->dep_next) {
841
0
      last_sib = stream_last_sib(stream->dep_next);
842
843
0
      link_sib(last_sib, dep_next);
844
0
    } else {
845
0
      link_dep(stream, dep_next);
846
0
    }
847
848
0
    for (si = dep_next; si; si = si->sib_next) {
849
0
      si->dep_prev = stream;
850
0
      if (si->queued) {
851
0
        rv = stream_obq_move(stream, dep_stream, si);
852
0
        if (rv != 0) {
853
0
          return rv;
854
0
        }
855
0
      }
856
0
    }
857
0
  } else {
858
0
    link_dep(dep_stream, stream);
859
0
  }
860
861
0
  if (stream_subtree_active(stream)) {
862
0
    rv = stream_obq_push(dep_stream, stream);
863
0
    if (rv != 0) {
864
0
      return rv;
865
0
    }
866
0
  }
867
868
0
  validate_tree(dep_stream);
869
870
0
  return 0;
871
0
}
872
873
int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream,
874
0
                                   nghttp2_stream *stream) {
875
0
  int rv;
876
877
0
  DEBUGF("stream: dep_add_subtree dep_stream(%p)=%d stream(%p)=%d\n",
878
0
         dep_stream, dep_stream->stream_id, stream, stream->stream_id);
879
880
0
  dep_stream->sum_dep_weight += stream->weight;
881
882
0
  if (dep_stream->dep_next) {
883
0
    insert_link_dep(dep_stream, stream);
884
0
  } else {
885
0
    link_dep(dep_stream, stream);
886
0
  }
887
888
0
  if (stream_subtree_active(stream)) {
889
0
    rv = stream_obq_push(dep_stream, stream);
890
0
    if (rv != 0) {
891
0
      return rv;
892
0
    }
893
0
  }
894
895
0
  validate_tree(dep_stream);
896
897
0
  return 0;
898
0
}
899
900
0
void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream) {
901
0
  nghttp2_stream *next, *dep_prev;
902
903
0
  DEBUGF("stream: dep_remove_subtree stream(%p)=%d\n", stream,
904
0
         stream->stream_id);
905
906
0
  assert(stream->dep_prev);
907
908
0
  dep_prev = stream->dep_prev;
909
910
0
  if (stream->sib_prev) {
911
0
    link_sib(stream->sib_prev, stream->sib_next);
912
0
  } else {
913
0
    next = stream->sib_next;
914
915
0
    link_dep(dep_prev, next);
916
917
0
    if (next) {
918
0
      next->sib_prev = NULL;
919
0
    }
920
0
  }
921
922
0
  dep_prev->sum_dep_weight -= stream->weight;
923
924
0
  if (stream->queued) {
925
0
    stream_obq_remove(stream);
926
0
  }
927
928
0
  validate_tree(dep_prev);
929
930
0
  stream->sib_prev = NULL;
931
0
  stream->sib_next = NULL;
932
0
  stream->dep_prev = NULL;
933
0
}
934
935
0
int nghttp2_stream_in_dep_tree(nghttp2_stream *stream) {
936
0
  return stream->dep_prev || stream->dep_next || stream->sib_prev ||
937
0
         stream->sib_next;
938
0
}
939
940
nghttp2_outbound_item *
941
0
nghttp2_stream_next_outbound_item(nghttp2_stream *stream) {
942
0
  nghttp2_pq_entry *ent;
943
0
  nghttp2_stream *si;
944
945
0
  for (;;) {
946
0
    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
0
      for (si = stream; si->dep_prev; si = si->dep_prev) {
950
0
        si->dep_prev->descendant_last_cycle = si->cycle;
951
0
      }
952
0
      return stream->item;
953
0
    }
954
0
    ent = nghttp2_pq_top(&stream->obq);
955
0
    if (!ent) {
956
0
      return NULL;
957
0
    }
958
0
    stream = nghttp2_struct_of(ent, nghttp2_stream, pq_entry);
959
0
  }
960
0
}
961
962
0
nghttp2_stream_proto_state nghttp2_stream_get_state(nghttp2_stream *stream) {
963
0
  if (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) {
964
0
    return NGHTTP2_STREAM_STATE_CLOSED;
965
0
  }
966
967
0
  if (stream->flags & NGHTTP2_STREAM_FLAG_PUSH) {
968
0
    if (stream->shut_flags & NGHTTP2_SHUT_RD) {
969
0
      return NGHTTP2_STREAM_STATE_RESERVED_LOCAL;
970
0
    }
971
972
0
    if (stream->shut_flags & NGHTTP2_SHUT_WR) {
973
0
      return NGHTTP2_STREAM_STATE_RESERVED_REMOTE;
974
0
    }
975
0
  }
976
977
0
  if (stream->shut_flags & NGHTTP2_SHUT_RD) {
978
0
    return NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE;
979
0
  }
980
981
0
  if (stream->shut_flags & NGHTTP2_SHUT_WR) {
982
0
    return NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL;
983
0
  }
984
985
0
  if (stream->state == NGHTTP2_STREAM_IDLE) {
986
0
    return NGHTTP2_STREAM_STATE_IDLE;
987
0
  }
988
989
0
  return NGHTTP2_STREAM_STATE_OPEN;
990
0
}
991
992
0
nghttp2_stream *nghttp2_stream_get_parent(nghttp2_stream *stream) {
993
0
  return stream->dep_prev;
994
0
}
995
996
0
nghttp2_stream *nghttp2_stream_get_next_sibling(nghttp2_stream *stream) {
997
0
  return stream->sib_next;
998
0
}
999
1000
0
nghttp2_stream *nghttp2_stream_get_previous_sibling(nghttp2_stream *stream) {
1001
0
  return stream->sib_prev;
1002
0
}
1003
1004
0
nghttp2_stream *nghttp2_stream_get_first_child(nghttp2_stream *stream) {
1005
0
  return stream->dep_next;
1006
0
}
1007
1008
0
int32_t nghttp2_stream_get_weight(nghttp2_stream *stream) {
1009
0
  return stream->weight;
1010
0
}
1011
1012
0
int32_t nghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream) {
1013
0
  return stream->sum_dep_weight;
1014
0
}
1015
1016
0
int32_t nghttp2_stream_get_stream_id(nghttp2_stream *stream) {
1017
0
  return stream->stream_id;
1018
0
}