Merge branch 'master' into v1.0.0

This commit is contained in:
Tatsuhiro Tsujikawa 2015-04-28 22:48:34 +09:00
commit 1ad1fe6005
34 changed files with 521 additions and 433 deletions

View File

@ -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"`

View File

@ -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)/*

View File

@ -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

View File

@ -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
--------

View File

@ -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
--------

View File

@ -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
.

View File

@ -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
.

View File

@ -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
.

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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.
*/

View File

@ -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) {

View File

@ -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;

View File

@ -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. */

View File

@ -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;
}

View File

@ -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 */

View File

@ -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;
}
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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>

View File

@ -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;

View File

@ -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;

View File

@ -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);