Commit 0caefe20 authored by Tatsuhiro Tsujikawa's avatar Tatsuhiro Tsujikawa

Merge branch 'master' into simple-extensions

parents 9c84f60b a7ec9050
The MIT License The MIT License
Copyright (c) 2012, 2014, 2015 Tatsuhiro Tsujikawa Copyright (c) 2012, 2014, 2015, 2016 Tatsuhiro Tsujikawa
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
......
See COPYING
...@@ -274,7 +274,7 @@ for this 24 bytes byte string and updated API. ...@@ -274,7 +274,7 @@ for this 24 bytes byte string and updated API.
``NGHTTP2_CLIENT_MAGIC_LEN``. ``NGHTTP2_CLIENT_MAGIC_LEN``.
* ``NGHTTP2_BAD_PREFACE`` was renamed as ``NGHTTP2_BAD_CLIENT_MAGIC`` * ``NGHTTP2_BAD_PREFACE`` was renamed as ``NGHTTP2_BAD_CLIENT_MAGIC``
The alreay deprecated ``NGHTTP2_CLIENT_CONNECTION_HEADER`` and The already deprecated ``NGHTTP2_CLIENT_CONNECTION_HEADER`` and
``NGHTTP2_CLIENT_CONNECTION_HEADER_LEN`` were removed. ``NGHTTP2_CLIENT_CONNECTION_HEADER_LEN`` were removed.
If application uses these macros, just replace old ones with new ones. If application uses these macros, just replace old ones with new ones.
......
...@@ -25,7 +25,7 @@ dnl Do not change user variables! ...@@ -25,7 +25,7 @@ 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], [1.5.1-DEV], [t-tujikawa@users.sourceforge.net]) AC_INIT([nghttp2], [1.6.1-DEV], [t-tujikawa@users.sourceforge.net])
AC_CONFIG_AUX_DIR([.]) AC_CONFIG_AUX_DIR([.])
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
...@@ -46,9 +46,9 @@ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) ...@@ -46,9 +46,9 @@ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
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, 17) AC_SUBST(LT_CURRENT, 18)
AC_SUBST(LT_REVISION, 0) AC_SUBST(LT_REVISION, 0)
AC_SUBST(LT_AGE, 3) AC_SUBST(LT_AGE, 4)
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"`
minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"` minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"`
...@@ -104,6 +104,11 @@ AC_ARG_ENABLE([failmalloc], ...@@ -104,6 +104,11 @@ AC_ARG_ENABLE([failmalloc],
[Do not build failmalloc test program])], [Do not build failmalloc test program])],
[request_failmalloc=$enableval], [request_failmalloc=yes]) [request_failmalloc=$enableval], [request_failmalloc=yes])
AC_ARG_ENABLE([lib-only],
[AS_HELP_STRING([--enable-lib-only],
[Build libnghttp2 only. This is a short hand for --disable-app --disable-examples --disable-hpack-tools --disable-python-bindings])],
[request_lib_only=$enableval], [request_lib_only=no])
AC_ARG_WITH([libxml2], AC_ARG_WITH([libxml2],
[AS_HELP_STRING([--with-libxml2], [AS_HELP_STRING([--with-libxml2],
[Use libxml2 [default=check]])], [Use libxml2 [default=check]])],
...@@ -150,6 +155,13 @@ PKG_PROG_PKG_CONFIG([0.20]) ...@@ -150,6 +155,13 @@ PKG_PROG_PKG_CONFIG([0.20])
AM_PATH_PYTHON([2.7],, [:]) AM_PATH_PYTHON([2.7],, [:])
if [test "x$request_lib_only" = "xyes"]; then
request_app=no
request_hpack_tools=no
request_examples=no
request_python_bindings=no
fi
if [test "x$request_python_bindings" != "xno"]; then if [test "x$request_python_bindings" != "xno"]; then
AX_PYTHON_DEVEL([>= '2.7']) AX_PYTHON_DEVEL([>= '2.7'])
fi fi
...@@ -243,6 +255,11 @@ if test "x${have_zlib}" = "xno"; then ...@@ -243,6 +255,11 @@ if test "x${have_zlib}" = "xno"; then
AC_MSG_NOTICE($ZLIB_PKG_ERRORS) AC_MSG_NOTICE($ZLIB_PKG_ERRORS)
fi fi
# dl: openssl requires libdl when it is statically linked.
LIBS_OLD=$LIBS
AC_SEARCH_LIBS([dlopen], [dl], [APPLDFLAGS="-ldl $APPLDFLAGS"], [], [])
LIBS=$LIBS_OLD
# cunit # cunit
PKG_CHECK_MODULES([CUNIT], [cunit >= 2.1], [have_cunit=yes], [have_cunit=no]) PKG_CHECK_MODULES([CUNIT], [cunit >= 2.1], [have_cunit=yes], [have_cunit=no])
# If pkg-config does not find cunit, check it using AC_CHECK_LIB. We # If pkg-config does not find cunit, check it using AC_CHECK_LIB. We
...@@ -613,6 +630,10 @@ AC_CHECK_FUNCS([ \ ...@@ -613,6 +630,10 @@ AC_CHECK_FUNCS([ \
AC_CHECK_FUNC([timerfd_create], AC_CHECK_FUNC([timerfd_create],
[have_timerfd_create=yes], [have_timerfd_create=no]) [have_timerfd_create=yes], [have_timerfd_create=no])
# For cygwin: we can link initgroups, so AC_CHECK_FUNCS succeeds, but
# cygwin disables initgroups due to feature test macro magic with our
# configuration.
AC_CHECK_DECLS([initgroups], [], [], [[#include <grp.h>]])
# Checks for epoll availability, primarily for examples/tiny-nghttpd # Checks for epoll availability, primarily for examples/tiny-nghttpd
AX_HAVE_EPOLL([have_epoll=yes], [have_epoll=no]) AX_HAVE_EPOLL([have_epoll=yes], [have_epoll=no])
......
...@@ -66,7 +66,7 @@ master_doc = 'index' ...@@ -66,7 +66,7 @@ master_doc = 'index'
# General information about the project. # General information about the project.
project = u'nghttp2' project = u'nghttp2'
copyright = u'2012, 2015, Tatsuhiro Tsujikawa' copyright = u'2012, 2015, 2016, Tatsuhiro Tsujikawa'
# The version info for the project you're documenting, acts as replacement for # The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the # |version| and |release|, also used in various other places throughout the
......
.\" Man page generated from reStructuredText. .\" Man page generated from reStructuredText.
. .
.TH "H2LOAD" "1" "November 29, 2015" "1.5.1-DEV" "nghttp2" .TH "H2LOAD" "1" "December 23, 2015" "1.6.0" "nghttp2"
.SH NAME .SH NAME
h2load \- HTTP/2 benchmarking tool h2load \- HTTP/2 benchmarking tool
. .
...@@ -374,6 +374,28 @@ The fraction of the number of connections within standard ...@@ -374,6 +374,28 @@ The fraction of the number of connections within standard
deviation range (mean +/\- sd) against total number of successful deviation range (mean +/\- sd) against total number of successful
connections. connections.
.UNINDENT .UNINDENT
.TP
.B req/s
.INDENT 7.0
.TP
.B min
The minimum request per second among all clients.
.TP
.B max
The maximum request per second among all clients.
.TP
.B mean
The mean request per second among all clients.
.TP
.B sd
The standard deviation of request per second among all clients.
server.
.TP
.B +/\- sd
The fraction of the number of connections within standard
deviation range (mean +/\- sd) against total number of successful
connections.
.UNINDENT
.UNINDENT .UNINDENT
.SH FLOW CONTROL .SH FLOW CONTROL
.sp .sp
......
...@@ -213,6 +213,8 @@ is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms ...@@ -213,6 +213,8 @@ is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms
(hours, minutes, seconds and milliseconds, respectively). If a unit (hours, minutes, seconds and milliseconds, respectively). If a unit
is omitted, a second is used as unit. is omitted, a second is used as unit.
.. _h2load-1-output:
OUTPUT OUTPUT
------ ------
...@@ -301,6 +303,21 @@ time for 1st byte (of (decrypted in case of TLS) application data) ...@@ -301,6 +303,21 @@ time for 1st byte (of (decrypted in case of TLS) application data)
deviation range (mean +/- sd) against total number of successful deviation range (mean +/- sd) against total number of successful
connections. connections.
req/s
min
The minimum request per second among all clients.
max
The maximum request per second among all clients.
mean
The mean request per second among all clients.
sd
The standard deviation of request per second among all clients.
server.
+/- sd
The fraction of the number of connections within standard
deviation range (mean +/- sd) against total number of successful
connections.
FLOW CONTROL FLOW CONTROL
------------ ------------
......
.. _h2load-1-output:
OUTPUT OUTPUT
------ ------
...@@ -86,7 +88,7 @@ time for 1st byte (of (decrypted in case of TLS) application data) ...@@ -86,7 +88,7 @@ time for 1st byte (of (decrypted in case of TLS) application data)
deviation range (mean +/- sd) against total number of successful deviation range (mean +/- sd) against total number of successful
connections. connections.
req/s (client) req/s
min min
The minimum request per second among all clients. The minimum request per second among all clients.
max max
......
.\" Man page generated from reStructuredText. .\" Man page generated from reStructuredText.
. .
.TH "NGHTTP" "1" "November 29, 2015" "1.5.1-DEV" "nghttp2" .TH "NGHTTP" "1" "December 23, 2015" "1.6.0" "nghttp2"
.SH NAME .SH NAME
nghttp \- HTTP/2 client nghttp \- HTTP/2 client
. .
...@@ -152,7 +152,8 @@ Default: \fB16\fP ...@@ -152,7 +152,8 @@ Default: \fB16\fP
.B \-M, \-\-peer\-max\-concurrent\-streams=<N> .B \-M, \-\-peer\-max\-concurrent\-streams=<N>
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value of Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value of
remote endpoint as if it is received in SETTINGS frame. remote endpoint as if it is received in SETTINGS frame.
The default is large enough as it is seen as unlimited. .sp
Default: \fB100\fP
.UNINDENT .UNINDENT
.INDENT 0.0 .INDENT 0.0
.TP .TP
......
...@@ -116,7 +116,8 @@ OPTIONS ...@@ -116,7 +116,8 @@ OPTIONS
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value of Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value of
remote endpoint as if it is received in SETTINGS frame. remote endpoint as if it is received in SETTINGS frame.
The default is large enough as it is seen as unlimited.
Default: ``100``
.. option:: -c, --header-table-size=<SIZE> .. option:: -c, --header-table-size=<SIZE>
......
.\" Man page generated from reStructuredText. .\" Man page generated from reStructuredText.
. .
.TH "NGHTTPD" "1" "November 29, 2015" "1.5.1-DEV" "nghttp2" .TH "NGHTTPD" "1" "December 23, 2015" "1.6.0" "nghttp2"
.SH NAME .SH NAME
nghttpd \- HTTP/2 server nghttpd \- HTTP/2 server
. .
......
.\" Man page generated from reStructuredText. .\" Man page generated from reStructuredText.
. .
.TH "NGHTTPX" "1" "November 29, 2015" "1.5.1-DEV" "nghttp2" .TH "NGHTTPX" "1" "December 23, 2015" "1.6.0" "nghttp2"
.SH NAME .SH NAME
nghttpx \- HTTP/2 proxy nghttpx \- HTTP/2 proxy
. .
......
h2load - HTTP/2 benchmarking tool - HOW-TO h2load - HTTP/2 benchmarking tool - HOW-TO
========================================== ==========================================
h2load is benchmarking tool for HTTP/2. If built with h2load is benchmarking tool for HTTP/2 and HTTP/1.1. If built with
spdylay (http://tatsuhiro-t.github.io/spdylay/) library, it also spdylay (http://tatsuhiro-t.github.io/spdylay/) library, it also
supports SPDY protocol. It supports SSL/TLS and clear text for both supports SPDY protocol. It supports SSL/TLS and clear text for all
HTTP/2 and SPDY. supported protocols.
Basic Usage Basic Usage
----------- -----------
...@@ -22,29 +22,37 @@ In order to set benchmark settings, specify following 3 options. ...@@ -22,29 +22,37 @@ In order to set benchmark settings, specify following 3 options.
If ``auto`` is given, the number of given URIs is used. If ``auto`` is given, the number of given URIs is used.
Default: ``auto`` Default: ``auto``
For SSL/TLS connection, the protocol will be negotiated via ALPN/NPN.
You can set specific protocols in ``--npn-list`` option. For
cleartext connection, the default protocol is HTTP/2. To change the
protocol in cleartext connection, use ``--no-tls-proto`` option. For
convenience, ``--h1`` option forces HTTP/1.1 for both cleartext and
SSL/TLS connections.
Here is a command-line to perform benchmark to URI \https://localhost Here is a command-line to perform benchmark to URI \https://localhost
using total 100000 requests, 100 concurrent clients and 10 max using total 100000 requests, 100 concurrent clients and 10 max
concurrent streams:: concurrent streams:
.. code-block:: text
$ h2load -n100000 -c100 -m10 https://localhost $ h2load -n100000 -c100 -m10 https://localhost
The benchmarking result looks like this:: The benchmarking result looks like this:
finished in 0 sec, 385 millisec and 851 microsec, 2591 req/s, 1689 kbytes/s .. code-block:: text
requests: 1000 total, 1000 started, 1000 done, 1000 succeeded, 0 failed, 0 errored
status codes: 1000 2xx, 0 3xx, 0 4xx, 0 5xx
traffic: 667500 bytes total, 28700 bytes headers, 612000 bytes data
The number of ``failed`` is the number of requests returned with non finished in 7.08s, 141164.80 req/s, 555.33MB/s
2xx status. The number of ``error`` is the number of ``failed`` plus requests: 1000000 total, 1000000 started, 1000000 done, 1000000 succeeded, 0 failed, 0 errored, 0 timeout
the number of requests which failed with connection error. status codes: 1000000 2xx, 0 3xx, 0 4xx, 0 5xx
traffic: 4125025824 bytes total, 11023424 bytes headers (space savings 93.07%), 4096000000 bytes data
min max mean sd +/- sd
time for request: 15.31ms 146.85ms 69.78ms 9.26ms 92.43%
time for connect: 1.08ms 25.04ms 10.71ms 9.80ms 64.00%
time to 1st byte: 25.36ms 184.96ms 79.11ms 53.97ms 78.00%
req/s (client) : 1412.04 1447.84 1426.52 10.57 63.00%
The number of ``total`` in ``traffic`` is the received application See the h2load manual page :ref:`h2load-1-output` section for the
data. If SSL/TLS is used, this number is calculated after decryption. explanation of the above numbers.
The number of ``headers`` is the sum of payload size of response
HEADERS (or SYN_REPLY for SPDY). This number comes before
decompressing header block. The number of ``data`` is the sum of
response body.
Flow Control Flow Control
------------ ------------
......
...@@ -3296,6 +3296,9 @@ nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec); ...@@ -3296,6 +3296,9 @@ nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec);
* :enum:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` * :enum:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE`
* No stream ID is available because maximum stream ID was * No stream ID is available because maximum stream ID was
* reached. * reached.
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
* Trying to depend on itself (new stream ID equals
* ``pri_spec->stream_id``).
* *
* .. warning:: * .. warning::
* *
...@@ -3407,7 +3410,7 @@ nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, ...@@ -3407,7 +3410,7 @@ nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,
* has already sent and if `nghttp2_submit_trailer()` is called before * has already sent and if `nghttp2_submit_trailer()` is called before
* any response HEADERS submission (usually by * any response HEADERS submission (usually by
* `nghttp2_submit_response()`), the content of |nva| will be sent as * `nghttp2_submit_response()`), the content of |nva| will be sent as
* reponse headers, which will result in error. * response headers, which will result in error.
* *
* This function has the same effect with `nghttp2_submit_headers()`, * This function has the same effect with `nghttp2_submit_headers()`,
* with flags = :enum:`NGHTTP2_FLAG_END_HEADERS` and both pri_spec and * with flags = :enum:`NGHTTP2_FLAG_END_HEADERS` and both pri_spec and
...@@ -3506,7 +3509,8 @@ NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session, ...@@ -3506,7 +3509,8 @@ NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session,
* No stream ID is available because maximum stream ID was * No stream ID is available because maximum stream ID was
* reached. * reached.
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
* The |stream_id| is 0. * The |stream_id| is 0; or trying to depend on itself (stream ID
* equals ``pri_spec->stream_id``).
* :enum:`NGHTTP2_ERR_DATA_EXIST` * :enum:`NGHTTP2_ERR_DATA_EXIST`
* DATA or HEADERS has been already submitted and not fully * DATA or HEADERS has been already submitted and not fully
* processed yet. This happens if stream denoted by |stream_id| * processed yet. This happens if stream denoted by |stream_id|
...@@ -3549,7 +3553,7 @@ nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, ...@@ -3549,7 +3553,7 @@ nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
* The |stream_id| is 0. * The |stream_id| is 0.
* :enum:`NGHTTP2_ERR_STREAM_CLOSED` * :enum:`NGHTTP2_ERR_STREAM_CLOSED`
* The stream was alreay closed; or the |stream_id| is invalid. * The stream was already closed; or the |stream_id| is invalid.
* *
* .. note:: * .. note::
* *
...@@ -3718,7 +3722,7 @@ NGHTTP2_EXTERN int nghttp2_submit_settings(nghttp2_session *session, ...@@ -3718,7 +3722,7 @@ NGHTTP2_EXTERN int nghttp2_submit_settings(nghttp2_session *session,
* The |stream_id| is 0; The |stream_id| does not designate stream * The |stream_id| is 0; The |stream_id| does not designate stream
* that peer initiated. * that peer initiated.
* :enum:`NGHTTP2_ERR_STREAM_CLOSED` * :enum:`NGHTTP2_ERR_STREAM_CLOSED`
* The stream was alreay closed; or the |stream_id| is invalid. * The stream was already closed; or the |stream_id| is invalid.
* *
* .. warning:: * .. warning::
* *
...@@ -4336,7 +4340,7 @@ typedef enum { ...@@ -4336,7 +4340,7 @@ typedef enum {
* :enum:`NGHTTP2_ERR_HEADER_COMP` * :enum:`NGHTTP2_ERR_HEADER_COMP`
* Inflation process has failed. * Inflation process has failed.
* :enum:`NGHTTP2_ERR_BUFFER_ERROR` * :enum:`NGHTTP2_ERR_BUFFER_ERROR`
* The heder field name or value is too large. * The header field name or value is too large.
* *
* Example follows:: * Example follows::
* *
......
...@@ -43,9 +43,6 @@ typedef struct { ...@@ -43,9 +43,6 @@ typedef struct {
/* nonzero if request HEADERS is canceled. The error code is stored /* nonzero if request HEADERS is canceled. The error code is stored
in |error_code|. */ in |error_code|. */
uint8_t canceled; uint8_t canceled;
/* nonzero if this item should be attached to stream object to make
it under priority control */
uint8_t attach_stream;
} nghttp2_headers_aux_data; } nghttp2_headers_aux_data;
/* struct used for DATA frame */ /* struct used for DATA frame */
......
...@@ -44,11 +44,13 @@ void nghttp2_pq_free(nghttp2_pq *pq) { ...@@ -44,11 +44,13 @@ void nghttp2_pq_free(nghttp2_pq *pq) {
} }
static void swap(nghttp2_pq *pq, size_t i, size_t j) { static void swap(nghttp2_pq *pq, size_t i, size_t j) {
nghttp2_pq_entry *t = pq->q[i]; nghttp2_pq_entry *a = pq->q[i];
pq->q[i] = pq->q[j]; nghttp2_pq_entry *b = pq->q[j];
pq->q[i]->index = i;
pq->q[j] = t; pq->q[i] = b;
pq->q[j]->index = j; b->index = i;
pq->q[j] = a;
a->index = j;
} }
static void bubble_up(nghttp2_pq *pq, size_t index) { static void bubble_up(nghttp2_pq *pq, size_t index) {
......
This diff is collapsed.
...@@ -73,6 +73,10 @@ typedef struct { ...@@ -73,6 +73,10 @@ typedef struct {
/* The default maximum number of incoming reserved streams */ /* The default maximum number of incoming reserved streams */
#define NGHTTP2_MAX_INCOMING_RESERVED_STREAMS 200 #define NGHTTP2_MAX_INCOMING_RESERVED_STREAMS 200
/* Even if we have less SETTINGS_MAX_CONCURRENT_STREAMS than this
number, we keep NGHTTP2_MIN_IDLE_STREAMS streams in idle state */
#define NGHTTP2_MIN_IDLE_STREAMS 16
/* The maximum number of items in outbound queue, which is considered /* The maximum number of items in outbound queue, which is considered
as flooding caused by peer. All frames are not considered here. as flooding caused by peer. All frames are not considered here.
We only consider PING + ACK and SETTINGS + ACK. This is because We only consider PING + ACK and SETTINGS + ACK. This is because
...@@ -450,6 +454,11 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, ...@@ -450,6 +454,11 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
* *
* This function returns a pointer to created new stream object, or * This function returns a pointer to created new stream object, or
* NULL. * NULL.
*
* This function adjusts neither the number of closed streams or idle
* streams. The caller should manually call
* nghttp2_session_adjust_closed_stream() or
* nghttp2_session_adjust_idle_stream() respectively.
*/ */
nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session,
int32_t stream_id, uint8_t flags, int32_t stream_id, uint8_t flags,
...@@ -498,28 +507,16 @@ int nghttp2_session_destroy_stream(nghttp2_session *session, ...@@ -498,28 +507,16 @@ int nghttp2_session_destroy_stream(nghttp2_session *session,
* limitation of maximum number of streams in memory, |stream| is not * limitation of maximum number of streams in memory, |stream| is not
* closed and just deleted from memory (see * closed and just deleted from memory (see
* nghttp2_session_destroy_stream). * nghttp2_session_destroy_stream).
*
* This function returns 0 if it succeeds, or one the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/ */
int nghttp2_session_keep_closed_stream(nghttp2_session *session, void nghttp2_session_keep_closed_stream(nghttp2_session *session,
nghttp2_stream *stream); nghttp2_stream *stream);
/* /*
* Appends |stream| to linked list |session->idle_stream_head|. We * Appends |stream| to linked list |session->idle_stream_head|. We
* apply fixed limit for list size. To fit into that limit, one or * apply fixed limit for list size. To fit into that limit, one or
* more oldest streams are removed from list as necessary. * more oldest streams are removed from list as necessary.
*
* This function returns 0 if it succeeds, or one the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/ */
int nghttp2_session_keep_idle_stream(nghttp2_session *session, void nghttp2_session_keep_idle_stream(nghttp2_session *session,
nghttp2_stream *stream); nghttp2_stream *stream);
/* /*
...@@ -531,9 +528,7 @@ void nghttp2_session_detach_idle_stream(nghttp2_session *session, ...@@ -531,9 +528,7 @@ void nghttp2_session_detach_idle_stream(nghttp2_session *session,
/* /*
* Deletes closed stream to ensure that number of incoming streams * Deletes closed stream to ensure that number of incoming streams
* including active and closed is in the maximum number of allowed * including active and closed is in the maximum number of allowed
* stream. If |offset| is nonzero, it is decreased from the maximum * stream.
* number of allowed stream when comparing number of active and closed
* stream and the maximum number.
* *
* This function returns 0 if it succeeds, or one the following * This function returns 0 if it succeeds, or one the following
* negative error codes: * negative error codes:
...@@ -541,8 +536,7 @@ void nghttp2_session_detach_idle_stream(nghttp2_session *session, ...@@ -541,8 +536,7 @@ void nghttp2_session_detach_idle_stream(nghttp2_session *session,
* NGHTTP2_ERR_NOMEM * NGHTTP2_ERR_NOMEM
* Out of memory * Out of memory
*/ */
int nghttp2_session_adjust_closed_stream(nghttp2_session *session, int nghttp2_session_adjust_closed_stream(nghttp2_session *session);
size_t offset);
/* /*
* Deletes idle stream to ensure that number of idle streams is in * Deletes idle stream to ensure that number of idle streams is in
...@@ -814,6 +808,9 @@ int nghttp2_session_update_local_settings(nghttp2_session *session, ...@@ -814,6 +808,9 @@ int nghttp2_session_update_local_settings(nghttp2_session *session,
* |pri_spec|. Caller must ensure that stream->hd.stream_id != * |pri_spec|. Caller must ensure that stream->hd.stream_id !=
* pri_spec->stream_id. * pri_spec->stream_id.
* *
* This function does not adjust the number of idle streams. The
* caller should call nghttp2_session_adjust_idle_stream() later.
*
* This function returns 0 if it succeeds, or one of the following * This function returns 0 if it succeeds, or one of the following
* negative error codes: * negative error codes:
* *
......
...@@ -80,6 +80,7 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, ...@@ -80,6 +80,7 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
stream->queued = 0; stream->queued = 0;
stream->descendant_last_cycle = 0; stream->descendant_last_cycle = 0;
stream->cycle = 0; stream->cycle = 0;
stream->pending_penalty = 0;
stream->descendant_next_seq = 0; stream->descendant_next_seq = 0;
stream->seq = 0; stream->seq = 0;
stream->last_writelen = 0; stream->last_writelen = 0;
...@@ -115,9 +116,14 @@ static int stream_subtree_active(nghttp2_stream *stream) { ...@@ -115,9 +116,14 @@ static int stream_subtree_active(nghttp2_stream *stream) {
/* /*
* Returns next cycle for |stream|. * Returns next cycle for |stream|.
*/ */
static uint64_t stream_next_cycle(nghttp2_stream *stream, uint64_t last_cycle) { static void stream_next_cycle(nghttp2_stream *stream, uint64_t last_cycle) {
return last_cycle + size_t penalty;
stream->last_writelen * NGHTTP2_MAX_WEIGHT / (uint32_t)stream->weight;
penalty =
stream->last_writelen * NGHTTP2_MAX_WEIGHT + stream->pending_penalty;
stream->cycle = last_cycle + penalty / (uint32_t)stream->weight;
stream->pending_penalty = (uint32_t)(penalty % (uint32_t)stream->weight);
} }
static int stream_obq_push(nghttp2_stream *dep_stream, nghttp2_stream *stream) { static int stream_obq_push(nghttp2_stream *dep_stream, nghttp2_stream *stream) {
...@@ -125,7 +131,6 @@ static int stream_obq_push(nghttp2_stream *dep_stream, nghttp2_stream *stream) { ...@@ -125,7 +131,6 @@ static int stream_obq_push(nghttp2_stream *dep_stream, nghttp2_stream *stream) {
for (; dep_stream && !stream->queued; for (; dep_stream && !stream->queued;
stream = dep_stream, dep_stream = dep_stream->dep_prev) { stream = dep_stream, dep_stream = dep_stream->dep_prev) {
stream->cycle =
stream_next_cycle(stream, dep_stream->descendant_last_cycle); stream_next_cycle(stream, dep_stream->descendant_last_cycle);
stream->seq = dep_stream->descendant_next_seq++; stream->seq = dep_stream->descendant_next_seq++;
...@@ -169,6 +174,7 @@ static void stream_obq_remove(nghttp2_stream *stream) { ...@@ -169,6 +174,7 @@ static void stream_obq_remove(nghttp2_stream *stream) {
stream->queued = 0; stream->queued = 0;
stream->cycle = 0; stream->cycle = 0;
stream->pending_penalty = 0;
stream->descendant_last_cycle = 0; stream->descendant_last_cycle = 0;
stream->last_writelen = 0; stream->last_writelen = 0;
...@@ -207,25 +213,12 @@ void nghttp2_stream_reschedule(nghttp2_stream *stream) { ...@@ -207,25 +213,12 @@ void nghttp2_stream_reschedule(nghttp2_stream *stream) {
dep_stream = stream->dep_prev; dep_stream = stream->dep_prev;
for (; dep_stream; stream = dep_stream, dep_stream = dep_stream->dep_prev) { for (; dep_stream; stream = dep_stream, dep_stream = dep_stream->dep_prev) {
if (nghttp2_pq_size(&dep_stream->obq) == 1) {
dep_stream->descendant_last_cycle = 0;
stream->cycle = 0;
} else {
/* We update descendant_last_cycle here, and we don't do it when
no data is written for stream. This effectively means that
we treat these streams as if they are not scheduled at all.
This does not cause disruption in scheduling machinery. It
just makes new streams scheduled a bit early. */
dep_stream->descendant_last_cycle = stream->cycle;
nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry); nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry);
stream->cycle =
stream_next_cycle(stream, dep_stream->descendant_last_cycle); stream_next_cycle(stream, dep_stream->descendant_last_cycle);
stream->seq = dep_stream->descendant_next_seq++; stream->seq = dep_stream->descendant_next_seq++;
nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry); nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry);
}
DEBUGF(fprintf(stderr, "stream: stream=%d obq resched cycle=%ld\n", DEBUGF(fprintf(stderr, "stream: stream=%d obq resched cycle=%ld\n",
stream->stream_id, stream->cycle)); stream->stream_id, stream->cycle));
...@@ -234,6 +227,61 @@ void nghttp2_stream_reschedule(nghttp2_stream *stream) { ...@@ -234,6 +227,61 @@ void nghttp2_stream_reschedule(nghttp2_stream *stream) {
} }
} }
void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight) {
nghttp2_stream *dep_stream;
uint64_t last_cycle;
int32_t old_weight;
size_t wlen_penalty;
if (stream->weight == weight) {
return;
}
old_weight = stream->weight;
stream->weight = weight;
dep_stream = stream->dep_prev;
if (!dep_stream) {
return;
}
dep_stream->sum_dep_weight += weight - old_weight;
if (!stream->queued) {
return;
}
nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry);
wlen_penalty = stream->last_writelen * NGHTTP2_MAX_WEIGHT;
/* Compute old stream->pending_penalty we used to calculate
stream->cycle */
stream->pending_penalty =
(uint32_t)((stream->pending_penalty + (uint32_t)old_weight -
(wlen_penalty % (uint32_t)old_weight)) %
(uint32_t)old_weight);
last_cycle = stream->cycle -
(wlen_penalty + stream->pending_penalty) / (uint32_t)old_weight;
/* Now we have old stream->pending_penalty and new stream->weight in
place */
stream_next_cycle(stream, last_cycle);
if (stream->cycle < dep_stream->descendant_last_cycle) {
stream->cycle = dep_stream->descendant_last_cycle;
}
/* Continue to use same stream->seq */
nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry);
DEBUGF(fprintf(stderr, "stream: stream=%d obq resched cycle=%ld\n",
stream->stream_id, stream->cycle));
}
static nghttp2_stream *stream_last_sib(nghttp2_stream *stream) { static nghttp2_stream *stream_last_sib(nghttp2_stream *stream) {
for (; stream->sib_next; stream = stream->sib_next) for (; stream->sib_next; stream = stream->sib_next)
; ;
...@@ -861,9 +909,15 @@ int nghttp2_stream_in_dep_tree(nghttp2_stream *stream) { ...@@ -861,9 +909,15 @@ int nghttp2_stream_in_dep_tree(nghttp2_stream *stream) {
nghttp2_outbound_item * nghttp2_outbound_item *
nghttp2_stream_next_outbound_item(nghttp2_stream *stream) { nghttp2_stream_next_outbound_item(nghttp2_stream *stream) {
nghttp2_pq_entry *ent; nghttp2_pq_entry *ent;
nghttp2_stream *si;
for (;;) { for (;;) {
if (stream_active(stream)) { if (stream_active(stream)) {
/* Update ascendant's descendant_last_cycle here, so that we can
assure that new stream is scheduled based on it. */
for (si = stream; si->dep_prev; si = si->dep_prev) {
si->dep_prev->descendant_last_cycle = si->cycle;
}
return stream->item; return stream->item;
} }
ent = nghttp2_pq_top(&stream->obq); ent = nghttp2_pq_top(&stream->obq);
......
...@@ -197,6 +197,8 @@ struct nghttp2_stream { ...@@ -197,6 +197,8 @@ struct nghttp2_stream {
int32_t local_window_size; int32_t local_window_size;
/* weight of this stream */ /* weight of this stream */
int32_t weight; int32_t weight;
/* This is unpaid penalty (offset) when calculating cycle. */
uint32_t pending_penalty;
/* sum of weight of direct descendants */ /* sum of weight of direct descendants */
int32_t sum_dep_weight; int32_t sum_dep_weight;
nghttp2_stream_state state; nghttp2_stream_state state;
...@@ -419,7 +421,14 @@ int nghttp2_stream_in_dep_tree(nghttp2_stream *stream); ...@@ -419,7 +421,14 @@ int nghttp2_stream_in_dep_tree(nghttp2_stream *stream);
void nghttp2_stream_reschedule(nghttp2_stream *stream); void nghttp2_stream_reschedule(nghttp2_stream *stream);
/* /*
* Returns a stream which has highest priority. * Changes |stream|'s weight to |weight|. If |stream| is queued, it
* will be rescheduled based on new weight.
*/
void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight);
/*
* Returns a stream which has highest priority, updating
* descendant_last_cycle of selected stream's ancestors.
*/ */
nghttp2_outbound_item * nghttp2_outbound_item *
nghttp2_stream_next_outbound_item(nghttp2_stream *stream); nghttp2_stream_next_outbound_item(nghttp2_stream *stream);
......
...@@ -40,8 +40,7 @@ static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags, ...@@ -40,8 +40,7 @@ static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags,
const nghttp2_priority_spec *pri_spec, const nghttp2_priority_spec *pri_spec,
nghttp2_nv *nva_copy, size_t nvlen, nghttp2_nv *nva_copy, size_t nvlen,
const nghttp2_data_provider *data_prd, const nghttp2_data_provider *data_prd,
void *stream_user_data, void *stream_user_data) {
uint8_t attach_stream) {
int rv; int rv;
uint8_t flags_copy; uint8_t flags_copy;
nghttp2_outbound_item *item = NULL; nghttp2_outbound_item *item = NULL;
...@@ -56,6 +55,16 @@ static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags, ...@@ -56,6 +55,16 @@ static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags,
goto fail; goto fail;
} }
if (stream_id == -1) {
if ((int32_t)session->next_stream_id == pri_spec->stream_id) {
rv = NGHTTP2_ERR_INVALID_ARGUMENT;
goto fail;
}
} else if (stream_id == pri_spec->stream_id) {
rv = NGHTTP2_ERR_INVALID_ARGUMENT;
goto fail;
}
item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
if (item == NULL) { if (item == NULL) {
rv = NGHTTP2_ERR_NOMEM; rv = NGHTTP2_ERR_NOMEM;
...@@ -69,7 +78,6 @@ static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags, ...@@ -69,7 +78,6 @@ static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags,
} }
item->aux_data.headers.stream_user_data = stream_user_data; item->aux_data.headers.stream_user_data = stream_user_data;
item->aux_data.headers.attach_stream = attach_stream;
flags_copy = flags_copy =
(uint8_t)((flags & (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PRIORITY)) | (uint8_t)((flags & (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PRIORITY)) |
...@@ -122,8 +130,7 @@ static int32_t submit_headers_shared_nva(nghttp2_session *session, ...@@ -122,8 +130,7 @@ static int32_t submit_headers_shared_nva(nghttp2_session *session,
const nghttp2_priority_spec *pri_spec, const nghttp2_priority_spec *pri_spec,
const nghttp2_nv *nva, size_t nvlen, const nghttp2_nv *nva, size_t nvlen,
const nghttp2_data_provider *data_prd, const nghttp2_data_provider *data_prd,
void *stream_user_data, void *stream_user_data) {
uint8_t attach_stream) {
int rv; int rv;
nghttp2_nv *nva_copy; nghttp2_nv *nva_copy;
nghttp2_priority_spec copy_pri_spec; nghttp2_priority_spec copy_pri_spec;
...@@ -144,15 +151,14 @@ static int32_t submit_headers_shared_nva(nghttp2_session *session, ...@@ -144,15 +151,14 @@ static int32_t submit_headers_shared_nva(nghttp2_session *session,
} }
return submit_headers_shared(session, flags, stream_id, &copy_pri_spec, return submit_headers_shared(session, flags, stream_id, &copy_pri_spec,
nva_copy, nvlen, data_prd, stream_user_data, nva_copy, nvlen, data_prd, stream_user_data);
attach_stream);
} }
int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id, int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id,
const nghttp2_nv *nva, size_t nvlen) { const nghttp2_nv *nva, size_t nvlen) {
return (int)submit_headers_shared_nva(session, NGHTTP2_FLAG_END_STREAM, return (int)submit_headers_shared_nva(session, NGHTTP2_FLAG_END_STREAM,
stream_id, NULL, nva, nvlen, NULL, NULL, stream_id, NULL, nva, nvlen, NULL,
0); NULL);
} }
int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
...@@ -169,7 +175,7 @@ int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, ...@@ -169,7 +175,7 @@ int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
} }
return submit_headers_shared_nva(session, flags, stream_id, pri_spec, nva, return submit_headers_shared_nva(session, flags, stream_id, pri_spec, nva,
nvlen, NULL, stream_user_data, 0); nvlen, NULL, stream_user_data);
} }
int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags _U_, int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags _U_,
...@@ -398,7 +404,7 @@ int32_t nghttp2_submit_request(nghttp2_session *session, ...@@ -398,7 +404,7 @@ int32_t nghttp2_submit_request(nghttp2_session *session,
flags = set_request_flags(pri_spec, data_prd); flags = set_request_flags(pri_spec, data_prd);
return submit_headers_shared_nva(session, flags, -1, pri_spec, nva, nvlen, return submit_headers_shared_nva(session, flags, -1, pri_spec, nva, nvlen,
data_prd, stream_user_data, 0); data_prd, stream_user_data);
} }
static uint8_t set_response_flags(const nghttp2_data_provider *data_prd) { static uint8_t set_response_flags(const nghttp2_data_provider *data_prd) {
...@@ -414,7 +420,7 @@ int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, ...@@ -414,7 +420,7 @@ int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,
const nghttp2_data_provider *data_prd) { const nghttp2_data_provider *data_prd) {
uint8_t flags = set_response_flags(data_prd); uint8_t flags = set_response_flags(data_prd);
return submit_headers_shared_nva(session, flags, stream_id, NULL, nva, nvlen, return submit_headers_shared_nva(session, flags, stream_id, NULL, nva, nvlen,
data_prd, NULL, 1); data_prd, NULL);
} }
int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, int nghttp2_submit_data(nghttp2_session *session, uint8_t flags,
......
...@@ -53,6 +53,7 @@ ...@@ -53,6 +53,7 @@
#include <deque> #include <deque>
#include <openssl/err.h> #include <openssl/err.h>
#include <openssl/dh.h>
#include <zlib.h> #include <zlib.h>
...@@ -105,7 +106,7 @@ Config::Config() ...@@ -105,7 +106,7 @@ Config::Config()
max_concurrent_streams(100), header_table_size(-1), port(0), max_concurrent_streams(100), header_table_size(-1), port(0),
verbose(false), daemon(false), verify_client(false), no_tls(false), verbose(false), daemon(false), verify_client(false), no_tls(false),
error_gzip(false), early_response(false), hexdump(false), error_gzip(false), early_response(false), hexdump(false),
echo_upload(false) {} echo_upload(false), no_content_length(false) {}
Config::~Config() {} Config::~Config() {}
...@@ -872,12 +873,14 @@ int Http2Handler::submit_file_response(const std::string &status, ...@@ -872,12 +873,14 @@ int Http2Handler::submit_file_response(const std::string &status,
std::string last_modified_str; std::string last_modified_str;
auto nva = make_array(http2::make_nv_ls(":status", status), auto nva = make_array(http2::make_nv_ls(":status", status),
http2::make_nv_ll("server", NGHTTPD_SERVER), http2::make_nv_ll("server", NGHTTPD_SERVER),
http2::make_nv_ls("content-length", content_length),
http2::make_nv_ll("cache-control", "max-age=3600"), http2::make_nv_ll("cache-control", "max-age=3600"),
http2::make_nv_ls("date", sessions_->get_cached_date()), http2::make_nv_ls("date", sessions_->get_cached_date()),
http2::make_nv_ll("", ""), http2::make_nv_ll("", ""), http2::make_nv_ll("", ""), http2::make_nv_ll("", ""),
http2::make_nv_ll("", "")); http2::make_nv_ll("", ""), http2::make_nv_ll("", ""));
size_t nvlen = 5; size_t nvlen = 4;
if (!get_config()->no_content_length) {
nva[nvlen++] = http2::make_nv_ls("content-length", content_length);
}
if (last_modified != 0) { if (last_modified != 0) {
last_modified_str = util::http_date(last_modified); last_modified_str = util::http_date(last_modified);
nva[nvlen++] = http2::make_nv_ls("last-modified", last_modified_str); nva[nvlen++] = http2::make_nv_ls("last-modified", last_modified_str);
...@@ -1087,7 +1090,9 @@ void prepare_echo_response(Stream *stream, Http2Handler *hd) { ...@@ -1087,7 +1090,9 @@ void prepare_echo_response(Stream *stream, Http2Handler *hd) {
Headers headers; Headers headers;
headers.emplace_back("nghttpd-response", "echo"); headers.emplace_back("nghttpd-response", "echo");
if (!hd->get_config()->no_content_length) {
headers.emplace_back("content-length", util::utos(length)); headers.emplace_back("content-length", util::utos(length));
}
hd->submit_response("200", stream->stream_id, headers, &data_prd); hd->submit_response("200", stream->stream_id, headers, &data_prd);
} }
......
...@@ -76,6 +76,7 @@ struct Config { ...@@ -76,6 +76,7 @@ struct Config {
bool early_response; bool early_response;
bool hexdump; bool hexdump;
bool echo_upload; bool echo_upload;
bool no_content_length;
Config(); Config();
~Config(); ~Config();
}; };
......
...@@ -39,12 +39,16 @@ using boost::asio::ip::tcp; ...@@ -39,12 +39,16 @@ using boost::asio::ip::tcp;
session::session(boost::asio::io_service &io_service, const std::string &host, session::session(boost::asio::io_service &io_service, const std::string &host,
const std::string &service) const std::string &service)
: impl_(make_unique<session_tcp_impl>(io_service, host, service)) {} : impl_(std::make_shared<session_tcp_impl>(io_service, host, service)) {
impl_->start_resolve(host, service);
}
session::session(boost::asio::io_service &io_service, session::session(boost::asio::io_service &io_service,
boost::asio::ssl::context &tls_ctx, const std::string &host, boost::asio::ssl::context &tls_ctx, const std::string &host,
const std::string &service) const std::string &service)
: impl_(make_unique<session_tls_impl>(io_service, tls_ctx, host, service)) { : impl_(std::make_shared<session_tls_impl>(io_service, tls_ctx, host,
service)) {
impl_->start_resolve(host, service);
} }
session::~session() {} session::~session() {}
...@@ -93,6 +97,14 @@ const request *session::submit(boost::system::error_code &ec, ...@@ -93,6 +97,14 @@ const request *session::submit(boost::system::error_code &ec,
return impl_->submit(ec, method, uri, std::move(cb), std::move(h)); return impl_->submit(ec, method, uri, std::move(cb), std::move(h));
} }
void session::connect_timeout(const boost::posix_time::time_duration &t) {
impl_->connect_timeout(t);
}
void session::read_timeout(const boost::posix_time::time_duration &t) {
impl_->read_timeout(t);
}
} // namespace client } // namespace client
} // namespace asio_http2 } // namespace asio_http2
} // nghttp2 } // nghttp2
...@@ -40,8 +40,10 @@ namespace client { ...@@ -40,8 +40,10 @@ namespace client {
session_impl::session_impl(boost::asio::io_service &io_service) session_impl::session_impl(boost::asio::io_service &io_service)
: wblen_(0), io_service_(io_service), resolver_(io_service), : wblen_(0), io_service_(io_service), resolver_(io_service),
session_(nullptr), data_pending_(nullptr), data_pendinglen_(0), deadline_(io_service), connect_timeout_(boost::posix_time::seconds(60)),
writing_(false), inside_callback_(false) {} read_timeout_(boost::posix_time::seconds(60)), session_(nullptr),
data_pending_(nullptr), data_pendinglen_(0), writing_(false),
inside_callback_(false), stopped_(false) {}
session_impl::~session_impl() { session_impl::~session_impl() {
// finish up all active stream // finish up all active stream
...@@ -56,8 +58,12 @@ session_impl::~session_impl() { ...@@ -56,8 +58,12 @@ session_impl::~session_impl() {
void session_impl::start_resolve(const std::string &host, void session_impl::start_resolve(const std::string &host,
const std::string &service) { const std::string &service) {
deadline_.expires_from_now(connect_timeout_);
auto self = this->shared_from_this();
resolver_.async_resolve({host, service}, resolver_.async_resolve({host, service},
[this](const boost::system::error_code &ec, [this, self](const boost::system::error_code &ec,
tcp::resolver::iterator endpoint_it) { tcp::resolver::iterator endpoint_it) {
if (ec) { if (ec) {
not_connected(ec); not_connected(ec);
...@@ -66,6 +72,25 @@ void session_impl::start_resolve(const std::string &host, ...@@ -66,6 +72,25 @@ void session_impl::start_resolve(const std::string &host,
start_connect(endpoint_it); start_connect(endpoint_it);
}); });
deadline_.async_wait(std::bind(&session_impl::handle_deadline, self));
}
void session_impl::handle_deadline() {
if (stopped_) {
return;
}
if (deadline_.expires_at() <=
boost::asio::deadline_timer::traits_type::now()) {
call_error_cb(boost::asio::error::timed_out);
stop();
deadline_.expires_at(boost::posix_time::pos_infin);
return;
}
deadline_.async_wait(
std::bind(&session_impl::handle_deadline, this->shared_from_this()));
} }
void session_impl::connected(tcp::resolver::iterator endpoint_it) { void session_impl::connected(tcp::resolver::iterator endpoint_it) {
...@@ -86,6 +111,7 @@ void session_impl::connected(tcp::resolver::iterator endpoint_it) { ...@@ -86,6 +111,7 @@ void session_impl::connected(tcp::resolver::iterator endpoint_it) {
void session_impl::not_connected(const boost::system::error_code &ec) { void session_impl::not_connected(const boost::system::error_code &ec) {
call_error_cb(ec); call_error_cb(ec);
stop();
} }
void session_impl::on_connect(connect_cb cb) { connect_cb_ = std::move(cb); } void session_impl::on_connect(connect_cb cb) { connect_cb_ = std::move(cb); }
...@@ -97,6 +123,9 @@ const connect_cb &session_impl::on_connect() const { return connect_cb_; } ...@@ -97,6 +123,9 @@ const connect_cb &session_impl::on_connect() const { return connect_cb_; }
const error_cb &session_impl::on_error() const { return error_cb_; } const error_cb &session_impl::on_error() const { return error_cb_; }
void session_impl::call_error_cb(const boost::system::error_code &ec) { void session_impl::call_error_cb(const boost::system::error_code &ec) {
if (stopped_) {
return;
}
auto &error_cb = on_error(); auto &error_cb = on_error();
if (!error_cb) { if (!error_cb) {
return; return;
...@@ -350,12 +379,20 @@ int session_impl::write_trailer(stream &strm, header_map h) { ...@@ -350,12 +379,20 @@ int session_impl::write_trailer(stream &strm, header_map h) {
} }
void session_impl::cancel(stream &strm, uint32_t error_code) { void session_impl::cancel(stream &strm, uint32_t error_code) {
if (stopped_) {
return;
}
nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE, strm.stream_id(), nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE, strm.stream_id(),
error_code); error_code);
signal_write(); signal_write();
} }
void session_impl::resume(stream &strm) { void session_impl::resume(stream &strm) {
if (stopped_) {
return;
}
nghttp2_session_resume_data(session_, strm.stream_id()); nghttp2_session_resume_data(session_, strm.stream_id());
signal_write(); signal_write();
} }
...@@ -396,6 +433,11 @@ const request *session_impl::submit(boost::system::error_code &ec, ...@@ -396,6 +433,11 @@ const request *session_impl::submit(boost::system::error_code &ec,
header_map h) { header_map h) {
ec.clear(); ec.clear();
if (stopped_) {
ec = make_error_code(static_cast<nghttp2_error>(NGHTTP2_INTERNAL_ERROR));
return nullptr;
}
http_parser_url u{}; http_parser_url u{};
// TODO Handle CONNECT method // TODO Handle CONNECT method
if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) { if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
...@@ -485,6 +527,10 @@ const request *session_impl::submit(boost::system::error_code &ec, ...@@ -485,6 +527,10 @@ const request *session_impl::submit(boost::system::error_code &ec,
} }
void session_impl::shutdown() { void session_impl::shutdown() {
if (stopped_) {
return;
}
nghttp2_session_terminate_session(session_, NGHTTP2_NO_ERROR); nghttp2_session_terminate_session(session_, NGHTTP2_NO_ERROR);
signal_write(); signal_write();
} }
...@@ -522,13 +568,21 @@ void session_impl::leave_callback() { ...@@ -522,13 +568,21 @@ void session_impl::leave_callback() {
} }
void session_impl::do_read() { void session_impl::do_read() {
read_socket([this](const boost::system::error_code &ec, if (stopped_) {
return;
}
deadline_.expires_from_now(read_timeout_);
auto self = this->shared_from_this();
read_socket([this, self](const boost::system::error_code &ec,
std::size_t bytes_transferred) { std::size_t bytes_transferred) {
if (ec) { if (ec) {
if (!should_stop()) { if (!should_stop()) {
call_error_cb(ec); call_error_cb(ec);
shutdown_socket();
} }
stop();
return; return;
} }
...@@ -541,7 +595,7 @@ void session_impl::do_read() { ...@@ -541,7 +595,7 @@ void session_impl::do_read() {
if (rv != static_cast<ssize_t>(bytes_transferred)) { if (rv != static_cast<ssize_t>(bytes_transferred)) {
call_error_cb(make_error_code( call_error_cb(make_error_code(
static_cast<nghttp2_error>(rv < 0 ? rv : NGHTTP2_ERR_PROTO))); static_cast<nghttp2_error>(rv < 0 ? rv : NGHTTP2_ERR_PROTO)));
shutdown_socket(); stop();
return; return;
} }
} }
...@@ -549,7 +603,7 @@ void session_impl::do_read() { ...@@ -549,7 +603,7 @@ void session_impl::do_read() {
do_write(); do_write();
if (should_stop()) { if (should_stop()) {
shutdown_socket(); stop();
return; return;
} }
...@@ -558,6 +612,10 @@ void session_impl::do_read() { ...@@ -558,6 +612,10 @@ void session_impl::do_read() {
} }
void session_impl::do_write() { void session_impl::do_write() {
if (stopped_) {
return;
}
if (writing_) { if (writing_) {
return; return;
} }
...@@ -579,7 +637,7 @@ void session_impl::do_write() { ...@@ -579,7 +637,7 @@ void session_impl::do_write() {
auto n = nghttp2_session_mem_send(session_, &data); auto n = nghttp2_session_mem_send(session_, &data);
if (n < 0) { if (n < 0) {
call_error_cb(make_error_code(static_cast<nghttp2_error>(n))); call_error_cb(make_error_code(static_cast<nghttp2_error>(n)));
shutdown_socket(); stop();
return; return;
} }
...@@ -601,15 +659,25 @@ void session_impl::do_write() { ...@@ -601,15 +659,25 @@ void session_impl::do_write() {
} }
if (wblen_ == 0) { if (wblen_ == 0) {
if (should_stop()) {
stop();
}
return; return;
} }
writing_ = true; writing_ = true;
write_socket([this](const boost::system::error_code &ec, std::size_t n) { // Reset read deadline here, because normally client is sending
// something, it does not expect timeout while doing it.
deadline_.expires_from_now(read_timeout_);
auto self = this->shared_from_this();
write_socket(
[this, self](const boost::system::error_code &ec, std::size_t n) {
if (ec) { if (ec) {
call_error_cb(ec); call_error_cb(ec);
shutdown_socket(); stop();
return; return;
} }
...@@ -620,6 +688,24 @@ void session_impl::do_write() { ...@@ -620,6 +688,24 @@ void session_impl::do_write() {
}); });
} }
void session_impl::stop() {
if (stopped_) {
return;
}
shutdown_socket();
deadline_.cancel();
stopped_ = true;
}
void session_impl::connect_timeout(const boost::posix_time::time_duration &t) {
connect_timeout_ = t;
}
void session_impl::read_timeout(const boost::posix_time::time_duration &t) {
read_timeout_ = t;
}
} // namespace client } // namespace client
} // namespace asio_http2 } // namespace asio_http2
} // nghttp2 } // nghttp2
...@@ -41,7 +41,7 @@ class stream; ...@@ -41,7 +41,7 @@ class stream;
using boost::asio::ip::tcp; using boost::asio::ip::tcp;
class session_impl { class session_impl : public std::enable_shared_from_this<session_impl> {
public: public:
session_impl(boost::asio::io_service &io_service); session_impl(boost::asio::io_service &io_service);
virtual ~session_impl(); virtual ~session_impl();
...@@ -91,6 +91,11 @@ public: ...@@ -91,6 +91,11 @@ public:
void do_read(); void do_read();
void do_write(); void do_write();
void connect_timeout(const boost::posix_time::time_duration &t);
void read_timeout(const boost::posix_time::time_duration &t);
void stop();
protected: protected:
boost::array<uint8_t, 8_k> rb_; boost::array<uint8_t, 8_k> rb_;
boost::array<uint8_t, 64_k> wb_; boost::array<uint8_t, 64_k> wb_;
...@@ -100,6 +105,7 @@ private: ...@@ -100,6 +105,7 @@ private:
bool should_stop() const; bool should_stop() const;
bool setup_session(); bool setup_session();
void call_error_cb(const boost::system::error_code &ec); void call_error_cb(const boost::system::error_code &ec);
void handle_deadline();
boost::asio::io_service &io_service_; boost::asio::io_service &io_service_;
tcp::resolver resolver_; tcp::resolver resolver_;
...@@ -109,6 +115,10 @@ private: ...@@ -109,6 +115,10 @@ private:
connect_cb connect_cb_; connect_cb connect_cb_;
error_cb error_cb_; error_cb error_cb_;
boost::asio::deadline_timer deadline_;
boost::posix_time::time_duration connect_timeout_;
boost::posix_time::time_duration read_timeout_;
nghttp2_session *session_; nghttp2_session *session_;
const uint8_t *data_pending_; const uint8_t *data_pending_;
...@@ -116,6 +126,7 @@ private: ...@@ -116,6 +126,7 @@ private:
bool writing_; bool writing_;
bool inside_callback_; bool inside_callback_;
bool stopped_;
}; };
} // namespace client } // namespace client
......
...@@ -31,9 +31,7 @@ namespace client { ...@@ -31,9 +31,7 @@ namespace client {
session_tcp_impl::session_tcp_impl(boost::asio::io_service &io_service, session_tcp_impl::session_tcp_impl(boost::asio::io_service &io_service,
const std::string &host, const std::string &host,
const std::string &service) const std::string &service)
: session_impl(io_service), socket_(io_service) { : session_impl(io_service), socket_(io_service) {}
start_resolve(host, service);
}
session_tcp_impl::~session_tcp_impl() {} session_tcp_impl::~session_tcp_impl() {}
...@@ -62,7 +60,10 @@ void session_tcp_impl::write_socket( ...@@ -62,7 +60,10 @@ void session_tcp_impl::write_socket(
boost::asio::async_write(socket_, boost::asio::buffer(wb_, wblen_), h); boost::asio::async_write(socket_, boost::asio::buffer(wb_, wblen_), h);
} }
void session_tcp_impl::shutdown_socket() { socket_.close(); } void session_tcp_impl::shutdown_socket() {
boost::system::error_code ignored_ec;
socket_.close(ignored_ec);
}
} // namespace client } // namespace client
} // namespace asio_http2 } // namespace asio_http2
......
...@@ -38,8 +38,6 @@ session_tls_impl::session_tls_impl(boost::asio::io_service &io_service, ...@@ -38,8 +38,6 @@ session_tls_impl::session_tls_impl(boost::asio::io_service &io_service,
// ssl::context::set_verify_mode(boost::asio::ssl::verify_peer) is // ssl::context::set_verify_mode(boost::asio::ssl::verify_peer) is
// not used, which is what we want. // not used, which is what we want.
socket_.set_verify_callback(boost::asio::ssl::rfc2818_verification(host)); socket_.set_verify_callback(boost::asio::ssl::rfc2818_verification(host));
start_resolve(host, service);
} }
session_tls_impl::~session_tls_impl() {} session_tls_impl::~session_tls_impl() {}
...@@ -85,7 +83,8 @@ void session_tls_impl::write_socket( ...@@ -85,7 +83,8 @@ void session_tls_impl::write_socket(
} }
void session_tls_impl::shutdown_socket() { void session_tls_impl::shutdown_socket() {
socket_.async_shutdown([](const boost::system::error_code &ec) {}); boost::system::error_code ignored_ec;
socket_.lowest_layer().close(ignored_ec);
} }
} // namespace client } // namespace client
......
...@@ -92,6 +92,11 @@ boost::asio::io_service &io_service_pool::get_io_service() { ...@@ -92,6 +92,11 @@ boost::asio::io_service &io_service_pool::get_io_service() {
return io_service; return io_service;
} }
const std::vector<std::shared_ptr<boost::asio::io_service>> &
io_service_pool::io_services() const {
return io_services_;
}
} // namespace asio_http2 } // namespace asio_http2
} // namespace nghttp2 } // namespace nghttp2
...@@ -70,6 +70,10 @@ public: ...@@ -70,6 +70,10 @@ public:
/// Get an io_service to use. /// Get an io_service to use.
boost::asio::io_service &get_io_service(); boost::asio::io_service &get_io_service();
/// Get access to all io_service objects.
const std::vector<std::shared_ptr<boost::asio::io_service>> &
io_services() const;
private: private:
/// The pool of io_services. /// The pool of io_services.
std::vector<std::shared_ptr<boost::asio::io_service>> io_services_; std::vector<std::shared_ptr<boost::asio::io_service>> io_services_;
......
...@@ -44,8 +44,12 @@ namespace nghttp2 { ...@@ -44,8 +44,12 @@ namespace nghttp2 {
namespace asio_http2 { namespace asio_http2 {
namespace server { namespace server {
server::server(std::size_t io_service_pool_size) server::server(std::size_t io_service_pool_size,
: io_service_pool_(io_service_pool_size) {} const boost::posix_time::time_duration &tls_handshake_timeout,
const boost::posix_time::time_duration &read_timeout)
: io_service_pool_(io_service_pool_size),
tls_handshake_timeout_(tls_handshake_timeout),
read_timeout_(read_timeout) {}
boost::system::error_code boost::system::error_code
server::listen_and_serve(boost::system::error_code &ec, server::listen_and_serve(boost::system::error_code &ec,
...@@ -121,7 +125,8 @@ boost::system::error_code server::bind_and_listen(boost::system::error_code &ec, ...@@ -121,7 +125,8 @@ boost::system::error_code server::bind_and_listen(boost::system::error_code &ec,
void server::start_accept(boost::asio::ssl::context &tls_context, void server::start_accept(boost::asio::ssl::context &tls_context,
tcp::acceptor &acceptor, serve_mux &mux) { tcp::acceptor &acceptor, serve_mux &mux) {
auto new_connection = std::make_shared<connection<ssl_socket>>( auto new_connection = std::make_shared<connection<ssl_socket>>(
mux, io_service_pool_.get_io_service(), tls_context); mux, tls_handshake_timeout_, read_timeout_,
io_service_pool_.get_io_service(), tls_context);
acceptor.async_accept( acceptor.async_accept(
new_connection->socket().lowest_layer(), new_connection->socket().lowest_layer(),
...@@ -130,14 +135,17 @@ void server::start_accept(boost::asio::ssl::context &tls_context, ...@@ -130,14 +135,17 @@ void server::start_accept(boost::asio::ssl::context &tls_context,
if (!e) { if (!e) {
new_connection->socket().lowest_layer().set_option( new_connection->socket().lowest_layer().set_option(
tcp::no_delay(true)); tcp::no_delay(true));
new_connection->start_tls_handshake_deadline();
new_connection->socket().async_handshake( new_connection->socket().async_handshake(
boost::asio::ssl::stream_base::server, boost::asio::ssl::stream_base::server,
[new_connection](const boost::system::error_code &e) { [new_connection](const boost::system::error_code &e) {
if (e) { if (e) {
new_connection->stop();
return; return;
} }
if (!tls_h2_negotiated(new_connection->socket())) { if (!tls_h2_negotiated(new_connection->socket())) {
new_connection->stop();
return; return;
} }
...@@ -151,13 +159,15 @@ void server::start_accept(boost::asio::ssl::context &tls_context, ...@@ -151,13 +159,15 @@ void server::start_accept(boost::asio::ssl::context &tls_context,
void server::start_accept(tcp::acceptor &acceptor, serve_mux &mux) { void server::start_accept(tcp::acceptor &acceptor, serve_mux &mux) {
auto new_connection = std::make_shared<connection<tcp::socket>>( auto new_connection = std::make_shared<connection<tcp::socket>>(
mux, io_service_pool_.get_io_service()); mux, tls_handshake_timeout_, read_timeout_,
io_service_pool_.get_io_service());
acceptor.async_accept( acceptor.async_accept(
new_connection->socket(), [this, &acceptor, &mux, new_connection]( new_connection->socket(), [this, &acceptor, &mux, new_connection](
const boost::system::error_code &e) { const boost::system::error_code &e) {
if (!e) { if (!e) {
new_connection->socket().set_option(tcp::no_delay(true)); new_connection->socket().set_option(tcp::no_delay(true));
new_connection->start_read_deadline();
new_connection->start(); new_connection->start();
} }
...@@ -169,6 +179,11 @@ void server::stop() { io_service_pool_.stop(); } ...@@ -169,6 +179,11 @@ void server::stop() { io_service_pool_.stop(); }
void server::join() { io_service_pool_.join(); } void server::join() { io_service_pool_.join(); }
const std::vector<std::shared_ptr<boost::asio::io_service>> &
server::io_services() const {
return io_service_pool_.io_services();
}
} // namespace server } // namespace server
} // namespace asio_http2 } // namespace asio_http2
} // namespace nghttp2 } // namespace nghttp2
...@@ -63,7 +63,9 @@ using ssl_socket = boost::asio::ssl::stream<tcp::socket>; ...@@ -63,7 +63,9 @@ using ssl_socket = boost::asio::ssl::stream<tcp::socket>;
class server : private boost::noncopyable { class server : private boost::noncopyable {
public: public:
explicit server(std::size_t io_service_pool_size); explicit server(std::size_t io_service_pool_size,
const boost::posix_time::time_duration &tls_handshake_timeout,
const boost::posix_time::time_duration &read_timeout);
boost::system::error_code boost::system::error_code
listen_and_serve(boost::system::error_code &ec, listen_and_serve(boost::system::error_code &ec,
...@@ -73,6 +75,10 @@ public: ...@@ -73,6 +75,10 @@ public:
void join(); void join();
void stop(); void stop();
/// Get access to all io_service objects.
const std::vector<std::shared_ptr<boost::asio::io_service>> &
io_services() const;
private: private:
/// Initiate an asynchronous accept operation. /// Initiate an asynchronous accept operation.
void start_accept(tcp::acceptor &acceptor, serve_mux &mux); void start_accept(tcp::acceptor &acceptor, serve_mux &mux);
...@@ -94,6 +100,9 @@ private: ...@@ -94,6 +100,9 @@ private:
std::vector<tcp::acceptor> acceptors_; std::vector<tcp::acceptor> acceptors_;
std::unique_ptr<boost::asio::ssl::context> ssl_ctx_; std::unique_ptr<boost::asio::ssl::context> ssl_ctx_;
boost::posix_time::time_duration tls_handshake_timeout_;
boost::posix_time::time_duration read_timeout_;
}; };
} // namespace server } // namespace server
......
...@@ -64,15 +64,23 @@ class connection : public std::enable_shared_from_this<connection<socket_type>>, ...@@ -64,15 +64,23 @@ class connection : public std::enable_shared_from_this<connection<socket_type>>,
public: public:
/// Construct a connection with the given io_service. /// Construct a connection with the given io_service.
template <typename... SocketArgs> template <typename... SocketArgs>
explicit connection(serve_mux &mux, SocketArgs &&... args) explicit connection(
: socket_(std::forward<SocketArgs>(args)...), mux_(mux), writing_(false) { serve_mux &mux,
} const boost::posix_time::time_duration &tls_handshake_timeout,
const boost::posix_time::time_duration &read_timeout,
SocketArgs &&... args)
: socket_(std::forward<SocketArgs>(args)...), mux_(mux),
deadline_(socket_.get_io_service()),
tls_handshake_timeout_(tls_handshake_timeout),
read_timeout_(read_timeout), writing_(false), stopped_(false) {}
/// Start the first asynchronous operation for the connection. /// Start the first asynchronous operation for the connection.
void start() { void start() {
handler_ = std::make_shared<http2_handler>(socket_.get_io_service(), handler_ = std::make_shared<http2_handler>(
socket_.get_io_service(), socket_.lowest_layer().remote_endpoint(),
[this]() { do_write(); }, mux_); [this]() { do_write(); }, mux_);
if (handler_->start() != 0) { if (handler_->start() != 0) {
stop();
return; return;
} }
do_read(); do_read();
...@@ -80,26 +88,61 @@ public: ...@@ -80,26 +88,61 @@ public:
socket_type &socket() { return socket_; } socket_type &socket() { return socket_; }
void start_tls_handshake_deadline() {
deadline_.expires_from_now(tls_handshake_timeout_);
deadline_.async_wait(
std::bind(&connection::handle_deadline, this->shared_from_this()));
}
void start_read_deadline() {
deadline_.expires_from_now(read_timeout_);
deadline_.async_wait(
std::bind(&connection::handle_deadline, this->shared_from_this()));
}
void handle_deadline() {
if (stopped_) {
return;
}
if (deadline_.expires_at() <=
boost::asio::deadline_timer::traits_type::now()) {
stop();
deadline_.expires_at(boost::posix_time::pos_infin);
return;
}
deadline_.async_wait(
std::bind(&connection::handle_deadline, this->shared_from_this()));
}
void do_read() { void do_read() {
auto self = this->shared_from_this(); auto self = this->shared_from_this();
deadline_.expires_from_now(read_timeout_);
socket_.async_read_some( socket_.async_read_some(
boost::asio::buffer(buffer_), boost::asio::buffer(buffer_),
[this, self](const boost::system::error_code &e, [this, self](const boost::system::error_code &e,
std::size_t bytes_transferred) { std::size_t bytes_transferred) {
if (!e) { if (e) {
stop();
return;
}
if (handler_->on_read(buffer_, bytes_transferred) != 0) { if (handler_->on_read(buffer_, bytes_transferred) != 0) {
stop();
return; return;
} }
do_write(); do_write();
if (!writing_ && handler_->should_stop()) { if (!writing_ && handler_->should_stop()) {
stop();
return; return;
} }
do_read(); do_read();
}
// If an error occurs then no new asynchronous operations are // If an error occurs then no new asynchronous operations are
// started. This means that all shared_ptr references to the // started. This means that all shared_ptr references to the
...@@ -122,23 +165,34 @@ public: ...@@ -122,23 +165,34 @@ public:
rv = handler_->on_write(outbuf_, nwrite); rv = handler_->on_write(outbuf_, nwrite);
if (rv != 0) { if (rv != 0) {
stop();
return; return;
} }
if (nwrite == 0) { if (nwrite == 0) {
if (handler_->should_stop()) {
stop();
}
return; return;
} }
writing_ = true; writing_ = true;
// Reset read deadline here, because normally client is sending
// something, it does not expect timeout while doing it.
deadline_.expires_from_now(read_timeout_);
boost::asio::async_write( boost::asio::async_write(
socket_, boost::asio::buffer(outbuf_, nwrite), socket_, boost::asio::buffer(outbuf_, nwrite),
[this, self](const boost::system::error_code &e, std::size_t) { [this, self](const boost::system::error_code &e, std::size_t) {
if (!e) { if (e) {
stop();
return;
}
writing_ = false; writing_ = false;
do_write(); do_write();
}
}); });
// No new asynchronous operations are started. This means that all // No new asynchronous operations are started. This means that all
...@@ -147,6 +201,17 @@ public: ...@@ -147,6 +201,17 @@ public:
// returns. The connection class's destructor closes the socket. // returns. The connection class's destructor closes the socket.
} }
void stop() {
if (stopped_) {
return;
}
stopped_ = true;
boost::system::error_code ignored_ec;
socket_.lowest_layer().close(ignored_ec);
deadline_.cancel();
}
private: private:
socket_type socket_; socket_type socket_;
...@@ -159,7 +224,12 @@ private: ...@@ -159,7 +224,12 @@ private:
boost::array<uint8_t, 64_k> outbuf_; boost::array<uint8_t, 64_k> outbuf_;
boost::asio::deadline_timer deadline_;
boost::posix_time::time_duration tls_handshake_timeout_;
boost::posix_time::time_duration read_timeout_;
bool writing_; bool writing_;
bool stopped_;
}; };
} // namespace server } // namespace server
......
...@@ -69,6 +69,14 @@ void http2::num_threads(size_t num_threads) { impl_->num_threads(num_threads); } ...@@ -69,6 +69,14 @@ void http2::num_threads(size_t num_threads) { impl_->num_threads(num_threads); }
void http2::backlog(int backlog) { impl_->backlog(backlog); } void http2::backlog(int backlog) { impl_->backlog(backlog); }
void http2::tls_handshake_timeout(const boost::posix_time::time_duration &t) {
impl_->tls_handshake_timeout(t);
}
void http2::read_timeout(const boost::posix_time::time_duration &t) {
impl_->read_timeout(t);
}
bool http2::handle(std::string pattern, request_cb cb) { 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));
} }
...@@ -77,6 +85,11 @@ void http2::stop() { impl_->stop(); } ...@@ -77,6 +85,11 @@ void http2::stop() { impl_->stop(); }
void http2::join() { return impl_->join(); } void http2::join() { return impl_->join(); }
const std::vector<std::shared_ptr<boost::asio::io_service>> &
http2::io_services() const {
return impl_->io_services();
}
} // namespace server } // namespace server
} // namespace asio_http2 } // namespace asio_http2
......
...@@ -136,6 +136,9 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, ...@@ -136,6 +136,9 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
break; break;
} }
auto &req = strm->request().impl();
req.remote_endpoint(handler->remote_endpoint());
handler->call_on_request(*strm); handler->call_on_request(*strm);
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
...@@ -225,8 +228,9 @@ int on_frame_not_send_callback(nghttp2_session *session, ...@@ -225,8 +228,9 @@ int on_frame_not_send_callback(nghttp2_session *session,
} // namespace } // namespace
http2_handler::http2_handler(boost::asio::io_service &io_service, http2_handler::http2_handler(boost::asio::io_service &io_service,
boost::asio::ip::tcp::endpoint ep,
connection_write writefun, serve_mux &mux) connection_write writefun, serve_mux &mux)
: writefun_(writefun), mux_(mux), io_service_(io_service), : writefun_(writefun), mux_(mux), io_service_(io_service), remote_ep_(ep),
session_(nullptr), buf_(nullptr), buflen_(0), inside_callback_(false), session_(nullptr), buf_(nullptr), buflen_(0), inside_callback_(false),
tstamp_cached_(time(nullptr)), tstamp_cached_(time(nullptr)),
formatted_date_(util::http_date(tstamp_cached_)) {} formatted_date_(util::http_date(tstamp_cached_)) {}
...@@ -449,6 +453,10 @@ response *http2_handler::push_promise(boost::system::error_code &ec, ...@@ -449,6 +453,10 @@ response *http2_handler::push_promise(boost::system::error_code &ec,
boost::asio::io_service &http2_handler::io_service() { return io_service_; } boost::asio::io_service &http2_handler::io_service() { return io_service_; }
const boost::asio::ip::tcp::endpoint &http2_handler::remote_endpoint() {
return remote_ep_;
}
callback_guard::callback_guard(http2_handler &h) : handler(h) { callback_guard::callback_guard(http2_handler &h) : handler(h) {
handler.enter_callback(); handler.enter_callback();
} }
......
...@@ -53,7 +53,8 @@ using connection_write = std::function<void(void)>; ...@@ -53,7 +53,8 @@ using connection_write = std::function<void(void)>;
class http2_handler : public std::enable_shared_from_this<http2_handler> { class http2_handler : public std::enable_shared_from_this<http2_handler> {
public: public:
http2_handler(boost::asio::io_service &io_service, connection_write writefun, http2_handler(boost::asio::io_service &io_service,
boost::asio::ip::tcp::endpoint ep, connection_write writefun,
serve_mux &mux); serve_mux &mux);
~http2_handler(); ~http2_handler();
...@@ -89,6 +90,8 @@ public: ...@@ -89,6 +90,8 @@ public:
boost::asio::io_service &io_service(); boost::asio::io_service &io_service();
const boost::asio::ip::tcp::endpoint &remote_endpoint();
const std::string &http_date(); const std::string &http_date();
template <size_t N> template <size_t N>
...@@ -152,6 +155,7 @@ private: ...@@ -152,6 +155,7 @@ private:
connection_write writefun_; connection_write writefun_;
serve_mux &mux_; serve_mux &mux_;
boost::asio::io_service &io_service_; boost::asio::io_service &io_service_;
boost::asio::ip::tcp::endpoint remote_ep_;
nghttp2_session *session_; nghttp2_session *session_;
const uint8_t *buf_; const uint8_t *buf_;
std::size_t buflen_; std::size_t buflen_;
......
...@@ -37,12 +37,16 @@ namespace asio_http2 { ...@@ -37,12 +37,16 @@ namespace asio_http2 {
namespace server { namespace server {
http2_impl::http2_impl() : num_threads_(1), backlog_(-1) {} http2_impl::http2_impl()
: num_threads_(1), backlog_(-1),
tls_handshake_timeout_(boost::posix_time::seconds(60)),
read_timeout_(boost::posix_time::seconds(60)) {}
boost::system::error_code http2_impl::listen_and_serve( 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_, tls_handshake_timeout_, read_timeout_));
return server_->listen_and_serve(ec, tls_context, address, port, backlog_, return server_->listen_and_serve(ec, tls_context, address, port, backlog_,
mux_, asynchronous); mux_, asynchronous);
} }
...@@ -51,6 +55,15 @@ void http2_impl::num_threads(size_t num_threads) { num_threads_ = num_threads; } ...@@ -51,6 +55,15 @@ void http2_impl::num_threads(size_t num_threads) { num_threads_ = num_threads; }
void http2_impl::backlog(int backlog) { backlog_ = backlog; } void http2_impl::backlog(int backlog) { backlog_ = backlog; }
void http2_impl::tls_handshake_timeout(
const boost::posix_time::time_duration &t) {
tls_handshake_timeout_ = t;
}
void http2_impl::read_timeout(const boost::posix_time::time_duration &t) {
read_timeout_ = t;
}
bool http2_impl::handle(std::string pattern, request_cb cb) { 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));
} }
...@@ -59,6 +72,11 @@ void http2_impl::stop() { return server_->stop(); } ...@@ -59,6 +72,11 @@ void http2_impl::stop() { return server_->stop(); }
void http2_impl::join() { return server_->join(); } void http2_impl::join() { return server_->join(); }
const std::vector<std::shared_ptr<boost::asio::io_service>> &
http2_impl::io_services() const {
return server_->io_services();
}
} // namespace server } // namespace server
} // namespace asio_http2 } // namespace asio_http2
......
...@@ -47,15 +47,21 @@ public: ...@@ -47,15 +47,21 @@ public:
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);
void tls_handshake_timeout(const boost::posix_time::time_duration &t);
void read_timeout(const boost::posix_time::time_duration &t);
bool handle(std::string pattern, request_cb cb); bool handle(std::string pattern, request_cb cb);
void stop(); void stop();
void join(); void join();
const std::vector<std::shared_ptr<boost::asio::io_service>> &
io_services() const;
private: private:
std::unique_ptr<server> server_; std::unique_ptr<server> server_;
std::size_t num_threads_; std::size_t num_threads_;
int backlog_; int backlog_;
serve_mux mux_; serve_mux mux_;
boost::posix_time::time_duration tls_handshake_timeout_;
boost::posix_time::time_duration read_timeout_;
}; };
} // namespace server } // namespace server
......
...@@ -50,6 +50,10 @@ void request::on_data(data_cb cb) const { ...@@ -50,6 +50,10 @@ void request::on_data(data_cb cb) const {
request_impl &request::impl() const { return *impl_; } request_impl &request::impl() const { return *impl_; }
const boost::asio::ip::tcp::endpoint &request::remote_endpoint() const {
return impl_->remote_endpoint();
}
} // namespace server } // namespace server
} // namespace asio_http2 } // namespace asio_http2
} // namespace nghttp2 } // namespace nghttp2
...@@ -54,6 +54,14 @@ void request_impl::call_on_data(const uint8_t *data, std::size_t len) { ...@@ -54,6 +54,14 @@ void request_impl::call_on_data(const uint8_t *data, std::size_t len) {
} }
} }
const boost::asio::ip::tcp::endpoint &request_impl::remote_endpoint() const {
return remote_ep_;
}
void request_impl::remote_endpoint(boost::asio::ip::tcp::endpoint ep) {
remote_ep_ = std::move(ep);
}
} // namespace server } // namespace server
} // namespace asio_http2 } // namespace asio_http2
} // namespace nghttp2 } // namespace nghttp2
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "nghttp2_config.h" #include "nghttp2_config.h"
#include <nghttp2/asio_http2_server.h> #include <nghttp2/asio_http2_server.h>
#include <boost/asio/ip/tcp.hpp>
namespace nghttp2 { namespace nghttp2 {
namespace asio_http2 { namespace asio_http2 {
...@@ -54,12 +55,16 @@ public: ...@@ -54,12 +55,16 @@ public:
void stream(class stream *s); void stream(class stream *s);
void call_on_data(const uint8_t *data, std::size_t len); void call_on_data(const uint8_t *data, std::size_t len);
const boost::asio::ip::tcp::endpoint &remote_endpoint() const;
void remote_endpoint(boost::asio::ip::tcp::endpoint ep);
private: private:
class stream *strm_; class stream *strm_;
header_map header_; header_map header_;
std::string method_; std::string method_;
uri_ref uri_; uri_ref uri_;
data_cb on_data_cb_; data_cb on_data_cb_;
boost::asio::ip::tcp::endpoint remote_ep_;
}; };
} // namespace server } // namespace server
......
This diff is collapsed.
...@@ -112,7 +112,6 @@ struct Config { ...@@ -112,7 +112,6 @@ struct Config {
}; };
struct RequestStat { struct RequestStat {
RequestStat();
// time point when request was sent // time point when request was sent
std::chrono::steady_clock::time_point request_time; std::chrono::steady_clock::time_point request_time;
// time point when stream was closed // time point when stream was closed
...@@ -208,9 +207,21 @@ enum ClientState { CLIENT_IDLE, CLIENT_CONNECTED }; ...@@ -208,9 +207,21 @@ enum ClientState { CLIENT_IDLE, CLIENT_CONNECTED };
struct Client; struct Client;
// We use systematic sampling method
struct Sampling {
// sampling interval
double interval;
// cumulative value of interval, and the next point is the integer
// rounded up from this value.
double point;
// number of samples seen, including discarded samples.
size_t n;
};
struct Worker { struct Worker {
std::vector<std::unique_ptr<Client>> clients;
Stats stats; Stats stats;
Sampling request_times_smp;
Sampling client_smp;
struct ev_loop *loop; struct ev_loop *loop;
SSL_CTX *ssl_ctx; SSL_CTX *ssl_ctx;
Config *config; Config *config;
...@@ -226,24 +237,32 @@ struct Worker { ...@@ -226,24 +237,32 @@ struct Worker {
// at most nreqs_rem clients get an extra request // at most nreqs_rem clients get an extra request
size_t nreqs_rem; size_t nreqs_rem;
size_t rate; size_t rate;
// maximum number of samples in this worker thread
size_t max_samples;
ev_timer timeout_watcher; ev_timer timeout_watcher;
// The next client ID this worker assigns // The next client ID this worker assigns
uint32_t next_client_id; uint32_t next_client_id;
Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t nreq_todo, size_t nclients, Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t nreq_todo, size_t nclients,
size_t rate, Config *config); size_t rate, size_t max_samples, Config *config);
~Worker(); ~Worker();
Worker(Worker &&o) = default; Worker(Worker &&o) = default;
void run(); void run();
void sample_req_stat(RequestStat *req_stat);
void sample_client_stat(ClientStat *cstat);
void report_progress();
void report_rate_progress();
}; };
struct Stream { struct Stream {
RequestStat req_stat;
int status_success; int status_success;
Stream(); Stream();
}; };
struct Client { struct Client {
std::unordered_map<int32_t, Stream> streams; std::unordered_map<int32_t, Stream> streams;
ClientStat cstat;
std::unique_ptr<Session> session; std::unique_ptr<Session> session;
ev_io wev; ev_io wev;
ev_io rev; ev_io rev;
...@@ -288,7 +307,6 @@ struct Client { ...@@ -288,7 +307,6 @@ struct Client {
void process_request_failure(); void process_request_failure();
void process_timedout_streams(); void process_timedout_streams();
void process_abandoned_streams(); void process_abandoned_streams();
void report_progress();
void report_tls_info(); void report_tls_info();
void report_app_info(); void report_app_info();
void terminate_session(); void terminate_session();
...@@ -318,8 +336,12 @@ struct Client { ...@@ -318,8 +336,12 @@ struct Client {
// |success| == true means that the request/response was exchanged // |success| == true means that the request/response was exchanged
// |successfully, but it does not mean response carried successful // |successfully, but it does not mean response carried successful
// |HTTP status code. // |HTTP status code.
void on_stream_close(int32_t stream_id, bool success, RequestStat *req_stat, void on_stream_close(int32_t stream_id, bool success, bool final = false);
bool final = false); // Returns RequestStat for |stream_id|. This function must be
// called after on_request(stream_id), and before
// on_stream_close(stream_id, ...). Otherwise, this will return
// nullptr.
RequestStat *get_req_stat(int32_t stream_id);
void record_request_time(RequestStat *req_stat); void record_request_time(RequestStat *req_stat);
void record_connect_start_time(); void record_connect_start_time();
......
...@@ -80,9 +80,11 @@ int htp_msg_completecb(http_parser *htp) { ...@@ -80,9 +80,11 @@ int htp_msg_completecb(http_parser *htp) {
auto client = session->get_client(); auto client = session->get_client();
auto final = http_should_keep_alive(htp) == 0; auto final = http_should_keep_alive(htp) == 0;
client->on_stream_close(session->stream_resp_counter_, true, auto req_stat = client->get_req_stat(session->stream_resp_counter_);
session->req_stats_[session->stream_resp_counter_],
final); assert(req_stat);
client->on_stream_close(session->stream_resp_counter_, true, final);
session->stream_resp_counter_ += 2; session->stream_resp_counter_ += 2;
...@@ -150,7 +152,7 @@ http_parser_settings htp_hooks = { ...@@ -150,7 +152,7 @@ http_parser_settings htp_hooks = {
void Http1Session::on_connect() { client_->signal_write(); } void Http1Session::on_connect() { client_->signal_write(); }
int Http1Session::submit_request(RequestStat *req_stat) { int Http1Session::submit_request() {
auto config = client_->worker->config; auto config = client_->worker->config;
const auto &req = config->h1reqs[client_->reqidx]; const auto &req = config->h1reqs[client_->reqidx];
client_->reqidx++; client_->reqidx++;
...@@ -159,13 +161,13 @@ int Http1Session::submit_request(RequestStat *req_stat) { ...@@ -159,13 +161,13 @@ int Http1Session::submit_request(RequestStat *req_stat) {
client_->reqidx = 0; client_->reqidx = 0;
} }
assert(req_stat); client_->on_request(stream_req_counter_);
auto req_stat = client_->get_req_stat(stream_req_counter_);
client_->record_request_time(req_stat); client_->record_request_time(req_stat);
client_->wb.write(req.c_str(), req.size()); client_->wb.write(req.c_str(), req.size());
client_->on_request(stream_req_counter_);
req_stats_[stream_req_counter_] = req_stat;
// increment for next request // increment for next request
stream_req_counter_ += 2; stream_req_counter_ += 2;
......
...@@ -38,14 +38,13 @@ public: ...@@ -38,14 +38,13 @@ public:
Http1Session(Client *client); Http1Session(Client *client);
virtual ~Http1Session(); virtual ~Http1Session();
virtual void on_connect(); virtual void on_connect();
virtual int submit_request(RequestStat *req_stat); virtual int submit_request();
virtual int on_read(const uint8_t *data, size_t len); virtual int on_read(const uint8_t *data, size_t len);
virtual int on_write(); virtual int on_write();
virtual void terminate(); virtual void terminate();
Client *get_client(); Client *get_client();
int32_t stream_req_counter_; int32_t stream_req_counter_;
int32_t stream_resp_counter_; int32_t stream_resp_counter_;
std::unordered_map<int32_t, RequestStat *> req_stats_;
private: private:
Client *client_; Client *client_;
......
...@@ -89,12 +89,25 @@ namespace { ...@@ -89,12 +89,25 @@ namespace {
int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, 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) {
auto client = static_cast<Client *>(user_data); auto client = static_cast<Client *>(user_data);
auto req_stat = static_cast<RequestStat *>( client->on_stream_close(stream_id, error_code == NGHTTP2_NO_ERROR);
nghttp2_session_get_stream_user_data(session, stream_id));
if (!req_stat) { return 0;
}
} // namespace
namespace {
int on_frame_not_send_callback(nghttp2_session *session,
const nghttp2_frame *frame, int lib_error_code,
void *user_data) {
if (frame->hd.type != NGHTTP2_HEADERS ||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
return 0; return 0;
} }
client->on_stream_close(stream_id, error_code == NGHTTP2_NO_ERROR, req_stat);
auto client = static_cast<Client *>(user_data);
// request was not sent. Mark it as error.
client->on_stream_close(frame->hd.stream_id, false);
return 0; return 0;
} }
} // namespace } // namespace
...@@ -108,9 +121,7 @@ int before_frame_send_callback(nghttp2_session *session, ...@@ -108,9 +121,7 @@ int before_frame_send_callback(nghttp2_session *session,
} }
auto client = static_cast<Client *>(user_data); auto client = static_cast<Client *>(user_data);
client->on_request(frame->hd.stream_id); auto req_stat = client->get_req_stat(frame->hd.stream_id);
auto req_stat = static_cast<RequestStat *>(
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
assert(req_stat); assert(req_stat);
client->record_request_time(req_stat); client->record_request_time(req_stat);
...@@ -124,8 +135,7 @@ ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id, ...@@ -124,8 +135,7 @@ ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
nghttp2_data_source *source, void *user_data) { nghttp2_data_source *source, void *user_data) {
auto client = static_cast<Client *>(user_data); auto client = static_cast<Client *>(user_data);
auto config = client->worker->config; auto config = client->worker->config;
auto req_stat = static_cast<RequestStat *>( auto req_stat = client->get_req_stat(stream_id);
nghttp2_session_get_stream_user_data(session, stream_id));
assert(req_stat); assert(req_stat);
ssize_t nread; ssize_t nread;
while ((nread = pread(config->data_fd, buf, length, req_stat->data_offset)) == while ((nread = pread(config->data_fd, buf, length, req_stat->data_offset)) ==
...@@ -183,6 +193,9 @@ void Http2Session::on_connect() { ...@@ -183,6 +193,9 @@ void Http2Session::on_connect() {
nghttp2_session_callbacks_set_on_header_callback(callbacks, nghttp2_session_callbacks_set_on_header_callback(callbacks,
on_header_callback); on_header_callback);
nghttp2_session_callbacks_set_on_frame_not_send_callback(
callbacks, on_frame_not_send_callback);
nghttp2_session_callbacks_set_before_frame_send_callback( nghttp2_session_callbacks_set_before_frame_send_callback(
callbacks, before_frame_send_callback); callbacks, before_frame_send_callback);
...@@ -212,7 +225,7 @@ void Http2Session::on_connect() { ...@@ -212,7 +225,7 @@ void Http2Session::on_connect() {
client_->signal_write(); client_->signal_write();
} }
int Http2Session::submit_request(RequestStat *req_stat) { int Http2Session::submit_request() {
if (nghttp2_session_check_request_allowed(session_) == 0) { if (nghttp2_session_check_request_allowed(session_) == 0) {
return -1; return -1;
} }
...@@ -228,11 +241,13 @@ int Http2Session::submit_request(RequestStat *req_stat) { ...@@ -228,11 +241,13 @@ int Http2Session::submit_request(RequestStat *req_stat) {
auto stream_id = auto stream_id =
nghttp2_submit_request(session_, nullptr, nva.data(), nva.size(), nghttp2_submit_request(session_, nullptr, nva.data(), nva.size(),
config->data_fd == -1 ? nullptr : &prd, req_stat); config->data_fd == -1 ? nullptr : &prd, nullptr);
if (stream_id < 0) { if (stream_id < 0) {
return -1; return -1;
} }
client_->on_request(stream_id);
return 0; return 0;
} }
......
...@@ -38,7 +38,7 @@ public: ...@@ -38,7 +38,7 @@ public:
Http2Session(Client *client); Http2Session(Client *client);
virtual ~Http2Session(); virtual ~Http2Session();
virtual void on_connect(); virtual void on_connect();
virtual int submit_request(RequestStat *req_stat); virtual int submit_request();
virtual int on_read(const uint8_t *data, size_t len); virtual int on_read(const uint8_t *data, size_t len);
virtual int on_write(); virtual int on_write();
virtual void terminate(); virtual void terminate();
......
...@@ -41,7 +41,7 @@ public: ...@@ -41,7 +41,7 @@ public:
// Called when the connection was made. // Called when the connection was made.
virtual void on_connect() = 0; virtual void on_connect() = 0;
// Called when one request must be issued. // Called when one request must be issued.
virtual int submit_request(RequestStat *req_stat) = 0; virtual int submit_request() = 0;
// Called when incoming bytes are available. The subclass has to // Called when incoming bytes are available. The subclass has to
// return the number of bytes read. // return the number of bytes read.
virtual int on_read(const uint8_t *data, size_t len) = 0; virtual int on_read(const uint8_t *data, size_t len) = 0;
......
...@@ -48,9 +48,7 @@ void before_ctrl_send_callback(spdylay_session *session, ...@@ -48,9 +48,7 @@ void before_ctrl_send_callback(spdylay_session *session,
return; return;
} }
client->on_request(frame->syn_stream.stream_id); client->on_request(frame->syn_stream.stream_id);
auto req_stat = auto req_stat = client->get_req_stat(frame->syn_stream.stream_id);
static_cast<RequestStat *>(spdylay_session_get_stream_user_data(
session, frame->syn_stream.stream_id));
client->record_request_time(req_stat); client->record_request_time(req_stat);
} }
} // namespace } // namespace
...@@ -104,9 +102,7 @@ void on_stream_close_callback(spdylay_session *session, int32_t stream_id, ...@@ -104,9 +102,7 @@ void on_stream_close_callback(spdylay_session *session, int32_t stream_id,
spdylay_status_code status_code, spdylay_status_code status_code,
void *user_data) { void *user_data) {
auto client = static_cast<Client *>(user_data); auto client = static_cast<Client *>(user_data);
auto req_stat = static_cast<RequestStat *>( client->on_stream_close(stream_id, status_code == SPDYLAY_OK);
spdylay_session_get_stream_user_data(session, stream_id));
client->on_stream_close(stream_id, status_code == SPDYLAY_OK, req_stat);
} }
} // namespace } // namespace
...@@ -130,8 +126,7 @@ ssize_t file_read_callback(spdylay_session *session, int32_t stream_id, ...@@ -130,8 +126,7 @@ ssize_t file_read_callback(spdylay_session *session, int32_t stream_id,
spdylay_data_source *source, void *user_data) { spdylay_data_source *source, void *user_data) {
auto client = static_cast<Client *>(user_data); auto client = static_cast<Client *>(user_data);
auto config = client->worker->config; auto config = client->worker->config;
auto req_stat = static_cast<RequestStat *>( auto req_stat = client->get_req_stat(stream_id);
spdylay_session_get_stream_user_data(session, stream_id));
ssize_t nread; ssize_t nread;
while ((nread = pread(config->data_fd, buf, length, req_stat->data_offset)) == while ((nread = pread(config->data_fd, buf, length, req_stat->data_offset)) ==
...@@ -185,7 +180,7 @@ void SpdySession::on_connect() { ...@@ -185,7 +180,7 @@ void SpdySession::on_connect() {
client_->signal_write(); client_->signal_write();
} }
int SpdySession::submit_request(RequestStat *req_stat) { int SpdySession::submit_request() {
int rv; int rv;
auto config = client_->worker->config; auto config = client_->worker->config;
auto &nv = config->nv[client_->reqidx++]; auto &nv = config->nv[client_->reqidx++];
...@@ -197,7 +192,7 @@ int SpdySession::submit_request(RequestStat *req_stat) { ...@@ -197,7 +192,7 @@ int SpdySession::submit_request(RequestStat *req_stat) {
spdylay_data_provider prd{{0}, file_read_callback}; spdylay_data_provider prd{{0}, file_read_callback};
rv = spdylay_submit_request(session_, 0, nv.data(), rv = spdylay_submit_request(session_, 0, nv.data(),
config->data_fd == -1 ? nullptr : &prd, req_stat); config->data_fd == -1 ? nullptr : &prd, nullptr);
if (rv != 0) { if (rv != 0) {
return -1; return -1;
......
...@@ -40,7 +40,7 @@ public: ...@@ -40,7 +40,7 @@ public:
SpdySession(Client *client, uint16_t spdy_version); SpdySession(Client *client, uint16_t spdy_version);
virtual ~SpdySession(); virtual ~SpdySession();
virtual void on_connect(); virtual void on_connect();
virtual int submit_request(RequestStat *req_stat); virtual int submit_request();
virtual int on_read(const uint8_t *data, size_t len); virtual int on_read(const uint8_t *data, size_t len);
virtual int on_write(); virtual int on_write();
virtual void terminate(); virtual void terminate();
......
...@@ -113,6 +113,8 @@ std::string get_status_string(unsigned int status_code) { ...@@ -113,6 +113,8 @@ std::string get_status_string(unsigned int status_code) {
return "429 Too Many Requests"; return "429 Too Many Requests";
case 431: case 431:
return "431 Request Header Fields Too Large"; return "431 Request Header Fields Too Large";
case 451:
return "451 Unavailable For Legal Reasons";
case 500: case 500:
return "500 Internal Server Error"; return "500 Internal Server Error";
case 501: case 501:
...@@ -215,6 +217,8 @@ const char *stringify_status(unsigned int status_code) { ...@@ -215,6 +217,8 @@ const char *stringify_status(unsigned int status_code) {
return "429"; return "429";
case 431: case 431:
return "431"; return "431";
case 451:
return "451";
case 500: case 500:
return "500"; return "500";
case 501: case 501:
......
...@@ -144,6 +144,12 @@ public: ...@@ -144,6 +144,12 @@ public:
// and session is terminated. // and session is terminated.
void on_error(error_cb cb) const; void on_error(error_cb cb) const;
// Sets connect timeout, which defaults to 60 seconds.
void connect_timeout(const boost::posix_time::time_duration &t);
// Sets read timeout, which defaults to 60 seconds.
void read_timeout(const boost::posix_time::time_duration &t);
// Shutdowns connection. // Shutdowns connection.
void shutdown() const; void shutdown() const;
...@@ -177,7 +183,7 @@ public: ...@@ -177,7 +183,7 @@ public:
generator_cb cb, header_map h = header_map{}) const; generator_cb cb, header_map h = header_map{}) const;
private: private:
std::unique_ptr<session_impl> impl_; std::shared_ptr<session_impl> impl_;
}; };
// configure |tls_ctx| for client use. Currently, we just set NPN // configure |tls_ctx| for client use. Currently, we just set NPN
......
...@@ -59,6 +59,9 @@ public: ...@@ -59,6 +59,9 @@ public:
// Application must not call this directly. // Application must not call this directly.
request_impl &impl() const; request_impl &impl() const;
// Returns the remote endpoint of the request
const boost::asio::ip::tcp::endpoint &remote_endpoint() const;
private: private:
std::unique_ptr<request_impl> impl_; std::unique_ptr<request_impl> impl_;
}; };
...@@ -195,12 +198,22 @@ public: ...@@ -195,12 +198,22 @@ public:
// connections. // connections.
void backlog(int backlog); void backlog(int backlog);
// Sets TLS handshake timeout, which defaults to 60 seconds.
void tls_handshake_timeout(const boost::posix_time::time_duration &t);
// Sets read timeout, which defaults to 60 seconds.
void read_timeout(const boost::posix_time::time_duration &t);
// Gracefully stop http2 server // Gracefully stop http2 server
void stop(); void stop();
// Join on http2 server and wait for it to fully stop // Join on http2 server and wait for it to fully stop
void join(); void join();
// Get access to the io_service objects.
const std::vector<std::shared_ptr<boost::asio::io_service>> &
io_services() const;
private: private:
std::unique_ptr<http2_impl> impl_; std::unique_ptr<http2_impl> impl_;
}; };
......
...@@ -164,6 +164,8 @@ Options: ...@@ -164,6 +164,8 @@ Options:
Path to file that contains MIME media types and the Path to file that contains MIME media types and the
extensions that represent them. extensions that represent them.
Default: )" << config.mime_types_file << R"( Default: )" << config.mime_types_file << R"(
--no-content-length
Don't send content-length header field.
--version Display version information and exit. --version Display version information and exit.
-h, --help Display this help and exit. -h, --help Display this help and exit.
...@@ -209,6 +211,7 @@ int main(int argc, char **argv) { ...@@ -209,6 +211,7 @@ int main(int argc, char **argv) {
{"hexdump", no_argument, &flag, 7}, {"hexdump", no_argument, &flag, 7},
{"echo-upload", no_argument, &flag, 8}, {"echo-upload", no_argument, &flag, 8},
{"mime-types-file", required_argument, &flag, 9}, {"mime-types-file", required_argument, &flag, 9},
{"no-content-length", no_argument, &flag, 10},
{nullptr, 0, nullptr, 0}}; {nullptr, 0, nullptr, 0}};
int option_index = 0; int option_index = 0;
int c = getopt_long(argc, argv, "DVb:c:d:ehm:n:p:va:", long_options, int c = getopt_long(argc, argv, "DVb:c:d:ehm:n:p:va:", long_options,
...@@ -340,6 +343,10 @@ int main(int argc, char **argv) { ...@@ -340,6 +343,10 @@ int main(int argc, char **argv) {
mime_types_file_set_manually = true; mime_types_file_set_manually = true;
config.mime_types_file = optarg; config.mime_types_file = optarg;
break; break;
case 10:
// no-content-length option
config.no_content_length = true;
break;
} }
break; break;
default: default:
......
...@@ -1610,14 +1610,15 @@ HTTP: ...@@ -1610,14 +1610,15 @@ HTTP:
used several times to specify multiple header fields. used several times to specify multiple header fields.
Example: --add-response-header="foo: bar" Example: --add-response-header="foo: bar"
--header-field-buffer=<SIZE> --header-field-buffer=<SIZE>
Set maximum buffer size for incoming HTTP header field Set maximum buffer size for incoming HTTP request header
list. This is the sum of header name and value in field list. This is the sum of header name and value in
bytes. bytes.
Default: )" Default: )"
<< util::utos_with_unit(get_config()->header_field_buffer) << R"( << util::utos_with_unit(get_config()->header_field_buffer) << R"(
--max-header-fields=<N> --max-header-fields=<N>
Set maximum number of incoming HTTP header fields, which Set maximum number of incoming HTTP request header
appear in one request or response header field list. fields, which appear in one request or response header
field list.
Default: )" << get_config()->max_header_fields << R"( Default: )" << get_config()->max_header_fields << R"(
Debug: Debug:
......
...@@ -44,4 +44,8 @@ ...@@ -44,4 +44,8 @@
#define DIE() _Exit(EXIT_FAILURE) #define DIE() _Exit(EXIT_FAILURE)
#if defined(HAVE_DECL_INITGROUPS) && !HAVE_DECL_INITGROUPS
inline int initgroups(const char *user, gid_t group) { return 0; }
#endif // defined(HAVE_DECL_INITGROUPS) && !HAVE_DECL_INITGROUPS
#endif // SHRPX_H #endif // SHRPX_H
...@@ -718,6 +718,34 @@ void ClientHandler::direct_http2_upgrade() { ...@@ -718,6 +718,34 @@ void ClientHandler::direct_http2_upgrade() {
int ClientHandler::perform_http2_upgrade(HttpsUpstream *http) { int ClientHandler::perform_http2_upgrade(HttpsUpstream *http) {
auto upstream = make_unique<Http2Upstream>(this); auto upstream = make_unique<Http2Upstream>(this);
auto output = upstream->get_response_buf();
// We might have written non-final header in response_buf, in this
// case, response_state is still INITIAL. If this non-final header
// and upgrade header fit in output buffer, do upgrade. Otherwise,
// to avoid to send this non-final header as response body in HTTP/2
// upstream, fail upgrade.
auto downstream = http->get_downstream();
auto input = downstream->get_response_buf();
static constexpr char res[] =
"HTTP/1.1 101 Switching Protocols\r\n"
"Connection: Upgrade\r\n"
"Upgrade: " NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "\r\n"
"\r\n";
auto required_size = str_size(res) + input->rleft();
if (output->wleft() < required_size) {
if (LOG_ENABLED(INFO)) {
CLOG(INFO, this)
<< "HTTP Upgrade failed because of insufficient buffer space: need "
<< required_size << ", available " << output->wleft();
}
return -1;
}
if (upstream->upgrade_upstream(http) != 0) { if (upstream->upgrade_upstream(http) != 0) {
return -1; return -1;
} }
...@@ -729,11 +757,11 @@ int ClientHandler::perform_http2_upgrade(HttpsUpstream *http) { ...@@ -729,11 +757,11 @@ int ClientHandler::perform_http2_upgrade(HttpsUpstream *http) {
on_read_ = &ClientHandler::upstream_http2_connhd_read; on_read_ = &ClientHandler::upstream_http2_connhd_read;
write_ = &ClientHandler::write_clear; write_ = &ClientHandler::write_clear;
static char res[] = "HTTP/1.1 101 Switching Protocols\r\n" auto nread =
"Connection: Upgrade\r\n" downstream->get_response_buf()->remove(output->last, output->wleft());
"Upgrade: " NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "\r\n" output->write(nread);
"\r\n";
upstream->get_response_buf()->write(res, sizeof(res) - 1); output->write(res, str_size(res));
upstream_ = std::move(upstream); upstream_ = std::move(upstream);
signal_write(); signal_write();
......
...@@ -276,11 +276,9 @@ std::string read_passwd_from_file(const char *filename) { ...@@ -276,11 +276,9 @@ std::string read_passwd_from_file(const char *filename) {
} }
std::pair<std::string, std::string> parse_header(const char *optarg) { std::pair<std::string, std::string> parse_header(const char *optarg) {
// We skip possible ":" at the start of optarg. const auto *colon = strchr(optarg, ':');
const auto *colon = strchr(optarg + 1, ':');
// name = ":" is not allowed if (colon == nullptr || colon == optarg) {
if (colon == nullptr || (optarg[0] == ':' && colon == optarg + 1)) {
return {"", ""}; return {"", ""};
} }
...@@ -291,7 +289,14 @@ std::pair<std::string, std::string> parse_header(const char *optarg) { ...@@ -291,7 +289,14 @@ std::pair<std::string, std::string> parse_header(const char *optarg) {
auto p = std::make_pair(std::string(optarg, colon), auto p = std::make_pair(std::string(optarg, colon),
std::string(value, strlen(value))); std::string(value, strlen(value)));
util::inp_strlower(p.first); util::inp_strlower(p.first);
util::inp_strlower(p.second);
if (!nghttp2_check_header_name(
reinterpret_cast<const uint8_t *>(p.first.c_str()), p.first.size()) ||
!nghttp2_check_header_value(
reinterpret_cast<const uint8_t *>(p.second.c_str()),
p.second.size())) {
return {"", ""};
}
return p; return p;
} }
...@@ -1800,7 +1805,7 @@ int parse_config(const char *opt, const char *optarg, ...@@ -1800,7 +1805,7 @@ int parse_config(const char *opt, const char *optarg,
case SHRPX_OPTID_ADD_RESPONSE_HEADER: { case SHRPX_OPTID_ADD_RESPONSE_HEADER: {
auto p = parse_header(optarg); auto p = parse_header(optarg);
if (p.first.empty()) { if (p.first.empty()) {
LOG(ERROR) << opt << ": header field name is empty: " << optarg; LOG(ERROR) << opt << ": invalid header field: " << optarg;
return -1; return -1;
} }
if (optid == SHRPX_OPTID_ADD_REQUEST_HEADER) { if (optid == SHRPX_OPTID_ADD_REQUEST_HEADER) {
......
...@@ -46,8 +46,7 @@ void test_shrpx_config_parse_header(void) { ...@@ -46,8 +46,7 @@ void test_shrpx_config_parse_header(void) {
CU_ASSERT("b" == p.second); CU_ASSERT("b" == p.second);
p = parse_header(":a: b"); p = parse_header(":a: b");
CU_ASSERT(":a" == p.first); CU_ASSERT(p.first.empty());
CU_ASSERT("b" == p.second);
p = parse_header("a: :b"); p = parse_header("a: :b");
CU_ASSERT("a" == p.first); CU_ASSERT("a" == p.first);
...@@ -59,6 +58,12 @@ void test_shrpx_config_parse_header(void) { ...@@ -59,6 +58,12 @@ void test_shrpx_config_parse_header(void) {
p = parse_header("alpha: bravo charlie"); p = parse_header("alpha: bravo charlie");
CU_ASSERT("alpha" == p.first); CU_ASSERT("alpha" == p.first);
CU_ASSERT("bravo charlie" == p.second); CU_ASSERT("bravo charlie" == p.second);
p = parse_header("a,: b");
CU_ASSERT(p.first.empty());
p = parse_header("a: b\x0a");
CU_ASSERT(p.first.empty());
} }
void test_shrpx_config_parse_log_format(void) { void test_shrpx_config_parse_log_format(void) {
......
...@@ -757,26 +757,6 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, ...@@ -757,26 +757,6 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
auto trailer = frame->headers.cat == NGHTTP2_HCAT_HEADERS && auto trailer = frame->headers.cat == NGHTTP2_HCAT_HEADERS &&
!downstream->get_expect_final_response(); !downstream->get_expect_final_response();
if (downstream->get_response_headers_sum() + namelen + valuelen >
get_config()->header_field_buffer ||
downstream->get_response_headers().size() >=
get_config()->max_header_fields) {
if (LOG_ENABLED(INFO)) {
DLOG(INFO, downstream)
<< "Too large or many header field size="
<< downstream->get_response_headers_sum() + namelen + valuelen
<< ", num=" << downstream->get_response_headers().size() + 1;
}
if (trailer) {
// we don't care trailer part exceeds header size limit; just
// discard it.
return 0;
}
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
if (trailer) { if (trailer) {
// just store header fields for trailer part // just store header fields for trailer part
downstream->add_response_trailer(name, namelen, value, valuelen, downstream->add_response_trailer(name, namelen, value, valuelen,
...@@ -803,21 +783,6 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, ...@@ -803,21 +783,6 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
assert(promised_downstream); assert(promised_downstream);
if (promised_downstream->get_request_headers_sum() + namelen + valuelen >
get_config()->header_field_buffer ||
promised_downstream->get_request_headers().size() >=
get_config()->max_header_fields) {
if (LOG_ENABLED(INFO)) {
DLOG(INFO, promised_downstream)
<< "Too large or many header field size="
<< promised_downstream->get_request_headers_sum() + namelen +
valuelen << ", num="
<< promised_downstream->get_request_headers().size() + 1;
}
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
auto token = http2::lookup_token(name, namelen); auto token = http2::lookup_token(name, namelen);
promised_downstream->add_request_header(name, namelen, value, valuelen, promised_downstream->add_request_header(name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX, flags & NGHTTP2_NV_FLAG_NO_INDEX,
......
...@@ -169,7 +169,8 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, ...@@ -169,7 +169,8 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
if (downstream->get_request_headers_sum() + namelen + valuelen > if (downstream->get_request_headers_sum() + namelen + valuelen >
get_config()->header_field_buffer || get_config()->header_field_buffer ||
downstream->get_request_headers().size() >= downstream->get_request_headers().size() +
downstream->get_request_trailers().size() >=
get_config()->max_header_fields) { get_config()->max_header_fields) {
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
return 0; return 0;
......
...@@ -562,29 +562,10 @@ int htp_hdrs_completecb(http_parser *htp) { ...@@ -562,29 +562,10 @@ int htp_hdrs_completecb(http_parser *htp) {
namespace { namespace {
int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) { int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
auto downstream = static_cast<Downstream *>(htp->data); auto downstream = static_cast<Downstream *>(htp->data);
if (downstream->get_response_headers_sum() + len >
get_config()->header_field_buffer) {
if (LOG_ENABLED(INFO)) {
DLOG(INFO, downstream) << "Too large header block size="
<< downstream->get_response_headers_sum() + len;
}
return -1;
}
if (downstream->get_response_state() == Downstream::INITIAL) { if (downstream->get_response_state() == Downstream::INITIAL) {
if (downstream->get_response_header_key_prev()) { if (downstream->get_response_header_key_prev()) {
downstream->append_last_response_header_key(data, len); downstream->append_last_response_header_key(data, len);
} else { } else {
if (downstream->get_response_headers().size() >=
get_config()->max_header_fields) {
if (LOG_ENABLED(INFO)) {
DLOG(INFO, downstream)
<< "Too many header field num="
<< downstream->get_response_headers().size() + 1;
}
return -1;
}
downstream->add_response_header(std::string(data, len), ""); downstream->add_response_header(std::string(data, len), "");
} }
} else { } else {
...@@ -592,15 +573,6 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) { ...@@ -592,15 +573,6 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
if (downstream->get_response_trailer_key_prev()) { if (downstream->get_response_trailer_key_prev()) {
downstream->append_last_response_trailer_key(data, len); downstream->append_last_response_trailer_key(data, len);
} else { } else {
if (downstream->get_response_headers().size() >=
get_config()->max_header_fields) {
if (LOG_ENABLED(INFO)) {
DLOG(INFO, downstream)
<< "Too many header field num="
<< downstream->get_response_headers().size() + 1;
}
return -1;
}
downstream->add_response_trailer(std::string(data, len), ""); downstream->add_response_trailer(std::string(data, len), "");
} }
} }
...@@ -611,14 +583,6 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) { ...@@ -611,14 +583,6 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
namespace { namespace {
int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) { int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
auto downstream = static_cast<Downstream *>(htp->data); auto downstream = static_cast<Downstream *>(htp->data);
if (downstream->get_response_headers_sum() + len >
get_config()->header_field_buffer) {
if (LOG_ENABLED(INFO)) {
DLOG(INFO, downstream) << "Too large header block size="
<< downstream->get_response_headers_sum() + len;
}
return -1;
}
if (downstream->get_response_state() == Downstream::INITIAL) { if (downstream->get_response_state() == Downstream::INITIAL) {
if (downstream->get_response_header_key_prev()) { if (downstream->get_response_header_key_prev()) {
downstream->set_last_response_header_value(data, len); downstream->set_last_response_header_value(data, len);
......
...@@ -146,7 +146,8 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) { ...@@ -146,7 +146,8 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
if (downstream->get_request_trailer_key_prev()) { if (downstream->get_request_trailer_key_prev()) {
downstream->append_last_request_trailer_key(data, len); downstream->append_last_request_trailer_key(data, len);
} else { } else {
if (downstream->get_request_headers().size() >= if (downstream->get_request_headers().size() +
downstream->get_request_trailers().size() >=
get_config()->max_header_fields) { get_config()->max_header_fields) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
ULOG(INFO, upstream) << "Too many header field num=" ULOG(INFO, upstream) << "Too many header field num="
...@@ -409,11 +410,6 @@ int htp_msg_completecb(http_parser *htp) { ...@@ -409,11 +410,6 @@ int htp_msg_completecb(http_parser *htp) {
if (handler->get_http2_upgrade_allowed() && if (handler->get_http2_upgrade_allowed() &&
downstream->get_http2_upgrade_request() && downstream->get_http2_upgrade_request() &&
// we may write non-final header in response_buf, in this case,
// response_state is still INITIAL. So don't upgrade in this
// case, otherwise we end up send this non-final header as
// response body in HTTP/2 upstream.
downstream->get_response_buf()->rleft() == 0 &&
handler->perform_http2_upgrade(upstream) != 0) { handler->perform_http2_upgrade(upstream) != 0) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
ULOG(INFO, upstream) << "HTTP Upgrade to HTTP/2 failed"; ULOG(INFO, upstream) << "HTTP Upgrade to HTTP/2 failed";
......
...@@ -169,6 +169,8 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type, ...@@ -169,6 +169,8 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
header_buffer += strlen(nv[i]) + strlen(nv[i + 1]); header_buffer += strlen(nv[i]) + strlen(nv[i + 1]);
} }
// spdy does not define usage of trailer fields, and we ignores
// them.
if (header_buffer > get_config()->header_field_buffer || if (header_buffer > get_config()->header_field_buffer ||
num_headers > get_config()->max_header_fields) { num_headers > get_config()->max_header_fields) {
upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include <openssl/x509.h> #include <openssl/x509.h>
#include <openssl/x509v3.h> #include <openssl/x509v3.h>
#include <openssl/rand.h> #include <openssl/rand.h>
#include <openssl/dh.h>
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
......
...@@ -80,12 +80,7 @@ LibsslGlobalLock::LibsslGlobalLock() { ...@@ -80,12 +80,7 @@ LibsslGlobalLock::LibsslGlobalLock() {
LibsslGlobalLock::~LibsslGlobalLock() { ssl_global_locks.clear(); } LibsslGlobalLock::~LibsslGlobalLock() { ssl_global_locks.clear(); }
const char *get_tls_protocol(SSL *ssl) { const char *get_tls_protocol(SSL *ssl) {
auto session = SSL_get_session(ssl); switch (SSL_version(ssl)) {
if (!session) {
return "unknown";
}
switch (session->ssl_version) {
case SSL2_VERSION: case SSL2_VERSION:
return "SSLv2"; return "SSLv2";
case SSL3_VERSION: case SSL3_VERSION:
...@@ -113,10 +108,12 @@ TLSSessionInfo *get_tls_session_info(TLSSessionInfo *tls_info, SSL *ssl) { ...@@ -113,10 +108,12 @@ TLSSessionInfo *get_tls_session_info(TLSSessionInfo *tls_info, SSL *ssl) {
tls_info->cipher = SSL_get_cipher_name(ssl); tls_info->cipher = SSL_get_cipher_name(ssl);
tls_info->protocol = get_tls_protocol(ssl); tls_info->protocol = get_tls_protocol(ssl);
tls_info->session_id = session->session_id;
tls_info->session_id_length = session->session_id_length;
tls_info->session_reused = SSL_session_reused(ssl); tls_info->session_reused = SSL_session_reused(ssl);
unsigned int session_id_length;
tls_info->session_id = SSL_SESSION_get_id(session, &session_id_length);
tls_info->session_id_length = session_id_length;
return tls_info; return tls_info;
} }
......
...@@ -89,6 +89,8 @@ int main(int argc _U_, char *argv[] _U_) { ...@@ -89,6 +89,8 @@ int main(int argc _U_, char *argv[] _U_) {
test_nghttp2_session_recv_headers_with_priority) || test_nghttp2_session_recv_headers_with_priority) ||
!CU_add_test(pSuite, "session_recv_headers_early_response", !CU_add_test(pSuite, "session_recv_headers_early_response",
test_nghttp2_session_recv_headers_early_response) || test_nghttp2_session_recv_headers_early_response) ||
!CU_add_test(pSuite, "session_server_recv_push_response",
test_nghttp2_session_server_recv_push_response) ||
!CU_add_test(pSuite, "session_recv_premature_headers", !CU_add_test(pSuite, "session_recv_premature_headers",
test_nghttp2_session_recv_premature_headers) || test_nghttp2_session_recv_premature_headers) ||
!CU_add_test(pSuite, "session_recv_unknown_frame", !CU_add_test(pSuite, "session_recv_unknown_frame",
...@@ -128,6 +130,8 @@ int main(int argc _U_, char *argv[] _U_) { ...@@ -128,6 +130,8 @@ int main(int argc _U_, char *argv[] _U_) {
test_nghttp2_session_on_window_update_received) || test_nghttp2_session_on_window_update_received) ||
!CU_add_test(pSuite, "session_on_data_received", !CU_add_test(pSuite, "session_on_data_received",
test_nghttp2_session_on_data_received) || test_nghttp2_session_on_data_received) ||
!CU_add_test(pSuite, "session_on_data_received_fail_fast",
test_nghttp2_session_on_data_received_fail_fast) ||
!CU_add_test(pSuite, "session_send_headers_start_stream", !CU_add_test(pSuite, "session_send_headers_start_stream",
test_nghttp2_session_send_headers_start_stream) || test_nghttp2_session_send_headers_start_stream) ||
!CU_add_test(pSuite, "session_send_headers_reply", !CU_add_test(pSuite, "session_send_headers_reply",
...@@ -205,8 +209,6 @@ int main(int argc _U_, char *argv[] _U_) { ...@@ -205,8 +209,6 @@ int main(int argc _U_, char *argv[] _U_) {
test_nghttp2_session_reply_fail) || test_nghttp2_session_reply_fail) ||
!CU_add_test(pSuite, "session_max_concurrent_streams", !CU_add_test(pSuite, "session_max_concurrent_streams",
test_nghttp2_session_max_concurrent_streams) || test_nghttp2_session_max_concurrent_streams) ||
!CU_add_test(pSuite, "session_stream_close_on_headers_push",
test_nghttp2_session_stream_close_on_headers_push) ||
!CU_add_test(pSuite, "session_stop_data_with_rst_stream", !CU_add_test(pSuite, "session_stop_data_with_rst_stream",
test_nghttp2_session_stop_data_with_rst_stream) || test_nghttp2_session_stop_data_with_rst_stream) ||
!CU_add_test(pSuite, "session_defer_data", !CU_add_test(pSuite, "session_defer_data",
...@@ -291,6 +293,10 @@ int main(int argc _U_, char *argv[] _U_) { ...@@ -291,6 +293,10 @@ int main(int argc _U_, char *argv[] _U_) {
!CU_add_test(pSuite, "session_flooding", test_nghttp2_session_flooding) || !CU_add_test(pSuite, "session_flooding", test_nghttp2_session_flooding) ||
!CU_add_test(pSuite, "session_change_stream_priority", !CU_add_test(pSuite, "session_change_stream_priority",
test_nghttp2_session_change_stream_priority) || test_nghttp2_session_change_stream_priority) ||
!CU_add_test(pSuite, "session_repeated_priority_change",
test_nghttp2_session_repeated_priority_change) ||
!CU_add_test(pSuite, "session_repeated_priority_submission",
test_nghttp2_session_repeated_priority_submission) ||
!CU_add_test(pSuite, "http_mandatory_headers", !CU_add_test(pSuite, "http_mandatory_headers",
test_nghttp2_http_mandatory_headers) || test_nghttp2_http_mandatory_headers) ||
!CU_add_test(pSuite, "http_content_length", !CU_add_test(pSuite, "http_content_length",
......
This diff is collapsed.
...@@ -34,6 +34,7 @@ void test_nghttp2_session_recv_data_no_auto_flow_control(void); ...@@ -34,6 +34,7 @@ void test_nghttp2_session_recv_data_no_auto_flow_control(void);
void test_nghttp2_session_recv_continuation(void); void test_nghttp2_session_recv_continuation(void);
void test_nghttp2_session_recv_headers_with_priority(void); void test_nghttp2_session_recv_headers_with_priority(void);
void test_nghttp2_session_recv_headers_early_response(void); void test_nghttp2_session_recv_headers_early_response(void);
void test_nghttp2_session_server_recv_push_response(void);
void test_nghttp2_session_recv_premature_headers(void); void test_nghttp2_session_recv_premature_headers(void);
void test_nghttp2_session_recv_unknown_frame(void); void test_nghttp2_session_recv_unknown_frame(void);
void test_nghttp2_session_recv_unexpected_continuation(void); void test_nghttp2_session_recv_unexpected_continuation(void);
...@@ -54,6 +55,7 @@ void test_nghttp2_session_on_ping_received(void); ...@@ -54,6 +55,7 @@ void test_nghttp2_session_on_ping_received(void);
void test_nghttp2_session_on_goaway_received(void); void test_nghttp2_session_on_goaway_received(void);
void test_nghttp2_session_on_window_update_received(void); void test_nghttp2_session_on_window_update_received(void);
void test_nghttp2_session_on_data_received(void); void test_nghttp2_session_on_data_received(void);
void test_nghttp2_session_on_data_received_fail_fast(void);
void test_nghttp2_session_send_headers_start_stream(void); void test_nghttp2_session_send_headers_start_stream(void);
void test_nghttp2_session_send_headers_reply(void); void test_nghttp2_session_send_headers_reply(void);
void test_nghttp2_session_send_headers_frame_size_error(void); void test_nghttp2_session_send_headers_frame_size_error(void);
...@@ -95,7 +97,6 @@ void test_nghttp2_session_get_next_ob_item(void); ...@@ -95,7 +97,6 @@ void test_nghttp2_session_get_next_ob_item(void);
void test_nghttp2_session_pop_next_ob_item(void); void test_nghttp2_session_pop_next_ob_item(void);
void test_nghttp2_session_reply_fail(void); void test_nghttp2_session_reply_fail(void);
void test_nghttp2_session_max_concurrent_streams(void); void test_nghttp2_session_max_concurrent_streams(void);
void test_nghttp2_session_stream_close_on_headers_push(void);
void test_nghttp2_session_stop_data_with_rst_stream(void); void test_nghttp2_session_stop_data_with_rst_stream(void);
void test_nghttp2_session_defer_data(void); void test_nghttp2_session_defer_data(void);
void test_nghttp2_session_flow_control(void); void test_nghttp2_session_flow_control(void);
...@@ -139,6 +140,8 @@ void test_nghttp2_session_detach_item_from_closed_stream(void); ...@@ -139,6 +140,8 @@ void test_nghttp2_session_detach_item_from_closed_stream(void);
void test_nghttp2_session_flooding(void); void test_nghttp2_session_flooding(void);
void test_nghttp2_session_change_stream_priority(void); void test_nghttp2_session_change_stream_priority(void);
void test_nghttp2_session_create_idle_stream(void); void test_nghttp2_session_create_idle_stream(void);
void test_nghttp2_session_repeated_priority_change(void);
void test_nghttp2_session_repeated_priority_submission(void);
void test_nghttp2_http_mandatory_headers(void); void test_nghttp2_http_mandatory_headers(void);
void test_nghttp2_http_content_length(void); void test_nghttp2_http_content_length(void);
void test_nghttp2_http_content_length_mismatch(void); void test_nghttp2_http_content_length_mismatch(void);
......
Subproject commit 81eff20bd84b4d0dce2cbbd1a5ad1384d086423b Subproject commit 5c47587bc2855f2b9577a9bd369ed70088b77fec
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment