commit
cff9ebe1dd
82
README.rst
82
README.rst
|
@ -4,10 +4,10 @@ nghttp2 - HTTP/2 C Library
|
|||
This is an implementation of the Hypertext Transfer Protocol version 2
|
||||
in C.
|
||||
|
||||
The framing layer of HTTP/2 is implemented as a reusable C
|
||||
library. On top of that, we have implemented an HTTP/2 client, server
|
||||
and proxy. We have also developed load test and benchmarking tools for
|
||||
HTTP/2 and SPDY.
|
||||
The framing layer of HTTP/2 is implemented as a reusable C library.
|
||||
On top of that, we have implemented an HTTP/2 client, server and
|
||||
proxy. We have also developed load test and benchmarking tools for
|
||||
HTTP/2.
|
||||
|
||||
An HPACK encoder and decoder are available as a public API.
|
||||
|
||||
|
@ -34,8 +34,8 @@ implementation.
|
|||
|
||||
* https://nghttp2.org/ (TLS + ALPN/NPN)
|
||||
|
||||
This endpoint supports ``h2``, ``h2-16``, ``h2-14``, ``spdy/3.1``
|
||||
and ``http/1.1`` via ALPN/NPN and requires TLSv1.2 for HTTP/2
|
||||
This endpoint supports ``h2``, ``h2-16``, ``h2-14``, and
|
||||
``http/1.1`` via ALPN/NPN and requires TLSv1.2 for HTTP/2
|
||||
connection.
|
||||
|
||||
* 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
|
||||
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
|
||||
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 \
|
||||
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
|
||||
|
||||
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
|
||||
<https://github.com/mruby/mruby>`_ is required. We need to build
|
||||
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/websocket
|
||||
* https://github.com/tatsuhiro-t/go-nghttp2
|
||||
* https://github.com/tatsuhiro-t/spdy
|
||||
|
||||
To download the above packages, after settings ``GOPATH``, run the
|
||||
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.
|
||||
|
||||
.. 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
|
||||
---------------------------------
|
||||
|
||||
|
@ -755,7 +737,7 @@ information. Here is sample output from ``nghttpd``:
|
|||
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
|
||||
push.
|
||||
|
||||
|
@ -770,31 +752,30 @@ to know how to migrate from earlier releases.
|
|||
``nghttpx`` implements `important performance-oriented features
|
||||
<https://istlsfastyet.com/#server-performance>`_ in TLS, such as
|
||||
session IDs, session tickets (with automatic key rotation), OCSP
|
||||
stapling, dynamic record sizing, ALPN/NPN, forward secrecy and SPDY &
|
||||
HTTP/2. ``nghttpx`` also offers the functionality to share session
|
||||
cache and ticket keys among multiple ``nghttpx`` instances via
|
||||
memcached.
|
||||
stapling, dynamic record sizing, ALPN/NPN, forward secrecy and HTTP/2.
|
||||
``nghttpx`` also offers the functionality to share session cache and
|
||||
ticket keys among multiple ``nghttpx`` instances via memcached.
|
||||
|
||||
``nghttpx`` has 2 operation modes:
|
||||
|
||||
================== ====================== ================ =============
|
||||
Mode option Frontend Backend Note
|
||||
================== ====================== ================ =============
|
||||
default mode HTTP/2, SPDY, 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
|
||||
================== ====================== ================ =============
|
||||
================== ================ ================ =============
|
||||
Mode option Frontend Backend Note
|
||||
================== ================ ================ =============
|
||||
default mode HTTP/2, HTTP/1.1 HTTP/1.1, HTTP/2 Reverse 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
|
||||
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.
|
||||
|
||||
In all modes, the frontend connections are encrypted by SSL/TLS by
|
||||
default. To disable encryption, use the ``no-tls`` keyword in
|
||||
``--frontend`` option. If encryption is disabled, SPDY is disabled in
|
||||
the frontend and incoming HTTP/1.1 connections can be upgraded to
|
||||
HTTP/2 through HTTP Upgrade. On the other hard, backend connections
|
||||
are not encrypted by default. To encrypt backend connections, use
|
||||
``tls`` keyword in ``--backend`` option.
|
||||
``--frontend`` option. If encryption is disabled, incoming HTTP/1.1
|
||||
connections can be upgraded to HTTP/2 through HTTP Upgrade. On the
|
||||
other hard, backend connections are not encrypted by default. To
|
||||
encrypt backend connections, use ``tls`` keyword in ``--backend``
|
||||
option.
|
||||
|
||||
``nghttpx`` supports a configuration file. See the ``--conf`` option and
|
||||
sample configuration file ``nghttpx.conf.sample``.
|
||||
|
@ -804,16 +785,16 @@ server:
|
|||
|
||||
.. code-block:: text
|
||||
|
||||
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1, HTTP/2) --> Web Server
|
||||
[reverse proxy]
|
||||
Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/1.1, HTTP/2) --> Web Server
|
||||
[reverse proxy]
|
||||
|
||||
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
|
||||
|
||||
Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy
|
||||
[secure proxy] (e.g., Squid, ATS)
|
||||
Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy
|
||||
[secure proxy] (e.g., Squid, ATS)
|
||||
|
||||
The ``Client`` in the above example needs to be configured to use
|
||||
``nghttpx`` as secure proxy.
|
||||
|
@ -845,7 +826,7 @@ proxy through an HTTP proxy:
|
|||
|
||||
.. 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 proxy tunnel) (e.g., nghttpx -s)
|
||||
|
@ -853,9 +834,8 @@ proxy through an HTTP proxy:
|
|||
Benchmarking tool
|
||||
-----------------
|
||||
|
||||
The ``h2load`` program is a benchmarking tool for HTTP/2 and SPDY.
|
||||
The SPDY support is enabled if the program was built with the spdylay
|
||||
library. The UI of ``h2load`` is heavily inspired by ``weighttp``
|
||||
The ``h2load`` program is a benchmarking tool for HTTP/2. The UI of
|
||||
``h2load`` is heavily inspired by ``weighttp``
|
||||
(https://github.com/lighttpd/weighttp). The typical usage is as
|
||||
follows:
|
||||
|
||||
|
|
30
configure.ac
30
configure.ac
|
@ -117,11 +117,6 @@ AC_ARG_WITH([jemalloc],
|
|||
[Use jemalloc [default=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],
|
||||
[AS_HELP_STRING([--with-systemd],
|
||||
[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])
|
||||
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
|
||||
have_asio_lib=no
|
||||
|
||||
|
@ -928,7 +903,6 @@ AC_MSG_NOTICE([summary of build options:
|
|||
Libev: ${have_libev} (CFLAGS='${LIBEV_CFLAGS}' LIBS='${LIBEV_LIBS}')
|
||||
Libc-ares ${have_libcares} (CFLAGS='${LIBCARES_CFLAGS}' LIBS='${LIBCARES_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}')
|
||||
Jemalloc: ${have_jemalloc} (LIBS='${JEMALLOC_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}
|
||||
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``
|
||||
is calculated by (1 - ``headers`` / ``decompressed(headers)``) *
|
||||
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
|
||||
numbers.
|
||||
header compression. For HTTP/2, it shows some insightful numbers.
|
||||
data
|
||||
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
|
||||
performance. To set smaller flow control window, use :option:`-w` and
|
||||
:option:`-W` options. For example, use ``-w16 -W16`` to set default
|
||||
window size described in HTTP/2 and SPDY protocol specification.
|
||||
window size described in HTTP/2 protocol specification.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
|
|
@ -3,10 +3,8 @@
|
|||
h2load - HTTP/2 benchmarking tool - HOW-TO
|
||||
==========================================
|
||||
|
||||
:doc:`h2load.1` is benchmarking tool for HTTP/2 and HTTP/1.1. If
|
||||
built with spdylay (http://tatsuhiro-t.github.io/spdylay/) library, it
|
||||
also supports SPDY protocol. It supports SSL/TLS and clear text for
|
||||
all supported protocols.
|
||||
:doc:`h2load.1` is benchmarking tool for HTTP/2 and HTTP/1.1. It
|
||||
supports SSL/TLS and clear text for all supported protocols.
|
||||
|
||||
Compiling from source
|
||||
---------------------
|
||||
|
@ -86,20 +84,18 @@ seconds warming up period:
|
|||
Flow Control
|
||||
------------
|
||||
|
||||
HTTP/2 and SPDY/3 or later employ flow control and it may affect
|
||||
benchmarking results. By default, h2load uses large enough flow
|
||||
control window, which effectively disables flow control. To adjust
|
||||
receiver flow control window size, there are following options:
|
||||
HTTP/2 has flow control and it may affect benchmarking results. By
|
||||
default, h2load uses large enough flow control window, which
|
||||
effectively disables flow control. To adjust receiver flow control
|
||||
window size, there are following options:
|
||||
|
||||
:option:`-w`
|
||||
Sets the stream level initial window size to
|
||||
(2**<N>)-1. For SPDY, 2**<N> is used instead.
|
||||
(2**<N>)-1.
|
||||
|
||||
:option:`-W`
|
||||
Sets the connection level initial window size to
|
||||
(2**<N>)-1. For SPDY, if <N> is strictly less
|
||||
than 16, this option is ignored. Otherwise
|
||||
2**<N> is used for SPDY.
|
||||
(2**<N>)-1.
|
||||
|
||||
Multi-Threading
|
||||
---------------
|
||||
|
|
|
@ -4,10 +4,10 @@ nghttpx - HTTP/2 proxy - HOW-TO
|
|||
===============================
|
||||
|
||||
:doc:`nghttpx.1` is a proxy translating protocols between HTTP/2 and
|
||||
other protocols (e.g., HTTP/1, SPDY). It operates in several modes
|
||||
and each mode may require additional programs to work with. This
|
||||
article describes each operation mode and explains the intended
|
||||
use-cases. It also covers some useful options later.
|
||||
other protocols (e.g., HTTP/1). It operates in several modes and each
|
||||
mode may require additional programs to work with. This article
|
||||
describes each operation mode and explains the intended use-cases. It
|
||||
also covers some useful options later.
|
||||
|
||||
Default mode
|
||||
------------
|
||||
|
@ -15,9 +15,7 @@ Default mode
|
|||
If nghttpx is invoked without :option:`--http2-proxy`, it operates in
|
||||
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
|
||||
as "HTTP/2 router". If nghttpx is linked with spdylay library and
|
||||
frontend connection is SSL/TLS, the frontend also supports SPDY
|
||||
protocol.
|
||||
as "HTTP/2 router".
|
||||
|
||||
By default, frontend connection is encrypted using SSL/TLS. So
|
||||
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.
|
||||
|
||||
To turn off encryption on frontend connection, use ``no-tls`` keyword
|
||||
in :option:`--frontend` option. In this case, SPDY protocol is not
|
||||
available even if spdylay library is linked to nghttpx. HTTP/2 and
|
||||
HTTP/1 are available on the frontend, and an HTTP/1 connection can be
|
||||
upgraded to HTTP/2 using HTTP Upgrade. Starting HTTP/2 connection by
|
||||
sending HTTP/2 connection preface is also supported.
|
||||
in :option:`--frontend` option. HTTP/2 and HTTP/1 are available on
|
||||
the frontend, and an HTTP/1 connection can be upgraded to HTTP/2 using
|
||||
HTTP Upgrade. Starting HTTP/2 connection by sending HTTP/2 connection
|
||||
preface is also supported.
|
||||
|
||||
nghttpx can listen on multiple frontend addresses. This is achieved
|
||||
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
|
||||
Server). HTTP/1 requests must include an absolute URI in request line.
|
||||
|
||||
By default, the frontend connection is encrypted. So this mode is also
|
||||
called secure proxy. If nghttpx is linked with spdylay, it supports
|
||||
SPDY protocols and it works as so called SPDY proxy.
|
||||
By default, the frontend connection is encrypted. So this mode is
|
||||
also called secure proxy.
|
||||
|
||||
To turn off encryption on the frontend connection, use ``no-tls`` keyword
|
||||
in :option:`--frontend` option.
|
||||
|
@ -102,8 +98,8 @@ like this:
|
|||
At the time of this writing, Firefox 41 and Chromium v46 can use
|
||||
nghttpx as HTTP/2 proxy.
|
||||
|
||||
To make Firefox or Chromium use nghttpx as HTTP/2 or SPDY proxy, user
|
||||
has to create proxy.pac script file like this:
|
||||
To make Firefox or Chromium use nghttpx as HTTP/2 proxy, user has to
|
||||
create proxy.pac script file like this:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
GO_FILES = \
|
||||
nghttpx_http1_test.go \
|
||||
nghttpx_http2_test.go \
|
||||
nghttpx_spdy_test.go \
|
||||
server_tester.go
|
||||
|
||||
EXTRA_DIST = \
|
||||
|
@ -43,7 +42,6 @@ EXTRA_DIST = \
|
|||
itprep:
|
||||
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/spdy
|
||||
go get -d -v golang.org/x/net/websocket
|
||||
|
||||
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)/src/includes \
|
||||
-I$(top_srcdir)/third-party \
|
||||
@LIBSPDYLAY_CFLAGS@ \
|
||||
@LIBXML2_CFLAGS@ \
|
||||
@LIBEV_CFLAGS@ \
|
||||
@OPENSSL_CFLAGS@ \
|
||||
|
@ -52,7 +51,6 @@ AM_CPPFLAGS = \
|
|||
LDADD = $(top_builddir)/lib/libnghttp2.la \
|
||||
$(top_builddir)/third-party/libhttp-parser.la \
|
||||
@JEMALLOC_LIBS@ \
|
||||
@LIBSPDYLAY_LIBS@ \
|
||||
@LIBXML2_LIBS@ \
|
||||
@LIBEV_LIBS@ \
|
||||
@OPENSSL_LIBS@ \
|
||||
|
@ -97,10 +95,6 @@ h2load_SOURCES = util.cc util.h \
|
|||
h2load_http2_session.cc h2load_http2_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 = \
|
||||
util.cc util.h http2.cc http2.h timegm.c timegm.h base64.h \
|
||||
app_helper.cc app_helper.h \
|
||||
|
@ -148,10 +142,6 @@ NGHTTPX_SRCS = \
|
|||
buffer.h memchunk.h template.h allocator.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
|
||||
NGHTTPX_SRCS += \
|
||||
shrpx_mruby.cc shrpx_mruby.h \
|
||||
|
|
|
@ -46,19 +46,12 @@
|
|||
#include <future>
|
||||
#include <random>
|
||||
|
||||
#ifdef HAVE_SPDYLAY
|
||||
#include <spdylay/spdylay.h>
|
||||
#endif // HAVE_SPDYLAY
|
||||
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include "http-parser/http_parser.h"
|
||||
|
||||
#include "h2load_http1_session.h"
|
||||
#include "h2load_http2_session.h"
|
||||
#ifdef HAVE_SPDYLAY
|
||||
#include "h2load_spdy_session.h"
|
||||
#endif // HAVE_SPDYLAY
|
||||
#include "tls.h"
|
||||
#include "http2.h"
|
||||
#include "util.h"
|
||||
|
@ -878,14 +871,6 @@ int Client::connection_made() {
|
|||
} else if (util::streq(NGHTTP2_H1_1, proto)) {
|
||||
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
|
||||
// negotiation result.
|
||||
|
@ -930,20 +915,6 @@ int Client::connection_made() {
|
|||
session = make_unique<Http1Session>(this);
|
||||
selected_proto = NGHTTP2_H1_1.str();
|
||||
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:
|
||||
// unreachable
|
||||
assert(0);
|
||||
|
@ -1788,17 +1759,13 @@ void print_version(std::ostream &out) {
|
|||
namespace {
|
||||
void print_usage(std::ostream &out) {
|
||||
out << R"(Usage: h2load [OPTIONS]... [URI]...
|
||||
benchmarking tool for HTTP/2 and SPDY server)"
|
||||
benchmarking tool for HTTP/2 server)"
|
||||
<< std::endl;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
constexpr char DEFAULT_NPN_LIST[] = "h2,h2-16,h2-14"
|
||||
#ifdef HAVE_SPDYLAY
|
||||
",spdy/3.1,spdy/3,spdy/2"
|
||||
#endif // HAVE_SPDYLAY
|
||||
",http/1.1";
|
||||
constexpr char DEFAULT_NPN_LIST[] = "h2,h2-16,h2-14,http/1.1";
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
|
@ -1849,14 +1816,11 @@ Options:
|
|||
Default: 1
|
||||
-w, --window-bits=<N>
|
||||
Sets the stream level initial window size to (2**<N>)-1.
|
||||
For SPDY, 2**<N> is used instead.
|
||||
Default: )"
|
||||
<< config.window_bits << R"(
|
||||
-W, --connection-window-bits=<N>
|
||||
Sets the connection level initial window size to
|
||||
(2**<N>)-1. For SPDY, if <N> is strictly less than 16,
|
||||
this option is ignored. Otherwise 2**<N> is used for
|
||||
SPDY.
|
||||
(2**<N>)-1.
|
||||
Default: )" << config.connection_window_bits << R"(
|
||||
-H, --header=<HEADER>
|
||||
Add/Override a header to the requests.
|
||||
|
@ -1867,17 +1831,9 @@ Options:
|
|||
<< config.ciphers << R"(
|
||||
-p, --no-tls-proto=<PROTOID>
|
||||
Specify ALPN identifier of the protocol to be used when
|
||||
accessing http URI without SSL/TLS.)";
|
||||
|
||||
#ifdef HAVE_SPDYLAY
|
||||
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"(
|
||||
accessing http URI without SSL/TLS.
|
||||
Available protocols: )" << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID
|
||||
<< R"( and )" << NGHTTP2_H1_1 << R"(
|
||||
Default: )"
|
||||
<< NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
|
||||
-d, --data=<PATH>
|
||||
|
@ -2115,14 +2071,6 @@ int main(int argc, char **argv) {
|
|||
config.no_tls_proto = Config::PROTO_HTTP2;
|
||||
} else if (util::strieq(NGHTTP2_H1_1, proto)) {
|
||||
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 {
|
||||
std::cerr << "-p: unsupported protocol " << proto << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
|
@ -2507,7 +2455,6 @@ int main(int argc, char **argv) {
|
|||
|
||||
config.h1reqs.reserve(reqlines.size());
|
||||
config.nva.reserve(reqlines.size());
|
||||
config.nv.reserve(reqlines.size());
|
||||
|
||||
for (auto &req : reqlines) {
|
||||
// For HTTP/1.1
|
||||
|
@ -2557,35 +2504,6 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
|
||||
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!
|
||||
|
|
|
@ -64,7 +64,6 @@ struct Worker;
|
|||
|
||||
struct Config {
|
||||
std::vector<std::vector<nghttp2_nv>> nva;
|
||||
std::vector<std::vector<const char *>> nv;
|
||||
std::vector<std::string> h1reqs;
|
||||
std::vector<ev_tstamp> timings;
|
||||
nghttp2::Headers custom_headers;
|
||||
|
@ -93,13 +92,7 @@ struct Config {
|
|||
ev_tstamp conn_active_timeout;
|
||||
// amount of time to wait after the last request is made on a connection
|
||||
ev_tstamp conn_inactivity_timeout;
|
||||
enum {
|
||||
PROTO_HTTP2,
|
||||
PROTO_SPDY2,
|
||||
PROTO_SPDY3,
|
||||
PROTO_SPDY3_1,
|
||||
PROTO_HTTP1_1
|
||||
} no_tls_proto;
|
||||
enum { PROTO_HTTP2, PROTO_HTTP1_1 } no_tls_proto;
|
||||
uint32_t header_table_size;
|
||||
uint32_t encoder_header_table_size;
|
||||
// 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 {
|
||||
constexpr auto DEFAULT_NPN_LIST = StringRef::from_lit("h2,h2-16,h2-14,"
|
||||
#ifdef HAVE_SPDYLAY
|
||||
"spdy/3.1,"
|
||||
#endif // HAVE_SPDYLAY
|
||||
"http/1.1");
|
||||
constexpr auto DEFAULT_NPN_LIST =
|
||||
StringRef::from_lit("h2,h2-16,h2-14,http/1.1");
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
|
@ -1490,13 +1487,11 @@ void fill_default_config(Config *config) {
|
|||
timeoutconf.settings = 10_s;
|
||||
}
|
||||
|
||||
// window size for HTTP/2 and SPDY upstream connection per stream.
|
||||
// 2**16-1 = 64KiB-1, which is HTTP/2 default. Please note that
|
||||
// SPDY/3 default is 64KiB.
|
||||
// window size for HTTP/2 upstream connection per stream. 2**16-1
|
||||
// = 64KiB-1, which is HTTP/2 default.
|
||||
upstreamconf.window_size = 64_k - 1;
|
||||
// HTTP/2 and SPDY/3.1 has connection-level flow control. The
|
||||
// default window size for HTTP/2 is 64KiB - 1. SPDY/3's default
|
||||
// is 64KiB
|
||||
// HTTP/2 has connection-level flow control. The default window
|
||||
// size for HTTP/2 is 64KiB - 1.
|
||||
upstreamconf.connection_window_size = 64_k - 1;
|
||||
upstreamconf.max_concurrent_streams = 100;
|
||||
|
||||
|
@ -1624,7 +1619,7 @@ void print_version(std::ostream &out) {
|
|||
namespace {
|
||||
void print_usage(std::ostream &out) {
|
||||
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;
|
||||
}
|
||||
} // namespace
|
||||
|
@ -1990,8 +1985,7 @@ Performance:
|
|||
|
||||
Timeout:
|
||||
--frontend-http2-read-timeout=<DURATION>
|
||||
Specify read timeout for HTTP/2 and SPDY frontend
|
||||
connection.
|
||||
Specify read timeout for HTTP/2 frontend connection.
|
||||
Default: )"
|
||||
<< util::duration_str(config->conn.upstream.timeout.http2_read) << R"(
|
||||
--frontend-read-timeout=<DURATION>
|
||||
|
@ -2008,13 +2002,13 @@ Timeout:
|
|||
Default: )"
|
||||
<< util::duration_str(config->conn.upstream.timeout.idle_read) << R"(
|
||||
--stream-read-timeout=<DURATION>
|
||||
Specify read timeout for HTTP/2 and SPDY streams. 0
|
||||
means no timeout.
|
||||
Specify read timeout for HTTP/2 streams. 0 means no
|
||||
timeout.
|
||||
Default: )"
|
||||
<< util::duration_str(config->http2.timeout.stream_read) << R"(
|
||||
--stream-write-timeout=<DURATION>
|
||||
Specify write timeout for HTTP/2 and SPDY streams. 0
|
||||
means no timeout.
|
||||
Specify write timeout for HTTP/2 streams. 0 means no
|
||||
timeout.
|
||||
Default: )"
|
||||
<< util::duration_str(config->http2.timeout.stream_write) << R"(
|
||||
--backend-read-timeout=<DURATION>
|
||||
|
@ -2351,10 +2345,10 @@ SSL/TLS:
|
|||
consider to use --client-no-http2-cipher-black-list
|
||||
option. But be aware its implications.
|
||||
|
||||
HTTP/2 and SPDY:
|
||||
HTTP/2:
|
||||
-c, --frontend-http2-max-concurrent-streams=<N>
|
||||
Set the maximum number of the concurrent streams in one
|
||||
frontend HTTP/2 and SPDY session.
|
||||
frontend HTTP/2 session.
|
||||
Default: )"
|
||||
<< config->http2.upstream.max_concurrent_streams << R"(
|
||||
--backend-http2-max-concurrent-streams=<N>
|
||||
|
@ -2365,14 +2359,13 @@ HTTP/2 and SPDY:
|
|||
Default: )"
|
||||
<< config->http2.downstream.max_concurrent_streams << R"(
|
||||
--frontend-http2-window-size=<SIZE>
|
||||
Sets the per-stream initial window size of HTTP/2 and
|
||||
SPDY frontend connection.
|
||||
Sets the per-stream initial window size of HTTP/2
|
||||
frontend connection.
|
||||
Default: )"
|
||||
<< config->http2.upstream.window_size << R"(
|
||||
--frontend-http2-connection-window-size=<SIZE>
|
||||
Sets the per-connection window size of HTTP/2 and SPDY
|
||||
frontend connection. For SPDY connection, the value
|
||||
less than 64KiB is rounded up to 64KiB.
|
||||
Sets the per-connection window size of HTTP/2 frontend
|
||||
connection.
|
||||
Default: )"
|
||||
<< config->http2.upstream.connection_window_size << R"(
|
||||
--backend-http2-window-size=<SIZE>
|
||||
|
@ -2398,8 +2391,7 @@ HTTP/2 and SPDY:
|
|||
It is also supported if both frontend and backend are
|
||||
HTTP/2 in default mode. In this case, server push from
|
||||
backend session is relayed to frontend, and server push
|
||||
via Link header field is also supported. SPDY frontend
|
||||
does not support server push.
|
||||
via Link header field is also supported.
|
||||
--frontend-http2-optimize-write-buffer-size
|
||||
(Experimental) Enable write buffer size optimization in
|
||||
frontend HTTP/2 TLS connection. This optimization aims
|
||||
|
@ -2454,7 +2446,7 @@ HTTP/2 and SPDY:
|
|||
|
||||
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
|
||||
and HTTP/1.1 over cleartext TCP. The incoming HTTP/1.1
|
||||
connection can be upgraded to HTTP/2 through HTTP
|
||||
|
|
|
@ -51,9 +51,6 @@
|
|||
#include "shrpx_api_downstream_connection.h"
|
||||
#include "shrpx_health_monitor_downstream_connection.h"
|
||||
#include "shrpx_log.h"
|
||||
#ifdef HAVE_SPDYLAY
|
||||
#include "shrpx_spdy_upstream.h"
|
||||
#endif // HAVE_SPDYLAY
|
||||
#include "util.h"
|
||||
#include "template.h"
|
||||
#include "tls.h"
|
||||
|
@ -608,36 +605,6 @@ int ClientHandler::validate_next_proto() {
|
|||
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")) {
|
||||
upstream_ = make_unique<HttpsUpstream>(this);
|
||||
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
|
||||
// behaviour of previous code. For SPDY, we adjust this value in
|
||||
// SpdyUpstream to look like the SPDY default.
|
||||
// behaviour of previous code.
|
||||
*resp = (1 << n) - 1;
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -469,7 +469,7 @@ private:
|
|||
Upstream *upstream_;
|
||||
std::unique_ptr<DownstreamConnection> dconn_;
|
||||
|
||||
// only used by HTTP/2 or SPDY upstream
|
||||
// only used by HTTP/2 upstream
|
||||
BlockedLink *blocked_link_;
|
||||
// The backend address used to fulfill this request. These are for
|
||||
// logging purpose.
|
||||
|
@ -492,7 +492,7 @@ private:
|
|||
int request_state_;
|
||||
// response state
|
||||
int response_state_;
|
||||
// only used by HTTP/2 or SPDY upstream
|
||||
// only used by HTTP/2 upstream
|
||||
int dispatch_state_;
|
||||
// true if the connection is upgraded (HTTP Upgrade or CONNECT),
|
||||
// excluding upgrade to HTTP/2.
|
||||
|
|
|
@ -270,8 +270,8 @@ int Http2Session::disconnect(bool hard) {
|
|||
// When deleting Http2DownstreamConnection, it calls this object's
|
||||
// remove_downstream_connection(). The multiple
|
||||
// Http2DownstreamConnection objects belong to the same
|
||||
// ClientHandler object if upstream is h2 or SPDY. So be careful
|
||||
// when you delete ClientHandler here.
|
||||
// ClientHandler object if upstream is h2. So be careful when you
|
||||
// delete ClientHandler here.
|
||||
//
|
||||
// We allow creating new pending Http2DownstreamConnection with this
|
||||
// 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)) {
|
||||
// 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();
|
||||
}
|
||||
|
||||
|
|
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>
|
||||
|
||||
#ifdef HAVE_SPDYLAY
|
||||
#include <spdylay/spdylay.h>
|
||||
#endif // HAVE_SPDYLAY
|
||||
|
||||
#include "shrpx_log.h"
|
||||
#include "shrpx_client_handler.h"
|
||||
#include "shrpx_config.h"
|
||||
|
|
Loading…
Reference in New Issue