Files
nghttp2/lib/nghttp2_stream.c
Tatsuhiro Tsujikawa b157d4ebb2 Validate HTTP semantics by default
Previously we did not check HTTP semantics and it is left out for
application.  Although checking is relatively easy, but they are
scattered and error prone.  We have implemented these checks in our
applications and also feel they are tedious.  To make application
development a bit easier, this commit adds basic HTTP semantics
validation to library code.  We do following checks:

server:

* HEADERS is either request header or trailer header.  Other type of
header is disallowed.

client:

* HEADERS is either zero or more non-final response header or final
  response header or trailer header.  Other type of header is
  disallowed.

For both:

* Check mandatory pseudo header fields.
* Make sure that content-length matches the amount of DATA we
  received.

If validation fails, RST_STREAM of type PROTOCOL_ERROR is issued.
2015-02-20 01:01:10 +09:00

1066 lines
28 KiB
C

/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2012 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "nghttp2_stream.h"
#include <assert.h>
#include <stdio.h>
#include "nghttp2_session.h"
#include "nghttp2_helper.h"
void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
uint8_t flags, nghttp2_stream_state initial_state,
int32_t weight, nghttp2_stream_roots *roots,
int32_t remote_initial_window_size,
int32_t local_initial_window_size,
void *stream_user_data) {
nghttp2_map_entry_init(&stream->map_entry, stream_id);
stream->stream_id = stream_id;
stream->flags = flags;
stream->state = initial_state;
stream->shut_flags = NGHTTP2_SHUT_NONE;
stream->stream_user_data = stream_user_data;
stream->item = NULL;
stream->remote_window_size = remote_initial_window_size;
stream->local_window_size = local_initial_window_size;
stream->recv_window_size = 0;
stream->consumed_size = 0;
stream->recv_reduction = 0;
stream->dep_prev = NULL;
stream->dep_next = NULL;
stream->sib_prev = NULL;
stream->sib_next = NULL;
stream->closed_prev = NULL;
stream->closed_next = NULL;
stream->dpri = NGHTTP2_STREAM_DPRI_NO_ITEM;
stream->num_substreams = 1;
stream->weight = weight;
stream->effective_weight = stream->weight;
stream->sum_dep_weight = 0;
stream->sum_norest_weight = 0;
stream->sum_top_weight = 0;
stream->roots = roots;
stream->root_prev = NULL;
stream->root_next = NULL;
stream->http_flags = NGHTTP2_HTTP_FLAG_NONE;
stream->content_length = -1;
stream->recv_content_length = 0;
stream->status_code = -1;
}
void nghttp2_stream_free(nghttp2_stream *stream _U_) {
/* We don't free stream->item. If it is assigned to aob, then
active_outbound_item_reset() will delete it. If it is queued,
then it is deleted when pq is deleted in nghttp2_session_del().
Otherwise, nghttp2_session_del() will delete it. */
}
void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag) {
stream->shut_flags |= flag;
}
static int stream_push_item(nghttp2_stream *stream, nghttp2_session *session) {
int rv;
nghttp2_outbound_item *item;
assert(stream->item);
assert(stream->item->queued == 0);
item = stream->item;
/* If item is now sent, don't push it to the queue. Otherwise, we
may push same item twice. */
if (session->aob.item == item) {
return 0;
}
if (item->weight > stream->effective_weight) {
item->weight = stream->effective_weight;
}
item->cycle = session->last_cycle;
switch (item->frame.hd.type) {
case NGHTTP2_DATA:
rv = nghttp2_pq_push(&session->ob_da_pq, item);
break;
case NGHTTP2_HEADERS:
if (stream->state == NGHTTP2_STREAM_RESERVED) {
rv = nghttp2_pq_push(&session->ob_ss_pq, item);
} else {
rv = nghttp2_pq_push(&session->ob_pq, item);
}
break;
default:
/* should not reach here */
assert(0);
}
if (rv != 0) {
return rv;
}
item->queued = 1;
return 0;
}
static nghttp2_stream *stream_first_sib(nghttp2_stream *stream) {
for (; stream->sib_prev; stream = stream->sib_prev)
;
return stream;
}
static nghttp2_stream *stream_last_sib(nghttp2_stream *stream) {
for (; stream->sib_next; stream = stream->sib_next)
;
return stream;
}
static nghttp2_stream *stream_update_dep_length(nghttp2_stream *stream,
ssize_t delta) {
stream->num_substreams += delta;
stream = stream_first_sib(stream);
if (stream->dep_prev) {
return stream_update_dep_length(stream->dep_prev, delta);
}
return stream;
}
int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream,
int32_t weight) {
weight = stream->weight * weight / stream->sum_dep_weight;
return nghttp2_max(1, weight);
}
int32_t nghttp2_stream_dep_distributed_effective_weight(nghttp2_stream *stream,
int32_t weight) {
if (stream->sum_norest_weight == 0) {
return stream->effective_weight;
}
weight = stream->effective_weight * weight / stream->sum_norest_weight;
return nghttp2_max(1, weight);
}
static int32_t
stream_dep_distributed_top_effective_weight(nghttp2_stream *stream,
int32_t weight) {
if (stream->sum_top_weight == 0) {
return stream->effective_weight;
}
weight = stream->effective_weight * weight / stream->sum_top_weight;
return nghttp2_max(1, weight);
}
static void stream_update_dep_set_rest(nghttp2_stream *stream);
/* Updates effective_weight of descendant streams in subtree of
|stream|. We assume that stream->effective_weight is already set
right. */
static void stream_update_dep_effective_weight(nghttp2_stream *stream) {
nghttp2_stream *si;
DEBUGF(fprintf(stderr, "stream: update_dep_effective_weight "
"stream(%p)=%d, weight=%d, sum_norest_weight=%d, "
"sum_top_weight=%d\n",
stream, stream->stream_id, stream->weight,
stream->sum_norest_weight, stream->sum_top_weight));
/* stream->sum_norest_weight == 0 means there is no
NGHTTP2_STREAM_DPRI_TOP under stream */
if (stream->dpri != NGHTTP2_STREAM_DPRI_NO_ITEM ||
stream->sum_norest_weight == 0) {
return;
}
/* If there is no direct descendant whose dpri is
NGHTTP2_STREAM_DPRI_TOP, indirect descendants have the chance to
send data, so recursively set weight for descendants. */
if (stream->sum_top_weight == 0) {
for (si = stream->dep_next; si; si = si->sib_next) {
if (si->dpri != NGHTTP2_STREAM_DPRI_REST) {
si->effective_weight =
nghttp2_stream_dep_distributed_effective_weight(stream, si->weight);
}
stream_update_dep_effective_weight(si);
}
return;
}
/* If there is at least one direct descendant whose dpri is
NGHTTP2_STREAM_DPRI_TOP, we won't give a chance to indirect
descendants, since closed or blocked stream's weight is
distributed among its siblings */
for (si = stream->dep_next; si; si = si->sib_next) {
if (si->dpri == NGHTTP2_STREAM_DPRI_TOP) {
si->effective_weight =
stream_dep_distributed_top_effective_weight(stream, si->weight);
DEBUGF(fprintf(stderr, "stream: stream=%d top eweight=%d\n",
si->stream_id, si->effective_weight));
continue;
}
if (si->dpri == NGHTTP2_STREAM_DPRI_NO_ITEM) {
DEBUGF(fprintf(stderr, "stream: stream=%d no_item, ignored\n",
si->stream_id));
/* Since we marked NGHTTP2_STREAM_DPRI_TOP under si, we make
them NGHTTP2_STREAM_DPRI_REST again. */
stream_update_dep_set_rest(si->dep_next);
} else {
DEBUGF(
fprintf(stderr, "stream: stream=%d rest, ignored\n", si->stream_id));
}
}
}
static void stream_update_dep_set_rest(nghttp2_stream *stream) {
if (stream == NULL) {
return;
}
DEBUGF(fprintf(stderr, "stream: stream=%d is rest\n", stream->stream_id));
if (stream->dpri == NGHTTP2_STREAM_DPRI_REST) {
return;
}
if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP) {
stream->dpri = NGHTTP2_STREAM_DPRI_REST;
stream_update_dep_set_rest(stream->sib_next);
return;
}
stream_update_dep_set_rest(stream->sib_next);
stream_update_dep_set_rest(stream->dep_next);
}
/*
* Performs dfs starting |stream|, search stream which can become
* NGHTTP2_STREAM_DPRI_TOP and set its dpri.
*/
static void stream_update_dep_set_top(nghttp2_stream *stream) {
nghttp2_stream *si;
if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP) {
return;
}
if (stream->dpri == NGHTTP2_STREAM_DPRI_REST) {
DEBUGF(
fprintf(stderr, "stream: stream=%d item is top\n", stream->stream_id));
stream->dpri = NGHTTP2_STREAM_DPRI_TOP;
return;
}
for (si = stream->dep_next; si; si = si->sib_next) {
stream_update_dep_set_top(si);
}
}
/*
* Performs dfs starting |stream|, and dueue stream whose dpri is
* NGHTTP2_STREAM_DPRI_TOP and has not been queued yet.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory.
*/
static int stream_update_dep_queue_top(nghttp2_stream *stream,
nghttp2_session *session) {
int rv;
nghttp2_stream *si;
if (stream->dpri == NGHTTP2_STREAM_DPRI_REST) {
return 0;
}
if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP) {
if (!stream->item->queued) {
DEBUGF(fprintf(stderr, "stream: stream=%d enqueue\n", stream->stream_id));
rv = stream_push_item(stream, session);
if (rv != 0) {
return rv;
}
}
return 0;
}
for (si = stream->dep_next; si; si = si->sib_next) {
rv = stream_update_dep_queue_top(si, session);
if (rv != 0) {
return rv;
}
}
return 0;
}
/*
* Updates stream->sum_norest_weight and stream->sum_top_weight
* recursively. We have to gather effective sum of weight of
* descendants. If stream->dpri == NGHTTP2_STREAM_DPRI_NO_ITEM, we
* have to go deeper and check that any of its descendants has dpri
* value of NGHTTP2_STREAM_DPRI_TOP. If so, we have to add weight of
* its direct descendants to stream->sum_norest_weight. To make this
* work, this function returns 1 if any of its descendants has dpri
* value of NGHTTP2_STREAM_DPRI_TOP, otherwise 0.
*
* Calculating stream->sum_top-weight is very simple compared to
* stream->sum_norest_weight. It just adds up the weight of direct
* descendants whose dpri is NGHTTP2_STREAM_DPRI_TOP.
*/
static int stream_update_dep_sum_norest_weight(nghttp2_stream *stream) {
nghttp2_stream *si;
int rv;
stream->sum_norest_weight = 0;
stream->sum_top_weight = 0;
if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP) {
return 1;
}
if (stream->dpri == NGHTTP2_STREAM_DPRI_REST) {
return 0;
}
rv = 0;
for (si = stream->dep_next; si; si = si->sib_next) {
if (stream_update_dep_sum_norest_weight(si)) {
rv = 1;
stream->sum_norest_weight += si->weight;
}
if (si->dpri == NGHTTP2_STREAM_DPRI_TOP) {
stream->sum_top_weight += si->weight;
}
}
return rv;
}
static int stream_update_dep_on_attach_item(nghttp2_stream *stream,
nghttp2_session *session) {
nghttp2_stream *root_stream;
stream->dpri = NGHTTP2_STREAM_DPRI_REST;
stream_update_dep_set_rest(stream->dep_next);
root_stream = nghttp2_stream_get_dep_root(stream);
DEBUGF(fprintf(stderr, "root=%p, stream=%p\n", root_stream, stream));
stream_update_dep_set_top(root_stream);
stream_update_dep_sum_norest_weight(root_stream);
stream_update_dep_effective_weight(root_stream);
return stream_update_dep_queue_top(root_stream, session);
}
static int stream_update_dep_on_detach_item(nghttp2_stream *stream,
nghttp2_session *session) {
nghttp2_stream *root_stream;
stream->dpri = NGHTTP2_STREAM_DPRI_NO_ITEM;
root_stream = nghttp2_stream_get_dep_root(stream);
stream_update_dep_set_top(root_stream);
stream_update_dep_sum_norest_weight(root_stream);
stream_update_dep_effective_weight(root_stream);
return stream_update_dep_queue_top(root_stream, session);
}
int nghttp2_stream_attach_item(nghttp2_stream *stream,
nghttp2_outbound_item *item,
nghttp2_session *session) {
assert((stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0);
assert(stream->item == NULL);
DEBUGF(fprintf(stderr, "stream: stream=%d attach item=%p\n",
stream->stream_id, item));
stream->item = item;
return stream_update_dep_on_attach_item(stream, session);
}
int nghttp2_stream_detach_item(nghttp2_stream *stream,
nghttp2_session *session) {
DEBUGF(fprintf(stderr, "stream: stream=%d detach item=%p\n",
stream->stream_id, stream->item));
stream->item = NULL;
stream->flags &= ~NGHTTP2_STREAM_FLAG_DEFERRED_ALL;
return stream_update_dep_on_detach_item(stream, session);
}
int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags,
nghttp2_session *session) {
assert(stream->item);
DEBUGF(fprintf(stderr, "stream: stream=%d defer item=%p cause=%02x\n",
stream->stream_id, stream->item, flags));
stream->flags |= flags;
return stream_update_dep_on_detach_item(stream, session);
}
int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags,
nghttp2_session *session) {
assert(stream->item);
DEBUGF(fprintf(stderr, "stream: stream=%d resume item=%p flags=%02x\n",
stream->stream_id, stream->item, flags));
stream->flags &= ~flags;
if (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) {
return 0;
}
return stream_update_dep_on_attach_item(stream, session);
}
int nghttp2_stream_check_deferred_item(nghttp2_stream *stream) {
return stream->item && (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL);
}
int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream) {
return stream->item &&
(stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
}
static int update_initial_window_size(int32_t *window_size_ptr,
int32_t new_initial_window_size,
int32_t old_initial_window_size) {
int64_t new_window_size = (int64_t)(*window_size_ptr) +
new_initial_window_size - old_initial_window_size;
if (INT32_MIN > new_window_size ||
new_window_size > NGHTTP2_MAX_WINDOW_SIZE) {
return -1;
}
*window_size_ptr = (int32_t)new_window_size;
return 0;
}
int nghttp2_stream_update_remote_initial_window_size(
nghttp2_stream *stream, int32_t new_initial_window_size,
int32_t old_initial_window_size) {
return update_initial_window_size(&stream->remote_window_size,
new_initial_window_size,
old_initial_window_size);
}
int nghttp2_stream_update_local_initial_window_size(
nghttp2_stream *stream, int32_t new_initial_window_size,
int32_t old_initial_window_size) {
return update_initial_window_size(&stream->local_window_size,
new_initial_window_size,
old_initial_window_size);
}
void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream) {
stream->state = NGHTTP2_STREAM_OPENED;
stream->flags &= ~NGHTTP2_STREAM_FLAG_PUSH;
}
nghttp2_stream *nghttp2_stream_get_dep_root(nghttp2_stream *stream) {
for (;;) {
if (stream->sib_prev) {
stream = stream->sib_prev;
continue;
}
if (stream->dep_prev) {
stream = stream->dep_prev;
continue;
}
break;
}
return stream;
}
int nghttp2_stream_dep_subtree_find(nghttp2_stream *stream,
nghttp2_stream *target) {
if (stream == NULL) {
return 0;
}
if (stream == target) {
return 1;
}
if (nghttp2_stream_dep_subtree_find(stream->sib_next, target)) {
return 1;
}
return nghttp2_stream_dep_subtree_find(stream->dep_next, target);
}
void nghttp2_stream_dep_insert(nghttp2_stream *dep_stream,
nghttp2_stream *stream) {
nghttp2_stream *si;
nghttp2_stream *root_stream;
assert(stream->item == NULL);
DEBUGF(fprintf(stderr,
"stream: dep_insert dep_stream(%p)=%d, stream(%p)=%d\n",
dep_stream, dep_stream->stream_id, stream, stream->stream_id));
stream->sum_dep_weight = dep_stream->sum_dep_weight;
dep_stream->sum_dep_weight = stream->weight;
if (dep_stream->dep_next) {
for (si = dep_stream->dep_next; si; si = si->sib_next) {
stream->num_substreams += si->num_substreams;
}
stream->dep_next = dep_stream->dep_next;
stream->dep_next->dep_prev = stream;
}
dep_stream->dep_next = stream;
stream->dep_prev = dep_stream;
root_stream = stream_update_dep_length(dep_stream, 1);
stream_update_dep_sum_norest_weight(root_stream);
stream_update_dep_effective_weight(root_stream);
++stream->roots->num_streams;
}
static void link_dep(nghttp2_stream *dep_stream, nghttp2_stream *stream) {
dep_stream->dep_next = stream;
stream->dep_prev = dep_stream;
}
static void link_sib(nghttp2_stream *prev_stream, nghttp2_stream *stream) {
prev_stream->sib_next = stream;
stream->sib_prev = prev_stream;
}
static void insert_link_dep(nghttp2_stream *dep_stream,
nghttp2_stream *stream) {
nghttp2_stream *sib_next;
assert(stream->sib_prev == NULL);
sib_next = dep_stream->dep_next;
link_sib(stream, sib_next);
sib_next->dep_prev = NULL;
link_dep(dep_stream, stream);
}
static void unlink_sib(nghttp2_stream *stream) {
nghttp2_stream *prev, *next, *dep_next;
prev = stream->sib_prev;
dep_next = stream->dep_next;
assert(prev);
if (dep_next) {
/*
* prev--stream(--sib_next--...)
* |
* dep_next
*/
dep_next->dep_prev = NULL;
link_sib(prev, dep_next);
if (stream->sib_next) {
link_sib(stream_last_sib(dep_next), stream->sib_next);
}
} else {
/*
* prev--stream(--sib_next--...)
*/
next = stream->sib_next;
prev->sib_next = next;
if (next) {
next->sib_prev = prev;
}
}
}
static void unlink_dep(nghttp2_stream *stream) {
nghttp2_stream *prev, *next, *dep_next;
prev = stream->dep_prev;
dep_next = stream->dep_next;
assert(prev);
if (dep_next) {
/*
* prev
* |
* stream(--sib_next--...)
* |
* dep_next
*/
link_dep(prev, dep_next);
if (stream->sib_next) {
link_sib(stream_last_sib(dep_next), stream->sib_next);
}
} else if (stream->sib_next) {
/*
* prev
* |
* stream--sib_next
*/
next = stream->sib_next;
next->sib_prev = NULL;
link_dep(prev, next);
} else {
prev->dep_next = NULL;
}
}
void nghttp2_stream_dep_add(nghttp2_stream *dep_stream,
nghttp2_stream *stream) {
nghttp2_stream *root_stream;
assert(stream->item == NULL);
DEBUGF(fprintf(stderr, "stream: dep_add dep_stream(%p)=%d, stream(%p)=%d\n",
dep_stream, dep_stream->stream_id, stream, stream->stream_id));
root_stream = stream_update_dep_length(dep_stream, 1);
dep_stream->sum_dep_weight += stream->weight;
if (dep_stream->dep_next == NULL) {
link_dep(dep_stream, stream);
} else {
insert_link_dep(dep_stream, stream);
}
stream_update_dep_sum_norest_weight(root_stream);
stream_update_dep_effective_weight(root_stream);
++stream->roots->num_streams;
}
void nghttp2_stream_dep_remove(nghttp2_stream *stream) {
nghttp2_stream *prev, *next, *dep_prev, *si, *root_stream;
int32_t sum_dep_weight_delta;
root_stream = NULL;
DEBUGF(fprintf(stderr, "stream: dep_remove stream(%p)=%d\n", stream,
stream->stream_id));
/* Distribute weight of |stream| to direct descendants */
sum_dep_weight_delta = -stream->weight;
for (si = stream->dep_next; si; si = si->sib_next) {
si->weight = nghttp2_stream_dep_distributed_weight(stream, si->weight);
sum_dep_weight_delta += si->weight;
}
prev = stream_first_sib(stream);
dep_prev = prev->dep_prev;
if (dep_prev) {
root_stream = stream_update_dep_length(dep_prev, -1);
dep_prev->sum_dep_weight += sum_dep_weight_delta;
}
if (stream->sib_prev) {
unlink_sib(stream);
} else if (stream->dep_prev) {
unlink_dep(stream);
} else {
nghttp2_stream_roots_remove(stream->roots, stream);
/* stream is a root of tree. Removing stream makes its
descendants a root of its own subtree. */
for (si = stream->dep_next; si;) {
next = si->sib_next;
si->dep_prev = NULL;
si->sib_prev = NULL;
si->sib_next = NULL;
/* We already distributed weight of |stream| to this. */
si->effective_weight = si->weight;
nghttp2_stream_roots_add(si->roots, si);
si = next;
}
}
if (root_stream) {
stream_update_dep_sum_norest_weight(root_stream);
stream_update_dep_effective_weight(root_stream);
}
stream->num_substreams = 1;
stream->sum_dep_weight = 0;
stream->dep_prev = NULL;
stream->dep_next = NULL;
stream->sib_prev = NULL;
stream->sib_next = NULL;
--stream->roots->num_streams;
}
int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream,
nghttp2_stream *stream,
nghttp2_session *session) {
nghttp2_stream *last_sib;
nghttp2_stream *dep_next;
nghttp2_stream *root_stream;
size_t delta_substreams;
DEBUGF(fprintf(stderr, "stream: dep_insert_subtree dep_stream(%p)=%d "
"stream(%p)=%d\n",
dep_stream, dep_stream->stream_id, stream, stream->stream_id));
delta_substreams = stream->num_substreams;
stream_update_dep_set_rest(stream);
if (dep_stream->dep_next) {
/* dep_stream->num_substreams includes dep_stream itself */
stream->num_substreams += dep_stream->num_substreams - 1;
stream->sum_dep_weight += dep_stream->sum_dep_weight;
dep_stream->sum_dep_weight = stream->weight;
dep_next = dep_stream->dep_next;
stream_update_dep_set_rest(dep_next);
link_dep(dep_stream, stream);
if (stream->dep_next) {
last_sib = stream_last_sib(stream->dep_next);
link_sib(last_sib, dep_next);
dep_next->dep_prev = NULL;
} else {
link_dep(stream, dep_next);
}
} else {
link_dep(dep_stream, stream);
assert(dep_stream->sum_dep_weight == 0);
dep_stream->sum_dep_weight = stream->weight;
}
root_stream = stream_update_dep_length(dep_stream, delta_substreams);
stream_update_dep_set_top(root_stream);
stream_update_dep_sum_norest_weight(root_stream);
stream_update_dep_effective_weight(root_stream);
return stream_update_dep_queue_top(root_stream, session);
}
int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream,
nghttp2_stream *stream,
nghttp2_session *session) {
nghttp2_stream *root_stream;
DEBUGF(fprintf(stderr, "stream: dep_add_subtree dep_stream(%p)=%d "
"stream(%p)=%d\n",
dep_stream, dep_stream->stream_id, stream, stream->stream_id));
stream_update_dep_set_rest(stream);
if (dep_stream->dep_next) {
dep_stream->sum_dep_weight += stream->weight;
insert_link_dep(dep_stream, stream);
} else {
link_dep(dep_stream, stream);
assert(dep_stream->sum_dep_weight == 0);
dep_stream->sum_dep_weight = stream->weight;
}
root_stream = stream_update_dep_length(dep_stream, stream->num_substreams);
stream_update_dep_set_top(root_stream);
stream_update_dep_sum_norest_weight(root_stream);
stream_update_dep_effective_weight(root_stream);
return stream_update_dep_queue_top(root_stream, session);
}
void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream) {
nghttp2_stream *prev, *next, *dep_prev, *root_stream;
DEBUGF(fprintf(stderr, "stream: dep_remove_subtree stream(%p)=%d\n", stream,
stream->stream_id));
if (stream->sib_prev) {
prev = stream->sib_prev;
prev->sib_next = stream->sib_next;
if (prev->sib_next) {
prev->sib_next->sib_prev = prev;
}
prev = stream_first_sib(prev);
dep_prev = prev->dep_prev;
} else if (stream->dep_prev) {
dep_prev = stream->dep_prev;
next = stream->sib_next;
dep_prev->dep_next = next;
if (next) {
next->dep_prev = dep_prev;
next->sib_prev = NULL;
}
} else {
nghttp2_stream_roots_remove(stream->roots, stream);
dep_prev = NULL;
}
if (dep_prev) {
dep_prev->sum_dep_weight -= stream->weight;
root_stream = stream_update_dep_length(dep_prev, -stream->num_substreams);
stream_update_dep_sum_norest_weight(root_stream);
stream_update_dep_effective_weight(root_stream);
}
stream->sib_prev = NULL;
stream->sib_next = NULL;
stream->dep_prev = NULL;
}
int nghttp2_stream_dep_make_root(nghttp2_stream *stream,
nghttp2_session *session) {
DEBUGF(fprintf(stderr, "stream: dep_make_root stream(%p)=%d\n", stream,
stream->stream_id));
nghttp2_stream_roots_add(stream->roots, stream);
stream_update_dep_set_rest(stream);
stream->effective_weight = stream->weight;
stream_update_dep_set_top(stream);
stream_update_dep_sum_norest_weight(stream);
stream_update_dep_effective_weight(stream);
return stream_update_dep_queue_top(stream, session);
}
int
nghttp2_stream_dep_all_your_stream_are_belong_to_us(nghttp2_stream *stream,
nghttp2_session *session) {
nghttp2_stream *first, *si;
DEBUGF(fprintf(stderr, "stream: ALL YOUR STREAM ARE BELONG TO US "
"stream(%p)=%d\n",
stream, stream->stream_id));
first = stream->roots->head;
/* stream must not be include in stream->roots->head list */
assert(first != stream);
if (first) {
nghttp2_stream *prev;
prev = first;
DEBUGF(fprintf(stderr, "stream: root stream(%p)=%d\n", first,
first->stream_id));
stream->sum_dep_weight += first->weight;
stream->num_substreams += first->num_substreams;
for (si = first->root_next; si; si = si->root_next) {
assert(si != stream);
DEBUGF(
fprintf(stderr, "stream: root stream(%p)=%d\n", si, si->stream_id));
stream->sum_dep_weight += si->weight;
stream->num_substreams += si->num_substreams;
link_sib(prev, si);
prev = si;
}
if (stream->dep_next) {
nghttp2_stream *sib_next;
sib_next = stream->dep_next;
sib_next->dep_prev = NULL;
link_sib(first, sib_next);
link_dep(stream, prev);
} else {
link_dep(stream, first);
}
}
nghttp2_stream_roots_remove_all(stream->roots);
return nghttp2_stream_dep_make_root(stream, session);
}
int nghttp2_stream_in_dep_tree(nghttp2_stream *stream) {
return stream->dep_prev || stream->dep_next || stream->sib_prev ||
stream->sib_next || stream->root_next || stream->root_prev ||
stream->roots->head == stream;
}
void nghttp2_stream_roots_init(nghttp2_stream_roots *roots) {
roots->head = NULL;
roots->num_streams = 0;
}
void nghttp2_stream_roots_free(nghttp2_stream_roots *roots _U_) {}
void nghttp2_stream_roots_add(nghttp2_stream_roots *roots,
nghttp2_stream *stream) {
if (roots->head) {
stream->root_next = roots->head;
roots->head->root_prev = stream;
}
roots->head = stream;
}
void nghttp2_stream_roots_remove(nghttp2_stream_roots *roots,
nghttp2_stream *stream) {
nghttp2_stream *root_prev, *root_next;
root_prev = stream->root_prev;
root_next = stream->root_next;
if (root_prev) {
root_prev->root_next = root_next;
if (root_next) {
root_next->root_prev = root_prev;
}
} else {
if (root_next) {
root_next->root_prev = NULL;
}
roots->head = root_next;
}
stream->root_prev = NULL;
stream->root_next = NULL;
}
void nghttp2_stream_roots_remove_all(nghttp2_stream_roots *roots) {
nghttp2_stream *si, *next;
for (si = roots->head; si;) {
next = si->root_next;
si->root_prev = NULL;
si->root_next = NULL;
si = next;
}
roots->head = NULL;
}