mirror of
https://github.com/nghttp2/nghttp2.git
synced 2025-12-09 03:28:52 +08:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7146954de2 | ||
|
|
75b9be2d5a | ||
|
|
99aaaccf03 | ||
|
|
7e217511bf | ||
|
|
8c67bbe3a8 | ||
|
|
cc250386df | ||
|
|
3b7c733246 | ||
|
|
7e5567341f | ||
|
|
4f7223e89f | ||
|
|
88b69bb669 | ||
|
|
7a797b2c11 | ||
|
|
832f2fc00f | ||
|
|
d113055899 | ||
|
|
53e75ff0d0 | ||
|
|
3921af434a | ||
|
|
5e6a2fa256 | ||
|
|
86ab9f33de | ||
|
|
d844b0acd0 | ||
|
|
d733c87567 | ||
|
|
589d3e71a3 | ||
|
|
db354b228a | ||
|
|
1fa5852f8f | ||
|
|
ebf0e4d787 | ||
|
|
9677788317 | ||
|
|
78a55935ac | ||
|
|
2aa84019c7 | ||
|
|
6d942dc0a6 | ||
|
|
d408ee1783 | ||
|
|
672ad82849 | ||
|
|
896717f5d4 | ||
|
|
eba2825286 | ||
|
|
a4a06947a5 | ||
|
|
e54e86375b |
36
.travis.yml
Normal file
36
.travis.yml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
language: cpp
|
||||||
|
compiler:
|
||||||
|
- clang
|
||||||
|
- gcc
|
||||||
|
python:
|
||||||
|
- "3.4"
|
||||||
|
before_install:
|
||||||
|
- $CC --version
|
||||||
|
- sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test
|
||||||
|
- sudo apt-get update -qq
|
||||||
|
#Install and use gcc-4.8 (don't build with gcc-4.6)
|
||||||
|
#libstdc++-4.8 is needed by Clang to build too
|
||||||
|
- sudo apt-get -qq install g++-4.8 libstdc++-4.8-dev
|
||||||
|
- >
|
||||||
|
sudo apt-get install --no-install-recommends -qq
|
||||||
|
autoconf
|
||||||
|
automake
|
||||||
|
autotools-dev
|
||||||
|
libtool
|
||||||
|
pkg-config
|
||||||
|
zlib1g-dev
|
||||||
|
libcunit1-dev
|
||||||
|
libssl-dev
|
||||||
|
libxml2-dev
|
||||||
|
libevent-dev
|
||||||
|
libjansson-dev
|
||||||
|
libjemalloc-dev
|
||||||
|
- if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi
|
||||||
|
- $CC --version
|
||||||
|
before_script:
|
||||||
|
- autoreconf -i
|
||||||
|
- automake
|
||||||
|
- autoconf
|
||||||
|
- ./configure --enable-werror
|
||||||
|
script:
|
||||||
|
- make
|
||||||
12
README.rst
12
README.rst
@@ -28,9 +28,10 @@ Public Test Server
|
|||||||
The following endpoints are available to try out nghttp2
|
The following endpoints are available to try out nghttp2
|
||||||
implementation.
|
implementation.
|
||||||
|
|
||||||
* https://nghttp2.org/ (TLS + NPN / ALPN)
|
* https://nghttp2.org/ (TLS + NPN)
|
||||||
|
|
||||||
ALPN and NPN offer ``h2-12``, ``spdy/3.1`` and ``http/1.1``.
|
NPN offer ``h2-12``, ``spdy/3.1`` and ``http/1.1``.
|
||||||
|
ALPN is currently disabled.
|
||||||
|
|
||||||
* http://nghttp2.org/ (Upgrade / Direct)
|
* http://nghttp2.org/ (Upgrade / Direct)
|
||||||
|
|
||||||
@@ -122,6 +123,13 @@ used::
|
|||||||
$ ./configure
|
$ ./configure
|
||||||
$ make
|
$ make
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Mac OS X users may need ``--disable-threads`` configure option to
|
||||||
|
disable multi threading in nghttpd, nghttpx and h2load to prevent
|
||||||
|
them from crashing. Patch is welcome to make multi threading work
|
||||||
|
on Mac OS X platform.
|
||||||
|
|
||||||
Building documentation
|
Building documentation
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
|||||||
@@ -21,13 +21,13 @@ dnl LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|||||||
dnl OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
dnl OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
dnl WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
dnl WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
AC_PREREQ(2.61)
|
AC_PREREQ(2.61)
|
||||||
AC_INIT([nghttp2], [0.4.0], [t-tujikawa@users.sourceforge.net])
|
AC_INIT([nghttp2], [0.4.1], [t-tujikawa@users.sourceforge.net])
|
||||||
LT_PREREQ([2.2.6])
|
LT_PREREQ([2.2.6])
|
||||||
LT_INIT()
|
LT_INIT()
|
||||||
dnl See versioning rule:
|
dnl See versioning rule:
|
||||||
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||||
AC_SUBST(LT_CURRENT, 3)
|
AC_SUBST(LT_CURRENT, 3)
|
||||||
AC_SUBST(LT_REVISION, 0)
|
AC_SUBST(LT_REVISION, 1)
|
||||||
AC_SUBST(LT_AGE, 0)
|
AC_SUBST(LT_AGE, 0)
|
||||||
|
|
||||||
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"`
|
||||||
@@ -275,8 +275,9 @@ if test "x${request_jemalloc}" != "xno"; then
|
|||||||
LIBS_OLD=$LIBS
|
LIBS_OLD=$LIBS
|
||||||
AC_SEARCH_LIBS([malloc_stats_print], [jemalloc], [have_jemalloc=yes])
|
AC_SEARCH_LIBS([malloc_stats_print], [jemalloc], [have_jemalloc=yes])
|
||||||
LIBS=$LIBS_OLD
|
LIBS=$LIBS_OLD
|
||||||
if test "x${have_jemalloc}" = "xyes"; then
|
if test "x${have_jemalloc}" = "xyes" &&
|
||||||
JEMALLOC_LIBS="-ljemalloc"
|
test "x${ac_cv_search_malloc_stats_print}" != "xnone required"; then
|
||||||
|
JEMALLOC_LIBS=${ac_cv_search_malloc_stats_print}
|
||||||
AC_SUBST([JEMALLOC_LIBS])
|
AC_SUBST([JEMALLOC_LIBS])
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.45.1.
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.45.1.
|
||||||
.TH H2LOAD "1" "May 2014" "h2load nghttp2/0.4.0" "User Commands"
|
.TH H2LOAD "1" "June 2014" "h2load nghttp2/0.4.1" "User Commands"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
h2load \- HTTP/2 benchmarking tool
|
h2load \- HTTP/2 benchmarking tool
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.45.1.
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.45.1.
|
||||||
.TH NGHTTP "1" "May 2014" "nghttp nghttp2/0.4.0" "User Commands"
|
.TH NGHTTP "1" "June 2014" "nghttp nghttp2/0.4.1" "User Commands"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
nghttp \- HTTP/2 experimental client
|
nghttp \- HTTP/2 experimental client
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.45.1.
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.45.1.
|
||||||
.TH NGHTTPD "1" "May 2014" "nghttpd nghttp2/0.4.0" "User Commands"
|
.TH NGHTTPD "1" "June 2014" "nghttpd nghttp2/0.4.1" "User Commands"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
nghttpd \- HTTP/2 experimental server
|
nghttpd \- HTTP/2 experimental server
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.45.1.
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.45.1.
|
||||||
.TH NGHTTPX "1" "May 2014" "nghttpx nghttp2/0.4.0" "User Commands"
|
.TH NGHTTPX "1" "June 2014" "nghttpx nghttp2/0.4.1" "User Commands"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
nghttpx \- HTTP/2 experimental proxy
|
nghttpx \- HTTP/2 experimental proxy
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@@ -27,8 +27,10 @@ Set backend host and port.
|
|||||||
Default: '127.0.0.1,80'
|
Default: '127.0.0.1,80'
|
||||||
.TP
|
.TP
|
||||||
\fB\-f\fR, \fB\-\-frontend=\fR<HOST,PORT>
|
\fB\-f\fR, \fB\-\-frontend=\fR<HOST,PORT>
|
||||||
Set frontend host and port.
|
Set frontend host and port. If <HOST> is '*', it
|
||||||
Default: '0.0.0.0,3000'
|
assumes all addresses including both IPv4 and
|
||||||
|
IPv6.
|
||||||
|
Default: '*,3000'
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-backlog=\fR<NUM>
|
\fB\-\-backlog=\fR<NUM>
|
||||||
Set listen backlog size. If \fB\-1\fR is given,
|
Set listen backlog size. If \fB\-1\fR is given,
|
||||||
@@ -188,12 +190,12 @@ format. Without this option, DHE cipher suites
|
|||||||
are not available.
|
are not available.
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-npn\-list=\fR<LIST>
|
\fB\-\-npn\-list=\fR<LIST>
|
||||||
Comma delimited list of NPN/ALPN protocol sorted
|
Comma delimited list of ALPN protocol identifier
|
||||||
in the order of preference. That means most
|
sorted in the order of preference. That means
|
||||||
desirable protocol comes first. The parameter
|
most desirable protocol comes first. This is
|
||||||
must be delimited by a single comma only and any
|
used in both ALPN and NPN. The parameter must be
|
||||||
white spaces are treated as a part of protocol
|
delimited by a single comma only and any white
|
||||||
string.
|
spaces are treated as a part of protocol string.
|
||||||
Default: h2\-12,spdy/3.1,spdy/3,spdy/2,http/1.1
|
Default: h2\-12,spdy/3.1,spdy/3,spdy/2,http/1.1
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-verify\-client\fR
|
\fB\-\-verify\-client\fR
|
||||||
|
|||||||
@@ -2200,6 +2200,15 @@ int 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.
|
||||||
|
*
|
||||||
|
* .. warning::
|
||||||
|
*
|
||||||
|
* This function returns assigned stream ID if it succeeds. But
|
||||||
|
* that stream is not opened yet. The application must not submit
|
||||||
|
* frame to that stream ID before
|
||||||
|
* :member:`nghttp2_session_callbacks.before_frame_send_callback` is
|
||||||
|
* called for this frame.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
int32_t nghttp2_submit_request(nghttp2_session *session,
|
int32_t nghttp2_submit_request(nghttp2_session *session,
|
||||||
const nghttp2_priority_spec *pri_spec,
|
const nghttp2_priority_spec *pri_spec,
|
||||||
@@ -2309,6 +2318,15 @@ int nghttp2_submit_response(nghttp2_session *session,
|
|||||||
* :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.
|
||||||
|
*
|
||||||
|
* .. warning::
|
||||||
|
*
|
||||||
|
* This function returns assigned stream ID if it succeeds and
|
||||||
|
* |stream_id| is -1. But that stream is not opened yet. The
|
||||||
|
* application must not submit frame to that stream ID before
|
||||||
|
* :member:`nghttp2_session_callbacks.before_frame_send_callback` is
|
||||||
|
* called for this frame.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
|
int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
|
||||||
int32_t stream_id,
|
int32_t stream_id,
|
||||||
@@ -2469,6 +2487,15 @@ int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags,
|
|||||||
* :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.
|
||||||
|
*
|
||||||
|
* .. warning::
|
||||||
|
*
|
||||||
|
* This function returns assigned promised stream ID if it succeeds.
|
||||||
|
* But that stream is not opened yet. The application must not
|
||||||
|
* submit frame to that stream ID before
|
||||||
|
* :member:`nghttp2_session_callbacks.before_frame_send_callback` is
|
||||||
|
* called for this frame.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,
|
int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,
|
||||||
int32_t stream_id,
|
int32_t stream_id,
|
||||||
|
|||||||
@@ -91,8 +91,8 @@ void nghttp2_frame_headers_free(nghttp2_headers *frame)
|
|||||||
void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id,
|
void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id,
|
||||||
const nghttp2_priority_spec *pri_spec)
|
const nghttp2_priority_spec *pri_spec)
|
||||||
{
|
{
|
||||||
frame_set_hd(&frame->hd, 5, NGHTTP2_PRIORITY, NGHTTP2_FLAG_NONE,
|
frame_set_hd(&frame->hd, NGHTTP2_PRIORITY_SPECLEN, NGHTTP2_PRIORITY,
|
||||||
stream_id);
|
NGHTTP2_FLAG_NONE, stream_id);
|
||||||
frame->pri_spec = *pri_spec;
|
frame->pri_spec = *pri_spec;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,8 +196,7 @@ void nghttp2_frame_altsvc_init(nghttp2_altsvc *frame, int32_t stream_id,
|
|||||||
{
|
{
|
||||||
size_t payloadlen;
|
size_t payloadlen;
|
||||||
|
|
||||||
/* 8 for Max-Age, Port, Reserved and PID_LEN. 1 for HOST_LEN. */
|
payloadlen = NGHTTP2_ALTSVC_MINLEN + protocol_id_len + host_len + origin_len;
|
||||||
payloadlen = 8 + protocol_id_len + 1 + host_len + origin_len;
|
|
||||||
|
|
||||||
frame_set_hd(&frame->hd, payloadlen, NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE,
|
frame_set_hd(&frame->hd, payloadlen, NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE,
|
||||||
stream_id);
|
stream_id);
|
||||||
@@ -263,7 +262,7 @@ void nghttp2_frame_private_data_free(nghttp2_private_data *frame)
|
|||||||
size_t nghttp2_frame_priority_len(uint8_t flags)
|
size_t nghttp2_frame_priority_len(uint8_t flags)
|
||||||
{
|
{
|
||||||
if(flags & NGHTTP2_FLAG_PRIORITY) {
|
if(flags & NGHTTP2_FLAG_PRIORITY) {
|
||||||
return 5;
|
return NGHTTP2_PRIORITY_SPECLEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -437,7 +436,7 @@ int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame)
|
|||||||
|
|
||||||
buf = &bufs->head->buf;
|
buf = &bufs->head->buf;
|
||||||
|
|
||||||
assert(nghttp2_buf_avail(buf) >= 5);
|
assert(nghttp2_buf_avail(buf) >= NGHTTP2_PRIORITY_SPECLEN);
|
||||||
|
|
||||||
buf->pos -= NGHTTP2_FRAME_HDLEN;
|
buf->pos -= NGHTTP2_FRAME_HDLEN;
|
||||||
|
|
||||||
@@ -445,7 +444,7 @@ int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame)
|
|||||||
|
|
||||||
nghttp2_frame_pack_priority_spec(buf->last, &frame->pri_spec);
|
nghttp2_frame_pack_priority_spec(buf->last, &frame->pri_spec);
|
||||||
|
|
||||||
buf->last += 5;
|
buf->last += NGHTTP2_PRIORITY_SPECLEN;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,18 @@
|
|||||||
/* The maximum header table size in SETTINGS_HEADER_TABLE_SIZE */
|
/* The maximum header table size in SETTINGS_HEADER_TABLE_SIZE */
|
||||||
#define NGHTTP2_MAX_HEADER_TABLE_SIZE ((1u << 31) - 1)
|
#define NGHTTP2_MAX_HEADER_TABLE_SIZE ((1u << 31) - 1)
|
||||||
|
|
||||||
|
/* Length of priority related fields in HEADERS/PRIORITY frames */
|
||||||
|
#define NGHTTP2_PRIORITY_SPECLEN 5
|
||||||
|
|
||||||
|
/* Length of fixed part in ALTSVC frame, that is the sum of fields of
|
||||||
|
Max-Age, Port, Reserved and PID_LEN. */
|
||||||
|
#define NGHTTP2_ALTSVC_FIXED_PARTLEN 8
|
||||||
|
|
||||||
|
/* Minimum length of ALTSVC frame. NGHTTP2_ALTSVC_FIXED_PARTLEN +
|
||||||
|
HOST_LEN. */
|
||||||
|
#define NGHTTP2_ALTSVC_MINLEN 9
|
||||||
|
|
||||||
|
|
||||||
/* Category of frames. */
|
/* Category of frames. */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
/* non-DATA frame */
|
/* non-DATA frame */
|
||||||
|
|||||||
@@ -352,37 +352,29 @@ int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater)
|
|||||||
NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE;
|
NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE;
|
||||||
|
|
||||||
inflater->ent_keep = NULL;
|
inflater->ent_keep = NULL;
|
||||||
inflater->name_keep = NULL;
|
inflater->nv_keep = NULL;
|
||||||
inflater->value_keep = NULL;
|
|
||||||
inflater->end_headers_index = 0;
|
inflater->end_headers_index = 0;
|
||||||
|
|
||||||
inflater->opcode = NGHTTP2_HD_OPCODE_NONE;
|
inflater->opcode = NGHTTP2_HD_OPCODE_NONE;
|
||||||
inflater->state = NGHTTP2_HD_STATE_OPCODE;
|
inflater->state = NGHTTP2_HD_STATE_OPCODE;
|
||||||
|
|
||||||
rv = nghttp2_bufs_init(&inflater->namebufs, NGHTTP2_HD_MAX_NAME, 1);
|
rv = nghttp2_bufs_init(&inflater->nvbufs, NGHTTP2_HD_MAX_NV / 2, 2);
|
||||||
|
|
||||||
if(rv != 0) {
|
if(rv != 0) {
|
||||||
goto namebuf_fail;
|
goto nvbufs_fail;
|
||||||
}
|
|
||||||
|
|
||||||
rv = nghttp2_bufs_init(&inflater->valuebufs, NGHTTP2_HD_MAX_VALUE / 2, 2);
|
|
||||||
|
|
||||||
if(rv != 0) {
|
|
||||||
goto valuebuf_fail;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inflater->huffman_encoded = 0;
|
inflater->huffman_encoded = 0;
|
||||||
inflater->index = 0;
|
inflater->index = 0;
|
||||||
inflater->left = 0;
|
inflater->left = 0;
|
||||||
|
inflater->newnamelen = 0;
|
||||||
inflater->index_required = 0;
|
inflater->index_required = 0;
|
||||||
inflater->no_index = 0;
|
inflater->no_index = 0;
|
||||||
inflater->ent_name = NULL;
|
inflater->ent_name = NULL;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
valuebuf_fail:
|
nvbufs_fail:
|
||||||
nghttp2_bufs_free(&inflater->namebufs);
|
|
||||||
namebuf_fail:
|
|
||||||
hd_context_free(&inflater->ctx);
|
hd_context_free(&inflater->ctx);
|
||||||
fail:
|
fail:
|
||||||
return rv;
|
return rv;
|
||||||
@@ -397,10 +389,9 @@ static void hd_inflate_keep_free(nghttp2_hd_inflater *inflater)
|
|||||||
}
|
}
|
||||||
inflater->ent_keep = NULL;
|
inflater->ent_keep = NULL;
|
||||||
}
|
}
|
||||||
free(inflater->name_keep);
|
|
||||||
free(inflater->value_keep);
|
free(inflater->nv_keep);
|
||||||
inflater->name_keep = NULL;
|
inflater->nv_keep = NULL;
|
||||||
inflater->value_keep = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater)
|
void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater)
|
||||||
@@ -411,8 +402,7 @@ void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater)
|
|||||||
void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater)
|
void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater)
|
||||||
{
|
{
|
||||||
hd_inflate_keep_free(inflater);
|
hd_inflate_keep_free(inflater);
|
||||||
nghttp2_bufs_free(&inflater->namebufs);
|
nghttp2_bufs_free(&inflater->nvbufs);
|
||||||
nghttp2_bufs_free(&inflater->valuebufs);
|
|
||||||
hd_context_free(&inflater->ctx);
|
hd_context_free(&inflater->ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1488,27 +1478,27 @@ static int hd_inflate_remove_bufs(nghttp2_hd_inflater *inflater,
|
|||||||
nghttp2_nv *nv, int value_only)
|
nghttp2_nv *nv, int value_only)
|
||||||
{
|
{
|
||||||
ssize_t rv;
|
ssize_t rv;
|
||||||
|
size_t buflen;
|
||||||
|
uint8_t *buf;
|
||||||
|
|
||||||
|
rv = nghttp2_bufs_remove(&inflater->nvbufs, &buf);
|
||||||
|
|
||||||
|
if(rv < 0) {
|
||||||
|
return NGHTTP2_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
buflen = rv;
|
||||||
|
|
||||||
if(value_only) {
|
if(value_only) {
|
||||||
nv->name = NULL;
|
nv->name = NULL;
|
||||||
|
nv->namelen = 0;
|
||||||
} else {
|
} else {
|
||||||
rv = nghttp2_bufs_remove(&inflater->namebufs, &nv->name);
|
nv->name = buf;
|
||||||
|
nv->namelen = inflater->newnamelen;
|
||||||
if(rv < 0) {
|
|
||||||
return NGHTTP2_ERR_NOMEM;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nv->namelen = rv;
|
nv->value = buf + nv->namelen;
|
||||||
}
|
nv->valuelen = buflen - nv->namelen;
|
||||||
|
|
||||||
rv = nghttp2_bufs_remove(&inflater->valuebufs, &nv->value);
|
|
||||||
if(rv < 0) {
|
|
||||||
free(nv->name);
|
|
||||||
|
|
||||||
return NGHTTP2_ERR_NOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
nv->valuelen = rv;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1545,9 +1535,10 @@ static int hd_inflate_commit_newname(nghttp2_hd_inflater *inflater,
|
|||||||
nghttp2_hd_entry *new_ent;
|
nghttp2_hd_entry *new_ent;
|
||||||
uint8_t ent_flags;
|
uint8_t ent_flags;
|
||||||
|
|
||||||
ent_flags =
|
/* nv->value points to the middle of the buffer pointed by
|
||||||
NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_VALUE_ALLOC |
|
nv->name. So we just need to keep track of nv->name for memory
|
||||||
NGHTTP2_HD_FLAG_NAME_GIFT | NGHTTP2_HD_FLAG_VALUE_GIFT;
|
management. */
|
||||||
|
ent_flags = NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_NAME_GIFT;
|
||||||
|
|
||||||
new_ent = add_hd_table_incremental(&inflater->ctx, NULL, &nv, ent_flags);
|
new_ent = add_hd_table_incremental(&inflater->ctx, NULL, &nv, ent_flags);
|
||||||
|
|
||||||
@@ -1559,15 +1550,13 @@ static int hd_inflate_commit_newname(nghttp2_hd_inflater *inflater,
|
|||||||
}
|
}
|
||||||
|
|
||||||
free(nv.name);
|
free(nv.name);
|
||||||
free(nv.value);
|
|
||||||
|
|
||||||
return NGHTTP2_ERR_NOMEM;
|
return NGHTTP2_ERR_NOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit_literal_header(nv_out, &nv);
|
emit_literal_header(nv_out, &nv);
|
||||||
|
|
||||||
inflater->name_keep = nv.name;
|
inflater->nv_keep = nv.name;
|
||||||
inflater->value_keep = nv.value;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1642,7 +1631,7 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater,
|
|||||||
|
|
||||||
emit_literal_header(nv_out, &nv);
|
emit_literal_header(nv_out, &nv);
|
||||||
|
|
||||||
inflater->value_keep = nv.value;
|
inflater->nv_keep = nv.value;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1793,7 +1782,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
|
|||||||
case NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN:
|
case NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN:
|
||||||
rfin = 0;
|
rfin = 0;
|
||||||
rv = hd_inflate_read_len(inflater, &rfin, in, last, 7,
|
rv = hd_inflate_read_len(inflater, &rfin, in, last, 7,
|
||||||
NGHTTP2_HD_MAX_NAME);
|
NGHTTP2_HD_MAX_NV);
|
||||||
if(rv < 0) {
|
if(rv < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@@ -1815,7 +1804,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF:
|
case NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF:
|
||||||
rv = hd_inflate_read_huff(inflater, &inflater->namebufs, in, last);
|
rv = hd_inflate_read_huff(inflater, &inflater->nvbufs, in, last);
|
||||||
if(rv < 0) {
|
if(rv < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@@ -1831,11 +1820,13 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
|
|||||||
goto almost_ok;
|
goto almost_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inflater->newnamelen = nghttp2_bufs_len(&inflater->nvbufs);
|
||||||
|
|
||||||
inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN;
|
inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case NGHTTP2_HD_STATE_NEWNAME_READ_NAME:
|
case NGHTTP2_HD_STATE_NEWNAME_READ_NAME:
|
||||||
rv = hd_inflate_read(inflater, &inflater->namebufs, in, last);
|
rv = hd_inflate_read(inflater, &inflater->nvbufs, in, last);
|
||||||
if(rv < 0) {
|
if(rv < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@@ -1850,6 +1841,8 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
|
|||||||
goto almost_ok;
|
goto almost_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inflater->newnamelen = nghttp2_bufs_len(&inflater->nvbufs);
|
||||||
|
|
||||||
inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN;
|
inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -1863,7 +1856,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
|
|||||||
case NGHTTP2_HD_STATE_READ_VALUELEN:
|
case NGHTTP2_HD_STATE_READ_VALUELEN:
|
||||||
rfin = 0;
|
rfin = 0;
|
||||||
rv = hd_inflate_read_len(inflater, &rfin, in, last, 7,
|
rv = hd_inflate_read_len(inflater, &rfin, in, last, 7,
|
||||||
NGHTTP2_HD_MAX_VALUE);
|
NGHTTP2_HD_MAX_NV);
|
||||||
if(rv < 0) {
|
if(rv < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@@ -1898,7 +1891,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NGHTTP2_HD_STATE_READ_VALUEHUFF:
|
case NGHTTP2_HD_STATE_READ_VALUEHUFF:
|
||||||
rv = hd_inflate_read_huff(inflater, &inflater->valuebufs, in, last);
|
rv = hd_inflate_read_huff(inflater, &inflater->nvbufs, in, last);
|
||||||
if(rv < 0) {
|
if(rv < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@@ -1929,7 +1922,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
|
|||||||
|
|
||||||
return in - first;
|
return in - first;
|
||||||
case NGHTTP2_HD_STATE_READ_VALUE:
|
case NGHTTP2_HD_STATE_READ_VALUE:
|
||||||
rv = hd_inflate_read(inflater, &inflater->valuebufs, in, last);
|
rv = hd_inflate_read(inflater, &inflater->nvbufs, in, last);
|
||||||
if(rv < 0) {
|
if(rv < 0) {
|
||||||
DEBUGF(fprintf(stderr, "inflatehd: value read failure %zd: %s\n",
|
DEBUGF(fprintf(stderr, "inflatehd: value read failure %zd: %s\n",
|
||||||
rv, nghttp2_strerror(rv)));
|
rv, nghttp2_strerror(rv)));
|
||||||
|
|||||||
@@ -37,10 +37,10 @@
|
|||||||
#define NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE NGHTTP2_DEFAULT_HEADER_TABLE_SIZE
|
#define NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE NGHTTP2_DEFAULT_HEADER_TABLE_SIZE
|
||||||
#define NGHTTP2_HD_ENTRY_OVERHEAD 32
|
#define NGHTTP2_HD_ENTRY_OVERHEAD 32
|
||||||
|
|
||||||
/* The maximum value length of name/value pair. This is not specified
|
/* The maximum length of one name/value pair. This is the sum of the
|
||||||
by the spec. We just chose the arbitrary size */
|
length of name and value. This is not specified by the spec. We
|
||||||
#define NGHTTP2_HD_MAX_NAME 256
|
just chose the arbitrary size */
|
||||||
#define NGHTTP2_HD_MAX_VALUE 8192
|
#define NGHTTP2_HD_MAX_NV 8192
|
||||||
|
|
||||||
/* Default size of maximum table buffer size for encoder. Even if
|
/* Default size of maximum table buffer size for encoder. Even if
|
||||||
remote decoder notifies larger buffer size for its decoding,
|
remote decoder notifies larger buffer size for its decoding,
|
||||||
@@ -146,20 +146,17 @@ struct nghttp2_hd_deflater {
|
|||||||
|
|
||||||
struct nghttp2_hd_inflater {
|
struct nghttp2_hd_inflater {
|
||||||
nghttp2_hd_context ctx;
|
nghttp2_hd_context ctx;
|
||||||
/* header name buffer */
|
/* header buffer */
|
||||||
nghttp2_bufs namebufs;
|
nghttp2_bufs nvbufs;
|
||||||
/* header value buffer */
|
|
||||||
nghttp2_bufs valuebufs;
|
|
||||||
/* Stores current state of huffman decoding */
|
/* Stores current state of huffman decoding */
|
||||||
nghttp2_hd_huff_decode_context huff_decode_ctx;
|
nghttp2_hd_huff_decode_context huff_decode_ctx;
|
||||||
/* Pointer to the nghttp2_hd_entry which is used current header
|
/* Pointer to the nghttp2_hd_entry which is used current header
|
||||||
emission. This is required because in some cases the
|
emission. This is required because in some cases the
|
||||||
ent_keep->ref == 0 and we have to keep track of it. */
|
ent_keep->ref == 0 and we have to keep track of it. */
|
||||||
nghttp2_hd_entry *ent_keep;
|
nghttp2_hd_entry *ent_keep;
|
||||||
/* Pointers to the name/value pair which are used current header
|
/* Pointer to the name/value pair buffer which is used in the
|
||||||
emission. They are usually used to keep track of malloc'ed memory
|
current header emission. */
|
||||||
for huffman decoding. */
|
uint8_t *nv_keep;
|
||||||
uint8_t *name_keep, *value_keep;
|
|
||||||
/* Pointers to the name/value pair which is referred as indexed
|
/* Pointers to the name/value pair which is referred as indexed
|
||||||
name. This entry must be in header table. */
|
name. This entry must be in header table. */
|
||||||
nghttp2_hd_entry *ent_name;
|
nghttp2_hd_entry *ent_name;
|
||||||
@@ -170,6 +167,9 @@ struct nghttp2_hd_inflater {
|
|||||||
/* The index of header table to toggle off the entry from reference
|
/* The index of header table to toggle off the entry from reference
|
||||||
set at the end of decompression. */
|
set at the end of decompression. */
|
||||||
size_t end_headers_index;
|
size_t end_headers_index;
|
||||||
|
/* The length of new name encoded in literal. For huffman encoded
|
||||||
|
string, this is the length after it is decoded. */
|
||||||
|
size_t newnamelen;
|
||||||
/* The maximum header table size the inflater supports. This is the
|
/* The maximum header table size the inflater supports. This is the
|
||||||
same value transmitted in SETTINGS_HEADER_TABLE_SIZE */
|
same value transmitted in SETTINGS_HEADER_TABLE_SIZE */
|
||||||
size_t settings_hd_table_bufsize_max;
|
size_t settings_hd_table_bufsize_max;
|
||||||
|
|||||||
@@ -177,8 +177,10 @@ ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
|
|||||||
for(i = 0; i < srclen; ++i) {
|
for(i = 0; i < srclen; ++i) {
|
||||||
uint8_t in = src[i] >> 4;
|
uint8_t in = src[i] >> 4;
|
||||||
for(j = 0; j < 2; ++j) {
|
for(j = 0; j < 2; ++j) {
|
||||||
const nghttp2_huff_decode *t = &huff_decode_table[ctx->state][in];
|
const nghttp2_huff_decode *t;
|
||||||
if(t->state == -1) {
|
|
||||||
|
t = &huff_decode_table[ctx->state][in];
|
||||||
|
if(t->flags & NGHTTP2_HUFF_FAIL) {
|
||||||
return NGHTTP2_ERR_HEADER_COMP;
|
return NGHTTP2_ERR_HEADER_COMP;
|
||||||
}
|
}
|
||||||
if(t->flags & NGHTTP2_HUFF_SYM) {
|
if(t->flags & NGHTTP2_HUFF_SYM) {
|
||||||
|
|||||||
@@ -36,13 +36,17 @@ typedef enum {
|
|||||||
sequence. */
|
sequence. */
|
||||||
NGHTTP2_HUFF_ACCEPTED = 1,
|
NGHTTP2_HUFF_ACCEPTED = 1,
|
||||||
/* This state emits symbol */
|
/* This state emits symbol */
|
||||||
NGHTTP2_HUFF_SYM = (1 << 1)
|
NGHTTP2_HUFF_SYM = (1 << 1),
|
||||||
|
/* If state machine reaches this state, decoding fails. */
|
||||||
|
NGHTTP2_HUFF_FAIL = (1 << 2),
|
||||||
} nghttp2_huff_decode_flag;
|
} nghttp2_huff_decode_flag;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* huffman decoding state, which is actually the node ID of internal
|
/* huffman decoding state, which is actually the node ID of internal
|
||||||
huffman tree */
|
huffman tree. We have 257 leaf nodes, but they are identical to
|
||||||
int16_t state;
|
root node other than emitting a symbol, so we have 256 internal
|
||||||
|
nodes [1..255], inclusive. */
|
||||||
|
uint8_t state;
|
||||||
/* bitwise OR of zero or more of the nghttp2_huff_decode_flag */
|
/* bitwise OR of zero or more of the nghttp2_huff_decode_flag */
|
||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
/* symbol if NGHTTP2_HUFF_SYM flag set */
|
/* symbol if NGHTTP2_HUFF_SYM flag set */
|
||||||
|
|||||||
@@ -3624,7 +3624,7 @@ const nghttp2_huff_decode huff_decode_table[][16] = {
|
|||||||
{0, 0x03, 253},
|
{0, 0x03, 253},
|
||||||
{0, 0x03, 254},
|
{0, 0x03, 254},
|
||||||
{0, 0x03, 255},
|
{0, 0x03, 255},
|
||||||
{-1, 0x00, 0},
|
{0, 0x04, 0},
|
||||||
{189, 0x00, 0},
|
{189, 0x00, 0},
|
||||||
{191, 0x00, 0},
|
{191, 0x00, 0},
|
||||||
{192, 0x00, 0},
|
{192, 0x00, 0},
|
||||||
@@ -3772,8 +3772,8 @@ const nghttp2_huff_decode huff_decode_table[][16] = {
|
|||||||
{14, 0x03, 254},
|
{14, 0x03, 254},
|
||||||
{1, 0x02, 255},
|
{1, 0x02, 255},
|
||||||
{14, 0x03, 255},
|
{14, 0x03, 255},
|
||||||
{-1, 0x00, 0},
|
{0, 0x04, 0},
|
||||||
{-1, 0x00, 0},
|
{0, 0x04, 0},
|
||||||
{0, 0x03, 0},
|
{0, 0x03, 0},
|
||||||
{0, 0x03, 1},
|
{0, 0x03, 1},
|
||||||
{0, 0x03, 2},
|
{0, 0x03, 2},
|
||||||
@@ -3840,10 +3840,10 @@ const nghttp2_huff_decode huff_decode_table[][16] = {
|
|||||||
},
|
},
|
||||||
/* 187 */
|
/* 187 */
|
||||||
{
|
{
|
||||||
{-1, 0x00, 0},
|
{0, 0x04, 0},
|
||||||
{-1, 0x00, 0},
|
{0, 0x04, 0},
|
||||||
{-1, 0x00, 0},
|
{0, 0x04, 0},
|
||||||
{-1, 0x00, 0},
|
{0, 0x04, 0},
|
||||||
{1, 0x02, 0},
|
{1, 0x02, 0},
|
||||||
{14, 0x03, 0},
|
{14, 0x03, 0},
|
||||||
{1, 0x02, 1},
|
{1, 0x02, 1},
|
||||||
@@ -3859,14 +3859,14 @@ const nghttp2_huff_decode huff_decode_table[][16] = {
|
|||||||
},
|
},
|
||||||
/* 188 */
|
/* 188 */
|
||||||
{
|
{
|
||||||
{-1, 0x00, 0},
|
{0, 0x04, 0},
|
||||||
{-1, 0x00, 0},
|
{0, 0x04, 0},
|
||||||
{-1, 0x00, 0},
|
{0, 0x04, 0},
|
||||||
{-1, 0x00, 0},
|
{0, 0x04, 0},
|
||||||
{-1, 0x00, 0},
|
{0, 0x04, 0},
|
||||||
{-1, 0x00, 0},
|
{0, 0x04, 0},
|
||||||
{-1, 0x00, 0},
|
{0, 0x04, 0},
|
||||||
{-1, 0x00, 0},
|
{0, 0x04, 0},
|
||||||
{2, 0x02, 0},
|
{2, 0x02, 0},
|
||||||
{6, 0x02, 0},
|
{6, 0x02, 0},
|
||||||
{15, 0x02, 0},
|
{15, 0x02, 0},
|
||||||
|
|||||||
@@ -29,8 +29,6 @@
|
|||||||
# include <config.h>
|
# include <config.h>
|
||||||
#endif /* HAVE_CONFIG_H */
|
#endif /* HAVE_CONFIG_H */
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/* Macros, types and constants for internal use */
|
/* Macros, types and constants for internal use */
|
||||||
|
|
||||||
#ifdef DEBUGBUILD
|
#ifdef DEBUGBUILD
|
||||||
|
|||||||
@@ -4342,7 +4342,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
|||||||
|
|
||||||
iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
|
iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
|
||||||
|
|
||||||
if(iframe->payloadleft != 5) {
|
if(iframe->payloadleft != NGHTTP2_PRIORITY_SPECLEN) {
|
||||||
busy = 1;
|
busy = 1;
|
||||||
|
|
||||||
iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
|
iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
|
||||||
@@ -4352,7 +4352,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
|||||||
|
|
||||||
iframe->state = NGHTTP2_IB_READ_NBYTE;
|
iframe->state = NGHTTP2_IB_READ_NBYTE;
|
||||||
|
|
||||||
inbound_frame_set_mark(iframe, 5);
|
inbound_frame_set_mark(iframe, NGHTTP2_PRIORITY_SPECLEN);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case NGHTTP2_RST_STREAM:
|
case NGHTTP2_RST_STREAM:
|
||||||
@@ -4490,7 +4490,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(iframe->payloadleft < 9) {
|
if(iframe->payloadleft < NGHTTP2_ALTSVC_MINLEN) {
|
||||||
busy = 1;
|
busy = 1;
|
||||||
|
|
||||||
iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
|
iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
|
||||||
@@ -4499,7 +4499,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
|||||||
}
|
}
|
||||||
|
|
||||||
iframe->state = NGHTTP2_IB_READ_NBYTE;
|
iframe->state = NGHTTP2_IB_READ_NBYTE;
|
||||||
inbound_frame_set_mark(iframe, 8);
|
inbound_frame_set_mark(iframe, NGHTTP2_ALTSVC_FIXED_PARTLEN);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case NGHTTP2_BLOCKED:
|
case NGHTTP2_BLOCKED:
|
||||||
@@ -4711,7 +4711,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
|||||||
case NGHTTP2_ALTSVC: {
|
case NGHTTP2_ALTSVC: {
|
||||||
size_t varlen;
|
size_t varlen;
|
||||||
|
|
||||||
varlen = iframe->frame.hd.length - 8;
|
varlen = iframe->frame.hd.length - NGHTTP2_ALTSVC_FIXED_PARTLEN;
|
||||||
|
|
||||||
if(varlen > 0) {
|
if(varlen > 0) {
|
||||||
iframe->raw_lbuf = malloc(varlen);
|
iframe->raw_lbuf = malloc(varlen);
|
||||||
@@ -5195,7 +5195,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
|||||||
|
|
||||||
DEBUGF(fprintf(stderr, "recv: data_readlen=%zu\n", data_readlen));
|
DEBUGF(fprintf(stderr, "recv: data_readlen=%zu\n", data_readlen));
|
||||||
|
|
||||||
if(data_readlen > 0 &&
|
if(stream && data_readlen > 0 &&
|
||||||
session->callbacks.on_data_chunk_recv_callback) {
|
session->callbacks.on_data_chunk_recv_callback) {
|
||||||
rv = session->callbacks.on_data_chunk_recv_callback
|
rv = session->callbacks.on_data_chunk_recv_callback
|
||||||
(session,
|
(session,
|
||||||
@@ -5293,28 +5293,47 @@ int nghttp2_session_recv(nghttp2_session *session)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the number of active streams, which includes streams in
|
||||||
|
* reserved state.
|
||||||
|
*/
|
||||||
|
static size_t session_get_num_active_streams(nghttp2_session *session)
|
||||||
|
{
|
||||||
|
return nghttp2_map_size(&session->streams) - session->num_closed_streams;
|
||||||
|
}
|
||||||
|
|
||||||
int nghttp2_session_want_read(nghttp2_session *session)
|
int nghttp2_session_want_read(nghttp2_session *session)
|
||||||
{
|
{
|
||||||
|
size_t num_active_streams;
|
||||||
|
|
||||||
/* If these flags are set, we don't want to read. The application
|
/* If these flags are set, we don't want to read. The application
|
||||||
should drop the connection. */
|
should drop the connection. */
|
||||||
if((session->goaway_flags & NGHTTP2_GOAWAY_FAIL_ON_SEND) &&
|
if((session->goaway_flags & NGHTTP2_GOAWAY_FAIL_ON_SEND) &&
|
||||||
(session->goaway_flags & NGHTTP2_GOAWAY_SEND)) {
|
(session->goaway_flags & NGHTTP2_GOAWAY_SEND)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
num_active_streams = session_get_num_active_streams(session);
|
||||||
|
|
||||||
/* Unless GOAWAY is sent or received, we always want to read
|
/* Unless GOAWAY is sent or received, we always want to read
|
||||||
incoming frames. After GOAWAY is sent or received, we are only
|
incoming frames. After GOAWAY is sent or received, we are only
|
||||||
interested in active streams. */
|
interested in active streams. */
|
||||||
return !session->goaway_flags || nghttp2_map_size(&session->streams) > 0;
|
return !session->goaway_flags || num_active_streams > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_session_want_write(nghttp2_session *session)
|
int nghttp2_session_want_write(nghttp2_session *session)
|
||||||
{
|
{
|
||||||
|
size_t num_active_streams;
|
||||||
|
|
||||||
/* If these flags are set, we don't want to write any data. The
|
/* If these flags are set, we don't want to write any data. The
|
||||||
application should drop the connection. */
|
application should drop the connection. */
|
||||||
if((session->goaway_flags & NGHTTP2_GOAWAY_FAIL_ON_SEND) &&
|
if((session->goaway_flags & NGHTTP2_GOAWAY_FAIL_ON_SEND) &&
|
||||||
(session->goaway_flags & NGHTTP2_GOAWAY_SEND)) {
|
(session->goaway_flags & NGHTTP2_GOAWAY_SEND)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
num_active_streams = session_get_num_active_streams(session);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Unless GOAWAY is sent or received, we want to write frames if
|
* Unless GOAWAY is sent or received, we want to write frames if
|
||||||
* there is pending ones. If pending frame is request/push response
|
* there is pending ones. If pending frame is request/push response
|
||||||
@@ -5325,7 +5344,7 @@ int nghttp2_session_want_write(nghttp2_session *session)
|
|||||||
return (session->aob.item != NULL || !nghttp2_pq_empty(&session->ob_pq) ||
|
return (session->aob.item != NULL || !nghttp2_pq_empty(&session->ob_pq) ||
|
||||||
(!nghttp2_pq_empty(&session->ob_ss_pq) &&
|
(!nghttp2_pq_empty(&session->ob_ss_pq) &&
|
||||||
!session_is_outgoing_concurrent_streams_max(session))) &&
|
!session_is_outgoing_concurrent_streams_max(session))) &&
|
||||||
(!session->goaway_flags || nghttp2_map_size(&session->streams) > 0);
|
(!session->goaway_flags || num_active_streams > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags,
|
int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags,
|
||||||
|
|||||||
11
mkhufftbl.py
11
mkhufftbl.py
@@ -84,6 +84,7 @@ def dfs(node, root):
|
|||||||
|
|
||||||
NGHTTP2_HUFF_ACCEPTED = 1
|
NGHTTP2_HUFF_ACCEPTED = 1
|
||||||
NGHTTP2_HUFF_SYM = 1 << 1
|
NGHTTP2_HUFF_SYM = 1 << 1
|
||||||
|
NGHTTP2_HUFF_FAIL = 1 << 2
|
||||||
|
|
||||||
def dfs_print(node):
|
def dfs_print(node):
|
||||||
if node.term is not None:
|
if node.term is not None:
|
||||||
@@ -100,7 +101,8 @@ def dfs_print(node):
|
|||||||
out = syms[0]
|
out = syms[0]
|
||||||
flags |= NGHTTP2_HUFF_SYM
|
flags |= NGHTTP2_HUFF_SYM
|
||||||
if nd is None:
|
if nd is None:
|
||||||
id = -1
|
id = 0
|
||||||
|
flags |= NGHTTP2_HUFF_FAIL
|
||||||
else:
|
else:
|
||||||
id = nd.id
|
id = nd.id
|
||||||
if id is None:
|
if id is None:
|
||||||
@@ -159,13 +161,14 @@ print ''
|
|||||||
print '''\
|
print '''\
|
||||||
enum {{
|
enum {{
|
||||||
NGHTTP2_HUFF_ACCEPTED = {},
|
NGHTTP2_HUFF_ACCEPTED = {},
|
||||||
NGHTTP2_HUFF_SYM = {}
|
NGHTTP2_HUFF_SYM = {},
|
||||||
|
NGHTTP2_HUFF_FAIL = {},
|
||||||
}} nghttp2_huff_decode_flag;
|
}} nghttp2_huff_decode_flag;
|
||||||
'''.format(NGHTTP2_HUFF_ACCEPTED, NGHTTP2_HUFF_SYM)
|
'''.format(NGHTTP2_HUFF_ACCEPTED, NGHTTP2_HUFF_SYM, NGHTTP2_HUFF_FAIL)
|
||||||
|
|
||||||
print '''\
|
print '''\
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int16_t state;
|
uint8_t state;
|
||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
uint8_t sym;
|
uint8_t sym;
|
||||||
} nghttp2_huff_decode;
|
} nghttp2_huff_decode;
|
||||||
|
|||||||
@@ -1258,14 +1258,14 @@ int hd_on_frame_recv_callback
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS: {
|
||||||
switch(frame->headers.cat) {
|
|
||||||
case NGHTTP2_HCAT_REQUEST: {
|
|
||||||
auto stream = hd->get_stream(frame->hd.stream_id);
|
auto stream = hd->get_stream(frame->hd.stream_id);
|
||||||
if(!stream) {
|
if(!stream) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
|
||||||
|
|
||||||
http2::normalize_headers(stream->headers);
|
http2::normalize_headers(stream->headers);
|
||||||
if(!http2::check_http2_headers(stream->headers)) {
|
if(!http2::check_http2_headers(stream->headers)) {
|
||||||
hd->submit_rst_stream(stream, NGHTTP2_PROTOCOL_ERROR);
|
hd->submit_rst_stream(stream, NGHTTP2_PROTOCOL_ERROR);
|
||||||
@@ -1285,6 +1285,8 @@ int hd_on_frame_recv_callback
|
|||||||
hd->submit_rst_stream(stream, NGHTTP2_PROTOCOL_ERROR);
|
hd->submit_rst_stream(stream, NGHTTP2_PROTOCOL_ERROR);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||||
remove_stream_read_timeout(stream);
|
remove_stream_read_timeout(stream);
|
||||||
|
|
||||||
@@ -1292,12 +1294,9 @@ int hd_on_frame_recv_callback
|
|||||||
} else {
|
} else {
|
||||||
add_stream_read_timeout(stream);
|
add_stream_read_timeout(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case NGHTTP2_SETTINGS:
|
case NGHTTP2_SETTINGS:
|
||||||
if(frame->hd.flags & NGHTTP2_FLAG_ACK) {
|
if(frame->hd.flags & NGHTTP2_FLAG_ACK) {
|
||||||
hd->remove_settings_timer();
|
hd->remove_settings_timer();
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <future>
|
||||||
|
|
||||||
#ifdef HAVE_SPDYLAY
|
#ifdef HAVE_SPDYLAY
|
||||||
#include <spdylay/spdylay.h>
|
#include <spdylay/spdylay.h>
|
||||||
@@ -549,6 +550,15 @@ std::string get_reqline(const char *uri, const http_parser_url& u)
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::unique_ptr<Worker> run(std::unique_ptr<Worker> worker)
|
||||||
|
{
|
||||||
|
worker->run();
|
||||||
|
|
||||||
|
return worker;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
int client_select_next_proto_cb(SSL* ssl,
|
int client_select_next_proto_cb(SSL* ssl,
|
||||||
unsigned char **out, unsigned char *outlen,
|
unsigned char **out, unsigned char *outlen,
|
||||||
@@ -888,7 +898,7 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
std::cout << "starting benchmark..." << std::endl;
|
std::cout << "starting benchmark..." << std::endl;
|
||||||
|
|
||||||
std::vector<std::thread> threads;
|
std::vector<std::future<std::unique_ptr<Worker>>> futures;
|
||||||
auto start = std::chrono::steady_clock::now();
|
auto start = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
std::vector<std::unique_ptr<Worker>> workers;
|
std::vector<std::unique_ptr<Worker>> workers;
|
||||||
@@ -899,9 +909,9 @@ int main(int argc, char **argv)
|
|||||||
<< nclients << " concurrent clients, "
|
<< nclients << " concurrent clients, "
|
||||||
<< nreqs << " total requests"
|
<< nreqs << " total requests"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
workers.push_back(util::make_unique<Worker>(i, ssl_ctx, nreqs, nclients,
|
auto worker = util::make_unique<Worker>(i, ssl_ctx, nreqs, nclients,
|
||||||
&config));
|
&config);
|
||||||
threads.emplace_back(&Worker::run, workers.back().get());
|
futures.push_back(std::async(std::launch::async, run, std::move(worker)));
|
||||||
}
|
}
|
||||||
auto nreqs_last = nreqs_per_thread + (nreqs_rem-- > 0);
|
auto nreqs_last = nreqs_per_thread + (nreqs_rem-- > 0);
|
||||||
auto nclients_last = nclients_per_thread + (nclients_rem-- > 0);
|
auto nclients_last = nclients_per_thread + (nclients_rem-- > 0);
|
||||||
@@ -913,19 +923,23 @@ int main(int argc, char **argv)
|
|||||||
&config);
|
&config);
|
||||||
worker.run();
|
worker.run();
|
||||||
|
|
||||||
for(size_t i = 0; i < config.nthreads - 1; ++i) {
|
for(auto& fut : futures) {
|
||||||
threads[i].join();
|
auto subworker = fut.get();
|
||||||
worker.stats.req_todo += workers[i]->stats.req_todo;
|
auto& stats = subworker->stats;
|
||||||
worker.stats.req_started += workers[i]->stats.req_started;
|
|
||||||
worker.stats.req_done += workers[i]->stats.req_done;
|
worker.stats.req_todo += stats.req_todo;
|
||||||
worker.stats.req_success += workers[i]->stats.req_success;
|
worker.stats.req_started += stats.req_started;
|
||||||
worker.stats.req_failed += workers[i]->stats.req_failed;
|
worker.stats.req_done += stats.req_done;
|
||||||
worker.stats.req_error += workers[i]->stats.req_error;
|
worker.stats.req_success += stats.req_success;
|
||||||
worker.stats.bytes_total += workers[i]->stats.bytes_total;
|
worker.stats.req_failed += stats.req_failed;
|
||||||
worker.stats.bytes_head += workers[i]->stats.bytes_head;
|
worker.stats.req_error += stats.req_error;
|
||||||
worker.stats.bytes_body += workers[i]->stats.bytes_body;
|
worker.stats.bytes_total += stats.bytes_total;
|
||||||
for(size_t j = 0; j < 6; ++j) {
|
worker.stats.bytes_head += stats.bytes_head;
|
||||||
worker.stats.status[j] += workers[i]->stats.status[j];
|
worker.stats.bytes_body += stats.bytes_body;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < sizeof(stats.status) / sizeof(stats.status[0]);
|
||||||
|
++i) {
|
||||||
|
worker.stats.status[i] += stats.status[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -153,16 +153,33 @@ void Http2Session::submit_request()
|
|||||||
ssize_t Http2Session::on_read()
|
ssize_t Http2Session::on_read()
|
||||||
{
|
{
|
||||||
int rv;
|
int rv;
|
||||||
|
size_t nread = 0;
|
||||||
|
|
||||||
auto input = bufferevent_get_input(client_->bev);
|
auto input = bufferevent_get_input(client_->bev);
|
||||||
auto inputlen = evbuffer_get_length(input);
|
|
||||||
auto mem = evbuffer_pullup(input, -1);
|
for(;;) {
|
||||||
|
auto inputlen = evbuffer_get_contiguous_space(input);
|
||||||
|
|
||||||
|
if(inputlen == 0) {
|
||||||
|
assert(evbuffer_get_length(input) == 0);
|
||||||
|
|
||||||
|
return nread;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mem = evbuffer_pullup(input, inputlen);
|
||||||
|
|
||||||
rv = nghttp2_session_mem_recv(session_, mem, inputlen);
|
rv = nghttp2_session_mem_recv(session_, mem, inputlen);
|
||||||
|
|
||||||
if(rv < 0) {
|
if(rv < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
evbuffer_drain(input, rv);
|
|
||||||
return rv;
|
nread += rv;
|
||||||
|
|
||||||
|
if(evbuffer_drain(input, rv) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Http2Session::on_write()
|
int Http2Session::on_write()
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
*/
|
*/
|
||||||
#include "h2load_spdy_session.h"
|
#include "h2load_spdy_session.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
#include "h2load.h"
|
#include "h2load.h"
|
||||||
|
|
||||||
namespace h2load {
|
namespace h2load {
|
||||||
@@ -163,16 +165,32 @@ void SpdySession::submit_request()
|
|||||||
ssize_t SpdySession::on_read()
|
ssize_t SpdySession::on_read()
|
||||||
{
|
{
|
||||||
int rv;
|
int rv;
|
||||||
|
size_t nread = 0;
|
||||||
auto input = bufferevent_get_input(client_->bev);
|
auto input = bufferevent_get_input(client_->bev);
|
||||||
auto inputlen = evbuffer_get_length(input);
|
|
||||||
auto mem = evbuffer_pullup(input, -1);
|
for(;;) {
|
||||||
|
auto inputlen = evbuffer_get_contiguous_space(input);
|
||||||
|
|
||||||
|
if(inputlen == 0) {
|
||||||
|
assert(evbuffer_get_length(input) == 0);
|
||||||
|
|
||||||
|
return nread;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mem = evbuffer_pullup(input, inputlen);
|
||||||
|
|
||||||
rv = spdylay_session_mem_recv(session_, mem, inputlen);
|
rv = spdylay_session_mem_recv(session_, mem, inputlen);
|
||||||
|
|
||||||
if(rv < 0) {
|
if(rv < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
evbuffer_drain(input, rv);
|
|
||||||
return rv;
|
nread += rv;
|
||||||
|
|
||||||
|
if(evbuffer_drain(input, rv) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int SpdySession::on_write()
|
int SpdySession::on_write()
|
||||||
|
|||||||
100
src/nghttp.cc
100
src/nghttp.cc
@@ -651,55 +651,77 @@ struct HttpClient {
|
|||||||
{
|
{
|
||||||
int rv;
|
int rv;
|
||||||
auto input = bufferevent_get_input(bev);
|
auto input = bufferevent_get_input(bev);
|
||||||
auto inputlen = evbuffer_get_length(input);
|
|
||||||
|
for(;;) {
|
||||||
|
auto inputlen = evbuffer_get_contiguous_space(input);
|
||||||
|
|
||||||
if(inputlen == 0) {
|
if(inputlen == 0) {
|
||||||
|
assert(evbuffer_get_length(input) == 0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
auto mem = evbuffer_pullup(input, -1);
|
|
||||||
|
auto mem = evbuffer_pullup(input, inputlen);
|
||||||
|
|
||||||
auto nread = http_parser_execute(htp.get(), &htp_hooks,
|
auto nread = http_parser_execute(htp.get(), &htp_hooks,
|
||||||
reinterpret_cast<const char*>(mem),
|
reinterpret_cast<const char*>(mem),
|
||||||
inputlen);
|
inputlen);
|
||||||
|
|
||||||
if(config.verbose) {
|
if(config.verbose) {
|
||||||
std::cout.write(reinterpret_cast<const char*>(mem), nread);
|
std::cout.write(reinterpret_cast<const char*>(mem), nread);
|
||||||
}
|
}
|
||||||
evbuffer_drain(input, nread);
|
|
||||||
|
|
||||||
auto htperr = HTTP_PARSER_ERRNO(htp.get());
|
if(evbuffer_drain(input, nread) != 0) {
|
||||||
if(htperr == HPE_OK) {
|
|
||||||
if(upgrade_response_complete) {
|
|
||||||
if(config.verbose) {
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
|
||||||
if(upgrade_response_status_code == 101) {
|
|
||||||
if(config.verbose) {
|
|
||||||
print_timer();
|
|
||||||
std::cout << " HTTP Upgrade success" << std::endl;
|
|
||||||
}
|
|
||||||
bufferevent_setcb(bev, readcb, writecb, eventcb, this);
|
|
||||||
rv = on_connect();
|
|
||||||
if(rv != 0) {
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
// Read remaining data in the buffer because it is not
|
|
||||||
// notified callback anymore.
|
|
||||||
rv = on_read();
|
|
||||||
if(rv != 0) {
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::cerr << "HTTP Upgrade failed" << std::endl;
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
auto htperr = HTTP_PARSER_ERRNO(htp.get());
|
||||||
|
|
||||||
|
if(htperr != HPE_OK) {
|
||||||
std::cerr << "Failed to parse HTTP Upgrade response header: "
|
std::cerr << "Failed to parse HTTP Upgrade response header: "
|
||||||
<< "(" << http_errno_name(htperr) << ") "
|
<< "(" << http_errno_name(htperr) << ") "
|
||||||
<< http_errno_description(htperr) << std::endl;
|
<< http_errno_description(htperr) << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(upgrade_response_complete) {
|
||||||
|
|
||||||
|
if(config.verbose) {
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(upgrade_response_status_code == 101) {
|
||||||
|
if(config.verbose) {
|
||||||
|
print_timer();
|
||||||
|
std::cout << " HTTP Upgrade success" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferevent_setcb(bev, readcb, writecb, eventcb, this);
|
||||||
|
|
||||||
|
rv = on_connect();
|
||||||
|
|
||||||
|
if(rv != 0) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read remaining data in the buffer because it is not
|
||||||
|
// notified callback anymore.
|
||||||
|
rv = on_read();
|
||||||
|
|
||||||
|
if(rv != 0) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::cerr << "HTTP Upgrade failed" << std::endl;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int on_connect()
|
int on_connect()
|
||||||
{
|
{
|
||||||
int rv;
|
int rv;
|
||||||
@@ -776,18 +798,30 @@ struct HttpClient {
|
|||||||
{
|
{
|
||||||
int rv;
|
int rv;
|
||||||
auto input = bufferevent_get_input(bev);
|
auto input = bufferevent_get_input(bev);
|
||||||
auto inputlen = evbuffer_get_length(input);
|
|
||||||
auto mem = evbuffer_pullup(input, -1);
|
for(;;) {
|
||||||
|
auto inputlen = evbuffer_get_contiguous_space(input);
|
||||||
|
|
||||||
|
if(inputlen == 0) {
|
||||||
|
assert(evbuffer_get_length(input) == 0);
|
||||||
|
|
||||||
|
return on_write();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mem = evbuffer_pullup(input, inputlen);
|
||||||
|
|
||||||
rv = nghttp2_session_mem_recv(session, mem, inputlen);
|
rv = nghttp2_session_mem_recv(session, mem, inputlen);
|
||||||
|
|
||||||
if(rv < 0) {
|
if(rv < 0) {
|
||||||
std::cerr << "nghttp2_session_mem_recv() returned error: "
|
std::cerr << "nghttp2_session_mem_recv() returned error: "
|
||||||
<< nghttp2_strerror(rv) << std::endl;
|
<< nghttp2_strerror(rv) << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
evbuffer_drain(input, rv);
|
|
||||||
|
|
||||||
return on_write();
|
if(evbuffer_drain(input, rv) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int on_write()
|
int on_write()
|
||||||
|
|||||||
23
src/shrpx.cc
23
src/shrpx.cc
@@ -131,7 +131,6 @@ void evlistener_errorcb(evconnlistener *listener, void *ptr)
|
|||||||
namespace {
|
namespace {
|
||||||
evconnlistener* create_evlistener(ListenHandler *handler, int family)
|
evconnlistener* create_evlistener(ListenHandler *handler, int family)
|
||||||
{
|
{
|
||||||
// TODO Listen both IPv4 and IPv6
|
|
||||||
addrinfo hints;
|
addrinfo hints;
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
int r;
|
int r;
|
||||||
@@ -145,8 +144,10 @@ evconnlistener* create_evlistener(ListenHandler *handler, int family)
|
|||||||
hints.ai_flags |= AI_ADDRCONFIG;
|
hints.ai_flags |= AI_ADDRCONFIG;
|
||||||
#endif // AI_ADDRCONFIG
|
#endif // AI_ADDRCONFIG
|
||||||
|
|
||||||
|
auto node = strcmp("*", get_config()->host) == 0 ? NULL : get_config()->host;
|
||||||
|
|
||||||
addrinfo *res, *rp;
|
addrinfo *res, *rp;
|
||||||
r = getaddrinfo(get_config()->host, service.c_str(), &hints, &res);
|
r = getaddrinfo(node, service.c_str(), &hints, &res);
|
||||||
if(r != 0) {
|
if(r != 0) {
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
LOG(INFO) << "Unable to get IPv" << (family == AF_INET ? "4" : "6")
|
LOG(INFO) << "Unable to get IPv" << (family == AF_INET ? "4" : "6")
|
||||||
@@ -346,7 +347,7 @@ void fill_default_config()
|
|||||||
mod_config()->daemon = false;
|
mod_config()->daemon = false;
|
||||||
|
|
||||||
mod_config()->server_name = "nghttpx nghttp2/" NGHTTP2_VERSION;
|
mod_config()->server_name = "nghttpx nghttp2/" NGHTTP2_VERSION;
|
||||||
set_config_str(&mod_config()->host, "0.0.0.0");
|
set_config_str(&mod_config()->host, "*");
|
||||||
mod_config()->port = 3000;
|
mod_config()->port = 3000;
|
||||||
mod_config()->private_key_file = 0;
|
mod_config()->private_key_file = 0;
|
||||||
mod_config()->private_key_passwd = 0;
|
mod_config()->private_key_passwd = 0;
|
||||||
@@ -499,7 +500,9 @@ Connections:
|
|||||||
<< get_config()->downstream_host << ","
|
<< get_config()->downstream_host << ","
|
||||||
<< get_config()->downstream_port << R"('
|
<< get_config()->downstream_port << R"('
|
||||||
-f, --frontend=<HOST,PORT>
|
-f, --frontend=<HOST,PORT>
|
||||||
Set frontend host and port.
|
Set frontend host and port. If <HOST> is '*', it
|
||||||
|
assumes all addresses including both IPv4 and
|
||||||
|
IPv6.
|
||||||
Default: ')"
|
Default: ')"
|
||||||
<< get_config()->host << "," << get_config()->port << R"('
|
<< get_config()->host << "," << get_config()->port << R"('
|
||||||
--backlog=<NUM> Set listen backlog size. If -1 is given,
|
--backlog=<NUM> Set listen backlog size. If -1 is given,
|
||||||
@@ -646,12 +649,12 @@ SSL/TLS:
|
|||||||
Path to file that contains DH parameters in PEM
|
Path to file that contains DH parameters in PEM
|
||||||
format. Without this option, DHE cipher suites
|
format. Without this option, DHE cipher suites
|
||||||
are not available.
|
are not available.
|
||||||
--npn-list=<LIST> Comma delimited list of NPN/ALPN protocol sorted
|
--npn-list=<LIST> Comma delimited list of ALPN protocol identifier
|
||||||
in the order of preference. That means most
|
sorted in the order of preference. That means
|
||||||
desirable protocol comes first. The parameter
|
most desirable protocol comes first. This is
|
||||||
must be delimited by a single comma only and any
|
used in both ALPN and NPN. The parameter must be
|
||||||
white spaces are treated as a part of protocol
|
delimited by a single comma only and any white
|
||||||
string.
|
spaces are treated as a part of protocol string.
|
||||||
Default: )" << DEFAULT_NPN_LIST << R"(
|
Default: )" << DEFAULT_NPN_LIST << R"(
|
||||||
--verify-client Require and verify client certificate.
|
--verify-client Require and verify client certificate.
|
||||||
--verify-client-cacert=<PATH>
|
--verify-client-cacert=<PATH>
|
||||||
|
|||||||
@@ -36,6 +36,6 @@
|
|||||||
#define DIE() \
|
#define DIE() \
|
||||||
assert(0);
|
assert(0);
|
||||||
|
|
||||||
#define SHRPX_READ_WARTER_MARK (64*1024)
|
#define SHRPX_READ_WATERMARK (16 * 1024)
|
||||||
|
|
||||||
#endif // SHRPX_H
|
#endif // SHRPX_H
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ namespace {
|
|||||||
void upstream_writecb(bufferevent *bev, void *arg)
|
void upstream_writecb(bufferevent *bev, void *arg)
|
||||||
{
|
{
|
||||||
auto handler = static_cast<ClientHandler*>(arg);
|
auto handler = static_cast<ClientHandler*>(arg);
|
||||||
// We actually depend on write low-warter mark == 0.
|
// We actually depend on write low-water mark == 0.
|
||||||
if(handler->get_outbuf_length() > 0) {
|
if(handler->get_outbuf_length() > 0) {
|
||||||
// Possibly because of deferred callback, we may get this callback
|
// Possibly because of deferred callback, we may get this callback
|
||||||
// when the output buffer is not empty.
|
// when the output buffer is not empty.
|
||||||
@@ -279,7 +279,7 @@ ClientHandler::ClientHandler(bufferevent *bev,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bufferevent_enable(bev_, EV_READ | EV_WRITE);
|
bufferevent_enable(bev_, EV_READ | EV_WRITE);
|
||||||
bufferevent_setwatermark(bev_, EV_READ, 0, SHRPX_READ_WARTER_MARK);
|
bufferevent_setwatermark(bev_, EV_READ, 0, SHRPX_READ_WATERMARK);
|
||||||
set_upstream_timeouts(&get_config()->upstream_read_timeout,
|
set_upstream_timeouts(&get_config()->upstream_read_timeout,
|
||||||
&get_config()->upstream_write_timeout);
|
&get_config()->upstream_write_timeout);
|
||||||
if(ssl_) {
|
if(ssl_) {
|
||||||
@@ -361,7 +361,13 @@ void ClientHandler::set_bev_cb
|
|||||||
void ClientHandler::set_upstream_timeouts(const timeval *read_timeout,
|
void ClientHandler::set_upstream_timeouts(const timeval *read_timeout,
|
||||||
const timeval *write_timeout)
|
const timeval *write_timeout)
|
||||||
{
|
{
|
||||||
bufferevent_set_timeouts(bev_, read_timeout, write_timeout);
|
auto bev = bufferevent_get_underlying(bev_);
|
||||||
|
|
||||||
|
if(!bev) {
|
||||||
|
bev = bev_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferevent_set_timeouts(bev, read_timeout, write_timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ClientHandler::validate_next_proto()
|
int ClientHandler::validate_next_proto()
|
||||||
|
|||||||
@@ -272,19 +272,25 @@ void eventcb(bufferevent *bev, short events, void *ptr)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int fd = bufferevent_getfd(bev);
|
auto fd = bufferevent_getfd(bev);
|
||||||
int val = 1;
|
int val = 1;
|
||||||
if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
|
if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
|
||||||
reinterpret_cast<char *>(&val), sizeof(val)) == -1) {
|
reinterpret_cast<char*>(&val), sizeof(val)) == -1) {
|
||||||
SSLOG(WARNING, http2session)
|
SSLOG(WARNING, http2session)
|
||||||
<< "Setting option TCP_NODELAY failed: errno=" << errno;
|
<< "Setting option TCP_NODELAY failed: errno=" << errno;
|
||||||
}
|
}
|
||||||
} else if(events & BEV_EVENT_EOF) {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(events & BEV_EVENT_EOF) {
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
SSLOG(INFO, http2session) << "EOF";
|
SSLOG(INFO, http2session) << "EOF";
|
||||||
}
|
}
|
||||||
http2session->disconnect();
|
http2session->disconnect();
|
||||||
} else if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
if(events & BEV_EVENT_ERROR) {
|
if(events & BEV_EVENT_ERROR) {
|
||||||
SSLOG(INFO, http2session) << "Network error";
|
SSLOG(INFO, http2session) << "Network error";
|
||||||
@@ -293,6 +299,7 @@ void eventcb(bufferevent *bev, short events, void *ptr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
http2session->disconnect();
|
http2session->disconnect();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -350,12 +357,18 @@ void proxy_eventcb(bufferevent *bev, short events, void *ptr)
|
|||||||
SSLOG(ERROR, http2session) << "bufferevent_write() failed";
|
SSLOG(ERROR, http2session) << "bufferevent_write() failed";
|
||||||
http2session->disconnect();
|
http2session->disconnect();
|
||||||
}
|
}
|
||||||
} else if(events & BEV_EVENT_EOF) {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(events & BEV_EVENT_EOF) {
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
SSLOG(INFO, http2session) << "Proxy EOF";
|
SSLOG(INFO, http2session) << "Proxy EOF";
|
||||||
}
|
}
|
||||||
http2session->disconnect();
|
http2session->disconnect();
|
||||||
} else if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
if(events & BEV_EVENT_ERROR) {
|
if(events & BEV_EVENT_ERROR) {
|
||||||
SSLOG(INFO, http2session) << "Network error";
|
SSLOG(INFO, http2session) << "Network error";
|
||||||
@@ -364,6 +377,7 @@ void proxy_eventcb(bufferevent *bev, short events, void *ptr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
http2session->disconnect();
|
http2session->disconnect();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -409,7 +423,11 @@ int Http2Session::initiate_connection()
|
|||||||
proxy_htp_->data = this;
|
proxy_htp_->data = this;
|
||||||
|
|
||||||
state_ = PROXY_CONNECTING;
|
state_ = PROXY_CONNECTING;
|
||||||
} else if(state_ == DISCONNECTED || state_ == PROXY_CONNECTED) {
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(state_ == DISCONNECTED || state_ == PROXY_CONNECTED) {
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
SSLOG(INFO, this) << "Connecting to downstream server";
|
SSLOG(INFO, this) << "Connecting to downstream server";
|
||||||
}
|
}
|
||||||
@@ -481,7 +499,7 @@ int Http2Session::initiate_connection()
|
|||||||
return SHRPX_ERR_NETWORK;
|
return SHRPX_ERR_NETWORK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bufferevent_setwatermark(bev_, EV_READ, 0, SHRPX_READ_WARTER_MARK);
|
bufferevent_setwatermark(bev_, EV_READ, 0, SHRPX_READ_WATERMARK);
|
||||||
bufferevent_enable(bev_, EV_READ);
|
bufferevent_enable(bev_, EV_READ);
|
||||||
bufferevent_setcb(bev_, readcb, writecb, eventcb, this);
|
bufferevent_setcb(bev_, readcb, writecb, eventcb, this);
|
||||||
// Set timeout for HTTP2 session
|
// Set timeout for HTTP2 session
|
||||||
@@ -492,11 +510,12 @@ int Http2Session::initiate_connection()
|
|||||||
if(state_ != CONNECTED) {
|
if(state_ != CONNECTED) {
|
||||||
state_ = CONNECTING;
|
state_ = CONNECTING;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Unreachable
|
// Unreachable
|
||||||
DIE();
|
DIE();
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Http2Session::unwrap_free_bev()
|
void Http2Session::unwrap_free_bev()
|
||||||
@@ -517,10 +536,13 @@ int htp_hdrs_completecb(http_parser *htp)
|
|||||||
SSLOG(INFO, http2session) << "Tunneling success";
|
SSLOG(INFO, http2session) << "Tunneling success";
|
||||||
}
|
}
|
||||||
http2session->set_state(Http2Session::PROXY_CONNECTED);
|
http2session->set_state(Http2Session::PROXY_CONNECTED);
|
||||||
} else {
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
SSLOG(WARNING, http2session) << "Tunneling failed";
|
SSLOG(WARNING, http2session) << "Tunneling failed";
|
||||||
http2session->set_state(Http2Session::PROXY_FAILED);
|
http2session->set_state(Http2Session::PROXY_FAILED);
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -541,22 +563,33 @@ http_parser_settings htp_hooks = {
|
|||||||
int Http2Session::on_read_proxy()
|
int Http2Session::on_read_proxy()
|
||||||
{
|
{
|
||||||
auto input = bufferevent_get_input(bev_);
|
auto input = bufferevent_get_input(bev_);
|
||||||
auto mem = evbuffer_pullup(input, -1);
|
|
||||||
|
for(;;) {
|
||||||
|
auto inputlen = evbuffer_get_contiguous_space(input);
|
||||||
|
|
||||||
|
if(inputlen == 0) {
|
||||||
|
assert(evbuffer_get_length(input) == 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mem = evbuffer_pullup(input, inputlen);
|
||||||
|
|
||||||
size_t nread = http_parser_execute(proxy_htp_.get(), &htp_hooks,
|
size_t nread = http_parser_execute(proxy_htp_.get(), &htp_hooks,
|
||||||
reinterpret_cast<const char*>(mem),
|
reinterpret_cast<const char*>(mem),
|
||||||
evbuffer_get_length(input));
|
inputlen);
|
||||||
|
|
||||||
if(evbuffer_drain(input, nread) != 0) {
|
if(evbuffer_drain(input, nread) != 0) {
|
||||||
SSLOG(FATAL, this) << "evbuffer_drain() failed";
|
SSLOG(FATAL, this) << "evbuffer_drain() failed";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto htperr = HTTP_PARSER_ERRNO(proxy_htp_.get());
|
auto htperr = HTTP_PARSER_ERRNO(proxy_htp_.get());
|
||||||
if(htperr == HPE_OK) {
|
|
||||||
return 0;
|
if(htperr != HPE_OK) {
|
||||||
} else {
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Http2Session::add_downstream_connection(Http2DownstreamConnection *dconn)
|
void Http2Session::add_downstream_connection(Http2DownstreamConnection *dconn)
|
||||||
@@ -1250,17 +1283,31 @@ int Http2Session::on_read()
|
|||||||
{
|
{
|
||||||
ssize_t rv = 0;
|
ssize_t rv = 0;
|
||||||
auto input = bufferevent_get_input(bev_);
|
auto input = bufferevent_get_input(bev_);
|
||||||
auto inputlen = evbuffer_get_length(input);
|
|
||||||
auto mem = evbuffer_pullup(input, -1);
|
for(;;) {
|
||||||
|
auto inputlen = evbuffer_get_contiguous_space(input);
|
||||||
|
|
||||||
|
if(inputlen == 0) {
|
||||||
|
assert(evbuffer_get_length(input) == 0);
|
||||||
|
|
||||||
|
return send();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mem = evbuffer_pullup(input, inputlen);
|
||||||
|
|
||||||
rv = nghttp2_session_mem_recv(session_, mem, inputlen);
|
rv = nghttp2_session_mem_recv(session_, mem, inputlen);
|
||||||
|
|
||||||
if(rv < 0) {
|
if(rv < 0) {
|
||||||
SSLOG(ERROR, this) << "nghttp2_session_recv() returned error: "
|
SSLOG(ERROR, this) << "nghttp2_session_recv() returned error: "
|
||||||
<< nghttp2_strerror(rv);
|
<< nghttp2_strerror(rv);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
evbuffer_drain(input, rv);
|
|
||||||
return send();
|
if(evbuffer_drain(input, rv) != 0) {
|
||||||
|
SSLOG(FATAL, this) << "evbuffer_drain() faild";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Http2Session::on_write()
|
int Http2Session::on_write()
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ using namespace nghttp2;
|
|||||||
namespace shrpx {
|
namespace shrpx {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const size_t OUTBUF_MAX_THRES = 64*1024;
|
const size_t OUTBUF_MAX_THRES = 16*1024;
|
||||||
const size_t INBUF_MAX_THRES = 64*1024;
|
const size_t INBUF_MAX_THRES = 16*1024;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -61,25 +61,40 @@ int on_stream_close_callback
|
|||||||
<< " is being closed";
|
<< " is being closed";
|
||||||
}
|
}
|
||||||
auto downstream = upstream->find_downstream(stream_id);
|
auto downstream = upstream->find_downstream(stream_id);
|
||||||
if(downstream) {
|
|
||||||
|
if(!downstream) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if(downstream->get_request_state() == Downstream::CONNECT_FAIL) {
|
if(downstream->get_request_state() == Downstream::CONNECT_FAIL) {
|
||||||
upstream->remove_downstream(downstream);
|
upstream->remove_downstream(downstream);
|
||||||
|
|
||||||
delete downstream;
|
delete downstream;
|
||||||
} else {
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
downstream->set_request_state(Downstream::STREAM_CLOSED);
|
downstream->set_request_state(Downstream::STREAM_CLOSED);
|
||||||
|
|
||||||
if(downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
if(downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
||||||
// At this point, downstream response was read
|
// At this point, downstream response was read
|
||||||
if(!downstream->get_upgraded() &&
|
if(!downstream->get_upgraded() &&
|
||||||
!downstream->get_response_connection_close()) {
|
!downstream->get_response_connection_close()) {
|
||||||
// Keep-alive
|
// Keep-alive
|
||||||
auto dconn = downstream->get_downstream_connection();
|
auto dconn = downstream->get_downstream_connection();
|
||||||
|
|
||||||
if(dconn) {
|
if(dconn) {
|
||||||
dconn->detach_downstream(downstream);
|
dconn->detach_downstream(downstream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
upstream->remove_downstream(downstream);
|
upstream->remove_downstream(downstream);
|
||||||
|
|
||||||
delete downstream;
|
delete downstream;
|
||||||
} else {
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// At this point, downstream read may be paused.
|
// At this point, downstream read may be paused.
|
||||||
|
|
||||||
// If shrpx_downstream::push_request_headers() failed, the
|
// If shrpx_downstream::push_request_headers() failed, the
|
||||||
@@ -89,9 +104,7 @@ int on_stream_close_callback
|
|||||||
// How to test this case? Request sufficient large download
|
// How to test this case? Request sufficient large download
|
||||||
// and make client send RST_STREAM after it gets first DATA
|
// and make client send RST_STREAM after it gets first DATA
|
||||||
// frame chunk.
|
// frame chunk.
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -250,14 +263,11 @@ int on_begin_headers_callback(nghttp2_session *session,
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
int on_request_headers(Http2Upstream *upstream,
|
int on_request_headers(Http2Upstream *upstream,
|
||||||
|
Downstream *downstream,
|
||||||
nghttp2_session *session,
|
nghttp2_session *session,
|
||||||
const nghttp2_frame *frame)
|
const nghttp2_frame *frame)
|
||||||
{
|
{
|
||||||
int rv;
|
int rv;
|
||||||
auto downstream = upstream->find_downstream(frame->hd.stream_id);
|
|
||||||
if(!downstream) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
downstream->normalize_request_headers();
|
downstream->normalize_request_headers();
|
||||||
auto& nva = downstream->get_request_headers();
|
auto& nva = downstream->get_request_headers();
|
||||||
@@ -358,25 +368,38 @@ int on_frame_recv_callback
|
|||||||
verbose_on_frame_recv_callback(session, frame, user_data);
|
verbose_on_frame_recv_callback(session, frame, user_data);
|
||||||
}
|
}
|
||||||
auto upstream = static_cast<Http2Upstream*>(user_data);
|
auto upstream = static_cast<Http2Upstream*>(user_data);
|
||||||
|
|
||||||
switch(frame->hd.type) {
|
switch(frame->hd.type) {
|
||||||
case NGHTTP2_DATA: {
|
case NGHTTP2_DATA: {
|
||||||
|
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||||
auto downstream = upstream->find_downstream(frame->hd.stream_id);
|
auto downstream = upstream->find_downstream(frame->hd.stream_id);
|
||||||
if(!downstream) {
|
if(!downstream) {
|
||||||
break;
|
return 0;
|
||||||
}
|
}
|
||||||
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
|
||||||
downstream->end_upload_data();
|
downstream->end_upload_data();
|
||||||
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS: {
|
||||||
return on_request_headers(upstream, session, frame);
|
|
||||||
case NGHTTP2_PRIORITY: {
|
|
||||||
auto downstream = upstream->find_downstream(frame->hd.stream_id);
|
auto downstream = upstream->find_downstream(frame->hd.stream_id);
|
||||||
if(!downstream) {
|
if(!downstream) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
|
||||||
|
return on_request_headers(upstream, downstream, session, frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||||
|
downstream->end_upload_data();
|
||||||
|
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case NGHTTP2_PRIORITY: {
|
||||||
// TODO comment out for now
|
// TODO comment out for now
|
||||||
// rv = downstream->change_priority(frame->priority.pri);
|
// rv = downstream->change_priority(frame->priority.pri);
|
||||||
// if(rv != 0) {
|
// if(rv != 0) {
|
||||||
@@ -413,12 +436,16 @@ int on_data_chunk_recv_callback(nghttp2_session *session,
|
|||||||
{
|
{
|
||||||
auto upstream = static_cast<Http2Upstream*>(user_data);
|
auto upstream = static_cast<Http2Upstream*>(user_data);
|
||||||
auto downstream = upstream->find_downstream(stream_id);
|
auto downstream = upstream->find_downstream(stream_id);
|
||||||
if(downstream) {
|
|
||||||
|
if(!downstream) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if(downstream->push_upload_data_chunk(data, len) != 0) {
|
if(downstream->push_upload_data_chunk(data, len) != 0) {
|
||||||
upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -584,8 +611,17 @@ int Http2Upstream::on_read()
|
|||||||
ssize_t rv = 0;
|
ssize_t rv = 0;
|
||||||
auto bev = handler_->get_bev();
|
auto bev = handler_->get_bev();
|
||||||
auto input = bufferevent_get_input(bev);
|
auto input = bufferevent_get_input(bev);
|
||||||
auto inputlen = evbuffer_get_length(input);
|
|
||||||
auto mem = evbuffer_pullup(input, -1);
|
for(;;) {
|
||||||
|
auto inputlen = evbuffer_get_contiguous_space(input);
|
||||||
|
|
||||||
|
if(inputlen == 0) {
|
||||||
|
assert(evbuffer_get_length(input) == 0);
|
||||||
|
|
||||||
|
return send();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mem = evbuffer_pullup(input, inputlen);
|
||||||
|
|
||||||
rv = nghttp2_session_mem_recv(session_, mem, inputlen);
|
rv = nghttp2_session_mem_recv(session_, mem, inputlen);
|
||||||
if(rv < 0) {
|
if(rv < 0) {
|
||||||
@@ -593,8 +629,12 @@ int Http2Upstream::on_read()
|
|||||||
<< nghttp2_strerror(rv);
|
<< nghttp2_strerror(rv);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
evbuffer_drain(input, rv);
|
|
||||||
return send();
|
if(evbuffer_drain(input, rv) != 0) {
|
||||||
|
DCLOG(FATAL, this) << "evbuffer_drain() failed";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Http2Upstream::on_write()
|
int Http2Upstream::on_write()
|
||||||
@@ -668,6 +708,7 @@ void downstream_readcb(bufferevent *bev, void *ptr)
|
|||||||
auto dconn = static_cast<DownstreamConnection*>(ptr);
|
auto dconn = static_cast<DownstreamConnection*>(ptr);
|
||||||
auto downstream = dconn->get_downstream();
|
auto downstream = dconn->get_downstream();
|
||||||
auto upstream = static_cast<Http2Upstream*>(downstream->get_upstream());
|
auto upstream = static_cast<Http2Upstream*>(downstream->get_upstream());
|
||||||
|
|
||||||
if(downstream->get_request_state() == Downstream::STREAM_CLOSED) {
|
if(downstream->get_request_state() == Downstream::STREAM_CLOSED) {
|
||||||
// If upstream HTTP2 stream was closed, we just close downstream,
|
// If upstream HTTP2 stream was closed, we just close downstream,
|
||||||
// because there is no consumer now. Downstream connection is also
|
// because there is no consumer now. Downstream connection is also
|
||||||
@@ -684,11 +725,11 @@ void downstream_readcb(bufferevent *bev, void *ptr)
|
|||||||
// on_stream_close_callback.
|
// on_stream_close_callback.
|
||||||
upstream->rst_stream(downstream, infer_upstream_rst_stream_error_code
|
upstream->rst_stream(downstream, infer_upstream_rst_stream_error_code
|
||||||
(downstream->get_response_rst_stream_error_code()));
|
(downstream->get_response_rst_stream_error_code()));
|
||||||
downstream->set_downstream_connection(0);
|
downstream->set_downstream_connection(nullptr);
|
||||||
delete dconn;
|
delete dconn;
|
||||||
dconn = 0;
|
dconn = nullptr;
|
||||||
} else {
|
} else {
|
||||||
int rv = downstream->on_read();
|
auto rv = downstream->on_read();
|
||||||
if(rv != 0) {
|
if(rv != 0) {
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
DCLOG(INFO, dconn) << "HTTP parser failure";
|
DCLOG(INFO, dconn) << "HTTP parser failure";
|
||||||
@@ -705,9 +746,9 @@ void downstream_readcb(bufferevent *bev, void *ptr)
|
|||||||
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
||||||
// Clearly, we have to close downstream connection on http parser
|
// Clearly, we have to close downstream connection on http parser
|
||||||
// failure.
|
// failure.
|
||||||
downstream->set_downstream_connection(0);
|
downstream->set_downstream_connection(nullptr);
|
||||||
delete dconn;
|
delete dconn;
|
||||||
dconn = 0;
|
dconn = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(upstream->send() != 0) {
|
if(upstream->send() != 0) {
|
||||||
@@ -742,14 +783,18 @@ void downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
|||||||
DCLOG(INFO, dconn) << "Connection established. stream_id="
|
DCLOG(INFO, dconn) << "Connection established. stream_id="
|
||||||
<< downstream->get_stream_id();
|
<< downstream->get_stream_id();
|
||||||
}
|
}
|
||||||
int fd = bufferevent_getfd(bev);
|
auto fd = bufferevent_getfd(bev);
|
||||||
int val = 1;
|
int val = 1;
|
||||||
if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
|
if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
|
||||||
reinterpret_cast<char *>(&val), sizeof(val)) == -1) {
|
reinterpret_cast<char*>(&val), sizeof(val)) == -1) {
|
||||||
DCLOG(WARNING, dconn) << "Setting option TCP_NODELAY failed: errno="
|
DCLOG(WARNING, dconn) << "Setting option TCP_NODELAY failed: errno="
|
||||||
<< errno;
|
<< errno;
|
||||||
}
|
}
|
||||||
} else if(events & BEV_EVENT_EOF) {
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(events & BEV_EVENT_EOF) {
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
DCLOG(INFO, dconn) << "EOF. stream_id=" << downstream->get_stream_id();
|
DCLOG(INFO, dconn) << "EOF. stream_id=" << downstream->get_stream_id();
|
||||||
}
|
}
|
||||||
@@ -758,12 +803,15 @@ void downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
|||||||
// the first place. We can delete downstream.
|
// the first place. We can delete downstream.
|
||||||
upstream->remove_downstream(downstream);
|
upstream->remove_downstream(downstream);
|
||||||
delete downstream;
|
delete downstream;
|
||||||
} else {
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Delete downstream connection. If we don't delete it here, it
|
// Delete downstream connection. If we don't delete it here, it
|
||||||
// will be pooled in on_stream_close_callback.
|
// will be pooled in on_stream_close_callback.
|
||||||
downstream->set_downstream_connection(0);
|
downstream->set_downstream_connection(nullptr);
|
||||||
delete dconn;
|
delete dconn;
|
||||||
dconn = 0;
|
dconn = nullptr;
|
||||||
// downstream wil be deleted in on_stream_close_callback.
|
// downstream wil be deleted in on_stream_close_callback.
|
||||||
if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
||||||
// Server may indicate the end of the request by EOF
|
// Server may indicate the end of the request by EOF
|
||||||
@@ -791,8 +839,10 @@ void downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// At this point, downstream may be deleted.
|
// At this point, downstream may be deleted.
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} else if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {
|
|
||||||
|
if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
if(events & BEV_EVENT_ERROR) {
|
if(events & BEV_EVENT_ERROR) {
|
||||||
DCLOG(INFO, dconn) << "Downstream network error: "
|
DCLOG(INFO, dconn) << "Downstream network error: "
|
||||||
@@ -805,15 +855,20 @@ void downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
|||||||
DCLOG(INFO, dconn) << "Note: this is tunnel connection";
|
DCLOG(INFO, dconn) << "Note: this is tunnel connection";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(downstream->get_request_state() == Downstream::STREAM_CLOSED) {
|
if(downstream->get_request_state() == Downstream::STREAM_CLOSED) {
|
||||||
upstream->remove_downstream(downstream);
|
upstream->remove_downstream(downstream);
|
||||||
delete downstream;
|
delete downstream;
|
||||||
} else {
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Delete downstream connection. If we don't delete it here, it
|
// Delete downstream connection. If we don't delete it here, it
|
||||||
// will be pooled in on_stream_close_callback.
|
// will be pooled in on_stream_close_callback.
|
||||||
downstream->set_downstream_connection(0);
|
downstream->set_downstream_connection(nullptr);
|
||||||
delete dconn;
|
delete dconn;
|
||||||
dconn = 0;
|
dconn = nullptr;
|
||||||
|
|
||||||
if(downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
if(downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
||||||
// For SSL tunneling, we issue RST_STREAM. For other types of
|
// For SSL tunneling, we issue RST_STREAM. For other types of
|
||||||
// stream, we don't have to do anything since response was
|
// stream, we don't have to do anything since response was
|
||||||
@@ -843,7 +898,7 @@ void downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// At this point, downstream may be deleted.
|
// At this point, downstream may be deleted.
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ int HttpDownstreamConnection::attach_downstream(Downstream *downstream)
|
|||||||
http_parser_init(&response_htp_, HTTP_RESPONSE);
|
http_parser_init(&response_htp_, HTTP_RESPONSE);
|
||||||
response_htp_.data = downstream_;
|
response_htp_.data = downstream_;
|
||||||
|
|
||||||
bufferevent_setwatermark(bev_, EV_READ, 0, SHRPX_READ_WARTER_MARK);
|
bufferevent_setwatermark(bev_, EV_READ, 0, SHRPX_READ_WATERMARK);
|
||||||
bufferevent_enable(bev_, EV_READ);
|
bufferevent_enable(bev_, EV_READ);
|
||||||
bufferevent_setcb(bev_,
|
bufferevent_setcb(bev_,
|
||||||
upstream->get_downstream_readcb(),
|
upstream->get_downstream_readcb(),
|
||||||
@@ -502,10 +502,20 @@ http_parser_settings htp_hooks = {
|
|||||||
int HttpDownstreamConnection::on_read()
|
int HttpDownstreamConnection::on_read()
|
||||||
{
|
{
|
||||||
auto input = bufferevent_get_input(bev_);
|
auto input = bufferevent_get_input(bev_);
|
||||||
size_t inputlen = evbuffer_get_length(input);
|
|
||||||
auto mem = evbuffer_pullup(input, -1);
|
|
||||||
if(downstream_->get_upgraded()) {
|
if(downstream_->get_upgraded()) {
|
||||||
// For upgraded connection, just pass data to the upstream.
|
// For upgraded connection, just pass data to the upstream.
|
||||||
|
for(;;) {
|
||||||
|
auto inputlen = evbuffer_get_contiguous_space(input);
|
||||||
|
|
||||||
|
if(inputlen == 0) {
|
||||||
|
assert(evbuffer_get_length(input) == 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mem = evbuffer_pullup(input, inputlen);
|
||||||
|
|
||||||
int rv;
|
int rv;
|
||||||
rv = downstream_->get_upstream()->on_downstream_body
|
rv = downstream_->get_upstream()->on_downstream_body
|
||||||
(downstream_, reinterpret_cast<const uint8_t*>(mem), inputlen, true);
|
(downstream_, reinterpret_cast<const uint8_t*>(mem), inputlen, true);
|
||||||
@@ -516,9 +526,21 @@ int HttpDownstreamConnection::on_read()
|
|||||||
DCLOG(FATAL, this) << "evbuffer_drain() failed";
|
DCLOG(FATAL, this) << "evbuffer_drain() failed";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
auto inputlen = evbuffer_get_contiguous_space(input);
|
||||||
|
|
||||||
|
if(inputlen == 0) {
|
||||||
|
assert(evbuffer_get_length(input) == 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
size_t nread = http_parser_execute(&response_htp_, &htp_hooks,
|
|
||||||
|
auto mem = evbuffer_pullup(input, inputlen);
|
||||||
|
|
||||||
|
auto nread = http_parser_execute(&response_htp_, &htp_hooks,
|
||||||
reinterpret_cast<const char*>(mem),
|
reinterpret_cast<const char*>(mem),
|
||||||
inputlen);
|
inputlen);
|
||||||
|
|
||||||
@@ -526,17 +548,19 @@ int HttpDownstreamConnection::on_read()
|
|||||||
DCLOG(FATAL, this) << "evbuffer_drain() failed";
|
DCLOG(FATAL, this) << "evbuffer_drain() failed";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto htperr = HTTP_PARSER_ERRNO(&response_htp_);
|
auto htperr = HTTP_PARSER_ERRNO(&response_htp_);
|
||||||
if(htperr == HPE_OK) {
|
|
||||||
return 0;
|
if(htperr != HPE_OK) {
|
||||||
} else {
|
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
DCLOG(INFO, this) << "HTTP parser failure: "
|
DCLOG(INFO, this) << "HTTP parser failure: "
|
||||||
<< "(" << http_errno_name(htperr) << ") "
|
<< "(" << http_errno_name(htperr) << ") "
|
||||||
<< http_errno_description(htperr);
|
<< http_errno_description(htperr);
|
||||||
}
|
}
|
||||||
|
|
||||||
return SHRPX_ERR_HTTP_PARSE;
|
return SHRPX_ERR_HTTP_PARSE;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int HttpDownstreamConnection::on_write()
|
int HttpDownstreamConnection::on_write()
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ using namespace nghttp2;
|
|||||||
namespace shrpx {
|
namespace shrpx {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const size_t OUTBUF_MAX_THRES = 64*1024;
|
const size_t OUTBUF_MAX_THRES = 16*1024;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
HttpsUpstream::HttpsUpstream(ClientHandler *handler)
|
HttpsUpstream::HttpsUpstream(ClientHandler *handler)
|
||||||
@@ -193,19 +193,23 @@ int htp_hdrs_completecb(http_parser *htp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
rv = dconn->attach_downstream(downstream);
|
rv = dconn->attach_downstream(downstream);
|
||||||
|
|
||||||
if(rv != 0) {
|
if(rv != 0) {
|
||||||
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
||||||
downstream->set_downstream_connection(0);
|
downstream->set_downstream_connection(nullptr);
|
||||||
delete dconn;
|
delete dconn;
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
}
|
||||||
|
|
||||||
rv = downstream->push_request_headers();
|
rv = downstream->push_request_headers();
|
||||||
|
|
||||||
if(rv != 0) {
|
if(rv != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
downstream->set_request_state(Downstream::HEADER_COMPLETE);
|
downstream->set_request_state(Downstream::HEADER_COMPLETE);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@@ -264,49 +268,75 @@ int HttpsUpstream::on_read()
|
|||||||
{
|
{
|
||||||
auto bev = handler_->get_bev();
|
auto bev = handler_->get_bev();
|
||||||
auto input = bufferevent_get_input(bev);
|
auto input = bufferevent_get_input(bev);
|
||||||
size_t inputlen = evbuffer_get_length(input);
|
auto downstream = get_downstream();
|
||||||
auto mem = evbuffer_pullup(input, -1);
|
|
||||||
|
// downstream can be nullptr here, because it is initialized in the
|
||||||
|
// callback chain called by http_parser_execute()
|
||||||
|
if(downstream && downstream->get_upgraded()) {
|
||||||
|
for(;;) {
|
||||||
|
auto inputlen = evbuffer_get_contiguous_space(input);
|
||||||
|
|
||||||
if(inputlen == 0) {
|
if(inputlen == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
auto downstream = get_downstream();
|
|
||||||
// downstream can be nullptr here, because it is initialized in the
|
auto mem = evbuffer_pullup(input, inputlen);
|
||||||
// callback chain called by http_parser_execute()
|
|
||||||
if(downstream && downstream->get_upgraded()) {
|
auto rv = downstream->push_upload_data_chunk
|
||||||
int rv = downstream->push_upload_data_chunk
|
|
||||||
(reinterpret_cast<const uint8_t*>(mem), inputlen);
|
(reinterpret_cast<const uint8_t*>(mem), inputlen);
|
||||||
|
|
||||||
if(rv != 0) {
|
if(rv != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(evbuffer_drain(input, inputlen) != 0) {
|
if(evbuffer_drain(input, inputlen) != 0) {
|
||||||
ULOG(FATAL, this) << "evbuffer_drain() failed";
|
ULOG(FATAL, this) << "evbuffer_drain() failed";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(downstream->get_output_buffer_full()) {
|
if(downstream->get_output_buffer_full()) {
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
ULOG(INFO, this) << "Downstream output buffer is full";
|
ULOG(INFO, this) << "Downstream output buffer is full";
|
||||||
}
|
}
|
||||||
pause_read(SHRPX_NO_BUFFER);
|
pause_read(SHRPX_NO_BUFFER);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
auto inputlen = evbuffer_get_contiguous_space(input);
|
||||||
|
|
||||||
|
if(inputlen == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t nread = http_parser_execute(&htp_, &htp_hooks,
|
auto mem = evbuffer_pullup(input, inputlen);
|
||||||
|
|
||||||
|
auto nread = http_parser_execute(&htp_, &htp_hooks,
|
||||||
reinterpret_cast<const char*>(mem),
|
reinterpret_cast<const char*>(mem),
|
||||||
inputlen);
|
inputlen);
|
||||||
|
|
||||||
if(evbuffer_drain(input, nread) != 0) {
|
if(evbuffer_drain(input, nread) != 0) {
|
||||||
ULOG(FATAL, this) << "evbuffer_drain() failed";
|
ULOG(FATAL, this) << "evbuffer_drain() failed";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Well, actually header length + some body bytes
|
// Well, actually header length + some body bytes
|
||||||
current_header_length_ += nread;
|
current_header_length_ += nread;
|
||||||
|
|
||||||
// Get downstream again because it may be initialized in http parser
|
// Get downstream again because it may be initialized in http parser
|
||||||
// execution
|
// execution
|
||||||
downstream = get_downstream();
|
downstream = get_downstream();
|
||||||
|
|
||||||
auto handler = get_client_handler();
|
auto handler = get_client_handler();
|
||||||
auto htperr = HTTP_PARSER_ERRNO(&htp_);
|
auto htperr = HTTP_PARSER_ERRNO(&htp_);
|
||||||
|
|
||||||
if(htperr == HPE_PAUSED) {
|
if(htperr == HPE_PAUSED) {
|
||||||
|
|
||||||
|
assert(downstream);
|
||||||
|
|
||||||
if(downstream->get_request_state() == Downstream::CONNECT_FAIL) {
|
if(downstream->get_request_state() == Downstream::CONNECT_FAIL) {
|
||||||
handler->set_should_close_after_write(true);
|
handler->set_should_close_after_write(true);
|
||||||
// Following paues_read is needed to avoid reading next data.
|
// Following paues_read is needed to avoid reading next data.
|
||||||
@@ -315,46 +345,62 @@ int HttpsUpstream::on_read()
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
// Downstream gets deleted after response body is read.
|
// Downstream gets deleted after response body is read.
|
||||||
} else {
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
assert(downstream->get_request_state() == Downstream::MSG_COMPLETE);
|
assert(downstream->get_request_state() == Downstream::MSG_COMPLETE);
|
||||||
if(downstream->get_downstream_connection() == 0) {
|
|
||||||
|
if(downstream->get_downstream_connection() == nullptr) {
|
||||||
// Error response has already be sent
|
// Error response has already be sent
|
||||||
assert(downstream->get_response_state() == Downstream::MSG_COMPLETE);
|
assert(downstream->get_response_state() == Downstream::MSG_COMPLETE);
|
||||||
delete_downstream();
|
delete_downstream();
|
||||||
} else {
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if(handler->get_http2_upgrade_allowed() &&
|
if(handler->get_http2_upgrade_allowed() &&
|
||||||
downstream->http2_upgrade_request()) {
|
downstream->http2_upgrade_request()) {
|
||||||
|
|
||||||
if(handler->perform_http2_upgrade(this) != 0) {
|
if(handler->perform_http2_upgrade(this) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pause_read(SHRPX_MSG_BLOCK);
|
pause_read(SHRPX_MSG_BLOCK);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else if(htperr == HPE_OK) {
|
if(htperr != HPE_OK) {
|
||||||
// downstream can be NULL here.
|
|
||||||
if(downstream) {
|
|
||||||
if(downstream->get_output_buffer_full()) {
|
|
||||||
if(LOG_ENABLED(INFO)) {
|
|
||||||
ULOG(INFO, this) << "Downstream output buffer is full";
|
|
||||||
}
|
|
||||||
pause_read(SHRPX_NO_BUFFER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
ULOG(INFO, this) << "HTTP parse failure: "
|
ULOG(INFO, this) << "HTTP parse failure: "
|
||||||
<< "(" << http_errno_name(htperr) << ") "
|
<< "(" << http_errno_name(htperr) << ") "
|
||||||
<< http_errno_description(htperr);
|
<< http_errno_description(htperr);
|
||||||
}
|
}
|
||||||
|
|
||||||
handler->set_should_close_after_write(true);
|
handler->set_should_close_after_write(true);
|
||||||
pause_read(SHRPX_MSG_BLOCK);
|
pause_read(SHRPX_MSG_BLOCK);
|
||||||
|
|
||||||
if(error_reply(400) != 0) {
|
if(error_reply(400) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// downstream can be NULL here.
|
||||||
|
if(downstream && downstream->get_output_buffer_full()) {
|
||||||
|
if(LOG_ENABLED(INFO)) {
|
||||||
|
ULOG(INFO, this) << "Downstream output buffer is full";
|
||||||
|
}
|
||||||
|
|
||||||
|
pause_read(SHRPX_NO_BUFFER);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int HttpsUpstream::on_write()
|
int HttpsUpstream::on_write()
|
||||||
@@ -389,9 +435,9 @@ int HttpsUpstream::resume_read(IOCtrlReason reason, Downstream *downstream)
|
|||||||
// are not notified by readcb until new data arrive.
|
// are not notified by readcb until new data arrive.
|
||||||
http_parser_pause(&htp_, 0);
|
http_parser_pause(&htp_, 0);
|
||||||
return on_read();
|
return on_read();
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -401,22 +447,67 @@ void https_downstream_readcb(bufferevent *bev, void *ptr)
|
|||||||
auto downstream = dconn->get_downstream();
|
auto downstream = dconn->get_downstream();
|
||||||
auto upstream = static_cast<HttpsUpstream*>(downstream->get_upstream());
|
auto upstream = static_cast<HttpsUpstream*>(downstream->get_upstream());
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
rv = downstream->on_read();
|
rv = downstream->on_read();
|
||||||
|
|
||||||
if(downstream->get_response_state() == Downstream::MSG_RESET) {
|
if(downstream->get_response_state() == Downstream::MSG_RESET) {
|
||||||
delete upstream->get_client_handler();
|
delete upstream->get_client_handler();
|
||||||
} else if(rv == 0) {
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rv != 0) {
|
||||||
|
if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
||||||
|
// We already sent HTTP response headers to upstream
|
||||||
|
// client. Just close the upstream connection.
|
||||||
|
delete upstream->get_client_handler();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We did not sent any HTTP response, so sent error
|
||||||
|
// response. Cannot reuse downstream connection in this case.
|
||||||
|
if(upstream->error_reply(502) != 0) {
|
||||||
|
delete upstream->get_client_handler();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(downstream->get_request_state() == Downstream::MSG_COMPLETE) {
|
||||||
|
upstream->delete_downstream();
|
||||||
|
|
||||||
|
// Process next HTTP request
|
||||||
|
if(upstream->resume_read(SHRPX_MSG_BLOCK, 0) == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto handler = upstream->get_client_handler();
|
auto handler = upstream->get_client_handler();
|
||||||
|
|
||||||
if(downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
if(downstream->get_response_state() != Downstream::MSG_COMPLETE) {
|
||||||
|
if(handler->get_outbuf_length() >= OUTBUF_MAX_THRES) {
|
||||||
|
downstream->pause_read(SHRPX_NO_BUFFER);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if(downstream->get_response_connection_close()) {
|
if(downstream->get_response_connection_close()) {
|
||||||
// Connection close
|
// Connection close
|
||||||
downstream->set_downstream_connection(0);
|
downstream->set_downstream_connection(nullptr);
|
||||||
|
|
||||||
delete dconn;
|
delete dconn;
|
||||||
dconn = 0;
|
|
||||||
|
dconn = nullptr;
|
||||||
} else {
|
} else {
|
||||||
// Keep-alive
|
// Keep-alive
|
||||||
dconn->detach_downstream(downstream);
|
dconn->detach_downstream(downstream);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(downstream->get_request_state() == Downstream::MSG_COMPLETE) {
|
if(downstream->get_request_state() == Downstream::MSG_COMPLETE) {
|
||||||
if(handler->get_should_close_after_write() &&
|
if(handler->get_should_close_after_write() &&
|
||||||
handler->get_outbuf_length() == 0) {
|
handler->get_outbuf_length() == 0) {
|
||||||
@@ -424,15 +515,21 @@ void https_downstream_readcb(bufferevent *bev, void *ptr)
|
|||||||
// the peer, we cannot use writecb for ClientHandler. In
|
// the peer, we cannot use writecb for ClientHandler. In
|
||||||
// this case, we just delete handler here.
|
// this case, we just delete handler here.
|
||||||
delete handler;
|
delete handler;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} else {
|
}
|
||||||
|
|
||||||
upstream->delete_downstream();
|
upstream->delete_downstream();
|
||||||
|
|
||||||
// Process next HTTP request
|
// Process next HTTP request
|
||||||
if(upstream->resume_read(SHRPX_MSG_BLOCK, 0) == -1) {
|
if(upstream->resume_read(SHRPX_MSG_BLOCK, 0) == -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} else if(downstream->get_upgraded()) {
|
|
||||||
|
if(downstream->get_upgraded()) {
|
||||||
// This path is effectively only taken for HTTP2 downstream
|
// This path is effectively only taken for HTTP2 downstream
|
||||||
// because only HTTP2 downstream sets response_state to
|
// because only HTTP2 downstream sets response_state to
|
||||||
// MSG_COMPLETE and this function. For HTTP downstream, EOF
|
// MSG_COMPLETE and this function. For HTTP downstream, EOF
|
||||||
@@ -444,38 +541,16 @@ void https_downstream_readcb(bufferevent *bev, void *ptr)
|
|||||||
// For tunneled connection, if there is no pending data,
|
// For tunneled connection, if there is no pending data,
|
||||||
// delete handler because on_write will not be called.
|
// delete handler because on_write will not be called.
|
||||||
delete handler;
|
delete handler;
|
||||||
} else {
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
DLOG(INFO, downstream) << "Tunneled connection has pending data";
|
DLOG(INFO, downstream) << "Tunneled connection has pending data";
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(handler->get_outbuf_length() >= OUTBUF_MAX_THRES) {
|
|
||||||
downstream->pause_read(SHRPX_NO_BUFFER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
|
||||||
// We already sent HTTP response headers to upstream
|
|
||||||
// client. Just close the upstream connection.
|
|
||||||
delete upstream->get_client_handler();
|
|
||||||
} else {
|
|
||||||
// We did not sent any HTTP response, so sent error
|
|
||||||
// response. Cannot reuse downstream connection in this case.
|
|
||||||
if(upstream->error_reply(502) != 0) {
|
|
||||||
delete upstream->get_client_handler();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(downstream->get_request_state() == Downstream::MSG_COMPLETE) {
|
|
||||||
upstream->delete_downstream();
|
|
||||||
// Process next HTTP request
|
|
||||||
if(upstream->resume_read(SHRPX_MSG_BLOCK, 0) == -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@@ -503,7 +578,11 @@ void https_downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
|||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
DCLOG(INFO, dconn) << "Connection established";
|
DCLOG(INFO, dconn) << "Connection established";
|
||||||
}
|
}
|
||||||
} else if(events & BEV_EVENT_EOF) {
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(events & BEV_EVENT_EOF) {
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
DCLOG(INFO, dconn) << "EOF";
|
DCLOG(INFO, dconn) << "EOF";
|
||||||
}
|
}
|
||||||
@@ -525,9 +604,7 @@ void https_downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
|||||||
delete handler;
|
delete handler;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if(downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
} else if(downstream->get_response_state() != Downstream::MSG_COMPLETE) {
|
||||||
// Nothing to do
|
|
||||||
} else {
|
|
||||||
// error
|
// error
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
DCLOG(INFO, dconn) << "Treated as error";
|
DCLOG(INFO, dconn) << "Treated as error";
|
||||||
@@ -543,7 +620,11 @@ void https_downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
if(events & BEV_EVENT_ERROR) {
|
if(events & BEV_EVENT_ERROR) {
|
||||||
DCLOG(INFO, dconn) << "Network error";
|
DCLOG(INFO, dconn) << "Network error";
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ using namespace nghttp2;
|
|||||||
namespace shrpx {
|
namespace shrpx {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const size_t OUTBUF_MAX_THRES = 64*1024;
|
const size_t OUTBUF_MAX_THRES = 16*1024;
|
||||||
const size_t INBUF_MAX_THRES = 64*1024;
|
const size_t INBUF_MAX_THRES = 16*1024;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -103,11 +103,16 @@ void on_stream_close_callback
|
|||||||
<< " is being closed";
|
<< " is being closed";
|
||||||
}
|
}
|
||||||
auto downstream = upstream->find_downstream(stream_id);
|
auto downstream = upstream->find_downstream(stream_id);
|
||||||
if(downstream) {
|
if(!downstream) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(downstream->get_request_state() == Downstream::CONNECT_FAIL) {
|
if(downstream->get_request_state() == Downstream::CONNECT_FAIL) {
|
||||||
upstream->remove_downstream(downstream);
|
upstream->remove_downstream(downstream);
|
||||||
delete downstream;
|
delete downstream;
|
||||||
} else {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
downstream->set_request_state(Downstream::STREAM_CLOSED);
|
downstream->set_request_state(Downstream::STREAM_CLOSED);
|
||||||
if(downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
if(downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
||||||
// At this point, downstream response was read
|
// At this point, downstream response was read
|
||||||
@@ -121,7 +126,9 @@ void on_stream_close_callback
|
|||||||
}
|
}
|
||||||
upstream->remove_downstream(downstream);
|
upstream->remove_downstream(downstream);
|
||||||
delete downstream;
|
delete downstream;
|
||||||
} else {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// At this point, downstream read may be paused.
|
// At this point, downstream read may be paused.
|
||||||
|
|
||||||
// If shrpx_downstream::push_request_headers() failed, the
|
// If shrpx_downstream::push_request_headers() failed, the
|
||||||
@@ -131,9 +138,6 @@ void on_stream_close_callback
|
|||||||
// How to test this case? Request sufficient large download
|
// How to test this case? Request sufficient large download
|
||||||
// and make client send RST_STREAM after it gets first DATA
|
// and make client send RST_STREAM after it gets first DATA
|
||||||
// frame chunk.
|
// frame chunk.
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@@ -245,12 +249,20 @@ void on_data_chunk_recv_callback(spdylay_session *session,
|
|||||||
{
|
{
|
||||||
auto upstream = static_cast<SpdyUpstream*>(user_data);
|
auto upstream = static_cast<SpdyUpstream*>(user_data);
|
||||||
auto downstream = upstream->find_downstream(stream_id);
|
auto downstream = upstream->find_downstream(stream_id);
|
||||||
if(downstream) {
|
|
||||||
|
if(!downstream) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(downstream->push_upload_data_chunk(data, len) != 0) {
|
if(downstream->push_upload_data_chunk(data, len) != 0) {
|
||||||
upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(upstream->get_flow_control()) {
|
|
||||||
|
if(!upstream->get_flow_control()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// If connection-level window control is not enabled (e.g,
|
// If connection-level window control is not enabled (e.g,
|
||||||
// spdy/3), spdylay_session_get_recv_data_length() is always
|
// spdy/3), spdylay_session_get_recv_data_length() is always
|
||||||
// returns 0.
|
// returns 0.
|
||||||
@@ -281,8 +293,6 @@ void on_data_chunk_recv_callback(spdylay_session *session,
|
|||||||
upstream->rst_stream(downstream, SPDYLAY_FLOW_CONTROL_ERROR);
|
upstream->rst_stream(downstream, SPDYLAY_FLOW_CONTROL_ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@@ -520,11 +530,11 @@ void spdy_downstream_readcb(bufferevent *bev, void *ptr)
|
|||||||
// on_stream_close_callback.
|
// on_stream_close_callback.
|
||||||
upstream->rst_stream(downstream, infer_upstream_rst_stream_status_code
|
upstream->rst_stream(downstream, infer_upstream_rst_stream_status_code
|
||||||
(downstream->get_response_rst_stream_error_code()));
|
(downstream->get_response_rst_stream_error_code()));
|
||||||
downstream->set_downstream_connection(0);
|
downstream->set_downstream_connection(nullptr);
|
||||||
delete dconn;
|
delete dconn;
|
||||||
dconn = 0;
|
dconn = nullptr;
|
||||||
} else {
|
} else {
|
||||||
int rv = downstream->on_read();
|
auto rv = downstream->on_read();
|
||||||
if(rv != 0) {
|
if(rv != 0) {
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
DCLOG(INFO, dconn) << "HTTP parser failure";
|
DCLOG(INFO, dconn) << "HTTP parser failure";
|
||||||
@@ -541,9 +551,9 @@ void spdy_downstream_readcb(bufferevent *bev, void *ptr)
|
|||||||
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
||||||
// Clearly, we have to close downstream connection on http parser
|
// Clearly, we have to close downstream connection on http parser
|
||||||
// failure.
|
// failure.
|
||||||
downstream->set_downstream_connection(0);
|
downstream->set_downstream_connection(nullptr);
|
||||||
delete dconn;
|
delete dconn;
|
||||||
dconn = 0;
|
dconn = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(upstream->send() != 0) {
|
if(upstream->send() != 0) {
|
||||||
@@ -573,6 +583,7 @@ void spdy_downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
|||||||
auto dconn = static_cast<DownstreamConnection*>(ptr);
|
auto dconn = static_cast<DownstreamConnection*>(ptr);
|
||||||
auto downstream = dconn->get_downstream();
|
auto downstream = dconn->get_downstream();
|
||||||
auto upstream = static_cast<SpdyUpstream*>(downstream->get_upstream());
|
auto upstream = static_cast<SpdyUpstream*>(downstream->get_upstream());
|
||||||
|
|
||||||
if(events & BEV_EVENT_CONNECTED) {
|
if(events & BEV_EVENT_CONNECTED) {
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
DCLOG(INFO, dconn) << "Connection established. stream_id="
|
DCLOG(INFO, dconn) << "Connection established. stream_id="
|
||||||
@@ -585,7 +596,10 @@ void spdy_downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
|||||||
DCLOG(WARNING, dconn) << "Setting option TCP_NODELAY failed: errno="
|
DCLOG(WARNING, dconn) << "Setting option TCP_NODELAY failed: errno="
|
||||||
<< errno;
|
<< errno;
|
||||||
}
|
}
|
||||||
} else if(events & BEV_EVENT_EOF) {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(events & BEV_EVENT_EOF) {
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
DCLOG(INFO, dconn) << "EOF. stream_id=" << downstream->get_stream_id();
|
DCLOG(INFO, dconn) << "EOF. stream_id=" << downstream->get_stream_id();
|
||||||
}
|
}
|
||||||
@@ -594,12 +608,14 @@ void spdy_downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
|||||||
// the first place. We can delete downstream.
|
// the first place. We can delete downstream.
|
||||||
upstream->remove_downstream(downstream);
|
upstream->remove_downstream(downstream);
|
||||||
delete downstream;
|
delete downstream;
|
||||||
} else {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Delete downstream connection. If we don't delete it here, it
|
// Delete downstream connection. If we don't delete it here, it
|
||||||
// will be pooled in on_stream_close_callback.
|
// will be pooled in on_stream_close_callback.
|
||||||
downstream->set_downstream_connection(0);
|
downstream->set_downstream_connection(nullptr);
|
||||||
delete dconn;
|
delete dconn;
|
||||||
dconn = 0;
|
dconn = nullptr;
|
||||||
// downstream wil be deleted in on_stream_close_callback.
|
// downstream wil be deleted in on_stream_close_callback.
|
||||||
if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
||||||
// Server may indicate the end of the request by EOF
|
// Server may indicate the end of the request by EOF
|
||||||
@@ -627,8 +643,11 @@ void spdy_downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// At this point, downstream may be deleted.
|
// At this point, downstream may be deleted.
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} else if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {
|
|
||||||
|
if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
if(events & BEV_EVENT_ERROR) {
|
if(events & BEV_EVENT_ERROR) {
|
||||||
DCLOG(INFO, dconn) << "Downstream network error: "
|
DCLOG(INFO, dconn) << "Downstream network error: "
|
||||||
@@ -644,12 +663,15 @@ void spdy_downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
|||||||
if(downstream->get_request_state() == Downstream::STREAM_CLOSED) {
|
if(downstream->get_request_state() == Downstream::STREAM_CLOSED) {
|
||||||
upstream->remove_downstream(downstream);
|
upstream->remove_downstream(downstream);
|
||||||
delete downstream;
|
delete downstream;
|
||||||
} else {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Delete downstream connection. If we don't delete it here, it
|
// Delete downstream connection. If we don't delete it here, it
|
||||||
// will be pooled in on_stream_close_callback.
|
// will be pooled in on_stream_close_callback.
|
||||||
downstream->set_downstream_connection(0);
|
downstream->set_downstream_connection(nullptr);
|
||||||
delete dconn;
|
delete dconn;
|
||||||
dconn = 0;
|
dconn = nullptr;
|
||||||
|
|
||||||
if(downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
if(downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
||||||
// For SSL tunneling, we issue RST_STREAM. For other types of
|
// For SSL tunneling, we issue RST_STREAM. For other types of
|
||||||
// stream, we don't have to do anything since response was
|
// stream, we don't have to do anything since response was
|
||||||
@@ -679,7 +701,7 @@ void spdy_downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// At this point, downstream may be deleted.
|
// At this point, downstream may be deleted.
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -156,24 +156,6 @@ void info_callback(const SSL *ssl, int where, int ret)
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
namespace {
|
|
||||||
// Returns true if ALPN identifier list |in| of length |inlen|
|
|
||||||
// contains http/1.1.
|
|
||||||
bool check_http1_available_in_alpn_list(const unsigned char *in,
|
|
||||||
unsigned int inlen)
|
|
||||||
{
|
|
||||||
for(unsigned int i = 0; i < inlen; i += 1 + in[i]) {
|
|
||||||
if(in[i] == 8 && i + 1 + in[i] <= inlen &&
|
|
||||||
memcmp("http/1.1", &in[i + 1], in[i]) == 0) {
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
int alpn_select_proto_cb(SSL* ssl,
|
int alpn_select_proto_cb(SSL* ssl,
|
||||||
const unsigned char **out,
|
const unsigned char **out,
|
||||||
@@ -181,38 +163,44 @@ int alpn_select_proto_cb(SSL* ssl,
|
|||||||
const unsigned char *in, unsigned int inlen,
|
const unsigned char *in, unsigned int inlen,
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
int rv;
|
// We assume that get_config()->npn_list contains ALPN protocol
|
||||||
|
// identifier sorted by preference order. So we just break when we
|
||||||
|
// found the first overlap.
|
||||||
|
for(auto needle_ptr = get_config()->npn_list,
|
||||||
|
end_needle_ptr = needle_ptr + get_config()->npn_list_len;
|
||||||
|
needle_ptr < end_needle_ptr; ++needle_ptr) {
|
||||||
|
|
||||||
if(check_http2_requirement(ssl)) {
|
auto target_proto_id = *needle_ptr;
|
||||||
|
auto target_proto_len =
|
||||||
|
strlen(reinterpret_cast<const char*>(target_proto_id));
|
||||||
|
|
||||||
rv = nghttp2_select_next_protocol
|
if(target_proto_len == NGHTTP2_PROTO_VERSION_ID_LEN &&
|
||||||
(const_cast<unsigned char**>(out), outlen, in, inlen);
|
memcmp(target_proto_id, NGHTTP2_PROTO_VERSION_ID,
|
||||||
|
NGHTTP2_PROTO_VERSION_ID_LEN) == 0) {
|
||||||
|
|
||||||
|
if(!check_http2_requirement(ssl)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto p = in, end = in + inlen; p < end;) {
|
||||||
|
auto proto_id = p + 1;
|
||||||
|
auto proto_len = *p;
|
||||||
|
|
||||||
|
if(proto_id + proto_len <= end &&
|
||||||
|
util::streq(target_proto_id, target_proto_len, proto_id, proto_len)) {
|
||||||
|
|
||||||
|
*out = reinterpret_cast<const unsigned char*>(proto_id);
|
||||||
|
*outlen = proto_len;
|
||||||
|
|
||||||
if(rv == 1) {
|
|
||||||
// HTTP/2 was selected
|
|
||||||
return SSL_TLSEXT_ERR_OK;
|
return SSL_TLSEXT_ERR_OK;
|
||||||
}
|
}
|
||||||
} else if(check_http1_available_in_alpn_list(in, inlen)) {
|
|
||||||
*out = reinterpret_cast<const unsigned char*>("http/1.1");
|
|
||||||
*outlen = strlen("http/1.1");
|
|
||||||
|
|
||||||
rv = 0;
|
p += 1 + proto_len;
|
||||||
} else {
|
}
|
||||||
rv = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_SPDYLAY
|
|
||||||
rv = spdylay_select_next_protocol
|
|
||||||
(const_cast<unsigned char**>(out), outlen, in, inlen);
|
|
||||||
#endif // HAVE_SPDYLAY
|
|
||||||
|
|
||||||
if(rv == -1) {
|
|
||||||
// No selection was made
|
|
||||||
return SSL_TLSEXT_ERR_NOACK;
|
return SSL_TLSEXT_ERR_NOACK;
|
||||||
}
|
|
||||||
|
|
||||||
// We selected http/1.1
|
|
||||||
return SSL_TLSEXT_ERR_OK;
|
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ void test_nghttp2_frame_pack_headers_frame_too_large(void)
|
|||||||
nghttp2_bufs bufs;
|
nghttp2_bufs bufs;
|
||||||
nghttp2_nv *nva;
|
nghttp2_nv *nva;
|
||||||
ssize_t nvlen;
|
ssize_t nvlen;
|
||||||
size_t big_vallen = NGHTTP2_HD_MAX_VALUE;
|
size_t big_vallen = NGHTTP2_HD_MAX_NV;
|
||||||
nghttp2_nv big_hds[16];
|
nghttp2_nv big_hds[16];
|
||||||
size_t big_hdslen = ARRLEN(big_hds);
|
size_t big_hdslen = ARRLEN(big_hds);
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|||||||
@@ -2374,7 +2374,7 @@ void test_nghttp2_session_send_headers_header_comp_error(void)
|
|||||||
nghttp2_frame *frame = malloc(sizeof(nghttp2_frame));
|
nghttp2_frame *frame = malloc(sizeof(nghttp2_frame));
|
||||||
nghttp2_nv *nva;
|
nghttp2_nv *nva;
|
||||||
ssize_t nvlen;
|
ssize_t nvlen;
|
||||||
size_t vallen = NGHTTP2_HD_MAX_VALUE;
|
size_t vallen = NGHTTP2_HD_MAX_NV;
|
||||||
nghttp2_nv nv[28];
|
nghttp2_nv nv[28];
|
||||||
size_t nnv = ARRLEN(nv);
|
size_t nnv = ARRLEN(nv);
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|||||||
Reference in New Issue
Block a user