Merge branch 'master' into v1.0.0
This commit is contained in:
commit
1ad1fe6005
|
@ -25,13 +25,13 @@ dnl Do not change user variables!
|
|||
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
|
||||
|
||||
AC_PREREQ(2.61)
|
||||
AC_INIT([nghttp2], [0.7.13-DEV], [t-tujikawa@users.sourceforge.net])
|
||||
AC_INIT([nghttp2], [0.7.14-DEV], [t-tujikawa@users.sourceforge.net])
|
||||
LT_PREREQ([2.2.6])
|
||||
LT_INIT()
|
||||
dnl See versioning rule:
|
||||
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
AC_SUBST(LT_CURRENT, 13)
|
||||
AC_SUBST(LT_REVISION, 1)
|
||||
AC_SUBST(LT_REVISION, 2)
|
||||
AC_SUBST(LT_AGE, 8)
|
||||
|
||||
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
|
||||
|
|
|
@ -200,11 +200,28 @@ help:
|
|||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
apiref.rst: $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \
|
||||
apiref.rst macros.rst enums.rst types.rst: \
|
||||
$(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \
|
||||
$(top_builddir)/lib/includes/nghttp2/nghttp2.h
|
||||
$(PYTHON) $(top_srcdir)/doc/mkapiref.py \
|
||||
$@ macros.rst enums.rst types.rst . $^
|
||||
|
||||
# Inspired by
|
||||
# http://www.gnu.org/savannah-checkouts/gnu/automake/manual/html_node/Multiple-Outputs.html
|
||||
apidoc.stamp: $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \
|
||||
$(top_builddir)/lib/includes/nghttp2/nghttp2.h
|
||||
@rm -f apidoc.tmp
|
||||
@touch apidoc.tmp
|
||||
$(PYTHON) $(top_srcdir)/doc/mkapiref.py \
|
||||
$@ macros.rst enums.rst types.rst . $^
|
||||
@mv -f apidoc.tmp $@
|
||||
$(APIDOC): apidoc.stamp
|
||||
## Recover from the removal of $@
|
||||
@if test -f $@; then :; else \
|
||||
rm -f apidoc.stamp; \
|
||||
$(MAKE) $(AM_MAKEFLAGS) apidoc.stamp; \
|
||||
fi
|
||||
|
||||
clean-local:
|
||||
-rm $(APIDOCS)
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
|
13
doc/h2load.1
13
doc/h2load.1
|
@ -1,6 +1,6 @@
|
|||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "H2LOAD" "1" "April 19, 2015" "0.7.12" "nghttp2"
|
||||
.TH "H2LOAD" "1" "April 27, 2015" "0.7.13" "nghttp2"
|
||||
.SH NAME
|
||||
h2load \- HTTP/2 benchmarking tool
|
||||
.
|
||||
|
@ -93,6 +93,8 @@ Default: \fBauto\fP
|
|||
.B \-w, \-\-window\-bits=<N>
|
||||
Sets the stream level initial window size to (2**<N>)\-1.
|
||||
For SPDY, 2**<N> is used instead.
|
||||
.sp
|
||||
Default: \fB30\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
|
@ -101,6 +103,8 @@ Sets the connection level initial window size to
|
|||
(2**<N>)\-1. For SPDY, if <N> is strictly less than 16,
|
||||
this option is ignored. Otherwise 2**<N> is used for
|
||||
SPDY.
|
||||
.sp
|
||||
Default: \fB30\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
|
@ -208,6 +212,13 @@ The fraction of the number of requests within standard deviation
|
|||
range (mean +/\- sd) against total number of successful requests.
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.SH FLOW CONTROL
|
||||
.sp
|
||||
h2load sets large flow control window by default, and effectively
|
||||
disables flow control to avoid under utilization of server
|
||||
performance. To set smaller flow control window, use \fI\%\-w\fP and
|
||||
\fI\%\-W\fP options. For example, use \fB\-w16 \-W16\fP to set default
|
||||
window size described in HTTP/2 and SPDY protocol specification.
|
||||
.SH SEE ALSO
|
||||
.sp
|
||||
\fInghttp(1)\fP, \fInghttpd(1)\fP, \fInghttpx(1)\fP
|
||||
|
|
|
@ -67,6 +67,8 @@ OPTIONS
|
|||
Sets the stream level initial window size to (2\*\*<N>)-1.
|
||||
For SPDY, 2**<N> is used instead.
|
||||
|
||||
Default: ``30``
|
||||
|
||||
.. option:: -W, --connection-window-bits=<N>
|
||||
|
||||
Sets the connection level initial window size to
|
||||
|
@ -74,6 +76,8 @@ OPTIONS
|
|||
this option is ignored. Otherwise 2\*\*<N> is used for
|
||||
SPDY.
|
||||
|
||||
Default: ``30``
|
||||
|
||||
.. option:: -H, --header=<HEADER>
|
||||
|
||||
Add/Override a header to the requests.
|
||||
|
@ -154,6 +158,15 @@ time for request
|
|||
The fraction of the number of requests within standard deviation
|
||||
range (mean +/- sd) against total number of successful requests.
|
||||
|
||||
FLOW CONTROL
|
||||
------------
|
||||
|
||||
h2load sets large flow control window by default, and effectively
|
||||
disables flow control to avoid under utilization of server
|
||||
performance. To set smaller flow control window, use :option:`-w` and
|
||||
:option:`-W` options. For example, use ``-w16 -W16`` to set default
|
||||
window size described in HTTP/2 and SPDY protocol specification.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
|
|
|
@ -49,6 +49,15 @@ time for request
|
|||
The fraction of the number of requests within standard deviation
|
||||
range (mean +/- sd) against total number of successful requests.
|
||||
|
||||
FLOW CONTROL
|
||||
------------
|
||||
|
||||
h2load sets large flow control window by default, and effectively
|
||||
disables flow control to avoid under utilization of server
|
||||
performance. To set smaller flow control window, use :option:`-w` and
|
||||
:option:`-W` options. For example, use ``-w16 -W16`` to set default
|
||||
window size described in HTTP/2 and SPDY protocol specification.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTP" "1" "April 19, 2015" "0.7.12" "nghttp2"
|
||||
.TH "NGHTTP" "1" "April 27, 2015" "0.7.13" "nghttp2"
|
||||
.SH NAME
|
||||
nghttp \- HTTP/2 experimental client
|
||||
.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPD" "1" "April 19, 2015" "0.7.12" "nghttp2"
|
||||
.TH "NGHTTPD" "1" "April 27, 2015" "0.7.13" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpd \- HTTP/2 experimental server
|
||||
.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPX" "1" "April 19, 2015" "0.7.12" "nghttp2"
|
||||
.TH "NGHTTPX" "1" "April 27, 2015" "0.7.13" "nghttp2"
|
||||
.SH NAME
|
||||
nghttpx \- HTTP/2 experimental proxy
|
||||
.
|
||||
|
|
|
@ -50,8 +50,9 @@ Flow Control
|
|||
------------
|
||||
|
||||
HTTP/2 and SPDY/3 or later employ flow control and it may affect
|
||||
benchmarking results. To adjust receiver flow control window size,
|
||||
there is following options:
|
||||
benchmarking results. By default, h2load uses large enough flow
|
||||
control window, which effectively disables flow control. To adjust
|
||||
receiver flow control window size, there are following options:
|
||||
|
||||
``-w``
|
||||
Sets the stream level initial window size to
|
||||
|
|
|
@ -258,8 +258,7 @@ static int on_data_chunk_recv_callback(nghttp2_session *session _U_,
|
|||
stream), if it is closed, we send GOAWAY and tear down the
|
||||
session */
|
||||
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code,
|
||||
void *user_data) {
|
||||
uint32_t error_code, void *user_data) {
|
||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||
int rv;
|
||||
|
||||
|
|
|
@ -432,6 +432,24 @@ ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) {
|
|||
return (ssize_t)len;
|
||||
}
|
||||
|
||||
size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out) {
|
||||
size_t len;
|
||||
nghttp2_buf_chain *chain;
|
||||
nghttp2_buf *buf;
|
||||
nghttp2_buf resbuf;
|
||||
|
||||
len = nghttp2_bufs_len(bufs);
|
||||
|
||||
nghttp2_buf_wrap_init(&resbuf, out, len);
|
||||
|
||||
for (chain = bufs->head; chain; chain = chain->next) {
|
||||
buf = &chain->buf;
|
||||
resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf));
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void nghttp2_bufs_reset(nghttp2_bufs *bufs) {
|
||||
nghttp2_buf_chain *chain, *ci;
|
||||
size_t k;
|
||||
|
|
|
@ -324,6 +324,17 @@ int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b);
|
|||
*/
|
||||
ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out);
|
||||
|
||||
/*
|
||||
* Copies all data stored in |bufs| to |out|. This function assumes
|
||||
* that the buffer space pointed by |out| has at least
|
||||
* nghttp2_bufs(bufs) bytes.
|
||||
*
|
||||
* The contents of |bufs| is left unchanged.
|
||||
*
|
||||
* This function returns the length of copied data.
|
||||
*/
|
||||
size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out);
|
||||
|
||||
/*
|
||||
* Resets |bufs| and makes the buffers empty.
|
||||
*/
|
||||
|
|
|
@ -1730,6 +1730,42 @@ static int hd_inflate_remove_bufs(nghttp2_hd_inflater *inflater, nghttp2_nv *nv,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int hd_inflate_remove_bufs_with_name(nghttp2_hd_inflater *inflater,
|
||||
nghttp2_nv *nv,
|
||||
nghttp2_hd_entry *ent_name) {
|
||||
size_t rv;
|
||||
size_t buflen;
|
||||
uint8_t *buf;
|
||||
nghttp2_mem *mem;
|
||||
|
||||
mem = inflater->ctx.mem;
|
||||
|
||||
/* Allocate buffer including name in ent_name, plus terminating
|
||||
NULL. */
|
||||
buflen = ent_name->nv.namelen + 1 + nghttp2_bufs_len(&inflater->nvbufs);
|
||||
|
||||
buf = nghttp2_mem_malloc(mem, buflen);
|
||||
if (buf == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
/* Copy including terminal NULL */
|
||||
memcpy(buf, ent_name->nv.name, ent_name->nv.namelen + 1);
|
||||
rv = nghttp2_bufs_remove_copy(&inflater->nvbufs,
|
||||
buf + ent_name->nv.namelen + 1);
|
||||
assert(ent_name->nv.namelen + 1 + rv == buflen);
|
||||
|
||||
nghttp2_bufs_reset(&inflater->nvbufs);
|
||||
|
||||
nv->name = buf;
|
||||
nv->namelen = ent_name->nv.namelen;
|
||||
|
||||
nv->value = buf + nv->namelen + 1;
|
||||
nv->valuelen = buflen - nv->namelen - 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finalize literal header representation - new name- reception. If
|
||||
* header is emitted, |*nv_out| is filled with that value and 0 is
|
||||
|
@ -1813,11 +1849,6 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater,
|
|||
|
||||
mem = inflater->ctx.mem;
|
||||
|
||||
rv = hd_inflate_remove_bufs(inflater, &nv, 1 /* value only */);
|
||||
if (rv != 0) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
if (inflater->no_index) {
|
||||
nv.flags = NGHTTP2_NV_FLAG_NO_INDEX;
|
||||
} else {
|
||||
|
@ -1826,31 +1857,33 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater,
|
|||
|
||||
ent_name = nghttp2_hd_table_get(&inflater->ctx, inflater->index);
|
||||
|
||||
nv.name = ent_name->nv.name;
|
||||
nv.namelen = ent_name->nv.namelen;
|
||||
|
||||
if (inflater->index_required) {
|
||||
nghttp2_hd_entry *new_ent;
|
||||
uint8_t ent_flags;
|
||||
int static_name;
|
||||
|
||||
if (inflater->index < NGHTTP2_STATIC_TABLE_LENGTH) {
|
||||
/* We don't copy name in static table */
|
||||
rv = hd_inflate_remove_bufs(inflater, &nv, 1 /* value only */);
|
||||
if (rv != 0) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
nv.name = ent_name->nv.name;
|
||||
nv.namelen = ent_name->nv.namelen;
|
||||
|
||||
ent_flags = NGHTTP2_HD_FLAG_VALUE_ALLOC | NGHTTP2_HD_FLAG_VALUE_GIFT;
|
||||
static_name = inflater->index < NGHTTP2_STATIC_TABLE_LENGTH;
|
||||
|
||||
if (!static_name) {
|
||||
ent_flags |= NGHTTP2_HD_FLAG_NAME_ALLOC;
|
||||
/* For entry in static table, we must not touch ref, because it
|
||||
is shared by threads */
|
||||
++ent_name->ref;
|
||||
} else {
|
||||
rv = hd_inflate_remove_bufs_with_name(inflater, &nv, ent_name);
|
||||
if (rv != 0) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
/* nv->name and nv->value are in the same buffer. */
|
||||
ent_flags = NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_NAME_GIFT;
|
||||
}
|
||||
|
||||
new_ent = add_hd_table_incremental(&inflater->ctx, &nv, ent_name->token,
|
||||
ent_flags);
|
||||
|
||||
if (!static_name && --ent_name->ref == 0) {
|
||||
nghttp2_hd_entry_free(ent_name, mem);
|
||||
nghttp2_mem_free(mem, ent_name);
|
||||
}
|
||||
/* At this point, ent_name might be deleted. */
|
||||
|
||||
if (new_ent) {
|
||||
emit_indexed_header(nv_out, token_out, new_ent);
|
||||
|
@ -1865,6 +1898,14 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater,
|
|||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
rv = hd_inflate_remove_bufs(inflater, &nv, 1 /* value only */);
|
||||
if (rv != 0) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
nv.name = ent_name->nv.name;
|
||||
nv.namelen = ent_name->nv.namelen;
|
||||
|
||||
emit_literal_header(nv_out, token_out, &nv);
|
||||
|
||||
if (nv.value != inflater->nvbufs.head->buf.pos) {
|
||||
|
|
|
@ -168,10 +168,27 @@ void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) {
|
|||
ctx->accept = 1;
|
||||
}
|
||||
|
||||
/* Use macro to make the code simpler..., but error case is tricky.
|
||||
We spent most of the CPU in decoding, so we are doing this
|
||||
thing. */
|
||||
#define hd_huff_decode_sym_emit(bufs, sym, avail) \
|
||||
do { \
|
||||
if ((avail)) { \
|
||||
nghttp2_bufs_fast_addb((bufs), (sym)); \
|
||||
--(avail); \
|
||||
} else { \
|
||||
rv = nghttp2_bufs_addb((bufs), (sym)); \
|
||||
if (rv != 0) { \
|
||||
return rv; \
|
||||
} \
|
||||
(avail) = nghttp2_bufs_cur_avail((bufs)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
|
||||
nghttp2_bufs *bufs, const uint8_t *src,
|
||||
size_t srclen, int final) {
|
||||
size_t i, j;
|
||||
size_t i;
|
||||
int rv;
|
||||
size_t avail;
|
||||
|
||||
|
@ -180,30 +197,28 @@ ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
|
|||
/* We use the decoding algorithm described in
|
||||
http://graphics.ics.uci.edu/pub/Prefix.pdf */
|
||||
for (i = 0; i < srclen; ++i) {
|
||||
uint8_t in = src[i] >> 4;
|
||||
for (j = 0; j < 2; ++j) {
|
||||
const nghttp2_huff_decode *t;
|
||||
|
||||
t = &huff_decode_table[ctx->state][in];
|
||||
t = &huff_decode_table[ctx->state][src[i] >> 4];
|
||||
if (t->flags & NGHTTP2_HUFF_FAIL) {
|
||||
return NGHTTP2_ERR_HEADER_COMP;
|
||||
}
|
||||
if (t->flags & NGHTTP2_HUFF_SYM) {
|
||||
if (avail) {
|
||||
nghttp2_bufs_fast_addb(bufs, t->sym);
|
||||
--avail;
|
||||
} else {
|
||||
rv = nghttp2_bufs_addb(bufs, t->sym);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
/* this is macro, and may return from this function on error */
|
||||
hd_huff_decode_sym_emit(bufs, t->sym, avail);
|
||||
}
|
||||
avail = nghttp2_bufs_cur_avail(bufs);
|
||||
|
||||
t = &huff_decode_table[t->state][src[i] & 0xf];
|
||||
if (t->flags & NGHTTP2_HUFF_FAIL) {
|
||||
return NGHTTP2_ERR_HEADER_COMP;
|
||||
}
|
||||
if (t->flags & NGHTTP2_HUFF_SYM) {
|
||||
/* this is macro, and may return from this function on error */
|
||||
hd_huff_decode_sym_emit(bufs, t->sym, avail);
|
||||
}
|
||||
|
||||
ctx->state = t->state;
|
||||
ctx->accept = (t->flags & NGHTTP2_HUFF_ACCEPTED) != 0;
|
||||
in = src[i] & 0xf;
|
||||
}
|
||||
}
|
||||
if (final && !ctx->accept) {
|
||||
return NGHTTP2_ERR_HEADER_COMP;
|
||||
|
|
|
@ -39,7 +39,8 @@
|
|||
} while (0)
|
||||
#endif
|
||||
|
||||
typedef int (*nghttp2_compar)(const void *lhs, const void *rhs);
|
||||
/* "less" function, return nonzero if |lhs| is less than |rhs|. */
|
||||
typedef int (*nghttp2_less)(const void *lhs, const void *rhs);
|
||||
|
||||
/* Internal error code. They must be in the range [-499, -100],
|
||||
inclusive. */
|
||||
|
|
|
@ -25,6 +25,15 @@
|
|||
#include "nghttp2_outbound_item.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
void nghttp2_outbound_item_init(nghttp2_outbound_item *item) {
|
||||
item->cycle = 0;
|
||||
item->qnext = NULL;
|
||||
item->queued = 0;
|
||||
|
||||
memset(&item->aux_data, 0, sizeof(nghttp2_aux_data));
|
||||
}
|
||||
|
||||
void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) {
|
||||
nghttp2_frame *frame;
|
||||
|
@ -65,3 +74,32 @@ void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q) {
|
||||
q->head = q->tail = NULL;
|
||||
q->n = 0;
|
||||
}
|
||||
|
||||
void nghttp2_outbound_queue_push(nghttp2_outbound_queue *q,
|
||||
nghttp2_outbound_item *item) {
|
||||
if (q->tail) {
|
||||
q->tail = q->tail->qnext = item;
|
||||
} else {
|
||||
q->head = q->tail = item;
|
||||
}
|
||||
++q->n;
|
||||
}
|
||||
|
||||
void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q) {
|
||||
nghttp2_outbound_item *item;
|
||||
if (!q->head) {
|
||||
return;
|
||||
}
|
||||
item = q->head;
|
||||
q->head = q->head->qnext;
|
||||
item->qnext = NULL;
|
||||
if (!q->head) {
|
||||
q->tail = NULL;
|
||||
}
|
||||
--q->n;
|
||||
}
|
||||
|
|
|
@ -33,13 +33,6 @@
|
|||
#include "nghttp2_frame.h"
|
||||
#include "nghttp2_mem.h"
|
||||
|
||||
/* A bit higher priority for non-DATA frames */
|
||||
#define NGHTTP2_OB_EX_CYCLE 2
|
||||
/* Even more higher priority for SETTINGS frame */
|
||||
#define NGHTTP2_OB_SETTINGS_CYCLE 1
|
||||
/* Highest priority for PING frame */
|
||||
#define NGHTTP2_OB_PING_CYCLE 0
|
||||
|
||||
/* struct used for HEADERS and PUSH_PROMISE frame */
|
||||
typedef struct {
|
||||
nghttp2_data_provider data_prd;
|
||||
|
@ -104,10 +97,12 @@ typedef union {
|
|||
nghttp2_goaway_aux_data goaway;
|
||||
} nghttp2_aux_data;
|
||||
|
||||
typedef struct {
|
||||
struct nghttp2_outbound_item;
|
||||
typedef struct nghttp2_outbound_item nghttp2_outbound_item;
|
||||
|
||||
struct nghttp2_outbound_item {
|
||||
nghttp2_frame frame;
|
||||
nghttp2_aux_data aux_data;
|
||||
int64_t seq;
|
||||
/* The priority used in priority comparion. Smaller is served
|
||||
ealier. For PING, SETTINGS and non-DATA frames (excluding
|
||||
response HEADERS frame) have dedicated cycle value defined above.
|
||||
|
@ -116,9 +111,17 @@ typedef struct {
|
|||
that the amount of transmission is distributed across streams
|
||||
proportional to effective weight (inside a tree). */
|
||||
uint64_t cycle;
|
||||
nghttp2_outbound_item *qnext;
|
||||
/* nonzero if this object is queued. */
|
||||
uint8_t queued;
|
||||
} nghttp2_outbound_item;
|
||||
};
|
||||
|
||||
/*
|
||||
* Initializes |item|. No memory allocation is done in this function.
|
||||
* Don't call nghttp2_outbound_item_free() until frame member is
|
||||
* initialized.
|
||||
*/
|
||||
void nghttp2_outbound_item_init(nghttp2_outbound_item *item);
|
||||
|
||||
/*
|
||||
* Deallocates resource for |item|. If |item| is NULL, this function
|
||||
|
@ -126,4 +129,29 @@ typedef struct {
|
|||
*/
|
||||
void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem);
|
||||
|
||||
/*
|
||||
* queue for nghttp2_outbound_item.
|
||||
*/
|
||||
typedef struct {
|
||||
nghttp2_outbound_item *head, *tail;
|
||||
/* number of items in this queue. */
|
||||
size_t n;
|
||||
} nghttp2_outbound_queue;
|
||||
|
||||
void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q);
|
||||
|
||||
/* Pushes |item| into |q| */
|
||||
void nghttp2_outbound_queue_push(nghttp2_outbound_queue *q,
|
||||
nghttp2_outbound_item *item);
|
||||
|
||||
/* Pops |item| at the top from |q|. If |q| is empty, nothing
|
||||
happens. */
|
||||
void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q);
|
||||
|
||||
/* Returns the top item. */
|
||||
#define nghttp2_outbound_queue_top(Q) ((Q)->head)
|
||||
|
||||
/* Returns the size of the queue */
|
||||
#define nghttp2_outbound_queue_size(Q) ((Q)->n)
|
||||
|
||||
#endif /* NGHTTP2_OUTBOUND_ITEM_H */
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
*/
|
||||
#include "nghttp2_pq.h"
|
||||
|
||||
int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_compar compar, nghttp2_mem *mem) {
|
||||
int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem) {
|
||||
pq->mem = mem;
|
||||
pq->capacity = 128;
|
||||
pq->q = nghttp2_mem_malloc(mem, pq->capacity * sizeof(void *));
|
||||
|
@ -32,7 +32,7 @@ int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_compar compar, nghttp2_mem *mem) {
|
|||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
pq->length = 0;
|
||||
pq->compar = compar;
|
||||
pq->less = less;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ static void bubble_up(nghttp2_pq *pq, size_t index) {
|
|||
return;
|
||||
} else {
|
||||
size_t parent = (index - 1) / 2;
|
||||
if (pq->compar(pq->q[parent], pq->q[index]) > 0) {
|
||||
if (pq->less(pq->q[index], pq->q[parent])) {
|
||||
swap(pq, parent, index);
|
||||
bubble_up(pq, parent);
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ static void bubble_down(nghttp2_pq *pq, size_t index) {
|
|||
if (j >= pq->length) {
|
||||
break;
|
||||
}
|
||||
if (pq->compar(pq->q[minindex], pq->q[j]) > 0) {
|
||||
if (pq->less(pq->q[j], pq->q[minindex])) {
|
||||
minindex = j;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,8 +45,8 @@ typedef struct {
|
|||
/* The maximum number of items this pq can store. This is
|
||||
automatically extended when length is reached to this value. */
|
||||
size_t capacity;
|
||||
/* The compare function between items */
|
||||
nghttp2_compar compar;
|
||||
/* The less function between items */
|
||||
nghttp2_less less;
|
||||
} nghttp2_pq;
|
||||
|
||||
/*
|
||||
|
@ -58,7 +58,7 @@ typedef struct {
|
|||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_compar cmp, nghttp2_mem *mem);
|
||||
int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem);
|
||||
|
||||
/*
|
||||
* Deallocates any resources allocated for |pq|. The stored items are
|
||||
|
|
|
@ -223,17 +223,13 @@ nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session,
|
|||
return (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id);
|
||||
}
|
||||
|
||||
static int outbound_item_compar(const void *lhsx, const void *rhsx) {
|
||||
static int outbound_item_less(const void *lhsx, const void *rhsx) {
|
||||
const nghttp2_outbound_item *lhs, *rhs;
|
||||
|
||||
lhs = (const nghttp2_outbound_item *)lhsx;
|
||||
rhs = (const nghttp2_outbound_item *)rhsx;
|
||||
|
||||
if (lhs->cycle == rhs->cycle) {
|
||||
return (lhs->seq < rhs->seq) ? -1 : ((lhs->seq > rhs->seq) ? 1 : 0);
|
||||
}
|
||||
|
||||
return (lhs->cycle < rhs->cycle) ? -1 : 1;
|
||||
return (lhs->cycle < rhs->cycle) ? 1 : 0;
|
||||
}
|
||||
|
||||
static void session_inbound_frame_reset(nghttp2_session *session) {
|
||||
|
@ -335,15 +331,7 @@ static int session_new(nghttp2_session **session_ptr,
|
|||
/* next_stream_id is initialized in either
|
||||
nghttp2_session_client_new2 or nghttp2_session_server_new2 */
|
||||
|
||||
rv = nghttp2_pq_init(&(*session_ptr)->ob_pq, outbound_item_compar, mem);
|
||||
if (rv != 0) {
|
||||
goto fail_ob_pq;
|
||||
}
|
||||
rv = nghttp2_pq_init(&(*session_ptr)->ob_ss_pq, outbound_item_compar, mem);
|
||||
if (rv != 0) {
|
||||
goto fail_ob_ss_pq;
|
||||
}
|
||||
rv = nghttp2_pq_init(&(*session_ptr)->ob_da_pq, outbound_item_compar, mem);
|
||||
rv = nghttp2_pq_init(&(*session_ptr)->ob_da_pq, outbound_item_less, mem);
|
||||
if (rv != 0) {
|
||||
goto fail_ob_da_pq;
|
||||
}
|
||||
|
@ -363,11 +351,6 @@ static int session_new(nghttp2_session **session_ptr,
|
|||
|
||||
nghttp2_stream_roots_init(&(*session_ptr)->roots);
|
||||
|
||||
(*session_ptr)->next_seq = 0;
|
||||
/* Do +1 so that any HEADERS/DATA frames are scheduled after urgent
|
||||
frames. */
|
||||
(*session_ptr)->last_cycle = NGHTTP2_OB_EX_CYCLE + 1;
|
||||
|
||||
(*session_ptr)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
|
||||
(*session_ptr)->recv_window_size = 0;
|
||||
(*session_ptr)->consumed_size = 0;
|
||||
|
@ -462,10 +445,6 @@ fail_hd_inflater:
|
|||
fail_hd_deflater:
|
||||
nghttp2_pq_free(&(*session_ptr)->ob_da_pq);
|
||||
fail_ob_da_pq:
|
||||
nghttp2_pq_free(&(*session_ptr)->ob_ss_pq);
|
||||
fail_ob_ss_pq:
|
||||
nghttp2_pq_free(&(*session_ptr)->ob_pq);
|
||||
fail_ob_pq:
|
||||
nghttp2_mem_free(mem, *session_ptr);
|
||||
fail_session:
|
||||
return rv;
|
||||
|
@ -571,6 +550,16 @@ static void ob_pq_free(nghttp2_pq *pq, nghttp2_mem *mem) {
|
|||
nghttp2_pq_free(pq);
|
||||
}
|
||||
|
||||
static void ob_q_free(nghttp2_outbound_queue *q, nghttp2_mem *mem) {
|
||||
nghttp2_outbound_item *item, *next;
|
||||
for (item = q->head; item;) {
|
||||
next = item->qnext;
|
||||
nghttp2_outbound_item_free(item, mem);
|
||||
nghttp2_mem_free(mem, item);
|
||||
item = next;
|
||||
}
|
||||
}
|
||||
|
||||
void nghttp2_session_del(nghttp2_session *session) {
|
||||
nghttp2_mem *mem;
|
||||
|
||||
|
@ -589,8 +578,9 @@ void nghttp2_session_del(nghttp2_session *session) {
|
|||
nghttp2_map_each_free(&session->streams, free_streams, session);
|
||||
nghttp2_map_free(&session->streams);
|
||||
|
||||
ob_pq_free(&session->ob_pq, mem);
|
||||
ob_pq_free(&session->ob_ss_pq, mem);
|
||||
ob_q_free(&session->ob_urgent, mem);
|
||||
ob_q_free(&session->ob_reg, mem);
|
||||
ob_q_free(&session->ob_syn, mem);
|
||||
ob_pq_free(&session->ob_da_pq, mem);
|
||||
active_outbound_item_reset(&session->aob, mem);
|
||||
session_inbound_frame_reset(session);
|
||||
|
@ -696,15 +686,6 @@ nghttp2_session_reprioritize_stream(nghttp2_session *session,
|
|||
return 0;
|
||||
}
|
||||
|
||||
void nghttp2_session_outbound_item_init(nghttp2_session *session,
|
||||
nghttp2_outbound_item *item) {
|
||||
item->seq = session->next_seq++;
|
||||
item->cycle = NGHTTP2_OB_EX_CYCLE;
|
||||
item->queued = 0;
|
||||
|
||||
memset(&item->aux_data, 0, sizeof(nghttp2_aux_data));
|
||||
}
|
||||
|
||||
int nghttp2_session_add_item(nghttp2_session *session,
|
||||
nghttp2_outbound_item *item) {
|
||||
/* TODO Return error if stream is not found for the frame requiring
|
||||
|
@ -719,65 +700,45 @@ int nghttp2_session_add_item(nghttp2_session *session,
|
|||
if (frame->hd.type != NGHTTP2_DATA) {
|
||||
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_RST_STREAM:
|
||||
if (stream) {
|
||||
stream->state = NGHTTP2_STREAM_CLOSING;
|
||||
}
|
||||
|
||||
break;
|
||||
case NGHTTP2_SETTINGS:
|
||||
item->cycle = NGHTTP2_OB_SETTINGS_CYCLE;
|
||||
|
||||
break;
|
||||
case NGHTTP2_PING:
|
||||
/* Ping has highest priority. */
|
||||
item->cycle = NGHTTP2_OB_PING_CYCLE;
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (frame->hd.type == NGHTTP2_HEADERS) {
|
||||
case NGHTTP2_HEADERS:
|
||||
/* We push request HEADERS and push response HEADERS to
|
||||
dedicated queue because their transmission is affected by
|
||||
SETTINGS_MAX_CONCURRENT_STREAMS */
|
||||
/* TODO If 2 HEADERS are submitted for reserved stream, then
|
||||
both of them are queued into ob_ss_pq, which is not
|
||||
both of them are queued into ob_syn, which is not
|
||||
desirable. */
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
|
||||
rv = nghttp2_pq_push(&session->ob_ss_pq, item);
|
||||
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
nghttp2_outbound_queue_push(&session->ob_syn, item);
|
||||
item->queued = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
item->queued = 1;
|
||||
} else if (stream && (stream->state == NGHTTP2_STREAM_RESERVED ||
|
||||
if (stream && (stream->state == NGHTTP2_STREAM_RESERVED ||
|
||||
item->aux_data.headers.attach_stream)) {
|
||||
item->cycle = session->last_cycle;
|
||||
|
||||
rv = nghttp2_stream_attach_item(stream, item, session);
|
||||
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
} else {
|
||||
rv = nghttp2_pq_push(&session->ob_pq, item);
|
||||
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
break;
|
||||
}
|
||||
|
||||
nghttp2_outbound_queue_push(&session->ob_reg, item);
|
||||
item->queued = 1;
|
||||
break;
|
||||
case NGHTTP2_SETTINGS:
|
||||
case NGHTTP2_PING:
|
||||
nghttp2_outbound_queue_push(&session->ob_urgent, item);
|
||||
item->queued = 1;
|
||||
break;
|
||||
case NGHTTP2_RST_STREAM:
|
||||
if (stream) {
|
||||
stream->state = NGHTTP2_STREAM_CLOSING;
|
||||
}
|
||||
} else {
|
||||
rv = nghttp2_pq_push(&session->ob_pq, item);
|
||||
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* fall through */
|
||||
default:
|
||||
nghttp2_outbound_queue_push(&session->ob_reg, item);
|
||||
item->queued = 1;
|
||||
}
|
||||
|
||||
|
@ -792,8 +753,6 @@ int nghttp2_session_add_item(nghttp2_session *session,
|
|||
return NGHTTP2_ERR_DATA_EXIST;
|
||||
}
|
||||
|
||||
item->cycle = session->last_cycle;
|
||||
|
||||
rv = nghttp2_stream_attach_item(stream, item, session);
|
||||
|
||||
if (rv != 0) {
|
||||
|
@ -803,30 +762,6 @@ int nghttp2_session_add_item(nghttp2_session *session,
|
|||
return 0;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int32_t stream_id;
|
||||
uint32_t error_code;
|
||||
} nghttp2_rst_target;
|
||||
|
||||
static int cancel_pending_request(void *pq_item, void *arg) {
|
||||
nghttp2_outbound_item *item;
|
||||
nghttp2_rst_target *t;
|
||||
nghttp2_headers_aux_data *aux_data;
|
||||
|
||||
item = pq_item;
|
||||
t = arg;
|
||||
aux_data = &item->aux_data.headers;
|
||||
|
||||
if (item->frame.hd.stream_id != t->stream_id || aux_data->canceled) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
aux_data->error_code = t->error_code;
|
||||
aux_data->canceled = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code) {
|
||||
int rv;
|
||||
|
@ -834,7 +769,6 @@ int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
|
|||
nghttp2_frame *frame;
|
||||
nghttp2_stream *stream;
|
||||
nghttp2_mem *mem;
|
||||
nghttp2_rst_target t = {stream_id, error_code};
|
||||
|
||||
mem = &session->mem;
|
||||
stream = nghttp2_session_get_stream(session, stream_id);
|
||||
|
@ -842,21 +776,35 @@ int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Cancel pending request HEADERS in ob_ss_pq if this RST_STREAM
|
||||
/* Cancel pending request HEADERS in ob_syn if this RST_STREAM
|
||||
refers to that stream. */
|
||||
if (!session->server && nghttp2_session_is_my_stream_id(session, stream_id) &&
|
||||
nghttp2_pq_top(&session->ob_ss_pq)) {
|
||||
nghttp2_outbound_item *top;
|
||||
nghttp2_outbound_queue_top(&session->ob_syn)) {
|
||||
nghttp2_headers_aux_data *aux_data;
|
||||
nghttp2_frame *headers_frame;
|
||||
|
||||
top = nghttp2_pq_top(&session->ob_ss_pq);
|
||||
headers_frame = &top->frame;
|
||||
|
||||
headers_frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame;
|
||||
assert(headers_frame->hd.type == NGHTTP2_HEADERS);
|
||||
|
||||
if (headers_frame->hd.stream_id <= stream_id &&
|
||||
(uint32_t)stream_id < session->next_stream_id) {
|
||||
if (nghttp2_pq_each(&session->ob_ss_pq, cancel_pending_request, &t)) {
|
||||
|
||||
for (item = session->ob_syn.head; item; item = item->qnext) {
|
||||
aux_data = &item->aux_data.headers;
|
||||
|
||||
if (item->frame.hd.stream_id < stream_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* stream_id in ob_syn queue must be strictly increasing. If
|
||||
we found larger ID, then we can break here. */
|
||||
if (item->frame.hd.stream_id > stream_id || aux_data->canceled) {
|
||||
break;
|
||||
}
|
||||
|
||||
aux_data->error_code = error_code;
|
||||
aux_data->canceled = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -867,7 +815,7 @@ int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
|
|||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
frame = &item->frame;
|
||||
|
||||
|
@ -2030,123 +1978,66 @@ static int session_prep_frame(nghttp2_session *session,
|
|||
}
|
||||
}
|
||||
|
||||
/* Used only for tests */
|
||||
nghttp2_outbound_item *nghttp2_session_get_ob_pq_top(nghttp2_session *session) {
|
||||
return (nghttp2_outbound_item *)nghttp2_pq_top(&session->ob_pq);
|
||||
}
|
||||
|
||||
nghttp2_outbound_item *
|
||||
nghttp2_session_get_next_ob_item(nghttp2_session *session) {
|
||||
nghttp2_outbound_item *item, *headers_item;
|
||||
|
||||
if (nghttp2_pq_empty(&session->ob_pq)) {
|
||||
if (nghttp2_pq_empty(&session->ob_ss_pq)) {
|
||||
if (session->remote_window_size == 0 ||
|
||||
nghttp2_pq_empty(&session->ob_da_pq)) {
|
||||
return NULL;
|
||||
if (nghttp2_outbound_queue_top(&session->ob_urgent)) {
|
||||
return nghttp2_outbound_queue_top(&session->ob_urgent);
|
||||
}
|
||||
|
||||
if (nghttp2_outbound_queue_top(&session->ob_reg)) {
|
||||
return nghttp2_outbound_queue_top(&session->ob_reg);
|
||||
}
|
||||
|
||||
if (!session_is_outgoing_concurrent_streams_max(session)) {
|
||||
if (nghttp2_outbound_queue_top(&session->ob_syn)) {
|
||||
return nghttp2_outbound_queue_top(&session->ob_syn);
|
||||
}
|
||||
}
|
||||
|
||||
if (session->remote_window_size > 0 &&
|
||||
!nghttp2_pq_empty(&session->ob_da_pq)) {
|
||||
return nghttp2_pq_top(&session->ob_da_pq);
|
||||
}
|
||||
|
||||
/* Return item only when concurrent connection limit is not
|
||||
reached */
|
||||
if (session_is_outgoing_concurrent_streams_max(session)) {
|
||||
if (session->remote_window_size == 0 ||
|
||||
nghttp2_pq_empty(&session->ob_da_pq)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return nghttp2_pq_top(&session->ob_da_pq);
|
||||
}
|
||||
|
||||
return nghttp2_pq_top(&session->ob_ss_pq);
|
||||
}
|
||||
|
||||
if (nghttp2_pq_empty(&session->ob_ss_pq)) {
|
||||
return nghttp2_pq_top(&session->ob_pq);
|
||||
}
|
||||
|
||||
item = nghttp2_pq_top(&session->ob_pq);
|
||||
headers_item = nghttp2_pq_top(&session->ob_ss_pq);
|
||||
|
||||
if (session_is_outgoing_concurrent_streams_max(session) ||
|
||||
outbound_item_compar(item, headers_item) < 0) {
|
||||
return item;
|
||||
}
|
||||
|
||||
return headers_item;
|
||||
}
|
||||
|
||||
nghttp2_outbound_item *
|
||||
nghttp2_session_pop_next_ob_item(nghttp2_session *session) {
|
||||
nghttp2_outbound_item *item, *headers_item;
|
||||
nghttp2_outbound_item *item;
|
||||
|
||||
if (nghttp2_pq_empty(&session->ob_pq)) {
|
||||
if (nghttp2_pq_empty(&session->ob_ss_pq)) {
|
||||
if (session->remote_window_size == 0 ||
|
||||
nghttp2_pq_empty(&session->ob_da_pq)) {
|
||||
return NULL;
|
||||
item = nghttp2_outbound_queue_top(&session->ob_urgent);
|
||||
if (item) {
|
||||
nghttp2_outbound_queue_pop(&session->ob_urgent);
|
||||
item->queued = 0;
|
||||
return item;
|
||||
}
|
||||
|
||||
item = nghttp2_outbound_queue_top(&session->ob_reg);
|
||||
if (item) {
|
||||
nghttp2_outbound_queue_pop(&session->ob_reg);
|
||||
item->queued = 0;
|
||||
return item;
|
||||
}
|
||||
|
||||
if (!session_is_outgoing_concurrent_streams_max(session)) {
|
||||
item = nghttp2_outbound_queue_top(&session->ob_syn);
|
||||
if (item) {
|
||||
nghttp2_outbound_queue_pop(&session->ob_syn);
|
||||
item->queued = 0;
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
if (session->remote_window_size > 0 &&
|
||||
!nghttp2_pq_empty(&session->ob_da_pq)) {
|
||||
item = nghttp2_pq_top(&session->ob_da_pq);
|
||||
nghttp2_pq_pop(&session->ob_da_pq);
|
||||
|
||||
item->queued = 0;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
/* Pop item only when concurrent connection limit is not
|
||||
reached */
|
||||
if (session_is_outgoing_concurrent_streams_max(session)) {
|
||||
if (session->remote_window_size == 0 ||
|
||||
nghttp2_pq_empty(&session->ob_da_pq)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
item = nghttp2_pq_top(&session->ob_da_pq);
|
||||
nghttp2_pq_pop(&session->ob_da_pq);
|
||||
|
||||
item->queued = 0;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
item = nghttp2_pq_top(&session->ob_ss_pq);
|
||||
nghttp2_pq_pop(&session->ob_ss_pq);
|
||||
|
||||
item->queued = 0;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
if (nghttp2_pq_empty(&session->ob_ss_pq)) {
|
||||
item = nghttp2_pq_top(&session->ob_pq);
|
||||
nghttp2_pq_pop(&session->ob_pq);
|
||||
|
||||
item->queued = 0;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
item = nghttp2_pq_top(&session->ob_pq);
|
||||
headers_item = nghttp2_pq_top(&session->ob_ss_pq);
|
||||
|
||||
if (session_is_outgoing_concurrent_streams_max(session) ||
|
||||
outbound_item_compar(item, headers_item) < 0) {
|
||||
nghttp2_pq_pop(&session->ob_pq);
|
||||
|
||||
item->queued = 0;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
nghttp2_pq_pop(&session->ob_ss_pq);
|
||||
|
||||
headers_item->queued = 0;
|
||||
|
||||
return headers_item;
|
||||
}
|
||||
|
||||
static int session_call_before_frame_send(nghttp2_session *session,
|
||||
|
@ -2587,7 +2478,8 @@ static int session_after_frame_sent2(nghttp2_session *session) {
|
|||
waiting at the top of the queue, we continue to send this
|
||||
data. */
|
||||
if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP &&
|
||||
(next_item == NULL || outbound_item_compar(item, next_item) < 0)) {
|
||||
(next_item == NULL || (next_item->frame.hd.type == NGHTTP2_DATA &&
|
||||
outbound_item_less(item, next_item)))) {
|
||||
size_t next_readmax;
|
||||
|
||||
next_readmax = nghttp2_session_next_data_read(session, stream);
|
||||
|
@ -3291,12 +3183,12 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
|
|||
}
|
||||
if (proclen < 0) {
|
||||
if (session->iframe.state == NGHTTP2_IB_READ_HEADER_BLOCK) {
|
||||
if (stream && stream->state != NGHTTP2_STREAM_CLOSING) {
|
||||
if (subject_stream && subject_stream->state != NGHTTP2_STREAM_CLOSING) {
|
||||
/* Adding RST_STREAM here is very important. It prevents
|
||||
from invoking subsequent callbacks for the same stream
|
||||
ID. */
|
||||
rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id,
|
||||
NGHTTP2_COMPRESSION_ERROR);
|
||||
rv = nghttp2_session_add_rst_stream(
|
||||
session, subject_stream->stream_id, NGHTTP2_COMPRESSION_ERROR);
|
||||
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
|
@ -6046,10 +5938,12 @@ int nghttp2_session_want_write(nghttp2_session *session) {
|
|||
* want to write them.
|
||||
*/
|
||||
|
||||
if (session->aob.item == NULL && nghttp2_pq_empty(&session->ob_pq) &&
|
||||
if (session->aob.item == NULL &&
|
||||
nghttp2_outbound_queue_top(&session->ob_urgent) == NULL &&
|
||||
nghttp2_outbound_queue_top(&session->ob_reg) == NULL &&
|
||||
(nghttp2_pq_empty(&session->ob_da_pq) ||
|
||||
session->remote_window_size == 0) &&
|
||||
(nghttp2_pq_empty(&session->ob_ss_pq) ||
|
||||
(nghttp2_outbound_queue_top(&session->ob_syn) == NULL ||
|
||||
session_is_outgoing_concurrent_streams_max(session))) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -6073,7 +5967,7 @@ int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags,
|
|||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
frame = &item->frame;
|
||||
|
||||
|
@ -6122,7 +6016,7 @@ int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id,
|
|||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
frame = &item->frame;
|
||||
|
||||
|
@ -6159,7 +6053,7 @@ int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags,
|
|||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
frame = &item->frame;
|
||||
|
||||
|
@ -6230,7 +6124,7 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
|
|||
session->inflight_niv = niv;
|
||||
}
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
frame = &item->frame;
|
||||
|
||||
|
@ -6450,8 +6344,9 @@ int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) {
|
|||
}
|
||||
|
||||
size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session) {
|
||||
return nghttp2_pq_size(&session->ob_pq) +
|
||||
nghttp2_pq_size(&session->ob_ss_pq) +
|
||||
return nghttp2_outbound_queue_size(&session->ob_urgent) +
|
||||
nghttp2_outbound_queue_size(&session->ob_reg) +
|
||||
nghttp2_outbound_queue_size(&session->ob_syn) +
|
||||
nghttp2_pq_size(&session->ob_da_pq);
|
||||
}
|
||||
|
||||
|
|
|
@ -143,12 +143,15 @@ typedef enum {
|
|||
struct nghttp2_session {
|
||||
nghttp2_map /* <nghttp2_stream*> */ streams;
|
||||
nghttp2_stream_roots roots;
|
||||
/* Queue for outbound frames other than stream-creating HEADERS and
|
||||
DATA */
|
||||
nghttp2_pq /* <nghttp2_outbound_item*> */ ob_pq;
|
||||
/* Queue for outbound stream-creating HEADERS frame */
|
||||
nghttp2_pq /* <nghttp2_outbound_item*> */ ob_ss_pq;
|
||||
/* QUeue for DATA frame */
|
||||
/* Queue for outbound urgent frames (PING and SETTINGS) */
|
||||
nghttp2_outbound_queue ob_urgent;
|
||||
/* Queue for non-DATA frames */
|
||||
nghttp2_outbound_queue ob_reg;
|
||||
/* Queue for outbound stream-creating HEADERS (request or push
|
||||
response) frame, which are subject to
|
||||
SETTINGS_MAX_CONCURRENT_STREAMS limit. */
|
||||
nghttp2_outbound_queue ob_syn;
|
||||
/* Queue for DATA frame */
|
||||
nghttp2_pq /* <nghttp2_outbound_item*> */ ob_da_pq;
|
||||
nghttp2_active_outbound_item aob;
|
||||
nghttp2_inbound_frame iframe;
|
||||
|
@ -157,22 +160,8 @@ struct nghttp2_session {
|
|||
nghttp2_session_callbacks callbacks;
|
||||
/* Memory allocator */
|
||||
nghttp2_mem mem;
|
||||
/* Sequence number of outbound frame to maintain the order of
|
||||
enqueue if priority is equal. */
|
||||
int64_t next_seq;
|
||||
/* Reset count of nghttp2_outbound_item's weight. We decrements
|
||||
weight each time DATA is sent to simulate resource sharing. We
|
||||
use priority queue and larger weight has the precedence. If
|
||||
weight is reached to lowest weight, it resets to its initial
|
||||
weight. If this happens, other items which have the lower weight
|
||||
currently but same initial weight cannot send DATA until item
|
||||
having large weight is decreased. To avoid this, we use this
|
||||
cycle variable. Initally, this is set to 1. If weight gets
|
||||
lowest weight, and if item's cycle == last_cycle, we increments
|
||||
last_cycle and assigns it to item's cycle. Otherwise, just
|
||||
assign last_cycle. In priority queue comparator, we first
|
||||
compare items' cycle value. Lower cycle value has the
|
||||
precedence. */
|
||||
/* Base value when we schedule next DATA frame write. This is
|
||||
updated when one frame was written. */
|
||||
uint64_t last_cycle;
|
||||
void *user_data;
|
||||
/* Points to the latest closed stream. NULL if there is no closed
|
||||
|
@ -292,14 +281,6 @@ typedef struct {
|
|||
int nghttp2_session_is_my_stream_id(nghttp2_session *session,
|
||||
int32_t stream_id);
|
||||
|
||||
/*
|
||||
* Initializes |item|. No memory allocation is done in this function.
|
||||
* Don't call nghttp2_outbound_item_free() until frame member is
|
||||
* initialized.
|
||||
*/
|
||||
void nghttp2_session_outbound_item_init(nghttp2_session *session,
|
||||
nghttp2_outbound_item *item);
|
||||
|
||||
/*
|
||||
* Adds |item| to the outbound queue in |session|. When this function
|
||||
* succeeds, it takes ownership of |item|. So caller must not free it
|
||||
|
@ -708,12 +689,6 @@ int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
|
|||
nghttp2_data_aux_data *aux_data,
|
||||
nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Returns top of outbound frame queue. This function returns NULL if
|
||||
* queue is empty.
|
||||
*/
|
||||
nghttp2_outbound_item *nghttp2_session_get_ob_pq_top(nghttp2_session *session);
|
||||
|
||||
/*
|
||||
* Pops and returns next item to send. If there is no such item,
|
||||
* returns NULL. This function takes into account max concurrent
|
||||
|
|
|
@ -101,22 +101,26 @@ static int stream_push_item(nghttp2_stream *stream, nghttp2_session *session) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
switch (item->frame.hd.type) {
|
||||
case NGHTTP2_DATA:
|
||||
/* Penalize item by delaying scheduling according to effective
|
||||
weight. This will delay low priority stream, which is good.
|
||||
OTOH, this may incur delay for high priority item. Will see. */
|
||||
OTOH, this may incur delay for high priority item. Will
|
||||
see. */
|
||||
item->cycle =
|
||||
session->last_cycle +
|
||||
NGHTTP2_DATA_PAYLOADLEN * NGHTTP2_MAX_WEIGHT / stream->effective_weight;
|
||||
|
||||
switch (item->frame.hd.type) {
|
||||
case NGHTTP2_DATA:
|
||||
rv = nghttp2_pq_push(&session->ob_da_pq, item);
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_HEADERS:
|
||||
if (stream->state == NGHTTP2_STREAM_RESERVED) {
|
||||
rv = nghttp2_pq_push(&session->ob_ss_pq, item);
|
||||
nghttp2_outbound_queue_push(&session->ob_syn, item);
|
||||
} else {
|
||||
rv = nghttp2_pq_push(&session->ob_pq, item);
|
||||
nghttp2_outbound_queue_push(&session->ob_reg, item);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -124,10 +128,6 @@ static int stream_push_item(nghttp2_stream *stream, nghttp2_session *session) {
|
|||
assert(0);
|
||||
}
|
||||
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
item->queued = 1;
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -62,7 +62,7 @@ static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
if (data_prd != NULL && data_prd->read_callback != NULL) {
|
||||
item->aux_data.headers.data_prd = *data_prd;
|
||||
|
@ -212,7 +212,7 @@ int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags _U_,
|
|||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
frame = &item->frame;
|
||||
|
||||
|
@ -299,7 +299,7 @@ int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags _U_,
|
|||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
item->aux_data.headers.stream_user_data = promised_stream_user_data;
|
||||
|
||||
|
@ -444,7 +444,7 @@ int nghttp2_submit_data(nghttp2_session *session, uint8_t flags,
|
|||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
frame = &item->frame;
|
||||
aux_data = &item->aux_data.data;
|
||||
|
|
|
@ -156,15 +156,9 @@ void server::start_accept(tcp::acceptor &acceptor, serve_mux &mux) {
|
|||
});
|
||||
}
|
||||
|
||||
void server::stop()
|
||||
{
|
||||
io_service_pool_.stop();
|
||||
}
|
||||
void server::stop() { io_service_pool_.stop(); }
|
||||
|
||||
void server::join()
|
||||
{
|
||||
io_service_pool_.join();
|
||||
}
|
||||
void server::join() { io_service_pool_.join(); }
|
||||
|
||||
} // namespace server
|
||||
} // namespace asio_http2
|
||||
|
|
|
@ -59,11 +59,9 @@ boost::system::error_code http2::listen_and_serve(boost::system::error_code &ec,
|
|||
return impl_->listen_and_serve(ec, nullptr, address, port, asynchronous);
|
||||
}
|
||||
|
||||
boost::system::error_code
|
||||
http2::listen_and_serve(boost::system::error_code &ec,
|
||||
boost::asio::ssl::context &tls_context,
|
||||
const std::string &address, const std::string &port,
|
||||
bool asynchronous) {
|
||||
boost::system::error_code http2::listen_and_serve(
|
||||
boost::system::error_code &ec, boost::asio::ssl::context &tls_context,
|
||||
const std::string &address, const std::string &port, bool asynchronous) {
|
||||
return impl_->listen_and_serve(ec, &tls_context, address, port, asynchronous);
|
||||
}
|
||||
|
||||
|
@ -75,15 +73,9 @@ bool http2::handle(std::string pattern, request_cb cb) {
|
|||
return impl_->handle(std::move(pattern), std::move(cb));
|
||||
}
|
||||
|
||||
void http2::stop()
|
||||
{
|
||||
impl_->stop();
|
||||
}
|
||||
void http2::stop() { impl_->stop(); }
|
||||
|
||||
void http2::join()
|
||||
{
|
||||
return impl_->join();
|
||||
}
|
||||
void http2::join() { return impl_->join(); }
|
||||
|
||||
} // namespace server
|
||||
|
||||
|
|
|
@ -43,7 +43,8 @@ boost::system::error_code http2_impl::listen_and_serve(
|
|||
boost::system::error_code &ec, boost::asio::ssl::context *tls_context,
|
||||
const std::string &address, const std::string &port, bool asynchronous) {
|
||||
server_.reset(new server(num_threads_));
|
||||
return server_->listen_and_serve(ec, tls_context, address, port, backlog_, mux_, asynchronous);
|
||||
return server_->listen_and_serve(ec, tls_context, address, port, backlog_,
|
||||
mux_, asynchronous);
|
||||
}
|
||||
|
||||
void http2_impl::num_threads(size_t num_threads) { num_threads_ = num_threads; }
|
||||
|
@ -54,14 +55,9 @@ bool http2_impl::handle(std::string pattern, request_cb cb) {
|
|||
return mux_.handle(std::move(pattern), std::move(cb));
|
||||
}
|
||||
|
||||
void http2_impl::stop() {
|
||||
return server_->stop();
|
||||
}
|
||||
void http2_impl::stop() { return server_->stop(); }
|
||||
|
||||
void http2_impl::join()
|
||||
{
|
||||
return server_->join();
|
||||
}
|
||||
void http2_impl::join() { return server_->join(); }
|
||||
|
||||
} // namespace server
|
||||
|
||||
|
|
|
@ -42,11 +42,9 @@ class server;
|
|||
class http2_impl {
|
||||
public:
|
||||
http2_impl();
|
||||
boost::system::error_code
|
||||
listen_and_serve(boost::system::error_code &ec,
|
||||
boost::asio::ssl::context *tls_context,
|
||||
const std::string &address, const std::string &port,
|
||||
bool asynchronous);
|
||||
boost::system::error_code listen_and_serve(
|
||||
boost::system::error_code &ec, boost::asio::ssl::context *tls_context,
|
||||
const std::string &address, const std::string &port, bool asynchronous);
|
||||
void num_threads(size_t num_threads);
|
||||
void backlog(int backlog);
|
||||
bool handle(std::string pattern, request_cb cb);
|
||||
|
|
|
@ -69,7 +69,7 @@ namespace h2load {
|
|||
|
||||
Config::Config()
|
||||
: data_length(-1), addrs(nullptr), nreqs(1), nclients(1), nthreads(1),
|
||||
max_concurrent_streams(-1), window_bits(16), connection_window_bits(16),
|
||||
max_concurrent_streams(-1), window_bits(30), connection_window_bits(30),
|
||||
no_tls_proto(PROTO_HTTP2), data_fd(-1), port(0), default_port(0),
|
||||
verbose(false) {}
|
||||
|
||||
|
@ -974,11 +974,13 @@ Options:
|
|||
-w, --window-bits=<N>
|
||||
Sets the stream level initial window size to (2**<N>)-1.
|
||||
For SPDY, 2**<N> is used instead.
|
||||
Default: )" << config.window_bits << R"(
|
||||
-W, --connection-window-bits=<N>
|
||||
Sets the connection level initial window size to
|
||||
(2**<N>)-1. For SPDY, if <N> is strictly less than 16,
|
||||
this option is ignored. Otherwise 2**<N> is used for
|
||||
SPDY.
|
||||
Default: )" << config.connection_window_bits << R"(
|
||||
-H, --header=<HEADER>
|
||||
Add/Override a header to the requests.
|
||||
-p, --no-tls-proto=<PROTOID>
|
||||
|
|
|
@ -2312,8 +2312,9 @@ Options:
|
|||
filename is dereived from URI. If URI ends with '/',
|
||||
'index.html' is used as a filename. Not implemented
|
||||
yet.
|
||||
-t, --timeout=<SEC>
|
||||
Timeout each request after <SEC> seconds.
|
||||
-t, --timeout=<DURATION>
|
||||
Timeout each request after <DURATION>. Set 0 to disable
|
||||
timeout.
|
||||
-w, --window-bits=<N>
|
||||
Sets the stream level initial window size to 2**<N>-1.
|
||||
-W, --connection-window-bits=<N>
|
||||
|
@ -2383,7 +2384,12 @@ Options:
|
|||
--
|
||||
|
||||
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||
10 * 1024). Units are K, M and G (powers of 1024).)" << std::endl;
|
||||
10 * 1024). Units are K, M and G (powers of 1024).
|
||||
|
||||
The <DURATION> argument is an integer and an optional unit (e.g., 1s
|
||||
is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms
|
||||
(hours, minutes, seconds and milliseconds, respectively). If a unit
|
||||
is omitted, a second is used as unit.)" << std::endl;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
@ -2469,7 +2475,11 @@ int main(int argc, char **argv) {
|
|||
++config.verbose;
|
||||
break;
|
||||
case 't':
|
||||
config.timeout = atoi(optarg);
|
||||
config.timeout = util::parse_duration_with_unit(optarg);
|
||||
if (config.timeout == std::numeric_limits<double>::infinity()) {
|
||||
std::cerr << "-t: bad timeout value: " << optarg << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
config.upgrade = true;
|
||||
|
|
|
@ -28,14 +28,14 @@
|
|||
|
||||
#include "nghttp2_pq.h"
|
||||
|
||||
static int pq_compar(const void *lhs, const void *rhs) {
|
||||
return strcmp(lhs, rhs);
|
||||
static int pq_less(const void *lhs, const void *rhs) {
|
||||
return strcmp(lhs, rhs) < 0;
|
||||
}
|
||||
|
||||
void test_nghttp2_pq(void) {
|
||||
int i;
|
||||
nghttp2_pq pq;
|
||||
nghttp2_pq_init(&pq, pq_compar, nghttp2_mem_default());
|
||||
nghttp2_pq_init(&pq, pq_less, nghttp2_mem_default());
|
||||
CU_ASSERT(nghttp2_pq_empty(&pq));
|
||||
CU_ASSERT(0 == nghttp2_pq_size(&pq));
|
||||
CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"foo"));
|
||||
|
@ -80,10 +80,10 @@ typedef struct {
|
|||
int val;
|
||||
} node;
|
||||
|
||||
static int node_compar(const void *lhs, const void *rhs) {
|
||||
static int node_less(const void *lhs, const void *rhs) {
|
||||
node *ln = (node *)lhs;
|
||||
node *rn = (node *)rhs;
|
||||
return ln->key - rn->key;
|
||||
return ln->key < rn->key;
|
||||
}
|
||||
|
||||
static int node_update(void *item, void *arg _U_) {
|
||||
|
@ -103,7 +103,7 @@ void test_nghttp2_pq_update(void) {
|
|||
node *nd;
|
||||
int ans[] = {-8, -6, -4, -2, 0, 1, 3, 5, 7, 9};
|
||||
|
||||
nghttp2_pq_init(&pq, node_compar, nghttp2_mem_default());
|
||||
nghttp2_pq_init(&pq, node_less, nghttp2_mem_default());
|
||||
|
||||
for (i = 0; i < (int)(sizeof(nodes) / sizeof(nodes[0])); ++i) {
|
||||
nodes[i].key = i;
|
||||
|
|
|
@ -1095,9 +1095,6 @@ void test_nghttp2_session_recv_headers_with_priority(void) {
|
|||
void test_nghttp2_session_recv_premature_headers(void) {
|
||||
nghttp2_session *session;
|
||||
nghttp2_session_callbacks callbacks;
|
||||
nghttp2_nv *nva;
|
||||
size_t nvlen;
|
||||
nghttp2_frame frame;
|
||||
nghttp2_bufs bufs;
|
||||
nghttp2_buf *buf;
|
||||
ssize_t rv;
|
||||
|
@ -1105,45 +1102,72 @@ void test_nghttp2_session_recv_premature_headers(void) {
|
|||
nghttp2_hd_deflater deflater;
|
||||
nghttp2_outbound_item *item;
|
||||
nghttp2_mem *mem;
|
||||
uint32_t payloadlen;
|
||||
|
||||
mem = nghttp2_mem_default();
|
||||
frame_pack_bufs_init(&bufs);
|
||||
|
||||
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
|
||||
callbacks.send_callback = null_send_callback;
|
||||
|
||||
nghttp2_session_server_new(&session, &callbacks, &ud);
|
||||
|
||||
nghttp2_hd_deflate_init(&deflater, mem);
|
||||
|
||||
nvlen = ARRLEN(reqnv);
|
||||
nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem);
|
||||
nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1,
|
||||
NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen);
|
||||
rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
|
||||
|
||||
CU_ASSERT(0 == rv);
|
||||
CU_ASSERT(nghttp2_bufs_len(&bufs) > 0);
|
||||
|
||||
nghttp2_frame_headers_free(&frame.headers, mem);
|
||||
rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, reqnv,
|
||||
ARRLEN(reqnv), mem);
|
||||
|
||||
buf = &bufs.head->buf;
|
||||
assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf));
|
||||
|
||||
/* Intentionally feed payload cutting last 1 byte off */
|
||||
nghttp2_put_uint32be(buf->pos,
|
||||
(uint32_t)(((frame.hd.length - 1) << 8) + buf->pos[3]));
|
||||
payloadlen = nghttp2_get_uint32(buf->pos) >> 8;
|
||||
nghttp2_put_uint32be(buf->pos, ((payloadlen - 1) << 8) + buf->pos[3]);
|
||||
rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf) - 1);
|
||||
|
||||
CU_ASSERT((ssize_t)(nghttp2_buf_len(buf) - 1) == rv);
|
||||
CU_ASSERT(rv == nghttp2_buf_len(buf) - 1);
|
||||
|
||||
item = nghttp2_session_get_next_ob_item(session);
|
||||
|
||||
CU_ASSERT(NULL != item);
|
||||
CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
|
||||
CU_ASSERT(NGHTTP2_COMPRESSION_ERROR == item->frame.rst_stream.error_code);
|
||||
CU_ASSERT(1 == item->frame.hd.stream_id);
|
||||
CU_ASSERT(0 == nghttp2_session_send(session));
|
||||
|
||||
nghttp2_bufs_free(&bufs);
|
||||
nghttp2_bufs_reset(&bufs);
|
||||
nghttp2_hd_deflate_free(&deflater);
|
||||
nghttp2_session_del(session);
|
||||
|
||||
/* Test for PUSH_PROMISE */
|
||||
nghttp2_session_client_new(&session, &callbacks, &ud);
|
||||
nghttp2_hd_deflate_init(&deflater, mem);
|
||||
|
||||
nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE,
|
||||
&pri_spec_default, NGHTTP2_STREAM_OPENING, NULL);
|
||||
|
||||
rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 2,
|
||||
reqnv, ARRLEN(reqnv), mem);
|
||||
|
||||
CU_ASSERT(0 == rv);
|
||||
|
||||
buf = &bufs.head->buf;
|
||||
payloadlen = nghttp2_get_uint32(buf->pos) >> 8;
|
||||
/* Intentionally feed payload cutting last 1 byte off */
|
||||
nghttp2_put_uint32be(buf->pos, ((payloadlen - 1) << 8) + buf->pos[3]);
|
||||
rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf) - 1);
|
||||
|
||||
CU_ASSERT(rv == nghttp2_buf_len(buf) - 1);
|
||||
|
||||
item = nghttp2_session_get_next_ob_item(session);
|
||||
|
||||
CU_ASSERT(NULL != item);
|
||||
CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
|
||||
CU_ASSERT(NGHTTP2_COMPRESSION_ERROR == item->frame.rst_stream.error_code);
|
||||
CU_ASSERT(2 == item->frame.hd.stream_id);
|
||||
CU_ASSERT(0 == nghttp2_session_send(session));
|
||||
|
||||
nghttp2_hd_deflate_free(&deflater);
|
||||
nghttp2_session_del(session);
|
||||
nghttp2_bufs_free(&bufs);
|
||||
}
|
||||
|
||||
void test_nghttp2_session_recv_unknown_frame(void) {
|
||||
|
@ -1629,7 +1653,7 @@ void test_nghttp2_session_add_frame(void) {
|
|||
|
||||
item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
frame = &item->frame;
|
||||
|
||||
|
@ -1643,7 +1667,7 @@ void test_nghttp2_session_add_frame(void) {
|
|||
session->next_stream_id += 2;
|
||||
|
||||
CU_ASSERT(0 == nghttp2_session_add_item(session, item));
|
||||
CU_ASSERT(0 == nghttp2_pq_empty(&session->ob_ss_pq));
|
||||
CU_ASSERT(NULL != nghttp2_outbound_queue_top(&session->ob_syn));
|
||||
CU_ASSERT(0 == nghttp2_session_send(session));
|
||||
CU_ASSERT(NGHTTP2_HEADERS == acc.buf[3]);
|
||||
CU_ASSERT((NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY) == acc.buf[4]);
|
||||
|
@ -2486,16 +2510,16 @@ void test_nghttp2_session_on_ping_received(void) {
|
|||
CU_ASSERT(0 == nghttp2_session_on_ping_received(session, &frame));
|
||||
CU_ASSERT(1 == user_data.frame_recv_cb_called);
|
||||
|
||||
/* Since this ping frame has PONG flag set, no further action is
|
||||
/* Since this ping frame has ACK flag set, no further action is
|
||||
performed. */
|
||||
CU_ASSERT(NULL == nghttp2_session_get_ob_pq_top(session));
|
||||
CU_ASSERT(NULL == nghttp2_outbound_queue_top(&session->ob_urgent));
|
||||
|
||||
/* Clear the flag, and receive it again */
|
||||
frame.hd.flags = NGHTTP2_FLAG_NONE;
|
||||
|
||||
CU_ASSERT(0 == nghttp2_session_on_ping_received(session, &frame));
|
||||
CU_ASSERT(2 == user_data.frame_recv_cb_called);
|
||||
top = nghttp2_session_get_ob_pq_top(session);
|
||||
top = nghttp2_outbound_queue_top(&session->ob_urgent);
|
||||
CU_ASSERT(NGHTTP2_PING == top->frame.hd.type);
|
||||
CU_ASSERT(NGHTTP2_FLAG_ACK == top->frame.hd.flags);
|
||||
CU_ASSERT(memcmp(opaque_data, top->frame.ping.opaque_data, 8) == 0);
|
||||
|
@ -2666,7 +2690,7 @@ void test_nghttp2_session_on_data_received(void) {
|
|||
frame.hd.stream_id = 4;
|
||||
|
||||
CU_ASSERT(0 == nghttp2_session_on_data_received(session, &frame));
|
||||
CU_ASSERT(NULL == nghttp2_session_get_ob_pq_top(session));
|
||||
CU_ASSERT(NULL == nghttp2_outbound_queue_top(&session->ob_reg));
|
||||
|
||||
/* Check INVALID_STREAM case: DATA frame with stream ID which does
|
||||
not exist. */
|
||||
|
@ -2674,7 +2698,7 @@ void test_nghttp2_session_on_data_received(void) {
|
|||
frame.hd.stream_id = 6;
|
||||
|
||||
CU_ASSERT(0 == nghttp2_session_on_data_received(session, &frame));
|
||||
top = nghttp2_session_get_ob_pq_top(session);
|
||||
top = nghttp2_outbound_queue_top(&session->ob_reg);
|
||||
/* DATA against nonexistent stream is just ignored for now */
|
||||
CU_ASSERT(top == NULL);
|
||||
/* CU_ASSERT(NGHTTP2_RST_STREAM == top->frame.hd.type); */
|
||||
|
@ -2700,7 +2724,7 @@ void test_nghttp2_session_send_headers_start_stream(void) {
|
|||
|
||||
item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
frame = &item->frame;
|
||||
|
||||
|
@ -2736,7 +2760,7 @@ void test_nghttp2_session_send_headers_reply(void) {
|
|||
|
||||
item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
frame = &item->frame;
|
||||
|
||||
|
@ -2786,7 +2810,7 @@ void test_nghttp2_session_send_headers_frame_size_error(void) {
|
|||
|
||||
item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
frame = &item->frame;
|
||||
|
||||
|
@ -2831,7 +2855,7 @@ void test_nghttp2_session_send_headers_push_reply(void) {
|
|||
|
||||
item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
frame = &item->frame;
|
||||
|
||||
|
@ -2865,7 +2889,7 @@ void test_nghttp2_session_send_rst_stream(void) {
|
|||
|
||||
item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
frame = &item->frame;
|
||||
|
||||
|
@ -2899,7 +2923,7 @@ void test_nghttp2_session_send_push_promise(void) {
|
|||
|
||||
item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
frame = &item->frame;
|
||||
|
||||
|
@ -2927,7 +2951,7 @@ void test_nghttp2_session_send_push_promise(void) {
|
|||
|
||||
item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
frame = &item->frame;
|
||||
|
||||
|
@ -2950,7 +2974,7 @@ void test_nghttp2_session_send_push_promise(void) {
|
|||
&pri_spec_default, NGHTTP2_STREAM_OPENING, NULL);
|
||||
item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
|
||||
|
||||
nghttp2_session_outbound_item_init(session, item);
|
||||
nghttp2_outbound_item_init(item);
|
||||
|
||||
frame = &item->frame;
|
||||
|
||||
|
@ -4515,7 +4539,7 @@ void test_nghttp2_session_pop_next_ob_item(void) {
|
|||
CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 2,
|
||||
NULL, NULL, 0, NULL));
|
||||
CU_ASSERT(NULL == nghttp2_session_pop_next_ob_item(session));
|
||||
CU_ASSERT(1 == nghttp2_pq_size(&session->ob_ss_pq));
|
||||
CU_ASSERT(1 == nghttp2_outbound_queue_size(&session->ob_syn));
|
||||
nghttp2_session_del(session);
|
||||
}
|
||||
|
||||
|
@ -4561,7 +4585,7 @@ void test_nghttp2_session_max_concurrent_streams(void) {
|
|||
CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
|
||||
nghttp2_session_on_request_headers_received(session, &frame));
|
||||
|
||||
item = nghttp2_session_get_ob_pq_top(session);
|
||||
item = nghttp2_outbound_queue_top(&session->ob_reg);
|
||||
CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
|
||||
CU_ASSERT(NGHTTP2_REFUSED_STREAM == item->frame.rst_stream.error_code);
|
||||
|
||||
|
@ -4574,7 +4598,7 @@ void test_nghttp2_session_max_concurrent_streams(void) {
|
|||
CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
|
||||
nghttp2_session_on_request_headers_received(session, &frame));
|
||||
|
||||
item = nghttp2_session_get_ob_pq_top(session);
|
||||
item = nghttp2_outbound_queue_top(&session->ob_reg);
|
||||
CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
|
||||
CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code);
|
||||
|
||||
|
@ -6935,7 +6959,7 @@ void test_nghttp2_session_cancel_reserved_remote(void) {
|
|||
|
||||
/* No RST_STREAM or GOAWAY is generated since stream should be in
|
||||
NGHTTP2_STREAM_CLOSING and push response should be ignored. */
|
||||
CU_ASSERT(0 == nghttp2_pq_size(&session->ob_pq));
|
||||
CU_ASSERT(0 == nghttp2_outbound_queue_size(&session->ob_reg));
|
||||
|
||||
/* Check that we can receive push response HEADERS while RST_STREAM
|
||||
is just queued. */
|
||||
|
@ -6958,7 +6982,7 @@ void test_nghttp2_session_cancel_reserved_remote(void) {
|
|||
|
||||
CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv);
|
||||
|
||||
CU_ASSERT(1 == nghttp2_pq_size(&session->ob_pq));
|
||||
CU_ASSERT(1 == nghttp2_outbound_queue_size(&session->ob_reg));
|
||||
|
||||
nghttp2_frame_headers_free(&frame.headers, mem);
|
||||
|
||||
|
|
Loading…
Reference in New Issue