Coverage Report

Created: 2024-05-21 06:52

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