Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
N
nghttp2
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Libraries
nghttp2
Commits
ba34e911
Commit
ba34e911
authored
Feb 25, 2016
by
Tatsuhiro Tsujikawa
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'simple-extensions'
parents
2782ef67
8aac5d6a
Changes
17
Show whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
669 additions
and
9 deletions
+669
-9
doc/Makefile.am
doc/Makefile.am
+5
-0
lib/includes/nghttp2/nghttp2.h
lib/includes/nghttp2/nghttp2.h
+187
-0
lib/nghttp2_callbacks.c
lib/nghttp2_callbacks.c
+18
-0
lib/nghttp2_callbacks.h
lib/nghttp2_callbacks.h
+3
-0
lib/nghttp2_frame.c
lib/nghttp2_frame.c
+11
-0
lib/nghttp2_frame.h
lib/nghttp2_frame.h
+6
-0
lib/nghttp2_helper.c
lib/nghttp2_helper.c
+2
-0
lib/nghttp2_option.c
lib/nghttp2_option.c
+11
-0
lib/nghttp2_option.h
lib/nghttp2_option.h
+6
-1
lib/nghttp2_outbound_item.c
lib/nghttp2_outbound_item.c
+3
-0
lib/nghttp2_session.c
lib/nghttp2_session.c
+164
-3
lib/nghttp2_session.h
lib/nghttp2_session.h
+9
-1
lib/nghttp2_submit.c
lib/nghttp2_submit.c
+37
-0
src/app_helper.cc
src/app_helper.cc
+8
-4
tests/main.c
tests/main.c
+3
-0
tests/nghttp2_session_test.c
tests/nghttp2_session_test.c
+194
-0
tests/nghttp2_session_test.h
tests/nghttp2_session_test.h
+2
-0
No files found.
doc/Makefile.am
View file @
ba34e911
...
...
@@ -58,6 +58,7 @@ APIDOCS= \
nghttp2_option_set_no_http_messaging.rst
\
nghttp2_option_set_no_recv_client_magic.rst
\
nghttp2_option_set_peer_max_concurrent_streams.rst
\
nghttp2_option_set_user_recv_extension_type.rst
\
nghttp2_pack_settings_payload.rst
\
nghttp2_priority_spec_check_default.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_headers_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_recv_callback.rst
\
nghttp2_session_callbacks_set_on_frame_send_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_stream_close_callback.rst
\
nghttp2_session_callbacks_set_pack_extension_callback.rst
\
nghttp2_session_callbacks_set_recv_callback.rst
\
nghttp2_session_callbacks_set_select_padding_callback.rst
\
nghttp2_session_callbacks_set_send_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_new2.rst
\
nghttp2_session_client_new3.rst
\
...
...
@@ -131,6 +135,7 @@ APIDOCS= \
nghttp2_stream_get_weight.rst
\
nghttp2_strerror.rst
\
nghttp2_submit_data.rst
\
nghttp2_submit_extension.rst
\
nghttp2_submit_goaway.rst
\
nghttp2_submit_headers.rst
\
nghttp2_submit_ping.rst
\
...
...
lib/includes/nghttp2/nghttp2.h
View file @
ba34e911
...
...
@@ -382,6 +382,10 @@ typedef enum {
* Unexpected internal error, but recovered.
*/
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
* 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
,
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
;
/**
...
...
@@ -1892,6 +1989,37 @@ NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_data_callback(
nghttp2_session_callbacks
*
cbs
,
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
*
...
...
@@ -2106,6 +2234,23 @@ NGHTTP2_EXTERN void
nghttp2_option_set_max_reserved_remote_streams
(
nghttp2_option
*
option
,
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
*
...
...
@@ -3776,6 +3921,48 @@ NGHTTP2_EXTERN int nghttp2_submit_window_update(nghttp2_session *session,
int32_t
stream_id
,
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
*
...
...
lib/nghttp2_callbacks.c
View file @
ba34e911
...
...
@@ -127,3 +127,21 @@ void nghttp2_session_callbacks_set_send_data_callback(
nghttp2_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
;
}
lib/nghttp2_callbacks.h
View file @
ba34e911
...
...
@@ -107,6 +107,9 @@ struct nghttp2_session_callbacks {
*/
nghttp2_on_begin_frame_callback
on_begin_frame_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 */
lib/nghttp2_frame.c
View file @
ba34e911
...
...
@@ -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_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
)
{
if
(
flags
&
NGHTTP2_FLAG_PRIORITY
)
{
return
NGHTTP2_PRIORITY_SPECLEN
;
...
...
lib/nghttp2_frame.h
View file @
ba34e911
...
...
@@ -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_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
* padding length is given in the |padlen|. The returned value does
...
...
lib/nghttp2_helper.c
View file @
ba34e911
...
...
@@ -288,6 +288,8 @@ const char *nghttp2_strerror(int error_code) {
return
"Stream was refused"
;
case
NGHTTP2_ERR_INTERNAL
:
return
"Internal error"
;
case
NGHTTP2_ERR_CANCEL
:
return
"Cancel"
;
case
NGHTTP2_ERR_NOMEM
:
return
"Out of memory"
;
case
NGHTTP2_ERR_CALLBACK_FAILURE
:
...
...
lib/nghttp2_option.c
View file @
ba34e911
...
...
@@ -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
->
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
)));
}
lib/nghttp2_option.h
View file @
ba34e911
...
...
@@ -59,7 +59,8 @@ typedef enum {
NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS
=
1
<<
1
,
NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC
=
1
<<
2
,
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
;
/**
...
...
@@ -91,6 +92,10 @@ struct nghttp2_option {
* NGHTTP2_OPT_NO_HTTP_MESSAGING
*/
int
no_http_messaging
;
/**
* NGHTTP2_OPT_USER_RECV_EXT_TYPES
*/
uint8_t
user_recv_ext_types
[
32
];
};
#endif
/* NGHTTP2_OPTION_H */
lib/nghttp2_outbound_item.c
View file @
ba34e911
...
...
@@ -72,6 +72,9 @@ void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) {
case
NGHTTP2_WINDOW_UPDATE
:
nghttp2_frame_window_update_free
(
&
frame
->
window_update
);
break
;
default:
nghttp2_frame_extension_free
(
&
frame
->
ext
);
break
;
}
}
...
...
lib/nghttp2_session.c
View file @
ba34e911
...
...
@@ -405,6 +405,11 @@ static int session_new(nghttp2_session **session_ptr,
(
*
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
;
...
...
@@ -1749,6 +1754,41 @@ static size_t session_estimate_headers_payload(nghttp2_session *session,
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.
*
...
...
@@ -1989,8 +2029,22 @@ static int session_prep_frame(nghttp2_session *session,
nghttp2_frame_pack_window_update
(
&
session
->
aob
.
framebufs
,
&
frame
->
window_update
);
break
;
case
NGHTTP2_CONTINUATION
:
/* We never handle CONTINUATION here. */
assert
(
0
);
break
;
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
;
}
else
{
...
...
@@ -3074,6 +3128,47 @@ static int session_call_on_header(nghttp2_session *session,
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.
*
...
...
@@ -4412,6 +4507,24 @@ static int session_process_window_update_frame(nghttp2_session *session) {
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
,
nghttp2_frame
*
frame
)
{
int
rv
=
0
;
...
...
@@ -5230,6 +5343,9 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
default:
DEBUGF
(
fprintf
(
stderr
,
"recv: unknown frame
\n
"
));
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
;
...
...
@@ -5239,6 +5355,13 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
break
;
}
busy
=
1
;
iframe
->
state
=
NGHTTP2_IB_READ_EXTENSION_PAYLOAD
;
break
;
}
if
(
!
on_begin_frame_called
)
{
switch
(
iframe
->
state
)
{
case
NGHTTP2_IB_IGN_HEADER_BLOCK
:
...
...
@@ -5934,6 +6057,44 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
break
;
case
NGHTTP2_IB_IGN_ALL
:
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
)
{
...
...
lib/nghttp2_session.h
View file @
ba34e911
...
...
@@ -105,7 +105,8 @@ typedef enum {
NGHTTP2_IB_READ_PAD_DATA
,
NGHTTP2_IB_READ_DATA
,
NGHTTP2_IB_IGN_DATA
,
NGHTTP2_IB_IGN_ALL
NGHTTP2_IB_IGN_ALL
,
NGHTTP2_IB_READ_EXTENSION_PAYLOAD
}
nghttp2_inbound_state
;
#define NGHTTP2_INBOUND_NUM_IV 7
...
...
@@ -304,6 +305,13 @@ struct nghttp2_session {
this session. The nonzero does not necessarily mean
WINDOW_UPDATE is not 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
...
...
lib/nghttp2_submit.c
View file @
ba34e911
...
...
@@ -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
);
}
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
;
}
src/app_helper.cc
View file @
ba34e911
...
...
@@ -121,7 +121,7 @@ const char *strsettingsid(int32_t id) {
}
// namespace
namespace
{
const
char
*
strframetype
(
uint8_t
type
)
{
std
::
string
strframetype
(
uint8_t
type
)
{
switch
(
type
)
{
case
NGHTTP2_DATA
:
return
"DATA"
;
...
...
@@ -141,9 +141,13 @@ const char *strframetype(uint8_t type) {
return
"GOAWAY"
;
case
NGHTTP2_WINDOW_UPDATE
:
return
"WINDOW_UPDATE"
;
default:
return
"UNKNOWN"
;
}
std
::
string
s
=
"UNKNOWN(0x"
;
s
+=
util
::
format_hex
(
&
type
,
1
);
s
+=
")"
;
return
s
;
};
}
// namespace
...
...
@@ -280,7 +284,7 @@ const char *frame_name_ansi_esc(print_type ptype) {
namespace
{
void
print_frame
(
print_type
ptype
,
const
nghttp2_frame
*
frame
)
{
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
);
if
(
frame
->
hd
.
flags
)
{
print_frame_attr_indent
();
...
...
tests/main.c
View file @
ba34e911
...
...
@@ -101,6 +101,8 @@ int main(int argc _U_, char *argv[] _U_) {
test_nghttp2_session_recv_settings_header_table_size
)
||
!
CU_add_test
(
pSuite
,
"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_add_frame"
,
test_nghttp2_session_add_frame
)
||
...
...
@@ -194,6 +196,7 @@ int main(int argc _U_, char *argv[] _U_) {
test_nghttp2_submit_shutdown_notice
)
||
!
CU_add_test
(
pSuite
,
"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"
,
test_nghttp2_session_open_stream
)
||
!
CU_add_test
(
pSuite
,
"session_open_stream_with_idle_stream_dep"
,
...
...
tests/nghttp2_session_test.c
View file @
ba34e911
...
...
@@ -54,6 +54,7 @@ typedef struct {
scripted_data_feed
*
df
;
int
frame_recv_cb_called
,
invalid_frame_recv_cb_called
;
uint8_t
recv_frame_type
;
nghttp2_frame_hd
recv_frame_hd
;
int
frame_send_cb_called
;
uint8_t
sent_frame_type
;
int
frame_not_send_cb_called
;
...
...
@@ -73,6 +74,7 @@ typedef struct {
size_t
data_chunk_len
;
size_t
padlen
;
int
begin_frame_cb_called
;
nghttp2_buf
scratchbuf
;
}
my_user_data
;
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
;
++
ud
->
frame_recv_cb_called
;
ud
->
recv_frame_type
=
frame
->
hd
.
type
;
ud
->
recv_frame_hd
=
frame
->
hd
;
return
0
;
}
...
...
@@ -416,6 +420,54 @@ static int on_stream_close_callback(nghttp2_session *session _U_,
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
,
size_t
niv
)
{
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
);
}
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
)
{
nghttp2_session
*
session
;
nghttp2_session_callbacks
callbacks
;
...
...
@@ -5005,6 +5138,67 @@ void test_nghttp2_submit_invalid_nv(void) {
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
)
{
nghttp2_session
*
session
;
nghttp2_session_callbacks
callbacks
;
...
...
tests/nghttp2_session_test.h
View file @
ba34e911
...
...
@@ -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_settings_header_table_size
(
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_add_frame
(
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_shutdown_notice
(
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_with_idle_stream_dep
(
void
);
void
test_nghttp2_session_get_next_ob_item
(
void
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment