Remove SPDY
This commit is contained in:
parent
48f574076c
commit
4d1139f653
76
README.rst
76
README.rst
|
@ -4,10 +4,10 @@ nghttp2 - HTTP/2 C Library
|
||||||
This is an implementation of the Hypertext Transfer Protocol version 2
|
This is an implementation of the Hypertext Transfer Protocol version 2
|
||||||
in C.
|
in C.
|
||||||
|
|
||||||
The framing layer of HTTP/2 is implemented as a reusable C
|
The framing layer of HTTP/2 is implemented as a reusable C library.
|
||||||
library. On top of that, we have implemented an HTTP/2 client, server
|
On top of that, we have implemented an HTTP/2 client, server and
|
||||||
and proxy. We have also developed load test and benchmarking tools for
|
proxy. We have also developed load test and benchmarking tools for
|
||||||
HTTP/2 and SPDY.
|
HTTP/2.
|
||||||
|
|
||||||
An HPACK encoder and decoder are available as a public API.
|
An HPACK encoder and decoder are available as a public API.
|
||||||
|
|
||||||
|
@ -34,8 +34,8 @@ implementation.
|
||||||
|
|
||||||
* https://nghttp2.org/ (TLS + ALPN/NPN)
|
* https://nghttp2.org/ (TLS + ALPN/NPN)
|
||||||
|
|
||||||
This endpoint supports ``h2``, ``h2-16``, ``h2-14``, ``spdy/3.1``
|
This endpoint supports ``h2``, ``h2-16``, ``h2-14``, and
|
||||||
and ``http/1.1`` via ALPN/NPN and requires TLSv1.2 for HTTP/2
|
``http/1.1`` via ALPN/NPN and requires TLSv1.2 for HTTP/2
|
||||||
connection.
|
connection.
|
||||||
|
|
||||||
* http://nghttp2.org/ (HTTP Upgrade and HTTP/2 Direct)
|
* http://nghttp2.org/ (HTTP Upgrade and HTTP/2 Direct)
|
||||||
|
@ -76,14 +76,6 @@ ALPN support requires OpenSSL >= 1.0.2 (released 22 January 2015).
|
||||||
LibreSSL >= 2.2.0 can be used instead of OpenSSL, but OpenSSL has more
|
LibreSSL >= 2.2.0 can be used instead of OpenSSL, but OpenSSL has more
|
||||||
features than LibreSSL at the time of this writing.
|
features than LibreSSL at the time of this writing.
|
||||||
|
|
||||||
To enable the SPDY protocol in the application program ``nghttpx`` and
|
|
||||||
``h2load``, the following package is required:
|
|
||||||
|
|
||||||
* spdylay >= 1.3.2
|
|
||||||
|
|
||||||
We no longer recommend to build nghttp2 with SPDY protocol support
|
|
||||||
enabled. SPDY support will be removed soon.
|
|
||||||
|
|
||||||
To enable ``-a`` option (getting linked assets from the downloaded
|
To enable ``-a`` option (getting linked assets from the downloaded
|
||||||
resource) in ``nghttp``, the following package is required:
|
resource) in ``nghttp``, the following package is required:
|
||||||
|
|
||||||
|
@ -130,13 +122,9 @@ and above, run the following to install the required packages:
|
||||||
|
|
||||||
sudo apt-get install g++ make binutils autoconf automake autotools-dev libtool pkg-config \
|
sudo apt-get install g++ make binutils autoconf automake autotools-dev libtool pkg-config \
|
||||||
zlib1g-dev libcunit1-dev libssl-dev libxml2-dev libev-dev libevent-dev libjansson-dev \
|
zlib1g-dev libcunit1-dev libssl-dev libxml2-dev libev-dev libevent-dev libjansson-dev \
|
||||||
libc-ares-dev libjemalloc-dev libsystemd-dev libspdylay-dev \
|
libc-ares-dev libjemalloc-dev libsystemd-dev \
|
||||||
cython python3-dev python-setuptools
|
cython python3-dev python-setuptools
|
||||||
|
|
||||||
Since Ubuntu 15.10, spdylay has been available as a package named
|
|
||||||
`libspdylay-dev`. For the earlier Ubuntu release, you need to build
|
|
||||||
it yourself: http://tatsuhiro-t.github.io/spdylay/
|
|
||||||
|
|
||||||
To enable mruby support for nghttpx, `mruby
|
To enable mruby support for nghttpx, `mruby
|
||||||
<https://github.com/mruby/mruby>`_ is required. We need to build
|
<https://github.com/mruby/mruby>`_ is required. We need to build
|
||||||
mruby with C++ ABI explicitly turned on, and probably need other
|
mruby with C++ ABI explicitly turned on, and probably need other
|
||||||
|
@ -332,7 +320,6 @@ its testing framework. We depend on the following libraries:
|
||||||
* golang.org/x/net/http2
|
* golang.org/x/net/http2
|
||||||
* golang.org/x/net/websocket
|
* golang.org/x/net/websocket
|
||||||
* https://github.com/tatsuhiro-t/go-nghttp2
|
* https://github.com/tatsuhiro-t/go-nghttp2
|
||||||
* https://github.com/tatsuhiro-t/spdy
|
|
||||||
|
|
||||||
To download the above packages, after settings ``GOPATH``, run the
|
To download the above packages, after settings ``GOPATH``, run the
|
||||||
following command under ``integration-tests`` directory:
|
following command under ``integration-tests`` directory:
|
||||||
|
@ -350,11 +337,6 @@ To run the tests, run the following command under
|
||||||
|
|
||||||
Inside the tests, we use port 3009 to run the test subject server.
|
Inside the tests, we use port 3009 to run the test subject server.
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
github.com/tatsuhiro-t/spdy is a copy used to be available at
|
|
||||||
golang.org/x/net/spdy, but it is now gone.
|
|
||||||
|
|
||||||
Migration from v0.7.15 or earlier
|
Migration from v0.7.15 or earlier
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
|
@ -755,7 +737,7 @@ information. Here is sample output from ``nghttpd``:
|
||||||
nghttpx - proxy
|
nghttpx - proxy
|
||||||
+++++++++++++++
|
+++++++++++++++
|
||||||
|
|
||||||
``nghttpx`` is a multi-threaded reverse proxy for HTTP/2, SPDY and
|
``nghttpx`` is a multi-threaded reverse proxy for HTTP/2, and
|
||||||
HTTP/1.1, and powers http://nghttp2.org and supports HTTP/2 server
|
HTTP/1.1, and powers http://nghttp2.org and supports HTTP/2 server
|
||||||
push.
|
push.
|
||||||
|
|
||||||
|
@ -770,31 +752,30 @@ to know how to migrate from earlier releases.
|
||||||
``nghttpx`` implements `important performance-oriented features
|
``nghttpx`` implements `important performance-oriented features
|
||||||
<https://istlsfastyet.com/#server-performance>`_ in TLS, such as
|
<https://istlsfastyet.com/#server-performance>`_ in TLS, such as
|
||||||
session IDs, session tickets (with automatic key rotation), OCSP
|
session IDs, session tickets (with automatic key rotation), OCSP
|
||||||
stapling, dynamic record sizing, ALPN/NPN, forward secrecy and SPDY &
|
stapling, dynamic record sizing, ALPN/NPN, forward secrecy and HTTP/2.
|
||||||
HTTP/2. ``nghttpx`` also offers the functionality to share session
|
``nghttpx`` also offers the functionality to share session cache and
|
||||||
cache and ticket keys among multiple ``nghttpx`` instances via
|
ticket keys among multiple ``nghttpx`` instances via memcached.
|
||||||
memcached.
|
|
||||||
|
|
||||||
``nghttpx`` has 2 operation modes:
|
``nghttpx`` has 2 operation modes:
|
||||||
|
|
||||||
================== ====================== ================ =============
|
================== ================ ================ =============
|
||||||
Mode option Frontend Backend Note
|
Mode option Frontend Backend Note
|
||||||
================== ====================== ================ =============
|
================== ================ ================ =============
|
||||||
default mode HTTP/2, SPDY, HTTP/1.1 HTTP/1.1, HTTP/2 Reverse proxy
|
default mode HTTP/2, HTTP/1.1 HTTP/1.1, HTTP/2 Reverse proxy
|
||||||
``--http2-proxy`` HTTP/2, SPDY, HTTP/1.1 HTTP/1.1, HTTP/2 Forward proxy
|
``--http2-proxy`` HTTP/2, HTTP/1.1 HTTP/1.1, HTTP/2 Forward proxy
|
||||||
================== ====================== ================ =============
|
================== ================ ================ =============
|
||||||
|
|
||||||
The interesting mode at the moment is the default mode. It works like
|
The interesting mode at the moment is the default mode. It works like
|
||||||
a reverse proxy and listens for HTTP/2, SPDY and HTTP/1.1 and can be
|
a reverse proxy and listens for HTTP/2, and HTTP/1.1 and can be
|
||||||
deployed as a SSL/TLS terminator for existing web server.
|
deployed as a SSL/TLS terminator for existing web server.
|
||||||
|
|
||||||
In all modes, the frontend connections are encrypted by SSL/TLS by
|
In all modes, the frontend connections are encrypted by SSL/TLS by
|
||||||
default. To disable encryption, use the ``no-tls`` keyword in
|
default. To disable encryption, use the ``no-tls`` keyword in
|
||||||
``--frontend`` option. If encryption is disabled, SPDY is disabled in
|
``--frontend`` option. If encryption is disabled, incoming HTTP/1.1
|
||||||
the frontend and incoming HTTP/1.1 connections can be upgraded to
|
connections can be upgraded to HTTP/2 through HTTP Upgrade. On the
|
||||||
HTTP/2 through HTTP Upgrade. On the other hard, backend connections
|
other hard, backend connections are not encrypted by default. To
|
||||||
are not encrypted by default. To encrypt backend connections, use
|
encrypt backend connections, use ``tls`` keyword in ``--backend``
|
||||||
``tls`` keyword in ``--backend`` option.
|
option.
|
||||||
|
|
||||||
``nghttpx`` supports a configuration file. See the ``--conf`` option and
|
``nghttpx`` supports a configuration file. See the ``--conf`` option and
|
||||||
sample configuration file ``nghttpx.conf.sample``.
|
sample configuration file ``nghttpx.conf.sample``.
|
||||||
|
@ -804,15 +785,15 @@ server:
|
||||||
|
|
||||||
.. code-block:: text
|
.. code-block:: text
|
||||||
|
|
||||||
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1, HTTP/2) --> Web Server
|
Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/1.1, HTTP/2) --> Web Server
|
||||||
[reverse proxy]
|
[reverse proxy]
|
||||||
|
|
||||||
With the ``--http2-proxy`` option, it works as forward proxy, and it
|
With the ``--http2-proxy`` option, it works as forward proxy, and it
|
||||||
is so called secure HTTP/2 proxy (aka SPDY proxy):
|
is so called secure HTTP/2 proxy:
|
||||||
|
|
||||||
.. code-block:: text
|
.. code-block:: text
|
||||||
|
|
||||||
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy
|
Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy
|
||||||
[secure proxy] (e.g., Squid, ATS)
|
[secure proxy] (e.g., Squid, ATS)
|
||||||
|
|
||||||
The ``Client`` in the above example needs to be configured to use
|
The ``Client`` in the above example needs to be configured to use
|
||||||
|
@ -845,7 +826,7 @@ proxy through an HTTP proxy:
|
||||||
|
|
||||||
.. code-block:: text
|
.. code-block:: text
|
||||||
|
|
||||||
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/2) --
|
Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) --
|
||||||
|
|
||||||
--===================---> HTTP/2 Proxy
|
--===================---> HTTP/2 Proxy
|
||||||
(HTTP proxy tunnel) (e.g., nghttpx -s)
|
(HTTP proxy tunnel) (e.g., nghttpx -s)
|
||||||
|
@ -853,9 +834,8 @@ proxy through an HTTP proxy:
|
||||||
Benchmarking tool
|
Benchmarking tool
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
The ``h2load`` program is a benchmarking tool for HTTP/2 and SPDY.
|
The ``h2load`` program is a benchmarking tool for HTTP/2. The UI of
|
||||||
The SPDY support is enabled if the program was built with the spdylay
|
``h2load`` is heavily inspired by ``weighttp``
|
||||||
library. The UI of ``h2load`` is heavily inspired by ``weighttp``
|
|
||||||
(https://github.com/lighttpd/weighttp). The typical usage is as
|
(https://github.com/lighttpd/weighttp). The typical usage is as
|
||||||
follows:
|
follows:
|
||||||
|
|
||||||
|
|
30
configure.ac
30
configure.ac
|
@ -117,11 +117,6 @@ AC_ARG_WITH([jemalloc],
|
||||||
[Use jemalloc [default=check]])],
|
[Use jemalloc [default=check]])],
|
||||||
[request_jemalloc=$withval], [request_jemalloc=check])
|
[request_jemalloc=$withval], [request_jemalloc=check])
|
||||||
|
|
||||||
AC_ARG_WITH([spdylay],
|
|
||||||
[AS_HELP_STRING([--with-spdylay],
|
|
||||||
[(Deprecated) Use spdylay [default=no]])],
|
|
||||||
[request_spdylay=$withval], [request_spdylay=no])
|
|
||||||
|
|
||||||
AC_ARG_WITH([systemd],
|
AC_ARG_WITH([systemd],
|
||||||
[AS_HELP_STRING([--with-systemd],
|
[AS_HELP_STRING([--with-systemd],
|
||||||
[Enable systemd support in nghttpx [default=check]])],
|
[Enable systemd support in nghttpx [default=check]])],
|
||||||
|
@ -458,26 +453,6 @@ if test "x${request_jemalloc}" = "xyes" &&
|
||||||
AC_MSG_ERROR([jemalloc was requested (--with-jemalloc) but not found])
|
AC_MSG_ERROR([jemalloc was requested (--with-jemalloc) but not found])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# spdylay (for src/nghttpx and src/h2load)
|
|
||||||
have_spdylay=no
|
|
||||||
if test "x${request_spdylay}" != "xno"; then
|
|
||||||
PKG_CHECK_MODULES([LIBSPDYLAY], [libspdylay >= 1.3.2],
|
|
||||||
[have_spdylay=yes], [have_spdylay=no])
|
|
||||||
if test "x${have_spdylay}" = "xyes"; then
|
|
||||||
AC_DEFINE([HAVE_SPDYLAY], [1], [Define to 1 if you have `spdylay` library.])
|
|
||||||
else
|
|
||||||
AC_MSG_NOTICE($LIBSPDYLAY_PKG_ERRORS)
|
|
||||||
AC_MSG_NOTICE([The SPDY support in nghttpx and h2load will be disabled.])
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test "x${request_spdylay}" = "xyes" &&
|
|
||||||
test "x${have_spdylay}" != "xyes"; then
|
|
||||||
AC_MSG_ERROR([spdylay was requested (--with-spdylay) but not found])
|
|
||||||
fi
|
|
||||||
|
|
||||||
AM_CONDITIONAL([HAVE_SPDYLAY], [ test "x${have_spdylay}" = "xyes" ])
|
|
||||||
|
|
||||||
# Check Boost Asio library
|
# Check Boost Asio library
|
||||||
have_asio_lib=no
|
have_asio_lib=no
|
||||||
|
|
||||||
|
@ -928,7 +903,6 @@ AC_MSG_NOTICE([summary of build options:
|
||||||
Libev: ${have_libev} (CFLAGS='${LIBEV_CFLAGS}' LIBS='${LIBEV_LIBS}')
|
Libev: ${have_libev} (CFLAGS='${LIBEV_CFLAGS}' LIBS='${LIBEV_LIBS}')
|
||||||
Libc-ares ${have_libcares} (CFLAGS='${LIBCARES_CFLAGS}' LIBS='${LIBCARES_LIBS}')
|
Libc-ares ${have_libcares} (CFLAGS='${LIBCARES_CFLAGS}' LIBS='${LIBCARES_LIBS}')
|
||||||
Libevent(SSL): ${have_libevent_openssl} (CFLAGS='${LIBEVENT_OPENSSL_CFLAGS}' LIBS='${LIBEVENT_OPENSSL_LIBS}')
|
Libevent(SSL): ${have_libevent_openssl} (CFLAGS='${LIBEVENT_OPENSSL_CFLAGS}' LIBS='${LIBEVENT_OPENSSL_LIBS}')
|
||||||
Spdylay: ${have_spdylay} (CFLAGS='${LIBSPDYLAY_CFLAGS}' LIBS='${LIBSPDYLAY_LIBS}')
|
|
||||||
Jansson: ${have_jansson} (CFLAGS='${JANSSON_CFLAGS}' LIBS='${JANSSON_LIBS}')
|
Jansson: ${have_jansson} (CFLAGS='${JANSSON_CFLAGS}' LIBS='${JANSSON_LIBS}')
|
||||||
Jemalloc: ${have_jemalloc} (LIBS='${JEMALLOC_LIBS}')
|
Jemalloc: ${have_jemalloc} (LIBS='${JEMALLOC_LIBS}')
|
||||||
Zlib: ${have_zlib} (CFLAGS='${ZLIB_CFLAGS}' LIBS='${ZLIB_LIBS}')
|
Zlib: ${have_zlib} (CFLAGS='${ZLIB_CFLAGS}' LIBS='${ZLIB_LIBS}')
|
||||||
|
@ -950,7 +924,3 @@ AC_MSG_NOTICE([summary of build options:
|
||||||
Python bindings:${enable_python_bindings}
|
Python bindings:${enable_python_bindings}
|
||||||
Threading: ${enable_threads}
|
Threading: ${enable_threads}
|
||||||
])
|
])
|
||||||
|
|
||||||
if test "x${have_spdylay}" == "xyes"; then
|
|
||||||
AC_MSG_WARN([spdylay support was deprecated, and will be removed in v1.29.0.])
|
|
||||||
fi
|
|
||||||
|
|
|
@ -41,8 +41,7 @@ traffic
|
||||||
used for header fields after decompression. The ``space savings``
|
used for header fields after decompression. The ``space savings``
|
||||||
is calculated by (1 - ``headers`` / ``decompressed(headers)``) *
|
is calculated by (1 - ``headers`` / ``decompressed(headers)``) *
|
||||||
100. For HTTP/1.1, this is usually 0.00%, since it does not have
|
100. For HTTP/1.1, this is usually 0.00%, since it does not have
|
||||||
header compression. For HTTP/2 and SPDY, it shows some insightful
|
header compression. For HTTP/2, it shows some insightful numbers.
|
||||||
numbers.
|
|
||||||
data
|
data
|
||||||
The number of response body bytes received from the server.
|
The number of response body bytes received from the server.
|
||||||
|
|
||||||
|
@ -110,7 +109,7 @@ h2load sets large flow control window by default, and effectively
|
||||||
disables flow control to avoid under utilization of server
|
disables flow control to avoid under utilization of server
|
||||||
performance. To set smaller flow control window, use :option:`-w` and
|
performance. To set smaller flow control window, use :option:`-w` and
|
||||||
:option:`-W` options. For example, use ``-w16 -W16`` to set default
|
:option:`-W` options. For example, use ``-w16 -W16`` to set default
|
||||||
window size described in HTTP/2 and SPDY protocol specification.
|
window size described in HTTP/2 protocol specification.
|
||||||
|
|
||||||
SEE ALSO
|
SEE ALSO
|
||||||
--------
|
--------
|
||||||
|
|
|
@ -3,10 +3,8 @@
|
||||||
h2load - HTTP/2 benchmarking tool - HOW-TO
|
h2load - HTTP/2 benchmarking tool - HOW-TO
|
||||||
==========================================
|
==========================================
|
||||||
|
|
||||||
:doc:`h2load.1` is benchmarking tool for HTTP/2 and HTTP/1.1. If
|
:doc:`h2load.1` is benchmarking tool for HTTP/2 and HTTP/1.1. It
|
||||||
built with spdylay (http://tatsuhiro-t.github.io/spdylay/) library, it
|
supports SSL/TLS and clear text for all supported protocols.
|
||||||
also supports SPDY protocol. It supports SSL/TLS and clear text for
|
|
||||||
all supported protocols.
|
|
||||||
|
|
||||||
Compiling from source
|
Compiling from source
|
||||||
---------------------
|
---------------------
|
||||||
|
@ -86,20 +84,18 @@ seconds warming up period:
|
||||||
Flow Control
|
Flow Control
|
||||||
------------
|
------------
|
||||||
|
|
||||||
HTTP/2 and SPDY/3 or later employ flow control and it may affect
|
HTTP/2 has flow control and it may affect benchmarking results. By
|
||||||
benchmarking results. By default, h2load uses large enough flow
|
default, h2load uses large enough flow control window, which
|
||||||
control window, which effectively disables flow control. To adjust
|
effectively disables flow control. To adjust receiver flow control
|
||||||
receiver flow control window size, there are following options:
|
window size, there are following options:
|
||||||
|
|
||||||
:option:`-w`
|
:option:`-w`
|
||||||
Sets the stream level initial window size to
|
Sets the stream level initial window size to
|
||||||
(2**<N>)-1. For SPDY, 2**<N> is used instead.
|
(2**<N>)-1.
|
||||||
|
|
||||||
:option:`-W`
|
:option:`-W`
|
||||||
Sets the connection level initial window size to
|
Sets the connection level initial window size to
|
||||||
(2**<N>)-1. For SPDY, if <N> is strictly less
|
(2**<N>)-1.
|
||||||
than 16, this option is ignored. Otherwise
|
|
||||||
2**<N> is used for SPDY.
|
|
||||||
|
|
||||||
Multi-Threading
|
Multi-Threading
|
||||||
---------------
|
---------------
|
||||||
|
|
|
@ -4,10 +4,10 @@ nghttpx - HTTP/2 proxy - HOW-TO
|
||||||
===============================
|
===============================
|
||||||
|
|
||||||
:doc:`nghttpx.1` is a proxy translating protocols between HTTP/2 and
|
:doc:`nghttpx.1` is a proxy translating protocols between HTTP/2 and
|
||||||
other protocols (e.g., HTTP/1, SPDY). It operates in several modes
|
other protocols (e.g., HTTP/1). It operates in several modes and each
|
||||||
and each mode may require additional programs to work with. This
|
mode may require additional programs to work with. This article
|
||||||
article describes each operation mode and explains the intended
|
describes each operation mode and explains the intended use-cases. It
|
||||||
use-cases. It also covers some useful options later.
|
also covers some useful options later.
|
||||||
|
|
||||||
Default mode
|
Default mode
|
||||||
------------
|
------------
|
||||||
|
@ -15,9 +15,7 @@ Default mode
|
||||||
If nghttpx is invoked without :option:`--http2-proxy`, it operates in
|
If nghttpx is invoked without :option:`--http2-proxy`, it operates in
|
||||||
default mode. In this mode, it works as reverse proxy (gateway) for
|
default mode. In this mode, it works as reverse proxy (gateway) for
|
||||||
both HTTP/2 and HTTP/1 clients to backend servers. This is also known
|
both HTTP/2 and HTTP/1 clients to backend servers. This is also known
|
||||||
as "HTTP/2 router". If nghttpx is linked with spdylay library and
|
as "HTTP/2 router".
|
||||||
frontend connection is SSL/TLS, the frontend also supports SPDY
|
|
||||||
protocol.
|
|
||||||
|
|
||||||
By default, frontend connection is encrypted using SSL/TLS. So
|
By default, frontend connection is encrypted using SSL/TLS. So
|
||||||
server's private key and certificate must be supplied to the command
|
server's private key and certificate must be supplied to the command
|
||||||
|
@ -25,11 +23,10 @@ line (or through configuration file). In this case, the frontend
|
||||||
protocol selection will be done via ALPN or NPN.
|
protocol selection will be done via ALPN or NPN.
|
||||||
|
|
||||||
To turn off encryption on frontend connection, use ``no-tls`` keyword
|
To turn off encryption on frontend connection, use ``no-tls`` keyword
|
||||||
in :option:`--frontend` option. In this case, SPDY protocol is not
|
in :option:`--frontend` option. HTTP/2 and HTTP/1 are available on
|
||||||
available even if spdylay library is linked to nghttpx. HTTP/2 and
|
the frontend, and an HTTP/1 connection can be upgraded to HTTP/2 using
|
||||||
HTTP/1 are available on the frontend, and an HTTP/1 connection can be
|
HTTP Upgrade. Starting HTTP/2 connection by sending HTTP/2 connection
|
||||||
upgraded to HTTP/2 using HTTP Upgrade. Starting HTTP/2 connection by
|
preface is also supported.
|
||||||
sending HTTP/2 connection preface is also supported.
|
|
||||||
|
|
||||||
nghttpx can listen on multiple frontend addresses. This is achieved
|
nghttpx can listen on multiple frontend addresses. This is achieved
|
||||||
by using multiple :option:`--frontend` options. For each frontend
|
by using multiple :option:`--frontend` options. For each frontend
|
||||||
|
@ -71,9 +68,8 @@ mode`_. The difference is that this mode acts like a forward proxy and
|
||||||
assumes the backend is an HTTP proxy server (e.g., Squid, Apache Traffic
|
assumes the backend is an HTTP proxy server (e.g., Squid, Apache Traffic
|
||||||
Server). HTTP/1 requests must include an absolute URI in request line.
|
Server). HTTP/1 requests must include an absolute URI in request line.
|
||||||
|
|
||||||
By default, the frontend connection is encrypted. So this mode is also
|
By default, the frontend connection is encrypted. So this mode is
|
||||||
called secure proxy. If nghttpx is linked with spdylay, it supports
|
also called secure proxy.
|
||||||
SPDY protocols and it works as so called SPDY proxy.
|
|
||||||
|
|
||||||
To turn off encryption on the frontend connection, use ``no-tls`` keyword
|
To turn off encryption on the frontend connection, use ``no-tls`` keyword
|
||||||
in :option:`--frontend` option.
|
in :option:`--frontend` option.
|
||||||
|
@ -102,8 +98,8 @@ like this:
|
||||||
At the time of this writing, Firefox 41 and Chromium v46 can use
|
At the time of this writing, Firefox 41 and Chromium v46 can use
|
||||||
nghttpx as HTTP/2 proxy.
|
nghttpx as HTTP/2 proxy.
|
||||||
|
|
||||||
To make Firefox or Chromium use nghttpx as HTTP/2 or SPDY proxy, user
|
To make Firefox or Chromium use nghttpx as HTTP/2 proxy, user has to
|
||||||
has to create proxy.pac script file like this:
|
create proxy.pac script file like this:
|
||||||
|
|
||||||
.. code-block:: javascript
|
.. code-block:: javascript
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
GO_FILES = \
|
GO_FILES = \
|
||||||
nghttpx_http1_test.go \
|
nghttpx_http1_test.go \
|
||||||
nghttpx_http2_test.go \
|
nghttpx_http2_test.go \
|
||||||
nghttpx_spdy_test.go \
|
|
||||||
server_tester.go
|
server_tester.go
|
||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
|
@ -43,7 +42,6 @@ EXTRA_DIST = \
|
||||||
itprep:
|
itprep:
|
||||||
go get -d -v golang.org/x/net/http2
|
go get -d -v golang.org/x/net/http2
|
||||||
go get -d -v github.com/tatsuhiro-t/go-nghttp2
|
go get -d -v github.com/tatsuhiro-t/go-nghttp2
|
||||||
go get -d -v github.com/tatsuhiro-t/spdy
|
|
||||||
go get -d -v golang.org/x/net/websocket
|
go get -d -v golang.org/x/net/websocket
|
||||||
|
|
||||||
it:
|
it:
|
||||||
|
|
|
@ -1,664 +0,0 @@
|
||||||
package nghttp2
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"github.com/tatsuhiro-t/spdy"
|
|
||||||
"golang.org/x/net/http2/hpack"
|
|
||||||
"net/http"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestS3H1PlainGET tests whether simple SPDY GET request works.
|
|
||||||
func TestS3H1PlainGET(t *testing.T) {
|
|
||||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, noopHandler)
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
res, err := st.spdy(requestParam{
|
|
||||||
name: "TestS3H1PlainGET",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := 200
|
|
||||||
if got := res.status; got != want {
|
|
||||||
t.Errorf("status = %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestS3H1BadRequestCL tests that server rejects request whose
|
|
||||||
// content-length header field value does not match its request body
|
|
||||||
// size.
|
|
||||||
func TestS3H1BadRequestCL(t *testing.T) {
|
|
||||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, noopHandler)
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
// we set content-length: 1024, but the actual request body is
|
|
||||||
// 3 bytes.
|
|
||||||
res, err := st.spdy(requestParam{
|
|
||||||
name: "TestS3H1BadRequestCL",
|
|
||||||
method: "POST",
|
|
||||||
header: []hpack.HeaderField{
|
|
||||||
pair("content-length", "1024"),
|
|
||||||
},
|
|
||||||
body: []byte("foo"),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := spdy.ProtocolError
|
|
||||||
if got := res.spdyRstErrCode; got != want {
|
|
||||||
t.Errorf("res.spdyRstErrCode = %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestS3H1MultipleRequestCL tests that server rejects request with
|
|
||||||
// multiple Content-Length request header fields.
|
|
||||||
func TestS3H1MultipleRequestCL(t *testing.T) {
|
|
||||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
t.Errorf("server should not forward bad request")
|
|
||||||
})
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
res, err := st.spdy(requestParam{
|
|
||||||
name: "TestS3H1MultipleRequestCL",
|
|
||||||
header: []hpack.HeaderField{
|
|
||||||
pair("content-length", "1"),
|
|
||||||
pair("content-length", "1"),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
}
|
|
||||||
want := 400
|
|
||||||
if got := res.status; got != want {
|
|
||||||
t.Errorf("status: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestS3H1InvalidRequestCL tests that server rejects request with
|
|
||||||
// Content-Length which cannot be parsed as a number.
|
|
||||||
func TestS3H1InvalidRequestCL(t *testing.T) {
|
|
||||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
t.Errorf("server should not forward bad request")
|
|
||||||
})
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
res, err := st.spdy(requestParam{
|
|
||||||
name: "TestS3H1InvalidRequestCL",
|
|
||||||
header: []hpack.HeaderField{
|
|
||||||
pair("content-length", ""),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
}
|
|
||||||
want := 400
|
|
||||||
if got := res.status; got != want {
|
|
||||||
t.Errorf("status: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestS3H1GenerateVia tests that server generates Via header field to and
|
|
||||||
// from backend server.
|
|
||||||
func TestS3H1GenerateVia(t *testing.T) {
|
|
||||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if got, want := r.Header.Get("Via"), "1.1 nghttpx"; got != want {
|
|
||||||
t.Errorf("Via: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
res, err := st.spdy(requestParam{
|
|
||||||
name: "TestS3H1GenerateVia",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
}
|
|
||||||
if got, want := res.header.Get("Via"), "1.1 nghttpx"; got != want {
|
|
||||||
t.Errorf("Via: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestS3H1AppendVia tests that server adds value to existing Via
|
|
||||||
// header field to and from backend server.
|
|
||||||
func TestS3H1AppendVia(t *testing.T) {
|
|
||||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if got, want := r.Header.Get("Via"), "foo, 1.1 nghttpx"; got != want {
|
|
||||||
t.Errorf("Via: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
w.Header().Add("Via", "bar")
|
|
||||||
})
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
res, err := st.spdy(requestParam{
|
|
||||||
name: "TestS3H1AppendVia",
|
|
||||||
header: []hpack.HeaderField{
|
|
||||||
pair("via", "foo"),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
}
|
|
||||||
if got, want := res.header.Get("Via"), "bar, 1.1 nghttpx"; got != want {
|
|
||||||
t.Errorf("Via: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestS3H1NoVia tests that server does not add value to existing Via
|
|
||||||
// header field to and from backend server.
|
|
||||||
func TestS3H1NoVia(t *testing.T) {
|
|
||||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--no-via"}, t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if got, want := r.Header.Get("Via"), "foo"; got != want {
|
|
||||||
t.Errorf("Via: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
w.Header().Add("Via", "bar")
|
|
||||||
})
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
res, err := st.spdy(requestParam{
|
|
||||||
name: "TestS3H1NoVia",
|
|
||||||
header: []hpack.HeaderField{
|
|
||||||
pair("via", "foo"),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
}
|
|
||||||
if got, want := res.header.Get("Via"), "bar"; got != want {
|
|
||||||
t.Errorf("Via: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestS3H1HeaderFieldBuffer tests that request with header fields
|
|
||||||
// larger than configured buffer size is rejected.
|
|
||||||
func TestS3H1HeaderFieldBuffer(t *testing.T) {
|
|
||||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--request-header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
t.Fatal("execution path should not be here")
|
|
||||||
})
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
res, err := st.spdy(requestParam{
|
|
||||||
name: "TestS3H1HeaderFieldBuffer",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
}
|
|
||||||
if got, want := res.spdyRstErrCode, spdy.InternalError; got != want {
|
|
||||||
t.Errorf("res.spdyRstErrCode: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestS3H1HeaderFields tests that request with header fields more
|
|
||||||
// than configured number is rejected.
|
|
||||||
func TestS3H1HeaderFields(t *testing.T) {
|
|
||||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--max-request-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
t.Fatal("execution path should not be here")
|
|
||||||
})
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
res, err := st.spdy(requestParam{
|
|
||||||
name: "TestS3H1HeaderFields",
|
|
||||||
// we have at least 5 pseudo-header fields sent, and
|
|
||||||
// that ensures that buffer limit exceeds.
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
}
|
|
||||||
if got, want := res.spdyRstErrCode, spdy.InternalError; got != want {
|
|
||||||
t.Errorf("res.spdyRstErrCode: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestS3H1InvalidMethod tests that server rejects invalid method with
|
|
||||||
// 501.
|
|
||||||
func TestS3H1InvalidMethod(t *testing.T) {
|
|
||||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
t.Errorf("server should not forward this request")
|
|
||||||
})
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
res, err := st.spdy(requestParam{
|
|
||||||
name: "TestS3H1InvalidMethod",
|
|
||||||
method: "get",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
}
|
|
||||||
if got, want := res.status, 501; got != want {
|
|
||||||
t.Errorf("status: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestS3H1BadHost tests that server rejects request including bad
|
|
||||||
// character in :host header field.
|
|
||||||
func TestS3H1BadHost(t *testing.T) {
|
|
||||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
t.Errorf("server should not forward this request")
|
|
||||||
})
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
res, err := st.spdy(requestParam{
|
|
||||||
name: "TestS3H1BadHost",
|
|
||||||
authority: `foo\bar`,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
}
|
|
||||||
if got, want := res.status, 400; got != want {
|
|
||||||
t.Errorf("status: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestS3H1BadScheme tests that server rejects request including bad
|
|
||||||
// character in :scheme header field.
|
|
||||||
func TestS3H1BadScheme(t *testing.T) {
|
|
||||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
t.Errorf("server should not forward this request")
|
|
||||||
})
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
res, err := st.spdy(requestParam{
|
|
||||||
name: "TestS3H1BadScheme",
|
|
||||||
scheme: `http*`,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
}
|
|
||||||
if got, want := res.status, 400; got != want {
|
|
||||||
t.Errorf("status: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestS3H1ReqPhaseSetHeader tests mruby request phase hook
|
|
||||||
// modifies request header fields.
|
|
||||||
func TestS3H1ReqPhaseSetHeader(t *testing.T) {
|
|
||||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--mruby-file=" + testDir + "/req-set-header.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if got, want := r.Header.Get("User-Agent"), "mruby"; got != want {
|
|
||||||
t.Errorf("User-Agent = %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
res, err := st.spdy(requestParam{
|
|
||||||
name: "TestS3H1ReqPhaseSetHeader",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if got, want := res.status, 200; got != want {
|
|
||||||
t.Errorf("status = %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestS3H1ReqPhaseReturn tests mruby request phase hook returns
|
|
||||||
// custom response.
|
|
||||||
func TestS3H1ReqPhaseReturn(t *testing.T) {
|
|
||||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--mruby-file=" + testDir + "/req-return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
t.Fatalf("request should not be forwarded")
|
|
||||||
})
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
res, err := st.spdy(requestParam{
|
|
||||||
name: "TestS3H1ReqPhaseReturn",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if got, want := res.status, 404; got != want {
|
|
||||||
t.Errorf("status = %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
hdtests := []struct {
|
|
||||||
k, v string
|
|
||||||
}{
|
|
||||||
{"content-length", "20"},
|
|
||||||
{"from", "mruby"},
|
|
||||||
}
|
|
||||||
for _, tt := range hdtests {
|
|
||||||
if got, want := res.header.Get(tt.k), tt.v; got != want {
|
|
||||||
t.Errorf("%v = %v; want %v", tt.k, got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if got, want := string(res.body), "Hello World from req"; got != want {
|
|
||||||
t.Errorf("body = %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestS3H1RespPhaseSetHeader tests mruby response phase hook modifies
|
|
||||||
// response header fields.
|
|
||||||
func TestS3H1RespPhaseSetHeader(t *testing.T) {
|
|
||||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--mruby-file=" + testDir + "/resp-set-header.rb"}, t, noopHandler)
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
res, err := st.spdy(requestParam{
|
|
||||||
name: "TestS3H1RespPhaseSetHeader",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if got, want := res.status, 200; got != want {
|
|
||||||
t.Errorf("status = %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
if got, want := res.header.Get("alpha"), "bravo"; got != want {
|
|
||||||
t.Errorf("alpha = %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestS3H1RespPhaseReturn tests mruby response phase hook returns
|
|
||||||
// custom response.
|
|
||||||
func TestS3H1RespPhaseReturn(t *testing.T) {
|
|
||||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--mruby-file=" + testDir + "/resp-return.rb"}, t, noopHandler)
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
res, err := st.spdy(requestParam{
|
|
||||||
name: "TestS3H1RespPhaseReturn",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if got, want := res.status, 404; got != want {
|
|
||||||
t.Errorf("status = %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
hdtests := []struct {
|
|
||||||
k, v string
|
|
||||||
}{
|
|
||||||
{"content-length", "21"},
|
|
||||||
{"from", "mruby"},
|
|
||||||
}
|
|
||||||
for _, tt := range hdtests {
|
|
||||||
if got, want := res.header.Get(tt.k), tt.v; got != want {
|
|
||||||
t.Errorf("%v = %v; want %v", tt.k, got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if got, want := string(res.body), "Hello World from resp"; got != want {
|
|
||||||
t.Errorf("body = %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// // TestS3H2ConnectFailure tests that server handles the situation that
|
|
||||||
// // connection attempt to HTTP/2 backend failed.
|
|
||||||
// func TestS3H2ConnectFailure(t *testing.T) {
|
|
||||||
// st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--http2-bridge"}, t, noopHandler)
|
|
||||||
// defer st.Close()
|
|
||||||
|
|
||||||
// // simulate backend connect attempt failure
|
|
||||||
// st.ts.Close()
|
|
||||||
|
|
||||||
// res, err := st.spdy(requestParam{
|
|
||||||
// name: "TestS3H2ConnectFailure",
|
|
||||||
// })
|
|
||||||
// if err != nil {
|
|
||||||
// t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
// }
|
|
||||||
// want := 503
|
|
||||||
// if got := res.status; got != want {
|
|
||||||
// t.Errorf("status: %v; want %v", got, want)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TestS3H2ReqPhaseReturn tests mruby request phase hook returns
|
|
||||||
// custom response.
|
|
||||||
func TestS3H2ReqPhaseReturn(t *testing.T) {
|
|
||||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--http2-bridge", "--mruby-file=" + testDir + "/req-return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
t.Fatalf("request should not be forwarded")
|
|
||||||
})
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
res, err := st.spdy(requestParam{
|
|
||||||
name: "TestS3H2ReqPhaseReturn",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if got, want := res.status, 404; got != want {
|
|
||||||
t.Errorf("status = %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
hdtests := []struct {
|
|
||||||
k, v string
|
|
||||||
}{
|
|
||||||
{"content-length", "20"},
|
|
||||||
{"from", "mruby"},
|
|
||||||
}
|
|
||||||
for _, tt := range hdtests {
|
|
||||||
if got, want := res.header.Get(tt.k), tt.v; got != want {
|
|
||||||
t.Errorf("%v = %v; want %v", tt.k, got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if got, want := string(res.body), "Hello World from req"; got != want {
|
|
||||||
t.Errorf("body = %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestS3H2RespPhaseReturn tests mruby response phase hook returns
|
|
||||||
// custom response.
|
|
||||||
func TestS3H2RespPhaseReturn(t *testing.T) {
|
|
||||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--http2-bridge", "--mruby-file=" + testDir + "/resp-return.rb"}, t, noopHandler)
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
res, err := st.spdy(requestParam{
|
|
||||||
name: "TestS3H2RespPhaseReturn",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if got, want := res.status, 404; got != want {
|
|
||||||
t.Errorf("status = %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
hdtests := []struct {
|
|
||||||
k, v string
|
|
||||||
}{
|
|
||||||
{"content-length", "21"},
|
|
||||||
{"from", "mruby"},
|
|
||||||
}
|
|
||||||
for _, tt := range hdtests {
|
|
||||||
if got, want := res.header.Get(tt.k), tt.v; got != want {
|
|
||||||
t.Errorf("%v = %v; want %v", tt.k, got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if got, want := string(res.body), "Hello World from resp"; got != want {
|
|
||||||
t.Errorf("body = %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestS3APIBackendconfig exercise backendconfig API endpoint routine
|
|
||||||
// for successful case.
|
|
||||||
func TestS3APIBackendconfig(t *testing.T) {
|
|
||||||
st := newServerTesterTLSConnectPort([]string{"--npn-list=spdy/3.1", "-f127.0.0.1,3010;api"}, t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
t.Fatalf("request should not be forwarded")
|
|
||||||
}, 3010)
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
res, err := st.spdy(requestParam{
|
|
||||||
name: "TestS3APIBackendconfig",
|
|
||||||
path: "/api/v1beta1/backendconfig",
|
|
||||||
method: "PUT",
|
|
||||||
body: []byte(`# comment
|
|
||||||
backend=127.0.0.1,3011
|
|
||||||
|
|
||||||
`),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
}
|
|
||||||
if got, want := res.status, 200; got != want {
|
|
||||||
t.Errorf("res.status: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
var apiResp APIResponse
|
|
||||||
err = json.Unmarshal(res.body, &apiResp)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error unmarshaling API response: %v", err)
|
|
||||||
}
|
|
||||||
if got, want := apiResp.Status, "Success"; got != want {
|
|
||||||
t.Errorf("apiResp.Status: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
if got, want := apiResp.Code, 200; got != want {
|
|
||||||
t.Errorf("apiResp.Status: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestS3APIBackendconfigQuery exercise backendconfig API endpoint
|
|
||||||
// routine with query.
|
|
||||||
func TestS3APIBackendconfigQuery(t *testing.T) {
|
|
||||||
st := newServerTesterTLSConnectPort([]string{"--npn-list=spdy/3.1", "-f127.0.0.1,3010;api"}, t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
t.Fatalf("request should not be forwarded")
|
|
||||||
}, 3010)
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
res, err := st.spdy(requestParam{
|
|
||||||
name: "TestS3APIBackendconfigQuery",
|
|
||||||
path: "/api/v1beta1/backendconfig?foo=bar",
|
|
||||||
method: "PUT",
|
|
||||||
body: []byte(`# comment
|
|
||||||
backend=127.0.0.1,3011
|
|
||||||
|
|
||||||
`),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
}
|
|
||||||
if got, want := res.status, 200; got != want {
|
|
||||||
t.Errorf("res.status: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
var apiResp APIResponse
|
|
||||||
err = json.Unmarshal(res.body, &apiResp)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error unmarshaling API response: %v", err)
|
|
||||||
}
|
|
||||||
if got, want := apiResp.Status, "Success"; got != want {
|
|
||||||
t.Errorf("apiResp.Status: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
if got, want := apiResp.Code, 200; got != want {
|
|
||||||
t.Errorf("apiResp.Status: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestS3APIBackendconfigBadMethod exercise backendconfig API endpoint
|
|
||||||
// routine with bad method.
|
|
||||||
func TestS3APIBackendconfigBadMethod(t *testing.T) {
|
|
||||||
st := newServerTesterTLSConnectPort([]string{"--npn-list=spdy/3.1", "-f127.0.0.1,3010;api"}, t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
t.Fatalf("request should not be forwarded")
|
|
||||||
}, 3010)
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
res, err := st.spdy(requestParam{
|
|
||||||
name: "TestS3APIBackendconfigBadMethod",
|
|
||||||
path: "/api/v1beta1/backendconfig",
|
|
||||||
method: "GET",
|
|
||||||
body: []byte(`# comment
|
|
||||||
backend=127.0.0.1,3011
|
|
||||||
|
|
||||||
`),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
}
|
|
||||||
if got, want := res.status, 405; got != want {
|
|
||||||
t.Errorf("res.status: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
var apiResp APIResponse
|
|
||||||
err = json.Unmarshal(res.body, &apiResp)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error unmarshaling API response: %v", err)
|
|
||||||
}
|
|
||||||
if got, want := apiResp.Status, "Failure"; got != want {
|
|
||||||
t.Errorf("apiResp.Status: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
if got, want := apiResp.Code, 405; got != want {
|
|
||||||
t.Errorf("apiResp.Status: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestS3APINotFound exercise backendconfig API endpoint routine when
|
|
||||||
// API endpoint is not found.
|
|
||||||
func TestS3APINotFound(t *testing.T) {
|
|
||||||
st := newServerTesterTLSConnectPort([]string{"--npn-list=spdy/3.1", "-f127.0.0.1,3010;api"}, t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
t.Fatalf("request should not be forwarded")
|
|
||||||
}, 3010)
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
res, err := st.spdy(requestParam{
|
|
||||||
name: "TestS3APINotFound",
|
|
||||||
path: "/api/notfound",
|
|
||||||
method: "GET",
|
|
||||||
body: []byte(`# comment
|
|
||||||
backend=127.0.0.1,3011
|
|
||||||
|
|
||||||
`),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
}
|
|
||||||
if got, want := res.status, 404; got != want {
|
|
||||||
t.Errorf("res.status: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
var apiResp APIResponse
|
|
||||||
err = json.Unmarshal(res.body, &apiResp)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error unmarshaling API response: %v", err)
|
|
||||||
}
|
|
||||||
if got, want := apiResp.Status, "Failure"; got != want {
|
|
||||||
t.Errorf("apiResp.Status: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
if got, want := apiResp.Code, 404; got != want {
|
|
||||||
t.Errorf("apiResp.Status: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestS3Healthmon tests health monitor endpoint.
|
|
||||||
func TestS3Healthmon(t *testing.T) {
|
|
||||||
st := newServerTesterTLSConnectPort([]string{"--npn-list=spdy/3.1", "-f127.0.0.1,3011;healthmon"}, t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
t.Fatalf("request should not be forwarded")
|
|
||||||
}, 3011)
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
res, err := st.spdy(requestParam{
|
|
||||||
name: "TestS3Healthmon",
|
|
||||||
path: "/alpha/bravo",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
}
|
|
||||||
if got, want := res.status, 200; got != want {
|
|
||||||
t.Errorf("res.status: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestS3ResponseBeforeRequestEnd tests the situation where response
|
|
||||||
// ends before request body finishes.
|
|
||||||
func TestS3ResponseBeforeRequestEnd(t *testing.T) {
|
|
||||||
st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--mruby-file=" + testDir + "/req-return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
t.Fatal("request should not be forwarded")
|
|
||||||
})
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
res, err := st.spdy(requestParam{
|
|
||||||
name: "TestS3ResponseBeforeRequestEnd",
|
|
||||||
noEndStream: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error st.spdy() = %v", err)
|
|
||||||
}
|
|
||||||
if got, want := res.status, 404; got != want {
|
|
||||||
t.Errorf("res.status: %v; want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -40,7 +40,6 @@ AM_CPPFLAGS = \
|
||||||
-I$(top_srcdir)/lib \
|
-I$(top_srcdir)/lib \
|
||||||
-I$(top_srcdir)/src/includes \
|
-I$(top_srcdir)/src/includes \
|
||||||
-I$(top_srcdir)/third-party \
|
-I$(top_srcdir)/third-party \
|
||||||
@LIBSPDYLAY_CFLAGS@ \
|
|
||||||
@LIBXML2_CFLAGS@ \
|
@LIBXML2_CFLAGS@ \
|
||||||
@LIBEV_CFLAGS@ \
|
@LIBEV_CFLAGS@ \
|
||||||
@OPENSSL_CFLAGS@ \
|
@OPENSSL_CFLAGS@ \
|
||||||
|
@ -52,7 +51,6 @@ AM_CPPFLAGS = \
|
||||||
LDADD = $(top_builddir)/lib/libnghttp2.la \
|
LDADD = $(top_builddir)/lib/libnghttp2.la \
|
||||||
$(top_builddir)/third-party/libhttp-parser.la \
|
$(top_builddir)/third-party/libhttp-parser.la \
|
||||||
@JEMALLOC_LIBS@ \
|
@JEMALLOC_LIBS@ \
|
||||||
@LIBSPDYLAY_LIBS@ \
|
|
||||||
@LIBXML2_LIBS@ \
|
@LIBXML2_LIBS@ \
|
||||||
@LIBEV_LIBS@ \
|
@LIBEV_LIBS@ \
|
||||||
@OPENSSL_LIBS@ \
|
@OPENSSL_LIBS@ \
|
||||||
|
@ -97,10 +95,6 @@ h2load_SOURCES = util.cc util.h \
|
||||||
h2load_http2_session.cc h2load_http2_session.h \
|
h2load_http2_session.cc h2load_http2_session.h \
|
||||||
h2load_http1_session.cc h2load_http1_session.h
|
h2load_http1_session.cc h2load_http1_session.h
|
||||||
|
|
||||||
if HAVE_SPDYLAY
|
|
||||||
h2load_SOURCES += h2load_spdy_session.cc h2load_spdy_session.h
|
|
||||||
endif # HAVE_SPDYLAY
|
|
||||||
|
|
||||||
NGHTTPX_SRCS = \
|
NGHTTPX_SRCS = \
|
||||||
util.cc util.h http2.cc http2.h timegm.c timegm.h base64.h \
|
util.cc util.h http2.cc http2.h timegm.c timegm.h base64.h \
|
||||||
app_helper.cc app_helper.h \
|
app_helper.cc app_helper.h \
|
||||||
|
@ -148,10 +142,6 @@ NGHTTPX_SRCS = \
|
||||||
buffer.h memchunk.h template.h allocator.h \
|
buffer.h memchunk.h template.h allocator.h \
|
||||||
xsi_strerror.c xsi_strerror.h
|
xsi_strerror.c xsi_strerror.h
|
||||||
|
|
||||||
if HAVE_SPDYLAY
|
|
||||||
NGHTTPX_SRCS += shrpx_spdy_upstream.cc shrpx_spdy_upstream.h
|
|
||||||
endif # HAVE_SPDYLAY
|
|
||||||
|
|
||||||
if HAVE_MRUBY
|
if HAVE_MRUBY
|
||||||
NGHTTPX_SRCS += \
|
NGHTTPX_SRCS += \
|
||||||
shrpx_mruby.cc shrpx_mruby.h \
|
shrpx_mruby.cc shrpx_mruby.h \
|
||||||
|
|
|
@ -46,19 +46,12 @@
|
||||||
#include <future>
|
#include <future>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
|
||||||
#ifdef HAVE_SPDYLAY
|
|
||||||
#include <spdylay/spdylay.h>
|
|
||||||
#endif // HAVE_SPDYLAY
|
|
||||||
|
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
|
|
||||||
#include "http-parser/http_parser.h"
|
#include "http-parser/http_parser.h"
|
||||||
|
|
||||||
#include "h2load_http1_session.h"
|
#include "h2load_http1_session.h"
|
||||||
#include "h2load_http2_session.h"
|
#include "h2load_http2_session.h"
|
||||||
#ifdef HAVE_SPDYLAY
|
|
||||||
#include "h2load_spdy_session.h"
|
|
||||||
#endif // HAVE_SPDYLAY
|
|
||||||
#include "tls.h"
|
#include "tls.h"
|
||||||
#include "http2.h"
|
#include "http2.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
@ -878,14 +871,6 @@ int Client::connection_made() {
|
||||||
} else if (util::streq(NGHTTP2_H1_1, proto)) {
|
} else if (util::streq(NGHTTP2_H1_1, proto)) {
|
||||||
session = make_unique<Http1Session>(this);
|
session = make_unique<Http1Session>(this);
|
||||||
}
|
}
|
||||||
#ifdef HAVE_SPDYLAY
|
|
||||||
else {
|
|
||||||
auto spdy_version = spdylay_npn_get_version(next_proto, next_proto_len);
|
|
||||||
if (spdy_version) {
|
|
||||||
session = make_unique<SpdySession>(this, spdy_version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif // HAVE_SPDYLAY
|
|
||||||
|
|
||||||
// Just assign next_proto to selected_proto anyway to show the
|
// Just assign next_proto to selected_proto anyway to show the
|
||||||
// negotiation result.
|
// negotiation result.
|
||||||
|
@ -930,20 +915,6 @@ int Client::connection_made() {
|
||||||
session = make_unique<Http1Session>(this);
|
session = make_unique<Http1Session>(this);
|
||||||
selected_proto = NGHTTP2_H1_1.str();
|
selected_proto = NGHTTP2_H1_1.str();
|
||||||
break;
|
break;
|
||||||
#ifdef HAVE_SPDYLAY
|
|
||||||
case Config::PROTO_SPDY2:
|
|
||||||
session = make_unique<SpdySession>(this, SPDYLAY_PROTO_SPDY2);
|
|
||||||
selected_proto = "spdy/2";
|
|
||||||
break;
|
|
||||||
case Config::PROTO_SPDY3:
|
|
||||||
session = make_unique<SpdySession>(this, SPDYLAY_PROTO_SPDY3);
|
|
||||||
selected_proto = "spdy/3";
|
|
||||||
break;
|
|
||||||
case Config::PROTO_SPDY3_1:
|
|
||||||
session = make_unique<SpdySession>(this, SPDYLAY_PROTO_SPDY3_1);
|
|
||||||
selected_proto = "spdy/3.1";
|
|
||||||
break;
|
|
||||||
#endif // HAVE_SPDYLAY
|
|
||||||
default:
|
default:
|
||||||
// unreachable
|
// unreachable
|
||||||
assert(0);
|
assert(0);
|
||||||
|
@ -1788,17 +1759,13 @@ void print_version(std::ostream &out) {
|
||||||
namespace {
|
namespace {
|
||||||
void print_usage(std::ostream &out) {
|
void print_usage(std::ostream &out) {
|
||||||
out << R"(Usage: h2load [OPTIONS]... [URI]...
|
out << R"(Usage: h2load [OPTIONS]... [URI]...
|
||||||
benchmarking tool for HTTP/2 and SPDY server)"
|
benchmarking tool for HTTP/2 server)"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr char DEFAULT_NPN_LIST[] = "h2,h2-16,h2-14"
|
constexpr char DEFAULT_NPN_LIST[] = "h2,h2-16,h2-14,http/1.1";
|
||||||
#ifdef HAVE_SPDYLAY
|
|
||||||
",spdy/3.1,spdy/3,spdy/2"
|
|
||||||
#endif // HAVE_SPDYLAY
|
|
||||||
",http/1.1";
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -1849,14 +1816,11 @@ Options:
|
||||||
Default: 1
|
Default: 1
|
||||||
-w, --window-bits=<N>
|
-w, --window-bits=<N>
|
||||||
Sets the stream level initial window size to (2**<N>)-1.
|
Sets the stream level initial window size to (2**<N>)-1.
|
||||||
For SPDY, 2**<N> is used instead.
|
|
||||||
Default: )"
|
Default: )"
|
||||||
<< config.window_bits << R"(
|
<< config.window_bits << R"(
|
||||||
-W, --connection-window-bits=<N>
|
-W, --connection-window-bits=<N>
|
||||||
Sets the connection level initial window size to
|
Sets the connection level initial window size to
|
||||||
(2**<N>)-1. For SPDY, if <N> is strictly less than 16,
|
(2**<N>)-1.
|
||||||
this option is ignored. Otherwise 2**<N> is used for
|
|
||||||
SPDY.
|
|
||||||
Default: )" << config.connection_window_bits << R"(
|
Default: )" << config.connection_window_bits << R"(
|
||||||
-H, --header=<HEADER>
|
-H, --header=<HEADER>
|
||||||
Add/Override a header to the requests.
|
Add/Override a header to the requests.
|
||||||
|
@ -1867,17 +1831,9 @@ Options:
|
||||||
<< config.ciphers << R"(
|
<< config.ciphers << R"(
|
||||||
-p, --no-tls-proto=<PROTOID>
|
-p, --no-tls-proto=<PROTOID>
|
||||||
Specify ALPN identifier of the protocol to be used when
|
Specify ALPN identifier of the protocol to be used when
|
||||||
accessing http URI without SSL/TLS.)";
|
accessing http URI without SSL/TLS.
|
||||||
|
Available protocols: )" << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID
|
||||||
#ifdef HAVE_SPDYLAY
|
<< R"( and )" << NGHTTP2_H1_1 << R"(
|
||||||
out << R"(
|
|
||||||
Available protocols: spdy/2, spdy/3, spdy/3.1, )";
|
|
||||||
#else // !HAVE_SPDYLAY
|
|
||||||
out << R"(
|
|
||||||
Available protocols: )";
|
|
||||||
#endif // !HAVE_SPDYLAY
|
|
||||||
out << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"( and
|
|
||||||
)" << NGHTTP2_H1_1 << R"(
|
|
||||||
Default: )"
|
Default: )"
|
||||||
<< NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
|
<< NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
|
||||||
-d, --data=<PATH>
|
-d, --data=<PATH>
|
||||||
|
@ -2115,14 +2071,6 @@ int main(int argc, char **argv) {
|
||||||
config.no_tls_proto = Config::PROTO_HTTP2;
|
config.no_tls_proto = Config::PROTO_HTTP2;
|
||||||
} else if (util::strieq(NGHTTP2_H1_1, proto)) {
|
} else if (util::strieq(NGHTTP2_H1_1, proto)) {
|
||||||
config.no_tls_proto = Config::PROTO_HTTP1_1;
|
config.no_tls_proto = Config::PROTO_HTTP1_1;
|
||||||
#ifdef HAVE_SPDYLAY
|
|
||||||
} else if (util::strieq_l("spdy/2", proto)) {
|
|
||||||
config.no_tls_proto = Config::PROTO_SPDY2;
|
|
||||||
} else if (util::strieq_l("spdy/3", proto)) {
|
|
||||||
config.no_tls_proto = Config::PROTO_SPDY3;
|
|
||||||
} else if (util::strieq_l("spdy/3.1", proto)) {
|
|
||||||
config.no_tls_proto = Config::PROTO_SPDY3_1;
|
|
||||||
#endif // HAVE_SPDYLAY
|
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "-p: unsupported protocol " << proto << std::endl;
|
std::cerr << "-p: unsupported protocol " << proto << std::endl;
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
|
@ -2507,7 +2455,6 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
config.h1reqs.reserve(reqlines.size());
|
config.h1reqs.reserve(reqlines.size());
|
||||||
config.nva.reserve(reqlines.size());
|
config.nva.reserve(reqlines.size());
|
||||||
config.nv.reserve(reqlines.size());
|
|
||||||
|
|
||||||
for (auto &req : reqlines) {
|
for (auto &req : reqlines) {
|
||||||
// For HTTP/1.1
|
// For HTTP/1.1
|
||||||
|
@ -2557,35 +2504,6 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
config.nva.push_back(std::move(nva));
|
config.nva.push_back(std::move(nva));
|
||||||
|
|
||||||
// For spdylay
|
|
||||||
std::vector<const char *> cva;
|
|
||||||
// 3 for :path, :version, and possible content-length, 1 for
|
|
||||||
// terminal nullptr
|
|
||||||
cva.reserve(2 * (3 + shared_nva.size()) + 1);
|
|
||||||
|
|
||||||
cva.push_back(":path");
|
|
||||||
cva.push_back(req.c_str());
|
|
||||||
|
|
||||||
for (auto &nv : shared_nva) {
|
|
||||||
if (nv.name == ":authority") {
|
|
||||||
cva.push_back(":host");
|
|
||||||
} else {
|
|
||||||
cva.push_back(nv.name.c_str());
|
|
||||||
}
|
|
||||||
cva.push_back(nv.value.c_str());
|
|
||||||
}
|
|
||||||
cva.push_back(":version");
|
|
||||||
cva.push_back("HTTP/1.1");
|
|
||||||
|
|
||||||
if (!content_length_str.empty()) {
|
|
||||||
cva.push_back("content-length");
|
|
||||||
cva.push_back(content_length_str.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
cva.push_back(nullptr);
|
|
||||||
|
|
||||||
config.nv.push_back(std::move(cva));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't DOS our server!
|
// Don't DOS our server!
|
||||||
|
|
|
@ -64,7 +64,6 @@ struct Worker;
|
||||||
|
|
||||||
struct Config {
|
struct Config {
|
||||||
std::vector<std::vector<nghttp2_nv>> nva;
|
std::vector<std::vector<nghttp2_nv>> nva;
|
||||||
std::vector<std::vector<const char *>> nv;
|
|
||||||
std::vector<std::string> h1reqs;
|
std::vector<std::string> h1reqs;
|
||||||
std::vector<ev_tstamp> timings;
|
std::vector<ev_tstamp> timings;
|
||||||
nghttp2::Headers custom_headers;
|
nghttp2::Headers custom_headers;
|
||||||
|
@ -93,13 +92,7 @@ struct Config {
|
||||||
ev_tstamp conn_active_timeout;
|
ev_tstamp conn_active_timeout;
|
||||||
// amount of time to wait after the last request is made on a connection
|
// amount of time to wait after the last request is made on a connection
|
||||||
ev_tstamp conn_inactivity_timeout;
|
ev_tstamp conn_inactivity_timeout;
|
||||||
enum {
|
enum { PROTO_HTTP2, PROTO_HTTP1_1 } no_tls_proto;
|
||||||
PROTO_HTTP2,
|
|
||||||
PROTO_SPDY2,
|
|
||||||
PROTO_SPDY3,
|
|
||||||
PROTO_SPDY3_1,
|
|
||||||
PROTO_HTTP1_1
|
|
||||||
} no_tls_proto;
|
|
||||||
uint32_t header_table_size;
|
uint32_t header_table_size;
|
||||||
uint32_t encoder_header_table_size;
|
uint32_t encoder_header_table_size;
|
||||||
// file descriptor for upload data
|
// file descriptor for upload data
|
||||||
|
|
|
@ -1,289 +0,0 @@
|
||||||
/*
|
|
||||||
* nghttp2 - HTTP/2 C Library
|
|
||||||
*
|
|
||||||
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
* a copy of this software and associated documentation files (the
|
|
||||||
* "Software"), to deal in the Software without restriction, including
|
|
||||||
* without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
* permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
* the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be
|
|
||||||
* included in all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#include "h2load_spdy_session.h"
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <cerrno>
|
|
||||||
|
|
||||||
#include "h2load.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
using namespace nghttp2;
|
|
||||||
|
|
||||||
namespace h2load {
|
|
||||||
|
|
||||||
SpdySession::SpdySession(Client *client, uint16_t spdy_version)
|
|
||||||
: client_(client), session_(nullptr), spdy_version_(spdy_version) {}
|
|
||||||
|
|
||||||
SpdySession::~SpdySession() { spdylay_session_del(session_); }
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
void before_ctrl_send_callback(spdylay_session *session,
|
|
||||||
spdylay_frame_type type, spdylay_frame *frame,
|
|
||||||
void *user_data) {
|
|
||||||
auto client = static_cast<Client *>(user_data);
|
|
||||||
if (type != SPDYLAY_SYN_STREAM) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
client->on_request(frame->syn_stream.stream_id);
|
|
||||||
auto req_stat = client->get_req_stat(frame->syn_stream.stream_id);
|
|
||||||
client->record_request_time(req_stat);
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
|
|
||||||
spdylay_frame *frame, void *user_data) {
|
|
||||||
auto client = static_cast<Client *>(user_data);
|
|
||||||
if (type != SPDYLAY_SYN_REPLY) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (auto p = frame->syn_reply.nv; *p; p += 2) {
|
|
||||||
auto name = *p;
|
|
||||||
auto value = *(p + 1);
|
|
||||||
auto namelen = strlen(name);
|
|
||||||
auto valuelen = strlen(value);
|
|
||||||
client->on_header(frame->syn_reply.stream_id,
|
|
||||||
reinterpret_cast<const uint8_t *>(name), namelen,
|
|
||||||
reinterpret_cast<const uint8_t *>(value), valuelen);
|
|
||||||
client->worker->stats.bytes_head_decomp += namelen + valuelen;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Strictly speaking, we have to subtract 2 (unused field) if SPDY
|
|
||||||
// version is 2. But it is already deprecated, and we don't do
|
|
||||||
// extra work for it.
|
|
||||||
client->worker->stats.bytes_head += frame->syn_reply.hd.length - 4;
|
|
||||||
|
|
||||||
if (frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN) {
|
|
||||||
client->record_ttfb();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags,
|
|
||||||
int32_t stream_id, const uint8_t *data,
|
|
||||||
size_t len, void *user_data) {
|
|
||||||
auto client = static_cast<Client *>(user_data);
|
|
||||||
|
|
||||||
client->record_ttfb();
|
|
||||||
client->worker->stats.bytes_body += len;
|
|
||||||
|
|
||||||
auto spdy_session = static_cast<SpdySession *>(client->session.get());
|
|
||||||
|
|
||||||
spdy_session->handle_window_update(stream_id, len);
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
void on_stream_close_callback(spdylay_session *session, int32_t stream_id,
|
|
||||||
spdylay_status_code status_code,
|
|
||||||
void *user_data) {
|
|
||||||
auto client = static_cast<Client *>(user_data);
|
|
||||||
client->on_stream_close(stream_id, status_code == SPDYLAY_OK);
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
ssize_t send_callback(spdylay_session *session, const uint8_t *data,
|
|
||||||
size_t length, int flags, void *user_data) {
|
|
||||||
auto client = static_cast<Client *>(user_data);
|
|
||||||
auto &wb = client->wb;
|
|
||||||
|
|
||||||
if (wb.rleft() >= BACKOFF_WRITE_BUFFER_THRES) {
|
|
||||||
return SPDYLAY_ERR_WOULDBLOCK;
|
|
||||||
}
|
|
||||||
|
|
||||||
return wb.append(data, length);
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
ssize_t file_read_callback(spdylay_session *session, int32_t stream_id,
|
|
||||||
uint8_t *buf, size_t length, int *eof,
|
|
||||||
spdylay_data_source *source, void *user_data) {
|
|
||||||
auto client = static_cast<Client *>(user_data);
|
|
||||||
auto config = client->worker->config;
|
|
||||||
auto req_stat = client->get_req_stat(stream_id);
|
|
||||||
|
|
||||||
ssize_t nread;
|
|
||||||
while ((nread = pread(config->data_fd, buf, length, req_stat->data_offset)) ==
|
|
||||||
-1 &&
|
|
||||||
errno == EINTR)
|
|
||||||
;
|
|
||||||
|
|
||||||
if (nread == -1) {
|
|
||||||
return SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
req_stat->data_offset += nread;
|
|
||||||
|
|
||||||
if (nread == 0 || req_stat->data_offset == config->data_length) {
|
|
||||||
*eof = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nread;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void SpdySession::on_connect() {
|
|
||||||
spdylay_session_callbacks callbacks = {0};
|
|
||||||
callbacks.send_callback = send_callback;
|
|
||||||
callbacks.before_ctrl_send_callback = before_ctrl_send_callback;
|
|
||||||
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
|
|
||||||
callbacks.on_stream_close_callback = on_stream_close_callback;
|
|
||||||
callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback;
|
|
||||||
|
|
||||||
spdylay_session_client_new(&session_, spdy_version_, &callbacks, client_);
|
|
||||||
|
|
||||||
int val = 1;
|
|
||||||
spdylay_session_set_option(session_, SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE, &val,
|
|
||||||
sizeof(val));
|
|
||||||
|
|
||||||
spdylay_settings_entry iv;
|
|
||||||
iv.settings_id = SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE;
|
|
||||||
iv.flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;
|
|
||||||
iv.value = (1 << client_->worker->config->window_bits);
|
|
||||||
spdylay_submit_settings(session_, SPDYLAY_FLAG_SETTINGS_NONE, &iv, 1);
|
|
||||||
|
|
||||||
auto config = client_->worker->config;
|
|
||||||
|
|
||||||
if (spdy_version_ >= SPDYLAY_PROTO_SPDY3_1 &&
|
|
||||||
config->connection_window_bits > 16) {
|
|
||||||
auto delta =
|
|
||||||
(1 << config->connection_window_bits) - SPDYLAY_INITIAL_WINDOW_SIZE;
|
|
||||||
spdylay_submit_window_update(session_, 0, delta);
|
|
||||||
}
|
|
||||||
|
|
||||||
client_->signal_write();
|
|
||||||
}
|
|
||||||
|
|
||||||
int SpdySession::submit_request() {
|
|
||||||
int rv;
|
|
||||||
auto config = client_->worker->config;
|
|
||||||
auto &nv = config->nv[client_->reqidx++];
|
|
||||||
|
|
||||||
if (client_->reqidx == config->nv.size()) {
|
|
||||||
client_->reqidx = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
spdylay_data_provider prd{{0}, file_read_callback};
|
|
||||||
|
|
||||||
rv = spdylay_submit_request(session_, 0, nv.data(),
|
|
||||||
config->data_fd == -1 ? nullptr : &prd, nullptr);
|
|
||||||
|
|
||||||
if (rv != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SpdySession::on_read(const uint8_t *data, size_t len) {
|
|
||||||
auto rv = spdylay_session_mem_recv(session_, data, len);
|
|
||||||
if (rv < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(static_cast<size_t>(rv) == len);
|
|
||||||
|
|
||||||
if (spdylay_session_want_read(session_) == 0 &&
|
|
||||||
spdylay_session_want_write(session_) == 0 && client_->wb.rleft() == 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
client_->signal_write();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SpdySession::on_write() {
|
|
||||||
auto rv = spdylay_session_send(session_);
|
|
||||||
if (rv != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spdylay_session_want_read(session_) == 0 &&
|
|
||||||
spdylay_session_want_write(session_) == 0 && client_->wb.rleft() == 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpdySession::terminate() {
|
|
||||||
spdylay_session_fail_session(session_, SPDYLAY_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
int32_t determine_window_update_transmission(spdylay_session *session,
|
|
||||||
int32_t stream_id,
|
|
||||||
size_t window_bits) {
|
|
||||||
int32_t recv_length;
|
|
||||||
|
|
||||||
if (stream_id == 0) {
|
|
||||||
recv_length = spdylay_session_get_recv_data_length(session);
|
|
||||||
} else {
|
|
||||||
recv_length =
|
|
||||||
spdylay_session_get_stream_recv_data_length(session, stream_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto window_size = 1 << window_bits;
|
|
||||||
|
|
||||||
if (recv_length != -1 && recv_length >= window_size / 2) {
|
|
||||||
return recv_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void SpdySession::handle_window_update(int32_t stream_id, size_t recvlen) {
|
|
||||||
auto config = client_->worker->config;
|
|
||||||
size_t connection_window_bits;
|
|
||||||
|
|
||||||
if (config->connection_window_bits > 16) {
|
|
||||||
connection_window_bits = config->connection_window_bits;
|
|
||||||
} else {
|
|
||||||
connection_window_bits = 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto delta =
|
|
||||||
determine_window_update_transmission(session_, 0, connection_window_bits);
|
|
||||||
if (delta > 0) {
|
|
||||||
spdylay_submit_window_update(session_, 0, delta);
|
|
||||||
}
|
|
||||||
|
|
||||||
delta = determine_window_update_transmission(session_, stream_id,
|
|
||||||
config->window_bits);
|
|
||||||
if (delta > 0) {
|
|
||||||
spdylay_submit_window_update(session_, stream_id, delta);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t SpdySession::max_concurrent_streams() {
|
|
||||||
return (size_t)client_->worker->config->max_concurrent_streams;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace h2load
|
|
|
@ -1,58 +0,0 @@
|
||||||
/*
|
|
||||||
* nghttp2 - HTTP/2 C Library
|
|
||||||
*
|
|
||||||
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
* a copy of this software and associated documentation files (the
|
|
||||||
* "Software"), to deal in the Software without restriction, including
|
|
||||||
* without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
* permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
* the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be
|
|
||||||
* included in all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#ifndef H2LOAD_SPDY_SESSION_H
|
|
||||||
#define H2LOAD_SPDY_SESSION_H
|
|
||||||
|
|
||||||
#include "h2load_session.h"
|
|
||||||
|
|
||||||
#include <spdylay/spdylay.h>
|
|
||||||
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
namespace h2load {
|
|
||||||
|
|
||||||
struct Client;
|
|
||||||
|
|
||||||
class SpdySession : public Session {
|
|
||||||
public:
|
|
||||||
SpdySession(Client *client, uint16_t spdy_version);
|
|
||||||
virtual ~SpdySession();
|
|
||||||
virtual void on_connect();
|
|
||||||
virtual int submit_request();
|
|
||||||
virtual int on_read(const uint8_t *data, size_t len);
|
|
||||||
virtual int on_write();
|
|
||||||
virtual void terminate();
|
|
||||||
virtual size_t max_concurrent_streams();
|
|
||||||
void handle_window_update(int32_t stream_id, size_t recvlen);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Client *client_;
|
|
||||||
spdylay_session *session_;
|
|
||||||
uint16_t spdy_version_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace h2load
|
|
||||||
|
|
||||||
#endif // H2LOAD_SPDY_SESSION_H
|
|
48
src/shrpx.cc
48
src/shrpx.cc
|
@ -1384,11 +1384,8 @@ bool conf_exists(const char *path) {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr auto DEFAULT_NPN_LIST = StringRef::from_lit("h2,h2-16,h2-14,"
|
constexpr auto DEFAULT_NPN_LIST =
|
||||||
#ifdef HAVE_SPDYLAY
|
StringRef::from_lit("h2,h2-16,h2-14,http/1.1");
|
||||||
"spdy/3.1,"
|
|
||||||
#endif // HAVE_SPDYLAY
|
|
||||||
"http/1.1");
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -1490,13 +1487,11 @@ void fill_default_config(Config *config) {
|
||||||
timeoutconf.settings = 10_s;
|
timeoutconf.settings = 10_s;
|
||||||
}
|
}
|
||||||
|
|
||||||
// window size for HTTP/2 and SPDY upstream connection per stream.
|
// window size for HTTP/2 upstream connection per stream. 2**16-1
|
||||||
// 2**16-1 = 64KiB-1, which is HTTP/2 default. Please note that
|
// = 64KiB-1, which is HTTP/2 default.
|
||||||
// SPDY/3 default is 64KiB.
|
|
||||||
upstreamconf.window_size = 64_k - 1;
|
upstreamconf.window_size = 64_k - 1;
|
||||||
// HTTP/2 and SPDY/3.1 has connection-level flow control. The
|
// HTTP/2 has connection-level flow control. The default window
|
||||||
// default window size for HTTP/2 is 64KiB - 1. SPDY/3's default
|
// size for HTTP/2 is 64KiB - 1.
|
||||||
// is 64KiB
|
|
||||||
upstreamconf.connection_window_size = 64_k - 1;
|
upstreamconf.connection_window_size = 64_k - 1;
|
||||||
upstreamconf.max_concurrent_streams = 100;
|
upstreamconf.max_concurrent_streams = 100;
|
||||||
|
|
||||||
|
@ -1624,7 +1619,7 @@ void print_version(std::ostream &out) {
|
||||||
namespace {
|
namespace {
|
||||||
void print_usage(std::ostream &out) {
|
void print_usage(std::ostream &out) {
|
||||||
out << R"(Usage: nghttpx [OPTIONS]... [<PRIVATE_KEY> <CERT>]
|
out << R"(Usage: nghttpx [OPTIONS]... [<PRIVATE_KEY> <CERT>]
|
||||||
A reverse proxy for HTTP/2, HTTP/1 and SPDY.)"
|
A reverse proxy for HTTP/2, and HTTP/1.)"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -1990,8 +1985,7 @@ Performance:
|
||||||
|
|
||||||
Timeout:
|
Timeout:
|
||||||
--frontend-http2-read-timeout=<DURATION>
|
--frontend-http2-read-timeout=<DURATION>
|
||||||
Specify read timeout for HTTP/2 and SPDY frontend
|
Specify read timeout for HTTP/2 frontend connection.
|
||||||
connection.
|
|
||||||
Default: )"
|
Default: )"
|
||||||
<< util::duration_str(config->conn.upstream.timeout.http2_read) << R"(
|
<< util::duration_str(config->conn.upstream.timeout.http2_read) << R"(
|
||||||
--frontend-read-timeout=<DURATION>
|
--frontend-read-timeout=<DURATION>
|
||||||
|
@ -2008,13 +2002,13 @@ Timeout:
|
||||||
Default: )"
|
Default: )"
|
||||||
<< util::duration_str(config->conn.upstream.timeout.idle_read) << R"(
|
<< util::duration_str(config->conn.upstream.timeout.idle_read) << R"(
|
||||||
--stream-read-timeout=<DURATION>
|
--stream-read-timeout=<DURATION>
|
||||||
Specify read timeout for HTTP/2 and SPDY streams. 0
|
Specify read timeout for HTTP/2 streams. 0 means no
|
||||||
means no timeout.
|
timeout.
|
||||||
Default: )"
|
Default: )"
|
||||||
<< util::duration_str(config->http2.timeout.stream_read) << R"(
|
<< util::duration_str(config->http2.timeout.stream_read) << R"(
|
||||||
--stream-write-timeout=<DURATION>
|
--stream-write-timeout=<DURATION>
|
||||||
Specify write timeout for HTTP/2 and SPDY streams. 0
|
Specify write timeout for HTTP/2 streams. 0 means no
|
||||||
means no timeout.
|
timeout.
|
||||||
Default: )"
|
Default: )"
|
||||||
<< util::duration_str(config->http2.timeout.stream_write) << R"(
|
<< util::duration_str(config->http2.timeout.stream_write) << R"(
|
||||||
--backend-read-timeout=<DURATION>
|
--backend-read-timeout=<DURATION>
|
||||||
|
@ -2351,10 +2345,10 @@ SSL/TLS:
|
||||||
consider to use --client-no-http2-cipher-black-list
|
consider to use --client-no-http2-cipher-black-list
|
||||||
option. But be aware its implications.
|
option. But be aware its implications.
|
||||||
|
|
||||||
HTTP/2 and SPDY:
|
HTTP/2:
|
||||||
-c, --frontend-http2-max-concurrent-streams=<N>
|
-c, --frontend-http2-max-concurrent-streams=<N>
|
||||||
Set the maximum number of the concurrent streams in one
|
Set the maximum number of the concurrent streams in one
|
||||||
frontend HTTP/2 and SPDY session.
|
frontend HTTP/2 session.
|
||||||
Default: )"
|
Default: )"
|
||||||
<< config->http2.upstream.max_concurrent_streams << R"(
|
<< config->http2.upstream.max_concurrent_streams << R"(
|
||||||
--backend-http2-max-concurrent-streams=<N>
|
--backend-http2-max-concurrent-streams=<N>
|
||||||
|
@ -2365,14 +2359,13 @@ HTTP/2 and SPDY:
|
||||||
Default: )"
|
Default: )"
|
||||||
<< config->http2.downstream.max_concurrent_streams << R"(
|
<< config->http2.downstream.max_concurrent_streams << R"(
|
||||||
--frontend-http2-window-size=<SIZE>
|
--frontend-http2-window-size=<SIZE>
|
||||||
Sets the per-stream initial window size of HTTP/2 and
|
Sets the per-stream initial window size of HTTP/2
|
||||||
SPDY frontend connection.
|
frontend connection.
|
||||||
Default: )"
|
Default: )"
|
||||||
<< config->http2.upstream.window_size << R"(
|
<< config->http2.upstream.window_size << R"(
|
||||||
--frontend-http2-connection-window-size=<SIZE>
|
--frontend-http2-connection-window-size=<SIZE>
|
||||||
Sets the per-connection window size of HTTP/2 and SPDY
|
Sets the per-connection window size of HTTP/2 frontend
|
||||||
frontend connection. For SPDY connection, the value
|
connection.
|
||||||
less than 64KiB is rounded up to 64KiB.
|
|
||||||
Default: )"
|
Default: )"
|
||||||
<< config->http2.upstream.connection_window_size << R"(
|
<< config->http2.upstream.connection_window_size << R"(
|
||||||
--backend-http2-window-size=<SIZE>
|
--backend-http2-window-size=<SIZE>
|
||||||
|
@ -2398,8 +2391,7 @@ HTTP/2 and SPDY:
|
||||||
It is also supported if both frontend and backend are
|
It is also supported if both frontend and backend are
|
||||||
HTTP/2 in default mode. In this case, server push from
|
HTTP/2 in default mode. In this case, server push from
|
||||||
backend session is relayed to frontend, and server push
|
backend session is relayed to frontend, and server push
|
||||||
via Link header field is also supported. SPDY frontend
|
via Link header field is also supported.
|
||||||
does not support server push.
|
|
||||||
--frontend-http2-optimize-write-buffer-size
|
--frontend-http2-optimize-write-buffer-size
|
||||||
(Experimental) Enable write buffer size optimization in
|
(Experimental) Enable write buffer size optimization in
|
||||||
frontend HTTP/2 TLS connection. This optimization aims
|
frontend HTTP/2 TLS connection. This optimization aims
|
||||||
|
@ -2454,7 +2446,7 @@ HTTP/2 and SPDY:
|
||||||
|
|
||||||
Mode:
|
Mode:
|
||||||
(default mode)
|
(default mode)
|
||||||
Accept HTTP/2, SPDY and HTTP/1.1 over SSL/TLS. "no-tls"
|
Accept HTTP/2, and HTTP/1.1 over SSL/TLS. "no-tls"
|
||||||
parameter is used in --frontend option, accept HTTP/2
|
parameter is used in --frontend option, accept HTTP/2
|
||||||
and HTTP/1.1 over cleartext TCP. The incoming HTTP/1.1
|
and HTTP/1.1 over cleartext TCP. The incoming HTTP/1.1
|
||||||
connection can be upgraded to HTTP/2 through HTTP
|
connection can be upgraded to HTTP/2 through HTTP
|
||||||
|
|
|
@ -51,9 +51,6 @@
|
||||||
#include "shrpx_api_downstream_connection.h"
|
#include "shrpx_api_downstream_connection.h"
|
||||||
#include "shrpx_health_monitor_downstream_connection.h"
|
#include "shrpx_health_monitor_downstream_connection.h"
|
||||||
#include "shrpx_log.h"
|
#include "shrpx_log.h"
|
||||||
#ifdef HAVE_SPDYLAY
|
|
||||||
#include "shrpx_spdy_upstream.h"
|
|
||||||
#endif // HAVE_SPDYLAY
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "template.h"
|
#include "template.h"
|
||||||
#include "tls.h"
|
#include "tls.h"
|
||||||
|
@ -608,36 +605,6 @@ int ClientHandler::validate_next_proto() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_SPDYLAY
|
|
||||||
auto spdy_version = spdylay_npn_get_version(proto.byte(), proto.size());
|
|
||||||
if (spdy_version) {
|
|
||||||
upstream_ = make_unique<SpdyUpstream>(spdy_version, this);
|
|
||||||
|
|
||||||
switch (spdy_version) {
|
|
||||||
case SPDYLAY_PROTO_SPDY2:
|
|
||||||
alpn_ = StringRef::from_lit("spdy/2");
|
|
||||||
break;
|
|
||||||
case SPDYLAY_PROTO_SPDY3:
|
|
||||||
alpn_ = StringRef::from_lit("spdy/3");
|
|
||||||
break;
|
|
||||||
case SPDYLAY_PROTO_SPDY3_1:
|
|
||||||
alpn_ = StringRef::from_lit("spdy/3.1");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
alpn_ = StringRef::from_lit("spdy/unknown");
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point, input buffer is already filled with some bytes.
|
|
||||||
// The read callback is not called until new data come. So consume
|
|
||||||
// input buffer here.
|
|
||||||
if (on_read() != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif // HAVE_SPDYLAY
|
|
||||||
|
|
||||||
if (proto == StringRef::from_lit("http/1.1")) {
|
if (proto == StringRef::from_lit("http/1.1")) {
|
||||||
upstream_ = make_unique<HttpsUpstream>(this);
|
upstream_ = make_unique<HttpsUpstream>(this);
|
||||||
alpn_ = StringRef::from_lit("http/1.1");
|
alpn_ = StringRef::from_lit("http/1.1");
|
||||||
|
|
|
@ -2630,8 +2630,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make 16 bits to the HTTP/2 default 64KiB - 1. This is the same
|
// Make 16 bits to the HTTP/2 default 64KiB - 1. This is the same
|
||||||
// behaviour of previous code. For SPDY, we adjust this value in
|
// behaviour of previous code.
|
||||||
// SpdyUpstream to look like the SPDY default.
|
|
||||||
*resp = (1 << n) - 1;
|
*resp = (1 << n) - 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -469,7 +469,7 @@ private:
|
||||||
Upstream *upstream_;
|
Upstream *upstream_;
|
||||||
std::unique_ptr<DownstreamConnection> dconn_;
|
std::unique_ptr<DownstreamConnection> dconn_;
|
||||||
|
|
||||||
// only used by HTTP/2 or SPDY upstream
|
// only used by HTTP/2 upstream
|
||||||
BlockedLink *blocked_link_;
|
BlockedLink *blocked_link_;
|
||||||
// The backend address used to fulfill this request. These are for
|
// The backend address used to fulfill this request. These are for
|
||||||
// logging purpose.
|
// logging purpose.
|
||||||
|
@ -492,7 +492,7 @@ private:
|
||||||
int request_state_;
|
int request_state_;
|
||||||
// response state
|
// response state
|
||||||
int response_state_;
|
int response_state_;
|
||||||
// only used by HTTP/2 or SPDY upstream
|
// only used by HTTP/2 upstream
|
||||||
int dispatch_state_;
|
int dispatch_state_;
|
||||||
// true if the connection is upgraded (HTTP Upgrade or CONNECT),
|
// true if the connection is upgraded (HTTP Upgrade or CONNECT),
|
||||||
// excluding upgrade to HTTP/2.
|
// excluding upgrade to HTTP/2.
|
||||||
|
|
|
@ -270,8 +270,8 @@ int Http2Session::disconnect(bool hard) {
|
||||||
// When deleting Http2DownstreamConnection, it calls this object's
|
// When deleting Http2DownstreamConnection, it calls this object's
|
||||||
// remove_downstream_connection(). The multiple
|
// remove_downstream_connection(). The multiple
|
||||||
// Http2DownstreamConnection objects belong to the same
|
// Http2DownstreamConnection objects belong to the same
|
||||||
// ClientHandler object if upstream is h2 or SPDY. So be careful
|
// ClientHandler object if upstream is h2. So be careful when you
|
||||||
// when you delete ClientHandler here.
|
// delete ClientHandler here.
|
||||||
//
|
//
|
||||||
// We allow creating new pending Http2DownstreamConnection with this
|
// We allow creating new pending Http2DownstreamConnection with this
|
||||||
// object. Upstream::on_downstream_reset() may add
|
// object. Upstream::on_downstream_reset() may add
|
||||||
|
@ -1520,7 +1520,7 @@ int on_frame_not_send_callback(nghttp2_session *session,
|
||||||
|
|
||||||
if (upstream->on_downstream_reset(downstream, false)) {
|
if (upstream->on_downstream_reset(downstream, false)) {
|
||||||
// This should be done for h1 upstream only. Deleting
|
// This should be done for h1 upstream only. Deleting
|
||||||
// ClientHandler for h2 or SPDY upstream may lead to crash.
|
// ClientHandler for h2 upstream may lead to crash.
|
||||||
delete upstream->get_client_handler();
|
delete upstream->get_client_handler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,113 +0,0 @@
|
||||||
/*
|
|
||||||
* nghttp2 - HTTP/2 C Library
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
* a copy of this software and associated documentation files (the
|
|
||||||
* "Software"), to deal in the Software without restriction, including
|
|
||||||
* without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
* permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
* the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be
|
|
||||||
* included in all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#ifndef SHRPX_SPDY_UPSTREAM_H
|
|
||||||
#define SHRPX_SPDY_UPSTREAM_H
|
|
||||||
|
|
||||||
#include "shrpx.h"
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <ev.h>
|
|
||||||
|
|
||||||
#include <spdylay/spdylay.h>
|
|
||||||
|
|
||||||
#include "shrpx_upstream.h"
|
|
||||||
#include "shrpx_downstream_queue.h"
|
|
||||||
#include "memchunk.h"
|
|
||||||
#include "buffer.h"
|
|
||||||
|
|
||||||
namespace shrpx {
|
|
||||||
|
|
||||||
class ClientHandler;
|
|
||||||
|
|
||||||
class SpdyUpstream : public Upstream {
|
|
||||||
public:
|
|
||||||
SpdyUpstream(uint16_t version, ClientHandler *handler);
|
|
||||||
virtual ~SpdyUpstream();
|
|
||||||
virtual int on_read();
|
|
||||||
virtual int on_write();
|
|
||||||
virtual int on_timeout(Downstream *downstream);
|
|
||||||
virtual int on_downstream_abort_request(Downstream *downstream,
|
|
||||||
unsigned int status_code);
|
|
||||||
virtual int
|
|
||||||
on_downstream_abort_request_with_https_redirect(Downstream *downstream);
|
|
||||||
virtual ClientHandler *get_client_handler() const;
|
|
||||||
virtual int downstream_read(DownstreamConnection *dconn);
|
|
||||||
virtual int downstream_write(DownstreamConnection *dconn);
|
|
||||||
virtual int downstream_eof(DownstreamConnection *dconn);
|
|
||||||
virtual int downstream_error(DownstreamConnection *dconn, int events);
|
|
||||||
Downstream *add_pending_downstream(int32_t stream_id);
|
|
||||||
void remove_downstream(Downstream *downstream);
|
|
||||||
|
|
||||||
int rst_stream(Downstream *downstream, int status_code);
|
|
||||||
int error_reply(Downstream *downstream, unsigned int status_code);
|
|
||||||
|
|
||||||
virtual void pause_read(IOCtrlReason reason);
|
|
||||||
virtual int resume_read(IOCtrlReason reason, Downstream *downstream,
|
|
||||||
size_t consumed);
|
|
||||||
|
|
||||||
virtual int on_downstream_header_complete(Downstream *downstream);
|
|
||||||
virtual int on_downstream_body(Downstream *downstream, const uint8_t *data,
|
|
||||||
size_t len, bool flush);
|
|
||||||
virtual int on_downstream_body_complete(Downstream *downstream);
|
|
||||||
|
|
||||||
virtual void on_handler_delete();
|
|
||||||
virtual int on_downstream_reset(Downstream *downstream, bool no_retry);
|
|
||||||
|
|
||||||
virtual int send_reply(Downstream *downstream, const uint8_t *body,
|
|
||||||
size_t bodylen);
|
|
||||||
virtual int initiate_push(Downstream *downstream, const StringRef &uri);
|
|
||||||
virtual int response_riovec(struct iovec *iov, int iovcnt) const;
|
|
||||||
virtual void response_drain(size_t n);
|
|
||||||
virtual bool response_empty() const;
|
|
||||||
|
|
||||||
virtual Downstream *on_downstream_push_promise(Downstream *downstream,
|
|
||||||
int32_t promised_stream_id);
|
|
||||||
virtual int
|
|
||||||
on_downstream_push_promise_complete(Downstream *downstream,
|
|
||||||
Downstream *promised_downstream);
|
|
||||||
virtual bool push_enabled() const;
|
|
||||||
virtual void cancel_premature_downstream(Downstream *promised_downstream);
|
|
||||||
|
|
||||||
bool get_flow_control() const;
|
|
||||||
|
|
||||||
int consume(int32_t stream_id, size_t len);
|
|
||||||
|
|
||||||
void start_downstream(Downstream *downstream);
|
|
||||||
void initiate_downstream(Downstream *downstream);
|
|
||||||
|
|
||||||
DefaultMemchunks *get_response_buf();
|
|
||||||
|
|
||||||
private:
|
|
||||||
DefaultMemchunks wb_;
|
|
||||||
DownstreamQueue downstream_queue_;
|
|
||||||
ClientHandler *handler_;
|
|
||||||
spdylay_session *session_;
|
|
||||||
bool flow_control_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace shrpx
|
|
||||||
|
|
||||||
#endif // SHRPX_SPDY_UPSTREAM_H
|
|
|
@ -51,10 +51,6 @@
|
||||||
|
|
||||||
#include <nghttp2/nghttp2.h>
|
#include <nghttp2/nghttp2.h>
|
||||||
|
|
||||||
#ifdef HAVE_SPDYLAY
|
|
||||||
#include <spdylay/spdylay.h>
|
|
||||||
#endif // HAVE_SPDYLAY
|
|
||||||
|
|
||||||
#include "shrpx_log.h"
|
#include "shrpx_log.h"
|
||||||
#include "shrpx_client_handler.h"
|
#include "shrpx_client_handler.h"
|
||||||
#include "shrpx_config.h"
|
#include "shrpx_config.h"
|
||||||
|
|
Loading…
Reference in New Issue