Merge branch 'v1.0.0'
This commit is contained in:
commit
b89140c311
133
README.rst
133
README.rst
|
@ -19,20 +19,12 @@ code coverage yet.
|
|||
Development Status
|
||||
------------------
|
||||
|
||||
We started to implement h2-14
|
||||
(http://tools.ietf.org/html/draft-ietf-httpbis-http2-14), and header
|
||||
compression
|
||||
(http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09).
|
||||
We have implemented `RFC 7540 <https://tools.ietf.org/html/rfc7540>`_
|
||||
HTTP/2 and `RFC 7541 <https://tools.ietf.org/html/rfc7541>`_ HPACK -
|
||||
Header Compression for HTTP/2
|
||||
|
||||
The nghttp2 code base was forked from the spdylay project.
|
||||
|
||||
=========================== =======
|
||||
HTTP/2 Features Support
|
||||
=========================== =======
|
||||
Core frames handling Yes
|
||||
Dependency Tree Yes
|
||||
Large header (CONTINUATION) Yes
|
||||
=========================== =======
|
||||
The nghttp2 code base was forked from the spdylay
|
||||
(https://github.com/tatsuhiro-t/spdylay) project.
|
||||
|
||||
Public Test Server
|
||||
------------------
|
||||
|
@ -46,9 +38,9 @@ implementation.
|
|||
and ``http/1.1`` via ALPN/NPN and requires TLSv1.2 for HTTP/2
|
||||
connection.
|
||||
|
||||
* http://nghttp2.org/ (Upgrade / Direct)
|
||||
* http://nghttp2.org/ (HTTP Upgrade and HTTP/2 Direct)
|
||||
|
||||
``h2c-14`` and ``http/1.1``.
|
||||
``h2c`` and ``http/1.1``.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
@ -212,6 +204,104 @@ To run the tests, run the following command under
|
|||
|
||||
Inside the tests, we use port 3009 to run the test subject server.
|
||||
|
||||
Migration from v0.7.9 or earlier
|
||||
--------------------------------
|
||||
|
||||
nghttp2 v1.0.0 introduced several backward incompatible changes. In
|
||||
this section, we describe these changes and how to migrate to v1.0.0.
|
||||
|
||||
ALPN protocol ID is now ``h2`` and ``h2c``
|
||||
++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
Previously we announced ``h2-14`` and ``h2c-14``. v1.0.0 implements
|
||||
final protocol version, and we changed ALPN ID to ``h2`` and ``h2c``.
|
||||
The macros ``NGHTTP2_PROTO_VERSION_ID``,
|
||||
``NGHTTP2_PROTO_VERSION_ID_LEN``,
|
||||
``NGHTTP2_CLEARTEXT_PROTO_VERSION_ID``, and
|
||||
``NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN`` have been updated to
|
||||
reflect this change.
|
||||
|
||||
Basically, existing applications do not have to do anything, just
|
||||
recompiling is enough for this change.
|
||||
|
||||
Use word "client magic" where we use "client connection preface"
|
||||
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
We use "client connection preface" to mean first 24 bytes of client
|
||||
connection preface. This is technically not correct, since client
|
||||
connection preface is composed of 24 bytes client magic byte string
|
||||
followed by SETTINGS frame. For clarification, we call "client magic"
|
||||
for this 24 bytes byte string and updated API.
|
||||
|
||||
* ``NGHTTP2_CLIENT_CONNECTION_PREFACE`` was replaced with
|
||||
``NGHTTP2_CLIENT_MAGIC``.
|
||||
* ``NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN`` was replaced with
|
||||
``NGHTTP2_CLIENT_MAGIC_LEN``.
|
||||
* ``NGHTTP2_BAD_PREFACE`` was renamed as ``NGHTTP2_BAD_CLIENT_MAGIC``
|
||||
|
||||
The alreay deprecated ``NGHTTP2_CLIENT_CONNECTION_HEADER`` and
|
||||
``NGHTTP2_CLIENT_CONNECTION_HEADER_LEN`` were removed.
|
||||
|
||||
If application uses these macros, just replace old ones with new ones.
|
||||
Since v1.0.0, client magic is sent by library (see next subsection),
|
||||
so client application may just remove these macro use.
|
||||
|
||||
Client magic is sent by library
|
||||
+++++++++++++++++++++++++++++++
|
||||
|
||||
Previously nghttp2 library did not send client magic, which is first
|
||||
24 bytes byte string of client connection preface, and client
|
||||
applications have to send it by themselves. Since v1.0.0, client
|
||||
magic is sent by library via first call of ``nghttp2_session_send()``
|
||||
or ``nghttp2_session_mem_send()``.
|
||||
|
||||
The client applications which send client magic must remove the
|
||||
relevant code.
|
||||
|
||||
Remove HTTP Alternative Services (Alt-Svc) related code
|
||||
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
Alt-Svc specification is not finalized yet. To make our API stable,
|
||||
we have decided to remove all Alt-Svc related API from nghttp2.
|
||||
|
||||
* ``NGHTTP2_EXT_ALTSVC`` was removed.
|
||||
* ``nghttp2_ext_altsvc`` was removed.
|
||||
|
||||
We have already removed the functionality of Alt-Svc in v0.7 series
|
||||
and they have been essentially noop. The application using these
|
||||
macro and struct, remove those lines.
|
||||
|
||||
Use nghttp2_error in nghttp2_on_invalid_frame_recv_callback
|
||||
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
Previously ``nghttp2_on_invalid_frame_recv_cb_called`` took the
|
||||
``error_code``, defined in ``nghttp2_error_code``, as parameter. But
|
||||
they are not detailed enough to debug. Therefore, we decided to use
|
||||
more detailed ``nghttp2_error`` values instead.
|
||||
|
||||
The application using this callback should update the callback
|
||||
signature. If it treats ``error_code`` as HTTP/2 error code, update
|
||||
the code so that it is treated as ``nghttp2_error``.
|
||||
|
||||
Receive client magic by default
|
||||
+++++++++++++++++++++++++++++++
|
||||
|
||||
Previously nghttp2 did not process client magic (24 bytes byte
|
||||
string). To make it deal with it, we had to use
|
||||
``nghttp2_option_set_recv_client_preface()``. Since v1.0.0, nghttp2
|
||||
processes client magic by default and
|
||||
``nghttp2_option_set_recv_client_preface()`` was removed.
|
||||
|
||||
Some application may want to disable this behaviour, so we added
|
||||
``nghttp2_option_set_no_recv_client_magic()`` to achieve this.
|
||||
|
||||
The application using ``nghttp2_option_set_recv_client_preface()``
|
||||
with nonzero value, just remove it.
|
||||
|
||||
The application using ``nghttp2_option_set_recv_client_preface()``
|
||||
with zero value or not using it must use
|
||||
``nghttp2_option_set_no_recv_client_magic()`` with nonzero value.
|
||||
|
||||
Client, Server and Proxy programs
|
||||
---------------------------------
|
||||
|
||||
|
@ -228,10 +318,10 @@ output from ``nghttp`` client::
|
|||
|
||||
$ nghttp -nv https://nghttp2.org
|
||||
[ 0.033][NPN] server offers:
|
||||
* h2-14
|
||||
* h2
|
||||
* spdy/3.1
|
||||
* http/1.1
|
||||
The negotiated protocol: h2-14
|
||||
The negotiated protocol: h2
|
||||
[ 0.068] send SETTINGS frame <length=15, flags=0x00, stream_id=0>
|
||||
(niv=3)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
||||
|
@ -458,8 +548,9 @@ information. Here is sample output from ``nghttpd``::
|
|||
nghttpx - proxy
|
||||
+++++++++++++++
|
||||
|
||||
``nghttpx`` is a multi-threaded reverse proxy for ``h2-14``, SPDY and
|
||||
HTTP/1.1, and powers http://nghttp2.org and supports HTTP/2 server push.
|
||||
``nghttpx`` is a multi-threaded reverse proxy for HTTP/2, SPDY and
|
||||
HTTP/1.1, and powers http://nghttp2.org and supports HTTP/2 server
|
||||
push.
|
||||
|
||||
``nghttpx`` implements `important performance-oriented features
|
||||
<https://istlsfastyet.com/#server-performance>`_ in TLS, such as
|
||||
|
@ -480,8 +571,8 @@ default mode HTTP/2, SPDY, HTTP/1.1 (TLS) HTTP/1.1 Reverse proxy
|
|||
================== ============================ ============== =============
|
||||
|
||||
The interesting mode at the moment is the default mode. It works like
|
||||
a reverse proxy and listens for ``h2-14``, SPDY and HTTP/1.1 and can
|
||||
be deployed as a SSL/TLS terminator for existing web server.
|
||||
a reverse proxy and listens for HTTP/2, SPDY and HTTP/1.1 and can be
|
||||
deployed as a SSL/TLS terminator for existing web server.
|
||||
|
||||
The default mode, ``--http2-proxy`` and ``--http2-bridge`` modes use
|
||||
SSL/TLS in the frontend connection by default. To disable SSL/TLS,
|
||||
|
|
|
@ -25,7 +25,7 @@ dnl Do not change user variables!
|
|||
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
|
||||
|
||||
AC_PREREQ(2.61)
|
||||
AC_INIT([nghttp2], [0.7.15], [t-tujikawa@users.sourceforge.net])
|
||||
AC_INIT([nghttp2], [1.0.0-DEV], [t-tujikawa@users.sourceforge.net])
|
||||
AC_USE_SYSTEM_EXTENSIONS
|
||||
|
||||
LT_PREREQ([2.2.6])
|
||||
|
|
|
@ -49,7 +49,7 @@ APIDOCS= \
|
|||
nghttp2_option_set_no_auto_window_update.rst \
|
||||
nghttp2_option_set_no_http_messaging.rst \
|
||||
nghttp2_option_set_peer_max_concurrent_streams.rst \
|
||||
nghttp2_option_set_recv_client_preface.rst \
|
||||
nghttp2_option_set_no_recv_client_magic.rst \
|
||||
nghttp2_pack_settings_payload.rst \
|
||||
nghttp2_priority_spec_check_default.rst \
|
||||
nghttp2_priority_spec_default_init.rst \
|
||||
|
@ -108,7 +108,6 @@ APIDOCS= \
|
|||
nghttp2_session_want_read.rst \
|
||||
nghttp2_session_want_write.rst \
|
||||
nghttp2_strerror.rst \
|
||||
nghttp2_submit_altsvc.rst \
|
||||
nghttp2_submit_data.rst \
|
||||
nghttp2_submit_goaway.rst \
|
||||
nghttp2_submit_headers.rst \
|
||||
|
|
|
@ -20,17 +20,15 @@ nghttp2 callback functions directly or indirectly. It will lead to the
|
|||
crash. You can submit requests or frames in the callbacks then call
|
||||
these functions outside the callbacks.
|
||||
|
||||
Currently, `nghttp2_session_send()` and `nghttp2_session_mem_send()`
|
||||
do not send client connection preface
|
||||
(:macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`). The applications are
|
||||
responsible to send it before sending any HTTP/2 frames using these
|
||||
functions if :type:`nghttp2_session` is configured as client.
|
||||
Similarly, `nghttp2_session_recv()` and `nghttp2_session_mem_recv()`
|
||||
do not consume client connection preface unless
|
||||
`nghttp2_option_set_recv_client_preface()` is used with nonzero option
|
||||
value. The applications are responsible to receive it before calling
|
||||
these functions if :type:`nghttp2_session` is configured as server and
|
||||
`nghttp2_option_set_recv_client_preface()` is not used.
|
||||
`nghttp2_session_send()` and `nghttp2_session_mem_send()` send first
|
||||
24 bytes of client magic string (MAGIC)
|
||||
(:macro:`NGHTTP2_CLIENT_MAGIC`) on client configuration. The
|
||||
applications are responsible to send SETTINGS frame as part of
|
||||
connection preface using `nghttp2_submit_settings()`. Similarly,
|
||||
`nghttp2_session_recv()` and `nghttp2_session_mem_recv()` consume
|
||||
MAGIC on server configuration unless
|
||||
`nghttp2_option_set_no_recv_client_magic()` is used with nonzero
|
||||
option value.
|
||||
|
||||
.. _http-messaging:
|
||||
|
||||
|
|
|
@ -184,9 +184,9 @@ its bufferevent, so it closes underlying connection as well. It also
|
|||
calls `nghttp2_session_del()` to delete nghttp2 session object.
|
||||
|
||||
We begin HTTP/2 communication by sending client connection preface,
|
||||
which is 24 bytes magic byte sequence
|
||||
(:macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`) and SETTINGS frame. The
|
||||
transmission of client connection header is done in
|
||||
which is 24 bytes magic byte string (:macro:`NGHTTP2_CLIENT_MAGIC`)
|
||||
followed by SETTINGS frame. First 24 bytes magic string is
|
||||
automatically sent by nghttp2 library. We send SETTINGS frame in
|
||||
``send_client_connection_header()``::
|
||||
|
||||
static void send_client_connection_header(http2_session_data *session_data) {
|
||||
|
@ -194,8 +194,7 @@ transmission of client connection header is done in
|
|||
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||
int rv;
|
||||
|
||||
bufferevent_write(session_data->bev, NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
/* client 24 bytes magic string will be sent by nghttp2 library */
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||
ARRLEN(iv));
|
||||
if (rv != 0) {
|
||||
|
|
|
@ -194,15 +194,8 @@ We initialize a nghttp2 session object which is done in
|
|||
``initialize_nghttp2_session()``::
|
||||
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||
nghttp2_option *option;
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
|
||||
nghttp2_option_new(&option);
|
||||
|
||||
/* Tells nghttp2_session object that it handles client connection
|
||||
preface */
|
||||
nghttp2_option_set_recv_client_preface(option, 1);
|
||||
|
||||
nghttp2_session_callbacks_new(&callbacks);
|
||||
|
||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||
|
@ -219,20 +212,15 @@ We initialize a nghttp2 session object which is done in
|
|||
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||
callbacks, on_begin_headers_callback);
|
||||
|
||||
nghttp2_session_server_new2(&session_data->session, callbacks, session_data,
|
||||
option);
|
||||
nghttp2_session_server_new(&session_data->session, callbacks, session_data);
|
||||
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
nghttp2_option_del(option);
|
||||
}
|
||||
|
||||
Since we are creating a server and uses options, the nghttp2 session
|
||||
object is created using `nghttp2_session_server_new2()` function. We
|
||||
registers five callbacks for nghttp2 session object. We'll talk about
|
||||
these callbacks later. Our server only speaks HTTP/2. In this case,
|
||||
we use `nghttp2_option_set_recv_client_preface()` to make
|
||||
:type:`nghttp2_session` object handle client connection preface, which
|
||||
saves some lines of application code.
|
||||
these callbacks later.
|
||||
|
||||
After initialization of the nghttp2 session object, we are going to send
|
||||
a server connection header in ``send_server_connection_header()``::
|
||||
|
|
|
@ -538,14 +538,6 @@ static void fetch_uri(const struct URI *uri) {
|
|||
connection.ssl = ssl;
|
||||
connection.want_io = IO_NONE;
|
||||
|
||||
/* Send connection header in blocking I/O mode */
|
||||
rv = SSL_write(ssl, NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
if (rv <= 0) {
|
||||
dief("SSL_write failed: could not send connection preface",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
|
||||
/* Here make file descriptor non-block */
|
||||
make_non_block(fd);
|
||||
set_tcp_nodelay(fd);
|
||||
|
|
|
@ -350,8 +350,7 @@ static void send_client_connection_header(http2_session_data *session_data) {
|
|||
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||
int rv;
|
||||
|
||||
bufferevent_write(session_data->bev, NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
/* client 24 bytes magic string will be sent by nghttp2 library */
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||
ARRLEN(iv));
|
||||
if (rv != 0) {
|
||||
|
|
|
@ -547,15 +547,8 @@ static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
|||
}
|
||||
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||
nghttp2_option *option;
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
|
||||
nghttp2_option_new(&option);
|
||||
|
||||
/* Tells nghttp2_session object that it handles client connection
|
||||
preface */
|
||||
nghttp2_option_set_recv_client_preface(option, 1);
|
||||
|
||||
nghttp2_session_callbacks_new(&callbacks);
|
||||
|
||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||
|
@ -572,11 +565,9 @@ static void initialize_nghttp2_session(http2_session_data *session_data) {
|
|||
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||
callbacks, on_begin_headers_callback);
|
||||
|
||||
nghttp2_session_server_new2(&session_data->session, callbacks, session_data,
|
||||
option);
|
||||
nghttp2_session_server_new(&session_data->session, callbacks, session_data);
|
||||
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
nghttp2_option_del(option);
|
||||
}
|
||||
|
||||
/* Send HTTP/2 client connection header, which includes 24 bytes
|
||||
|
|
|
@ -165,7 +165,6 @@ const char *docroot;
|
|||
size_t docrootlen;
|
||||
|
||||
nghttp2_session_callbacks *shared_callbacks;
|
||||
nghttp2_option *shared_option;
|
||||
|
||||
static int handle_accept(io_loop *loop, uint32_t events, void *ptr);
|
||||
static int handle_connection(io_loop *loop, uint32_t events, void *ptr);
|
||||
|
@ -400,8 +399,7 @@ static connection *connection_new(int fd) {
|
|||
|
||||
conn = malloc(sizeof(connection));
|
||||
|
||||
rv = nghttp2_session_server_new2(&conn->session, shared_callbacks, conn,
|
||||
shared_option);
|
||||
rv = nghttp2_session_server_new(&conn->session, shared_callbacks, conn);
|
||||
|
||||
if (rv != 0) {
|
||||
goto cleanup;
|
||||
|
@ -1322,14 +1320,6 @@ int main(int argc, char **argv) {
|
|||
nghttp2_session_callbacks_set_send_data_callback(shared_callbacks,
|
||||
send_data_callback);
|
||||
|
||||
rv = nghttp2_option_new(&shared_option);
|
||||
if (rv != 0) {
|
||||
fprintf(stderr, "nghttp2_option_new: %s", nghttp2_strerror(rv));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
nghttp2_option_set_recv_client_preface(shared_option, 1);
|
||||
|
||||
rv = io_loop_add(&loop, serv.fd, EPOLLIN, &serv);
|
||||
|
||||
if (rv != 0) {
|
||||
|
@ -1346,7 +1336,6 @@ int main(int argc, char **argv) {
|
|||
|
||||
io_loop_run(&loop, &serv);
|
||||
|
||||
nghttp2_option_del(shared_option);
|
||||
nghttp2_session_callbacks_del(shared_callbacks);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -607,7 +607,7 @@ func TestH2H1Upgrade(t *testing.T) {
|
|||
name: "TestH2H1Upgrade",
|
||||
header: []hpack.HeaderField{
|
||||
pair("Connection", "Upgrade, HTTP2-Settings"),
|
||||
pair("Upgrade", "h2c-14"),
|
||||
pair("Upgrade", "h2c"),
|
||||
pair("HTTP2-Settings", "AAMAAABkAAQAAP__"),
|
||||
},
|
||||
})
|
||||
|
|
|
@ -55,13 +55,13 @@ extern "C" {
|
|||
* The protocol version identification string of this library
|
||||
* supports. This identifier is used if HTTP/2 is used over TLS.
|
||||
*/
|
||||
#define NGHTTP2_PROTO_VERSION_ID "h2-14"
|
||||
#define NGHTTP2_PROTO_VERSION_ID "h2"
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* The length of :macro:`NGHTTP2_PROTO_VERSION_ID`.
|
||||
*/
|
||||
#define NGHTTP2_PROTO_VERSION_ID_LEN 5
|
||||
#define NGHTTP2_PROTO_VERSION_ID_LEN 2
|
||||
|
||||
/**
|
||||
* @macro
|
||||
|
@ -72,7 +72,7 @@ extern "C" {
|
|||
* extension <https://tools.ietf.org/html/rfc7301>`_. This is useful
|
||||
* to process incoming ALPN tokens in wire format.
|
||||
*/
|
||||
#define NGHTTP2_PROTO_ALPN "\x5h2-14"
|
||||
#define NGHTTP2_PROTO_ALPN "\x2h2"
|
||||
|
||||
/**
|
||||
* @macro
|
||||
|
@ -88,14 +88,14 @@ extern "C" {
|
|||
* supports. This identifier is used if HTTP/2 is used over cleartext
|
||||
* TCP.
|
||||
*/
|
||||
#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "h2c-14"
|
||||
#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "h2c"
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* The length of :macro:`NGHTTP2_CLEARTEXT_PROTO_VERSION_ID`.
|
||||
*/
|
||||
#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN 6
|
||||
#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN 3
|
||||
|
||||
struct nghttp2_session;
|
||||
/**
|
||||
|
@ -194,32 +194,17 @@ typedef struct {
|
|||
/**
|
||||
* @macro
|
||||
*
|
||||
* The client connection preface.
|
||||
* The client magic string, which is the first 24 bytes byte string of
|
||||
* client connection preface.
|
||||
*/
|
||||
#define NGHTTP2_CLIENT_CONNECTION_PREFACE "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
|
||||
#define NGHTTP2_CLIENT_MAGIC "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* The length of :macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`.
|
||||
* The length of :macro:`NGHTTP2_CLIENT_MAGIC`.
|
||||
*/
|
||||
#define NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN 24
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* The client connection header. This macro is obsoleted by
|
||||
* NGHTTP2_CLIENT_CONNECTION_PREFACE.
|
||||
*/
|
||||
#define NGHTTP2_CLIENT_CONNECTION_HEADER NGHTTP2_CLIENT_CONNECTION_PREFACE
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* The length of :macro:`NGHTTP2_CLIENT_CONNECTION_HEADER`.
|
||||
*/
|
||||
#define NGHTTP2_CLIENT_CONNECTION_HEADER_LEN \
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN
|
||||
#define NGHTTP2_CLIENT_MAGIC_LEN 24
|
||||
|
||||
/**
|
||||
* @enum
|
||||
|
@ -368,6 +353,18 @@ typedef enum {
|
|||
* closed.
|
||||
*/
|
||||
NGHTTP2_ERR_HTTP_HEADER = -531,
|
||||
/**
|
||||
* Violation in HTTP messaging rule.
|
||||
*/
|
||||
NGHTTP2_ERR_HTTP_MESSAGING = -532,
|
||||
/**
|
||||
* Stream was refused.
|
||||
*/
|
||||
NGHTTP2_ERR_REFUSED_STREAM = -533,
|
||||
/**
|
||||
* Unexpected internal error, but recovered.
|
||||
*/
|
||||
NGHTTP2_ERR_INTERNAL = -534,
|
||||
/**
|
||||
* The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is
|
||||
* under unexpected condition and processing was terminated (e.g.,
|
||||
|
@ -386,10 +383,10 @@ typedef enum {
|
|||
*/
|
||||
NGHTTP2_ERR_CALLBACK_FAILURE = -902,
|
||||
/**
|
||||
* Invalid connection preface was received and further processing is
|
||||
* not possible.
|
||||
* Invalid client magic (see :macro:`NGHTTP2_CLIENT_MAGIC`) was
|
||||
* received and further processing is not possible.
|
||||
*/
|
||||
NGHTTP2_ERR_BAD_PREFACE = -903
|
||||
NGHTTP2_ERR_BAD_CLIENT_MAGIC = -903
|
||||
} nghttp2_error;
|
||||
|
||||
/**
|
||||
|
@ -496,21 +493,6 @@ typedef enum {
|
|||
NGHTTP2_CONTINUATION = 0x09
|
||||
} nghttp2_frame_type;
|
||||
|
||||
/**
|
||||
* @enum
|
||||
*
|
||||
* The extension frame types.
|
||||
*
|
||||
* TODO: The assigned frame types were carried from draft-12, and now
|
||||
* actually TBD.
|
||||
*/
|
||||
typedef enum {
|
||||
/**
|
||||
* The ALTSVC extension frame.
|
||||
*/
|
||||
NGHTTP2_EXT_ALTSVC = 0x0a
|
||||
} nghttp2_ext_frame_type;
|
||||
|
||||
/**
|
||||
* @enum
|
||||
*
|
||||
|
@ -1079,52 +1061,12 @@ typedef struct {
|
|||
* The pointer to extension payload. The exact pointer type is
|
||||
* determined by hd.type.
|
||||
*
|
||||
* If hd.type == :enum:`NGHTTP2_EXT_ALTSVC`, it is a pointer to
|
||||
* :type:`nghttp2_ext_altsvc`.
|
||||
* Currently, no extension is supported. This is a place holder for
|
||||
* the future extensions.
|
||||
*/
|
||||
void *payload;
|
||||
} nghttp2_extension;
|
||||
|
||||
/**
|
||||
* @struct
|
||||
*
|
||||
* The ALTSVC extension frame payload. It has following members:
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* Protocol ID
|
||||
*/
|
||||
uint8_t *protocol_id;
|
||||
/**
|
||||
* Host
|
||||
*/
|
||||
uint8_t *host;
|
||||
/**
|
||||
* Origin
|
||||
*/
|
||||
uint8_t *origin;
|
||||
/**
|
||||
* The length of |protocol_id|
|
||||
*/
|
||||
size_t protocol_id_len;
|
||||
/**
|
||||
* The length of |host|
|
||||
*/
|
||||
size_t host_len;
|
||||
/**
|
||||
* The length of |origin|
|
||||
*/
|
||||
size_t origin_len;
|
||||
/**
|
||||
* Max-Age
|
||||
*/
|
||||
uint32_t max_age;
|
||||
/**
|
||||
* Port
|
||||
*/
|
||||
uint16_t port;
|
||||
} nghttp2_ext_altsvc;
|
||||
|
||||
/**
|
||||
* @union
|
||||
*
|
||||
|
@ -1316,11 +1258,11 @@ typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session,
|
|||
*
|
||||
* Callback function invoked by `nghttp2_session_recv()` and
|
||||
* `nghttp2_session_mem_recv()` when an invalid non-DATA frame is
|
||||
* received. The |error_code| indicates the error. It is usually one
|
||||
* of the :enum:`nghttp2_error_code` but that is not guaranteed. When
|
||||
* this callback function is invoked, the library automatically
|
||||
* submits either RST_STREAM or GOAWAY frame. The |user_data| pointer
|
||||
* is the third argument passed in to the call to
|
||||
* received. The error is indicated by the |lib_error_code|, which is
|
||||
* one of the values defined in :type:`nghttp2_error`. When this
|
||||
* callback function is invoked, the library automatically submits
|
||||
* either RST_STREAM or GOAWAY frame. The |user_data| pointer is the
|
||||
* third argument passed in to the call to
|
||||
* `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.
|
||||
*
|
||||
* If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen``
|
||||
|
@ -1336,7 +1278,7 @@ typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session,
|
|||
* `nghttp2_session_callbacks_set_on_invalid_frame_recv_callback()`.
|
||||
*/
|
||||
typedef int (*nghttp2_on_invalid_frame_recv_callback)(
|
||||
nghttp2_session *session, const nghttp2_frame *frame, uint32_t error_code,
|
||||
nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code,
|
||||
void *user_data);
|
||||
|
||||
/**
|
||||
|
@ -2036,31 +1978,33 @@ nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option,
|
|||
/**
|
||||
* @function
|
||||
*
|
||||
* By default, nghttp2 library only handles HTTP/2 frames and does not
|
||||
* recognize first 24 bytes of client connection preface. This design
|
||||
* choice is done due to the fact that server may want to detect the
|
||||
* application protocol based on first few bytes on clear text
|
||||
* communication. But for simple servers which only speak HTTP/2, it
|
||||
* is easier for developers if nghttp2 library takes care of client
|
||||
* connection preface.
|
||||
* By default, nghttp2 library, if configured as server, requires
|
||||
* first 24 bytes of client magic byte string (MAGIC). In most cases,
|
||||
* this will simplify the implementation of server. But sometimes
|
||||
* server may want to detect the application protocol based on first
|
||||
* few bytes on clear text communication.
|
||||
*
|
||||
* If this option is used with nonzero |val|, nghttp2 library checks
|
||||
* first 24 bytes client connection preface. If it is not a valid
|
||||
* one, `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` will
|
||||
* return error :enum:`NGHTTP2_ERR_BAD_PREFACE`, which is fatal error.
|
||||
* If this option is used with nonzero |val|, nghttp2 library does not
|
||||
* handle MAGIC. It still checks following SETTINGS frame. This
|
||||
* means that applications should deal with MAGIC by themselves.
|
||||
*
|
||||
* If this option is not used or used with zero value, if MAGIC does
|
||||
* not match :macro:`NGHTTP2_CLIENT_MAGIC`, `nghttp2_session_recv()`
|
||||
* and `nghttp2_session_mem_recv()` will return error
|
||||
* :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC`, which is fatal error.
|
||||
*/
|
||||
NGHTTP2_EXTERN void
|
||||
nghttp2_option_set_recv_client_preface(nghttp2_option *option, int val);
|
||||
nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* By default, nghttp2 library enforces subset of HTTP Messaging rules
|
||||
* described in `HTTP/2 specification, section 8
|
||||
* <https://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-8>`_.
|
||||
* See :ref:`http-messaging` section for details. For those
|
||||
* applications who use nghttp2 library as non-HTTP use, give nonzero
|
||||
* to |val| to disable this enforcement.
|
||||
* <https://tools.ietf.org/html/rfc7540#section-8>`_. See
|
||||
* :ref:`http-messaging` section for details. For those applications
|
||||
* who use nghttp2 library as non-HTTP use, give nonzero to |val| to
|
||||
* disable this enforcement.
|
||||
*/
|
||||
NGHTTP2_EXTERN void nghttp2_option_set_no_http_messaging(nghttp2_option *option,
|
||||
int val);
|
||||
|
@ -2367,10 +2311,11 @@ NGHTTP2_EXTERN ssize_t nghttp2_session_mem_send(nghttp2_session *session,
|
|||
* Out of memory.
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`
|
||||
* The callback function failed.
|
||||
* :enum:`NGHTTP2_ERR_BAD_PREFACE`
|
||||
* Invalid client preface was detected. This error only returns
|
||||
* :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC`
|
||||
* Invalid client magic was detected. This error only returns
|
||||
* when |session| was configured as server and
|
||||
* `nghttp2_option_set_recv_client_preface()` is used.
|
||||
* `nghttp2_option_set_no_recv_client_magic()` is not used with
|
||||
* nonzero value.
|
||||
*/
|
||||
NGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session);
|
||||
|
||||
|
@ -2402,10 +2347,11 @@ NGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session);
|
|||
* Out of memory.
|
||||
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`
|
||||
* The callback function failed.
|
||||
* :enum:`NGHTTP2_ERR_BAD_PREFACE`
|
||||
* Invalid client preface was detected. This error only returns
|
||||
* :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC`
|
||||
* Invalid client magic was detected. This error only returns
|
||||
* when |session| was configured as server and
|
||||
* `nghttp2_option_set_recv_client_preface()` is used.
|
||||
* `nghttp2_option_set_no_recv_client_magic()` is not used with
|
||||
* nonzero value.
|
||||
*/
|
||||
NGHTTP2_EXTERN ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
||||
const uint8_t *in,
|
||||
|
@ -3488,20 +3434,6 @@ NGHTTP2_EXTERN int nghttp2_submit_window_update(nghttp2_session *session,
|
|||
int32_t stream_id,
|
||||
int32_t window_size_increment);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* This function previously submits ALTSVC frame with given
|
||||
* parameters, but is deprecated and will be removed in a future
|
||||
* release. This function does nothing and just return 0.
|
||||
*/
|
||||
NGHTTP2_EXTERN int
|
||||
nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id, uint32_t max_age, uint16_t port,
|
||||
const uint8_t *protocol_id, size_t protocol_id_len,
|
||||
const uint8_t *host, size_t host_len,
|
||||
const uint8_t *origin, size_t origin_len);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
|
@ -3520,14 +3452,14 @@ NGHTTP2_EXTERN int nghttp2_nv_compare_name(const nghttp2_nv *lhs,
|
|||
* A helper function for dealing with NPN in client side or ALPN in
|
||||
* server side. The |in| contains peer's protocol list in preferable
|
||||
* order. The format of |in| is length-prefixed and not
|
||||
* null-terminated. For example, ``HTTP-draft-04/2.0`` and
|
||||
* null-terminated. For example, ``h2`` and
|
||||
* ``http/1.1`` stored in |in| like this::
|
||||
*
|
||||
* in[0] = 17
|
||||
* in[1..17] = "HTTP-draft-04/2.0"
|
||||
* in[18] = 8
|
||||
* in[19..26] = "http/1.1"
|
||||
* inlen = 27
|
||||
* in[0] = 2
|
||||
* in[1..2] = "h2"
|
||||
* in[3] = 8
|
||||
* in[4..11] = "http/1.1"
|
||||
* inlen = 12
|
||||
*
|
||||
* The selection algorithm is as follows:
|
||||
*
|
||||
|
@ -3541,12 +3473,10 @@ NGHTTP2_EXTERN int nghttp2_nv_compare_name(const nghttp2_nv *lhs,
|
|||
* non-overlap case). In this case, |out| and |outlen| are left
|
||||
* untouched.
|
||||
*
|
||||
* Selecting ``HTTP-draft-04/2.0`` means that ``HTTP-draft-04/2.0`` is
|
||||
* written into |*out| and its length (which is 17) is assigned to
|
||||
* |*outlen|.
|
||||
* Selecting ``h2`` means that ``h2`` is written into |*out| and its
|
||||
* length (which is 2) is assigned to |*outlen|.
|
||||
*
|
||||
* For ALPN, refer to
|
||||
* https://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg-05
|
||||
* For ALPN, refer to https://tools.ietf.org/html/rfc7301
|
||||
*
|
||||
* See http://technotes.googlecode.com/git/nextprotoneg.html for more
|
||||
* details about NPN.
|
||||
|
@ -3596,7 +3526,7 @@ NGHTTP2_EXTERN nghttp2_info *nghttp2_version(int least_version);
|
|||
* Returns nonzero if the :type:`nghttp2_error` library error code
|
||||
* |lib_error| is fatal.
|
||||
*/
|
||||
NGHTTP2_EXTERN int nghttp2_is_fatal(int lib_error);
|
||||
NGHTTP2_EXTERN int nghttp2_is_fatal(int lib_error_code);
|
||||
|
||||
/**
|
||||
* @function
|
||||
|
|
|
@ -75,7 +75,7 @@
|
|||
#define NGHTTP2_MAX_PADLEN 256
|
||||
|
||||
/* Union of extension frame payload */
|
||||
typedef union { nghttp2_ext_altsvc altsvc; } nghttp2_ext_frame_payload;
|
||||
typedef union { int dummy; } nghttp2_ext_frame_payload;
|
||||
|
||||
void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd);
|
||||
|
||||
|
|
|
@ -297,12 +297,18 @@ const char *nghttp2_strerror(int error_code) {
|
|||
return "The current session is closing";
|
||||
case NGHTTP2_ERR_HTTP_HEADER:
|
||||
return "Invalid HTTP header field was received";
|
||||
case NGHTTP2_ERR_HTTP_MESSAGING:
|
||||
return "Violation in HTTP messaging rule";
|
||||
case NGHTTP2_ERR_REFUSED_STREAM:
|
||||
return "Stream was refused";
|
||||
case NGHTTP2_ERR_INTERNAL:
|
||||
return "Internal error";
|
||||
case NGHTTP2_ERR_NOMEM:
|
||||
return "Out of memory";
|
||||
case NGHTTP2_ERR_CALLBACK_FAILURE:
|
||||
return "The user callback function failed";
|
||||
case NGHTTP2_ERR_BAD_PREFACE:
|
||||
return "Received bad connection preface";
|
||||
case NGHTTP2_ERR_BAD_CLIENT_MAGIC:
|
||||
return "Received bad clinet magic byte string";
|
||||
default:
|
||||
return "Unknown error code";
|
||||
}
|
||||
|
|
|
@ -47,9 +47,9 @@ void nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option,
|
|||
option->peer_max_concurrent_streams = val;
|
||||
}
|
||||
|
||||
void nghttp2_option_set_recv_client_preface(nghttp2_option *option, int val) {
|
||||
option->opt_set_mask |= NGHTTP2_OPT_RECV_CLIENT_PREFACE;
|
||||
option->recv_client_preface = val;
|
||||
void nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val) {
|
||||
option->opt_set_mask |= NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC;
|
||||
option->no_recv_client_magic = val;
|
||||
}
|
||||
|
||||
void nghttp2_option_set_no_http_messaging(nghttp2_option *option, int val) {
|
||||
|
|
|
@ -57,7 +57,7 @@ typedef enum {
|
|||
* SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint.
|
||||
*/
|
||||
NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1,
|
||||
NGHTTP2_OPT_RECV_CLIENT_PREFACE = 1 << 2,
|
||||
NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC = 1 << 2,
|
||||
NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3
|
||||
} nghttp2_option_flag;
|
||||
|
||||
|
@ -79,9 +79,9 @@ struct nghttp2_option {
|
|||
*/
|
||||
uint8_t no_auto_window_update;
|
||||
/**
|
||||
* NGHTTP2_OPT_RECV_CLIENT_PREFACE
|
||||
* NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC
|
||||
*/
|
||||
uint8_t recv_client_preface;
|
||||
uint8_t no_recv_client_magic;
|
||||
/**
|
||||
* NGHTTP2_OPT_NO_HTTP_MESSAGING
|
||||
*/
|
||||
|
|
|
@ -71,11 +71,13 @@ session_is_incoming_concurrent_streams_pending_max(nghttp2_session *session) {
|
|||
/*
|
||||
* Returns non-zero if |lib_error| is non-fatal error.
|
||||
*/
|
||||
static int is_non_fatal(int lib_error) {
|
||||
return lib_error < 0 && lib_error > NGHTTP2_ERR_FATAL;
|
||||
static int is_non_fatal(int lib_error_code) {
|
||||
return lib_error_code < 0 && lib_error_code > NGHTTP2_ERR_FATAL;
|
||||
}
|
||||
|
||||
int nghttp2_is_fatal(int lib_error) { return lib_error < NGHTTP2_ERR_FATAL; }
|
||||
int nghttp2_is_fatal(int lib_error_code) {
|
||||
return lib_error_code < NGHTTP2_ERR_FATAL;
|
||||
}
|
||||
|
||||
static int session_enforce_http_messaging(nghttp2_session *session) {
|
||||
return (session->opt_flags & NGHTTP2_OPTMASK_NO_HTTP_MESSAGING) == 0;
|
||||
|
@ -303,9 +305,9 @@ static void active_outbound_item_reset(nghttp2_active_outbound_item *aob,
|
|||
aob->state = NGHTTP2_OB_POP_ITEM;
|
||||
}
|
||||
|
||||
/* This global variable exists for tests where we want to disable this
|
||||
check. */
|
||||
int nghttp2_enable_strict_first_settings_check = 1;
|
||||
/* The global variable for tests where we want to disable strict
|
||||
preface handling. */
|
||||
int nghttp2_enable_strict_preface = 1;
|
||||
|
||||
static int session_new(nghttp2_session **session_ptr,
|
||||
const nghttp2_session_callbacks *callbacks,
|
||||
|
@ -395,10 +397,10 @@ static int session_new(nghttp2_session **session_ptr,
|
|||
option->peer_max_concurrent_streams;
|
||||
}
|
||||
|
||||
if ((option->opt_set_mask & NGHTTP2_OPT_RECV_CLIENT_PREFACE) &&
|
||||
option->recv_client_preface) {
|
||||
if ((option->opt_set_mask & NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC) &&
|
||||
option->no_recv_client_magic) {
|
||||
|
||||
(*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_RECV_CLIENT_PREFACE;
|
||||
(*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC;
|
||||
}
|
||||
|
||||
if ((option->opt_set_mask & NGHTTP2_OPT_NO_HTTP_MESSAGING) &&
|
||||
|
@ -413,17 +415,23 @@ static int session_new(nghttp2_session **session_ptr,
|
|||
|
||||
session_inbound_frame_reset(*session_ptr);
|
||||
|
||||
if (server &&
|
||||
((*session_ptr)->opt_flags & NGHTTP2_OPTMASK_RECV_CLIENT_PREFACE)) {
|
||||
|
||||
if (nghttp2_enable_strict_preface) {
|
||||
nghttp2_inbound_frame *iframe = &(*session_ptr)->iframe;
|
||||
|
||||
iframe->state = NGHTTP2_IB_READ_CLIENT_PREFACE;
|
||||
iframe->payloadleft = NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN;
|
||||
} else if (nghttp2_enable_strict_first_settings_check) {
|
||||
nghttp2_inbound_frame *iframe = &(*session_ptr)->iframe;
|
||||
if (server &&
|
||||
((*session_ptr)->opt_flags & NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC) ==
|
||||
0) {
|
||||
iframe->state = NGHTTP2_IB_READ_CLIENT_MAGIC;
|
||||
iframe->payloadleft = NGHTTP2_CLIENT_MAGIC_LEN;
|
||||
} else {
|
||||
iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS;
|
||||
}
|
||||
|
||||
iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS;
|
||||
if (!server) {
|
||||
(*session_ptr)->aob.state = NGHTTP2_OB_SEND_CLIENT_MAGIC;
|
||||
nghttp2_bufs_add(&(*session_ptr)->aob.framebufs, NGHTTP2_CLIENT_MAGIC,
|
||||
NGHTTP2_CLIENT_MAGIC_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -2852,6 +2860,25 @@ static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session,
|
|||
|
||||
break;
|
||||
}
|
||||
case NGHTTP2_OB_SEND_CLIENT_MAGIC: {
|
||||
size_t datalen;
|
||||
nghttp2_buf *buf;
|
||||
|
||||
buf = &framebufs->cur->buf;
|
||||
|
||||
if (buf->pos == buf->last) {
|
||||
DEBUGF(fprintf(stderr, "send: end transmission of client magic\n"));
|
||||
active_outbound_item_reset(aob, mem);
|
||||
break;
|
||||
}
|
||||
|
||||
*data_ptr = buf->pos;
|
||||
datalen = nghttp2_buf_len(buf);
|
||||
|
||||
buf->pos += datalen;
|
||||
|
||||
return datalen;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2866,14 +2893,16 @@ ssize_t nghttp2_session_mem_send(nghttp2_session *session,
|
|||
return len;
|
||||
}
|
||||
|
||||
/* We have to call session_after_frame_sent1 here to handle stream
|
||||
closure upon transmission of frames. Otherwise, END_STREAM may
|
||||
be reached to client before we call nghttp2_session_mem_send
|
||||
again and we may get exceeding number of incoming streams. */
|
||||
rv = session_after_frame_sent1(session);
|
||||
if (rv < 0) {
|
||||
assert(nghttp2_is_fatal(rv));
|
||||
return (ssize_t)rv;
|
||||
if (session->aob.item) {
|
||||
/* We have to call session_after_frame_sent1 here to handle stream
|
||||
closure upon transmission of frames. Otherwise, END_STREAM may
|
||||
be reached to client before we call nghttp2_session_mem_send
|
||||
again and we may get exceeding number of incoming streams. */
|
||||
rv = session_after_frame_sent1(session);
|
||||
if (rv < 0) {
|
||||
assert(nghttp2_is_fatal(rv));
|
||||
return (ssize_t)rv;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
|
@ -3006,18 +3035,40 @@ static int session_handle_frame_size_error(nghttp2_session *session,
|
|||
return nghttp2_session_terminate_session(session, NGHTTP2_FRAME_SIZE_ERROR);
|
||||
}
|
||||
|
||||
static int get_error_code_from_lib_error_code(int lib_error_code) {
|
||||
switch (lib_error_code) {
|
||||
case NGHTTP2_ERR_STREAM_CLOSED:
|
||||
return NGHTTP2_STREAM_CLOSED;
|
||||
case NGHTTP2_ERR_HEADER_COMP:
|
||||
return NGHTTP2_COMPRESSION_ERROR;
|
||||
case NGHTTP2_ERR_FRAME_SIZE_ERROR:
|
||||
return NGHTTP2_FRAME_SIZE_ERROR;
|
||||
case NGHTTP2_ERR_FLOW_CONTROL:
|
||||
return NGHTTP2_FLOW_CONTROL_ERROR;
|
||||
case NGHTTP2_ERR_REFUSED_STREAM:
|
||||
return NGHTTP2_REFUSED_STREAM;
|
||||
case NGHTTP2_ERR_PROTO:
|
||||
case NGHTTP2_ERR_HTTP_HEADER:
|
||||
case NGHTTP2_ERR_HTTP_MESSAGING:
|
||||
return NGHTTP2_PROTOCOL_ERROR;
|
||||
default:
|
||||
return NGHTTP2_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static int session_handle_invalid_stream2(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
nghttp2_frame *frame,
|
||||
uint32_t error_code) {
|
||||
int lib_error_code) {
|
||||
int rv;
|
||||
rv = nghttp2_session_add_rst_stream(session, stream_id, error_code);
|
||||
rv = nghttp2_session_add_rst_stream(
|
||||
session, stream_id, get_error_code_from_lib_error_code(lib_error_code));
|
||||
if (rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
if (session->callbacks.on_invalid_frame_recv_callback) {
|
||||
if (session->callbacks.on_invalid_frame_recv_callback(
|
||||
session, frame, error_code, session->user_data) != 0) {
|
||||
session, frame, lib_error_code, session->user_data) != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
}
|
||||
|
@ -3026,16 +3077,16 @@ static int session_handle_invalid_stream2(nghttp2_session *session,
|
|||
|
||||
static int session_handle_invalid_stream(nghttp2_session *session,
|
||||
nghttp2_frame *frame,
|
||||
uint32_t error_code) {
|
||||
int lib_error_code) {
|
||||
return session_handle_invalid_stream2(session, frame->hd.stream_id, frame,
|
||||
error_code);
|
||||
lib_error_code);
|
||||
}
|
||||
|
||||
static int session_inflate_handle_invalid_stream(nghttp2_session *session,
|
||||
nghttp2_frame *frame,
|
||||
uint32_t error_code) {
|
||||
int lib_error_code) {
|
||||
int rv;
|
||||
rv = session_handle_invalid_stream(session, frame, error_code);
|
||||
rv = session_handle_invalid_stream(session, frame, lib_error_code);
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -3047,24 +3098,25 @@ static int session_inflate_handle_invalid_stream(nghttp2_session *session,
|
|||
*/
|
||||
static int session_handle_invalid_connection(nghttp2_session *session,
|
||||
nghttp2_frame *frame,
|
||||
uint32_t error_code,
|
||||
int lib_error_code,
|
||||
const char *reason) {
|
||||
if (session->callbacks.on_invalid_frame_recv_callback) {
|
||||
if (session->callbacks.on_invalid_frame_recv_callback(
|
||||
session, frame, error_code, session->user_data) != 0) {
|
||||
session, frame, lib_error_code, session->user_data) != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
}
|
||||
return nghttp2_session_terminate_session_with_reason(session, error_code,
|
||||
reason);
|
||||
return nghttp2_session_terminate_session_with_reason(
|
||||
session, get_error_code_from_lib_error_code(lib_error_code), reason);
|
||||
}
|
||||
|
||||
static int session_inflate_handle_invalid_connection(nghttp2_session *session,
|
||||
nghttp2_frame *frame,
|
||||
uint32_t error_code,
|
||||
int lib_error_code,
|
||||
const char *reason) {
|
||||
int rv;
|
||||
rv = session_handle_invalid_connection(session, frame, error_code, reason);
|
||||
rv =
|
||||
session_handle_invalid_connection(session, frame, lib_error_code, reason);
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -3170,7 +3222,7 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
|
|||
|
||||
rv =
|
||||
session_handle_invalid_stream2(session, subject_stream->stream_id,
|
||||
frame, NGHTTP2_PROTOCOL_ERROR);
|
||||
frame, NGHTTP2_ERR_HTTP_HEADER);
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -3345,7 +3397,7 @@ static int session_after_header_block_received(nghttp2_session *session) {
|
|||
call_cb = 0;
|
||||
|
||||
rv = session_handle_invalid_stream2(session, stream_id, frame,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
NGHTTP2_ERR_HTTP_MESSAGING);
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -3384,8 +3436,7 @@ int nghttp2_session_on_request_headers_received(nghttp2_session *session,
|
|||
nghttp2_stream *stream;
|
||||
if (frame->hd.stream_id == 0) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
"request HEADERS: stream_id == 0");
|
||||
session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: stream_id == 0");
|
||||
}
|
||||
|
||||
/* If client recieves idle stream from server, it is invalid
|
||||
|
@ -3394,7 +3445,7 @@ int nghttp2_session_on_request_headers_received(nghttp2_session *session,
|
|||
if (!session->server) {
|
||||
if (session_detect_idle_stream(session, frame->hd.stream_id)) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
session, frame, NGHTTP2_ERR_PROTO,
|
||||
"request HEADERS: client received request");
|
||||
}
|
||||
|
||||
|
@ -3411,7 +3462,7 @@ int nghttp2_session_on_request_headers_received(nghttp2_session *session,
|
|||
just ignore HEADERS for now. */
|
||||
if (session_detect_idle_stream(session, frame->hd.stream_id)) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
session, frame, NGHTTP2_ERR_PROTO,
|
||||
"request HEADERS: invalid stream_id");
|
||||
}
|
||||
|
||||
|
@ -3426,19 +3477,18 @@ int nghttp2_session_on_request_headers_received(nghttp2_session *session,
|
|||
|
||||
if (session_is_incoming_concurrent_streams_max(session)) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
session, frame, NGHTTP2_ERR_PROTO,
|
||||
"request HEADERS: max concurrent streams exceeded");
|
||||
}
|
||||
|
||||
if (frame->headers.pri_spec.stream_id == frame->hd.stream_id) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
"request HEADERS: depend on itself");
|
||||
session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: depend on itself");
|
||||
}
|
||||
|
||||
if (session_is_incoming_concurrent_streams_pending_max(session)) {
|
||||
return session_inflate_handle_invalid_stream(session, frame,
|
||||
NGHTTP2_REFUSED_STREAM);
|
||||
NGHTTP2_ERR_REFUSED_STREAM);
|
||||
}
|
||||
|
||||
stream = nghttp2_session_open_stream(
|
||||
|
@ -3465,8 +3515,7 @@ int nghttp2_session_on_response_headers_received(nghttp2_session *session,
|
|||
nghttp2_session_is_my_stream_id(session, frame->hd.stream_id));
|
||||
if (frame->hd.stream_id == 0) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
"response HEADERS: stream_id == 0");
|
||||
session, frame, NGHTTP2_ERR_PROTO, "response HEADERS: stream_id == 0");
|
||||
}
|
||||
if (stream->shut_flags & NGHTTP2_SHUT_RD) {
|
||||
/* half closed (remote): from the spec:
|
||||
|
@ -3476,7 +3525,7 @@ int nghttp2_session_on_response_headers_received(nghttp2_session *session,
|
|||
5.4.2) of type STREAM_CLOSED.
|
||||
*/
|
||||
return session_inflate_handle_invalid_stream(session, frame,
|
||||
NGHTTP2_STREAM_CLOSED);
|
||||
NGHTTP2_ERR_STREAM_CLOSED);
|
||||
}
|
||||
stream->state = NGHTTP2_STREAM_OPENED;
|
||||
rv = session_call_on_begin_headers(session, frame);
|
||||
|
@ -3493,7 +3542,7 @@ int nghttp2_session_on_push_response_headers_received(nghttp2_session *session,
|
|||
assert(stream->state == NGHTTP2_STREAM_RESERVED);
|
||||
if (frame->hd.stream_id == 0) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
session, frame, NGHTTP2_ERR_PROTO,
|
||||
"push response HEADERS: stream_id == 0");
|
||||
}
|
||||
if (session->goaway_flags) {
|
||||
|
@ -3503,12 +3552,12 @@ int nghttp2_session_on_push_response_headers_received(nghttp2_session *session,
|
|||
|
||||
if (session_is_incoming_concurrent_streams_max(session)) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
session, frame, NGHTTP2_ERR_PROTO,
|
||||
"push response HEADERS: max concurrent streams exceeded");
|
||||
}
|
||||
if (session_is_incoming_concurrent_streams_pending_max(session)) {
|
||||
return session_inflate_handle_invalid_stream(session, frame,
|
||||
NGHTTP2_REFUSED_STREAM);
|
||||
NGHTTP2_ERR_REFUSED_STREAM);
|
||||
}
|
||||
|
||||
nghttp2_stream_promise_fulfilled(stream);
|
||||
|
@ -3526,7 +3575,7 @@ int nghttp2_session_on_headers_received(nghttp2_session *session,
|
|||
int rv = 0;
|
||||
if (frame->hd.stream_id == 0) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR, "HEADERS: stream_id == 0");
|
||||
session, frame, NGHTTP2_ERR_PROTO, "HEADERS: stream_id == 0");
|
||||
}
|
||||
if (stream->state == NGHTTP2_STREAM_RESERVED) {
|
||||
/* reserved. The valid push response HEADERS is processed by
|
||||
|
@ -3534,7 +3583,7 @@ int nghttp2_session_on_headers_received(nghttp2_session *session,
|
|||
generic HEADERS is called invalid cases for HEADERS against
|
||||
reserved state. */
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR, "HEADERS: stream in reserved");
|
||||
session, frame, NGHTTP2_ERR_PROTO, "HEADERS: stream in reserved");
|
||||
}
|
||||
if ((stream->shut_flags & NGHTTP2_SHUT_RD)) {
|
||||
/* half closed (remote): from the spec:
|
||||
|
@ -3544,7 +3593,7 @@ int nghttp2_session_on_headers_received(nghttp2_session *session,
|
|||
5.4.2) of type STREAM_CLOSED.
|
||||
*/
|
||||
return session_inflate_handle_invalid_stream(session, frame,
|
||||
NGHTTP2_STREAM_CLOSED);
|
||||
NGHTTP2_ERR_STREAM_CLOSED);
|
||||
}
|
||||
if (nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
|
||||
if (stream->state == NGHTTP2_STREAM_OPENED) {
|
||||
|
@ -3560,7 +3609,7 @@ int nghttp2_session_on_headers_received(nghttp2_session *session,
|
|||
return NGHTTP2_ERR_IGN_HEADER_BLOCK;
|
||||
} else {
|
||||
return session_inflate_handle_invalid_stream(session, frame,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
NGHTTP2_ERR_PROTO);
|
||||
}
|
||||
}
|
||||
/* If this is remote peer initiated stream, it is OK unless it
|
||||
|
@ -3620,8 +3669,8 @@ int nghttp2_session_on_priority_received(nghttp2_session *session,
|
|||
nghttp2_stream *stream;
|
||||
|
||||
if (frame->hd.stream_id == 0) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR, "PRIORITY: stream_id == 0");
|
||||
return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
|
||||
"PRIORITY: stream_id == 0");
|
||||
}
|
||||
|
||||
if (!session->server) {
|
||||
|
@ -3672,14 +3721,14 @@ int nghttp2_session_on_rst_stream_received(nghttp2_session *session,
|
|||
int rv;
|
||||
nghttp2_stream *stream;
|
||||
if (frame->hd.stream_id == 0) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR, "RST_STREAM: stream_id == 0");
|
||||
return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
|
||||
"RST_STREAM: stream_id == 0");
|
||||
}
|
||||
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
|
||||
if (!stream) {
|
||||
if (session_detect_idle_stream(session, frame->hd.stream_id)) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR, "RST_STREAM: stream in idle");
|
||||
session, frame, NGHTTP2_ERR_PROTO, "RST_STREAM: stream in idle");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3899,18 +3948,18 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
|
|||
mem = &session->mem;
|
||||
|
||||
if (frame->hd.stream_id != 0) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR, "SETTINGS: stream_id != 0");
|
||||
return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
|
||||
"SETTINGS: stream_id != 0");
|
||||
}
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
|
||||
if (frame->settings.niv != 0) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_FRAME_SIZE_ERROR,
|
||||
session, frame, NGHTTP2_ERR_FRAME_SIZE_ERROR,
|
||||
"SETTINGS: ACK and payload != 0");
|
||||
}
|
||||
if (session->inflight_niv == -1) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR, "SETTINGS: unexpected ACK");
|
||||
session, frame, NGHTTP2_ERR_PROTO, "SETTINGS: unexpected ACK");
|
||||
}
|
||||
rv = nghttp2_session_update_local_settings(session, session->inflight_iv,
|
||||
session->inflight_niv);
|
||||
|
@ -3918,15 +3967,10 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
|
|||
session->inflight_iv = NULL;
|
||||
session->inflight_niv = -1;
|
||||
if (rv != 0) {
|
||||
uint32_t error_code = NGHTTP2_INTERNAL_ERROR;
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (rv == NGHTTP2_ERR_HEADER_COMP) {
|
||||
error_code = NGHTTP2_COMPRESSION_ERROR;
|
||||
}
|
||||
return session_handle_invalid_connection(session, frame, error_code,
|
||||
NULL);
|
||||
return session_handle_invalid_connection(session, frame, rv, NULL);
|
||||
}
|
||||
return session_call_on_frame_received(session, frame);
|
||||
}
|
||||
|
@ -3939,7 +3983,7 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
|
|||
|
||||
if (entry->value > NGHTTP2_MAX_HEADER_TABLE_SIZE) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_COMPRESSION_ERROR,
|
||||
session, frame, NGHTTP2_ERR_HEADER_COMP,
|
||||
"SETTINGS: too large SETTINGS_HEADER_TABLE_SIZE");
|
||||
}
|
||||
|
||||
|
@ -3950,7 +3994,7 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
|
|||
return rv;
|
||||
} else {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_COMPRESSION_ERROR, NULL);
|
||||
session, frame, NGHTTP2_ERR_HEADER_COMP, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3961,13 +4005,13 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
|
|||
|
||||
if (entry->value != 0 && entry->value != 1) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
session, frame, NGHTTP2_ERR_PROTO,
|
||||
"SETTINGS: invalid SETTINGS_ENBLE_PUSH");
|
||||
}
|
||||
|
||||
if (!session->server && entry->value != 0) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
session, frame, NGHTTP2_ERR_PROTO,
|
||||
"SETTINGS: server attempted to enable push");
|
||||
}
|
||||
|
||||
|
@ -3985,7 +4029,7 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
|
|||
/* Check that initial_window_size < (1u << 31) */
|
||||
if (entry->value > NGHTTP2_MAX_WINDOW_SIZE) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_FLOW_CONTROL_ERROR,
|
||||
session, frame, NGHTTP2_ERR_FLOW_CONTROL,
|
||||
"SETTINGS: too large SETTINGS_INITIAL_WINDOW_SIZE");
|
||||
}
|
||||
|
||||
|
@ -3997,7 +4041,7 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
|
|||
|
||||
if (rv != 0) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_FLOW_CONTROL_ERROR, NULL);
|
||||
session, frame, NGHTTP2_ERR_FLOW_CONTROL, NULL);
|
||||
}
|
||||
|
||||
session->remote_settings.initial_window_size = entry->value;
|
||||
|
@ -4008,7 +4052,7 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
|
|||
if (entry->value < NGHTTP2_MAX_FRAME_SIZE_MIN ||
|
||||
entry->value > NGHTTP2_MAX_FRAME_SIZE_MAX) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
session, frame, NGHTTP2_ERR_PROTO,
|
||||
"SETTINGS: invalid SETTINGS_MAX_FRAME_SIZE");
|
||||
}
|
||||
|
||||
|
@ -4032,7 +4076,7 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
|
|||
}
|
||||
|
||||
return session_handle_invalid_connection(session, frame,
|
||||
NGHTTP2_INTERNAL_ERROR, NULL);
|
||||
NGHTTP2_ERR_INTERNAL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4085,11 +4129,11 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session,
|
|||
|
||||
if (frame->hd.stream_id == 0) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR, "PUSH_PROMISE: stream_id == 0");
|
||||
session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream_id == 0");
|
||||
}
|
||||
if (session->server || session->local_settings.enable_push == 0) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR, "PUSH_PROMISE: push disabled");
|
||||
session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: push disabled");
|
||||
}
|
||||
if (session->goaway_flags) {
|
||||
/* We just dicard PUSH_PROMISE after GOAWAY is sent or
|
||||
|
@ -4099,8 +4143,7 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session,
|
|||
|
||||
if (!nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
"PUSH_PROMISE: invalid stream_id");
|
||||
session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: invalid stream_id");
|
||||
}
|
||||
|
||||
if (!session_is_new_peer_stream_id(session,
|
||||
|
@ -4109,7 +4152,7 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session,
|
|||
illegal stream ID is subject to a connection error of type
|
||||
PROTOCOL_ERROR. */
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
session, frame, NGHTTP2_ERR_PROTO,
|
||||
"PUSH_PROMISE: invalid promised_stream_id");
|
||||
}
|
||||
session->last_recv_stream_id = frame->push_promise.promised_stream_id;
|
||||
|
@ -4119,8 +4162,7 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session,
|
|||
if (!stream) {
|
||||
if (session_detect_idle_stream(session, frame->hd.stream_id)) {
|
||||
return session_inflate_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
"PUSH_PROMISE: stream in idle");
|
||||
session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream in idle");
|
||||
}
|
||||
}
|
||||
rv = nghttp2_session_add_rst_stream(session,
|
||||
|
@ -4189,8 +4231,8 @@ int nghttp2_session_on_ping_received(nghttp2_session *session,
|
|||
nghttp2_frame *frame) {
|
||||
int rv = 0;
|
||||
if (frame->hd.stream_id != 0) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR, "PING: stream_id != 0");
|
||||
return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
|
||||
"PING: stream_id != 0");
|
||||
}
|
||||
if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0 &&
|
||||
!session_is_closing(session)) {
|
||||
|
@ -4219,8 +4261,8 @@ int nghttp2_session_on_goaway_received(nghttp2_session *session,
|
|||
int rv;
|
||||
|
||||
if (frame->hd.stream_id != 0) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR, "GOAWAY: stream_id != 0");
|
||||
return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
|
||||
"GOAWAY: stream_id != 0");
|
||||
}
|
||||
/* Spec says Endpoints MUST NOT increase the value they send in the
|
||||
last stream identifier. */
|
||||
|
@ -4228,8 +4270,7 @@ int nghttp2_session_on_goaway_received(nghttp2_session *session,
|
|||
!nghttp2_session_is_my_stream_id(session,
|
||||
frame->goaway.last_stream_id)) ||
|
||||
session->remote_last_stream_id < frame->goaway.last_stream_id) {
|
||||
return session_handle_invalid_connection(session, frame,
|
||||
NGHTTP2_PROTOCOL_ERROR,
|
||||
return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
|
||||
"GOAWAY: invalid last_stream_id");
|
||||
}
|
||||
|
||||
|
@ -4265,14 +4306,14 @@ session_on_connection_window_update_received(nghttp2_session *session,
|
|||
nghttp2_frame *frame) {
|
||||
/* Handle connection-level flow control */
|
||||
if (frame->window_update.window_size_increment == 0) {
|
||||
return session_handle_invalid_connection(session, frame,
|
||||
NGHTTP2_PROTOCOL_ERROR, NULL);
|
||||
return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment <
|
||||
session->remote_window_size) {
|
||||
return session_handle_invalid_connection(session, frame,
|
||||
NGHTTP2_FLOW_CONTROL_ERROR, NULL);
|
||||
NGHTTP2_ERR_FLOW_CONTROL, NULL);
|
||||
}
|
||||
session->remote_window_size += frame->window_update.window_size_increment;
|
||||
|
||||
|
@ -4286,25 +4327,22 @@ static int session_on_stream_window_update_received(nghttp2_session *session,
|
|||
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
|
||||
if (!stream) {
|
||||
if (session_detect_idle_stream(session, frame->hd.stream_id)) {
|
||||
return session_handle_invalid_connection(session, frame,
|
||||
NGHTTP2_PROTOCOL_ERROR,
|
||||
"WINDOW_UPDATE to idle stream");
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_ERR_PROTO, "WINDOW_UPDATE to idle stream");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (state_reserved_remote(session, stream)) {
|
||||
return session_handle_invalid_connection(
|
||||
session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
"WINDOW_UPADATE to reserved stream");
|
||||
session, frame, NGHTTP2_ERR_PROTO, "WINDOW_UPADATE to reserved stream");
|
||||
}
|
||||
if (frame->window_update.window_size_increment == 0) {
|
||||
return session_handle_invalid_stream(session, frame,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
return session_handle_invalid_stream(session, frame, NGHTTP2_ERR_PROTO);
|
||||
}
|
||||
if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment <
|
||||
stream->remote_window_size) {
|
||||
return session_handle_invalid_stream(session, frame,
|
||||
NGHTTP2_FLOW_CONTROL_ERROR);
|
||||
NGHTTP2_ERR_FLOW_CONTROL);
|
||||
}
|
||||
stream->remote_window_size += frame->window_update.window_size_increment;
|
||||
|
||||
|
@ -4340,18 +4378,6 @@ static int session_process_window_update_frame(nghttp2_session *session) {
|
|||
return nghttp2_session_on_window_update_received(session, frame);
|
||||
}
|
||||
|
||||
/* static int get_error_code_from_lib_error_code(int lib_error_code) */
|
||||
/* { */
|
||||
/* switch(lib_error_code) { */
|
||||
/* case NGHTTP2_ERR_HEADER_COMP: */
|
||||
/* return NGHTTP2_COMPRESSION_ERROR; */
|
||||
/* case NGHTTP2_ERR_FRAME_SIZE_ERROR: */
|
||||
/* return NGHTTP2_FRAME_SIZE_ERROR; */
|
||||
/* default: */
|
||||
/* return NGHTTP2_PROTOCOL_ERROR; */
|
||||
/* } */
|
||||
/* } */
|
||||
|
||||
int nghttp2_session_on_data_received(nghttp2_session *session,
|
||||
nghttp2_frame *frame) {
|
||||
int rv = 0;
|
||||
|
@ -4789,14 +4815,13 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
|||
|
||||
for (;;) {
|
||||
switch (iframe->state) {
|
||||
case NGHTTP2_IB_READ_CLIENT_PREFACE:
|
||||
case NGHTTP2_IB_READ_CLIENT_MAGIC:
|
||||
readlen = nghttp2_min(inlen, iframe->payloadleft);
|
||||
|
||||
if (memcmp(NGHTTP2_CLIENT_CONNECTION_PREFACE +
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN -
|
||||
if (memcmp(NGHTTP2_CLIENT_MAGIC + NGHTTP2_CLIENT_MAGIC_LEN -
|
||||
iframe->payloadleft,
|
||||
in, readlen) != 0) {
|
||||
return NGHTTP2_ERR_BAD_PREFACE;
|
||||
return NGHTTP2_ERR_BAD_CLIENT_MAGIC;
|
||||
}
|
||||
|
||||
iframe->payloadleft -= readlen;
|
||||
|
|
|
@ -46,14 +46,15 @@
|
|||
*/
|
||||
typedef enum {
|
||||
NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE = 1 << 0,
|
||||
NGHTTP2_OPTMASK_RECV_CLIENT_PREFACE = 1 << 1,
|
||||
NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC = 1 << 1,
|
||||
NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2
|
||||
} nghttp2_optmask;
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_OB_POP_ITEM,
|
||||
NGHTTP2_OB_SEND_DATA,
|
||||
NGHTTP2_OB_SEND_NO_COPY
|
||||
NGHTTP2_OB_SEND_NO_COPY,
|
||||
NGHTTP2_OB_SEND_CLIENT_MAGIC
|
||||
} nghttp2_outbound_state;
|
||||
|
||||
typedef struct {
|
||||
|
@ -69,7 +70,7 @@ typedef struct {
|
|||
/* Internal state when receiving incoming frame */
|
||||
typedef enum {
|
||||
/* Receiving frame header */
|
||||
NGHTTP2_IB_READ_CLIENT_PREFACE,
|
||||
NGHTTP2_IB_READ_CLIENT_MAGIC,
|
||||
NGHTTP2_IB_READ_FIRST_SETTINGS,
|
||||
NGHTTP2_IB_READ_HEAD,
|
||||
NGHTTP2_IB_READ_NBYTE,
|
||||
|
|
|
@ -376,15 +376,6 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_submit_altsvc(nghttp2_session *session _U_, uint8_t flags _U_,
|
||||
int32_t stream_id _U_, uint32_t max_age _U_,
|
||||
uint16_t port _U_, const uint8_t *protocol_id _U_,
|
||||
size_t protocol_id_len _U_, const uint8_t *host _U_,
|
||||
size_t host_len _U_, const uint8_t *origin _U_,
|
||||
size_t origin_len _U_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec,
|
||||
const nghttp2_data_provider *data_prd) {
|
||||
uint8_t flags = NGHTTP2_FLAG_NONE;
|
||||
|
|
|
@ -1237,7 +1237,6 @@ if asyncio:
|
|||
logging.info('connection_made, address:%s, port:%s', address[0], address[1])
|
||||
|
||||
self.transport = transport
|
||||
self.connection_header = cnghttp2.NGHTTP2_CLIENT_CONNECTION_PREFACE
|
||||
sock = self.transport.get_extra_info('socket')
|
||||
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||
ssl_ctx = self.transport.get_extra_info('sslcontext')
|
||||
|
@ -1247,6 +1246,16 @@ if asyncio:
|
|||
if protocol.encode('utf-8') != \
|
||||
cnghttp2.NGHTTP2_PROTO_VERSION_ID:
|
||||
self.transport.abort()
|
||||
return
|
||||
try:
|
||||
self.http2 = _HTTP2SessionCore\
|
||||
(self.transport,
|
||||
self.RequestHandlerClass)
|
||||
except Exception as err:
|
||||
sys.stderr.write(traceback.format_exc())
|
||||
self.transport.abort()
|
||||
return
|
||||
|
||||
|
||||
def connection_lost(self, exc):
|
||||
logging.info('connection_lost')
|
||||
|
@ -1254,28 +1263,6 @@ if asyncio:
|
|||
self.http2 = None
|
||||
|
||||
def data_received(self, data):
|
||||
nread = min(len(data), len(self.connection_header))
|
||||
|
||||
if self.connection_header.startswith(data[:nread]):
|
||||
data = data[nread:]
|
||||
self.connection_header = self.connection_header[nread:]
|
||||
if len(self.connection_header) == 0:
|
||||
try:
|
||||
self.http2 = _HTTP2SessionCore\
|
||||
(self.transport,
|
||||
self.RequestHandlerClass)
|
||||
except Exception as err:
|
||||
sys.stderr.write(traceback.format_exc())
|
||||
self.transport.abort()
|
||||
return
|
||||
|
||||
self.data_received = self.data_received2
|
||||
self.resume_writing = self.resume_writing2
|
||||
self.data_received(data)
|
||||
else:
|
||||
self.transport.abort()
|
||||
|
||||
def data_received2(self, data):
|
||||
try:
|
||||
self.http2.data_received(data)
|
||||
except Exception as err:
|
||||
|
@ -1283,7 +1270,7 @@ if asyncio:
|
|||
self.transport.close()
|
||||
return
|
||||
|
||||
def resume_writing2(self):
|
||||
def resume_writing(self):
|
||||
try:
|
||||
self.http2.send_data()
|
||||
except Exception as err:
|
||||
|
@ -1490,8 +1477,6 @@ if asyncio:
|
|||
cnghttp2.NGHTTP2_PROTO_VERSION_ID:
|
||||
self.transport.abort()
|
||||
|
||||
# Send preamble
|
||||
self.transport.write(cnghttp2.NGHTTP2_CLIENT_CONNECTION_PREFACE)
|
||||
self.http2 = _HTTP2ClientSessionCore(self.transport)
|
||||
|
||||
# Clear pending requests
|
||||
|
|
|
@ -99,17 +99,13 @@ template <typename Array> void append_nv(Stream *stream, const Array &nva) {
|
|||
} // namespace
|
||||
|
||||
Config::Config()
|
||||
: stream_read_timeout(60.), stream_write_timeout(60.),
|
||||
session_option(nullptr), data_ptr(nullptr), padding(0), num_worker(1),
|
||||
max_concurrent_streams(100), header_table_size(-1), port(0),
|
||||
verbose(false), daemon(false), verify_client(false), no_tls(false),
|
||||
error_gzip(false), early_response(false), hexdump(false),
|
||||
echo_upload(false) {
|
||||
nghttp2_option_new(&session_option);
|
||||
nghttp2_option_set_recv_client_preface(session_option, 1);
|
||||
}
|
||||
: stream_read_timeout(60.), stream_write_timeout(60.), data_ptr(nullptr),
|
||||
padding(0), num_worker(1), max_concurrent_streams(100),
|
||||
header_table_size(-1), port(0), verbose(false), daemon(false),
|
||||
verify_client(false), no_tls(false), error_gzip(false),
|
||||
early_response(false), hexdump(false), echo_upload(false) {}
|
||||
|
||||
Config::~Config() { nghttp2_option_del(session_option); }
|
||||
Config::~Config() {}
|
||||
|
||||
namespace {
|
||||
void stream_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||
|
@ -474,7 +470,7 @@ int Http2Handler::read_clear() {
|
|||
|
||||
rv = nghttp2_session_mem_recv(session_, buf.data(), nread);
|
||||
if (rv < 0) {
|
||||
if (rv != NGHTTP2_ERR_BAD_PREFACE) {
|
||||
if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) {
|
||||
std::cerr << "nghttp2_session_mem_recv() returned error: "
|
||||
<< nghttp2_strerror(rv) << std::endl;
|
||||
}
|
||||
|
@ -601,7 +597,7 @@ int Http2Handler::read_tls() {
|
|||
|
||||
rv = nghttp2_session_mem_recv(session_, buf.data(), nread);
|
||||
if (rv < 0) {
|
||||
if (rv != NGHTTP2_ERR_BAD_PREFACE) {
|
||||
if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) {
|
||||
std::cerr << "nghttp2_session_mem_recv() returned error: "
|
||||
<< nghttp2_strerror(rv) << std::endl;
|
||||
}
|
||||
|
@ -673,13 +669,13 @@ int Http2Handler::on_write() { return write_(*this); }
|
|||
int Http2Handler::connection_made() {
|
||||
int r;
|
||||
|
||||
auto config = sessions_->get_config();
|
||||
r = nghttp2_session_server_new(&session_, sessions_->get_callbacks(), this);
|
||||
|
||||
r = nghttp2_session_server_new2(&session_, sessions_->get_callbacks(), this,
|
||||
config->session_option);
|
||||
if (r != 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
auto config = sessions_->get_config();
|
||||
std::array<nghttp2_settings_entry, 4> entry;
|
||||
size_t niv = 1;
|
||||
|
||||
|
|
|
@ -59,7 +59,6 @@ struct Config {
|
|||
std::string address;
|
||||
ev_tstamp stream_read_timeout;
|
||||
ev_tstamp stream_write_timeout;
|
||||
nghttp2_option *session_option;
|
||||
void *data_ptr;
|
||||
size_t padding;
|
||||
size_t num_worker;
|
||||
|
|
|
@ -140,8 +140,6 @@ const char *strframetype(uint8_t type) {
|
|||
return "GOAWAY";
|
||||
case NGHTTP2_WINDOW_UPDATE:
|
||||
return "WINDOW_UPDATE";
|
||||
case NGHTTP2_EXT_ALTSVC:
|
||||
return "ALTSVC";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
@ -372,34 +370,6 @@ void print_frame(print_type ptype, const nghttp2_frame *frame) {
|
|||
fprintf(outfile, "(window_size_increment=%d)\n",
|
||||
frame->window_update.window_size_increment);
|
||||
break;
|
||||
case NGHTTP2_EXT_ALTSVC: {
|
||||
print_frame_attr_indent();
|
||||
|
||||
auto altsvc = static_cast<const nghttp2_ext_altsvc *>(frame->ext.payload);
|
||||
|
||||
fprintf(outfile, "(max-age=%u, port=%u, protocol_id=", altsvc->max_age,
|
||||
altsvc->port);
|
||||
|
||||
if (altsvc->protocol_id_len) {
|
||||
fwrite(altsvc->protocol_id, altsvc->protocol_id_len, 1, outfile);
|
||||
}
|
||||
|
||||
fprintf(outfile, ", host=");
|
||||
|
||||
if (altsvc->host_len) {
|
||||
fwrite(altsvc->host, altsvc->host_len, 1, outfile);
|
||||
}
|
||||
|
||||
fprintf(outfile, ", origin=");
|
||||
|
||||
if (altsvc->origin_len) {
|
||||
fwrite(altsvc->origin, altsvc->origin_len, 1, outfile);
|
||||
}
|
||||
|
||||
fprintf(outfile, ")\n");
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -439,10 +409,11 @@ int verbose_on_frame_recv_callback(nghttp2_session *session,
|
|||
|
||||
int verbose_on_invalid_frame_recv_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
uint32_t error_code,
|
||||
int lib_error_code,
|
||||
void *user_data) {
|
||||
print_timer();
|
||||
fprintf(outfile, " [INVALID; status=%s] recv ", strstatus(error_code));
|
||||
fprintf(outfile, " [INVALID; error=%s] recv ",
|
||||
nghttp2_strerror(lib_error_code));
|
||||
print_frame(PRINT_RECV, frame);
|
||||
fflush(outfile);
|
||||
return 0;
|
||||
|
|
|
@ -51,8 +51,7 @@ int verbose_on_frame_recv_callback(nghttp2_session *session,
|
|||
|
||||
int verbose_on_invalid_frame_recv_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
uint32_t error_code,
|
||||
void *user_data);
|
||||
int lib_error_code, void *user_data);
|
||||
|
||||
int verbose_on_frame_send_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data);
|
||||
|
|
|
@ -75,10 +75,6 @@ void session_impl::connected(tcp::resolver::iterator endpoint_it) {
|
|||
|
||||
socket().set_option(boost::asio::ip::tcp::no_delay(true));
|
||||
|
||||
std::copy_n(NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN, std::begin(wb_));
|
||||
wblen_ = NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN;
|
||||
|
||||
do_write();
|
||||
do_read();
|
||||
|
||||
|
|
|
@ -268,17 +268,7 @@ int http2_handler::start() {
|
|||
nghttp2_session_callbacks_set_on_frame_not_send_callback(
|
||||
callbacks, on_frame_not_send_callback);
|
||||
|
||||
nghttp2_option *option;
|
||||
rv = nghttp2_option_new(&option);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto opt_del = defer(nghttp2_option_del, option);
|
||||
|
||||
nghttp2_option_set_recv_client_preface(option, 1);
|
||||
|
||||
rv = nghttp2_session_server_new2(&session_, callbacks, this, option);
|
||||
rv = nghttp2_session_server_new(&session_, callbacks, this);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -202,12 +202,6 @@ void Http2Session::on_connect() {
|
|||
extra_connection_window);
|
||||
}
|
||||
|
||||
auto &wb = client_->wb;
|
||||
assert(wb.wleft() >= NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
|
||||
wb.write(NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
|
||||
client_->signal_write();
|
||||
}
|
||||
|
||||
|
|
|
@ -954,9 +954,6 @@ int HttpClient::connection_made() {
|
|||
request_done(stream_user_data);
|
||||
}
|
||||
}
|
||||
// Send connection header here
|
||||
wb.write(NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
// If upgrade succeeds, the SETTINGS value sent with
|
||||
// HTTP2-Settings header field has already been submitted to
|
||||
// session object.
|
||||
|
|
11
src/shrpx.cc
11
src/shrpx.cc
|
@ -768,7 +768,7 @@ bool conf_exists(const char *path) {
|
|||
} // namespace
|
||||
|
||||
namespace {
|
||||
const char *DEFAULT_NPN_LIST = "h2,h2-16," NGHTTP2_PROTO_VERSION_ID ","
|
||||
const char *DEFAULT_NPN_LIST = "h2,h2-16,h2-14,"
|
||||
#ifdef HAVE_SPDYLAY
|
||||
"spdy/3.1,"
|
||||
#endif // HAVE_SPDYLAY
|
||||
|
@ -903,6 +903,7 @@ void fill_default_config() {
|
|||
|
||||
nghttp2_option_new(&mod_config()->http2_option);
|
||||
nghttp2_option_set_no_auto_window_update(get_config()->http2_option, 1);
|
||||
nghttp2_option_set_no_recv_client_magic(get_config()->http2_option, 1);
|
||||
|
||||
nghttp2_option_new(&mod_config()->http2_client_option);
|
||||
nghttp2_option_set_no_auto_window_update(get_config()->http2_client_option,
|
||||
|
@ -1344,10 +1345,10 @@ HTTP:
|
|||
--altsvc=<PROTOID,PORT[,HOST,[ORIGIN]]>
|
||||
Specify protocol ID, port, host and origin of
|
||||
alternative service. <HOST> and <ORIGIN> are optional.
|
||||
They are advertised in alt-svc header field or HTTP/2
|
||||
ALTSVC frame. This option can be used multiple times to
|
||||
specify multiple alternative services. Example:
|
||||
--altsvc=h2,443
|
||||
They are advertised in alt-svc header field only in
|
||||
HTTP/1.1 frontend. This option can be used multiple
|
||||
times to specify multiple alternative services.
|
||||
Example: --altsvc=h2,443
|
||||
--add-response-header=<HEADER>
|
||||
Specify additional header field to add to response
|
||||
header set. This option just appends header field and
|
||||
|
|
|
@ -281,8 +281,7 @@ int ClientHandler::upstream_write() {
|
|||
|
||||
int ClientHandler::upstream_http2_connhd_read() {
|
||||
auto nread = std::min(left_connhd_len_, rb_.rleft());
|
||||
if (memcmp(NGHTTP2_CLIENT_CONNECTION_PREFACE +
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN - left_connhd_len_,
|
||||
if (memcmp(NGHTTP2_CLIENT_MAGIC + NGHTTP2_CLIENT_MAGIC_LEN - left_connhd_len_,
|
||||
rb_.pos, nread) != 0) {
|
||||
// There is no downgrade path here. Just drop the connection.
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
|
@ -311,8 +310,7 @@ int ClientHandler::upstream_http2_connhd_read() {
|
|||
|
||||
int ClientHandler::upstream_http1_connhd_read() {
|
||||
auto nread = std::min(left_connhd_len_, rb_.rleft());
|
||||
if (memcmp(NGHTTP2_CLIENT_CONNECTION_PREFACE +
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN - left_connhd_len_,
|
||||
if (memcmp(NGHTTP2_CLIENT_MAGIC + NGHTTP2_CLIENT_MAGIC_LEN - left_connhd_len_,
|
||||
rb_.pos, nread) != 0) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
CLOG(INFO, this) << "This is HTTP/1.1 connection, "
|
||||
|
@ -320,7 +318,7 @@ int ClientHandler::upstream_http1_connhd_read() {
|
|||
}
|
||||
|
||||
// Reset header length for later HTTP/2 upgrade
|
||||
left_connhd_len_ = NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN;
|
||||
left_connhd_len_ = NGHTTP2_CLIENT_MAGIC_LEN;
|
||||
on_read_ = &ClientHandler::upstream_read;
|
||||
on_write_ = &ClientHandler::upstream_write;
|
||||
|
||||
|
@ -364,7 +362,7 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
|
|||
get_config()->read_burst, writecb, readcb, timeoutcb, this),
|
||||
ipaddr_(ipaddr), port_(port), worker_(worker),
|
||||
http2session_(worker_->next_http2_session()),
|
||||
left_connhd_len_(NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN),
|
||||
left_connhd_len_(NGHTTP2_CLIENT_MAGIC_LEN),
|
||||
should_close_after_write_(false) {
|
||||
|
||||
++worker_->get_worker_stat()->num_connections;
|
||||
|
@ -462,9 +460,7 @@ int ClientHandler::validate_next_proto() {
|
|||
next_proto_len)) {
|
||||
break;
|
||||
}
|
||||
if (util::check_h2_is_selected(next_proto, next_proto_len) ||
|
||||
(next_proto_len == sizeof("h2-16") - 1 &&
|
||||
memcmp("h2-16", next_proto, next_proto_len) == 0)) {
|
||||
if (util::check_h2_is_selected(next_proto, next_proto_len)) {
|
||||
|
||||
on_read_ = &ClientHandler::upstream_http2_connhd_read;
|
||||
|
||||
|
@ -650,8 +646,6 @@ ConnectBlocker *ClientHandler::get_connect_blocker() const {
|
|||
|
||||
void ClientHandler::direct_http2_upgrade() {
|
||||
upstream_ = make_unique<Http2Upstream>(this);
|
||||
// TODO We don't know exact h2 draft version in direct upgrade. We
|
||||
// just use library default for now.
|
||||
alpn_ = NGHTTP2_CLEARTEXT_PROTO_VERSION_ID;
|
||||
on_read_ = &ClientHandler::upstream_read;
|
||||
}
|
||||
|
|
|
@ -1285,13 +1285,6 @@ int Http2Session::connection_made() {
|
|||
}
|
||||
}
|
||||
|
||||
auto nwrite = wb_.write(NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
if (nwrite != NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN) {
|
||||
SSLOG(FATAL, this) << "buffer is too small to send connection preface";
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto must_terminate = !get_config()->downstream_no_tls &&
|
||||
!ssl::check_http2_requirement(conn_.tls.ssl);
|
||||
|
||||
|
|
|
@ -781,24 +781,6 @@ Http2Upstream::Http2Upstream(ClientHandler *handler)
|
|||
}
|
||||
}
|
||||
|
||||
if (!get_config()->altsvcs.empty()) {
|
||||
// Set max_age to 24hrs, which is default for alt-svc header
|
||||
// field.
|
||||
for (auto &altsvc : get_config()->altsvcs) {
|
||||
rv = nghttp2_submit_altsvc(
|
||||
session_, NGHTTP2_FLAG_NONE, 0, 86400, altsvc.port,
|
||||
reinterpret_cast<const uint8_t *>(altsvc.protocol_id),
|
||||
altsvc.protocol_id_len,
|
||||
reinterpret_cast<const uint8_t *>(altsvc.host), altsvc.host_len,
|
||||
reinterpret_cast<const uint8_t *>(altsvc.origin), altsvc.origin_len);
|
||||
|
||||
if (rv != 0) {
|
||||
ULOG(ERROR, this) << "nghttp2_submit_altsvc() returned error: "
|
||||
<< nghttp2_strerror(rv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We wait for SETTINGS ACK at least 10 seconds.
|
||||
ev_timer_init(&settings_timer_, settings_timeout_cb, 10., 0.);
|
||||
|
||||
|
@ -834,7 +816,7 @@ int Http2Upstream::on_read() {
|
|||
if (rb->rleft()) {
|
||||
rv = nghttp2_session_mem_recv(session_, rb->pos, rb->rleft());
|
||||
if (rv < 0) {
|
||||
if (rv != NGHTTP2_ERR_BAD_PREFACE) {
|
||||
if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) {
|
||||
ULOG(ERROR, this) << "nghttp2_session_recv() returned error: "
|
||||
<< nghttp2_strerror(rv);
|
||||
}
|
||||
|
|
|
@ -567,7 +567,7 @@ int htp_hdrs_completecb(http_parser *htp) {
|
|||
// Content-Length or Transfer-Encoding: chunked. Some server send
|
||||
// 304 status code with nonzero Content-Length, but without response
|
||||
// body. See
|
||||
// http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-20#section-3.3
|
||||
// https://tools.ietf.org/html/rfc7230#section-3.3
|
||||
|
||||
// TODO It seems that the cases other than HEAD are handled by
|
||||
// http-parser. Need test.
|
||||
|
|
18
src/util.cc
18
src/util.cc
|
@ -781,9 +781,9 @@ int64_t to_time64(const timeval &tv) {
|
|||
}
|
||||
|
||||
bool check_h2_is_selected(const unsigned char *proto, size_t len) {
|
||||
return streq_l(NGHTTP2_H2, proto, len) ||
|
||||
return streq_l(NGHTTP2_PROTO_VERSION_ID, proto, len) ||
|
||||
streq_l(NGHTTP2_H2_16, proto, len) ||
|
||||
streq_l(NGHTTP2_PROTO_VERSION_ID, proto, len);
|
||||
streq_l(NGHTTP2_H2_14, proto, len);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -803,23 +803,23 @@ bool select_h2(const unsigned char **out, unsigned char *outlen,
|
|||
|
||||
bool select_h2(const unsigned char **out, unsigned char *outlen,
|
||||
const unsigned char *in, unsigned int inlen) {
|
||||
return select_h2(out, outlen, in, inlen, NGHTTP2_H2_ALPN,
|
||||
str_size(NGHTTP2_H2_ALPN)) ||
|
||||
return select_h2(out, outlen, in, inlen, NGHTTP2_PROTO_ALPN,
|
||||
str_size(NGHTTP2_PROTO_ALPN)) ||
|
||||
select_h2(out, outlen, in, inlen, NGHTTP2_H2_16_ALPN,
|
||||
str_size(NGHTTP2_H2_16_ALPN)) ||
|
||||
select_h2(out, outlen, in, inlen, NGHTTP2_PROTO_ALPN,
|
||||
str_size(NGHTTP2_PROTO_ALPN));
|
||||
select_h2(out, outlen, in, inlen, NGHTTP2_H2_14_ALPN,
|
||||
str_size(NGHTTP2_H2_14_ALPN));
|
||||
}
|
||||
|
||||
std::vector<unsigned char> get_default_alpn() {
|
||||
auto res = std::vector<unsigned char>(str_size(NGHTTP2_PROTO_ALPN) +
|
||||
str_size(NGHTTP2_H2_16_ALPN) +
|
||||
str_size(NGHTTP2_H2_ALPN));
|
||||
str_size(NGHTTP2_H2_14_ALPN));
|
||||
auto p = std::begin(res);
|
||||
|
||||
p = std::copy_n(NGHTTP2_H2_ALPN, str_size(NGHTTP2_H2_ALPN), p);
|
||||
p = std::copy_n(NGHTTP2_H2_16_ALPN, str_size(NGHTTP2_H2_16_ALPN), p);
|
||||
p = std::copy_n(NGHTTP2_PROTO_ALPN, str_size(NGHTTP2_PROTO_ALPN), p);
|
||||
p = std::copy_n(NGHTTP2_H2_16_ALPN, str_size(NGHTTP2_H2_16_ALPN), p);
|
||||
p = std::copy_n(NGHTTP2_H2_14_ALPN, str_size(NGHTTP2_H2_14_ALPN), p);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -50,13 +50,13 @@
|
|||
namespace nghttp2 {
|
||||
|
||||
// The additional HTTP/2 protocol ALPN protocol identifier we also
|
||||
// supports for our applications. This will be removed once HTTP/2
|
||||
// specification is finalized.
|
||||
// supports for our applications to make smooth migration into final
|
||||
// h2 ALPN ID.
|
||||
#define NGHTTP2_H2_16_ALPN "\x5h2-16"
|
||||
#define NGHTTP2_H2_16 "h2-16"
|
||||
|
||||
#define NGHTTP2_H2_ALPN "\x2h2"
|
||||
#define NGHTTP2_H2 "h2"
|
||||
#define NGHTTP2_H2_14_ALPN "\x5h2-14"
|
||||
#define NGHTTP2_H2_14 "h2-14"
|
||||
|
||||
namespace util {
|
||||
|
||||
|
|
|
@ -184,7 +184,7 @@ void test_util_select_h2(void) {
|
|||
unsigned char outlen = 0;
|
||||
|
||||
// Check single entry and select it.
|
||||
const unsigned char t1[] = "\x5h2-14";
|
||||
const unsigned char t1[] = "\x2h2";
|
||||
CU_ASSERT(util::select_h2(&out, &outlen, t1, sizeof(t1) - 1));
|
||||
CU_ASSERT(
|
||||
memcmp(NGHTTP2_PROTO_VERSION_ID, out, NGHTTP2_PROTO_VERSION_ID_LEN) == 0);
|
||||
|
@ -198,9 +198,10 @@ void test_util_select_h2(void) {
|
|||
const unsigned char t2[] = "\x6h2-14";
|
||||
CU_ASSERT(!util::select_h2(&out, &outlen, t2, sizeof(t2) - 1));
|
||||
|
||||
// Check the case where h2-14 is located after bogus ID.
|
||||
const unsigned char t3[] = "\x2h3\x5h2-14";
|
||||
// Check the case where h2 is located after bogus ID.
|
||||
const unsigned char t3[] = "\x2h3\x2h2";
|
||||
CU_ASSERT(util::select_h2(&out, &outlen, t3, sizeof(t3) - 1));
|
||||
|
||||
CU_ASSERT(
|
||||
memcmp(NGHTTP2_PROTO_VERSION_ID, out, NGHTTP2_PROTO_VERSION_ID_LEN) == 0);
|
||||
CU_ASSERT(NGHTTP2_PROTO_VERSION_ID_LEN == outlen);
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
#include "nghttp2_helper_test.h"
|
||||
#include "nghttp2_buf_test.h"
|
||||
|
||||
extern int nghttp2_enable_strict_first_settings_check;
|
||||
extern int nghttp2_enable_strict_preface;
|
||||
|
||||
static int init_suite1(void) { return 0; }
|
||||
|
||||
|
@ -51,7 +51,7 @@ int main(int argc _U_, char *argv[] _U_) {
|
|||
CU_pSuite pSuite = NULL;
|
||||
unsigned int num_tests_failed;
|
||||
|
||||
nghttp2_enable_strict_first_settings_check = 0;
|
||||
nghttp2_enable_strict_preface = 0;
|
||||
|
||||
/* initialize the CUnit test registry */
|
||||
if (CUE_SUCCESS != CU_initialize_registry())
|
||||
|
@ -250,8 +250,8 @@ int main(int argc _U_, char *argv[] _U_) {
|
|||
test_nghttp2_session_graceful_shutdown) ||
|
||||
!CU_add_test(pSuite, "session_on_header_temporal_failure",
|
||||
test_nghttp2_session_on_header_temporal_failure) ||
|
||||
!CU_add_test(pSuite, "session_recv_client_preface",
|
||||
test_nghttp2_session_recv_client_preface) ||
|
||||
!CU_add_test(pSuite, "session_recv_client_magic",
|
||||
test_nghttp2_session_recv_client_magic) ||
|
||||
!CU_add_test(pSuite, "session_delete_data_item",
|
||||
test_nghttp2_session_delete_data_item) ||
|
||||
!CU_add_test(pSuite, "session_open_idle_stream",
|
||||
|
|
|
@ -30,9 +30,8 @@
|
|||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
static void http2(void) {
|
||||
const unsigned char p[] = {8, 'h', 't', 't', 'p', '/', '1', '.',
|
||||
'1', 5, 'h', '2', '-', '1', '4', 6,
|
||||
's', 'p', 'd', 'y', '/', '3'};
|
||||
const unsigned char p[] = {8, 'h', 't', 't', 'p', '/', '1', '.', '1', 2,
|
||||
'h', '2', 6, 's', 'p', 'd', 'y', '/', '3'};
|
||||
unsigned char outlen;
|
||||
unsigned char *out;
|
||||
CU_ASSERT(1 == nghttp2_select_next_protocol(&out, &outlen, p, sizeof(p)));
|
||||
|
|
|
@ -36,6 +36,8 @@
|
|||
#include "nghttp2_test_helper.h"
|
||||
#include "nghttp2_priority_spec.h"
|
||||
|
||||
extern int nghttp2_enable_strict_preface;
|
||||
|
||||
#define OB_CTRL(ITEM) nghttp2_outbound_item_get_ctrl_frame(ITEM)
|
||||
#define OB_CTRL_TYPE(ITEM) nghttp2_outbound_item_get_ctrl_frame_type(ITEM)
|
||||
#define OB_DATA(ITEM) nghttp2_outbound_item_get_data_frame(ITEM)
|
||||
|
@ -184,7 +186,7 @@ static int on_frame_recv_callback(nghttp2_session *session _U_,
|
|||
|
||||
static int on_invalid_frame_recv_callback(nghttp2_session *session _U_,
|
||||
const nghttp2_frame *frame _U_,
|
||||
nghttp2_error_code error_code _U_,
|
||||
int lib_error_code _U_,
|
||||
void *user_data) {
|
||||
my_user_data *ud = (my_user_data *)user_data;
|
||||
++ud->invalid_frame_recv_cb_called;
|
||||
|
@ -6781,29 +6783,25 @@ void test_nghttp2_session_on_header_temporal_failure(void) {
|
|||
nghttp2_bufs_free(&bufs);
|
||||
}
|
||||
|
||||
void test_nghttp2_session_recv_client_preface(void) {
|
||||
void test_nghttp2_session_recv_client_magic(void) {
|
||||
nghttp2_session *session;
|
||||
nghttp2_session_callbacks callbacks;
|
||||
nghttp2_option *option;
|
||||
ssize_t rv;
|
||||
nghttp2_frame ping_frame;
|
||||
uint8_t buf[16];
|
||||
|
||||
/* enable global nghttp2_enable_strict_preface here */
|
||||
nghttp2_enable_strict_preface = 1;
|
||||
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
|
||||
nghttp2_option_new(&option);
|
||||
nghttp2_option_set_recv_client_preface(option, 1);
|
||||
|
||||
/* Check success case */
|
||||
nghttp2_session_server_new2(&session, &callbacks, NULL, option);
|
||||
nghttp2_session_server_new(&session, &callbacks, NULL);
|
||||
|
||||
CU_ASSERT(session->opt_flags & NGHTTP2_OPTMASK_RECV_CLIENT_PREFACE);
|
||||
rv = nghttp2_session_mem_recv(session, (const uint8_t *)NGHTTP2_CLIENT_MAGIC,
|
||||
NGHTTP2_CLIENT_MAGIC_LEN);
|
||||
|
||||
rv = nghttp2_session_mem_recv(
|
||||
session, (const uint8_t *)NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
|
||||
CU_ASSERT(rv == NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
CU_ASSERT(rv == NGHTTP2_CLIENT_MAGIC_LEN);
|
||||
CU_ASSERT(NGHTTP2_IB_READ_FIRST_SETTINGS == session->iframe.state);
|
||||
|
||||
/* Receiving PING is error because we want SETTINGS. */
|
||||
|
@ -6821,24 +6819,24 @@ void test_nghttp2_session_recv_client_preface(void) {
|
|||
nghttp2_session_del(session);
|
||||
|
||||
/* Check bad case */
|
||||
nghttp2_session_server_new2(&session, &callbacks, NULL, option);
|
||||
nghttp2_session_server_new(&session, &callbacks, NULL);
|
||||
|
||||
/* Feed preface with one byte less */
|
||||
rv = nghttp2_session_mem_recv(
|
||||
session, (const uint8_t *)NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN - 1);
|
||||
/* Feed magic with one byte less */
|
||||
rv = nghttp2_session_mem_recv(session, (const uint8_t *)NGHTTP2_CLIENT_MAGIC,
|
||||
NGHTTP2_CLIENT_MAGIC_LEN - 1);
|
||||
|
||||
CU_ASSERT(rv == NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN - 1);
|
||||
CU_ASSERT(NGHTTP2_IB_READ_CLIENT_PREFACE == session->iframe.state);
|
||||
CU_ASSERT(rv == NGHTTP2_CLIENT_MAGIC_LEN - 1);
|
||||
CU_ASSERT(NGHTTP2_IB_READ_CLIENT_MAGIC == session->iframe.state);
|
||||
CU_ASSERT(1 == session->iframe.payloadleft);
|
||||
|
||||
rv = nghttp2_session_mem_recv(session, (const uint8_t *)"\0", 1);
|
||||
|
||||
CU_ASSERT(NGHTTP2_ERR_BAD_PREFACE == rv);
|
||||
CU_ASSERT(NGHTTP2_ERR_BAD_CLIENT_MAGIC == rv);
|
||||
|
||||
nghttp2_session_del(session);
|
||||
|
||||
nghttp2_option_del(option);
|
||||
/* disable global nghttp2_enable_strict_preface here */
|
||||
nghttp2_enable_strict_preface = 0;
|
||||
}
|
||||
|
||||
void test_nghttp2_session_delete_data_item(void) {
|
||||
|
|
|
@ -118,7 +118,7 @@ void test_nghttp2_session_detach_idle_stream(void);
|
|||
void test_nghttp2_session_large_dep_tree(void);
|
||||
void test_nghttp2_session_graceful_shutdown(void);
|
||||
void test_nghttp2_session_on_header_temporal_failure(void);
|
||||
void test_nghttp2_session_recv_client_preface(void);
|
||||
void test_nghttp2_session_recv_client_magic(void);
|
||||
void test_nghttp2_session_delete_data_item(void);
|
||||
void test_nghttp2_session_open_idle_stream(void);
|
||||
void test_nghttp2_session_cancel_reserved_remote(void);
|
||||
|
|
Loading…
Reference in New Issue