diff --git a/README.rst b/README.rst index 02f33e18..d8bfb699 100644 --- a/README.rst +++ b/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 `_ +HTTP/2 and `RFC 7541 `_ 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 (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 `_ 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, diff --git a/configure.ac b/configure.ac index ce1d551f..4067178d 100644 --- a/configure.ac +++ b/configure.ac @@ -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]) diff --git a/doc/Makefile.am b/doc/Makefile.am index ed3b9ed1..0eefa442 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -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 \ diff --git a/doc/programmers-guide.rst b/doc/programmers-guide.rst index c67c09a3..94c72e98 100644 --- a/doc/programmers-guide.rst +++ b/doc/programmers-guide.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: diff --git a/doc/sources/tutorial-client.rst b/doc/sources/tutorial-client.rst index 94026323..6ce1238b 100644 --- a/doc/sources/tutorial-client.rst +++ b/doc/sources/tutorial-client.rst @@ -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) { diff --git a/doc/sources/tutorial-server.rst b/doc/sources/tutorial-server.rst index 12347ef6..8f5b5833 100644 --- a/doc/sources/tutorial-server.rst +++ b/doc/sources/tutorial-server.rst @@ -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()``:: diff --git a/examples/client.c b/examples/client.c index b12c3e48..4b4deeeb 100644 --- a/examples/client.c +++ b/examples/client.c @@ -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); diff --git a/examples/libevent-client.c b/examples/libevent-client.c index 019254c0..17ed94be 100644 --- a/examples/libevent-client.c +++ b/examples/libevent-client.c @@ -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) { diff --git a/examples/libevent-server.c b/examples/libevent-server.c index 05a24bbc..412c6d0d 100644 --- a/examples/libevent-server.c +++ b/examples/libevent-server.c @@ -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 diff --git a/examples/tiny-nghttpd.c b/examples/tiny-nghttpd.c index 613112fc..3410e9df 100644 --- a/examples/tiny-nghttpd.c +++ b/examples/tiny-nghttpd.c @@ -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; diff --git a/integration-tests/nghttpx_http2_test.go b/integration-tests/nghttpx_http2_test.go index 0a3115f3..953e9d42 100644 --- a/integration-tests/nghttpx_http2_test.go +++ b/integration-tests/nghttpx_http2_test.go @@ -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__"), }, }) diff --git a/lib/includes/nghttp2/nghttp2.h b/lib/includes/nghttp2/nghttp2.h index 4edba80e..5f5fdabd 100644 --- a/lib/includes/nghttp2/nghttp2.h +++ b/lib/includes/nghttp2/nghttp2.h @@ -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 `_. 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 - * `_. - * 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. + * `_. 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 diff --git a/lib/nghttp2_frame.h b/lib/nghttp2_frame.h index 2c4d6e9c..7fc336c1 100644 --- a/lib/nghttp2_frame.h +++ b/lib/nghttp2_frame.h @@ -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); diff --git a/lib/nghttp2_helper.c b/lib/nghttp2_helper.c index 79f8e92b..15f329cc 100644 --- a/lib/nghttp2_helper.c +++ b/lib/nghttp2_helper.c @@ -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"; } diff --git a/lib/nghttp2_option.c b/lib/nghttp2_option.c index db2a27aa..a45b5e0e 100644 --- a/lib/nghttp2_option.c +++ b/lib/nghttp2_option.c @@ -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) { diff --git a/lib/nghttp2_option.h b/lib/nghttp2_option.h index a79d0e77..739bd858 100644 --- a/lib/nghttp2_option.h +++ b/lib/nghttp2_option.h @@ -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 */ diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index 495d4102..7309aa0e 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -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; diff --git a/lib/nghttp2_session.h b/lib/nghttp2_session.h index 0f5a4eee..41b16081 100644 --- a/lib/nghttp2_session.h +++ b/lib/nghttp2_session.h @@ -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, diff --git a/lib/nghttp2_submit.c b/lib/nghttp2_submit.c index 54e8be00..0f6d9f5f 100644 --- a/lib/nghttp2_submit.c +++ b/lib/nghttp2_submit.c @@ -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; diff --git a/python/nghttp2.pyx b/python/nghttp2.pyx index ad1ebb82..c6b9b66c 100644 --- a/python/nghttp2.pyx +++ b/python/nghttp2.pyx @@ -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 diff --git a/src/HttpServer.cc b/src/HttpServer.cc index 03104284..0465395d 100644 --- a/src/HttpServer.cc +++ b/src/HttpServer.cc @@ -99,17 +99,13 @@ template 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 entry; size_t niv = 1; diff --git a/src/HttpServer.h b/src/HttpServer.h index a91e7d3c..212f12ad 100644 --- a/src/HttpServer.h +++ b/src/HttpServer.h @@ -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; diff --git a/src/app_helper.cc b/src/app_helper.cc index 48298b91..58f8439c 100644 --- a/src/app_helper.cc +++ b/src/app_helper.cc @@ -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(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; diff --git a/src/app_helper.h b/src/app_helper.h index 034489f4..408adac2 100644 --- a/src/app_helper.h +++ b/src/app_helper.h @@ -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); diff --git a/src/asio_client_session_impl.cc b/src/asio_client_session_impl.cc index 6334c7ac..9cb57c90 100644 --- a/src/asio_client_session_impl.cc +++ b/src/asio_client_session_impl.cc @@ -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(); diff --git a/src/asio_server_http2_handler.cc b/src/asio_server_http2_handler.cc index 7e2304ae..c55cb329 100644 --- a/src/asio_server_http2_handler.cc +++ b/src/asio_server_http2_handler.cc @@ -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; } diff --git a/src/h2load_http2_session.cc b/src/h2load_http2_session.cc index 2cdba9e1..0cbb702d 100644 --- a/src/h2load_http2_session.cc +++ b/src/h2load_http2_session.cc @@ -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(); } diff --git a/src/nghttp.cc b/src/nghttp.cc index d8c3a58c..8f6361e3 100644 --- a/src/nghttp.cc +++ b/src/nghttp.cc @@ -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. diff --git a/src/shrpx.cc b/src/shrpx.cc index 9cbcc367..5becb798 100644 --- a/src/shrpx.cc +++ b/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= Specify protocol ID, port, host and origin of alternative service. and 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=
Specify additional header field to add to response header set. This option just appends header field and diff --git a/src/shrpx_client_handler.cc b/src/shrpx_client_handler.cc index 51e0b524..354b95e7 100644 --- a/src/shrpx_client_handler.cc +++ b/src/shrpx_client_handler.cc @@ -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(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; } diff --git a/src/shrpx_http2_session.cc b/src/shrpx_http2_session.cc index 4fae1f0e..c071a722 100644 --- a/src/shrpx_http2_session.cc +++ b/src/shrpx_http2_session.cc @@ -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); diff --git a/src/shrpx_http2_upstream.cc b/src/shrpx_http2_upstream.cc index f14adcd6..e3aa67f6 100644 --- a/src/shrpx_http2_upstream.cc +++ b/src/shrpx_http2_upstream.cc @@ -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(altsvc.protocol_id), - altsvc.protocol_id_len, - reinterpret_cast(altsvc.host), altsvc.host_len, - reinterpret_cast(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); } diff --git a/src/shrpx_http_downstream_connection.cc b/src/shrpx_http_downstream_connection.cc index 6e5c5295..1840f753 100644 --- a/src/shrpx_http_downstream_connection.cc +++ b/src/shrpx_http_downstream_connection.cc @@ -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. diff --git a/src/util.cc b/src/util.cc index 20aaca70..9eb5258b 100644 --- a/src/util.cc +++ b/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 get_default_alpn() { auto res = std::vector(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; } diff --git a/src/util.h b/src/util.h index 4525df53..88e1ff59 100644 --- a/src/util.h +++ b/src/util.h @@ -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 { diff --git a/src/util_test.cc b/src/util_test.cc index 21d992d8..c1100b76 100644 --- a/src/util_test.cc +++ b/src/util_test.cc @@ -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); diff --git a/tests/main.c b/tests/main.c index ae42e70c..1d187f70 100644 --- a/tests/main.c +++ b/tests/main.c @@ -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", diff --git a/tests/nghttp2_npn_test.c b/tests/nghttp2_npn_test.c index bae6082b..cbd65b71 100644 --- a/tests/nghttp2_npn_test.c +++ b/tests/nghttp2_npn_test.c @@ -30,9 +30,8 @@ #include 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))); diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c index ccf42ce7..7783e17a 100644 --- a/tests/nghttp2_session_test.c +++ b/tests/nghttp2_session_test.c @@ -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) { diff --git a/tests/nghttp2_session_test.h b/tests/nghttp2_session_test.h index 1b4d2f64..e879c783 100644 --- a/tests/nghttp2_session_test.h +++ b/tests/nghttp2_session_test.h @@ -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);