Path: blob/master/Utilities/cmnghttp2/lib/nghttp2_stream.c
3153 views
/*1* nghttp2 - HTTP/2 C Library2*3* Copyright (c) 2012 Tatsuhiro Tsujikawa4*5* Permission is hereby granted, free of charge, to any person obtaining6* a copy of this software and associated documentation files (the7* "Software"), to deal in the Software without restriction, including8* without limitation the rights to use, copy, modify, merge, publish,9* distribute, sublicense, and/or sell copies of the Software, and to10* permit persons to whom the Software is furnished to do so, subject to11* the following conditions:12*13* The above copyright notice and this permission notice shall be14* included in all copies or substantial portions of the Software.15*16* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,17* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF18* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND19* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE20* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION21* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION22* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.23*/24#include "nghttp2_stream.h"2526#include <assert.h>27#include <stdio.h>2829#include "nghttp2_session.h"30#include "nghttp2_helper.h"31#include "nghttp2_debug.h"32#include "nghttp2_frame.h"3334/* Maximum distance between any two stream's cycle in the same35priority queue. Imagine stream A's cycle is A, and stream B's36cycle is B, and A < B. The cycle is unsigned 32 bit integer, it37may get overflow. Because of how we calculate the next cycle38value, if B - A is less than or equals to39NGHTTP2_MAX_CYCLE_DISTANCE, A and B are in the same scale, in other40words, B is really greater than or equal to A. Otherwise, A is a41result of overflow, and it is actually A > B if we consider that42fact. */43#define NGHTTP2_MAX_CYCLE_DISTANCE \44((uint64_t)NGHTTP2_MAX_FRAME_SIZE_MAX * 256 + 255)4546static int stream_less(const void *lhsx, const void *rhsx) {47const nghttp2_stream *lhs, *rhs;4849lhs = nghttp2_struct_of(lhsx, nghttp2_stream, pq_entry);50rhs = nghttp2_struct_of(rhsx, nghttp2_stream, pq_entry);5152if (lhs->cycle == rhs->cycle) {53return lhs->seq < rhs->seq;54}5556return rhs->cycle - lhs->cycle <= NGHTTP2_MAX_CYCLE_DISTANCE;57}5859void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,60uint8_t flags, nghttp2_stream_state initial_state,61int32_t weight, int32_t remote_initial_window_size,62int32_t local_initial_window_size,63void *stream_user_data, nghttp2_mem *mem) {64nghttp2_pq_init(&stream->obq, stream_less, mem);6566stream->stream_id = stream_id;67stream->flags = flags;68stream->state = initial_state;69stream->shut_flags = NGHTTP2_SHUT_NONE;70stream->stream_user_data = stream_user_data;71stream->item = NULL;72stream->remote_window_size = remote_initial_window_size;73stream->local_window_size = local_initial_window_size;74stream->recv_window_size = 0;75stream->consumed_size = 0;76stream->recv_reduction = 0;77stream->window_update_queued = 0;7879stream->dep_prev = NULL;80stream->dep_next = NULL;81stream->sib_prev = NULL;82stream->sib_next = NULL;8384stream->closed_prev = NULL;85stream->closed_next = NULL;8687stream->weight = weight;88stream->sum_dep_weight = 0;8990stream->http_flags = NGHTTP2_HTTP_FLAG_NONE;91stream->content_length = -1;92stream->recv_content_length = 0;93stream->status_code = -1;9495stream->queued = 0;96stream->descendant_last_cycle = 0;97stream->cycle = 0;98stream->pending_penalty = 0;99stream->descendant_next_seq = 0;100stream->seq = 0;101stream->last_writelen = 0;102103stream->extpri = stream->http_extpri = NGHTTP2_EXTPRI_DEFAULT_URGENCY;104}105106void nghttp2_stream_free(nghttp2_stream *stream) {107nghttp2_pq_free(&stream->obq);108/* We don't free stream->item. If it is assigned to aob, then109active_outbound_item_reset() will delete it. Otherwise,110nghttp2_stream_close() or session_del() will delete it. */111}112113void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag) {114stream->shut_flags = (uint8_t)(stream->shut_flags | flag);115}116117/*118* Returns nonzero if |stream| is active. This function does not take119* into account its descendants.120*/121static int stream_active(nghttp2_stream *stream) {122return stream->item &&123(stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0;124}125126/*127* Returns nonzero if |stream| or one of its descendants is active128*/129static int stream_subtree_active(nghttp2_stream *stream) {130return stream_active(stream) || !nghttp2_pq_empty(&stream->obq);131}132133/*134* Returns next cycle for |stream|.135*/136static void stream_next_cycle(nghttp2_stream *stream, uint64_t last_cycle) {137uint64_t penalty;138139penalty = (uint64_t)stream->last_writelen * NGHTTP2_MAX_WEIGHT +140stream->pending_penalty;141142stream->cycle = last_cycle + penalty / (uint32_t)stream->weight;143stream->pending_penalty = (uint32_t)(penalty % (uint32_t)stream->weight);144}145146static int stream_obq_push(nghttp2_stream *dep_stream, nghttp2_stream *stream) {147int rv;148149for (; dep_stream && !stream->queued;150stream = dep_stream, dep_stream = dep_stream->dep_prev) {151stream_next_cycle(stream, dep_stream->descendant_last_cycle);152stream->seq = dep_stream->descendant_next_seq++;153154DEBUGF("stream: stream=%d obq push cycle=%lu\n", stream->stream_id,155stream->cycle);156157DEBUGF("stream: push stream %d to stream %d\n", stream->stream_id,158dep_stream->stream_id);159160rv = nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry);161if (rv != 0) {162return rv;163}164stream->queued = 1;165}166167return 0;168}169170/*171* Removes |stream| from parent's obq. If removal of |stream| makes172* parent's obq empty, and parent is not active, then parent is also173* removed. This process is repeated recursively.174*/175static void stream_obq_remove(nghttp2_stream *stream) {176nghttp2_stream *dep_stream;177178dep_stream = stream->dep_prev;179180if (!stream->queued) {181return;182}183184for (; dep_stream; stream = dep_stream, dep_stream = dep_stream->dep_prev) {185DEBUGF("stream: remove stream %d from stream %d\n", stream->stream_id,186dep_stream->stream_id);187188nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry);189190assert(stream->queued);191192stream->queued = 0;193stream->cycle = 0;194stream->pending_penalty = 0;195stream->descendant_last_cycle = 0;196stream->last_writelen = 0;197198if (stream_subtree_active(dep_stream)) {199return;200}201}202}203204/*205* Moves |stream| from |src|'s obq to |dest|'s obq. Removal from206* |src|'s obq is just done calling nghttp2_pq_remove(), so it does207* not recursively remove |src| and ancestors, like208* stream_obq_remove().209*/210static int stream_obq_move(nghttp2_stream *dest, nghttp2_stream *src,211nghttp2_stream *stream) {212if (!stream->queued) {213return 0;214}215216DEBUGF("stream: remove stream %d from stream %d (move)\n", stream->stream_id,217src->stream_id);218219nghttp2_pq_remove(&src->obq, &stream->pq_entry);220stream->queued = 0;221222return stream_obq_push(dest, stream);223}224225void nghttp2_stream_reschedule(nghttp2_stream *stream) {226nghttp2_stream *dep_stream;227228assert(stream->queued);229230dep_stream = stream->dep_prev;231232for (; dep_stream; stream = dep_stream, dep_stream = dep_stream->dep_prev) {233nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry);234235stream_next_cycle(stream, dep_stream->descendant_last_cycle);236stream->seq = dep_stream->descendant_next_seq++;237238nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry);239240DEBUGF("stream: stream=%d obq resched cycle=%lu\n", stream->stream_id,241stream->cycle);242243dep_stream->last_writelen = stream->last_writelen;244}245}246247void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight) {248nghttp2_stream *dep_stream;249uint64_t last_cycle;250int32_t old_weight;251uint64_t wlen_penalty;252253if (stream->weight == weight) {254return;255}256257old_weight = stream->weight;258stream->weight = weight;259260dep_stream = stream->dep_prev;261262if (!dep_stream) {263return;264}265266dep_stream->sum_dep_weight += weight - old_weight;267268if (!stream->queued) {269return;270}271272nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry);273274wlen_penalty = (uint64_t)stream->last_writelen * NGHTTP2_MAX_WEIGHT;275276/* Compute old stream->pending_penalty we used to calculate277stream->cycle */278stream->pending_penalty =279(uint32_t)((stream->pending_penalty + (uint32_t)old_weight -280(wlen_penalty % (uint32_t)old_weight)) %281(uint32_t)old_weight);282283last_cycle = stream->cycle -284(wlen_penalty + stream->pending_penalty) / (uint32_t)old_weight;285286/* Now we have old stream->pending_penalty and new stream->weight in287place */288stream_next_cycle(stream, last_cycle);289290if (dep_stream->descendant_last_cycle - stream->cycle <=291NGHTTP2_MAX_CYCLE_DISTANCE) {292stream->cycle = dep_stream->descendant_last_cycle;293}294295/* Continue to use same stream->seq */296297nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry);298299DEBUGF("stream: stream=%d obq resched cycle=%lu\n", stream->stream_id,300stream->cycle);301}302303static nghttp2_stream *stream_last_sib(nghttp2_stream *stream) {304for (; stream->sib_next; stream = stream->sib_next)305;306307return stream;308}309310int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream,311int32_t weight) {312weight = stream->weight * weight / stream->sum_dep_weight;313314return nghttp2_max(1, weight);315}316317#ifdef STREAM_DEP_DEBUG318319static void ensure_inactive(nghttp2_stream *stream) {320nghttp2_stream *si;321322if (stream->queued) {323fprintf(stderr, "stream(%p)=%d, stream->queued = 1; want 0\n", stream,324stream->stream_id);325assert(0);326}327328if (stream_active(stream)) {329fprintf(stderr, "stream(%p)=%d, stream_active(stream) = 1; want 0\n",330stream, stream->stream_id);331assert(0);332}333334if (!nghttp2_pq_empty(&stream->obq)) {335fprintf(stderr, "stream(%p)=%d, nghttp2_pq_size() = %zu; want 0\n", stream,336stream->stream_id, nghttp2_pq_size(&stream->obq));337assert(0);338}339340for (si = stream->dep_next; si; si = si->sib_next) {341ensure_inactive(si);342}343}344345static void check_queued(nghttp2_stream *stream) {346nghttp2_stream *si;347int queued;348349if (stream->queued) {350if (!stream_subtree_active(stream)) {351fprintf(stderr,352"stream(%p)=%d, stream->queued == 1, but "353"stream_active() == %d and nghttp2_pq_size(&stream->obq) = %zu\n",354stream, stream->stream_id, stream_active(stream),355nghttp2_pq_size(&stream->obq));356assert(0);357}358if (!stream_active(stream)) {359queued = 0;360for (si = stream->dep_next; si; si = si->sib_next) {361if (si->queued) {362++queued;363}364}365if (queued == 0) {366fprintf(stderr,367"stream(%p)=%d, stream->queued == 1, and "368"!stream_active(), but no descendants is queued\n",369stream, stream->stream_id);370assert(0);371}372}373374for (si = stream->dep_next; si; si = si->sib_next) {375check_queued(si);376}377} else {378if (stream_active(stream) || !nghttp2_pq_empty(&stream->obq)) {379fprintf(stderr,380"stream(%p) = %d, stream->queued == 0, but "381"stream_active(stream) == %d and "382"nghttp2_pq_size(&stream->obq) = %zu\n",383stream, stream->stream_id, stream_active(stream),384nghttp2_pq_size(&stream->obq));385assert(0);386}387for (si = stream->dep_next; si; si = si->sib_next) {388ensure_inactive(si);389}390}391}392393static void check_sum_dep(nghttp2_stream *stream) {394nghttp2_stream *si;395int32_t n = 0;396for (si = stream->dep_next; si; si = si->sib_next) {397n += si->weight;398}399if (n != stream->sum_dep_weight) {400fprintf(stderr, "stream(%p)=%d, sum_dep_weight = %d; want %d\n", stream,401stream->stream_id, n, stream->sum_dep_weight);402assert(0);403}404for (si = stream->dep_next; si; si = si->sib_next) {405check_sum_dep(si);406}407}408409static void check_dep_prev(nghttp2_stream *stream) {410nghttp2_stream *si;411for (si = stream->dep_next; si; si = si->sib_next) {412if (si->dep_prev != stream) {413fprintf(stderr, "si->dep_prev = %p; want %p\n", si->dep_prev, stream);414assert(0);415}416check_dep_prev(si);417}418}419420#endif /* STREAM_DEP_DEBUG */421422#ifdef STREAM_DEP_DEBUG423static void validate_tree(nghttp2_stream *stream) {424nghttp2_stream *si;425426if (!stream) {427return;428}429430for (; stream->dep_prev; stream = stream->dep_prev)431;432433assert(stream->stream_id == 0);434assert(!stream->queued);435436fprintf(stderr, "checking...\n");437if (nghttp2_pq_empty(&stream->obq)) {438fprintf(stderr, "root obq empty\n");439for (si = stream->dep_next; si; si = si->sib_next) {440ensure_inactive(si);441}442} else {443for (si = stream->dep_next; si; si = si->sib_next) {444check_queued(si);445}446}447448check_sum_dep(stream);449check_dep_prev(stream);450}451#else /* !STREAM_DEP_DEBUG */452static void validate_tree(nghttp2_stream *stream) { (void)stream; }453#endif /* !STREAM_DEP_DEBUG*/454455static int stream_update_dep_on_attach_item(nghttp2_stream *stream) {456int rv;457458rv = stream_obq_push(stream->dep_prev, stream);459if (rv != 0) {460return rv;461}462463validate_tree(stream);464return 0;465}466467static int stream_update_dep_on_detach_item(nghttp2_stream *stream) {468if (nghttp2_pq_empty(&stream->obq)) {469stream_obq_remove(stream);470}471472validate_tree(stream);473474return 0;475}476477int nghttp2_stream_attach_item(nghttp2_stream *stream,478nghttp2_outbound_item *item) {479int rv;480481assert((stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0);482assert(stream->item == NULL);483484DEBUGF("stream: stream=%d attach item=%p\n", stream->stream_id, item);485486stream->item = item;487488if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {489return 0;490}491492rv = stream_update_dep_on_attach_item(stream);493if (rv != 0) {494/* This may relave stream->queued == 1, but stream->item == NULL.495But only consequence of this error is fatal one, and session496destruction. In that execution path, these inconsistency does497not matter. */498stream->item = NULL;499return rv;500}501502return 0;503}504505int nghttp2_stream_detach_item(nghttp2_stream *stream) {506DEBUGF("stream: stream=%d detach item=%p\n", stream->stream_id, stream->item);507508stream->item = NULL;509stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_DEFERRED_ALL);510511if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {512return 0;513}514515return stream_update_dep_on_detach_item(stream);516}517518int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags) {519assert(stream->item);520521DEBUGF("stream: stream=%d defer item=%p cause=%02x\n", stream->stream_id,522stream->item, flags);523524stream->flags |= flags;525526if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {527return 0;528}529530return stream_update_dep_on_detach_item(stream);531}532533int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags) {534assert(stream->item);535536DEBUGF("stream: stream=%d resume item=%p flags=%02x\n", stream->stream_id,537stream->item, flags);538539stream->flags = (uint8_t)(stream->flags & ~flags);540541if (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) {542return 0;543}544545if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {546return 0;547}548549return stream_update_dep_on_attach_item(stream);550}551552int nghttp2_stream_check_deferred_item(nghttp2_stream *stream) {553return stream->item && (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL);554}555556int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream) {557return stream->item &&558(stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);559}560561static int update_initial_window_size(int32_t *window_size_ptr,562int32_t new_initial_window_size,563int32_t old_initial_window_size) {564int64_t new_window_size = (int64_t)(*window_size_ptr) +565new_initial_window_size - old_initial_window_size;566if (INT32_MIN > new_window_size ||567new_window_size > NGHTTP2_MAX_WINDOW_SIZE) {568return -1;569}570*window_size_ptr = (int32_t)new_window_size;571return 0;572}573574int nghttp2_stream_update_remote_initial_window_size(575nghttp2_stream *stream, int32_t new_initial_window_size,576int32_t old_initial_window_size) {577return update_initial_window_size(&stream->remote_window_size,578new_initial_window_size,579old_initial_window_size);580}581582int nghttp2_stream_update_local_initial_window_size(583nghttp2_stream *stream, int32_t new_initial_window_size,584int32_t old_initial_window_size) {585return update_initial_window_size(&stream->local_window_size,586new_initial_window_size,587old_initial_window_size);588}589590void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream) {591stream->state = NGHTTP2_STREAM_OPENED;592stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_PUSH);593}594595int nghttp2_stream_dep_find_ancestor(nghttp2_stream *stream,596nghttp2_stream *target) {597for (; stream; stream = stream->dep_prev) {598if (stream == target) {599return 1;600}601}602return 0;603}604605int nghttp2_stream_dep_insert(nghttp2_stream *dep_stream,606nghttp2_stream *stream) {607nghttp2_stream *si;608int rv;609610DEBUGF("stream: dep_insert dep_stream(%p)=%d, stream(%p)=%d\n", dep_stream,611dep_stream->stream_id, stream, stream->stream_id);612613stream->sum_dep_weight = dep_stream->sum_dep_weight;614dep_stream->sum_dep_weight = stream->weight;615616if (dep_stream->dep_next) {617for (si = dep_stream->dep_next; si; si = si->sib_next) {618si->dep_prev = stream;619if (si->queued) {620rv = stream_obq_move(stream, dep_stream, si);621if (rv != 0) {622return rv;623}624}625}626627if (stream_subtree_active(stream)) {628rv = stream_obq_push(dep_stream, stream);629if (rv != 0) {630return rv;631}632}633634stream->dep_next = dep_stream->dep_next;635}636637dep_stream->dep_next = stream;638stream->dep_prev = dep_stream;639640validate_tree(stream);641642return 0;643}644645static void set_dep_prev(nghttp2_stream *stream, nghttp2_stream *dep) {646for (; stream; stream = stream->sib_next) {647stream->dep_prev = dep;648}649}650651static void link_dep(nghttp2_stream *dep_stream, nghttp2_stream *stream) {652dep_stream->dep_next = stream;653if (stream) {654stream->dep_prev = dep_stream;655}656}657658static void link_sib(nghttp2_stream *a, nghttp2_stream *b) {659a->sib_next = b;660if (b) {661b->sib_prev = a;662}663}664665static void insert_link_dep(nghttp2_stream *dep_stream,666nghttp2_stream *stream) {667nghttp2_stream *sib_next;668669assert(stream->sib_prev == NULL);670671sib_next = dep_stream->dep_next;672673link_sib(stream, sib_next);674675link_dep(dep_stream, stream);676}677678static void unlink_sib(nghttp2_stream *stream) {679nghttp2_stream *prev, *next, *dep_next;680681prev = stream->sib_prev;682dep_next = stream->dep_next;683684assert(prev);685686if (dep_next) {687/*688* prev--stream(--sib_next--...)689* |690* dep_next691*/692693link_sib(prev, dep_next);694695set_dep_prev(dep_next, stream->dep_prev);696697if (stream->sib_next) {698link_sib(stream_last_sib(dep_next), stream->sib_next);699}700} else {701/*702* prev--stream(--sib_next--...)703*/704next = stream->sib_next;705706prev->sib_next = next;707708if (next) {709next->sib_prev = prev;710}711}712}713714static void unlink_dep(nghttp2_stream *stream) {715nghttp2_stream *prev, *next, *dep_next;716717prev = stream->dep_prev;718dep_next = stream->dep_next;719720assert(prev);721722if (dep_next) {723/*724* prev725* |726* stream(--sib_next--...)727* |728* dep_next729*/730link_dep(prev, dep_next);731732set_dep_prev(dep_next, stream->dep_prev);733734if (stream->sib_next) {735link_sib(stream_last_sib(dep_next), stream->sib_next);736}737738} else if (stream->sib_next) {739/*740* prev741* |742* stream--sib_next743*/744next = stream->sib_next;745746next->sib_prev = NULL;747748link_dep(prev, next);749} else {750prev->dep_next = NULL;751}752}753754void nghttp2_stream_dep_add(nghttp2_stream *dep_stream,755nghttp2_stream *stream) {756DEBUGF("stream: dep_add dep_stream(%p)=%d, stream(%p)=%d\n", dep_stream,757dep_stream->stream_id, stream, stream->stream_id);758759dep_stream->sum_dep_weight += stream->weight;760761if (dep_stream->dep_next == NULL) {762link_dep(dep_stream, stream);763} else {764insert_link_dep(dep_stream, stream);765}766767validate_tree(stream);768}769770int nghttp2_stream_dep_remove(nghttp2_stream *stream) {771nghttp2_stream *dep_prev, *si;772int32_t sum_dep_weight_delta;773int rv;774775DEBUGF("stream: dep_remove stream(%p)=%d\n", stream, stream->stream_id);776777/* Distribute weight of |stream| to direct descendants */778sum_dep_weight_delta = -stream->weight;779780for (si = stream->dep_next; si; si = si->sib_next) {781si->weight = nghttp2_stream_dep_distributed_weight(stream, si->weight);782783sum_dep_weight_delta += si->weight;784785if (si->queued) {786rv = stream_obq_move(stream->dep_prev, stream, si);787if (rv != 0) {788return rv;789}790}791}792793assert(stream->dep_prev);794795dep_prev = stream->dep_prev;796797dep_prev->sum_dep_weight += sum_dep_weight_delta;798799if (stream->queued) {800stream_obq_remove(stream);801}802803if (stream->sib_prev) {804unlink_sib(stream);805} else {806unlink_dep(stream);807}808809stream->sum_dep_weight = 0;810811stream->dep_prev = NULL;812stream->dep_next = NULL;813stream->sib_prev = NULL;814stream->sib_next = NULL;815816validate_tree(dep_prev);817818return 0;819}820821int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream,822nghttp2_stream *stream) {823nghttp2_stream *last_sib;824nghttp2_stream *dep_next;825nghttp2_stream *si;826int rv;827828DEBUGF("stream: dep_insert_subtree dep_stream(%p)=%d stream(%p)=%d\n",829dep_stream, dep_stream->stream_id, stream, stream->stream_id);830831stream->sum_dep_weight += dep_stream->sum_dep_weight;832dep_stream->sum_dep_weight = stream->weight;833834if (dep_stream->dep_next) {835dep_next = dep_stream->dep_next;836837link_dep(dep_stream, stream);838839if (stream->dep_next) {840last_sib = stream_last_sib(stream->dep_next);841842link_sib(last_sib, dep_next);843} else {844link_dep(stream, dep_next);845}846847for (si = dep_next; si; si = si->sib_next) {848si->dep_prev = stream;849if (si->queued) {850rv = stream_obq_move(stream, dep_stream, si);851if (rv != 0) {852return rv;853}854}855}856} else {857link_dep(dep_stream, stream);858}859860if (stream_subtree_active(stream)) {861rv = stream_obq_push(dep_stream, stream);862if (rv != 0) {863return rv;864}865}866867validate_tree(dep_stream);868869return 0;870}871872int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream,873nghttp2_stream *stream) {874int rv;875876DEBUGF("stream: dep_add_subtree dep_stream(%p)=%d stream(%p)=%d\n",877dep_stream, dep_stream->stream_id, stream, stream->stream_id);878879dep_stream->sum_dep_weight += stream->weight;880881if (dep_stream->dep_next) {882insert_link_dep(dep_stream, stream);883} else {884link_dep(dep_stream, stream);885}886887if (stream_subtree_active(stream)) {888rv = stream_obq_push(dep_stream, stream);889if (rv != 0) {890return rv;891}892}893894validate_tree(dep_stream);895896return 0;897}898899void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream) {900nghttp2_stream *next, *dep_prev;901902DEBUGF("stream: dep_remove_subtree stream(%p)=%d\n", stream,903stream->stream_id);904905assert(stream->dep_prev);906907dep_prev = stream->dep_prev;908909if (stream->sib_prev) {910link_sib(stream->sib_prev, stream->sib_next);911} else {912next = stream->sib_next;913914link_dep(dep_prev, next);915916if (next) {917next->sib_prev = NULL;918}919}920921dep_prev->sum_dep_weight -= stream->weight;922923if (stream->queued) {924stream_obq_remove(stream);925}926927validate_tree(dep_prev);928929stream->sib_prev = NULL;930stream->sib_next = NULL;931stream->dep_prev = NULL;932}933934int nghttp2_stream_in_dep_tree(nghttp2_stream *stream) {935return stream->dep_prev || stream->dep_next || stream->sib_prev ||936stream->sib_next;937}938939nghttp2_outbound_item *940nghttp2_stream_next_outbound_item(nghttp2_stream *stream) {941nghttp2_pq_entry *ent;942nghttp2_stream *si;943944for (;;) {945if (stream_active(stream)) {946/* Update ascendant's descendant_last_cycle here, so that we can947assure that new stream is scheduled based on it. */948for (si = stream; si->dep_prev; si = si->dep_prev) {949si->dep_prev->descendant_last_cycle = si->cycle;950}951return stream->item;952}953ent = nghttp2_pq_top(&stream->obq);954if (!ent) {955return NULL;956}957stream = nghttp2_struct_of(ent, nghttp2_stream, pq_entry);958}959}960961nghttp2_stream_proto_state nghttp2_stream_get_state(nghttp2_stream *stream) {962if (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) {963return NGHTTP2_STREAM_STATE_CLOSED;964}965966if (stream->flags & NGHTTP2_STREAM_FLAG_PUSH) {967if (stream->shut_flags & NGHTTP2_SHUT_RD) {968return NGHTTP2_STREAM_STATE_RESERVED_LOCAL;969}970971if (stream->shut_flags & NGHTTP2_SHUT_WR) {972return NGHTTP2_STREAM_STATE_RESERVED_REMOTE;973}974}975976if (stream->shut_flags & NGHTTP2_SHUT_RD) {977return NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE;978}979980if (stream->shut_flags & NGHTTP2_SHUT_WR) {981return NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL;982}983984if (stream->state == NGHTTP2_STREAM_IDLE) {985return NGHTTP2_STREAM_STATE_IDLE;986}987988return NGHTTP2_STREAM_STATE_OPEN;989}990991nghttp2_stream *nghttp2_stream_get_parent(nghttp2_stream *stream) {992return stream->dep_prev;993}994995nghttp2_stream *nghttp2_stream_get_next_sibling(nghttp2_stream *stream) {996return stream->sib_next;997}998999nghttp2_stream *nghttp2_stream_get_previous_sibling(nghttp2_stream *stream) {1000return stream->sib_prev;1001}10021003nghttp2_stream *nghttp2_stream_get_first_child(nghttp2_stream *stream) {1004return stream->dep_next;1005}10061007int32_t nghttp2_stream_get_weight(nghttp2_stream *stream) {1008return stream->weight;1009}10101011int32_t nghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream) {1012return stream->sum_dep_weight;1013}10141015int32_t nghttp2_stream_get_stream_id(nghttp2_stream *stream) {1016return stream->stream_id;1017}101810191020