Merge branch 'simple-extensions'
This commit is contained in:
commit
ba34e911e1
|
@ -58,6 +58,7 @@ APIDOCS= \
|
||||||
nghttp2_option_set_no_http_messaging.rst \
|
nghttp2_option_set_no_http_messaging.rst \
|
||||||
nghttp2_option_set_no_recv_client_magic.rst \
|
nghttp2_option_set_no_recv_client_magic.rst \
|
||||||
nghttp2_option_set_peer_max_concurrent_streams.rst \
|
nghttp2_option_set_peer_max_concurrent_streams.rst \
|
||||||
|
nghttp2_option_set_user_recv_extension_type.rst \
|
||||||
nghttp2_pack_settings_payload.rst \
|
nghttp2_pack_settings_payload.rst \
|
||||||
nghttp2_priority_spec_check_default.rst \
|
nghttp2_priority_spec_check_default.rst \
|
||||||
nghttp2_priority_spec_default_init.rst \
|
nghttp2_priority_spec_default_init.rst \
|
||||||
|
@ -70,16 +71,19 @@ APIDOCS= \
|
||||||
nghttp2_session_callbacks_set_on_begin_frame_callback.rst \
|
nghttp2_session_callbacks_set_on_begin_frame_callback.rst \
|
||||||
nghttp2_session_callbacks_set_on_begin_headers_callback.rst \
|
nghttp2_session_callbacks_set_on_begin_headers_callback.rst \
|
||||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst \
|
nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst \
|
||||||
|
nghttp2_session_callbacks_set_on_extension_chunk_recv_callback.rst \
|
||||||
nghttp2_session_callbacks_set_on_frame_not_send_callback.rst \
|
nghttp2_session_callbacks_set_on_frame_not_send_callback.rst \
|
||||||
nghttp2_session_callbacks_set_on_frame_recv_callback.rst \
|
nghttp2_session_callbacks_set_on_frame_recv_callback.rst \
|
||||||
nghttp2_session_callbacks_set_on_frame_send_callback.rst \
|
nghttp2_session_callbacks_set_on_frame_send_callback.rst \
|
||||||
nghttp2_session_callbacks_set_on_header_callback.rst \
|
nghttp2_session_callbacks_set_on_header_callback.rst \
|
||||||
nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst \
|
nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst \
|
||||||
nghttp2_session_callbacks_set_on_stream_close_callback.rst \
|
nghttp2_session_callbacks_set_on_stream_close_callback.rst \
|
||||||
|
nghttp2_session_callbacks_set_pack_extension_callback.rst \
|
||||||
nghttp2_session_callbacks_set_recv_callback.rst \
|
nghttp2_session_callbacks_set_recv_callback.rst \
|
||||||
nghttp2_session_callbacks_set_select_padding_callback.rst \
|
nghttp2_session_callbacks_set_select_padding_callback.rst \
|
||||||
nghttp2_session_callbacks_set_send_callback.rst \
|
nghttp2_session_callbacks_set_send_callback.rst \
|
||||||
nghttp2_session_callbacks_set_send_data_callback.rst \
|
nghttp2_session_callbacks_set_send_data_callback.rst \
|
||||||
|
nghttp2_session_callbacks_set_unpack_extension_callback.rst \
|
||||||
nghttp2_session_client_new.rst \
|
nghttp2_session_client_new.rst \
|
||||||
nghttp2_session_client_new2.rst \
|
nghttp2_session_client_new2.rst \
|
||||||
nghttp2_session_client_new3.rst \
|
nghttp2_session_client_new3.rst \
|
||||||
|
@ -131,6 +135,7 @@ APIDOCS= \
|
||||||
nghttp2_stream_get_weight.rst \
|
nghttp2_stream_get_weight.rst \
|
||||||
nghttp2_strerror.rst \
|
nghttp2_strerror.rst \
|
||||||
nghttp2_submit_data.rst \
|
nghttp2_submit_data.rst \
|
||||||
|
nghttp2_submit_extension.rst \
|
||||||
nghttp2_submit_goaway.rst \
|
nghttp2_submit_goaway.rst \
|
||||||
nghttp2_submit_headers.rst \
|
nghttp2_submit_headers.rst \
|
||||||
nghttp2_submit_ping.rst \
|
nghttp2_submit_ping.rst \
|
||||||
|
|
|
@ -382,6 +382,10 @@ typedef enum {
|
||||||
* Unexpected internal error, but recovered.
|
* Unexpected internal error, but recovered.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_ERR_INTERNAL = -534,
|
NGHTTP2_ERR_INTERNAL = -534,
|
||||||
|
/**
|
||||||
|
* Indicates that a processing was canceled.
|
||||||
|
*/
|
||||||
|
NGHTTP2_ERR_CANCEL = -535,
|
||||||
/**
|
/**
|
||||||
* The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is
|
* The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is
|
||||||
* under unexpected condition and processing was terminated (e.g.,
|
* under unexpected condition and processing was terminated (e.g.,
|
||||||
|
@ -1700,6 +1704,99 @@ typedef int (*nghttp2_on_begin_frame_callback)(nghttp2_session *session,
|
||||||
const nghttp2_frame_hd *hd,
|
const nghttp2_frame_hd *hd,
|
||||||
void *user_data);
|
void *user_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @functypedef
|
||||||
|
*
|
||||||
|
* Callback function invoked when chunk of extension frame payload is
|
||||||
|
* received. The |hd| points to frame header. The received
|
||||||
|
* chunk is |data| of length |len|.
|
||||||
|
*
|
||||||
|
* The implementation of this function must return 0 if it succeeds.
|
||||||
|
*
|
||||||
|
* To abort processing this extension frame, return
|
||||||
|
* :enum:`NGHTTP2_ERR_CANCEL`.
|
||||||
|
*
|
||||||
|
* If fatal error occurred, application should return
|
||||||
|
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. In this case,
|
||||||
|
* `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions
|
||||||
|
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If the
|
||||||
|
* other values are returned, currently they are treated as
|
||||||
|
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||||
|
*/
|
||||||
|
typedef int (*nghttp2_on_extension_chunk_recv_callback)(
|
||||||
|
nghttp2_session *session, const nghttp2_frame_hd *hd, const uint8_t *data,
|
||||||
|
size_t len, void *user_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @functypedef
|
||||||
|
*
|
||||||
|
* Callback function invoked when library asks the application to
|
||||||
|
* unpack extension payload from its wire format. The extension
|
||||||
|
* payload has been passed to the application using
|
||||||
|
* :type:`nghttp2_on_extension_chunk_recv_callback`. The frame header
|
||||||
|
* is already unpacked by the library and provided as |hd|.
|
||||||
|
*
|
||||||
|
* To receive extension frames, the application must tell desired
|
||||||
|
* extension frame type to the library using
|
||||||
|
* `nghttp2_option_set_user_recv_extension_type()`.
|
||||||
|
*
|
||||||
|
* The implementation of this function may store the pointer to the
|
||||||
|
* created object as a result of unpacking in |*payload|, and returns
|
||||||
|
* 0. The pointer stored in |*payload| is opaque to the library, and
|
||||||
|
* the library does not own its pointer. |*payload| is initialized as
|
||||||
|
* ``NULL``. The |*payload| is available as ``frame->ext.payload`` in
|
||||||
|
* :type:`nghttp2_on_frame_recv_callback`. Therefore if application
|
||||||
|
* can free that memory inside :type:`nghttp2_on_frame_recv_callback`
|
||||||
|
* callback. Of course, application has a liberty not ot use
|
||||||
|
* |*payload|, and do its own mechanism to process extension frames.
|
||||||
|
*
|
||||||
|
* To abort processing this extension frame, return
|
||||||
|
* :enum:`NGHTTP2_ERR_CANCEL`.
|
||||||
|
*
|
||||||
|
* If fatal error occurred, application should return
|
||||||
|
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. In this case,
|
||||||
|
* `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions
|
||||||
|
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If the
|
||||||
|
* other values are returned, currently they are treated as
|
||||||
|
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||||
|
*/
|
||||||
|
typedef int (*nghttp2_unpack_extension_callback)(nghttp2_session *session,
|
||||||
|
void **payload,
|
||||||
|
const nghttp2_frame_hd *hd,
|
||||||
|
void *user_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @functypedef
|
||||||
|
*
|
||||||
|
* Callback function invoked when library asks the application to pack
|
||||||
|
* extension payload in its wire format. The frame header will be
|
||||||
|
* packed by library. Application must pack payload only.
|
||||||
|
* ``frame->ext.payload`` is the object passed to
|
||||||
|
* `nghttp2_submit_extension()` as payload parameter. Application
|
||||||
|
* must pack extension payload to the |buf| of its capacity |len|
|
||||||
|
* bytes. The |len| is at least 16KiB.
|
||||||
|
*
|
||||||
|
* The implementation of this function should return the number of
|
||||||
|
* bytes written into |buf| when it succeeds.
|
||||||
|
*
|
||||||
|
* To abort processing this extension frame, return
|
||||||
|
* :enum:`NGHTTP2_ERR_CANCEL`, and
|
||||||
|
* :type:`nghttp2_on_frame_not_send_callback` will be invoked.
|
||||||
|
*
|
||||||
|
* If fatal error occurred, application should return
|
||||||
|
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. In this case,
|
||||||
|
* `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions
|
||||||
|
* immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If the
|
||||||
|
* other values are returned, currently they are treated as
|
||||||
|
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If the return value is
|
||||||
|
* strictly larger than |len|, it is treated as
|
||||||
|
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||||
|
*/
|
||||||
|
typedef ssize_t (*nghttp2_pack_extension_callback)(nghttp2_session *session,
|
||||||
|
uint8_t *buf, size_t len,
|
||||||
|
const nghttp2_frame *frame,
|
||||||
|
void *user_data);
|
||||||
|
|
||||||
struct nghttp2_session_callbacks;
|
struct nghttp2_session_callbacks;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1892,6 +1989,37 @@ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_data_callback(
|
||||||
nghttp2_session_callbacks *cbs,
|
nghttp2_session_callbacks *cbs,
|
||||||
nghttp2_send_data_callback send_data_callback);
|
nghttp2_send_data_callback send_data_callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @function
|
||||||
|
*
|
||||||
|
* Sets callback function invoked when the library asks the
|
||||||
|
* application to pack extension frame payload in wire format.
|
||||||
|
*/
|
||||||
|
NGHTTP2_EXTERN void nghttp2_session_callbacks_set_pack_extension_callback(
|
||||||
|
nghttp2_session_callbacks *cbs,
|
||||||
|
nghttp2_pack_extension_callback pack_extension_callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @function
|
||||||
|
*
|
||||||
|
* Sets callback function invoked when the library asks the
|
||||||
|
* application to unpack extension frame payload from wire format.
|
||||||
|
*/
|
||||||
|
NGHTTP2_EXTERN void nghttp2_session_callbacks_set_unpack_extension_callback(
|
||||||
|
nghttp2_session_callbacks *cbs,
|
||||||
|
nghttp2_unpack_extension_callback unpack_extension_callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @function
|
||||||
|
*
|
||||||
|
* Sets callback function invoked when chunk of extension frame
|
||||||
|
* payload is received.
|
||||||
|
*/
|
||||||
|
NGHTTP2_EXTERN void
|
||||||
|
nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(
|
||||||
|
nghttp2_session_callbacks *cbs,
|
||||||
|
nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @functypedef
|
* @functypedef
|
||||||
*
|
*
|
||||||
|
@ -2106,6 +2234,23 @@ NGHTTP2_EXTERN void
|
||||||
nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option,
|
nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option,
|
||||||
uint32_t val);
|
uint32_t val);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @function
|
||||||
|
*
|
||||||
|
* Sets extension frame type the application is willing to handle with
|
||||||
|
* user defined callbacks (see
|
||||||
|
* :type:`nghttp2_on_extension_chunk_recv_callback` and
|
||||||
|
* :type:`nghttp2_unpack_extension_callback`). The |type| is
|
||||||
|
* extension frame type, and must be strictly greater than 0x9.
|
||||||
|
* Otherwise, this function does nothing. The application can call
|
||||||
|
* this function multiple times to set more than one frame type to
|
||||||
|
* receive. The application does not have to call this function if it
|
||||||
|
* just sends extension frames.
|
||||||
|
*/
|
||||||
|
NGHTTP2_EXTERN void
|
||||||
|
nghttp2_option_set_user_recv_extension_type(nghttp2_option *option,
|
||||||
|
uint8_t type);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function
|
* @function
|
||||||
*
|
*
|
||||||
|
@ -3776,6 +3921,48 @@ NGHTTP2_EXTERN int nghttp2_submit_window_update(nghttp2_session *session,
|
||||||
int32_t stream_id,
|
int32_t stream_id,
|
||||||
int32_t window_size_increment);
|
int32_t window_size_increment);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @function
|
||||||
|
*
|
||||||
|
* Submits extension frame.
|
||||||
|
*
|
||||||
|
* Application can pass arbitrary frame flags and stream ID in |flags|
|
||||||
|
* and |stream_id| respectively. The |payload| is opaque pointer, and
|
||||||
|
* it can be accessible though ``frame->ext.payload`` in
|
||||||
|
* :type:`nghttp2_pack_extension_callback`. The library will not own
|
||||||
|
* passed |payload| pointer.
|
||||||
|
*
|
||||||
|
* The application must set :type:`nghttp2_pack_extension_callback`
|
||||||
|
* using `nghttp2_session_callbacks_set_pack_extension_callback()`.
|
||||||
|
*
|
||||||
|
* The application should retain the memory pointed by |payload| until
|
||||||
|
* the transmission of extension frame is done (which is indicated by
|
||||||
|
* :type:`nghttp2_on_frame_send_callback`), or transmission fails
|
||||||
|
* (which is indicated by :type:`nghttp2_on_frame_not_send_callback`).
|
||||||
|
* If application does not touch this memory region after packing it
|
||||||
|
* into a wire format, application can free it inside
|
||||||
|
* :type:`nghttp2_pack_extension_callback`.
|
||||||
|
*
|
||||||
|
* The standard HTTP/2 frame cannot be sent with this function, so
|
||||||
|
* |type| must be strictly grater than 0x9. Otherwise, this function
|
||||||
|
* will fail with error code :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`.
|
||||||
|
*
|
||||||
|
* This function returns 0 if it succeeds, or one of the following
|
||||||
|
* negative error codes:
|
||||||
|
*
|
||||||
|
* :enum:`NGHTTP2_ERR_INVALID_STATE`
|
||||||
|
* If :type:`nghttp2_pack_extension_callback` is not set.
|
||||||
|
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
|
||||||
|
* If |type| specifies standard HTTP/2 frame type. The frame
|
||||||
|
* types in the rage [0x0, 0x9], both inclusive, are standard
|
||||||
|
* HTTP/2 frame type, and cannot be sent using this function.
|
||||||
|
* :enum:`NGHTTP2_ERR_NOMEM`
|
||||||
|
* Out of memory
|
||||||
|
*/
|
||||||
|
NGHTTP2_EXTERN int nghttp2_submit_extension(nghttp2_session *session,
|
||||||
|
uint8_t type, uint8_t flags,
|
||||||
|
int32_t stream_id, void *payload);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function
|
* @function
|
||||||
*
|
*
|
||||||
|
|
|
@ -127,3 +127,21 @@ void nghttp2_session_callbacks_set_send_data_callback(
|
||||||
nghttp2_send_data_callback send_data_callback) {
|
nghttp2_send_data_callback send_data_callback) {
|
||||||
cbs->send_data_callback = send_data_callback;
|
cbs->send_data_callback = send_data_callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nghttp2_session_callbacks_set_pack_extension_callback(
|
||||||
|
nghttp2_session_callbacks *cbs,
|
||||||
|
nghttp2_pack_extension_callback pack_extension_callback) {
|
||||||
|
cbs->pack_extension_callback = pack_extension_callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nghttp2_session_callbacks_set_unpack_extension_callback(
|
||||||
|
nghttp2_session_callbacks *cbs,
|
||||||
|
nghttp2_unpack_extension_callback unpack_extension_callback) {
|
||||||
|
cbs->unpack_extension_callback = unpack_extension_callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(
|
||||||
|
nghttp2_session_callbacks *cbs,
|
||||||
|
nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback) {
|
||||||
|
cbs->on_extension_chunk_recv_callback = on_extension_chunk_recv_callback;
|
||||||
|
}
|
||||||
|
|
|
@ -107,6 +107,9 @@ struct nghttp2_session_callbacks {
|
||||||
*/
|
*/
|
||||||
nghttp2_on_begin_frame_callback on_begin_frame_callback;
|
nghttp2_on_begin_frame_callback on_begin_frame_callback;
|
||||||
nghttp2_send_data_callback send_data_callback;
|
nghttp2_send_data_callback send_data_callback;
|
||||||
|
nghttp2_pack_extension_callback pack_extension_callback;
|
||||||
|
nghttp2_unpack_extension_callback unpack_extension_callback;
|
||||||
|
nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* NGHTTP2_CALLBACKS_H */
|
#endif /* NGHTTP2_CALLBACKS_H */
|
||||||
|
|
|
@ -184,6 +184,17 @@ void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags,
|
||||||
|
|
||||||
void nghttp2_frame_data_free(nghttp2_data *frame _U_) {}
|
void nghttp2_frame_data_free(nghttp2_data *frame _U_) {}
|
||||||
|
|
||||||
|
void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type,
|
||||||
|
uint8_t flags, int32_t stream_id,
|
||||||
|
void *payload) {
|
||||||
|
nghttp2_frame_hd_init(&frame->hd, 0, type, flags, stream_id);
|
||||||
|
frame->payload = payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nghttp2_frame_extension_free(nghttp2_extension *frame _U_) {
|
||||||
|
/* should be noop for performance reason */
|
||||||
|
}
|
||||||
|
|
||||||
size_t nghttp2_frame_priority_len(uint8_t flags) {
|
size_t nghttp2_frame_priority_len(uint8_t flags) {
|
||||||
if (flags & NGHTTP2_FLAG_PRIORITY) {
|
if (flags & NGHTTP2_FLAG_PRIORITY) {
|
||||||
return NGHTTP2_PRIORITY_SPECLEN;
|
return NGHTTP2_PRIORITY_SPECLEN;
|
||||||
|
|
|
@ -439,6 +439,12 @@ void nghttp2_frame_window_update_init(nghttp2_window_update *frame,
|
||||||
|
|
||||||
void nghttp2_frame_window_update_free(nghttp2_window_update *frame);
|
void nghttp2_frame_window_update_free(nghttp2_window_update *frame);
|
||||||
|
|
||||||
|
void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type,
|
||||||
|
uint8_t flags, int32_t stream_id,
|
||||||
|
void *payload);
|
||||||
|
|
||||||
|
void nghttp2_frame_extension_free(nghttp2_extension *frame);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns the number of padding bytes after payload. The total
|
* Returns the number of padding bytes after payload. The total
|
||||||
* padding length is given in the |padlen|. The returned value does
|
* padding length is given in the |padlen|. The returned value does
|
||||||
|
|
|
@ -288,6 +288,8 @@ const char *nghttp2_strerror(int error_code) {
|
||||||
return "Stream was refused";
|
return "Stream was refused";
|
||||||
case NGHTTP2_ERR_INTERNAL:
|
case NGHTTP2_ERR_INTERNAL:
|
||||||
return "Internal error";
|
return "Internal error";
|
||||||
|
case NGHTTP2_ERR_CANCEL:
|
||||||
|
return "Cancel";
|
||||||
case NGHTTP2_ERR_NOMEM:
|
case NGHTTP2_ERR_NOMEM:
|
||||||
return "Out of memory";
|
return "Out of memory";
|
||||||
case NGHTTP2_ERR_CALLBACK_FAILURE:
|
case NGHTTP2_ERR_CALLBACK_FAILURE:
|
||||||
|
|
|
@ -62,3 +62,14 @@ void nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option,
|
||||||
option->opt_set_mask |= NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS;
|
option->opt_set_mask |= NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS;
|
||||||
option->max_reserved_remote_streams = val;
|
option->max_reserved_remote_streams = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nghttp2_option_set_user_recv_extension_type(nghttp2_option *option,
|
||||||
|
uint8_t type) {
|
||||||
|
if (type < 10) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
option->opt_set_mask |= NGHTTP2_OPT_USER_RECV_EXT_TYPES;
|
||||||
|
option->user_recv_ext_types[type / 8] =
|
||||||
|
(uint8_t)(option->user_recv_ext_types[type / 8] | (1 << (type & 0x7)));
|
||||||
|
}
|
||||||
|
|
|
@ -59,7 +59,8 @@ typedef enum {
|
||||||
NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1,
|
NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1,
|
||||||
NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC = 1 << 2,
|
NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC = 1 << 2,
|
||||||
NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3,
|
NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3,
|
||||||
NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS = 1 << 4
|
NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS = 1 << 4,
|
||||||
|
NGHTTP2_OPT_USER_RECV_EXT_TYPES = 1 << 5
|
||||||
} nghttp2_option_flag;
|
} nghttp2_option_flag;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -91,6 +92,10 @@ struct nghttp2_option {
|
||||||
* NGHTTP2_OPT_NO_HTTP_MESSAGING
|
* NGHTTP2_OPT_NO_HTTP_MESSAGING
|
||||||
*/
|
*/
|
||||||
int no_http_messaging;
|
int no_http_messaging;
|
||||||
|
/**
|
||||||
|
* NGHTTP2_OPT_USER_RECV_EXT_TYPES
|
||||||
|
*/
|
||||||
|
uint8_t user_recv_ext_types[32];
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* NGHTTP2_OPTION_H */
|
#endif /* NGHTTP2_OPTION_H */
|
||||||
|
|
|
@ -72,6 +72,9 @@ void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) {
|
||||||
case NGHTTP2_WINDOW_UPDATE:
|
case NGHTTP2_WINDOW_UPDATE:
|
||||||
nghttp2_frame_window_update_free(&frame->window_update);
|
nghttp2_frame_window_update_free(&frame->window_update);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
nghttp2_frame_extension_free(&frame->ext);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -405,6 +405,11 @@ static int session_new(nghttp2_session **session_ptr,
|
||||||
|
|
||||||
(*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_HTTP_MESSAGING;
|
(*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_HTTP_MESSAGING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (option->opt_set_mask & NGHTTP2_OPT_USER_RECV_EXT_TYPES) {
|
||||||
|
memcpy((*session_ptr)->user_recv_ext_types, option->user_recv_ext_types,
|
||||||
|
sizeof((*session_ptr)->user_recv_ext_types));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(*session_ptr)->callbacks = *callbacks;
|
(*session_ptr)->callbacks = *callbacks;
|
||||||
|
@ -1749,6 +1754,41 @@ static size_t session_estimate_headers_payload(nghttp2_session *session,
|
||||||
additional;
|
additional;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int session_pack_extension(nghttp2_session *session, nghttp2_bufs *bufs,
|
||||||
|
nghttp2_frame *frame) {
|
||||||
|
ssize_t rv;
|
||||||
|
nghttp2_buf *buf;
|
||||||
|
size_t buflen;
|
||||||
|
size_t framelen;
|
||||||
|
|
||||||
|
assert(session->callbacks.pack_extension_callback);
|
||||||
|
|
||||||
|
buf = &bufs->head->buf;
|
||||||
|
buflen = nghttp2_min(nghttp2_buf_avail(buf), NGHTTP2_MAX_PAYLOADLEN);
|
||||||
|
|
||||||
|
rv = session->callbacks.pack_extension_callback(session, buf->last, buflen,
|
||||||
|
frame, session->user_data);
|
||||||
|
if (rv == NGHTTP2_ERR_CANCEL) {
|
||||||
|
return (int)rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rv < 0 || (size_t)rv > buflen) {
|
||||||
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
framelen = (size_t)rv;
|
||||||
|
|
||||||
|
frame->hd.length = framelen;
|
||||||
|
|
||||||
|
assert(buf->pos == buf->last);
|
||||||
|
buf->last += framelen;
|
||||||
|
buf->pos -= NGHTTP2_FRAME_HDLEN;
|
||||||
|
|
||||||
|
nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function serializes frame for transmission.
|
* This function serializes frame for transmission.
|
||||||
*
|
*
|
||||||
|
@ -1989,8 +2029,22 @@ static int session_prep_frame(nghttp2_session *session,
|
||||||
nghttp2_frame_pack_window_update(&session->aob.framebufs,
|
nghttp2_frame_pack_window_update(&session->aob.framebufs,
|
||||||
&frame->window_update);
|
&frame->window_update);
|
||||||
break;
|
break;
|
||||||
|
case NGHTTP2_CONTINUATION:
|
||||||
|
/* We never handle CONTINUATION here. */
|
||||||
|
assert(0);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
/* extension frame */
|
||||||
|
if (session_is_closing(session)) {
|
||||||
|
return NGHTTP2_ERR_SESSION_CLOSING;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = session_pack_extension(session, &session->aob.framebufs, frame);
|
||||||
|
if (rv != 0) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
|
@ -3074,6 +3128,47 @@ static int session_call_on_header(nghttp2_session *session,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
session_call_on_extension_chunk_recv_callback(nghttp2_session *session,
|
||||||
|
const uint8_t *data, size_t len) {
|
||||||
|
int rv;
|
||||||
|
nghttp2_inbound_frame *iframe = &session->iframe;
|
||||||
|
nghttp2_frame *frame = &iframe->frame;
|
||||||
|
|
||||||
|
if (session->callbacks.on_extension_chunk_recv_callback) {
|
||||||
|
rv = session->callbacks.on_extension_chunk_recv_callback(
|
||||||
|
session, &frame->hd, data, len, session->user_data);
|
||||||
|
if (rv == NGHTTP2_ERR_CANCEL) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
if (rv != 0) {
|
||||||
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int session_call_unpack_extension_callback(nghttp2_session *session) {
|
||||||
|
int rv;
|
||||||
|
nghttp2_inbound_frame *iframe = &session->iframe;
|
||||||
|
nghttp2_frame *frame = &iframe->frame;
|
||||||
|
void *payload = NULL;
|
||||||
|
|
||||||
|
rv = session->callbacks.unpack_extension_callback(
|
||||||
|
session, &payload, &frame->hd, session->user_data);
|
||||||
|
if (rv == NGHTTP2_ERR_CANCEL) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
if (rv != 0) {
|
||||||
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame->ext.payload = payload;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handles frame size error.
|
* Handles frame size error.
|
||||||
*
|
*
|
||||||
|
@ -4412,6 +4507,24 @@ static int session_process_window_update_frame(nghttp2_session *session) {
|
||||||
return nghttp2_session_on_window_update_received(session, frame);
|
return nghttp2_session_on_window_update_received(session, frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int session_process_extension_frame(nghttp2_session *session) {
|
||||||
|
int rv;
|
||||||
|
nghttp2_inbound_frame *iframe = &session->iframe;
|
||||||
|
nghttp2_frame *frame = &iframe->frame;
|
||||||
|
|
||||||
|
rv = session_call_unpack_extension_callback(session);
|
||||||
|
if (nghttp2_is_fatal(rv)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This handles the case where rv == NGHTTP2_ERR_CANCEL as well */
|
||||||
|
if (rv != 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return session_call_on_frame_received(session, frame);
|
||||||
|
}
|
||||||
|
|
||||||
int nghttp2_session_on_data_received(nghttp2_session *session,
|
int nghttp2_session_on_data_received(nghttp2_session *session,
|
||||||
nghttp2_frame *frame) {
|
nghttp2_frame *frame) {
|
||||||
int rv = 0;
|
int rv = 0;
|
||||||
|
@ -5230,11 +5343,21 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
||||||
default:
|
default:
|
||||||
DEBUGF(fprintf(stderr, "recv: unknown frame\n"));
|
DEBUGF(fprintf(stderr, "recv: unknown frame\n"));
|
||||||
|
|
||||||
/* Silently ignore unknown frame type. */
|
if (!session->callbacks.unpack_extension_callback ||
|
||||||
|
(session->user_recv_ext_types[iframe->frame.hd.type / 8] &
|
||||||
|
(1 << (iframe->frame.hd.type & 0x7))) == 0) {
|
||||||
|
/* Silently ignore unknown frame type. */
|
||||||
|
|
||||||
|
busy = 1;
|
||||||
|
|
||||||
|
iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
busy = 1;
|
busy = 1;
|
||||||
|
|
||||||
iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
|
iframe->state = NGHTTP2_IB_READ_EXTENSION_PAYLOAD;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -5934,6 +6057,44 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
||||||
break;
|
break;
|
||||||
case NGHTTP2_IB_IGN_ALL:
|
case NGHTTP2_IB_IGN_ALL:
|
||||||
return (ssize_t)inlen;
|
return (ssize_t)inlen;
|
||||||
|
case NGHTTP2_IB_READ_EXTENSION_PAYLOAD:
|
||||||
|
DEBUGF(fprintf(stderr, "recv: [IB_READ_EXTENSION_PAYLOAD]\n"));
|
||||||
|
|
||||||
|
readlen = inbound_frame_payload_readlen(iframe, in, last);
|
||||||
|
iframe->payloadleft -= readlen;
|
||||||
|
in += readlen;
|
||||||
|
|
||||||
|
DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen,
|
||||||
|
iframe->payloadleft));
|
||||||
|
|
||||||
|
if (readlen > 0) {
|
||||||
|
rv = session_call_on_extension_chunk_recv_callback(
|
||||||
|
session, in - readlen, readlen);
|
||||||
|
if (nghttp2_is_fatal(rv)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rv != 0) {
|
||||||
|
busy = 1;
|
||||||
|
|
||||||
|
iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iframe->payloadleft > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = session_process_extension_frame(session);
|
||||||
|
if (nghttp2_is_fatal(rv)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
session_inbound_frame_reset(session);
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!busy && in == last) {
|
if (!busy && in == last) {
|
||||||
|
|
|
@ -105,7 +105,8 @@ typedef enum {
|
||||||
NGHTTP2_IB_READ_PAD_DATA,
|
NGHTTP2_IB_READ_PAD_DATA,
|
||||||
NGHTTP2_IB_READ_DATA,
|
NGHTTP2_IB_READ_DATA,
|
||||||
NGHTTP2_IB_IGN_DATA,
|
NGHTTP2_IB_IGN_DATA,
|
||||||
NGHTTP2_IB_IGN_ALL
|
NGHTTP2_IB_IGN_ALL,
|
||||||
|
NGHTTP2_IB_READ_EXTENSION_PAYLOAD
|
||||||
} nghttp2_inbound_state;
|
} nghttp2_inbound_state;
|
||||||
|
|
||||||
#define NGHTTP2_INBOUND_NUM_IV 7
|
#define NGHTTP2_INBOUND_NUM_IV 7
|
||||||
|
@ -304,6 +305,13 @@ struct nghttp2_session {
|
||||||
this session. The nonzero does not necessarily mean
|
this session. The nonzero does not necessarily mean
|
||||||
WINDOW_UPDATE is not queued. */
|
WINDOW_UPDATE is not queued. */
|
||||||
uint8_t window_update_queued;
|
uint8_t window_update_queued;
|
||||||
|
/* Bitfield of extension frame types that application is willing to
|
||||||
|
receive. To designate the bit of given frame type i, use
|
||||||
|
user_recv_ext_types[i / 8] & (1 << (i & 0x7)). First 10 frame
|
||||||
|
types are standard frame types and not used in this bitfield. If
|
||||||
|
bit is set, it indicates that incoming frame with that type is
|
||||||
|
passed to user defined callbacks, otherwise they are ignored. */
|
||||||
|
uint8_t user_recv_ext_types[32];
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Struct used when updating initial window size of each active
|
/* Struct used when updating initial window size of each active
|
||||||
|
|
|
@ -530,3 +530,40 @@ ssize_t nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen,
|
||||||
|
|
||||||
return (ssize_t)nghttp2_frame_pack_settings_payload(buf, iv, niv);
|
return (ssize_t)nghttp2_frame_pack_settings_payload(buf, iv, niv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int nghttp2_submit_extension(nghttp2_session *session, uint8_t type,
|
||||||
|
uint8_t flags, int32_t stream_id, void *payload) {
|
||||||
|
int rv;
|
||||||
|
nghttp2_outbound_item *item;
|
||||||
|
nghttp2_frame *frame;
|
||||||
|
nghttp2_mem *mem;
|
||||||
|
|
||||||
|
mem = &session->mem;
|
||||||
|
|
||||||
|
if (type <= NGHTTP2_CONTINUATION) {
|
||||||
|
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!session->callbacks.pack_extension_callback) {
|
||||||
|
return NGHTTP2_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
|
||||||
|
if (item == NULL) {
|
||||||
|
return NGHTTP2_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
nghttp2_outbound_item_init(item);
|
||||||
|
|
||||||
|
frame = &item->frame;
|
||||||
|
nghttp2_frame_extension_init(&frame->ext, type, flags, stream_id, payload);
|
||||||
|
|
||||||
|
rv = nghttp2_session_add_item(session, item);
|
||||||
|
if (rv != 0) {
|
||||||
|
nghttp2_frame_extension_free(&frame->ext);
|
||||||
|
nghttp2_mem_free(mem, item);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -121,7 +121,7 @@ const char *strsettingsid(int32_t id) {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const char *strframetype(uint8_t type) {
|
std::string strframetype(uint8_t type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case NGHTTP2_DATA:
|
case NGHTTP2_DATA:
|
||||||
return "DATA";
|
return "DATA";
|
||||||
|
@ -141,9 +141,13 @@ const char *strframetype(uint8_t type) {
|
||||||
return "GOAWAY";
|
return "GOAWAY";
|
||||||
case NGHTTP2_WINDOW_UPDATE:
|
case NGHTTP2_WINDOW_UPDATE:
|
||||||
return "WINDOW_UPDATE";
|
return "WINDOW_UPDATE";
|
||||||
default:
|
|
||||||
return "UNKNOWN";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string s = "UNKNOWN(0x";
|
||||||
|
s += util::format_hex(&type, 1);
|
||||||
|
s += ")";
|
||||||
|
|
||||||
|
return s;
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -280,7 +284,7 @@ const char *frame_name_ansi_esc(print_type ptype) {
|
||||||
namespace {
|
namespace {
|
||||||
void print_frame(print_type ptype, const nghttp2_frame *frame) {
|
void print_frame(print_type ptype, const nghttp2_frame *frame) {
|
||||||
fprintf(outfile, "%s%s%s frame ", frame_name_ansi_esc(ptype),
|
fprintf(outfile, "%s%s%s frame ", frame_name_ansi_esc(ptype),
|
||||||
strframetype(frame->hd.type), ansi_escend());
|
strframetype(frame->hd.type).c_str(), ansi_escend());
|
||||||
print_frame_hd(frame->hd);
|
print_frame_hd(frame->hd);
|
||||||
if (frame->hd.flags) {
|
if (frame->hd.flags) {
|
||||||
print_frame_attr_indent();
|
print_frame_attr_indent();
|
||||||
|
|
|
@ -101,6 +101,8 @@ int main(int argc _U_, char *argv[] _U_) {
|
||||||
test_nghttp2_session_recv_settings_header_table_size) ||
|
test_nghttp2_session_recv_settings_header_table_size) ||
|
||||||
!CU_add_test(pSuite, "session_recv_too_large_frame_length",
|
!CU_add_test(pSuite, "session_recv_too_large_frame_length",
|
||||||
test_nghttp2_session_recv_too_large_frame_length) ||
|
test_nghttp2_session_recv_too_large_frame_length) ||
|
||||||
|
!CU_add_test(pSuite, "session_recv_extension",
|
||||||
|
test_nghttp2_session_recv_extension) ||
|
||||||
!CU_add_test(pSuite, "session_continue", test_nghttp2_session_continue) ||
|
!CU_add_test(pSuite, "session_continue", test_nghttp2_session_continue) ||
|
||||||
!CU_add_test(pSuite, "session_add_frame",
|
!CU_add_test(pSuite, "session_add_frame",
|
||||||
test_nghttp2_session_add_frame) ||
|
test_nghttp2_session_add_frame) ||
|
||||||
|
@ -194,6 +196,7 @@ int main(int argc _U_, char *argv[] _U_) {
|
||||||
test_nghttp2_submit_shutdown_notice) ||
|
test_nghttp2_submit_shutdown_notice) ||
|
||||||
!CU_add_test(pSuite, "submit_invalid_nv",
|
!CU_add_test(pSuite, "submit_invalid_nv",
|
||||||
test_nghttp2_submit_invalid_nv) ||
|
test_nghttp2_submit_invalid_nv) ||
|
||||||
|
!CU_add_test(pSuite, "submit_extension", test_nghttp2_submit_extension) ||
|
||||||
!CU_add_test(pSuite, "session_open_stream",
|
!CU_add_test(pSuite, "session_open_stream",
|
||||||
test_nghttp2_session_open_stream) ||
|
test_nghttp2_session_open_stream) ||
|
||||||
!CU_add_test(pSuite, "session_open_stream_with_idle_stream_dep",
|
!CU_add_test(pSuite, "session_open_stream_with_idle_stream_dep",
|
||||||
|
|
|
@ -54,6 +54,7 @@ typedef struct {
|
||||||
scripted_data_feed *df;
|
scripted_data_feed *df;
|
||||||
int frame_recv_cb_called, invalid_frame_recv_cb_called;
|
int frame_recv_cb_called, invalid_frame_recv_cb_called;
|
||||||
uint8_t recv_frame_type;
|
uint8_t recv_frame_type;
|
||||||
|
nghttp2_frame_hd recv_frame_hd;
|
||||||
int frame_send_cb_called;
|
int frame_send_cb_called;
|
||||||
uint8_t sent_frame_type;
|
uint8_t sent_frame_type;
|
||||||
int frame_not_send_cb_called;
|
int frame_not_send_cb_called;
|
||||||
|
@ -73,6 +74,7 @@ typedef struct {
|
||||||
size_t data_chunk_len;
|
size_t data_chunk_len;
|
||||||
size_t padlen;
|
size_t padlen;
|
||||||
int begin_frame_cb_called;
|
int begin_frame_cb_called;
|
||||||
|
nghttp2_buf scratchbuf;
|
||||||
} my_user_data;
|
} my_user_data;
|
||||||
|
|
||||||
static const nghttp2_nv reqnv[] = {
|
static const nghttp2_nv reqnv[] = {
|
||||||
|
@ -175,6 +177,8 @@ static int on_frame_recv_callback(nghttp2_session *session _U_,
|
||||||
my_user_data *ud = (my_user_data *)user_data;
|
my_user_data *ud = (my_user_data *)user_data;
|
||||||
++ud->frame_recv_cb_called;
|
++ud->frame_recv_cb_called;
|
||||||
ud->recv_frame_type = frame->hd.type;
|
ud->recv_frame_type = frame->hd.type;
|
||||||
|
ud->recv_frame_hd = frame->hd;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,6 +420,54 @@ static int on_stream_close_callback(nghttp2_session *session _U_,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ssize_t pack_extension_callback(nghttp2_session *session _U_,
|
||||||
|
uint8_t *buf, size_t len _U_,
|
||||||
|
const nghttp2_frame *frame,
|
||||||
|
void *user_data _U_) {
|
||||||
|
nghttp2_buf *p = frame->ext.payload;
|
||||||
|
|
||||||
|
memcpy(buf, p->pos, nghttp2_buf_len(p));
|
||||||
|
|
||||||
|
return (ssize_t)nghttp2_buf_len(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int on_extension_chunk_recv_callback(nghttp2_session *session _U_,
|
||||||
|
const nghttp2_frame_hd *hd _U_,
|
||||||
|
const uint8_t *data, size_t len,
|
||||||
|
void *user_data) {
|
||||||
|
my_user_data *my_data = (my_user_data *)user_data;
|
||||||
|
nghttp2_buf *buf = &my_data->scratchbuf;
|
||||||
|
|
||||||
|
buf->last = nghttp2_cpymem(buf->last, data, len);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cancel_on_extension_chunk_recv_callback(
|
||||||
|
nghttp2_session *session _U_, const nghttp2_frame_hd *hd _U_,
|
||||||
|
const uint8_t *data _U_, size_t len _U_, void *user_data _U_) {
|
||||||
|
return NGHTTP2_ERR_CANCEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int unpack_extension_callback(nghttp2_session *session _U_,
|
||||||
|
void **payload,
|
||||||
|
const nghttp2_frame_hd *hd _U_,
|
||||||
|
void *user_data) {
|
||||||
|
my_user_data *my_data = (my_user_data *)user_data;
|
||||||
|
nghttp2_buf *buf = &my_data->scratchbuf;
|
||||||
|
|
||||||
|
*payload = buf;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cancel_unpack_extension_callback(nghttp2_session *session _U_,
|
||||||
|
void **payload _U_,
|
||||||
|
const nghttp2_frame_hd *hd _U_,
|
||||||
|
void *user_data _U_) {
|
||||||
|
return NGHTTP2_ERR_CANCEL;
|
||||||
|
}
|
||||||
|
|
||||||
static nghttp2_settings_entry *dup_iv(const nghttp2_settings_entry *iv,
|
static nghttp2_settings_entry *dup_iv(const nghttp2_settings_entry *iv,
|
||||||
size_t niv) {
|
size_t niv) {
|
||||||
return nghttp2_frame_iv_copy(iv, niv, nghttp2_mem_default());
|
return nghttp2_frame_iv_copy(iv, niv, nghttp2_mem_default());
|
||||||
|
@ -1822,6 +1874,87 @@ void test_nghttp2_session_recv_too_large_frame_length(void) {
|
||||||
nghttp2_session_del(session);
|
nghttp2_session_del(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_nghttp2_session_recv_extension(void) {
|
||||||
|
nghttp2_session *session;
|
||||||
|
nghttp2_session_callbacks callbacks;
|
||||||
|
my_user_data ud;
|
||||||
|
nghttp2_buf buf;
|
||||||
|
nghttp2_frame_hd hd;
|
||||||
|
nghttp2_mem *mem;
|
||||||
|
const char data[] = "Hello World!";
|
||||||
|
ssize_t rv;
|
||||||
|
nghttp2_option *option;
|
||||||
|
|
||||||
|
mem = nghttp2_mem_default();
|
||||||
|
|
||||||
|
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
|
||||||
|
|
||||||
|
callbacks.on_extension_chunk_recv_callback = on_extension_chunk_recv_callback;
|
||||||
|
callbacks.unpack_extension_callback = unpack_extension_callback;
|
||||||
|
callbacks.on_frame_recv_callback = on_frame_recv_callback;
|
||||||
|
|
||||||
|
nghttp2_option_new(&option);
|
||||||
|
nghttp2_option_set_user_recv_extension_type(option, 111);
|
||||||
|
|
||||||
|
nghttp2_buf_init2(&ud.scratchbuf, 4096, mem);
|
||||||
|
nghttp2_buf_init2(&buf, 4096, mem);
|
||||||
|
|
||||||
|
nghttp2_frame_hd_init(&hd, sizeof(data), 111, 0xab, 1000000007);
|
||||||
|
nghttp2_frame_pack_frame_hd(buf.last, &hd);
|
||||||
|
buf.last += NGHTTP2_FRAME_HDLEN;
|
||||||
|
buf.last = nghttp2_cpymem(buf.last, data, sizeof(data));
|
||||||
|
|
||||||
|
nghttp2_session_client_new2(&session, &callbacks, &ud, option);
|
||||||
|
|
||||||
|
nghttp2_frame_hd_init(&ud.recv_frame_hd, 0, 0, 0, 0);
|
||||||
|
rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf));
|
||||||
|
|
||||||
|
CU_ASSERT(NGHTTP2_FRAME_HDLEN + hd.length == (size_t)rv);
|
||||||
|
CU_ASSERT(111 == ud.recv_frame_hd.type);
|
||||||
|
CU_ASSERT(0xab == ud.recv_frame_hd.flags);
|
||||||
|
CU_ASSERT(1000000007 == ud.recv_frame_hd.stream_id);
|
||||||
|
CU_ASSERT(0 == memcmp(data, ud.scratchbuf.pos, sizeof(data)));
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
|
/* cancel in on_extension_chunk_recv_callback */
|
||||||
|
nghttp2_buf_reset(&ud.scratchbuf);
|
||||||
|
|
||||||
|
callbacks.on_extension_chunk_recv_callback =
|
||||||
|
cancel_on_extension_chunk_recv_callback;
|
||||||
|
|
||||||
|
nghttp2_session_server_new2(&session, &callbacks, &ud, option);
|
||||||
|
|
||||||
|
ud.frame_recv_cb_called = 0;
|
||||||
|
rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf));
|
||||||
|
|
||||||
|
CU_ASSERT(NGHTTP2_FRAME_HDLEN + hd.length == (size_t)rv);
|
||||||
|
CU_ASSERT(0 == ud.frame_recv_cb_called);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
|
/* cancel in unpack_extension_callback */
|
||||||
|
nghttp2_buf_reset(&ud.scratchbuf);
|
||||||
|
|
||||||
|
callbacks.on_extension_chunk_recv_callback = on_extension_chunk_recv_callback;
|
||||||
|
callbacks.unpack_extension_callback = cancel_unpack_extension_callback;
|
||||||
|
|
||||||
|
nghttp2_session_server_new2(&session, &callbacks, &ud, option);
|
||||||
|
|
||||||
|
ud.frame_recv_cb_called = 0;
|
||||||
|
rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf));
|
||||||
|
|
||||||
|
CU_ASSERT(NGHTTP2_FRAME_HDLEN + hd.length == (size_t)rv);
|
||||||
|
CU_ASSERT(0 == ud.frame_recv_cb_called);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
|
nghttp2_buf_free(&buf, mem);
|
||||||
|
nghttp2_buf_free(&ud.scratchbuf, mem);
|
||||||
|
|
||||||
|
nghttp2_option_del(option);
|
||||||
|
}
|
||||||
|
|
||||||
void test_nghttp2_session_continue(void) {
|
void test_nghttp2_session_continue(void) {
|
||||||
nghttp2_session *session;
|
nghttp2_session *session;
|
||||||
nghttp2_session_callbacks callbacks;
|
nghttp2_session_callbacks callbacks;
|
||||||
|
@ -5005,6 +5138,67 @@ void test_nghttp2_submit_invalid_nv(void) {
|
||||||
nghttp2_session_del(session);
|
nghttp2_session_del(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_nghttp2_submit_extension(void) {
|
||||||
|
nghttp2_session *session;
|
||||||
|
nghttp2_session_callbacks callbacks;
|
||||||
|
my_user_data ud;
|
||||||
|
accumulator acc;
|
||||||
|
nghttp2_mem *mem;
|
||||||
|
const char data[] = "Hello World!";
|
||||||
|
size_t len;
|
||||||
|
int32_t stream_id;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
mem = nghttp2_mem_default();
|
||||||
|
|
||||||
|
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
|
||||||
|
|
||||||
|
callbacks.pack_extension_callback = pack_extension_callback;
|
||||||
|
callbacks.send_callback = accumulator_send_callback;
|
||||||
|
|
||||||
|
nghttp2_buf_init2(&ud.scratchbuf, 4096, mem);
|
||||||
|
|
||||||
|
nghttp2_session_client_new(&session, &callbacks, &ud);
|
||||||
|
|
||||||
|
ud.scratchbuf.last = nghttp2_cpymem(ud.scratchbuf.last, data, sizeof(data));
|
||||||
|
ud.acc = &acc;
|
||||||
|
|
||||||
|
rv = nghttp2_submit_extension(session, 211, 0x01, 3, &ud.scratchbuf);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == rv);
|
||||||
|
|
||||||
|
acc.length = 0;
|
||||||
|
|
||||||
|
rv = nghttp2_session_send(session);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == rv);
|
||||||
|
CU_ASSERT(NGHTTP2_FRAME_HDLEN + sizeof(data) == acc.length);
|
||||||
|
|
||||||
|
len = nghttp2_get_uint32(acc.buf) >> 8;
|
||||||
|
|
||||||
|
CU_ASSERT(sizeof(data) == len);
|
||||||
|
CU_ASSERT(211 == acc.buf[3]);
|
||||||
|
CU_ASSERT(0x01 == acc.buf[4]);
|
||||||
|
|
||||||
|
stream_id = (int32_t)nghttp2_get_uint32(acc.buf + 5);
|
||||||
|
|
||||||
|
CU_ASSERT(3 == stream_id);
|
||||||
|
CU_ASSERT(0 == memcmp(data, &acc.buf[NGHTTP2_FRAME_HDLEN], sizeof(data)));
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
|
/* submitting standard HTTP/2 frame is error */
|
||||||
|
nghttp2_session_server_new(&session, &callbacks, &ud);
|
||||||
|
|
||||||
|
rv = nghttp2_submit_extension(session, NGHTTP2_GOAWAY, NGHTTP2_FLAG_NONE, 0,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
nghttp2_buf_free(&ud.scratchbuf, mem);
|
||||||
|
}
|
||||||
|
|
||||||
void test_nghttp2_session_open_stream(void) {
|
void test_nghttp2_session_open_stream(void) {
|
||||||
nghttp2_session *session;
|
nghttp2_session *session;
|
||||||
nghttp2_session_callbacks callbacks;
|
nghttp2_session_callbacks callbacks;
|
||||||
|
|
|
@ -40,6 +40,7 @@ void test_nghttp2_session_recv_unknown_frame(void);
|
||||||
void test_nghttp2_session_recv_unexpected_continuation(void);
|
void test_nghttp2_session_recv_unexpected_continuation(void);
|
||||||
void test_nghttp2_session_recv_settings_header_table_size(void);
|
void test_nghttp2_session_recv_settings_header_table_size(void);
|
||||||
void test_nghttp2_session_recv_too_large_frame_length(void);
|
void test_nghttp2_session_recv_too_large_frame_length(void);
|
||||||
|
void test_nghttp2_session_recv_extension(void);
|
||||||
void test_nghttp2_session_continue(void);
|
void test_nghttp2_session_continue(void);
|
||||||
void test_nghttp2_session_add_frame(void);
|
void test_nghttp2_session_add_frame(void);
|
||||||
void test_nghttp2_session_on_request_headers_received(void);
|
void test_nghttp2_session_on_request_headers_received(void);
|
||||||
|
@ -89,6 +90,7 @@ void test_nghttp2_submit_window_update(void);
|
||||||
void test_nghttp2_submit_window_update_local_window_size(void);
|
void test_nghttp2_submit_window_update_local_window_size(void);
|
||||||
void test_nghttp2_submit_shutdown_notice(void);
|
void test_nghttp2_submit_shutdown_notice(void);
|
||||||
void test_nghttp2_submit_invalid_nv(void);
|
void test_nghttp2_submit_invalid_nv(void);
|
||||||
|
void test_nghttp2_submit_extension(void);
|
||||||
void test_nghttp2_session_open_stream(void);
|
void test_nghttp2_session_open_stream(void);
|
||||||
void test_nghttp2_session_open_stream_with_idle_stream_dep(void);
|
void test_nghttp2_session_open_stream_with_idle_stream_dep(void);
|
||||||
void test_nghttp2_session_get_next_ob_item(void);
|
void test_nghttp2_session_get_next_ob_item(void);
|
||||||
|
|
Loading…
Reference in New Issue