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
|
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
|
||||||
|
|
||||||
AC_PREREQ(2.61)
|
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_PREREQ([2.2.6])
|
||||||
LT_INIT()
|
LT_INIT()
|
||||||
dnl See versioning rule:
|
dnl See versioning rule:
|
||||||
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||||
AC_SUBST(LT_CURRENT, 13)
|
AC_SUBST(LT_CURRENT, 13)
|
||||||
AC_SUBST(LT_REVISION, 1)
|
AC_SUBST(LT_REVISION, 2)
|
||||||
AC_SUBST(LT_AGE, 8)
|
AC_SUBST(LT_AGE, 8)
|
||||||
|
|
||||||
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
|
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 " linkcheck to check all external links for integrity"
|
||||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
@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
|
$(top_builddir)/lib/includes/nghttp2/nghttp2.h
|
||||||
$(PYTHON) $(top_srcdir)/doc/mkapiref.py \
|
$(PYTHON) $(top_srcdir)/doc/mkapiref.py \
|
||||||
$@ macros.rst enums.rst types.rst . $^
|
$@ 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:
|
clean-local:
|
||||||
-rm $(APIDOCS)
|
-rm $(APIDOCS)
|
||||||
-rm -rf $(BUILDDIR)/*
|
-rm -rf $(BUILDDIR)/*
|
||||||
|
|
13
doc/h2load.1
13
doc/h2load.1
|
@ -1,6 +1,6 @@
|
||||||
.\" Man page generated from reStructuredText.
|
.\" 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
|
.SH NAME
|
||||||
h2load \- HTTP/2 benchmarking tool
|
h2load \- HTTP/2 benchmarking tool
|
||||||
.
|
.
|
||||||
|
@ -93,6 +93,8 @@ Default: \fBauto\fP
|
||||||
.B \-w, \-\-window\-bits=<N>
|
.B \-w, \-\-window\-bits=<N>
|
||||||
Sets the stream level initial window size to (2**<N>)\-1.
|
Sets the stream level initial window size to (2**<N>)\-1.
|
||||||
For SPDY, 2**<N> is used instead.
|
For SPDY, 2**<N> is used instead.
|
||||||
|
.sp
|
||||||
|
Default: \fB30\fP
|
||||||
.UNINDENT
|
.UNINDENT
|
||||||
.INDENT 0.0
|
.INDENT 0.0
|
||||||
.TP
|
.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,
|
(2**<N>)\-1. For SPDY, if <N> is strictly less than 16,
|
||||||
this option is ignored. Otherwise 2**<N> is used for
|
this option is ignored. Otherwise 2**<N> is used for
|
||||||
SPDY.
|
SPDY.
|
||||||
|
.sp
|
||||||
|
Default: \fB30\fP
|
||||||
.UNINDENT
|
.UNINDENT
|
||||||
.INDENT 0.0
|
.INDENT 0.0
|
||||||
.TP
|
.TP
|
||||||
|
@ -208,6 +212,13 @@ The fraction of the number of requests within standard deviation
|
||||||
range (mean +/\- sd) against total number of successful requests.
|
range (mean +/\- sd) against total number of successful requests.
|
||||||
.UNINDENT
|
.UNINDENT
|
||||||
.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
|
.SH SEE ALSO
|
||||||
.sp
|
.sp
|
||||||
\fInghttp(1)\fP, \fInghttpd(1)\fP, \fInghttpx(1)\fP
|
\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.
|
Sets the stream level initial window size to (2\*\*<N>)-1.
|
||||||
For SPDY, 2**<N> is used instead.
|
For SPDY, 2**<N> is used instead.
|
||||||
|
|
||||||
|
Default: ``30``
|
||||||
|
|
||||||
.. option:: -W, --connection-window-bits=<N>
|
.. option:: -W, --connection-window-bits=<N>
|
||||||
|
|
||||||
Sets the connection level initial window size to
|
Sets the connection level initial window size to
|
||||||
|
@ -74,6 +76,8 @@ OPTIONS
|
||||||
this option is ignored. Otherwise 2\*\*<N> is used for
|
this option is ignored. Otherwise 2\*\*<N> is used for
|
||||||
SPDY.
|
SPDY.
|
||||||
|
|
||||||
|
Default: ``30``
|
||||||
|
|
||||||
.. option:: -H, --header=<HEADER>
|
.. option:: -H, --header=<HEADER>
|
||||||
|
|
||||||
Add/Override a header to the requests.
|
Add/Override a header to the requests.
|
||||||
|
@ -154,6 +158,15 @@ time for request
|
||||||
The fraction of the number of requests within standard deviation
|
The fraction of the number of requests within standard deviation
|
||||||
range (mean +/- sd) against total number of successful requests.
|
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
|
SEE ALSO
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,15 @@ time for request
|
||||||
The fraction of the number of requests within standard deviation
|
The fraction of the number of requests within standard deviation
|
||||||
range (mean +/- sd) against total number of successful requests.
|
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
|
SEE ALSO
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
.\" Man page generated from reStructuredText.
|
.\" 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
|
.SH NAME
|
||||||
nghttp \- HTTP/2 experimental client
|
nghttp \- HTTP/2 experimental client
|
||||||
.
|
.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
.\" Man page generated from reStructuredText.
|
.\" 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
|
.SH NAME
|
||||||
nghttpd \- HTTP/2 experimental server
|
nghttpd \- HTTP/2 experimental server
|
||||||
.
|
.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
.\" Man page generated from reStructuredText.
|
.\" 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
|
.SH NAME
|
||||||
nghttpx \- HTTP/2 experimental proxy
|
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
|
HTTP/2 and SPDY/3 or later employ flow control and it may affect
|
||||||
benchmarking results. To adjust receiver flow control window size,
|
benchmarking results. By default, h2load uses large enough flow
|
||||||
there is following options:
|
control window, which effectively disables flow control. To adjust
|
||||||
|
receiver flow control window size, there are following options:
|
||||||
|
|
||||||
``-w``
|
``-w``
|
||||||
Sets the stream level initial window size to
|
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
|
stream), if it is closed, we send GOAWAY and tear down the
|
||||||
session */
|
session */
|
||||||
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||||
uint32_t error_code,
|
uint32_t error_code, void *user_data) {
|
||||||
void *user_data) {
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
|
|
|
@ -432,6 +432,24 @@ ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) {
|
||||||
return (ssize_t)len;
|
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) {
|
void nghttp2_bufs_reset(nghttp2_bufs *bufs) {
|
||||||
nghttp2_buf_chain *chain, *ci;
|
nghttp2_buf_chain *chain, *ci;
|
||||||
size_t k;
|
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);
|
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.
|
* 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;
|
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
|
* Finalize literal header representation - new name- reception. If
|
||||||
* header is emitted, |*nv_out| is filled with that value and 0 is
|
* 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;
|
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) {
|
if (inflater->no_index) {
|
||||||
nv.flags = NGHTTP2_NV_FLAG_NO_INDEX;
|
nv.flags = NGHTTP2_NV_FLAG_NO_INDEX;
|
||||||
} else {
|
} 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);
|
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) {
|
if (inflater->index_required) {
|
||||||
nghttp2_hd_entry *new_ent;
|
nghttp2_hd_entry *new_ent;
|
||||||
uint8_t ent_flags;
|
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;
|
ent_flags = NGHTTP2_HD_FLAG_VALUE_ALLOC | NGHTTP2_HD_FLAG_VALUE_GIFT;
|
||||||
static_name = inflater->index < NGHTTP2_STATIC_TABLE_LENGTH;
|
} else {
|
||||||
|
rv = hd_inflate_remove_bufs_with_name(inflater, &nv, ent_name);
|
||||||
if (!static_name) {
|
if (rv != 0) {
|
||||||
ent_flags |= NGHTTP2_HD_FLAG_NAME_ALLOC;
|
return NGHTTP2_ERR_NOMEM;
|
||||||
/* For entry in static table, we must not touch ref, because it
|
}
|
||||||
is shared by threads */
|
/* nv->name and nv->value are in the same buffer. */
|
||||||
++ent_name->ref;
|
ent_flags = NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_NAME_GIFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
new_ent = add_hd_table_incremental(&inflater->ctx, &nv, ent_name->token,
|
new_ent = add_hd_table_incremental(&inflater->ctx, &nv, ent_name->token,
|
||||||
ent_flags);
|
ent_flags);
|
||||||
|
|
||||||
if (!static_name && --ent_name->ref == 0) {
|
/* At this point, ent_name might be deleted. */
|
||||||
nghttp2_hd_entry_free(ent_name, mem);
|
|
||||||
nghttp2_mem_free(mem, ent_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (new_ent) {
|
if (new_ent) {
|
||||||
emit_indexed_header(nv_out, token_out, 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;
|
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);
|
emit_literal_header(nv_out, token_out, &nv);
|
||||||
|
|
||||||
if (nv.value != inflater->nvbufs.head->buf.pos) {
|
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;
|
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,
|
ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
|
||||||
nghttp2_bufs *bufs, const uint8_t *src,
|
nghttp2_bufs *bufs, const uint8_t *src,
|
||||||
size_t srclen, int final) {
|
size_t srclen, int final) {
|
||||||
size_t i, j;
|
size_t i;
|
||||||
int rv;
|
int rv;
|
||||||
size_t avail;
|
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
|
/* We use the decoding algorithm described in
|
||||||
http://graphics.ics.uci.edu/pub/Prefix.pdf */
|
http://graphics.ics.uci.edu/pub/Prefix.pdf */
|
||||||
for (i = 0; i < srclen; ++i) {
|
for (i = 0; i < srclen; ++i) {
|
||||||
uint8_t in = src[i] >> 4;
|
|
||||||
for (j = 0; j < 2; ++j) {
|
|
||||||
const nghttp2_huff_decode *t;
|
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) {
|
if (t->flags & NGHTTP2_HUFF_FAIL) {
|
||||||
return NGHTTP2_ERR_HEADER_COMP;
|
return NGHTTP2_ERR_HEADER_COMP;
|
||||||
}
|
}
|
||||||
if (t->flags & NGHTTP2_HUFF_SYM) {
|
if (t->flags & NGHTTP2_HUFF_SYM) {
|
||||||
if (avail) {
|
/* this is macro, and may return from this function on error */
|
||||||
nghttp2_bufs_fast_addb(bufs, t->sym);
|
hd_huff_decode_sym_emit(bufs, t->sym, avail);
|
||||||
--avail;
|
|
||||||
} else {
|
|
||||||
rv = nghttp2_bufs_addb(bufs, t->sym);
|
|
||||||
if (rv != 0) {
|
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
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->state = t->state;
|
||||||
ctx->accept = (t->flags & NGHTTP2_HUFF_ACCEPTED) != 0;
|
ctx->accept = (t->flags & NGHTTP2_HUFF_ACCEPTED) != 0;
|
||||||
in = src[i] & 0xf;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (final && !ctx->accept) {
|
if (final && !ctx->accept) {
|
||||||
return NGHTTP2_ERR_HEADER_COMP;
|
return NGHTTP2_ERR_HEADER_COMP;
|
||||||
|
|
|
@ -39,7 +39,8 @@
|
||||||
} while (0)
|
} while (0)
|
||||||
#endif
|
#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],
|
/* Internal error code. They must be in the range [-499, -100],
|
||||||
inclusive. */
|
inclusive. */
|
||||||
|
|
|
@ -25,6 +25,15 @@
|
||||||
#include "nghttp2_outbound_item.h"
|
#include "nghttp2_outbound_item.h"
|
||||||
|
|
||||||
#include <assert.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) {
|
void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) {
|
||||||
nghttp2_frame *frame;
|
nghttp2_frame *frame;
|
||||||
|
@ -65,3 +74,32 @@ void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) {
|
||||||
break;
|
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_frame.h"
|
||||||
#include "nghttp2_mem.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 */
|
/* struct used for HEADERS and PUSH_PROMISE frame */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
nghttp2_data_provider data_prd;
|
nghttp2_data_provider data_prd;
|
||||||
|
@ -104,10 +97,12 @@ typedef union {
|
||||||
nghttp2_goaway_aux_data goaway;
|
nghttp2_goaway_aux_data goaway;
|
||||||
} nghttp2_aux_data;
|
} 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_frame frame;
|
||||||
nghttp2_aux_data aux_data;
|
nghttp2_aux_data aux_data;
|
||||||
int64_t seq;
|
|
||||||
/* The priority used in priority comparion. Smaller is served
|
/* The priority used in priority comparion. Smaller is served
|
||||||
ealier. For PING, SETTINGS and non-DATA frames (excluding
|
ealier. For PING, SETTINGS and non-DATA frames (excluding
|
||||||
response HEADERS frame) have dedicated cycle value defined above.
|
response HEADERS frame) have dedicated cycle value defined above.
|
||||||
|
@ -116,9 +111,17 @@ typedef struct {
|
||||||
that the amount of transmission is distributed across streams
|
that the amount of transmission is distributed across streams
|
||||||
proportional to effective weight (inside a tree). */
|
proportional to effective weight (inside a tree). */
|
||||||
uint64_t cycle;
|
uint64_t cycle;
|
||||||
|
nghttp2_outbound_item *qnext;
|
||||||
/* nonzero if this object is queued. */
|
/* nonzero if this object is queued. */
|
||||||
uint8_t 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
|
* 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);
|
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 */
|
#endif /* NGHTTP2_OUTBOUND_ITEM_H */
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
*/
|
*/
|
||||||
#include "nghttp2_pq.h"
|
#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->mem = mem;
|
||||||
pq->capacity = 128;
|
pq->capacity = 128;
|
||||||
pq->q = nghttp2_mem_malloc(mem, pq->capacity * sizeof(void *));
|
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;
|
return NGHTTP2_ERR_NOMEM;
|
||||||
}
|
}
|
||||||
pq->length = 0;
|
pq->length = 0;
|
||||||
pq->compar = compar;
|
pq->less = less;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ static void bubble_up(nghttp2_pq *pq, size_t index) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
size_t parent = (index - 1) / 2;
|
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);
|
swap(pq, parent, index);
|
||||||
bubble_up(pq, parent);
|
bubble_up(pq, parent);
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ static void bubble_down(nghttp2_pq *pq, size_t index) {
|
||||||
if (j >= pq->length) {
|
if (j >= pq->length) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (pq->compar(pq->q[minindex], pq->q[j]) > 0) {
|
if (pq->less(pq->q[j], pq->q[minindex])) {
|
||||||
minindex = j;
|
minindex = j;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,8 +45,8 @@ typedef struct {
|
||||||
/* The maximum number of items this pq can store. This is
|
/* The maximum number of items this pq can store. This is
|
||||||
automatically extended when length is reached to this value. */
|
automatically extended when length is reached to this value. */
|
||||||
size_t capacity;
|
size_t capacity;
|
||||||
/* The compare function between items */
|
/* The less function between items */
|
||||||
nghttp2_compar compar;
|
nghttp2_less less;
|
||||||
} nghttp2_pq;
|
} nghttp2_pq;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -58,7 +58,7 @@ typedef struct {
|
||||||
* NGHTTP2_ERR_NOMEM
|
* NGHTTP2_ERR_NOMEM
|
||||||
* Out of memory.
|
* 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
|
* 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);
|
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;
|
const nghttp2_outbound_item *lhs, *rhs;
|
||||||
|
|
||||||
lhs = (const nghttp2_outbound_item *)lhsx;
|
lhs = (const nghttp2_outbound_item *)lhsx;
|
||||||
rhs = (const nghttp2_outbound_item *)rhsx;
|
rhs = (const nghttp2_outbound_item *)rhsx;
|
||||||
|
|
||||||
if (lhs->cycle == rhs->cycle) {
|
return (lhs->cycle < rhs->cycle) ? 1 : 0;
|
||||||
return (lhs->seq < rhs->seq) ? -1 : ((lhs->seq > rhs->seq) ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (lhs->cycle < rhs->cycle) ? -1 : 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void session_inbound_frame_reset(nghttp2_session *session) {
|
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
|
/* next_stream_id is initialized in either
|
||||||
nghttp2_session_client_new2 or nghttp2_session_server_new2 */
|
nghttp2_session_client_new2 or nghttp2_session_server_new2 */
|
||||||
|
|
||||||
rv = nghttp2_pq_init(&(*session_ptr)->ob_pq, outbound_item_compar, mem);
|
rv = nghttp2_pq_init(&(*session_ptr)->ob_da_pq, outbound_item_less, 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);
|
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
goto fail_ob_da_pq;
|
goto fail_ob_da_pq;
|
||||||
}
|
}
|
||||||
|
@ -363,11 +351,6 @@ static int session_new(nghttp2_session **session_ptr,
|
||||||
|
|
||||||
nghttp2_stream_roots_init(&(*session_ptr)->roots);
|
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)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
|
||||||
(*session_ptr)->recv_window_size = 0;
|
(*session_ptr)->recv_window_size = 0;
|
||||||
(*session_ptr)->consumed_size = 0;
|
(*session_ptr)->consumed_size = 0;
|
||||||
|
@ -462,10 +445,6 @@ fail_hd_inflater:
|
||||||
fail_hd_deflater:
|
fail_hd_deflater:
|
||||||
nghttp2_pq_free(&(*session_ptr)->ob_da_pq);
|
nghttp2_pq_free(&(*session_ptr)->ob_da_pq);
|
||||||
fail_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);
|
nghttp2_mem_free(mem, *session_ptr);
|
||||||
fail_session:
|
fail_session:
|
||||||
return rv;
|
return rv;
|
||||||
|
@ -571,6 +550,16 @@ static void ob_pq_free(nghttp2_pq *pq, nghttp2_mem *mem) {
|
||||||
nghttp2_pq_free(pq);
|
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) {
|
void nghttp2_session_del(nghttp2_session *session) {
|
||||||
nghttp2_mem *mem;
|
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_each_free(&session->streams, free_streams, session);
|
||||||
nghttp2_map_free(&session->streams);
|
nghttp2_map_free(&session->streams);
|
||||||
|
|
||||||
ob_pq_free(&session->ob_pq, mem);
|
ob_q_free(&session->ob_urgent, mem);
|
||||||
ob_pq_free(&session->ob_ss_pq, mem);
|
ob_q_free(&session->ob_reg, mem);
|
||||||
|
ob_q_free(&session->ob_syn, mem);
|
||||||
ob_pq_free(&session->ob_da_pq, mem);
|
ob_pq_free(&session->ob_da_pq, mem);
|
||||||
active_outbound_item_reset(&session->aob, mem);
|
active_outbound_item_reset(&session->aob, mem);
|
||||||
session_inbound_frame_reset(session);
|
session_inbound_frame_reset(session);
|
||||||
|
@ -696,15 +686,6 @@ nghttp2_session_reprioritize_stream(nghttp2_session *session,
|
||||||
return 0;
|
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,
|
int nghttp2_session_add_item(nghttp2_session *session,
|
||||||
nghttp2_outbound_item *item) {
|
nghttp2_outbound_item *item) {
|
||||||
/* TODO Return error if stream is not found for the frame requiring
|
/* 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) {
|
if (frame->hd.type != NGHTTP2_DATA) {
|
||||||
|
|
||||||
switch (frame->hd.type) {
|
switch (frame->hd.type) {
|
||||||
case NGHTTP2_RST_STREAM:
|
case NGHTTP2_HEADERS:
|
||||||
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) {
|
|
||||||
/* We push request HEADERS and push response HEADERS to
|
/* We push request HEADERS and push response HEADERS to
|
||||||
dedicated queue because their transmission is affected by
|
dedicated queue because their transmission is affected by
|
||||||
SETTINGS_MAX_CONCURRENT_STREAMS */
|
SETTINGS_MAX_CONCURRENT_STREAMS */
|
||||||
/* TODO If 2 HEADERS are submitted for reserved stream, then
|
/* 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. */
|
desirable. */
|
||||||
if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
|
if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
|
||||||
rv = nghttp2_pq_push(&session->ob_ss_pq, item);
|
nghttp2_outbound_queue_push(&session->ob_syn, item);
|
||||||
|
item->queued = 1;
|
||||||
if (rv != 0) {
|
break;
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
item->queued = 1;
|
if (stream && (stream->state == NGHTTP2_STREAM_RESERVED ||
|
||||||
} else if (stream && (stream->state == NGHTTP2_STREAM_RESERVED ||
|
|
||||||
item->aux_data.headers.attach_stream)) {
|
item->aux_data.headers.attach_stream)) {
|
||||||
item->cycle = session->last_cycle;
|
|
||||||
|
|
||||||
rv = nghttp2_stream_attach_item(stream, item, session);
|
rv = nghttp2_stream_attach_item(stream, item, session);
|
||||||
|
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
rv = nghttp2_pq_push(&session->ob_pq, item);
|
|
||||||
|
|
||||||
if (rv != 0) {
|
break;
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nghttp2_outbound_queue_push(&session->ob_reg, item);
|
||||||
item->queued = 1;
|
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 {
|
/* fall through */
|
||||||
rv = nghttp2_pq_push(&session->ob_pq, item);
|
default:
|
||||||
|
nghttp2_outbound_queue_push(&session->ob_reg, item);
|
||||||
if (rv != 0) {
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
item->queued = 1;
|
item->queued = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -792,8 +753,6 @@ int nghttp2_session_add_item(nghttp2_session *session,
|
||||||
return NGHTTP2_ERR_DATA_EXIST;
|
return NGHTTP2_ERR_DATA_EXIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
item->cycle = session->last_cycle;
|
|
||||||
|
|
||||||
rv = nghttp2_stream_attach_item(stream, item, session);
|
rv = nghttp2_stream_attach_item(stream, item, session);
|
||||||
|
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
|
@ -803,30 +762,6 @@ int nghttp2_session_add_item(nghttp2_session *session,
|
||||||
return 0;
|
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,
|
int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
|
||||||
uint32_t error_code) {
|
uint32_t error_code) {
|
||||||
int rv;
|
int rv;
|
||||||
|
@ -834,7 +769,6 @@ int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
|
||||||
nghttp2_frame *frame;
|
nghttp2_frame *frame;
|
||||||
nghttp2_stream *stream;
|
nghttp2_stream *stream;
|
||||||
nghttp2_mem *mem;
|
nghttp2_mem *mem;
|
||||||
nghttp2_rst_target t = {stream_id, error_code};
|
|
||||||
|
|
||||||
mem = &session->mem;
|
mem = &session->mem;
|
||||||
stream = nghttp2_session_get_stream(session, stream_id);
|
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;
|
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. */
|
refers to that stream. */
|
||||||
if (!session->server && nghttp2_session_is_my_stream_id(session, stream_id) &&
|
if (!session->server && nghttp2_session_is_my_stream_id(session, stream_id) &&
|
||||||
nghttp2_pq_top(&session->ob_ss_pq)) {
|
nghttp2_outbound_queue_top(&session->ob_syn)) {
|
||||||
nghttp2_outbound_item *top;
|
nghttp2_headers_aux_data *aux_data;
|
||||||
nghttp2_frame *headers_frame;
|
nghttp2_frame *headers_frame;
|
||||||
|
|
||||||
top = nghttp2_pq_top(&session->ob_ss_pq);
|
headers_frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame;
|
||||||
headers_frame = &top->frame;
|
|
||||||
|
|
||||||
assert(headers_frame->hd.type == NGHTTP2_HEADERS);
|
assert(headers_frame->hd.type == NGHTTP2_HEADERS);
|
||||||
|
|
||||||
if (headers_frame->hd.stream_id <= stream_id &&
|
if (headers_frame->hd.stream_id <= stream_id &&
|
||||||
(uint32_t)stream_id < session->next_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;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -867,7 +815,7 @@ int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
|
||||||
return NGHTTP2_ERR_NOMEM;
|
return NGHTTP2_ERR_NOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
nghttp2_session_outbound_item_init(session, item);
|
nghttp2_outbound_item_init(item);
|
||||||
|
|
||||||
frame = &item->frame;
|
frame = &item->frame;
|
||||||
|
|
||||||
|
@ -2030,125 +1978,68 @@ 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_outbound_item *
|
||||||
nghttp2_session_get_next_ob_item(nghttp2_session *session) {
|
nghttp2_session_get_next_ob_item(nghttp2_session *session) {
|
||||||
nghttp2_outbound_item *item, *headers_item;
|
if (nghttp2_outbound_queue_top(&session->ob_urgent)) {
|
||||||
|
return nghttp2_outbound_queue_top(&session->ob_urgent);
|
||||||
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_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 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 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_outbound_item *
|
||||||
nghttp2_session_pop_next_ob_item(nghttp2_session *session) {
|
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)) {
|
item = nghttp2_outbound_queue_top(&session->ob_urgent);
|
||||||
if (nghttp2_pq_empty(&session->ob_ss_pq)) {
|
if (item) {
|
||||||
if (session->remote_window_size == 0 ||
|
nghttp2_outbound_queue_pop(&session->ob_urgent);
|
||||||
nghttp2_pq_empty(&session->ob_da_pq)) {
|
item->queued = 0;
|
||||||
return NULL;
|
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);
|
item = nghttp2_pq_top(&session->ob_da_pq);
|
||||||
nghttp2_pq_pop(&session->ob_da_pq);
|
nghttp2_pq_pop(&session->ob_da_pq);
|
||||||
|
|
||||||
item->queued = 0;
|
item->queued = 0;
|
||||||
|
|
||||||
return item;
|
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;
|
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,
|
static int session_call_before_frame_send(nghttp2_session *session,
|
||||||
nghttp2_frame *frame) {
|
nghttp2_frame *frame) {
|
||||||
int rv;
|
int rv;
|
||||||
|
@ -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
|
waiting at the top of the queue, we continue to send this
|
||||||
data. */
|
data. */
|
||||||
if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP &&
|
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;
|
size_t next_readmax;
|
||||||
|
|
||||||
next_readmax = nghttp2_session_next_data_read(session, stream);
|
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 (proclen < 0) {
|
||||||
if (session->iframe.state == NGHTTP2_IB_READ_HEADER_BLOCK) {
|
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
|
/* Adding RST_STREAM here is very important. It prevents
|
||||||
from invoking subsequent callbacks for the same stream
|
from invoking subsequent callbacks for the same stream
|
||||||
ID. */
|
ID. */
|
||||||
rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id,
|
rv = nghttp2_session_add_rst_stream(
|
||||||
NGHTTP2_COMPRESSION_ERROR);
|
session, subject_stream->stream_id, NGHTTP2_COMPRESSION_ERROR);
|
||||||
|
|
||||||
if (nghttp2_is_fatal(rv)) {
|
if (nghttp2_is_fatal(rv)) {
|
||||||
return rv;
|
return rv;
|
||||||
|
@ -6046,10 +5938,12 @@ int nghttp2_session_want_write(nghttp2_session *session) {
|
||||||
* want to write them.
|
* 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) ||
|
(nghttp2_pq_empty(&session->ob_da_pq) ||
|
||||||
session->remote_window_size == 0) &&
|
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))) {
|
session_is_outgoing_concurrent_streams_max(session))) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -6073,7 +5967,7 @@ int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags,
|
||||||
return NGHTTP2_ERR_NOMEM;
|
return NGHTTP2_ERR_NOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
nghttp2_session_outbound_item_init(session, item);
|
nghttp2_outbound_item_init(item);
|
||||||
|
|
||||||
frame = &item->frame;
|
frame = &item->frame;
|
||||||
|
|
||||||
|
@ -6122,7 +6016,7 @@ int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id,
|
||||||
return NGHTTP2_ERR_NOMEM;
|
return NGHTTP2_ERR_NOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
nghttp2_session_outbound_item_init(session, item);
|
nghttp2_outbound_item_init(item);
|
||||||
|
|
||||||
frame = &item->frame;
|
frame = &item->frame;
|
||||||
|
|
||||||
|
@ -6159,7 +6053,7 @@ int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags,
|
||||||
return NGHTTP2_ERR_NOMEM;
|
return NGHTTP2_ERR_NOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
nghttp2_session_outbound_item_init(session, item);
|
nghttp2_outbound_item_init(item);
|
||||||
|
|
||||||
frame = &item->frame;
|
frame = &item->frame;
|
||||||
|
|
||||||
|
@ -6230,7 +6124,7 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
|
||||||
session->inflight_niv = niv;
|
session->inflight_niv = niv;
|
||||||
}
|
}
|
||||||
|
|
||||||
nghttp2_session_outbound_item_init(session, item);
|
nghttp2_outbound_item_init(item);
|
||||||
|
|
||||||
frame = &item->frame;
|
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) {
|
size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session) {
|
||||||
return nghttp2_pq_size(&session->ob_pq) +
|
return nghttp2_outbound_queue_size(&session->ob_urgent) +
|
||||||
nghttp2_pq_size(&session->ob_ss_pq) +
|
nghttp2_outbound_queue_size(&session->ob_reg) +
|
||||||
|
nghttp2_outbound_queue_size(&session->ob_syn) +
|
||||||
nghttp2_pq_size(&session->ob_da_pq);
|
nghttp2_pq_size(&session->ob_da_pq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -143,12 +143,15 @@ typedef enum {
|
||||||
struct nghttp2_session {
|
struct nghttp2_session {
|
||||||
nghttp2_map /* <nghttp2_stream*> */ streams;
|
nghttp2_map /* <nghttp2_stream*> */ streams;
|
||||||
nghttp2_stream_roots roots;
|
nghttp2_stream_roots roots;
|
||||||
/* Queue for outbound frames other than stream-creating HEADERS and
|
/* Queue for outbound urgent frames (PING and SETTINGS) */
|
||||||
DATA */
|
nghttp2_outbound_queue ob_urgent;
|
||||||
nghttp2_pq /* <nghttp2_outbound_item*> */ ob_pq;
|
/* Queue for non-DATA frames */
|
||||||
/* Queue for outbound stream-creating HEADERS frame */
|
nghttp2_outbound_queue ob_reg;
|
||||||
nghttp2_pq /* <nghttp2_outbound_item*> */ ob_ss_pq;
|
/* Queue for outbound stream-creating HEADERS (request or push
|
||||||
/* QUeue for DATA frame */
|
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_pq /* <nghttp2_outbound_item*> */ ob_da_pq;
|
||||||
nghttp2_active_outbound_item aob;
|
nghttp2_active_outbound_item aob;
|
||||||
nghttp2_inbound_frame iframe;
|
nghttp2_inbound_frame iframe;
|
||||||
|
@ -157,22 +160,8 @@ struct nghttp2_session {
|
||||||
nghttp2_session_callbacks callbacks;
|
nghttp2_session_callbacks callbacks;
|
||||||
/* Memory allocator */
|
/* Memory allocator */
|
||||||
nghttp2_mem mem;
|
nghttp2_mem mem;
|
||||||
/* Sequence number of outbound frame to maintain the order of
|
/* Base value when we schedule next DATA frame write. This is
|
||||||
enqueue if priority is equal. */
|
updated when one frame was written. */
|
||||||
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. */
|
|
||||||
uint64_t last_cycle;
|
uint64_t last_cycle;
|
||||||
void *user_data;
|
void *user_data;
|
||||||
/* Points to the latest closed stream. NULL if there is no closed
|
/* 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,
|
int nghttp2_session_is_my_stream_id(nghttp2_session *session,
|
||||||
int32_t stream_id);
|
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
|
* Adds |item| to the outbound queue in |session|. When this function
|
||||||
* succeeds, it takes ownership of |item|. So caller must not free it
|
* 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_data_aux_data *aux_data,
|
||||||
nghttp2_stream *stream);
|
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,
|
* Pops and returns next item to send. If there is no such item,
|
||||||
* returns NULL. This function takes into account max concurrent
|
* 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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (item->frame.hd.type) {
|
||||||
|
case NGHTTP2_DATA:
|
||||||
/* Penalize item by delaying scheduling according to effective
|
/* Penalize item by delaying scheduling according to effective
|
||||||
weight. This will delay low priority stream, which is good.
|
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 =
|
item->cycle =
|
||||||
session->last_cycle +
|
session->last_cycle +
|
||||||
NGHTTP2_DATA_PAYLOADLEN * NGHTTP2_MAX_WEIGHT / stream->effective_weight;
|
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);
|
rv = nghttp2_pq_push(&session->ob_da_pq, item);
|
||||||
|
if (rv != 0) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
if (stream->state == NGHTTP2_STREAM_RESERVED) {
|
if (stream->state == NGHTTP2_STREAM_RESERVED) {
|
||||||
rv = nghttp2_pq_push(&session->ob_ss_pq, item);
|
nghttp2_outbound_queue_push(&session->ob_syn, item);
|
||||||
} else {
|
} else {
|
||||||
rv = nghttp2_pq_push(&session->ob_pq, item);
|
nghttp2_outbound_queue_push(&session->ob_reg, item);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -124,10 +128,6 @@ static int stream_push_item(nghttp2_stream *stream, nghttp2_session *session) {
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rv != 0) {
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
item->queued = 1;
|
item->queued = 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -62,7 +62,7 @@ static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
nghttp2_session_outbound_item_init(session, item);
|
nghttp2_outbound_item_init(item);
|
||||||
|
|
||||||
if (data_prd != NULL && data_prd->read_callback != NULL) {
|
if (data_prd != NULL && data_prd->read_callback != NULL) {
|
||||||
item->aux_data.headers.data_prd = *data_prd;
|
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;
|
return NGHTTP2_ERR_NOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
nghttp2_session_outbound_item_init(session, item);
|
nghttp2_outbound_item_init(item);
|
||||||
|
|
||||||
frame = &item->frame;
|
frame = &item->frame;
|
||||||
|
|
||||||
|
@ -299,7 +299,7 @@ int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags _U_,
|
||||||
return NGHTTP2_ERR_NOMEM;
|
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;
|
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;
|
return NGHTTP2_ERR_NOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
nghttp2_session_outbound_item_init(session, item);
|
nghttp2_outbound_item_init(item);
|
||||||
|
|
||||||
frame = &item->frame;
|
frame = &item->frame;
|
||||||
aux_data = &item->aux_data.data;
|
aux_data = &item->aux_data.data;
|
||||||
|
|
|
@ -156,15 +156,9 @@ void server::start_accept(tcp::acceptor &acceptor, serve_mux &mux) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void server::stop()
|
void server::stop() { io_service_pool_.stop(); }
|
||||||
{
|
|
||||||
io_service_pool_.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void server::join()
|
void server::join() { io_service_pool_.join(); }
|
||||||
{
|
|
||||||
io_service_pool_.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace server
|
} // namespace server
|
||||||
} // namespace asio_http2
|
} // 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);
|
return impl_->listen_and_serve(ec, nullptr, address, port, asynchronous);
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::system::error_code
|
boost::system::error_code http2::listen_and_serve(
|
||||||
http2::listen_and_serve(boost::system::error_code &ec,
|
boost::system::error_code &ec, boost::asio::ssl::context &tls_context,
|
||||||
boost::asio::ssl::context &tls_context,
|
const std::string &address, const std::string &port, bool asynchronous) {
|
||||||
const std::string &address, const std::string &port,
|
|
||||||
bool asynchronous) {
|
|
||||||
return impl_->listen_and_serve(ec, &tls_context, address, port, 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));
|
return impl_->handle(std::move(pattern), std::move(cb));
|
||||||
}
|
}
|
||||||
|
|
||||||
void http2::stop()
|
void http2::stop() { impl_->stop(); }
|
||||||
{
|
|
||||||
impl_->stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void http2::join()
|
void http2::join() { return impl_->join(); }
|
||||||
{
|
|
||||||
return impl_->join();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace server
|
} // 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,
|
boost::system::error_code &ec, boost::asio::ssl::context *tls_context,
|
||||||
const std::string &address, const std::string &port, bool asynchronous) {
|
const std::string &address, const std::string &port, bool asynchronous) {
|
||||||
server_.reset(new server(num_threads_));
|
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; }
|
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));
|
return mux_.handle(std::move(pattern), std::move(cb));
|
||||||
}
|
}
|
||||||
|
|
||||||
void http2_impl::stop() {
|
void http2_impl::stop() { return server_->stop(); }
|
||||||
return server_->stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void http2_impl::join()
|
void http2_impl::join() { return server_->join(); }
|
||||||
{
|
|
||||||
return server_->join();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace server
|
} // namespace server
|
||||||
|
|
||||||
|
|
|
@ -42,11 +42,9 @@ class server;
|
||||||
class http2_impl {
|
class http2_impl {
|
||||||
public:
|
public:
|
||||||
http2_impl();
|
http2_impl();
|
||||||
boost::system::error_code
|
boost::system::error_code listen_and_serve(
|
||||||
listen_and_serve(boost::system::error_code &ec,
|
boost::system::error_code &ec, boost::asio::ssl::context *tls_context,
|
||||||
boost::asio::ssl::context *tls_context,
|
const std::string &address, const std::string &port, bool asynchronous);
|
||||||
const std::string &address, const std::string &port,
|
|
||||||
bool asynchronous);
|
|
||||||
void num_threads(size_t num_threads);
|
void num_threads(size_t num_threads);
|
||||||
void backlog(int backlog);
|
void backlog(int backlog);
|
||||||
bool handle(std::string pattern, request_cb cb);
|
bool handle(std::string pattern, request_cb cb);
|
||||||
|
|
|
@ -69,7 +69,7 @@ namespace h2load {
|
||||||
|
|
||||||
Config::Config()
|
Config::Config()
|
||||||
: data_length(-1), addrs(nullptr), nreqs(1), nclients(1), nthreads(1),
|
: 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),
|
no_tls_proto(PROTO_HTTP2), data_fd(-1), port(0), default_port(0),
|
||||||
verbose(false) {}
|
verbose(false) {}
|
||||||
|
|
||||||
|
@ -974,11 +974,13 @@ Options:
|
||||||
-w, --window-bits=<N>
|
-w, --window-bits=<N>
|
||||||
Sets the stream level initial window size to (2**<N>)-1.
|
Sets the stream level initial window size to (2**<N>)-1.
|
||||||
For SPDY, 2**<N> is used instead.
|
For SPDY, 2**<N> is used instead.
|
||||||
|
Default: )" << config.window_bits << R"(
|
||||||
-W, --connection-window-bits=<N>
|
-W, --connection-window-bits=<N>
|
||||||
Sets the connection level initial window size to
|
Sets the connection level initial window size to
|
||||||
(2**<N>)-1. For SPDY, if <N> is strictly less than 16,
|
(2**<N>)-1. For SPDY, if <N> is strictly less than 16,
|
||||||
this option is ignored. Otherwise 2**<N> is used for
|
this option is ignored. Otherwise 2**<N> is used for
|
||||||
SPDY.
|
SPDY.
|
||||||
|
Default: )" << config.connection_window_bits << R"(
|
||||||
-H, --header=<HEADER>
|
-H, --header=<HEADER>
|
||||||
Add/Override a header to the requests.
|
Add/Override a header to the requests.
|
||||||
-p, --no-tls-proto=<PROTOID>
|
-p, --no-tls-proto=<PROTOID>
|
||||||
|
|
|
@ -2312,8 +2312,9 @@ Options:
|
||||||
filename is dereived from URI. If URI ends with '/',
|
filename is dereived from URI. If URI ends with '/',
|
||||||
'index.html' is used as a filename. Not implemented
|
'index.html' is used as a filename. Not implemented
|
||||||
yet.
|
yet.
|
||||||
-t, --timeout=<SEC>
|
-t, --timeout=<DURATION>
|
||||||
Timeout each request after <SEC> seconds.
|
Timeout each request after <DURATION>. Set 0 to disable
|
||||||
|
timeout.
|
||||||
-w, --window-bits=<N>
|
-w, --window-bits=<N>
|
||||||
Sets the stream level initial window size to 2**<N>-1.
|
Sets the stream level initial window size to 2**<N>-1.
|
||||||
-W, --connection-window-bits=<N>
|
-W, --connection-window-bits=<N>
|
||||||
|
@ -2383,7 +2384,12 @@ Options:
|
||||||
--
|
--
|
||||||
|
|
||||||
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
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
|
} // namespace
|
||||||
|
|
||||||
|
@ -2469,7 +2475,11 @@ int main(int argc, char **argv) {
|
||||||
++config.verbose;
|
++config.verbose;
|
||||||
break;
|
break;
|
||||||
case 't':
|
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;
|
break;
|
||||||
case 'u':
|
case 'u':
|
||||||
config.upgrade = true;
|
config.upgrade = true;
|
||||||
|
|
|
@ -28,14 +28,14 @@
|
||||||
|
|
||||||
#include "nghttp2_pq.h"
|
#include "nghttp2_pq.h"
|
||||||
|
|
||||||
static int pq_compar(const void *lhs, const void *rhs) {
|
static int pq_less(const void *lhs, const void *rhs) {
|
||||||
return strcmp(lhs, rhs);
|
return strcmp(lhs, rhs) < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_nghttp2_pq(void) {
|
void test_nghttp2_pq(void) {
|
||||||
int i;
|
int i;
|
||||||
nghttp2_pq pq;
|
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(nghttp2_pq_empty(&pq));
|
||||||
CU_ASSERT(0 == nghttp2_pq_size(&pq));
|
CU_ASSERT(0 == nghttp2_pq_size(&pq));
|
||||||
CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"foo"));
|
CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"foo"));
|
||||||
|
@ -80,10 +80,10 @@ typedef struct {
|
||||||
int val;
|
int val;
|
||||||
} node;
|
} 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 *ln = (node *)lhs;
|
||||||
node *rn = (node *)rhs;
|
node *rn = (node *)rhs;
|
||||||
return ln->key - rn->key;
|
return ln->key < rn->key;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int node_update(void *item, void *arg _U_) {
|
static int node_update(void *item, void *arg _U_) {
|
||||||
|
@ -103,7 +103,7 @@ void test_nghttp2_pq_update(void) {
|
||||||
node *nd;
|
node *nd;
|
||||||
int ans[] = {-8, -6, -4, -2, 0, 1, 3, 5, 7, 9};
|
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) {
|
for (i = 0; i < (int)(sizeof(nodes) / sizeof(nodes[0])); ++i) {
|
||||||
nodes[i].key = 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) {
|
void test_nghttp2_session_recv_premature_headers(void) {
|
||||||
nghttp2_session *session;
|
nghttp2_session *session;
|
||||||
nghttp2_session_callbacks callbacks;
|
nghttp2_session_callbacks callbacks;
|
||||||
nghttp2_nv *nva;
|
|
||||||
size_t nvlen;
|
|
||||||
nghttp2_frame frame;
|
|
||||||
nghttp2_bufs bufs;
|
nghttp2_bufs bufs;
|
||||||
nghttp2_buf *buf;
|
nghttp2_buf *buf;
|
||||||
ssize_t rv;
|
ssize_t rv;
|
||||||
|
@ -1105,45 +1102,72 @@ void test_nghttp2_session_recv_premature_headers(void) {
|
||||||
nghttp2_hd_deflater deflater;
|
nghttp2_hd_deflater deflater;
|
||||||
nghttp2_outbound_item *item;
|
nghttp2_outbound_item *item;
|
||||||
nghttp2_mem *mem;
|
nghttp2_mem *mem;
|
||||||
|
uint32_t payloadlen;
|
||||||
|
|
||||||
mem = nghttp2_mem_default();
|
mem = nghttp2_mem_default();
|
||||||
frame_pack_bufs_init(&bufs);
|
frame_pack_bufs_init(&bufs);
|
||||||
|
|
||||||
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
|
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
|
||||||
|
callbacks.send_callback = null_send_callback;
|
||||||
|
|
||||||
nghttp2_session_server_new(&session, &callbacks, &ud);
|
nghttp2_session_server_new(&session, &callbacks, &ud);
|
||||||
|
|
||||||
nghttp2_hd_deflate_init(&deflater, mem);
|
nghttp2_hd_deflate_init(&deflater, mem);
|
||||||
|
|
||||||
nvlen = ARRLEN(reqnv);
|
rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, reqnv,
|
||||||
nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem);
|
ARRLEN(reqnv), 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);
|
|
||||||
|
|
||||||
buf = &bufs.head->buf;
|
buf = &bufs.head->buf;
|
||||||
assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf));
|
|
||||||
|
|
||||||
/* Intentionally feed payload cutting last 1 byte off */
|
/* Intentionally feed payload cutting last 1 byte off */
|
||||||
nghttp2_put_uint32be(buf->pos,
|
payloadlen = nghttp2_get_uint32(buf->pos) >> 8;
|
||||||
(uint32_t)(((frame.hd.length - 1) << 8) + buf->pos[3]));
|
nghttp2_put_uint32be(buf->pos, ((payloadlen - 1) << 8) + buf->pos[3]);
|
||||||
rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf) - 1);
|
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);
|
item = nghttp2_session_get_next_ob_item(session);
|
||||||
|
|
||||||
CU_ASSERT(NULL != item);
|
CU_ASSERT(NULL != item);
|
||||||
CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
|
CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
|
||||||
CU_ASSERT(NGHTTP2_COMPRESSION_ERROR == item->frame.rst_stream.error_code);
|
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_hd_deflate_free(&deflater);
|
||||||
nghttp2_session_del(session);
|
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) {
|
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);
|
item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
|
||||||
|
|
||||||
nghttp2_session_outbound_item_init(session, item);
|
nghttp2_outbound_item_init(item);
|
||||||
|
|
||||||
frame = &item->frame;
|
frame = &item->frame;
|
||||||
|
|
||||||
|
@ -1643,7 +1667,7 @@ void test_nghttp2_session_add_frame(void) {
|
||||||
session->next_stream_id += 2;
|
session->next_stream_id += 2;
|
||||||
|
|
||||||
CU_ASSERT(0 == nghttp2_session_add_item(session, item));
|
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(0 == nghttp2_session_send(session));
|
||||||
CU_ASSERT(NGHTTP2_HEADERS == acc.buf[3]);
|
CU_ASSERT(NGHTTP2_HEADERS == acc.buf[3]);
|
||||||
CU_ASSERT((NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY) == acc.buf[4]);
|
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(0 == nghttp2_session_on_ping_received(session, &frame));
|
||||||
CU_ASSERT(1 == user_data.frame_recv_cb_called);
|
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. */
|
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 */
|
/* Clear the flag, and receive it again */
|
||||||
frame.hd.flags = NGHTTP2_FLAG_NONE;
|
frame.hd.flags = NGHTTP2_FLAG_NONE;
|
||||||
|
|
||||||
CU_ASSERT(0 == nghttp2_session_on_ping_received(session, &frame));
|
CU_ASSERT(0 == nghttp2_session_on_ping_received(session, &frame));
|
||||||
CU_ASSERT(2 == user_data.frame_recv_cb_called);
|
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_PING == top->frame.hd.type);
|
||||||
CU_ASSERT(NGHTTP2_FLAG_ACK == top->frame.hd.flags);
|
CU_ASSERT(NGHTTP2_FLAG_ACK == top->frame.hd.flags);
|
||||||
CU_ASSERT(memcmp(opaque_data, top->frame.ping.opaque_data, 8) == 0);
|
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;
|
frame.hd.stream_id = 4;
|
||||||
|
|
||||||
CU_ASSERT(0 == nghttp2_session_on_data_received(session, &frame));
|
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
|
/* Check INVALID_STREAM case: DATA frame with stream ID which does
|
||||||
not exist. */
|
not exist. */
|
||||||
|
@ -2674,7 +2698,7 @@ void test_nghttp2_session_on_data_received(void) {
|
||||||
frame.hd.stream_id = 6;
|
frame.hd.stream_id = 6;
|
||||||
|
|
||||||
CU_ASSERT(0 == nghttp2_session_on_data_received(session, &frame));
|
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 */
|
/* DATA against nonexistent stream is just ignored for now */
|
||||||
CU_ASSERT(top == NULL);
|
CU_ASSERT(top == NULL);
|
||||||
/* CU_ASSERT(NGHTTP2_RST_STREAM == top->frame.hd.type); */
|
/* 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);
|
item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
|
||||||
|
|
||||||
nghttp2_session_outbound_item_init(session, item);
|
nghttp2_outbound_item_init(item);
|
||||||
|
|
||||||
frame = &item->frame;
|
frame = &item->frame;
|
||||||
|
|
||||||
|
@ -2736,7 +2760,7 @@ void test_nghttp2_session_send_headers_reply(void) {
|
||||||
|
|
||||||
item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
|
item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
|
||||||
|
|
||||||
nghttp2_session_outbound_item_init(session, item);
|
nghttp2_outbound_item_init(item);
|
||||||
|
|
||||||
frame = &item->frame;
|
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);
|
item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
|
||||||
|
|
||||||
nghttp2_session_outbound_item_init(session, item);
|
nghttp2_outbound_item_init(item);
|
||||||
|
|
||||||
frame = &item->frame;
|
frame = &item->frame;
|
||||||
|
|
||||||
|
@ -2831,7 +2855,7 @@ void test_nghttp2_session_send_headers_push_reply(void) {
|
||||||
|
|
||||||
item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
|
item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
|
||||||
|
|
||||||
nghttp2_session_outbound_item_init(session, item);
|
nghttp2_outbound_item_init(item);
|
||||||
|
|
||||||
frame = &item->frame;
|
frame = &item->frame;
|
||||||
|
|
||||||
|
@ -2865,7 +2889,7 @@ void test_nghttp2_session_send_rst_stream(void) {
|
||||||
|
|
||||||
item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
|
item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
|
||||||
|
|
||||||
nghttp2_session_outbound_item_init(session, item);
|
nghttp2_outbound_item_init(item);
|
||||||
|
|
||||||
frame = &item->frame;
|
frame = &item->frame;
|
||||||
|
|
||||||
|
@ -2899,7 +2923,7 @@ void test_nghttp2_session_send_push_promise(void) {
|
||||||
|
|
||||||
item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
|
item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
|
||||||
|
|
||||||
nghttp2_session_outbound_item_init(session, item);
|
nghttp2_outbound_item_init(item);
|
||||||
|
|
||||||
frame = &item->frame;
|
frame = &item->frame;
|
||||||
|
|
||||||
|
@ -2927,7 +2951,7 @@ void test_nghttp2_session_send_push_promise(void) {
|
||||||
|
|
||||||
item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
|
item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
|
||||||
|
|
||||||
nghttp2_session_outbound_item_init(session, item);
|
nghttp2_outbound_item_init(item);
|
||||||
|
|
||||||
frame = &item->frame;
|
frame = &item->frame;
|
||||||
|
|
||||||
|
@ -2950,7 +2974,7 @@ void test_nghttp2_session_send_push_promise(void) {
|
||||||
&pri_spec_default, NGHTTP2_STREAM_OPENING, NULL);
|
&pri_spec_default, NGHTTP2_STREAM_OPENING, NULL);
|
||||||
item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
|
item = mem->malloc(sizeof(nghttp2_outbound_item), NULL);
|
||||||
|
|
||||||
nghttp2_session_outbound_item_init(session, item);
|
nghttp2_outbound_item_init(item);
|
||||||
|
|
||||||
frame = &item->frame;
|
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,
|
CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 2,
|
||||||
NULL, NULL, 0, NULL));
|
NULL, NULL, 0, NULL));
|
||||||
CU_ASSERT(NULL == nghttp2_session_pop_next_ob_item(session));
|
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);
|
nghttp2_session_del(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4561,7 +4585,7 @@ void test_nghttp2_session_max_concurrent_streams(void) {
|
||||||
CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
|
CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
|
||||||
nghttp2_session_on_request_headers_received(session, &frame));
|
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_RST_STREAM == item->frame.hd.type);
|
||||||
CU_ASSERT(NGHTTP2_REFUSED_STREAM == item->frame.rst_stream.error_code);
|
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 ==
|
CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
|
||||||
nghttp2_session_on_request_headers_received(session, &frame));
|
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_GOAWAY == item->frame.hd.type);
|
||||||
CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code);
|
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
|
/* No RST_STREAM or GOAWAY is generated since stream should be in
|
||||||
NGHTTP2_STREAM_CLOSING and push response should be ignored. */
|
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
|
/* Check that we can receive push response HEADERS while RST_STREAM
|
||||||
is just queued. */
|
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(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);
|
nghttp2_frame_headers_free(&frame.headers, mem);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue