Remove SPDY

This commit is contained in:
Tatsuhiro Tsujikawa 2017-12-17 13:23:37 +09:00
parent 48f574076c
commit 4d1139f653
20 changed files with 87 additions and 2846 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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