From d0e34312b8f401d10f35f49710cbabbd96f59c38 Mon Sep 17 00:00:00 2001 From: Alexis La Goutte Date: Wed, 29 Apr 2015 23:12:12 +0200 Subject: HTTP2: Update to libnghttp2 0.7.13 Change-Id: I69589a90077a9b009f2e1a45531059ebd61a0450 Reviewed-on: https://code.wireshark.org/review/8242 Petri-Dish: Alexis La Goutte Reviewed-by: Pascal Quantin Reviewed-by: Alexis La Goutte --- epan/nghttp2/nghttp2.h | 730 ++++++++++++++++++--------- epan/nghttp2/nghttp2_buf.c | 40 +- epan/nghttp2/nghttp2_buf.h | 16 +- epan/nghttp2/nghttp2_hd.c | 1001 ++++++++++++++++++++++++++----------- epan/nghttp2/nghttp2_hd.h | 106 +++- epan/nghttp2/nghttp2_hd_huffman.c | 61 ++- epan/nghttp2/nghttp2_helper.h | 4 + epan/nghttp2/nghttp2_int.h | 5 +- epan/nghttp2/nghttp2_net.h | 53 +- epan/nghttp2/nghttp2ver.h | 4 +- 10 files changed, 1434 insertions(+), 586 deletions(-) (limited to 'epan/nghttp2') diff --git a/epan/nghttp2/nghttp2.h b/epan/nghttp2/nghttp2.h index e723ef430a..a32104ad64 100644 --- a/epan/nghttp2/nghttp2.h +++ b/epan/nghttp2/nghttp2.h @@ -27,6 +27,12 @@ #include "config.h" +/* Define WIN32 when build target is Win32 API (borrowed from + libcurl) */ +#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) +#define WIN32 +#endif + #ifdef __cplusplus extern "C" { #endif @@ -37,6 +43,14 @@ extern "C" { #include "nghttp2ver.h" +#ifdef NGHTTP2_STATICLIB +#define NGHTTP2_EXTERN +#elif defined(WIN32) +#define NGHTTP2_EXTERN __declspec(dllexport) +#else /* !defined(WIN32) */ +#define NGHTTP2_EXTERN +#endif /* !defined(WIN32) */ + /** * @macro * @@ -229,8 +243,9 @@ typedef enum { */ NGHTTP2_ERR_UNSUPPORTED_VERSION = -503, /** - * Used as a return value from :type:`nghttp2_send_callback` and - * :type:`nghttp2_recv_callback` to indicate that the operation + * Used as a return value from :type:`nghttp2_send_callback`, + * :type:`nghttp2_recv_callback` and + * :type:`nghttp2_send_data_callback` to indicate that the operation * would block. */ NGHTTP2_ERR_WOULDBLOCK = -504, @@ -404,21 +419,27 @@ typedef enum { */ typedef struct { /** - * The |name| byte string, which is not necessarily ``NULL`` - * terminated. + * The |name| byte string. If this struct is presented from library + * (e.g., :type:`nghttp2_on_frame_recv_callback`), |name| is + * guaranteed to be NULL-terminated. When application is + * constructing this struct, |name| is not required to be + * NULL-terminated. */ uint8_t *name; /** - * The |value| byte string, which is not necessarily ``NULL`` - * terminated. + * The |value| byte string. If this struct is presented from + * library (e.g., :type:`nghttp2_on_frame_recv_callback`), |value| + * is guaranteed to be NULL-terminated. When application is + * constructing this struct, |value| is not required to be + * NULL-terminated. */ uint8_t *value; /** - * The length of the |name|. + * The length of the |name|, excluding terminating NULL. */ size_t namelen; /** - * The length of the |value|. + * The length of the |value|, excluding terminating NULL. */ size_t valuelen; /** @@ -470,7 +491,9 @@ typedef enum { */ NGHTTP2_WINDOW_UPDATE = 0x08, /** - * The CONTINUATION frame. + * The CONTINUATION frame. This frame type won't be passed to any + * callbacks because the library processes this frame type and its + * preceding HEADERS/PUSH_PROMISE as a single frame. */ NGHTTP2_CONTINUATION = 0x09 } nghttp2_frame_type; @@ -684,7 +707,19 @@ typedef enum { /** * Indicates EOF was sensed. */ - NGHTTP2_DATA_FLAG_EOF = 0x01 + NGHTTP2_DATA_FLAG_EOF = 0x01, + /** + * Indicates that END_STREAM flag must not be set even if + * NGHTTP2_DATA_FLAG_EOF is set. Usually this flag is used to send + * trailer header fields with `nghttp2_submit_request()` or + * `nghttp2_submit_response()`. + */ + NGHTTP2_DATA_FLAG_NO_END_STREAM = 0x02, + /** + * Indicates that application will send complete DATA frame in + * :type:`nghttp2_send_data_callback`. + */ + NGHTTP2_DATA_FLAG_NO_COPY = 0x04 } nghttp2_data_flag; /** @@ -697,6 +732,30 @@ typedef enum { * them in |buf| and return number of data stored in |buf|. If EOF is * reached, set :enum:`NGHTTP2_DATA_FLAG_EOF` flag in |*data_flags|. * + * Sometime it is desirable to avoid copying data into |buf| and let + * application to send data directly. To achieve this, set + * :enum:`NGHTTP2_DATA_FLAG_NO_COPY` to |*data_flags| (and possibly + * other flags, just like when we do copy), and return the number of + * bytes to send without copying data into |buf|. The library, seeing + * :enum:`NGHTTP2_DATA_FLAG_NO_COPY`, will invoke + * :type:`nghttp2_send_data_callback`. The application must send + * complete DATA frame in that callback. + * + * If this callback is set by `nghttp2_submit_request()`, + * `nghttp2_submit_response()` or `nghttp2_submit_headers()` and + * `nghttp2_submit_data()` with flag parameter + * :enum:`NGHTTP2_FLAG_END_STREAM` set, and + * :enum:`NGHTTP2_DATA_FLAG_EOF` flag is set to |*data_flags|, DATA + * frame will have END_STREAM flag set. Usually, this is expected + * behaviour and all are fine. One exception is send trailer header + * fields. You cannot send trailers after sending frame with + * END_STREAM set. To avoid this problem, one can set + * :enum:`NGHTTP2_DATA_FLAG_NO_END_STREAM` along with + * :enum:`NGHTTP2_DATA_FLAG_EOF` to signal the library not to set + * END_STREAM in DATA frame. Then application can use + * `nghttp2_submit_trailer()` to send trailers. + * `nghttp2_submit_trailer()` can be called inside this callback. + * * If the application wants to postpone DATA frames (e.g., * asynchronous I/O, or reading data blocks for long time), it is * achieved by returning :enum:`NGHTTP2_ERR_DEFERRED` without reading @@ -1147,6 +1206,47 @@ typedef ssize_t (*nghttp2_send_callback)(nghttp2_session *session, const uint8_t *data, size_t length, int flags, void *user_data); +/** + * @functypedef + * + * Callback function invoked when :enum:`NGHTTP2_DATA_FLAG_NO_COPY` is + * used in :type:`nghttp2_data_source_read_callback` to send complete + * DATA frame. + * + * The |frame| is a DATA frame to send. The |framehd| is the + * serialized frame header (9 bytes). The |length| is the length of + * application data to send (this does not include padding). The + * |source| is the same pointer passed to + * :type:`nghttp2_data_source_read_callback`. + * + * The application first must send frame header |framehd| of length 9 + * bytes. If ``frame->padlen > 0``, send 1 byte of value + * ``frame->padlen - 1``. Then send exactly |length| bytes of + * application data. Finally, if ``frame->padlen > 0``, send + * ``frame->padlen - 1`` bytes of zero (they are padding). + * + * The application has to send complete DATA frame in this callback. + * If all data were written successfully, return 0. + * + * If it cannot send it all, just return + * :enum:`NGHTTP2_ERR_WOULDBLOCK`; the library will call this callback + * with the same parameters later (It is recommended to send complete + * DATA frame at once in this function to deal with error; if partial + * frame data has already sent, it is impossible to send another data + * in that state, and all we can do is tear down connection). If + * application decided to reset this stream, return + * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`, then the library + * will send RST_STREAM with INTERNAL_ERROR as error code. The + * application can also return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`, + * which will result in connection closure. Returning any other value + * is treated as :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned. + */ +typedef int (*nghttp2_send_data_callback)(nghttp2_session *session, + nghttp2_frame *frame, + const uint8_t *framehd, size_t length, + nghttp2_data_source *source, + void *user_data); + /** * @functypedef * @@ -1178,10 +1278,10 @@ typedef ssize_t (*nghttp2_recv_callback)(nghttp2_session *session, uint8_t *buf, /** * @functypedef * - * Callback function invoked by `nghttp2_session_recv()` when a frame - * is received. The |user_data| pointer is the third argument passed - * in to the call to `nghttp2_session_client_new()` or - * `nghttp2_session_server_new()`. + * Callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv()` when a frame is received. 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`` * member of their data structure are always ``NULL`` and 0 @@ -1216,14 +1316,14 @@ typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session, /** * @functypedef * - * Callback function invoked by `nghttp2_session_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 `nghttp2_session_client_new()` or - * `nghttp2_session_server_new()`. + * 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 + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. * * If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` * member of their data structure are always ``NULL`` and 0 @@ -1231,7 +1331,7 @@ typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session, * * The implementation of this function must return 0 if it succeeds. * If nonzero is returned, it is treated as fatal error and - * `nghttp2_session_recv()` and `nghttp2_session_send()` functions + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. * * To set this callback to :type:`nghttp2_session_callbacks`, use @@ -1264,7 +1364,7 @@ typedef int (*nghttp2_on_invalid_frame_recv_callback)( * region included in the input bytes. * * The implementation of this function must return 0 if it succeeds. - * If nonzero is returned, it is treated as fatal error and + * If nonzero is returned, it is treated as fatal error, and * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. * @@ -1287,7 +1387,7 @@ typedef int (*nghttp2_on_data_chunk_recv_callback)(nghttp2_session *session, * * The implementation of this function must return 0 if it succeeds. * If nonzero is returned, it is treated as fatal error and - * `nghttp2_session_recv()` and `nghttp2_session_send()` functions + * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. * * To set this callback to :type:`nghttp2_session_callbacks`, use @@ -1306,7 +1406,7 @@ typedef int (*nghttp2_before_frame_send_callback)(nghttp2_session *session, * * The implementation of this function must return 0 if it succeeds. * If nonzero is returned, it is treated as fatal error and - * `nghttp2_session_recv()` and `nghttp2_session_send()` functions + * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. * * To set this callback to :type:`nghttp2_session_callbacks`, use @@ -1328,7 +1428,7 @@ typedef int (*nghttp2_on_frame_send_callback)(nghttp2_session *session, * * The implementation of this function must return 0 if it succeeds. * If nonzero is returned, it is treated as fatal error and - * `nghttp2_session_recv()` and `nghttp2_session_send()` functions + * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. * * `nghttp2_session_get_stream_user_data()` can be used to get @@ -1358,8 +1458,9 @@ typedef int (*nghttp2_on_frame_not_send_callback)(nghttp2_session *session, * * The implementation of this function must return 0 if it succeeds. * If nonzero is returned, it is treated as fatal error and - * `nghttp2_session_recv()` and `nghttp2_session_send()` functions - * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * `nghttp2_session_recv()`, `nghttp2_session_mem_recv()`, + * `nghttp2_session_send()`, and `nghttp2_session_mem_send()` + * functions immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. * * To set this callback to :type:`nghttp2_session_callbacks`, use * `nghttp2_session_callbacks_set_on_stream_close_callback()`. @@ -1401,13 +1502,26 @@ typedef int (*nghttp2_on_stream_close_callback)(nghttp2_session *session, * frame with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` * containing final response headers (non-1xx status code). The * trailer headers also has ``frame->headers.cat == - * NGHTTP2_HCAT_HEADERS`` which does not containg any status code. + * NGHTTP2_HCAT_HEADERS`` which does not contain any status code. * - * The implementation of this function must return 0 if it succeeds or - * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If nonzero value other than - * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned, it is treated as - * if :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned. If - * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned, + * Returning :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close + * the stream (promised stream if frame is PUSH_PROMISE) by issuing + * RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`. In this case, + * :type:`nghttp2_on_header_callback` and + * :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a + * different error code is desirable, use + * `nghttp2_submit_rst_stream()` with a desired error code and then + * return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Again, use + * ``frame->push_promise.promised_stream_id`` as stream_id parameter + * in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE. + * + * The implementation of this function must return 0 if it succeeds. + * It can return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` to + * reset the stream (promised stream if frame is PUSH_PROMISE). For + * critical errors, it must return + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If the other value is + * returned, it is treated as if :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` + * is returned. If :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned, * `nghttp2_session_mem_recv()` function will immediately return * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. * @@ -1439,14 +1553,18 @@ typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session, * :type:`nghttp2_on_frame_recv_callback` for the |frame| will not be * invoked. * - * The |value| may be ``NULL`` if the |valuelen| is 0. + * Both |name| and |value| are guaranteed to be NULL-terminated. The + * |namelen| and |valuelen| do not include terminal NULL. If + * `nghttp2_option_set_no_http_messaging()` is used with nonzero + * value, NULL character may be included in |name| or |value| before + * terminating NULL. * * Please note that unless `nghttp2_option_set_no_http_messaging()` is * used, nghttp2 library does perform validation against the |name| * and the |value| using `nghttp2_check_header_name()` and * `nghttp2_check_header_value()`. In addition to this, nghttp2 * performs vaidation based on HTTP Messaging rule, which is briefly - * explained in `HTTP Messaging`_ section. + * explained in :ref:`http-messaging` section. * * If the application uses `nghttp2_session_mem_recv()`, it can return * :enum:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()` @@ -1458,12 +1576,15 @@ typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session, * included in the input bytes. * * Returning :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close - * the stream by issuing RST_STREAM with - * :enum:`NGHTTP2_INTERNAL_ERROR`. In this case, + * the stream (promised stream if frame is PUSH_PROMISE) by issuing + * RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`. In this case, + * :type:`nghttp2_on_header_callback` and * :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a * different error code is desirable, use * `nghttp2_submit_rst_stream()` with a desired error code and then - * return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. + * return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Again, use + * ``frame->push_promise.promised_stream_id`` as stream_id parameter + * in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE. * * The implementation of this function must return 0 if it succeeds. * It may return :enum:`NGHTTP2_ERR_PAUSE` or @@ -1495,8 +1616,8 @@ typedef int (*nghttp2_on_header_callback)(nghttp2_session *session, * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. Returning * ``frame->hd.length`` means no padding is added. Returning * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` will make - * `nghttp2_session_send()` function immediately return - * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. * * To set this callback to :type:`nghttp2_session_callbacks`, use * `nghttp2_session_callbacks_set_select_padding_callback()`. @@ -1588,7 +1709,8 @@ typedef struct nghttp2_session_callbacks nghttp2_session_callbacks; * :enum:`NGHTTP2_ERR_NOMEM` * Out of memory. */ -int nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr); +NGHTTP2_EXTERN int +nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr); /** * @function @@ -1596,7 +1718,8 @@ int nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr); * Frees any resources allocated for |callbacks|. If |callbacks| is * ``NULL``, this function does nothing. */ -void nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks); +NGHTTP2_EXTERN void +nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks); /** * @function @@ -1606,7 +1729,7 @@ void nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks); * uses solely `nghttp2_session_mem_send()` to serialize data to * transmit. */ -void nghttp2_session_callbacks_set_send_callback( +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_callback( nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback); /** @@ -1617,26 +1740,28 @@ void nghttp2_session_callbacks_set_send_callback( * application uses solely `nghttp2_session_mem_recv()` to process * received data. */ -void nghttp2_session_callbacks_set_recv_callback( +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback); /** * @function * - * Sets callback function invoked by `nghttp2_session_recv()` when a - * frame is received. + * Sets callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv()` when a frame is received. */ -void nghttp2_session_callbacks_set_on_frame_recv_callback( +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_on_frame_recv_callback on_frame_recv_callback); /** * @function * - * Sets callback function invoked by `nghttp2_session_recv()` when an - * invalid non-DATA frame is received. + * Sets callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv()` when an invalid non-DATA frame is + * received. */ -void nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( +NGHTTP2_EXTERN void +nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback); @@ -1646,7 +1771,7 @@ void nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( * Sets callback function invoked when a chunk of data in DATA frame * is received. */ -void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback); @@ -1655,7 +1780,7 @@ void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( * * Sets callback function invoked before a non-DATA frame is sent. */ -void nghttp2_session_callbacks_set_before_frame_send_callback( +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_before_frame_send_callback( nghttp2_session_callbacks *cbs, nghttp2_before_frame_send_callback before_frame_send_callback); @@ -1664,7 +1789,7 @@ void nghttp2_session_callbacks_set_before_frame_send_callback( * * Sets callback function invoked after a frame is sent. */ -void nghttp2_session_callbacks_set_on_frame_send_callback( +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_send_callback( nghttp2_session_callbacks *cbs, nghttp2_on_frame_send_callback on_frame_send_callback); @@ -1674,7 +1799,7 @@ void nghttp2_session_callbacks_set_on_frame_send_callback( * Sets callback function invoked when a non-DATA frame is not sent * because of an error. */ -void nghttp2_session_callbacks_set_on_frame_not_send_callback( +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_not_send_callback( nghttp2_session_callbacks *cbs, nghttp2_on_frame_not_send_callback on_frame_not_send_callback); @@ -1683,7 +1808,7 @@ void nghttp2_session_callbacks_set_on_frame_not_send_callback( * * Sets callback function invoked when the stream is closed. */ -void nghttp2_session_callbacks_set_on_stream_close_callback( +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_stream_close_callback( nghttp2_session_callbacks *cbs, nghttp2_on_stream_close_callback on_stream_close_callback); @@ -1693,7 +1818,7 @@ void nghttp2_session_callbacks_set_on_stream_close_callback( * Sets callback function invoked when the reception of header block * in HEADERS or PUSH_PROMISE is started. */ -void nghttp2_session_callbacks_set_on_begin_headers_callback( +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_begin_headers_callback( nghttp2_session_callbacks *cbs, nghttp2_on_begin_headers_callback on_begin_headers_callback); @@ -1703,7 +1828,7 @@ void nghttp2_session_callbacks_set_on_begin_headers_callback( * Sets callback function invoked when a header name/value pair is * received. */ -void nghttp2_session_callbacks_set_on_header_callback( +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_header_callback( nghttp2_session_callbacks *cbs, nghttp2_on_header_callback on_header_callback); @@ -1714,7 +1839,7 @@ void nghttp2_session_callbacks_set_on_header_callback( * how many padding bytes are required for the transmission of the * given frame. */ -void nghttp2_session_callbacks_set_select_padding_callback( +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_select_padding_callback( nghttp2_session_callbacks *cbs, nghttp2_select_padding_callback select_padding_callback); @@ -1724,7 +1849,8 @@ void nghttp2_session_callbacks_set_select_padding_callback( * Sets callback function determine the length allowed in * :type:`nghttp2_data_source_read_callback`. */ -void nghttp2_session_callbacks_set_data_source_read_length_callback( +NGHTTP2_EXTERN void +nghttp2_session_callbacks_set_data_source_read_length_callback( nghttp2_session_callbacks *cbs, nghttp2_data_source_read_length_callback data_source_read_length_callback); @@ -1733,10 +1859,21 @@ void nghttp2_session_callbacks_set_data_source_read_length_callback( * * Sets callback function invoked when a frame header is received. */ -void nghttp2_session_callbacks_set_on_begin_frame_callback( +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_begin_frame_callback( nghttp2_session_callbacks *cbs, nghttp2_on_begin_frame_callback on_begin_frame_callback); +/** + * @function + * + * Sets callback function invoked when + * :enum:`NGHTTP2_DATA_FLAG_NO_COPY` is used in + * :type:`nghttp2_data_source_read_callback` to avoid data copy. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_data_callback( + nghttp2_session_callbacks *cbs, + nghttp2_send_data_callback send_data_callback); + /** * @functypedef * @@ -1856,7 +1993,7 @@ typedef struct nghttp2_option nghttp2_option; * :enum:`NGHTTP2_ERR_NOMEM` * Out of memory. */ -int nghttp2_option_new(nghttp2_option **option_ptr); +NGHTTP2_EXTERN int nghttp2_option_new(nghttp2_option **option_ptr); /** * @function @@ -1864,7 +2001,7 @@ int nghttp2_option_new(nghttp2_option **option_ptr); * Frees any resources allocated for |option|. If |option| is * ``NULL``, this function does nothing. */ -void nghttp2_option_del(nghttp2_option *option); +NGHTTP2_EXTERN void nghttp2_option_del(nghttp2_option *option); /** * @function @@ -1876,7 +2013,8 @@ void nghttp2_option_del(nghttp2_option *option); * data. Don't use `nghttp2_submit_window_update()` for this purpose. * By default, this option is set to zero. */ -void nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val); +NGHTTP2_EXTERN void +nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val); /** * @function @@ -1893,8 +2031,9 @@ void nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val); * overwritten if the local endpoint receives * SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint. */ -void nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option, - uint32_t val); +NGHTTP2_EXTERN void +nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option, + uint32_t val); /** * @function @@ -1912,7 +2051,8 @@ void nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option, * one, `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` will * return error :enum:`NGHTTP2_ERR_BAD_PREFACE`, which is fatal error. */ -void nghttp2_option_set_recv_client_preface(nghttp2_option *option, int val); +NGHTTP2_EXTERN void +nghttp2_option_set_recv_client_preface(nghttp2_option *option, int val); /** * @function @@ -1920,11 +2060,12 @@ void nghttp2_option_set_recv_client_preface(nghttp2_option *option, int val); * By default, nghttp2 library enforces subset of HTTP Messaging rules * described in `HTTP/2 specification, section 8 * `_. - * See `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. */ -void nghttp2_option_set_no_http_messaging(nghttp2_option *option, int val); +NGHTTP2_EXTERN void nghttp2_option_set_no_http_messaging(nghttp2_option *option, + int val); /** * @function @@ -1947,9 +2088,10 @@ void nghttp2_option_set_no_http_messaging(nghttp2_option *option, int val); * :enum:`NGHTTP2_ERR_NOMEM` * Out of memory. */ -int nghttp2_session_client_new(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data); +NGHTTP2_EXTERN int +nghttp2_session_client_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data); /** * @function @@ -1972,9 +2114,10 @@ int nghttp2_session_client_new(nghttp2_session **session_ptr, * :enum:`NGHTTP2_ERR_NOMEM` * Out of memory. */ -int nghttp2_session_server_new(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data); +NGHTTP2_EXTERN int +nghttp2_session_server_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data); /** * @function @@ -1997,9 +2140,10 @@ int nghttp2_session_server_new(nghttp2_session **session_ptr, * :enum:`NGHTTP2_ERR_NOMEM` * Out of memory. */ -int nghttp2_session_client_new2(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data, const nghttp2_option *option); +NGHTTP2_EXTERN int +nghttp2_session_client_new2(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option); /** * @function @@ -2022,9 +2166,10 @@ int nghttp2_session_client_new2(nghttp2_session **session_ptr, * :enum:`NGHTTP2_ERR_NOMEM` * Out of memory. */ -int nghttp2_session_server_new2(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data, const nghttp2_option *option); +NGHTTP2_EXTERN int +nghttp2_session_server_new2(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option); /** * @function @@ -2047,10 +2192,9 @@ int nghttp2_session_server_new2(nghttp2_session **session_ptr, * :enum:`NGHTTP2_ERR_NOMEM` * Out of memory. */ -int nghttp2_session_client_new3(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data, const nghttp2_option *option, - nghttp2_mem *mem); +NGHTTP2_EXTERN int nghttp2_session_client_new3( + nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option, nghttp2_mem *mem); /** * @function @@ -2073,10 +2217,9 @@ int nghttp2_session_client_new3(nghttp2_session **session_ptr, * :enum:`NGHTTP2_ERR_NOMEM` * Out of memory. */ -int nghttp2_session_server_new3(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data, const nghttp2_option *option, - nghttp2_mem *mem); +NGHTTP2_EXTERN int nghttp2_session_server_new3( + nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option, nghttp2_mem *mem); /** * @function @@ -2084,7 +2227,7 @@ int nghttp2_session_server_new3(nghttp2_session **session_ptr, * Frees any resources allocated for |session|. If |session| is * ``NULL``, this function does nothing. */ -void nghttp2_session_del(nghttp2_session *session); +NGHTTP2_EXTERN void nghttp2_session_del(nghttp2_session *session); /** * @function @@ -2133,7 +2276,7 @@ void nghttp2_session_del(nghttp2_session *session); * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` * The callback function failed. */ -int nghttp2_session_send(nghttp2_session *session); +NGHTTP2_EXTERN int nghttp2_session_send(nghttp2_session *session); /** * @function @@ -2165,8 +2308,8 @@ int nghttp2_session_send(nghttp2_session *session); * :enum:`NGHTTP2_ERR_NOMEM` * Out of memory. */ -ssize_t nghttp2_session_mem_send(nghttp2_session *session, - const uint8_t **data_ptr); +NGHTTP2_EXTERN ssize_t nghttp2_session_mem_send(nghttp2_session *session, + const uint8_t **data_ptr); /** * @function @@ -2231,7 +2374,7 @@ ssize_t nghttp2_session_mem_send(nghttp2_session *session, * when |session| was configured as server and * `nghttp2_option_set_recv_client_preface()` is used. */ -int nghttp2_session_recv(nghttp2_session *session); +NGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session); /** * @function @@ -2266,8 +2409,9 @@ int nghttp2_session_recv(nghttp2_session *session); * when |session| was configured as server and * `nghttp2_option_set_recv_client_preface()` is used. */ -ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, - size_t inlen); +NGHTTP2_EXTERN ssize_t nghttp2_session_mem_recv(nghttp2_session *session, + const uint8_t *in, + size_t inlen); /** * @function @@ -2283,7 +2427,8 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, * :enum:`NGHTTP2_ERR_NOMEM` * Out of memory. */ -int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id); +NGHTTP2_EXTERN int nghttp2_session_resume_data(nghttp2_session *session, + int32_t stream_id); /** * @function @@ -2295,7 +2440,7 @@ int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id); * `nghttp2_session_want_write()` return 0, the application should * drop the connection. */ -int nghttp2_session_want_read(nghttp2_session *session); +NGHTTP2_EXTERN int nghttp2_session_want_read(nghttp2_session *session); /** * @function @@ -2307,7 +2452,7 @@ int nghttp2_session_want_read(nghttp2_session *session); * `nghttp2_session_want_write()` return 0, the application should * drop the connection. */ -int nghttp2_session_want_write(nghttp2_session *session); +NGHTTP2_EXTERN int nghttp2_session_want_write(nghttp2_session *session); /** * @function @@ -2321,8 +2466,9 @@ int nghttp2_session_want_write(nghttp2_session *session); * ``NULL``. If the stream does not exist, this function returns * ``NULL``. */ -void *nghttp2_session_get_stream_user_data(nghttp2_session *session, - int32_t stream_id); +NGHTTP2_EXTERN void * +nghttp2_session_get_stream_user_data(nghttp2_session *session, + int32_t stream_id); /** * @function @@ -2342,9 +2488,9 @@ void *nghttp2_session_get_stream_user_data(nghttp2_session *session, * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` * The stream does not exist */ -int nghttp2_session_set_stream_user_data(nghttp2_session *session, - int32_t stream_id, - void *stream_user_data); +NGHTTP2_EXTERN int +nghttp2_session_set_stream_user_data(nghttp2_session *session, + int32_t stream_id, void *stream_user_data); /** * @function @@ -2352,7 +2498,8 @@ int nghttp2_session_set_stream_user_data(nghttp2_session *session, * Returns the number of frames in the outbound queue. This does not * include the deferred DATA frames. */ -size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session); +NGHTTP2_EXTERN size_t + nghttp2_session_get_outbound_queue_size(nghttp2_session *session); /** * @function @@ -2368,9 +2515,8 @@ size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session); * * This function returns -1 if it fails. */ -int32_t -nghttp2_session_get_stream_effective_recv_data_length(nghttp2_session *session, - int32_t stream_id); +NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_recv_data_length( + nghttp2_session *session, int32_t stream_id); /** * @function @@ -2382,9 +2528,8 @@ nghttp2_session_get_stream_effective_recv_data_length(nghttp2_session *session, * * This function returns -1 if it fails. */ -int32_t -nghttp2_session_get_stream_effective_local_window_size(nghttp2_session *session, - int32_t stream_id); +NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_local_window_size( + nghttp2_session *session, int32_t stream_id); /** * @function @@ -2400,8 +2545,8 @@ nghttp2_session_get_stream_effective_local_window_size(nghttp2_session *session, * * This function returns -1 if it fails. */ -int32_t -nghttp2_session_get_effective_recv_data_length(nghttp2_session *session); +NGHTTP2_EXTERN int32_t + nghttp2_session_get_effective_recv_data_length(nghttp2_session *session); /** * @function @@ -2413,8 +2558,8 @@ nghttp2_session_get_effective_recv_data_length(nghttp2_session *session); * * This function returns -1 if it fails. */ -int32_t -nghttp2_session_get_effective_local_window_size(nghttp2_session *session); +NGHTTP2_EXTERN int32_t + nghttp2_session_get_effective_local_window_size(nghttp2_session *session); /** * @function @@ -2430,8 +2575,9 @@ nghttp2_session_get_effective_local_window_size(nghttp2_session *session); * * This function returns -1 if it fails. */ -int32_t nghttp2_session_get_stream_remote_window_size(nghttp2_session *session, - int32_t stream_id); +NGHTTP2_EXTERN int32_t + nghttp2_session_get_stream_remote_window_size(nghttp2_session *session, + int32_t stream_id); /** * @function @@ -2440,7 +2586,8 @@ int32_t nghttp2_session_get_stream_remote_window_size(nghttp2_session *session, * * This function always succeeds. */ -int32_t nghttp2_session_get_remote_window_size(nghttp2_session *session); +NGHTTP2_EXTERN int32_t + nghttp2_session_get_remote_window_size(nghttp2_session *session); /** * @function @@ -2448,8 +2595,9 @@ int32_t nghttp2_session_get_remote_window_size(nghttp2_session *session); * Returns 1 if local peer half closed the given stream |stream_id|. * Returns 0 if it did not. Returns -1 if no such stream exists. */ -int nghttp2_session_get_stream_local_close(nghttp2_session *session, - int32_t stream_id); +NGHTTP2_EXTERN int +nghttp2_session_get_stream_local_close(nghttp2_session *session, + int32_t stream_id); /** * @function @@ -2457,8 +2605,9 @@ int nghttp2_session_get_stream_local_close(nghttp2_session *session, * Returns 1 if remote peer half closed the given stream |stream_id|. * Returns 0 if it did not. Returns -1 if no such stream exists. */ -int nghttp2_session_get_stream_remote_close(nghttp2_session *session, - int32_t stream_id); +NGHTTP2_EXTERN int +nghttp2_session_get_stream_remote_close(nghttp2_session *session, + int32_t stream_id); /** * @function @@ -2486,8 +2635,8 @@ int nghttp2_session_get_stream_remote_close(nghttp2_session *session, * :enum:`NGHTTP2_ERR_NOMEM` * Out of memory. */ -int nghttp2_session_terminate_session(nghttp2_session *session, - uint32_t error_code); +NGHTTP2_EXTERN int nghttp2_session_terminate_session(nghttp2_session *session, + uint32_t error_code); /** * @function @@ -2515,9 +2664,9 @@ int nghttp2_session_terminate_session(nghttp2_session *session, * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` * The |last_stream_id| is invalid. */ -int nghttp2_session_terminate_session2(nghttp2_session *session, - int32_t last_stream_id, - uint32_t error_code); +NGHTTP2_EXTERN int nghttp2_session_terminate_session2(nghttp2_session *session, + int32_t last_stream_id, + uint32_t error_code); /** * @function @@ -2554,7 +2703,7 @@ int nghttp2_session_terminate_session2(nghttp2_session *session, * :enum:`NGHTTP2_ERR_INVALID_STATE` * The |session| is initialized as client. */ -int nghttp2_submit_shutdown_notice(nghttp2_session *session); +NGHTTP2_EXTERN int nghttp2_submit_shutdown_notice(nghttp2_session *session); /** * @function @@ -2563,8 +2712,9 @@ int nghttp2_submit_shutdown_notice(nghttp2_session *session); * The |id| must be one of values defined in * :enum:`nghttp2_settings_id`. */ -uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session, - nghttp2_settings_id id); +NGHTTP2_EXTERN uint32_t + nghttp2_session_get_remote_settings(nghttp2_session *session, + nghttp2_settings_id id); /** * @function @@ -2578,10 +2728,12 @@ uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session, * * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` * The |next_stream_id| is strictly less than the value - * `nghttp2_session_get_next_stream_id()` returns. + * `nghttp2_session_get_next_stream_id()` returns; or + * |next_stream_id| is invalid (e.g., even integer for client, or + * odd integer for server). */ -int nghttp2_session_set_next_stream_id(nghttp2_session *session, - int32_t next_stream_id); +NGHTTP2_EXTERN int nghttp2_session_set_next_stream_id(nghttp2_session *session, + int32_t next_stream_id); /** * @function @@ -2590,14 +2742,19 @@ int nghttp2_session_set_next_stream_id(nghttp2_session *session, * uint32_t. If we run out of stream ID for this session, this * function returns 1 << 31. */ -uint32_t nghttp2_session_get_next_stream_id(nghttp2_session *session); +NGHTTP2_EXTERN uint32_t + nghttp2_session_get_next_stream_id(nghttp2_session *session); /** * @function * * Tells the |session| that |size| bytes for a stream denoted by * |stream_id| were consumed by application and are ready to - * WINDOW_UPDATE. This function is intended to be used without + * WINDOW_UPDATE. The consumed bytes are counted towards both + * connection and stream level WINDOW_UPDATE (see + * `nghttp2_session_consume_connection()` and + * `nghttp2_session_consume_stream()` to update consumption + * independently). This function is intended to be used without * automatic window update (see * `nghttp2_option_set_no_auto_window_update()`). * @@ -2611,8 +2768,49 @@ uint32_t nghttp2_session_get_next_stream_id(nghttp2_session *session); * :enum:`NGHTTP2_ERR_INVALID_STATE` * Automatic WINDOW_UPDATE is not disabled. */ -int nghttp2_session_consume(nghttp2_session *session, int32_t stream_id, - size_t size); +NGHTTP2_EXTERN int nghttp2_session_consume(nghttp2_session *session, + int32_t stream_id, size_t size); + +/** + * @function + * + * Like `nghttp2_session_consume()`, but this only tells library that + * |size| bytes were consumed only for connection level. Note that + * HTTP/2 maintains connection and stream level flow control windows + * independently. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_STATE` + * Automatic WINDOW_UPDATE is not disabled. + */ +NGHTTP2_EXTERN int nghttp2_session_consume_connection(nghttp2_session *session, + size_t size); + +/** + * @function + * + * Like `nghttp2_session_consume()`, but this only tells library that + * |size| bytes were consumed only for stream denoted by |stream_id|. + * Note that HTTP/2 maintains connection and stream level flow control + * windows independently. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * :enum:`NGHTTP2_ERR_INVALID_STATE` + * Automatic WINDOW_UPDATE is not disabled. + */ +NGHTTP2_EXTERN int nghttp2_session_consume_stream(nghttp2_session *session, + int32_t stream_id, + size_t size); /** * @function @@ -2650,9 +2848,10 @@ int nghttp2_session_consume(nghttp2_session *session, int32_t stream_id, * :enum:`NGHTTP2_ERR_PROTO` * The stream ID 1 is already used or closed; or is not available. */ -int nghttp2_session_upgrade(nghttp2_session *session, - const uint8_t *settings_payload, - size_t settings_payloadlen, void *stream_user_data); +NGHTTP2_EXTERN int nghttp2_session_upgrade(nghttp2_session *session, + const uint8_t *settings_payload, + size_t settings_payloadlen, + void *stream_user_data); /** * @function @@ -2675,9 +2874,9 @@ int nghttp2_session_upgrade(nghttp2_session *session, * :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE` * The provided |buflen| size is too small to hold the output. */ -ssize_t nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen, - const nghttp2_settings_entry *iv, - size_t niv); +NGHTTP2_EXTERN ssize_t + nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen, + const nghttp2_settings_entry *iv, size_t niv); /** * @function @@ -2685,7 +2884,7 @@ ssize_t nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen, * Returns string describing the |lib_error_code|. The * |lib_error_code| must be one of the :enum:`nghttp2_error`. */ -const char *nghttp2_strerror(int lib_error_code); +NGHTTP2_EXTERN const char *nghttp2_strerror(int lib_error_code); /** * @function @@ -2697,9 +2896,9 @@ const char *nghttp2_strerror(int lib_error_code); * The |weight| must be in [:enum:`NGHTTP2_MIN_WEIGHT`, * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive. */ -void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec, - int32_t stream_id, int32_t weight, - int exclusive); +NGHTTP2_EXTERN void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec, + int32_t stream_id, + int32_t weight, int exclusive); /** * @function @@ -2708,14 +2907,16 @@ void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec, * are: stream_id = 0, weight = :macro:`NGHTTP2_DEFAULT_WEIGHT` and * exclusive = 0. */ -void nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec); +NGHTTP2_EXTERN void +nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec); /** * @function * * Returns nonzero if the |pri_spec| is filled with default values. */ -int nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec); +NGHTTP2_EXTERN int +nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec); /** * @function @@ -2728,8 +2929,8 @@ int nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec); * use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``, * this function will copy its data members. * - * The `pri_spec->weight` must be in [:enum:`NGHTTP2_MIN_WEIGHT`, - * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive. If `pri_spec->weight` is + * The ``pri_spec->weight`` must be in [:enum:`NGHTTP2_MIN_WEIGHT`, + * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` is * strictly less than :enum:`NGHTTP2_MIN_WEIGHT`, it becomes * :enum:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than * :enum:`NGHTTP2_MAX_WEIGHT`, it becomes :enum:`NGHTTP2_MAX_WEIGHT`. @@ -2777,11 +2978,12 @@ int nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec); * frame. * */ -int32_t nghttp2_submit_request(nghttp2_session *session, - const nghttp2_priority_spec *pri_spec, - const nghttp2_nv *nva, size_t nvlen, - const nghttp2_data_provider *data_prd, - void *stream_user_data); +NGHTTP2_EXTERN int32_t + nghttp2_submit_request(nghttp2_session *session, + const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd, + void *stream_user_data); /** * @function @@ -2832,9 +3034,58 @@ int32_t nghttp2_submit_request(nghttp2_session *session, * program crash. It is generally considered to a programming error * to commit response twice. */ -int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, - const nghttp2_nv *nva, size_t nvlen, - const nghttp2_data_provider *data_prd); +NGHTTP2_EXTERN int +nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd); + +/** + * @function + * + * Submits trailer HEADERS against the stream |stream_id|. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible not to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva|. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. + * + * For server, trailer must be followed by response HEADERS or + * response DATA. The library does not check that response HEADERS + * has already sent and if `nghttp2_submit_trailer()` is called before + * any response HEADERS submission (usually by + * `nghttp2_submit_response()`), the content of |nva| will be sent as + * reponse headers, which will result in error. + * + * This function has the same effect with `nghttp2_submit_headers()`, + * with flags = :enum:`NGHTTP2_FLAG_END_HEADERS` and both pri_spec and + * stream_user_data to NULL. + * + * To submit trailer after `nghttp2_submit_response()` is called, the + * application has to specify :type:`nghttp2_data_provider` to + * `nghttp2_submit_response()`. In side + * :type:`nghttp2_data_source_read_callback`, when setting + * :enum:`NGHTTP2_DATA_FLAG_EOF`, also set + * :enum:`NGHTTP2_DATA_FLAG_NO_END_STREAM`. After that, the + * application can send trailer using `nghttp2_submit_trailer()`. + * `nghttp2_submit_trailer()` can be used inside + * :type:`nghttp2_data_source_read_callback`. + * + * This function returns 0 if it succeeds and |stream_id| is -1. + * Otherwise, this function returns 0 if it succeeds, or one of the + * following negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + */ +NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session, + int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen); /** * @function @@ -2862,8 +3113,8 @@ int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, * use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``, * this function will copy its data members. * - * The `pri_spec->weight` must be in [:enum:`NGHTTP2_MIN_WEIGHT`, - * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive. If `pri_spec->weight` is + * The ``pri_spec->weight`` must be in [:enum:`NGHTTP2_MIN_WEIGHT`, + * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` is * strictly less than :enum:`NGHTTP2_MIN_WEIGHT`, it becomes * :enum:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than * :enum:`NGHTTP2_MAX_WEIGHT`, it becomes :enum:`NGHTTP2_MAX_WEIGHT`. @@ -2908,11 +3159,12 @@ int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, * frame. * */ -int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - const nghttp2_priority_spec *pri_spec, - const nghttp2_nv *nva, size_t nvlen, - void *stream_user_data); +NGHTTP2_EXTERN int32_t + nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, + void *stream_user_data); /** * @function @@ -2948,9 +3200,9 @@ int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, * course, all data except for last one must not have * :enum:`NGHTTP2_FLAG_END_STREAM` flag set in |flags|. */ -int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - const nghttp2_data_provider *data_prd); +NGHTTP2_EXTERN int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_data_provider *data_prd); /** * @function @@ -2966,8 +3218,8 @@ int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, * `nghttp2_priority_spec_init()`. This function will copy its data * members. * - * The `pri_spec->weight` must be in [:enum:`NGHTTP2_MIN_WEIGHT`, - * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive. If `pri_spec->weight` is + * The ``pri_spec->weight`` must be in [:enum:`NGHTTP2_MIN_WEIGHT`, + * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` is * strictly less than :enum:`NGHTTP2_MIN_WEIGHT`, it becomes * :enum:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than * :enum:`NGHTTP2_MAX_WEIGHT`, it becomes :enum:`NGHTTP2_MAX_WEIGHT`. @@ -2981,9 +3233,10 @@ int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, * The |stream_id| is 0; or the |pri_spec| is NULL; or trying to * depend on itself. */ -int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - const nghttp2_priority_spec *pri_spec); +NGHTTP2_EXTERN int +nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec); /** * @function @@ -3004,8 +3257,9 @@ int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` * The |stream_id| is 0. */ -int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags, - int32_t stream_id, uint32_t error_code); +NGHTTP2_EXTERN int nghttp2_submit_rst_stream(nghttp2_session *session, + uint8_t flags, int32_t stream_id, + uint32_t error_code); /** * @function @@ -3040,8 +3294,10 @@ int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags, * :enum:`NGHTTP2_ERR_NOMEM` * Out of memory. */ -int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags, - const nghttp2_settings_entry *iv, size_t niv); +NGHTTP2_EXTERN int nghttp2_submit_settings(nghttp2_session *session, + uint8_t flags, + const nghttp2_settings_entry *iv, + size_t niv); /** * @function @@ -3100,10 +3356,10 @@ int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags, * frame. * */ -int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags, - int32_t stream_id, const nghttp2_nv *nva, - size_t nvlen, - void *promised_stream_user_data); +NGHTTP2_EXTERN int32_t + nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const nghttp2_nv *nva, + size_t nvlen, void *promised_stream_user_data); /** * @function @@ -3126,8 +3382,8 @@ int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags, * :enum:`NGHTTP2_ERR_NOMEM` * Out of memory. */ -int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags, - const uint8_t *opaque_data); +NGHTTP2_EXTERN int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags, + const uint8_t *opaque_data); /** * @function @@ -3174,9 +3430,11 @@ int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags, * The |opaque_data_len| is too large; the |last_stream_id| is * invalid. */ -int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags, - int32_t last_stream_id, uint32_t error_code, - const uint8_t *opaque_data, size_t opaque_data_len); +NGHTTP2_EXTERN int nghttp2_submit_goaway(nghttp2_session *session, + uint8_t flags, int32_t last_stream_id, + uint32_t error_code, + const uint8_t *opaque_data, + size_t opaque_data_len); /** * @function @@ -3189,7 +3447,8 @@ int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags, * * This function always succeeds. */ -int32_t nghttp2_session_get_last_proc_stream_id(nghttp2_session *session); +NGHTTP2_EXTERN int32_t + nghttp2_session_get_last_proc_stream_id(nghttp2_session *session); /** * @function @@ -3199,6 +3458,9 @@ int32_t nghttp2_session_get_last_proc_stream_id(nghttp2_session *session); * The |flags| is currently ignored and should be * :enum:`NGHTTP2_FLAG_NONE`. * + * The |stream_id| is the stream ID to send this WINDOW_UPDATE. To + * send connection level WINDOW_UPDATE, specify 0 to |stream_id|. + * * If the |window_size_increment| is positive, the WINDOW_UPDATE with * that value as window_size_increment is queued. If the * |window_size_increment| is larger than the received bytes from the @@ -3223,9 +3485,10 @@ int32_t nghttp2_session_get_last_proc_stream_id(nghttp2_session *session); * :enum:`NGHTTP2_ERR_NOMEM` * Out of memory. */ -int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - int32_t window_size_increment); +NGHTTP2_EXTERN int nghttp2_submit_window_update(nghttp2_session *session, + uint8_t flags, + int32_t stream_id, + int32_t window_size_increment); /** * @function @@ -3234,11 +3497,12 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags, * parameters, but is deprecated and will be removed in a future * release. This function does nothing and just return 0. */ -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); +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 @@ -3249,7 +3513,8 @@ int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags, * returns positive integer if ``lhs->name`` is found to be greater * than ``rhs->name``; or returns 0 otherwise. */ -int nghttp2_nv_compare_name(const nghttp2_nv *lhs, const nghttp2_nv *rhs); +NGHTTP2_EXTERN int nghttp2_nv_compare_name(const nghttp2_nv *lhs, + const nghttp2_nv *rhs); /** * @function @@ -3308,8 +3573,10 @@ int nghttp2_nv_compare_name(const nghttp2_nv *lhs, const nghttp2_nv *rhs); * SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, my_obj); * */ -int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen, - const unsigned char *in, unsigned int inlen); +NGHTTP2_EXTERN int nghttp2_select_next_protocol(unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen); /** * @function @@ -3320,7 +3587,7 @@ int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen, * version number and if the condition is not met, this function will * return a ``NULL``. Pass in 0 to skip the version checking. */ -nghttp2_info *nghttp2_version(int least_version); +NGHTTP2_EXTERN nghttp2_info *nghttp2_version(int least_version); /** * @function @@ -3328,7 +3595,7 @@ nghttp2_info *nghttp2_version(int least_version); * Returns nonzero if the :type:`nghttp2_error` library error code * |lib_error| is fatal. */ -int nghttp2_is_fatal(int lib_error); +NGHTTP2_EXTERN int nghttp2_is_fatal(int lib_error); /** * @function @@ -3339,7 +3606,7 @@ int nghttp2_is_fatal(int lib_error); * Because this is a header field name in HTTP2, the upper cased alphabet * is treated as error. */ -int nghttp2_check_header_name(const uint8_t *name, size_t len); +NGHTTP2_EXTERN int nghttp2_check_header_name(const uint8_t *name, size_t len); /** * @function @@ -3348,7 +3615,7 @@ int nghttp2_check_header_name(const uint8_t *name, size_t len); * is valid according to * http://tools.ietf.org/html/rfc7230#section-3.2 */ -int nghttp2_check_header_value(const uint8_t *value, size_t len); +NGHTTP2_EXTERN int nghttp2_check_header_value(const uint8_t *value, size_t len); /* HPACK API */ @@ -3377,8 +3644,8 @@ typedef struct nghttp2_hd_deflater nghttp2_hd_deflater; * :enum:`NGHTTP2_ERR_NOMEM` * Out of memory. */ -int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, - size_t deflate_hd_table_bufsize_max); +NGHTTP2_EXTERN int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, + size_t deflate_hd_table_bufsize_max); /** * @function @@ -3395,16 +3662,16 @@ int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, * The library code does not refer to |mem| pointer after this * function returns, so the application can safely free it. */ -int nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr, - size_t deflate_hd_table_bufsize_max, - nghttp2_mem *mem); +NGHTTP2_EXTERN int nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr, + size_t deflate_hd_table_bufsize_max, + nghttp2_mem *mem); /** * @function * * Deallocates any resources allocated for |deflater|. */ -void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater); +NGHTTP2_EXTERN void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater); /** * @function @@ -3429,8 +3696,9 @@ void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater); * :enum:`NGHTTP2_ERR_NOMEM` * Out of memory. */ -int nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, - size_t settings_hd_table_bufsize_max); +NGHTTP2_EXTERN int +nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, + size_t settings_hd_table_bufsize_max); /** * @function @@ -3459,9 +3727,9 @@ int nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, * :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE` * The provided |buflen| size is too small to hold the output. */ -ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, - size_t buflen, const nghttp2_nv *nva, - size_t nvlen); +NGHTTP2_EXTERN ssize_t + nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, + size_t buflen, const nghttp2_nv *nva, size_t nvlen); /** * @function @@ -3469,8 +3737,9 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, * Returns an upper bound on the compressed size after deflation of * |nva| of length |nvlen|. */ -size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, - const nghttp2_nv *nva, size_t nvlen); +NGHTTP2_EXTERN size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, + const nghttp2_nv *nva, + size_t nvlen); struct nghttp2_hd_inflater; @@ -3494,7 +3763,7 @@ typedef struct nghttp2_hd_inflater nghttp2_hd_inflater; * :enum:`NGHTTP2_ERR_NOMEM` * Out of memory. */ -int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr); +NGHTTP2_EXTERN int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr); /** * @function @@ -3511,15 +3780,15 @@ int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr); * The library code does not refer to |mem| pointer after this * function returns, so the application can safely free it. */ -int nghttp2_hd_inflate_new2(nghttp2_hd_inflater **inflater_ptr, - nghttp2_mem *mem); +NGHTTP2_EXTERN int nghttp2_hd_inflate_new2(nghttp2_hd_inflater **inflater_ptr, + nghttp2_mem *mem); /** * @function * * Deallocates any resources allocated for |inflater|. */ -void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater); +NGHTTP2_EXTERN void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater); /** * @function @@ -3536,8 +3805,9 @@ void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater); * :enum:`NGHTTP2_ERR_NOMEM` * Out of memory. */ -int nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater, - size_t settings_hd_table_bufsize_max); +NGHTTP2_EXTERN int +nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater, + size_t settings_hd_table_bufsize_max); /** * @enum @@ -3636,9 +3906,10 @@ typedef enum { * } * */ -ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, - int *inflate_flags, uint8_t *in, size_t inlen, - int in_final); +NGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, + int *inflate_flags, uint8_t *in, + size_t inlen, int in_final); /** * @function @@ -3648,7 +3919,8 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, * This function returns 0 if it succeeds. Currently this function * always succeeds. */ -int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater); +NGHTTP2_EXTERN int +nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater); #ifdef __cplusplus } diff --git a/epan/nghttp2/nghttp2_buf.c b/epan/nghttp2/nghttp2_buf.c index f747ae989a..821edb98e7 100644 --- a/epan/nghttp2/nghttp2_buf.c +++ b/epan/nghttp2/nghttp2_buf.c @@ -410,36 +410,46 @@ ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) { len += nghttp2_buf_len(&chain->buf); } - if (!len) { + if (len == 0) { res = NULL; - } else { - res = (uint8_t *)nghttp2_mem_malloc(bufs->mem, len); + return 0; + } - if (res == NULL) { - return NGHTTP2_ERR_NOMEM; - } + res = (uint8_t *)nghttp2_mem_malloc(bufs->mem, len); + if (res == NULL) { + return NGHTTP2_ERR_NOMEM; } nghttp2_buf_wrap_init(&resbuf, res, len); for (chain = bufs->head; chain; chain = chain->next) { buf = &chain->buf; - - if (resbuf.last) { - resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf)); - } - - nghttp2_buf_reset(buf); - nghttp2_buf_shift_right(&chain->buf, bufs->offset); + resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf)); } - bufs->cur = bufs->head; - *out = res; return (ssize_t)len; } +size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out) { + size_t len; + nghttp2_buf_chain *chain; + nghttp2_buf *buf; + nghttp2_buf resbuf; + + len = nghttp2_bufs_len(bufs); + + nghttp2_buf_wrap_init(&resbuf, out, len); + + for (chain = bufs->head; chain; chain = chain->next) { + buf = &chain->buf; + resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf)); + } + + return len; +} + void nghttp2_bufs_reset(nghttp2_bufs *bufs) { nghttp2_buf_chain *chain, *ci; size_t k; diff --git a/epan/nghttp2/nghttp2_buf.h b/epan/nghttp2/nghttp2_buf.h index 108147e914..a84dc7bea2 100644 --- a/epan/nghttp2/nghttp2_buf.h +++ b/epan/nghttp2/nghttp2_buf.h @@ -313,9 +313,8 @@ int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b); * function allocates the contagious memory to store all data in * |bufs| and assigns it to |*out|. * - * On successful return, nghttp2_bufs_len(bufs) returns 0, just like - * after calling nghttp2_bufs_reset(). - + * The contents of |bufs| is left unchanged. + * * This function returns the length of copied data and assigns the * pointer to copied data to |*out| if it succeeds, or one of the * following negative error codes: @@ -325,6 +324,17 @@ int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b); */ ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out); +/* + * Copies all data stored in |bufs| to |out|. This function assumes + * that the buffer space pointed by |out| has at least + * nghttp2_bufs(bufs) bytes. + * + * The contents of |bufs| is left unchanged. + * + * This function returns the length of copied data. + */ +size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out); + /* * Resets |bufs| and makes the buffers empty. */ diff --git a/epan/nghttp2/nghttp2_hd.c b/epan/nghttp2/nghttp2_hd.c index 81726cd5f1..2f0c00228a 100644 --- a/epan/nghttp2/nghttp2_hd.c +++ b/epan/nghttp2/nghttp2_hd.c @@ -31,118 +31,472 @@ #include "nghttp2_helper.h" #include "nghttp2_int.h" -#define STATIC_TABLE_LENGTH 61 - -/* Make scalar initialization form of nghttp2_nv */ -#define MAKE_STATIC_ENT(I, N, V, NH, VH) \ +/* Make scalar initialization form of nghttp2_hd_entry */ +#define MAKE_STATIC_ENT(N, V, T) \ { \ - { \ - { (uint8_t *)(N), (uint8_t *)(V), sizeof((N)) - 1, sizeof((V)) - 1, 0 } \ - , (NH), (VH), 1, NGHTTP2_HD_FLAG_NONE \ - } \ - , I \ + { (uint8_t *)(N), (uint8_t *)(V), sizeof((N)) - 1, sizeof((V)) - 1, 0 } \ + , (T), 1, NGHTTP2_HD_FLAG_NONE \ } /* Generated by mkstatictbl.py */ -/* Sorted by hash(name) and its table index */ -static nghttp2_hd_static_entry static_table[] = { - MAKE_STATIC_ENT(20, "age", "", 96511u, 0u), - MAKE_STATIC_ENT(59, "via", "", 116750u, 0u), - MAKE_STATIC_ENT(32, "date", "", 3076014u, 0u), - MAKE_STATIC_ENT(33, "etag", "", 3123477u, 0u), - MAKE_STATIC_ENT(36, "from", "", 3151786u, 0u), - MAKE_STATIC_ENT(37, "host", "", 3208616u, 0u), - MAKE_STATIC_ENT(44, "link", "", 3321850u, 0u), - MAKE_STATIC_ENT(58, "vary", "", 3612210u, 0u), - MAKE_STATIC_ENT(38, "if-match", "", 34533653u, 0u), - MAKE_STATIC_ENT(41, "if-range", "", 39145613u, 0u), - MAKE_STATIC_ENT(3, ":path", "/", 56997727u, 47u), - MAKE_STATIC_ENT(4, ":path", "/index.html", 56997727u, 2144181430u), - MAKE_STATIC_ENT(21, "allow", "", 92906313u, 0u), - MAKE_STATIC_ENT(49, "range", "", 108280125u, 0u), - MAKE_STATIC_ENT(14, "accept-charset", "", 124285319u, 0u), - MAKE_STATIC_ENT(43, "last-modified", "", 150043680u, 0u), - MAKE_STATIC_ENT(48, "proxy-authorization", "", 329532250u, 0u), - MAKE_STATIC_ENT(57, "user-agent", "", 486342275u, 0u), - MAKE_STATIC_ENT(40, "if-none-match", "", 646073760u, 0u), - MAKE_STATIC_ENT(30, "content-type", "", 785670158u, 0u), - MAKE_STATIC_ENT(16, "accept-language", "", 802785917u, 0u), - MAKE_STATIC_ENT(50, "referer", "", 1085069613u, 0u), - MAKE_STATIC_ENT(51, "refresh", "", 1085444827u, 0u), - MAKE_STATIC_ENT(55, "strict-transport-security", "", 1153852136u, 0u), - MAKE_STATIC_ENT(54, "set-cookie", "", 1237214767u, 0u), - MAKE_STATIC_ENT(56, "transfer-encoding", "", 1274458357u, 0u), - MAKE_STATIC_ENT(17, "accept-ranges", "", 1397189435u, 0u), - MAKE_STATIC_ENT(42, "if-unmodified-since", "", 1454068927u, 0u), - MAKE_STATIC_ENT(46, "max-forwards", "", 1619948695u, 0u), - MAKE_STATIC_ENT(45, "location", "", 1901043637u, 0u), - MAKE_STATIC_ENT(52, "retry-after", "", 1933352567u, 0u), - MAKE_STATIC_ENT(25, "content-encoding", "", 2095084583u, 0u), - MAKE_STATIC_ENT(28, "content-location", "", 2284906121u, 0u), - MAKE_STATIC_ENT(39, "if-modified-since", "", 2302095846u, 0u), - MAKE_STATIC_ENT(18, "accept", "", 2871506184u, 0u), - MAKE_STATIC_ENT(29, "content-range", "", 2878374633u, 0u), - MAKE_STATIC_ENT(22, "authorization", "", 2909397113u, 0u), - MAKE_STATIC_ENT(31, "cookie", "", 2940209764u, 0u), - MAKE_STATIC_ENT(0, ":authority", "", 2962729033u, 0u), - MAKE_STATIC_ENT(35, "expires", "", 2985731892u, 0u), - MAKE_STATIC_ENT(34, "expect", "", 3005803609u, 0u), - MAKE_STATIC_ENT(24, "content-disposition", "", 3027699811u, 0u), - MAKE_STATIC_ENT(26, "content-language", "", 3065240108u, 0u), - MAKE_STATIC_ENT(1, ":method", "GET", 3153018267u, 70454u), - MAKE_STATIC_ENT(2, ":method", "POST", 3153018267u, 2461856u), - MAKE_STATIC_ENT(27, "content-length", "", 3162187450u, 0u), - MAKE_STATIC_ENT(19, "access-control-allow-origin", "", 3297999203u, 0u), - MAKE_STATIC_ENT(5, ":scheme", "http", 3322585695u, 3213448u), - MAKE_STATIC_ENT(6, ":scheme", "https", 3322585695u, 99617003u), - MAKE_STATIC_ENT(7, ":status", "200", 3338091692u, 49586u), - MAKE_STATIC_ENT(8, ":status", "204", 3338091692u, 49590u), - MAKE_STATIC_ENT(9, ":status", "206", 3338091692u, 49592u), - MAKE_STATIC_ENT(10, ":status", "304", 3338091692u, 50551u), - MAKE_STATIC_ENT(11, ":status", "400", 3338091692u, 51508u), - MAKE_STATIC_ENT(12, ":status", "404", 3338091692u, 51512u), - MAKE_STATIC_ENT(13, ":status", "500", 3338091692u, 52469u), - MAKE_STATIC_ENT(53, "server", "", 3389140803u, 0u), - MAKE_STATIC_ENT(47, "proxy-authenticate", "", 3993199572u, 0u), - MAKE_STATIC_ENT(60, "www-authenticate", "", 4051929931u, 0u), - MAKE_STATIC_ENT(23, "cache-control", "", 4086191634u, 0u), - MAKE_STATIC_ENT(15, "accept-encoding", "gzip, deflate", 4127597688u, - 1733326877u), +/* 3rd parameter is nghttp2_token value for header field name. We use + first enum value if same header names are repeated (e.g., + :status). */ +static nghttp2_hd_entry static_table[] = { + MAKE_STATIC_ENT(":authority", "", 0), + MAKE_STATIC_ENT(":method", "GET", 1), + MAKE_STATIC_ENT(":method", "POST", 1), + MAKE_STATIC_ENT(":path", "/", 3), + MAKE_STATIC_ENT(":path", "/index.html", 3), + MAKE_STATIC_ENT(":scheme", "http", 5), + MAKE_STATIC_ENT(":scheme", "https", 5), + MAKE_STATIC_ENT(":status", "200", 7), + MAKE_STATIC_ENT(":status", "204", 7), + MAKE_STATIC_ENT(":status", "206", 7), + MAKE_STATIC_ENT(":status", "304", 7), + MAKE_STATIC_ENT(":status", "400", 7), + MAKE_STATIC_ENT(":status", "404", 7), + MAKE_STATIC_ENT(":status", "500", 7), + MAKE_STATIC_ENT("accept-charset", "", 14), + MAKE_STATIC_ENT("accept-encoding", "gzip, deflate", 15), + MAKE_STATIC_ENT("accept-language", "", 16), + MAKE_STATIC_ENT("accept-ranges", "", 17), + MAKE_STATIC_ENT("accept", "", 18), + MAKE_STATIC_ENT("access-control-allow-origin", "", 19), + MAKE_STATIC_ENT("age", "", 20), + MAKE_STATIC_ENT("allow", "", 21), + MAKE_STATIC_ENT("authorization", "", 22), + MAKE_STATIC_ENT("cache-control", "", 23), + MAKE_STATIC_ENT("content-disposition", "", 24), + MAKE_STATIC_ENT("content-encoding", "", 25), + MAKE_STATIC_ENT("content-language", "", 26), + MAKE_STATIC_ENT("content-length", "", 27), + MAKE_STATIC_ENT("content-location", "", 28), + MAKE_STATIC_ENT("content-range", "", 29), + MAKE_STATIC_ENT("content-type", "", 30), + MAKE_STATIC_ENT("cookie", "", 31), + MAKE_STATIC_ENT("date", "", 32), + MAKE_STATIC_ENT("etag", "", 33), + MAKE_STATIC_ENT("expect", "", 34), + MAKE_STATIC_ENT("expires", "", 35), + MAKE_STATIC_ENT("from", "", 36), + MAKE_STATIC_ENT("host", "", 37), + MAKE_STATIC_ENT("if-match", "", 38), + MAKE_STATIC_ENT("if-modified-since", "", 39), + MAKE_STATIC_ENT("if-none-match", "", 40), + MAKE_STATIC_ENT("if-range", "", 41), + MAKE_STATIC_ENT("if-unmodified-since", "", 42), + MAKE_STATIC_ENT("last-modified", "", 43), + MAKE_STATIC_ENT("link", "", 44), + MAKE_STATIC_ENT("location", "", 45), + MAKE_STATIC_ENT("max-forwards", "", 46), + MAKE_STATIC_ENT("proxy-authenticate", "", 47), + MAKE_STATIC_ENT("proxy-authorization", "", 48), + MAKE_STATIC_ENT("range", "", 49), + MAKE_STATIC_ENT("referer", "", 50), + MAKE_STATIC_ENT("refresh", "", 51), + MAKE_STATIC_ENT("retry-after", "", 52), + MAKE_STATIC_ENT("server", "", 53), + MAKE_STATIC_ENT("set-cookie", "", 54), + MAKE_STATIC_ENT("strict-transport-security", "", 55), + MAKE_STATIC_ENT("transfer-encoding", "", 56), + MAKE_STATIC_ENT("user-agent", "", 57), + MAKE_STATIC_ENT("vary", "", 58), + MAKE_STATIC_ENT("via", "", 59), + MAKE_STATIC_ENT("www-authenticate", "", 60), }; -/* Index to the position in static_table */ -const size_t static_table_index[] = { - 38, 43, 44, 10, 11, 47, 48, 49, 50, 51, 52, 53, 54, 55, 14, 60, - 20, 26, 34, 46, 0, 12, 36, 59, 41, 31, 42, 45, 32, 35, 19, 37, - 2, 3, 40, 39, 4, 5, 8, 33, 18, 9, 27, 15, 6, 29, 28, 57, - 16, 13, 21, 22, 30, 56, 24, 23, 25, 17, 7, 1, 58}; - -const size_t NGHTTP2_STATIC_TABLE_LENGTH = - sizeof(static_table) / sizeof(static_table[0]); - static int memeq(const void *s1, const void *s2, size_t n) { - const uint8_t *a = (const uint8_t *)s1, *b = (const uint8_t *)s2; - uint8_t c = 0; - while (n > 0) { - c |= (*a++) ^ (*b++); - --n; - } - return c == 0; + return memcmp(s1, s2, n) == 0; } -static uint32_t hash(const uint8_t *s, size_t n) { - uint32_t h = 0; - while (n > 0) { - h = h * 31 + *s++; - --n; +/* + * This function was generated by genlibtokenlookup.py. Inspired by + * h2o header lookup. https://github.com/h2o/h2o + */ +static int lookup_token(const uint8_t *name, size_t namelen) { + switch (namelen) { + case 2: + switch (name[1]) { + case 'e': + if (lstreq("t", name, 1)) { + return NGHTTP2_TOKEN_TE; + } + break; + } + break; + case 3: + switch (name[2]) { + case 'a': + if (lstreq("vi", name, 2)) { + return NGHTTP2_TOKEN_VIA; + } + break; + case 'e': + if (lstreq("ag", name, 2)) { + return NGHTTP2_TOKEN_AGE; + } + break; + } + break; + case 4: + switch (name[3]) { + case 'e': + if (lstreq("dat", name, 3)) { + return NGHTTP2_TOKEN_DATE; + } + break; + case 'g': + if (lstreq("eta", name, 3)) { + return NGHTTP2_TOKEN_ETAG; + } + break; + case 'k': + if (lstreq("lin", name, 3)) { + return NGHTTP2_TOKEN_LINK; + } + break; + case 'm': + if (lstreq("fro", name, 3)) { + return NGHTTP2_TOKEN_FROM; + } + break; + case 't': + if (lstreq("hos", name, 3)) { + return NGHTTP2_TOKEN_HOST; + } + break; + case 'y': + if (lstreq("var", name, 3)) { + return NGHTTP2_TOKEN_VARY; + } + break; + } + break; + case 5: + switch (name[4]) { + case 'e': + if (lstreq("rang", name, 4)) { + return NGHTTP2_TOKEN_RANGE; + } + break; + case 'h': + if (lstreq(":pat", name, 4)) { + return NGHTTP2_TOKEN__PATH; + } + if (lstreq(":pat", name, 4)) { + return NGHTTP2_TOKEN__PATH; + } + break; + case 'w': + if (lstreq("allo", name, 4)) { + return NGHTTP2_TOKEN_ALLOW; + } + break; + } + break; + case 6: + switch (name[5]) { + case 'e': + if (lstreq("cooki", name, 5)) { + return NGHTTP2_TOKEN_COOKIE; + } + break; + case 'r': + if (lstreq("serve", name, 5)) { + return NGHTTP2_TOKEN_SERVER; + } + break; + case 't': + if (lstreq("accep", name, 5)) { + return NGHTTP2_TOKEN_ACCEPT; + } + if (lstreq("expec", name, 5)) { + return NGHTTP2_TOKEN_EXPECT; + } + break; + } + break; + case 7: + switch (name[6]) { + case 'd': + if (lstreq(":metho", name, 6)) { + return NGHTTP2_TOKEN__METHOD; + } + if (lstreq(":metho", name, 6)) { + return NGHTTP2_TOKEN__METHOD; + } + break; + case 'e': + if (lstreq(":schem", name, 6)) { + return NGHTTP2_TOKEN__SCHEME; + } + if (lstreq(":schem", name, 6)) { + return NGHTTP2_TOKEN__SCHEME; + } + if (lstreq("upgrad", name, 6)) { + return NGHTTP2_TOKEN_UPGRADE; + } + break; + case 'h': + if (lstreq("refres", name, 6)) { + return NGHTTP2_TOKEN_REFRESH; + } + break; + case 'r': + if (lstreq("refere", name, 6)) { + return NGHTTP2_TOKEN_REFERER; + } + break; + case 's': + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq("expire", name, 6)) { + return NGHTTP2_TOKEN_EXPIRES; + } + break; + } + break; + case 8: + switch (name[7]) { + case 'e': + if (lstreq("if-rang", name, 7)) { + return NGHTTP2_TOKEN_IF_RANGE; + } + break; + case 'h': + if (lstreq("if-matc", name, 7)) { + return NGHTTP2_TOKEN_IF_MATCH; + } + break; + case 'n': + if (lstreq("locatio", name, 7)) { + return NGHTTP2_TOKEN_LOCATION; + } + break; + } + break; + case 10: + switch (name[9]) { + case 'e': + if (lstreq("keep-aliv", name, 9)) { + return NGHTTP2_TOKEN_KEEP_ALIVE; + } + if (lstreq("set-cooki", name, 9)) { + return NGHTTP2_TOKEN_SET_COOKIE; + } + break; + case 'n': + if (lstreq("connectio", name, 9)) { + return NGHTTP2_TOKEN_CONNECTION; + } + break; + case 't': + if (lstreq("user-agen", name, 9)) { + return NGHTTP2_TOKEN_USER_AGENT; + } + break; + case 'y': + if (lstreq(":authorit", name, 9)) { + return NGHTTP2_TOKEN__AUTHORITY; + } + break; + } + break; + case 11: + switch (name[10]) { + case 'r': + if (lstreq("retry-afte", name, 10)) { + return NGHTTP2_TOKEN_RETRY_AFTER; + } + break; + } + break; + case 12: + switch (name[11]) { + case 'e': + if (lstreq("content-typ", name, 11)) { + return NGHTTP2_TOKEN_CONTENT_TYPE; + } + break; + case 's': + if (lstreq("max-forward", name, 11)) { + return NGHTTP2_TOKEN_MAX_FORWARDS; + } + break; + } + break; + case 13: + switch (name[12]) { + case 'd': + if (lstreq("last-modifie", name, 12)) { + return NGHTTP2_TOKEN_LAST_MODIFIED; + } + break; + case 'e': + if (lstreq("content-rang", name, 12)) { + return NGHTTP2_TOKEN_CONTENT_RANGE; + } + break; + case 'h': + if (lstreq("if-none-matc", name, 12)) { + return NGHTTP2_TOKEN_IF_NONE_MATCH; + } + break; + case 'l': + if (lstreq("cache-contro", name, 12)) { + return NGHTTP2_TOKEN_CACHE_CONTROL; + } + break; + case 'n': + if (lstreq("authorizatio", name, 12)) { + return NGHTTP2_TOKEN_AUTHORIZATION; + } + break; + case 's': + if (lstreq("accept-range", name, 12)) { + return NGHTTP2_TOKEN_ACCEPT_RANGES; + } + break; + } + break; + case 14: + switch (name[13]) { + case 'h': + if (lstreq("content-lengt", name, 13)) { + return NGHTTP2_TOKEN_CONTENT_LENGTH; + } + break; + case 't': + if (lstreq("accept-charse", name, 13)) { + return NGHTTP2_TOKEN_ACCEPT_CHARSET; + } + break; + } + break; + case 15: + switch (name[14]) { + case 'e': + if (lstreq("accept-languag", name, 14)) { + return NGHTTP2_TOKEN_ACCEPT_LANGUAGE; + } + break; + case 'g': + if (lstreq("accept-encodin", name, 14)) { + return NGHTTP2_TOKEN_ACCEPT_ENCODING; + } + break; + } + break; + case 16: + switch (name[15]) { + case 'e': + if (lstreq("content-languag", name, 15)) { + return NGHTTP2_TOKEN_CONTENT_LANGUAGE; + } + if (lstreq("www-authenticat", name, 15)) { + return NGHTTP2_TOKEN_WWW_AUTHENTICATE; + } + break; + case 'g': + if (lstreq("content-encodin", name, 15)) { + return NGHTTP2_TOKEN_CONTENT_ENCODING; + } + break; + case 'n': + if (lstreq("content-locatio", name, 15)) { + return NGHTTP2_TOKEN_CONTENT_LOCATION; + } + if (lstreq("proxy-connectio", name, 15)) { + return NGHTTP2_TOKEN_PROXY_CONNECTION; + } + break; + } + break; + case 17: + switch (name[16]) { + case 'e': + if (lstreq("if-modified-sinc", name, 16)) { + return NGHTTP2_TOKEN_IF_MODIFIED_SINCE; + } + break; + case 'g': + if (lstreq("transfer-encodin", name, 16)) { + return NGHTTP2_TOKEN_TRANSFER_ENCODING; + } + break; + } + break; + case 18: + switch (name[17]) { + case 'e': + if (lstreq("proxy-authenticat", name, 17)) { + return NGHTTP2_TOKEN_PROXY_AUTHENTICATE; + } + break; + } + break; + case 19: + switch (name[18]) { + case 'e': + if (lstreq("if-unmodified-sinc", name, 18)) { + return NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE; + } + break; + case 'n': + if (lstreq("content-dispositio", name, 18)) { + return NGHTTP2_TOKEN_CONTENT_DISPOSITION; + } + if (lstreq("proxy-authorizatio", name, 18)) { + return NGHTTP2_TOKEN_PROXY_AUTHORIZATION; + } + break; + } + break; + case 25: + switch (name[24]) { + case 'y': + if (lstreq("strict-transport-securit", name, 24)) { + return NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY; + } + break; + } + break; + case 27: + switch (name[26]) { + case 'n': + if (lstreq("access-control-allow-origi", name, 26)) { + return NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN; + } + break; + } + break; } - return h; + return -1; } int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name, size_t namelen, uint8_t *value, size_t valuelen, - uint32_t name_hash, uint32_t value_hash, - nghttp2_mem *mem) { + int token, nghttp2_mem *mem) { int rv = 0; /* Since nghttp2_hd_entry is used for indexing, ent->nv.flags always @@ -152,10 +506,11 @@ int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name, if ((flags & NGHTTP2_HD_FLAG_NAME_ALLOC) && (flags & NGHTTP2_HD_FLAG_NAME_GIFT) == 0) { if (namelen == 0) { - /* We should not allow empty header field name */ - ent->nv.name = NULL; + flags &= ~NGHTTP2_HD_FLAG_NAME_ALLOC; + ent->nv.name = (uint8_t *)""; } else { - ent->nv.name = (uint8_t *)nghttp2_memdup(name, namelen, mem); + /* copy including terminating NULL byte */ + ent->nv.name = (uint8_t *)nghttp2_memdup(name, namelen + 1, mem); if (ent->nv.name == NULL) { rv = NGHTTP2_ERR_NOMEM; goto fail; @@ -167,9 +522,11 @@ int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name, if ((flags & NGHTTP2_HD_FLAG_VALUE_ALLOC) && (flags & NGHTTP2_HD_FLAG_VALUE_GIFT) == 0) { if (valuelen == 0) { - ent->nv.value = NULL; + flags &= ~NGHTTP2_HD_FLAG_VALUE_ALLOC; + ent->nv.value = (uint8_t *)""; } else { - ent->nv.value = (uint8_t *)nghttp2_memdup(value, valuelen, mem); + /* copy including terminating NULL byte */ + ent->nv.value = (uint8_t *)nghttp2_memdup(value, valuelen + 1, mem); if (ent->nv.value == NULL) { rv = NGHTTP2_ERR_NOMEM; goto fail2; @@ -180,12 +537,10 @@ int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name, } ent->nv.namelen = namelen; ent->nv.valuelen = valuelen; + ent->token = token; ent->ref = 1; ent->flags = flags; - ent->name_hash = name_hash; - ent->value_hash = value_hash; - return 0; fail2: @@ -403,25 +758,23 @@ static size_t entry_room(size_t namelen, size_t valuelen) { return NGHTTP2_HD_ENTRY_OVERHEAD + namelen + valuelen; } -static int emit_indexed_header(nghttp2_nv *nv_out, nghttp2_hd_entry *ent) { - DEBUGF(fprintf(stderr, "inflatehd: header emission: ")); - DEBUGF(fwrite(ent->nv.name, ent->nv.namelen, 1, stderr)); - DEBUGF(fprintf(stderr, ": ")); - DEBUGF(fwrite(ent->nv.value, ent->nv.valuelen, 1, stderr)); - DEBUGF(fprintf(stderr, "\n")); +static int emit_indexed_header(nghttp2_nv *nv_out, int *token_out, + nghttp2_hd_entry *ent) { + DEBUGF(fprintf(stderr, "inflatehd: header emission: %s: %s\n", ent->nv.name, + ent->nv.value)); /* ent->ref may be 0. This happens if the encoder emits literal block larger than header table capacity with indexing. */ *nv_out = ent->nv; + *token_out = ent->token; return 0; } -static int emit_literal_header(nghttp2_nv *nv_out, nghttp2_nv *nv) { - DEBUGF(fprintf(stderr, "inflatehd: header emission: ")); - DEBUGF(fwrite(nv->name, nv->namelen, 1, stderr)); - DEBUGF(fprintf(stderr, ": ")); - DEBUGF(fwrite(nv->value, nv->valuelen, 1, stderr)); - DEBUGF(fprintf(stderr, "\n")); +static int emit_literal_header(nghttp2_nv *nv_out, int *token_out, + nghttp2_nv *nv) { + DEBUGF(fprintf(stderr, "inflatehd: header emission: %s: %s\n", nv->name, + nv->value)); *nv_out = *nv; + *token_out = lookup_token(nv->name, nv->namelen); return 0; } @@ -642,38 +995,39 @@ static int emit_string(nghttp2_bufs *bufs, const uint8_t *str, size_t len) { return rv; } -static uint8_t pack_first_byte(int inc_indexing, int no_index) { - if (inc_indexing) { +static uint8_t pack_first_byte(int indexing_mode) { + switch (indexing_mode) { + case NGHTTP2_HD_WITH_INDEXING: return 0x40u; - } - - if (no_index) { + case NGHTTP2_HD_WITHOUT_INDEXING: + return 0; + case NGHTTP2_HD_NEVER_INDEXING: return 0x10u; + default: + assert(0); } - + /* This is required to compile with android NDK r10d + + --enable-werror */ return 0; } static int emit_indname_block(nghttp2_bufs *bufs, size_t idx, - const nghttp2_nv *nv, int inc_indexing) { + const nghttp2_nv *nv, int indexing_mode) { int rv; uint8_t *bufp; size_t blocklen; uint8_t sb[16]; size_t prefixlen; - int no_index; - no_index = (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0; - - if (inc_indexing) { + if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) { prefixlen = 6; } else { prefixlen = 4; } DEBUGF(fprintf(stderr, "deflatehd: emit indname index=%zu, valuelen=%zu, " - "indexing=%d, no_index=%d\n", - idx, nv->valuelen, inc_indexing, no_index)); + "indexing_mode=%d\n", + idx, nv->valuelen, indexing_mode)); blocklen = count_encoded_length(idx + 1, prefixlen); @@ -683,7 +1037,7 @@ static int emit_indname_block(nghttp2_bufs *bufs, size_t idx, bufp = sb; - *bufp = pack_first_byte(inc_indexing, no_index); + *bufp = pack_first_byte(indexing_mode); encode_length(bufp, idx + 1, prefixlen); @@ -701,17 +1055,14 @@ static int emit_indname_block(nghttp2_bufs *bufs, size_t idx, } static int emit_newname_block(nghttp2_bufs *bufs, const nghttp2_nv *nv, - int inc_indexing) { + int indexing_mode) { int rv; - int no_index; - - no_index = (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0; DEBUGF(fprintf(stderr, "deflatehd: emit newname namelen=%zu, valuelen=%zu, " - "indexing=%d, no_index=%d\n", - nv->namelen, nv->valuelen, inc_indexing, no_index)); + "indexing_mode=%d\n", + nv->namelen, nv->valuelen, indexing_mode)); - rv = nghttp2_bufs_addb(bufs, pack_first_byte(inc_indexing, no_index)); + rv = nghttp2_bufs_addb(bufs, pack_first_byte(indexing_mode)); if (rv != 0) { return rv; } @@ -731,8 +1082,7 @@ static int emit_newname_block(nghttp2_bufs *bufs, const nghttp2_nv *nv, static nghttp2_hd_entry *add_hd_table_incremental(nghttp2_hd_context *context, const nghttp2_nv *nv, - uint32_t name_hash, - uint32_t value_hash, + int token, uint8_t entry_flags) { int rv; nghttp2_hd_entry *new_ent; @@ -750,11 +1100,9 @@ static nghttp2_hd_entry *add_hd_table_incremental(nghttp2_hd_context *context, context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen); - DEBUGF(fprintf(stderr, "hpack: remove item from header table: ")); - DEBUGF(fwrite(ent->nv.name, ent->nv.namelen, 1, stderr)); - DEBUGF(fprintf(stderr, ": ")); - DEBUGF(fwrite(ent->nv.value, ent->nv.valuelen, 1, stderr)); - DEBUGF(fprintf(stderr, "\n")); + DEBUGF(fprintf(stderr, "hpack: remove item from header table: %s: %s\n", + ent->nv.name, ent->nv.value)); + hd_ringbuf_pop_back(&context->hd_table); if (--ent->ref == 0) { nghttp2_hd_entry_free(ent, mem); @@ -768,8 +1116,7 @@ static nghttp2_hd_entry *add_hd_table_incremental(nghttp2_hd_context *context, } rv = nghttp2_hd_entry_init(new_ent, entry_flags, nv->name, nv->namelen, - nv->value, nv->valuelen, name_hash, value_hash, - mem); + nv->value, nv->valuelen, token, mem); if (rv != 0) { nghttp2_mem_free(mem, new_ent); return NULL; @@ -810,11 +1157,15 @@ static nghttp2_hd_entry *add_hd_table_incremental(nghttp2_hd_context *context, } static int name_eq(const nghttp2_nv *a, const nghttp2_nv *b) { - return a->namelen == b->namelen && memeq(a->name, b->name, a->namelen); + return a->namelen == b->namelen && + a->name[a->namelen - 1] == b->name[a->namelen - 1] && + memeq(a->name, b->name, a->namelen); } static int value_eq(const nghttp2_nv *a, const nghttp2_nv *b) { - return a->valuelen == b->valuelen && memeq(a->value, b->value, a->valuelen); + return a->valuelen == b->valuelen && + a->value[a->valuelen - 1] == b->value[a->valuelen - 1] && + memeq(a->value, b->value, a->valuelen); } typedef struct { @@ -823,61 +1174,54 @@ typedef struct { uint8_t name_value_match; } search_result; +static search_result search_static_table(const nghttp2_nv *nv, int token, + int indexing_mode) { + search_result res = {token, 0}; + int i; + + if (indexing_mode == NGHTTP2_HD_NEVER_INDEXING) { + return res; + } + + for (i = token; + i <= NGHTTP2_TOKEN_WWW_AUTHENTICATE && static_table[i].token == token; + ++i) { + if (value_eq(&static_table[i].nv, nv)) { + res.index = i; + res.name_value_match = 1; + return res; + } + } + return res; +} + static search_result search_hd_table(nghttp2_hd_context *context, - const nghttp2_nv *nv, uint32_t name_hash, - uint32_t value_hash) { - ssize_t left = -1, right = (ssize_t)STATIC_TABLE_LENGTH; + const nghttp2_nv *nv, int token, + int indexing_mode) { search_result res = {-1, 0}; size_t i; - int use_index = (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) == 0; - - /* Search dynamic table first, so that we can find recently used - entry first */ - if (use_index) { - for (i = 0; i < context->hd_table.len; ++i) { - nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, i); - if (ent->name_hash != name_hash || !name_eq(&ent->nv, nv)) { - continue; - } - if (res.index == -1) { - res.index = (ssize_t)(i + NGHTTP2_STATIC_TABLE_LENGTH); - } - - if (ent->value_hash == value_hash && value_eq(&ent->nv, nv)) { - res.index = (ssize_t)(i + NGHTTP2_STATIC_TABLE_LENGTH); - res.name_value_match = 1; - return res; - } + if (token >= 0 && token <= NGHTTP2_TOKEN_WWW_AUTHENTICATE) { + res = search_static_table(nv, token, indexing_mode); + if (res.name_value_match) { + return res; } } - while (right - left > 1) { - ssize_t mid = (left + right) / 2; - nghttp2_hd_entry *ent = &static_table[mid].ent; - if (ent->name_hash < name_hash) { - left = mid; - } else { - right = mid; + for (i = 0; i < context->hd_table.len; ++i) { + nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, i); + if (ent->token != token || (token == -1 && !name_eq(&ent->nv, nv))) { + continue; } - } - for (i = right; i < STATIC_TABLE_LENGTH; ++i) { - nghttp2_hd_entry *ent = &static_table[i].ent; - if (ent->name_hash != name_hash) { - break; + if (res.index == -1) { + res.index = (ssize_t)(i + NGHTTP2_STATIC_TABLE_LENGTH); } - if (name_eq(&ent->nv, nv)) { - if (res.index == -1) { - res.index = (ssize_t)(static_table[i].index); - } - if (use_index && ent->value_hash == value_hash && - value_eq(&ent->nv, nv)) { - res.index = (ssize_t)(static_table[i].index); - res.name_value_match = 1; - return res; - } + if (indexing_mode != NGHTTP2_HD_NEVER_INDEXING && value_eq(&ent->nv, nv)) { + res.index = (ssize_t)(i + NGHTTP2_STATIC_TABLE_LENGTH); + res.name_value_match = 1; + return res; } } @@ -940,29 +1284,23 @@ nghttp2_hd_entry *nghttp2_hd_table_get(nghttp2_hd_context *context, return hd_ringbuf_get(&context->hd_table, idx - NGHTTP2_STATIC_TABLE_LENGTH); } else { - return &static_table[static_table_index[idx]].ent; + return &static_table[idx]; } } -#define name_match(NV, NAME) \ - (nv->namelen == sizeof(NAME) - 1 && memeq(nv->name, NAME, sizeof(NAME) - 1)) - -static int hd_deflate_should_indexing(nghttp2_hd_deflater *deflater, - const nghttp2_nv *nv) { - if ((nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) || +static int hd_deflate_decide_indexing(nghttp2_hd_deflater *deflater, + const nghttp2_nv *nv, int token) { + if (token == NGHTTP2_TOKEN__PATH || token == NGHTTP2_TOKEN_AGE || + token == NGHTTP2_TOKEN_CONTENT_LENGTH || token == NGHTTP2_TOKEN_ETAG || + token == NGHTTP2_TOKEN_IF_MODIFIED_SINCE || + token == NGHTTP2_TOKEN_IF_NONE_MATCH || token == NGHTTP2_TOKEN_LOCATION || + token == NGHTTP2_TOKEN_SET_COOKIE || entry_room(nv->namelen, nv->valuelen) > deflater->ctx.hd_table_bufsize_max * 3 / 4) { - return 0; + return NGHTTP2_HD_WITHOUT_INDEXING; } -#ifdef NGHTTP2_XHD - return !name_match(nv, NGHTTP2_XHD); -#else /* !NGHTTP2_XHD */ - return !name_match(nv, ":path") && !name_match(nv, "content-length") && - !name_match(nv, "set-cookie") && !name_match(nv, "etag") && - !name_match(nv, "if-modified-since") && - !name_match(nv, "if-none-match") && !name_match(nv, "location") && - !name_match(nv, "age"); -#endif /* !NGHTTP2_XHD */ + + return NGHTTP2_HD_WITH_INDEXING; } static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, @@ -970,20 +1308,28 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, int rv; search_result res; ssize_t idx; - int incidx = 0; - uint32_t name_hash = hash(nv->name, nv->namelen); - uint32_t value_hash = hash(nv->value, nv->valuelen); + int indexing_mode; + int token; nghttp2_mem *mem; - DEBUGF(fprintf(stderr, "deflatehd: deflating ")); - DEBUGF(fwrite(nv->name, nv->namelen, 1, stderr)); - DEBUGF(fprintf(stderr, ": ")); - DEBUGF(fwrite(nv->value, nv->valuelen, 1, stderr)); - DEBUGF(fprintf(stderr, "\n")); + DEBUGF(fprintf(stderr, "deflatehd: deflating %s: %s\n", nv->name, nv->value)); mem = deflater->ctx.mem; - res = search_hd_table(&deflater->ctx, nv, name_hash, value_hash); + token = lookup_token(nv->name, nv->namelen); + + /* Don't index authorization header field since it may contain low + entropy secret data (e.g., id/password). Also cookie header + field with less than 20 bytes value is also never indexed. This + is the same criteria used in Firefox codebase. */ + indexing_mode = + token == NGHTTP2_TOKEN_AUTHORIZATION || + (token == NGHTTP2_TOKEN_COOKIE && nv->valuelen < 20) || + (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) + ? NGHTTP2_HD_NEVER_INDEXING + : hd_deflate_decide_indexing(deflater, nv, token); + + res = search_hd_table(&deflater->ctx, nv, token, indexing_mode); idx = res.index; @@ -1003,19 +1349,18 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, DEBUGF(fprintf(stderr, "deflatehd: name match index=%zd\n", res.index)); } - if (hd_deflate_should_indexing(deflater, nv)) { + if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) { nghttp2_hd_entry *new_ent; if (idx != -1 && idx < (ssize_t)NGHTTP2_STATIC_TABLE_LENGTH) { nghttp2_nv nv_indname; nv_indname = *nv; nv_indname.name = nghttp2_hd_table_get(&deflater->ctx, idx)->nv.name; - new_ent = - add_hd_table_incremental(&deflater->ctx, &nv_indname, name_hash, - value_hash, NGHTTP2_HD_FLAG_VALUE_ALLOC); + new_ent = add_hd_table_incremental(&deflater->ctx, &nv_indname, token, + NGHTTP2_HD_FLAG_VALUE_ALLOC); } else { - new_ent = add_hd_table_incremental( - &deflater->ctx, nv, name_hash, value_hash, - NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_VALUE_ALLOC); + new_ent = add_hd_table_incremental(&deflater->ctx, nv, token, + NGHTTP2_HD_FLAG_NAME_ALLOC | + NGHTTP2_HD_FLAG_VALUE_ALLOC); } if (!new_ent) { return NGHTTP2_ERR_HEADER_COMP; @@ -1024,12 +1369,11 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, nghttp2_hd_entry_free(new_ent, mem); nghttp2_mem_free(mem, new_ent); } - incidx = 1; } if (idx == -1) { - rv = emit_newname_block(bufs, nv, incidx); + rv = emit_newname_block(bufs, nv, indexing_mode); } else { - rv = emit_indname_block(bufs, idx, nv, incidx); + rv = emit_indname_block(bufs, idx, nv, indexing_mode); } if (rv != 0) { return rv; @@ -1313,10 +1657,10 @@ static ssize_t hd_inflate_read(nghttp2_hd_inflater *inflater, * Out of memory */ static int hd_inflate_commit_indexed(nghttp2_hd_inflater *inflater, - nghttp2_nv *nv_out) { + nghttp2_nv *nv_out, int *token_out) { nghttp2_hd_entry *ent = nghttp2_hd_table_get(&inflater->ctx, inflater->index); - emit_indexed_header(nv_out, ent); + emit_indexed_header(nv_out, token_out, ent); return 0; } @@ -1337,18 +1681,24 @@ static int hd_inflate_remove_bufs(nghttp2_hd_inflater *inflater, nghttp2_nv *nv, return NGHTTP2_ERR_NOMEM; } + nghttp2_bufs_reset(&inflater->nvbufs); + buflen = rv; if (value_only) { + /* we don't use this value, so no need to NULL-terminate */ nv->name = NULL; nv->namelen = 0; + + nv->value = buf; + nv->valuelen = buflen - 1; } else { nv->name = buf; nv->namelen = inflater->newnamelen; - } - nv->value = buf + nv->namelen; - nv->valuelen = buflen - nv->namelen; + nv->value = buf + nv->namelen + 1; + nv->valuelen = buflen - nv->namelen - 2; + } return 0; } @@ -1360,15 +1710,19 @@ static int hd_inflate_remove_bufs(nghttp2_hd_inflater *inflater, nghttp2_nv *nv, pbuf = &inflater->nvbufs.head->buf; if (value_only) { + /* we don't use this value, so no need to NULL-terminate */ nv->name = NULL; nv->namelen = 0; + + nv->value = pbuf->pos; + nv->valuelen = nghttp2_buf_len(pbuf) - 1; } else { nv->name = pbuf->pos; nv->namelen = inflater->newnamelen; - } - nv->value = pbuf->pos + nv->namelen; - nv->valuelen = nghttp2_buf_len(pbuf) - nv->namelen; + nv->value = pbuf->pos + nv->namelen + 1; + nv->valuelen = nghttp2_buf_len(pbuf) - nv->namelen - 2; + } /* Resetting does not change the content of first buffer */ nghttp2_bufs_reset(&inflater->nvbufs); @@ -1376,6 +1730,42 @@ static int hd_inflate_remove_bufs(nghttp2_hd_inflater *inflater, nghttp2_nv *nv, return 0; } +static int hd_inflate_remove_bufs_with_name(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv, + nghttp2_hd_entry *ent_name) { + size_t rv; + size_t buflen; + uint8_t *buf; + nghttp2_mem *mem; + + mem = inflater->ctx.mem; + + /* Allocate buffer including name in ent_name, plus terminating + NULL. */ + buflen = ent_name->nv.namelen + 1 + nghttp2_bufs_len(&inflater->nvbufs); + + buf = (uint8_t *)nghttp2_mem_malloc(mem, buflen); + if (buf == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + /* Copy including terminal NULL */ + memcpy(buf, ent_name->nv.name, ent_name->nv.namelen + 1); + rv = nghttp2_bufs_remove_copy(&inflater->nvbufs, + buf + ent_name->nv.namelen + 1); + assert(ent_name->nv.namelen + 1 + rv == buflen); + + nghttp2_bufs_reset(&inflater->nvbufs); + + nv->name = buf; + nv->namelen = ent_name->nv.namelen; + + nv->value = buf + nv->namelen + 1; + nv->valuelen = buflen - nv->namelen - 2; + + return 0; +} + /* * Finalize literal header representation - new name- reception. If * header is emitted, |*nv_out| is filled with that value and 0 is @@ -1388,7 +1778,7 @@ static int hd_inflate_remove_bufs(nghttp2_hd_inflater *inflater, nghttp2_nv *nv, * Out of memory */ static int hd_inflate_commit_newname(nghttp2_hd_inflater *inflater, - nghttp2_nv *nv_out) { + nghttp2_nv *nv_out, int *token_out) { int rv; nghttp2_nv nv; nghttp2_mem *mem; @@ -1415,12 +1805,11 @@ static int hd_inflate_commit_newname(nghttp2_hd_inflater *inflater, management. */ ent_flags = NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_NAME_GIFT; - new_ent = - add_hd_table_incremental(&inflater->ctx, &nv, hash(nv.name, nv.namelen), - hash(nv.value, nv.valuelen), ent_flags); + new_ent = add_hd_table_incremental( + &inflater->ctx, &nv, lookup_token(nv.name, nv.namelen), ent_flags); if (new_ent) { - emit_indexed_header(nv_out, new_ent); + emit_indexed_header(nv_out, token_out, new_ent); inflater->ent_keep = new_ent; return 0; @@ -1431,7 +1820,7 @@ static int hd_inflate_commit_newname(nghttp2_hd_inflater *inflater, return NGHTTP2_ERR_NOMEM; } - emit_literal_header(nv_out, &nv); + emit_literal_header(nv_out, token_out, &nv); if (nv.name != inflater->nvbufs.head->buf.pos) { inflater->nv_keep = nv.name; @@ -1452,7 +1841,7 @@ static int hd_inflate_commit_newname(nghttp2_hd_inflater *inflater, * Out of memory */ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater, - nghttp2_nv *nv_out) { + nghttp2_nv *nv_out, int *token_out) { int rv; nghttp2_nv nv; nghttp2_hd_entry *ent_name; @@ -1460,11 +1849,6 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater, mem = inflater->ctx.mem; - rv = hd_inflate_remove_bufs(inflater, &nv, 1 /* value only */); - if (rv != 0) { - return NGHTTP2_ERR_NOMEM; - } - if (inflater->no_index) { nv.flags = NGHTTP2_NV_FLAG_NO_INDEX; } else { @@ -1473,34 +1857,36 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater, ent_name = nghttp2_hd_table_get(&inflater->ctx, inflater->index); - nv.name = ent_name->nv.name; - nv.namelen = ent_name->nv.namelen; - if (inflater->index_required) { nghttp2_hd_entry *new_ent; uint8_t ent_flags; - int static_name; - ent_flags = NGHTTP2_HD_FLAG_VALUE_ALLOC | NGHTTP2_HD_FLAG_VALUE_GIFT; - static_name = inflater->index < NGHTTP2_STATIC_TABLE_LENGTH; + if (inflater->index < NGHTTP2_STATIC_TABLE_LENGTH) { + /* We don't copy name in static table */ + rv = hd_inflate_remove_bufs(inflater, &nv, 1 /* value only */); + if (rv != 0) { + return NGHTTP2_ERR_NOMEM; + } + nv.name = ent_name->nv.name; + nv.namelen = ent_name->nv.namelen; - if (!static_name) { - ent_flags |= NGHTTP2_HD_FLAG_NAME_ALLOC; - /* For entry in static table, we must not touch ref, because it - is shared by threads */ - ++ent_name->ref; + ent_flags = NGHTTP2_HD_FLAG_VALUE_ALLOC | NGHTTP2_HD_FLAG_VALUE_GIFT; + } else { + rv = hd_inflate_remove_bufs_with_name(inflater, &nv, ent_name); + if (rv != 0) { + return NGHTTP2_ERR_NOMEM; + } + /* nv->name and nv->value are in the same buffer. */ + ent_flags = NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_NAME_GIFT; } - new_ent = add_hd_table_incremental(&inflater->ctx, &nv, ent_name->name_hash, - hash(nv.value, nv.valuelen), ent_flags); + new_ent = add_hd_table_incremental(&inflater->ctx, &nv, ent_name->token, + ent_flags); - if (!static_name && --ent_name->ref == 0) { - nghttp2_hd_entry_free(ent_name, mem); - nghttp2_mem_free(mem, ent_name); - } + /* At this point, ent_name might be deleted. */ if (new_ent) { - emit_indexed_header(nv_out, new_ent); + emit_indexed_header(nv_out, token_out, new_ent); inflater->ent_keep = new_ent; @@ -1512,7 +1898,15 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater, return NGHTTP2_ERR_NOMEM; } - emit_literal_header(nv_out, &nv); + rv = hd_inflate_remove_bufs(inflater, &nv, 1 /* value only */); + if (rv != 0) { + return NGHTTP2_ERR_NOMEM; + } + + nv.name = ent_name->nv.name; + nv.namelen = ent_name->nv.namelen; + + emit_literal_header(nv_out, token_out, &nv); if (nv.value != inflater->nvbufs.head->buf.pos) { inflater->nv_keep = nv.value; @@ -1524,10 +1918,21 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater, ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, int *inflate_flags, uint8_t *in, size_t inlen, int in_final) { + int token; + + return nghttp2_hd_inflate_hd2(inflater, nv_out, inflate_flags, &token, in, + inlen, in_final); +} + +ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, int *inflate_flags, + int *token_out, uint8_t *in, size_t inlen, + int in_final) { ssize_t rv = 0; uint8_t *first = in; uint8_t *last = in + inlen; int rfin = 0; + int busy = 0; if (inflater->ctx.bad) { return NGHTTP2_ERR_HEADER_COMP; @@ -1535,8 +1940,10 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, DEBUGF(fprintf(stderr, "inflatehd: start state=%d\n", inflater->state)); hd_inflate_keep_free(inflater); + *token_out = -1; *inflate_flags = NGHTTP2_HD_INFLATE_NONE; - for (; in != last;) { + for (; in != last || busy;) { + busy = 0; switch (inflater->state) { case NGHTTP2_HD_STATE_OPCODE: if ((*in & 0xe0u) == 0x20u) { @@ -1620,7 +2027,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, inflater->index = inflater->left; --inflater->index; - rv = hd_inflate_commit_indexed(inflater, nv_out); + rv = hd_inflate_commit_indexed(inflater, nv_out, token_out); if (rv < 0) { goto fail; } @@ -1688,6 +2095,11 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, inflater->newnamelen = nghttp2_bufs_len(&inflater->nvbufs); + rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0'); + if (rv != 0) { + goto fail; + } + inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; break; @@ -1709,6 +2121,11 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, inflater->newnamelen = nghttp2_bufs_len(&inflater->nvbufs); + rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0'); + if (rv != 0) { + goto fail; + } + inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; break; @@ -1734,19 +2151,6 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, } DEBUGF(fprintf(stderr, "inflatehd: valuelen=%zu\n", inflater->left)); - if (inflater->left == 0) { - if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { - rv = hd_inflate_commit_newname(inflater, nv_out); - } else { - rv = hd_inflate_commit_indname(inflater, nv_out); - } - if (rv != 0) { - goto fail; - } - inflater->state = NGHTTP2_HD_STATE_OPCODE; - *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; - return (ssize_t)(in - first); - } if (inflater->huffman_encoded) { nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx); @@ -1755,6 +2159,9 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, } else { inflater->state = NGHTTP2_HD_STATE_READ_VALUE; } + + busy = 1; + break; case NGHTTP2_HD_STATE_READ_VALUEHUFF: rv = hd_inflate_read_huff(inflater, &inflater->nvbufs, in, last); @@ -1773,10 +2180,15 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, goto almost_ok; } + rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0'); + if (rv != 0) { + goto fail; + } + if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { - rv = hd_inflate_commit_newname(inflater, nv_out); + rv = hd_inflate_commit_newname(inflater, nv_out, token_out); } else { - rv = hd_inflate_commit_indname(inflater, nv_out); + rv = hd_inflate_commit_indname(inflater, nv_out, token_out); } if (rv != 0) { @@ -1805,10 +2217,15 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, goto almost_ok; } + rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0'); + if (rv != 0) { + goto fail; + } + if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { - rv = hd_inflate_commit_newname(inflater, nv_out); + rv = hd_inflate_commit_newname(inflater, nv_out, token_out); } else { - rv = hd_inflate_commit_indname(inflater, nv_out); + rv = hd_inflate_commit_indname(inflater, nv_out, token_out); } if (rv != 0) { @@ -1904,14 +2321,14 @@ void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater) { } int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t idx, - nghttp2_nv *nv, int inc_indexing) { + nghttp2_nv *nv, int indexing_mode) { - return emit_indname_block(bufs, idx, nv, inc_indexing); + return emit_indname_block(bufs, idx, nv, indexing_mode); } int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv, - int inc_indexing) { - return emit_newname_block(bufs, nv, inc_indexing); + int indexing_mode) { + return emit_newname_block(bufs, nv, indexing_mode); } int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size) { diff --git a/epan/nghttp2/nghttp2_hd.h b/epan/nghttp2/nghttp2_hd.h index 1ed42c2ada..204d36ae98 100644 --- a/epan/nghttp2/nghttp2_hd.h +++ b/epan/nghttp2/nghttp2_hd.h @@ -49,7 +49,68 @@ #define NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE (1 << 12) /* Exported for unit test */ -extern const size_t NGHTTP2_STATIC_TABLE_LENGTH; +#define NGHTTP2_STATIC_TABLE_LENGTH 61 + +/* Generated by genlibtokenlookup.py */ +typedef enum { + NGHTTP2_TOKEN__AUTHORITY = 0, + NGHTTP2_TOKEN__METHOD = 1, + NGHTTP2_TOKEN__PATH = 3, + NGHTTP2_TOKEN__SCHEME = 5, + NGHTTP2_TOKEN__STATUS = 7, + NGHTTP2_TOKEN_ACCEPT_CHARSET = 14, + NGHTTP2_TOKEN_ACCEPT_ENCODING = 15, + NGHTTP2_TOKEN_ACCEPT_LANGUAGE = 16, + NGHTTP2_TOKEN_ACCEPT_RANGES = 17, + NGHTTP2_TOKEN_ACCEPT = 18, + NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN = 19, + NGHTTP2_TOKEN_AGE = 20, + NGHTTP2_TOKEN_ALLOW = 21, + NGHTTP2_TOKEN_AUTHORIZATION = 22, + NGHTTP2_TOKEN_CACHE_CONTROL = 23, + NGHTTP2_TOKEN_CONTENT_DISPOSITION = 24, + NGHTTP2_TOKEN_CONTENT_ENCODING = 25, + NGHTTP2_TOKEN_CONTENT_LANGUAGE = 26, + NGHTTP2_TOKEN_CONTENT_LENGTH = 27, + NGHTTP2_TOKEN_CONTENT_LOCATION = 28, + NGHTTP2_TOKEN_CONTENT_RANGE = 29, + NGHTTP2_TOKEN_CONTENT_TYPE = 30, + NGHTTP2_TOKEN_COOKIE = 31, + NGHTTP2_TOKEN_DATE = 32, + NGHTTP2_TOKEN_ETAG = 33, + NGHTTP2_TOKEN_EXPECT = 34, + NGHTTP2_TOKEN_EXPIRES = 35, + NGHTTP2_TOKEN_FROM = 36, + NGHTTP2_TOKEN_HOST = 37, + NGHTTP2_TOKEN_IF_MATCH = 38, + NGHTTP2_TOKEN_IF_MODIFIED_SINCE = 39, + NGHTTP2_TOKEN_IF_NONE_MATCH = 40, + NGHTTP2_TOKEN_IF_RANGE = 41, + NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE = 42, + NGHTTP2_TOKEN_LAST_MODIFIED = 43, + NGHTTP2_TOKEN_LINK = 44, + NGHTTP2_TOKEN_LOCATION = 45, + NGHTTP2_TOKEN_MAX_FORWARDS = 46, + NGHTTP2_TOKEN_PROXY_AUTHENTICATE = 47, + NGHTTP2_TOKEN_PROXY_AUTHORIZATION = 48, + NGHTTP2_TOKEN_RANGE = 49, + NGHTTP2_TOKEN_REFERER = 50, + NGHTTP2_TOKEN_REFRESH = 51, + NGHTTP2_TOKEN_RETRY_AFTER = 52, + NGHTTP2_TOKEN_SERVER = 53, + NGHTTP2_TOKEN_SET_COOKIE = 54, + NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY = 55, + NGHTTP2_TOKEN_TRANSFER_ENCODING = 56, + NGHTTP2_TOKEN_USER_AGENT = 57, + NGHTTP2_TOKEN_VARY = 58, + NGHTTP2_TOKEN_VIA = 59, + NGHTTP2_TOKEN_WWW_AUTHENTICATE = 60, + NGHTTP2_TOKEN_TE, + NGHTTP2_TOKEN_CONNECTION, + NGHTTP2_TOKEN_KEEP_ALIVE, + NGHTTP2_TOKEN_PROXY_CONNECTION, + NGHTTP2_TOKEN_UPGRADE, +} nghttp2_token; typedef enum { NGHTTP2_HD_FLAG_NONE = 0, @@ -67,18 +128,14 @@ typedef enum { typedef struct { nghttp2_nv nv; - uint32_t name_hash; - uint32_t value_hash; + /* nghttp2_token value for nv.name. It could be -1 if we have no + token for that header field name. */ + int token; /* Reference count */ uint8_t ref; uint8_t flags; } nghttp2_hd_entry; -typedef struct { - nghttp2_hd_entry ent; - size_t index; -} nghttp2_hd_static_entry; - typedef struct { nghttp2_hd_entry **buffer; size_t mask; @@ -107,6 +164,12 @@ typedef enum { NGHTTP2_HD_STATE_READ_VALUE } nghttp2_hd_inflate_state; +typedef enum { + NGHTTP2_HD_WITH_INDEXING, + NGHTTP2_HD_WITHOUT_INDEXING, + NGHTTP2_HD_NEVER_INDEXING +} nghttp2_hd_indexing_mode; + typedef struct { /* dynamic header table */ nghttp2_hd_ringbuf hd_table; @@ -176,9 +239,8 @@ struct nghttp2_hd_inflater { * set in the |flags|, the content pointed by the |name| with length * |namelen| is copied. Likewise, if NGHTTP2_HD_FLAG_VALUE_ALLOC bit * set in the |flags|, the content pointed by the |value| with length - * |valuelen| is copied. The |name_hash| and |value_hash| are hash - * value for |name| and |value| respectively. The hash function is - * defined in nghttp2_hd.c. + * |valuelen| is copied. The |token| is enum number looked up by + * |name|. It could be -1 if we don't have that enum value. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -188,8 +250,7 @@ struct nghttp2_hd_inflater { */ int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name, size_t namelen, uint8_t *value, size_t valuelen, - uint32_t name_hash, uint32_t value_hash, - nghttp2_mem *mem); + int token, nghttp2_mem *mem); void nghttp2_hd_entry_free(nghttp2_hd_entry *ent, nghttp2_mem *mem); @@ -271,13 +332,25 @@ int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem); */ void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater); +/* + * Similar to nghttp2_hd_inflate_hd(), but this takes additional + * output parameter |token|. On successful header emission, it + * contains nghttp2_token value for nv_out->name. It could be -1 if + * we don't have enum value for the name. Other than that return + * values and semantics are the same as nghttp2_hd_inflate_hd(). + */ +ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, int *inflate_flags, + int *token, uint8_t *in, size_t inlen, + int in_final); + /* For unittesting purpose */ int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t index, - nghttp2_nv *nv, int inc_indexing); + nghttp2_nv *nv, int indexing_mode); /* For unittesting purpose */ int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv, - int inc_indexing); + int indexing_mode); /* For unittesting purpose */ int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size); @@ -324,8 +397,7 @@ void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx); * be initialized by nghttp2_hd_huff_decode_context_init(). The result * will be added to |dest|. This function may expand |dest| as * needed. The caller is responsible to release the memory of |dest| - * by calling nghttp2_bufs_free() or export its content using - * nghttp2_bufs_remove(). + * by calling nghttp2_bufs_free(). * * The caller must set the |final| to nonzero if the given input is * the final block. diff --git a/epan/nghttp2/nghttp2_hd_huffman.c b/epan/nghttp2/nghttp2_hd_huffman.c index 6c4b2b64ce..4df1cd0425 100644 --- a/epan/nghttp2/nghttp2_hd_huffman.c +++ b/epan/nghttp2/nghttp2_hd_huffman.c @@ -168,10 +168,27 @@ void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) { ctx->accept = 1; } +/* Use macro to make the code simpler..., but error case is tricky. + We spent most of the CPU in decoding, so we are doing this + thing. */ +#define hd_huff_decode_sym_emit(bufs, sym, avail) \ + do { \ + if ((avail)) { \ + nghttp2_bufs_fast_addb((bufs), (sym)); \ + --(avail); \ + } else { \ + rv = nghttp2_bufs_addb((bufs), (sym)); \ + if (rv != 0) { \ + return rv; \ + } \ + (avail) = nghttp2_bufs_cur_avail((bufs)); \ + } \ + } while (0) + ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, nghttp2_bufs *bufs, const uint8_t *src, size_t srclen, int final) { - size_t i, j; + size_t i; int rv; size_t avail; @@ -180,30 +197,28 @@ ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, /* We use the decoding algorithm described in http://graphics.ics.uci.edu/pub/Prefix.pdf */ for (i = 0; i < srclen; ++i) { - uint8_t in = src[i] >> 4; - for (j = 0; j < 2; ++j) { - const nghttp2_huff_decode *t; + const nghttp2_huff_decode *t; - t = &huff_decode_table[ctx->state][in]; - if (t->flags & NGHTTP2_HUFF_FAIL) { - return NGHTTP2_ERR_HEADER_COMP; - } - if (t->flags & NGHTTP2_HUFF_SYM) { - if (avail) { - nghttp2_bufs_fast_addb(bufs, t->sym); - --avail; - } else { - rv = nghttp2_bufs_addb(bufs, t->sym); - if (rv != 0) { - return rv; - } - avail = nghttp2_bufs_cur_avail(bufs); - } - } - ctx->state = t->state; - ctx->accept = (t->flags & NGHTTP2_HUFF_ACCEPTED) != 0; - in = src[i] & 0xf; + t = &huff_decode_table[ctx->state][src[i] >> 4]; + if (t->flags & NGHTTP2_HUFF_FAIL) { + return NGHTTP2_ERR_HEADER_COMP; } + if (t->flags & NGHTTP2_HUFF_SYM) { + /* this is macro, and may return from this function on error */ + hd_huff_decode_sym_emit(bufs, t->sym, avail); + } + + t = &huff_decode_table[t->state][src[i] & 0xf]; + if (t->flags & NGHTTP2_HUFF_FAIL) { + return NGHTTP2_ERR_HEADER_COMP; + } + if (t->flags & NGHTTP2_HUFF_SYM) { + /* this is macro, and may return from this function on error */ + hd_huff_decode_sym_emit(bufs, t->sym, avail); + } + + ctx->state = t->state; + ctx->accept = (t->flags & NGHTTP2_HUFF_ACCEPTED) != 0; } if (final && !ctx->accept) { return NGHTTP2_ERR_HEADER_COMP; diff --git a/epan/nghttp2/nghttp2_helper.h b/epan/nghttp2/nghttp2_helper.h index 0facb1ce41..19595b0cf8 100644 --- a/epan/nghttp2/nghttp2_helper.h +++ b/epan/nghttp2/nghttp2_helper.h @@ -27,12 +27,16 @@ #include +#include + #include #include "nghttp2_mem.h" #define nghttp2_min(A, B) ((A) < (B) ? (A) : (B)) #define nghttp2_max(A, B) ((A) > (B) ? (A) : (B)) +#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0) + /* * Copies 2 byte unsigned integer |n| in host byte order to |buf| in * network byte order. diff --git a/epan/nghttp2/nghttp2_int.h b/epan/nghttp2/nghttp2_int.h index 3d77eaab58..c26c8e99a1 100644 --- a/epan/nghttp2/nghttp2_int.h +++ b/epan/nghttp2/nghttp2_int.h @@ -39,7 +39,8 @@ } while (0) #endif -typedef int (*nghttp2_compar)(const void *lhs, const void *rhs); +/* "less" function, return nonzero if |lhs| is less than |rhs|. */ +typedef int (*nghttp2_less)(const void *lhs, const void *rhs); /* Internal error code. They must be in the range [-499, -100], inclusive. */ @@ -51,7 +52,7 @@ typedef enum { * Invalid HTTP header field was received but it can be treated as * if it was not received because of compatibility reasons. */ - NGHTTP2_ERR_IGN_HTTP_HEADER = -105, + NGHTTP2_ERR_IGN_HTTP_HEADER = -105 } nghttp2_internal_error; #endif /* NGHTTP2_INT_H */ diff --git a/epan/nghttp2/nghttp2_net.h b/epan/nghttp2/nghttp2_net.h index 3f82adf721..16b52b3263 100644 --- a/epan/nghttp2/nghttp2_net.h +++ b/epan/nghttp2/nghttp2_net.h @@ -37,8 +37,55 @@ #include #endif /* HAVE_NETINET_IN_H */ -#ifdef HAVE_WINSOCK2_H -#include -#endif /* HAVE_WINSOCK2_H */ +#include + +#if defined(WIN32) +/* Windows requires ws2_32 library for ntonl family functions. We + define inline functions for those function so that we don't have + dependeny on that lib. */ + +#ifdef _MSC_VER +#define STIN static __inline +#else +#define STIN static inline +#endif + +STIN uint32_t htonl(uint32_t hostlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = hostlong >> 24; + *p++ = (hostlong >> 16) & 0xffu; + *p++ = (hostlong >> 8) & 0xffu; + *p = hostlong & 0xffu; + return res; +} + +STIN uint16_t htons(uint16_t hostshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = hostshort >> 8; + *p = hostshort & 0xffu; + return res; +} + +STIN uint32_t ntohl(uint32_t netlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&netlong; + res = *p++ << 24; + res += *p++ << 16; + res += *p++ << 8; + res += *p; + return res; +} + +STIN uint16_t ntohs(uint16_t netshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&netshort; + res = *p++ << 8; + res += *p; + return res; +} + +#endif /* WIN32 */ #endif /* NGHTTP2_NET_H */ diff --git a/epan/nghttp2/nghttp2ver.h b/epan/nghttp2/nghttp2ver.h index 51134a1b4d..8e67b275de 100644 --- a/epan/nghttp2/nghttp2ver.h +++ b/epan/nghttp2/nghttp2ver.h @@ -29,7 +29,7 @@ * @macro * Version number of the nghttp2 library release */ -#define NGHTTP2_VERSION "0.7.5" +#define NGHTTP2_VERSION "0.7.13" /** * @macro @@ -37,6 +37,6 @@ * release. This is a 24 bit number with 8 bits for major number, 8 bits * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203. */ -#define NGHTTP2_VERSION_NUM 0x000705 +#define NGHTTP2_VERSION_NUM 0x00070d #endif /* NGHTTP2VER_H */ -- cgit v1.2.3