Merge branch 'v1.0.0'

This commit is contained in:
Tatsuhiro Tsujikawa 2015-05-15 23:30:59 +09:00
commit b89140c311
40 changed files with 451 additions and 557 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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()``::

View File

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

View File

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

View File

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

View File

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

View File

@ -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__"),
},
})

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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