Replace on_end_headers_callback with on_begin_headers_callback

Previously, there is inconsistency when on_frame_recv_callback
is called between HEADERS/PUSH_PROMISE and the other frames.
For former case, it is called before header block, in latter
case, it is called after whole frame is received. To make it
consistent, we call on_frame_recv_callback for HEADERS/PUSH_PROMISE
after its frame is fully received. Since on_frame_recv_callback
can signal the end of header block, we replaced on_end_headers_callback
with on_begin_headers_callback, which is called when the reception
of the header block is started.
This commit is contained in:
Tatsuhiro Tsujikawa 2014-01-29 21:23:13 +09:00
parent ff475104ab
commit e186e01933
12 changed files with 417 additions and 432 deletions

View File

@ -231,30 +231,11 @@ static int on_header_callback(nghttp2_session *session,
return 0;
}
/* nghttp2_on_end_headers_callback: Called when nghttp2 library emits
all header name/value pairs, or may be called prematurely because
of errors which is indicated by |error_code|. */
static int on_end_headers_callback(nghttp2_session *session,
/* nghttp2_on_begin_headers_callback: Called when nghttp2 library gets
started to receive header block. */
static int on_begin_headers_callback(nghttp2_session *session,
const nghttp2_frame *frame,
nghttp2_error_code error_code,
void *user_data)
{
http2_session_data *session_data = (http2_session_data*)user_data;
switch(frame->hd.type) {
case NGHTTP2_HEADERS:
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
session_data->stream_data->stream_id == frame->hd.stream_id) {
fprintf(stderr, "All headers received with error_code=%d\n", error_code);
}
break;
}
return 0;
}
/* nghttp2_on_frame_recv_callback: Called when nghttp2 library
received a frame from the remote peer. */
static int on_frame_recv_callback(nghttp2_session *session,
const nghttp2_frame *frame, void *user_data)
{
http2_session_data *session_data = (http2_session_data*)user_data;
switch(frame->hd.type) {
@ -269,6 +250,23 @@ static int on_frame_recv_callback(nghttp2_session *session,
return 0;
}
/* nghttp2_on_frame_recv_callback: Called when nghttp2 library
received a complete frame from the remote peer. */
static int on_frame_recv_callback(nghttp2_session *session,
const nghttp2_frame *frame, void *user_data)
{
http2_session_data *session_data = (http2_session_data*)user_data;
switch(frame->hd.type) {
case NGHTTP2_HEADERS:
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
session_data->stream_data->stream_id == frame->hd.stream_id) {
fprintf(stderr, "All headers received\n");
}
break;
}
return 0;
}
/* nghttp2_on_data_chunk_recv_callback: Called when DATA frame is
received from the remote peer. In this implementation, if the frame
is meant to the stream we initiated, print the received data in
@ -361,7 +359,7 @@ static void initialize_nghttp2_session(http2_session_data *session_data)
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
callbacks.on_stream_close_callback = on_stream_close_callback;
callbacks.on_header_callback = on_header_callback;
callbacks.on_end_headers_callback = on_end_headers_callback;
callbacks.on_begin_headers_callback = on_begin_headers_callback;
nghttp2_session_client_new(&session_data->session, &callbacks, session_data);
}

View File

@ -325,56 +325,6 @@ static char* percent_decode(const uint8_t *value, size_t valuelen)
return res;
}
/* nghttp2_on_header_callback: Called when nghttp2 library emits
single header name/value pair. */
static int on_header_callback(nghttp2_session *session,
const nghttp2_frame *frame,
const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen,
void *user_data)
{
http2_stream_data *stream_data;
const char PATH[] = ":path";
switch(frame->hd.type) {
case NGHTTP2_HEADERS:
if(frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
break;
}
stream_data = nghttp2_session_get_stream_user_data(session,
frame->hd.stream_id);
if(stream_data->request_path) {
break;
}
if(namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
size_t j;
for(j = 0; j < valuelen && value[j] != '?'; ++j);
stream_data->request_path = percent_decode(value, j);
}
break;
}
return 0;
}
static int on_frame_recv_callback(nghttp2_session *session,
const nghttp2_frame *frame, void *user_data)
{
http2_session_data *session_data = (http2_session_data*)user_data;
http2_stream_data *stream_data;
switch(frame->hd.type) {
case NGHTTP2_HEADERS:
if(frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
break;
}
stream_data = create_http2_stream_data(session_data, frame->hd.stream_id);
nghttp2_session_set_stream_user_data(session, frame->hd.stream_id,
stream_data);
break;
default:
break;
}
return 0;
}
static ssize_t file_read_callback
(nghttp2_session *session, int32_t stream_id,
uint8_t *buf, size_t length, int *eof,
@ -443,6 +393,53 @@ static int error_reply(nghttp2_session *session,
return 0;
}
/* nghttp2_on_header_callback: Called when nghttp2 library emits
single header name/value pair. */
static int on_header_callback(nghttp2_session *session,
const nghttp2_frame *frame,
const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen,
void *user_data)
{
http2_stream_data *stream_data;
const char PATH[] = ":path";
switch(frame->hd.type) {
case NGHTTP2_HEADERS:
if(frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
break;
}
stream_data = nghttp2_session_get_stream_user_data(session,
frame->hd.stream_id);
if(stream_data->request_path) {
break;
}
if(namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
size_t j;
for(j = 0; j < valuelen && value[j] != '?'; ++j);
stream_data->request_path = percent_decode(value, j);
}
break;
}
return 0;
}
static int on_begin_headers_callback(nghttp2_session *session,
const nghttp2_frame *frame,
void *user_data)
{
http2_session_data *session_data = (http2_session_data*)user_data;
http2_stream_data *stream_data;
if(frame->hd.type != NGHTTP2_HEADERS ||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
return 0;
}
stream_data = create_http2_stream_data(session_data, frame->hd.stream_id);
nghttp2_session_set_stream_user_data(session, frame->hd.stream_id,
stream_data);
return 0;
}
/* Minimum check for directory traversal. Returns nonzero if it is
safe. */
static int check_path(const char *path)
@ -455,19 +452,16 @@ static int check_path(const char *path)
!ends_with(path, "/..") && !ends_with(path, "/.");
}
static int on_request_recv_callback(nghttp2_session *session,
int32_t stream_id, void *user_data)
static int on_request_recv(nghttp2_session *session,
http2_session_data *session_data,
http2_stream_data *stream_data)
{
int fd;
http2_session_data *session_data = (http2_session_data*)user_data;
http2_stream_data *stream_data;
nghttp2_nv hdrs[] = {
MAKE_NV(":status", "200")
};
char *rel_path;
stream_data = (http2_stream_data*)nghttp2_session_get_stream_user_data
(session, stream_id);
if(!stream_data->request_path) {
if(error_reply(session, stream_data) != 0) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
@ -492,13 +486,40 @@ static int on_request_recv_callback(nghttp2_session *session,
}
stream_data->fd = fd;
if(send_response(session, stream_id, hdrs, ARRLEN(hdrs), fd) != 0) {
if(send_response(session, stream_data->stream_id, hdrs,
ARRLEN(hdrs), fd) != 0) {
close(fd);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
return 0;
}
static int on_frame_recv_callback(nghttp2_session *session,
const nghttp2_frame *frame, void *user_data)
{
http2_session_data *session_data = (http2_session_data*)user_data;
http2_stream_data *stream_data;
switch(frame->hd.type) {
case NGHTTP2_DATA:
case NGHTTP2_HEADERS:
/* Check that the client request has finished */
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
stream_data = nghttp2_session_get_stream_user_data(session,
frame->hd.stream_id);
/* For DATA and HEADERS frame, this callback may be called after
on_stream_close_callback. Check that stream still alive. */
if(!stream_data) {
return 0;
}
return on_request_recv(session, session_data, stream_data);
}
break;
default:
break;
}
return 0;
}
static int on_stream_close_callback(nghttp2_session *session,
int32_t stream_id,
nghttp2_error_code error_code,
@ -519,9 +540,9 @@ static void initialize_nghttp2_session(http2_session_data *session_data)
callbacks.send_callback = send_callback;
callbacks.on_frame_recv_callback = on_frame_recv_callback;
callbacks.on_request_recv_callback = on_request_recv_callback;
callbacks.on_stream_close_callback = on_stream_close_callback;
callbacks.on_header_callback = on_header_callback;
callbacks.on_begin_headers_callback = on_begin_headers_callback;
nghttp2_session_server_new(&session_data->session, &callbacks, session_data);
}

View File

@ -916,10 +916,6 @@ typedef int (*nghttp2_on_frame_recv_callback)
* member of their data structure are always ``NULL`` and 0
* respectively.
*
* If this callback is called, :type:`nghttp2_on_header_callback` and
* :type:`nghttp2_on_end_headers_callback` will not be called for this
* frame.
*
* 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
@ -1076,6 +1072,30 @@ typedef int (*nghttp2_on_unknown_frame_recv_callback)
const uint8_t *payload, size_t payloadlen,
void *user_data);
/**
* @functypedef
*
* Callback function invoked when the reception of header block in
* HEADERS or PUSH_PROMISE is started. Each header name/value pair
* will be emitted by :type:`nghttp2_on_header_callback`.
*
* The |frame->hd.flags| may not have :enum:`NGHTTP2_FLAG_END_HEADERS`
* flag set, which indicates that one or more CONTINUATION frames are
* involved. But the application does not need to care about that
* because the header name/value pairs are emitted transparently
* regardless of CONTINUATION frames.
*
* 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,
* `nghttp2_session_mem_recv()` function will immediately return
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
*/
typedef int (*nghttp2_on_begin_headers_callback)
(nghttp2_session *session, const nghttp2_frame *frame, void *user_data);
/**
* @functypedef
*
@ -1083,9 +1103,11 @@ typedef int (*nghttp2_on_unknown_frame_recv_callback)
* for the |frame|. When this callback is invoked, ``frame->hd.type``
* is either :enum:`NGHTTP2_HEADERS` or :enum:`NGHTTP2_PUSH_PROMISE`.
* After all header name/value pairs are processed with this callback,
* or header decompression error occurred, then
* :type:`nghttp2_on_end_headers_callback` will be invoked unless
* application returns nonzero value from this callback.
* and no error has been detected,
* :type:`nghttp2_on_frame_recv_callback` will be invoked. If there
* is an error in decompression,
* :type:`nghttp2_on_frame_recv_callback` for the |frame| will not be
* invoked.
*
* The |name| may be ``NULL`` if the |namelen| is 0. The same thing
* can be said about the |value|.
@ -1102,7 +1124,7 @@ typedef int (*nghttp2_on_unknown_frame_recv_callback)
* Returning :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close
* the stream by issuing RST_STREAM with
* :enum:`NGHTTP2_INTERNAL_ERROR`. In this case,
* :type:`nghttp2_on_end_headers_callback` will not be invoked.
* :type:`nghttp2_on_frame_recv_callback` will not be invoked.
*
* The implementation of this function must return 0 if it
* succeeds. It may return :enum:`NGHTTP2_ERR_PAUSE` or
@ -1121,32 +1143,6 @@ typedef int (*nghttp2_on_header_callback)
const uint8_t *value, size_t valuelen,
void *user_data);
/**
* @functypedef
*
* Callback function invoked when all header name/value pairs are
* processed or after the header decompression error is detected. If
* the |error_code| is :enum:`NGHTTP2_NO_ERROR`, it indicates the
* header decompression succeeded. Otherwise, error prevented the
* completion of the header decompression. In this case, the library
* will handle the error by either transmitting RST_STREAM or GOAWAY
* and terminate session.
*
* If the |error_code| is not :enum:`NGHTTP2_NO_ERROR`, then
* :type:`nghttp2_on_request_recv_callback` will not called for this
* frame if the |frame| is HEADERS.
*
* The implementation of this function must return 0 if it
* succeeds. If nonzero value 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`.
*/
typedef int (*nghttp2_on_end_headers_callback)
(nghttp2_session *session,
const nghttp2_frame *frame,
nghttp2_error_code error_code,
void *user_data);
/**
* @struct
*
@ -1205,16 +1201,16 @@ typedef struct {
* unknown.
*/
nghttp2_on_unknown_frame_recv_callback on_unknown_frame_recv_callback;
/**
* Callback function invoked when the reception of header block in
* HEADERS or PUSH_PROMISE is started.
*/
nghttp2_on_begin_headers_callback on_begin_headers_callback;
/**
* Callback function invoked when a header name/value pair is
* received.
*/
nghttp2_on_header_callback on_header_callback;
/**
* Callback function invoked when all header name/value pairs are
* processed.
*/
nghttp2_on_end_headers_callback on_end_headers_callback;
} nghttp2_session_callbacks;
/**
@ -1462,14 +1458,19 @@ int nghttp2_session_send(nghttp2_session *session);
*
* 1. :member:`nghttp2_session_callbacks.recv_callback` is invoked
* one or more times to receive whole frame.
* 2. If the received frame is valid,
* :member:`nghttp2_session_callbacks.on_frame_recv_callback` is
* invoked. If frame is either HEADERS or PUSH_PROMISE,
*
* 2. If the received frame is valid, then following actions are
* taken. If the frame is either HEADERS or PUSH_PROMISE,
* :member:`nghttp2_session_callbacks.on_begin_headers_callback`
* is invoked. Then
* :member:`nghttp2_session_callbacks.on_header_callback` is
* invoked for each header name/value pair. After all name/value
* pairs are emitted (or decompression failed),
* :member:`nghttp2_session_callbacks.on_end_headers_callback`
* is invoked. If the frame is the final frame of the request,
* pairs are emitted successfully,
* :member:`nghttp2_session_callbacks.on_frame_recv_callback` is
* invoked. For other frames,
* :member:`nghttp2_session_callbacks.on_frame_recv_callback` is
* invoked.
* If the frame is the final frame of the request,
* :member:`nghttp2_session_callbacks.on_request_recv_callback`
* is invoked. If the reception of the frame triggers the
* closure of the stream,

View File

@ -1851,6 +1851,22 @@ static int nghttp2_session_call_on_frame_received
return 0;
}
static int session_call_on_begin_headers(nghttp2_session *session,
nghttp2_frame *frame)
{
int rv;
DEBUGF(fprintf(stderr, "Call on_begin_headers callback stream_id=%d\n",
frame->hd.stream_id));
if(session->callbacks.on_begin_headers_callback) {
rv = session->callbacks.on_begin_headers_callback(session, frame,
session->user_data);
if(rv != 0) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
}
return 0;
}
static int session_call_on_header(nghttp2_session *session,
const nghttp2_frame *frame,
const nghttp2_nv *nv)
@ -1872,22 +1888,6 @@ static int session_call_on_header(nghttp2_session *session,
return 0;
}
static int session_call_on_end_headers
(nghttp2_session *session, nghttp2_frame *frame, nghttp2_error_code error_code)
{
int rv;
DEBUGF(fprintf(stderr, "Call on_end_headers callback stream_id=%d\n",
frame->hd.stream_id));
if(session->callbacks.on_end_headers_callback) {
rv = session->callbacks.on_end_headers_callback(session, frame, error_code,
session->user_data);
if(rv != 0) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
}
return 0;
}
/*
* Checks whether received stream_id is valid.
* This function returns 1 if it succeeds, or 0.
@ -1931,12 +1931,12 @@ static int nghttp2_session_validate_request_headers(nghttp2_session *session,
* bytes. If this function returns NGHTTP2_ERR_PAUSE, the caller must
* call this function again, until it returns 0 or one of negative
* error code. If |call_header_cb| is zero, the on_header_callback
* and on_end_headers_callback are not invoked and the function never
* return NGHTTP2_ERR_PAUSE. If the given |in| is the last chunk of
* header block, the |final| must be nonzero. If header block is
* successfully processed (which is indicated by the return value 0,
* NGHTTP2_ERR_PAUSE or NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE), the
* number of processed input bytes is assigned to the |*readlen_ptr|.
* are not invoked and the function never return NGHTTP2_ERR_PAUSE. If
* the given |in| is the last chunk of header block, the |final| must
* be nonzero. If header block is successfully processed (which is
* indicated by the return value 0, NGHTTP2_ERR_PAUSE or
* NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE), the number of processed
* input bytes is assigned to the |*readlen_ptr|.
*
* This function return 0 if it succeeds, or one of the negative error
* codes:
@ -1973,13 +1973,6 @@ static ssize_t inflate_header_block(nghttp2_session *session,
return rv;
}
if(rv < 0) {
if(call_header_cb) {
rv = session_call_on_end_headers(session, frame,
NGHTTP2_COMPRESSION_ERROR);
if(rv != 0) {
return rv;
}
}
rv = nghttp2_session_terminate_session(session,
NGHTTP2_COMPRESSION_ERROR);
if(rv != 0) {
@ -2000,14 +1993,9 @@ static ssize_t inflate_header_block(nghttp2_session *session,
}
if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
nghttp2_hd_inflate_end_headers(&session->hd_inflater);
if(call_header_cb) {
rv = session_call_on_end_headers(session, frame, NGHTTP2_NO_ERROR);
if(rv != 0) {
return rv;
}
}
break;
} else if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) {
}
if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) {
break;
}
}
@ -2107,15 +2095,11 @@ static int nghttp2_session_inflate_handle_invalid_connection
* Out of memory.
*/
int nghttp2_session_end_request_headers_received(nghttp2_session *session,
nghttp2_frame *frame)
nghttp2_frame *frame,
nghttp2_stream *stream)
{
int rv;
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
nghttp2_stream *stream;
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
if(!stream) {
return 0;
}
nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
rv = nghttp2_session_call_on_request_recv(session, frame->hd.stream_id);
if(rv != 0) {
@ -2140,17 +2124,13 @@ int nghttp2_session_end_request_headers_received(nghttp2_session *session,
* Out of memory.
*/
int nghttp2_session_end_response_headers_received(nghttp2_session *session,
nghttp2_frame *frame)
nghttp2_frame *frame,
nghttp2_stream *stream)
{
int rv;
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
/* This is the last frame of this stream, so disallow
further receptions. */
nghttp2_stream *stream;
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
if(!stream) {
return 0;
}
nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
if(nghttp2_is_fatal(rv)) {
@ -2174,11 +2154,11 @@ int nghttp2_session_end_response_headers_received(nghttp2_session *session,
* Out of memory.
*/
int nghttp2_session_end_headers_received(nghttp2_session *session,
nghttp2_frame *frame)
nghttp2_frame *frame,
nghttp2_stream *stream)
{
int rv;
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
nghttp2_stream *stream;
if(!nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
rv = nghttp2_session_call_on_request_recv(session,
frame->hd.stream_id);
@ -2186,10 +2166,6 @@ int nghttp2_session_end_headers_received(nghttp2_session *session,
return rv;
}
}
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
if(!stream) {
return 0;
}
nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
if(nghttp2_is_fatal(rv)) {
@ -2199,17 +2175,35 @@ int nghttp2_session_end_headers_received(nghttp2_session *session,
return 0;
}
static int session_post_process_headers_frame(nghttp2_session *session)
static int session_after_header_block_received(nghttp2_session *session)
{
int rv;
nghttp2_frame *frame = &session->iframe.frame;
nghttp2_stream *stream;
/* We call on_frame_recv_callback regardless of the existence of
stream */
rv = nghttp2_session_call_on_frame_received(session, frame);
if(nghttp2_is_fatal(rv)) {
return rv;
}
if(frame->hd.type != NGHTTP2_HEADERS) {
return 0;
}
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
if(!stream) {
return 0;
}
switch(frame->headers.cat) {
case NGHTTP2_HCAT_REQUEST:
return nghttp2_session_end_request_headers_received(session, frame);
return nghttp2_session_end_request_headers_received
(session, frame, stream);
case NGHTTP2_HCAT_RESPONSE:
case NGHTTP2_HCAT_PUSH_RESPONSE:
return nghttp2_session_end_response_headers_received(session, frame);
return nghttp2_session_end_response_headers_received
(session, frame, stream);
case NGHTTP2_HCAT_HEADERS:
return nghttp2_session_end_headers_received(session, frame);
return nghttp2_session_end_headers_received(session, frame, stream);
default:
assert(0);
}
@ -2256,7 +2250,7 @@ int nghttp2_session_on_request_headers_received(nghttp2_session *session,
return NGHTTP2_ERR_NOMEM;
}
session->last_proc_stream_id = session->last_recv_stream_id;
rv = nghttp2_session_call_on_frame_received(session, frame);
rv = session_call_on_begin_headers(session, frame);
if(rv != 0) {
return rv;
}
@ -2287,7 +2281,7 @@ int nghttp2_session_on_response_headers_received(nghttp2_session *session,
(session, frame, NGHTTP2_STREAM_CLOSED);
}
stream->state = NGHTTP2_STREAM_OPENED;
rv = nghttp2_session_call_on_frame_received(session, frame);
rv = session_call_on_begin_headers(session, frame);
if(rv != 0) {
return rv;
}
@ -2314,7 +2308,7 @@ int nghttp2_session_on_push_response_headers_received(nghttp2_session *session,
}
nghttp2_stream_promise_fulfilled(stream);
++session->num_incoming_streams;
rv = nghttp2_session_call_on_frame_received(session, frame);
rv = session_call_on_begin_headers(session, frame);
if(rv != 0) {
return rv;
}
@ -2350,7 +2344,7 @@ int nghttp2_session_on_headers_received(nghttp2_session *session,
}
if(nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
if(stream->state == NGHTTP2_STREAM_OPENED) {
r = nghttp2_session_call_on_frame_received(session, frame);
r = session_call_on_begin_headers(session, frame);
if(r != 0) {
return r;
}
@ -2370,7 +2364,7 @@ int nghttp2_session_on_headers_received(nghttp2_session *session,
NGHTTP2_STREAM_CLOSING, we discard the frame. This is a race
condition. */
if(stream->state != NGHTTP2_STREAM_CLOSING) {
r = nghttp2_session_call_on_frame_received(session, frame);
r = session_call_on_begin_headers(session, frame);
if(r != 0) {
return r;
}
@ -2945,7 +2939,7 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session,
return NGHTTP2_ERR_NOMEM;
}
session->last_proc_stream_id = session->last_recv_stream_id;
rv = nghttp2_session_call_on_frame_received(session, frame);
rv = session_call_on_begin_headers(session, frame);
if(rv != 0) {
return rv;
}
@ -3650,8 +3644,8 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
}
if(rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
/* The application says no more headers. We decompress the
rest of the header block but not invoke
on_header_callback and on_end_headers_callback. */
rest of the header block but not invoke on_header_callback
and on_frame_recv_callback. */
rv = nghttp2_session_add_rst_stream(session,
iframe->frame.hd.stream_id,
NGHTTP2_INTERNAL_ERROR);
@ -3674,7 +3668,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
break;
}
if(iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) {
rv = session_post_process_headers_frame(session);
rv = session_after_header_block_received(session);
if(nghttp2_is_fatal(rv)) {
return rv;
}
@ -3785,13 +3779,6 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
if(nghttp2_is_fatal(rv)) {
return rv;
}
rv = session_call_on_end_headers(session, &iframe->frame,
NGHTTP2_PROTOCOL_ERROR);
if(nghttp2_is_fatal(rv)) {
return rv;
}
/* Mark inflater bad so that we won't perform further decoding */
session->hd_inflater.ctx.bad = 1;
busy = 1;

View File

@ -359,13 +359,16 @@ int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session,
int nghttp2_session_end_request_headers_received(nghttp2_session *session,
nghttp2_frame *frame);
nghttp2_frame *frame,
nghttp2_stream *stream);
int nghttp2_session_end_response_headers_received(nghttp2_session *session,
nghttp2_frame *frame);
nghttp2_frame *frame,
nghttp2_stream *stream);
int nghttp2_session_end_headers_received(nghttp2_session *session,
nghttp2_frame *frame);
nghttp2_frame *frame,
nghttp2_stream *stream);
int nghttp2_session_on_request_headers_received(nghttp2_session *session,
nghttp2_frame *frame);

View File

@ -65,7 +65,6 @@ const std::string NGHTTPD_SERVER = "nghttpd nghttp2/" NGHTTP2_VERSION;
Config::Config()
: data_ptr(nullptr),
on_request_recv_callback(nullptr),
output_upper_thres(1024*1024),
header_table_size(-1),
port(0),
@ -801,45 +800,17 @@ int on_header_callback(nghttp2_session *session,
} // namespace
namespace {
int on_end_headers_callback(nghttp2_session *session,
int on_begin_headers_callback(nghttp2_session *session,
const nghttp2_frame *frame,
nghttp2_error_code error_code,
void *user_data)
{
if(error_code != NGHTTP2_NO_ERROR) {
return 0;
}
auto hd = static_cast<Http2Handler*>(user_data);
if(frame->hd.type != NGHTTP2_HEADERS ||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
return 0;
}
auto hd = static_cast<Http2Handler*>(user_data);
auto stream = hd->get_stream(frame->hd.stream_id);
if(!stream) {
return 0;
}
http2::normalize_headers(stream->headers);
if(!http2::check_http2_headers(stream->headers)) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
NGHTTP2_PROTOCOL_ERROR);
return 0;
}
for(size_t i = 0; REQUIRED_HEADERS[i]; ++i) {
if(!http2::get_unique_header(stream->headers, REQUIRED_HEADERS[i])) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
frame->hd.stream_id, NGHTTP2_PROTOCOL_ERROR);
return 0;
}
}
// intermediary translating from HTTP/1 request to HTTP/2 may
// not produce :authority header field. In this case, it should
// provide host HTTP/1.1 header field.
if(!http2::get_unique_header(stream->headers, ":authority") &&
!http2::get_unique_header(stream->headers, "host")) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
NGHTTP2_PROTOCOL_ERROR);
return 0;
}
auto stream = util::make_unique<Request>(frame->hd.stream_id);
hd->add_stream(frame->hd.stream_id, std::move(stream));
return 0;
}
} // namespace
@ -856,12 +827,49 @@ int hd_on_frame_recv_callback
switch(frame->hd.type) {
case NGHTTP2_DATA:
// TODO Handle POST
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
auto stream = hd->get_stream(frame->hd.stream_id);
if(!stream) {
return 0;
}
prepare_response(stream, hd);
}
break;
case NGHTTP2_HEADERS:
switch(frame->headers.cat) {
case NGHTTP2_HCAT_REQUEST: {
auto req = util::make_unique<Request>(frame->hd.stream_id);
hd->add_stream(frame->hd.stream_id, std::move(req));
auto stream = hd->get_stream(frame->hd.stream_id);
if(!stream) {
return 0;
}
http2::normalize_headers(stream->headers);
if(!http2::check_http2_headers(stream->headers)) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
frame->hd.stream_id,
NGHTTP2_PROTOCOL_ERROR);
return 0;
}
for(size_t i = 0; REQUIRED_HEADERS[i]; ++i) {
if(!http2::get_unique_header(stream->headers, REQUIRED_HEADERS[i])) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
frame->hd.stream_id,
NGHTTP2_PROTOCOL_ERROR);
return 0;
}
}
// intermediary translating from HTTP/1 request to HTTP/2 may
// not produce :authority header field. In this case, it should
// provide host HTTP/1.1 header field.
if(!http2::get_unique_header(stream->headers, ":authority") &&
!http2::get_unique_header(stream->headers, "host")) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
frame->hd.stream_id,
NGHTTP2_PROTOCOL_ERROR);
return 0;
}
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
prepare_response(stream, hd);
}
break;
}
default:
@ -885,17 +893,6 @@ int hd_on_frame_recv_callback
}
} // namespace
int htdocs_on_request_recv_callback
(nghttp2_session *session, int32_t stream_id, void *user_data)
{
auto hd = static_cast<Http2Handler*>(user_data);
auto stream = hd->get_stream(stream_id);
if(stream) {
prepare_response(stream, hd);
}
return 0;
}
namespace {
int hd_before_frame_send_callback
(nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
@ -977,9 +974,8 @@ void fill_callback(nghttp2_session_callbacks& callbacks, const Config *config)
verbose_on_unknown_frame_recv_callback;
}
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
callbacks.on_request_recv_callback = config->on_request_recv_callback;
callbacks.on_header_callback = on_header_callback;
callbacks.on_end_headers_callback = on_end_headers_callback;
callbacks.on_begin_headers_callback = on_begin_headers_callback;
}
} // namespace

View File

@ -55,7 +55,6 @@ struct Config {
std::string private_key_file;
std::string cert_file;
void *data_ptr;
nghttp2_on_request_recv_callback on_request_recv_callback;
size_t output_upper_thres;
ssize_t header_table_size;
uint16_t port;
@ -144,9 +143,6 @@ private:
const Config *config_;
};
int htdocs_on_request_recv_callback
(nghttp2_session *session, int32_t stream_id, void *user_data);
ssize_t file_read_callback
(nghttp2_session *session, int32_t stream_id,
uint8_t *buf, size_t length, int *eof,

View File

@ -1176,30 +1176,6 @@ int on_header_callback(nghttp2_session *session,
}
} // namespace
namespace {
int on_end_headers_callback(nghttp2_session *session,
const nghttp2_frame *frame,
nghttp2_error_code error_code,
void *user_data)
{
if(error_code != NGHTTP2_NO_ERROR) {
return 0;
}
if(frame->hd.type != NGHTTP2_HEADERS ||
frame->headers.cat != NGHTTP2_HCAT_RESPONSE) {
return 0;
}
auto req = (Request*)nghttp2_session_get_stream_user_data
(session, frame->hd.stream_id);
if(!req) {
// Server-pushed stream does not have stream user data
return 0;
}
check_response_header(session, req);
return 0;
}
} // namespace
namespace {
int on_frame_recv_callback2
(nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
@ -1212,9 +1188,9 @@ int on_frame_recv_callback2
// req is nullptr.
if(req) {
req->record_response_time();
check_response_header(session, req);
}
}
if(frame->hd.type == NGHTTP2_SETTINGS &&
} else if(frame->hd.type == NGHTTP2_SETTINGS &&
(frame->hd.flags & NGHTTP2_FLAG_ACK)) {
auto client = get_session(user_data);
if(client->settings_timerev) {
@ -1603,7 +1579,6 @@ int run(char **uris, int n)
}
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
callbacks.on_header_callback = on_header_callback;
callbacks.on_end_headers_callback = on_end_headers_callback;
std::string prev_scheme;
std::string prev_host;

View File

@ -236,7 +236,6 @@ int main(int argc, char **argv)
SSL_load_error_strings();
SSL_library_init();
reset_timer();
config.on_request_recv_callback = htdocs_on_request_recv_callback;
HttpServer server(&config);
server.run();

View File

@ -833,20 +833,44 @@ int on_header_callback(nghttp2_session *session,
} // namespace
namespace {
int on_end_headers_callback(nghttp2_session *session,
int on_begin_headers_callback(nghttp2_session *session,
const nghttp2_frame *frame,
nghttp2_error_code error_code,
void *user_data)
{
if(error_code != NGHTTP2_NO_ERROR) {
return 0;
}
if(frame->hd.type != NGHTTP2_HEADERS ||
frame->headers.cat != NGHTTP2_HCAT_RESPONSE) {
return 0;
}
int rv;
auto http2session = static_cast<Http2Session*>(user_data);
if(frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
// server sends request HEADERS
http2session->submit_rst_stream(frame->hd.stream_id,
NGHTTP2_REFUSED_STREAM);
return 0;
}
if(frame->headers.cat != NGHTTP2_HCAT_RESPONSE) {
return 0;
}
auto sd = static_cast<StreamData*>
(nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
if(!sd || !sd->dconn) {
http2session->submit_rst_stream(frame->hd.stream_id,
NGHTTP2_INTERNAL_ERROR);
return 0;
}
auto downstream = sd->dconn->get_downstream();
if(!downstream ||
downstream->get_downstream_stream_id() != frame->hd.stream_id) {
http2session->submit_rst_stream(frame->hd.stream_id,
NGHTTP2_INTERNAL_ERROR);
return 0;
}
return 0;
}
} // namespace
namespace {
int on_response_headers(Http2Session *http2session,
nghttp2_session *session,
const nghttp2_frame *frame)
{
int rv;
auto sd = static_cast<StreamData*>
(nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
if(!sd || !sd->dconn) {
@ -952,32 +976,8 @@ int on_frame_recv_callback
{
auto http2session = static_cast<Http2Session*>(user_data);
switch(frame->hd.type) {
case NGHTTP2_HEADERS: {
if(frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
// server sends request HEADERS
http2session->submit_rst_stream(frame->hd.stream_id,
NGHTTP2_REFUSED_STREAM);
break;
}
if(frame->headers.cat != NGHTTP2_HCAT_RESPONSE) {
break;
}
auto sd = static_cast<StreamData*>
(nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
if(!sd || !sd->dconn) {
http2session->submit_rst_stream(frame->hd.stream_id,
NGHTTP2_INTERNAL_ERROR);
break;
}
auto downstream = sd->dconn->get_downstream();
if(!downstream ||
downstream->get_downstream_stream_id() != frame->hd.stream_id) {
http2session->submit_rst_stream(frame->hd.stream_id,
NGHTTP2_INTERNAL_ERROR);
break;
}
break;
}
case NGHTTP2_HEADERS:
return on_response_headers(http2session, session, frame);
case NGHTTP2_RST_STREAM: {
auto sd = static_cast<StreamData*>
(nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
@ -1192,7 +1192,7 @@ int Http2Session::on_connect()
callbacks.on_frame_not_send_callback = on_frame_not_send_callback;
callbacks.on_unknown_frame_recv_callback = on_unknown_frame_recv_callback;
callbacks.on_header_callback = on_header_callback;
callbacks.on_end_headers_callback = on_end_headers_callback;
callbacks.on_begin_headers_callback = on_begin_headers_callback;
nghttp2_opt_set opt_set;
opt_set.no_auto_stream_window_update = 1;

View File

@ -230,20 +230,35 @@ int on_header_callback(nghttp2_session *session,
} // namespace
namespace {
int on_end_headers_callback(nghttp2_session *session,
int on_begin_headers_callback(nghttp2_session *session,
const nghttp2_frame *frame,
nghttp2_error_code error_code,
void *user_data)
{
if(error_code != NGHTTP2_NO_ERROR) {
return 0;
}
if(frame->hd.type != NGHTTP2_HEADERS ||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
return 0;
}
int rv;
auto upstream = static_cast<Http2Upstream*>(user_data);
if(frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
return 0;
}
if(LOG_ENABLED(INFO)) {
ULOG(INFO, upstream) << "Received upstream request HEADERS stream_id="
<< frame->hd.stream_id;
}
auto downstream = new Downstream(upstream,
frame->hd.stream_id,
frame->headers.pri);
upstream->add_downstream(downstream);
downstream->init_response_body_buf();
return 0;
}
} // namespace
namespace {
int on_request_headers(Http2Upstream *upstream,
nghttp2_session *session,
const nghttp2_frame *frame)
{
int rv;
auto downstream = upstream->find_downstream(frame->hd.stream_id);
if(!downstream) {
return 0;
@ -357,21 +372,8 @@ int on_frame_recv_callback
}
break;
}
case NGHTTP2_HEADERS: {
if(frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
break;
}
if(LOG_ENABLED(INFO)) {
ULOG(INFO, upstream) << "Received upstream request HEADERS stream_id="
<< frame->hd.stream_id;
}
auto downstream = new Downstream(upstream,
frame->hd.stream_id,
frame->headers.pri);
upstream->add_downstream(downstream);
downstream->init_response_body_buf();
break;
}
case NGHTTP2_HEADERS:
return on_request_headers(upstream, session, frame);
case NGHTTP2_PRIORITY: {
auto downstream = upstream->find_downstream(frame->hd.stream_id);
if(!downstream) {
@ -505,7 +507,7 @@ Http2Upstream::Http2Upstream(ClientHandler *handler)
callbacks.on_frame_not_send_callback = on_frame_not_send_callback;
callbacks.on_unknown_frame_recv_callback = on_unknown_frame_recv_callback;
callbacks.on_header_callback = on_header_callback;
callbacks.on_end_headers_callback = on_end_headers_callback;
callbacks.on_begin_headers_callback = on_begin_headers_callback;
nghttp2_opt_set opt_set;
opt_set.no_auto_stream_window_update = 1;

View File

@ -69,7 +69,7 @@ typedef struct {
const nghttp2_frame *frame;
size_t fixed_sendlen;
int header_cb_called;
int end_headers_cb_called;
int begin_headers_cb_called;
nghttp2_nv nv;
} my_user_data;
@ -298,13 +298,12 @@ static int pause_on_header_callback(nghttp2_session *session,
return NGHTTP2_ERR_PAUSE;
}
static int on_end_headers_callback(nghttp2_session *session,
static int on_begin_headers_callback(nghttp2_session *session,
const nghttp2_frame *frame,
nghttp2_error_code error_code,
void *user_data)
{
my_user_data *ud = (my_user_data*)user_data;
++ud->end_headers_cb_called;
++ud->begin_headers_cb_called;
return 0;
}
@ -674,7 +673,7 @@ void test_nghttp2_session_recv_continuation(void)
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
callbacks.on_header_callback = on_header_callback;
callbacks.on_end_headers_callback = on_end_headers_callback;
callbacks.on_begin_headers_callback = on_begin_headers_callback;
nghttp2_session_server_new(&session, &callbacks, &ud);
@ -753,11 +752,11 @@ void test_nghttp2_session_recv_continuation(void)
memcpy(data + datalen, framedata, framedatalen);
datalen += framedatalen;
ud.end_headers_cb_called = 0;
ud.begin_headers_cb_called = 0;
rv = nghttp2_session_mem_recv(session, data, datalen);
CU_ASSERT((ssize_t)datalen == rv);
CU_ASSERT(1 == ud.end_headers_cb_called);
CU_ASSERT(1 == ud.begin_headers_cb_called);
CU_ASSERT(NGHTTP2_GOAWAY ==
OB_CTRL_TYPE(nghttp2_session_get_next_ob_item(session)));
@ -798,7 +797,7 @@ void test_nghttp2_session_continue(void)
callbacks.on_frame_recv_callback = on_frame_recv_callback;
callbacks.on_data_chunk_recv_callback = pause_on_data_chunk_recv_callback;
callbacks.on_header_callback = pause_on_header_callback;
callbacks.on_end_headers_callback = on_end_headers_callback;
callbacks.on_begin_headers_callback = on_begin_headers_callback;
nghttp2_session_server_new(&session, &callbacks, &user_data);
@ -827,8 +826,8 @@ void test_nghttp2_session_continue(void)
buflen = framelen1 + framelen2;
/* Receive 1st HEADERS and pause */
user_data.begin_headers_cb_called = 0;
user_data.header_cb_called = 0;
user_data.end_headers_cb_called = 0;
rv = nghttp2_session_mem_recv(session, bufp, buflen);
bufp += rv;
@ -839,27 +838,27 @@ void test_nghttp2_session_continue(void)
CU_ASSERT((size_t)framelen1 - NGHTTP2_FRAME_HEAD_LENGTH ==
recv_frame->hd.length);
CU_ASSERT(1 == user_data.begin_headers_cb_called);
CU_ASSERT(1 == user_data.header_cb_called);
CU_ASSERT(0 == user_data.end_headers_cb_called);
CU_ASSERT(nghttp2_nv_equal(&nv1[0], &user_data.nv));
/* get 2nd header field */
user_data.begin_headers_cb_called = 0;
user_data.header_cb_called = 0;
user_data.end_headers_cb_called = 0;
rv = nghttp2_session_mem_recv(session, bufp, buflen);
bufp += rv;
buflen -= rv;
CU_ASSERT(0 == user_data.begin_headers_cb_called);
CU_ASSERT(1 == user_data.header_cb_called);
CU_ASSERT(0 == user_data.end_headers_cb_called);
CU_ASSERT(nghttp2_nv_equal(&nv1[1], &user_data.nv));
/* will call end_headers_callback and receive 2nd HEADERS and pause */
user_data.begin_headers_cb_called = 0;
user_data.header_cb_called = 0;
user_data.end_headers_cb_called = 0;
rv = nghttp2_session_mem_recv(session, bufp, buflen);
bufp += rv;
@ -870,34 +869,36 @@ void test_nghttp2_session_continue(void)
CU_ASSERT((size_t)framelen2 - NGHTTP2_FRAME_HEAD_LENGTH ==
recv_frame->hd.length);
CU_ASSERT(1 == user_data.begin_headers_cb_called);
CU_ASSERT(1 == user_data.header_cb_called);
CU_ASSERT(1 == user_data.end_headers_cb_called);
CU_ASSERT(nghttp2_nv_equal(&nv2[0], &user_data.nv));
/* get 2nd header field */
user_data.begin_headers_cb_called = 0;
user_data.header_cb_called = 0;
user_data.end_headers_cb_called = 0;
rv = nghttp2_session_mem_recv(session, bufp, buflen);
bufp += rv;
buflen -= rv;
CU_ASSERT(0 == user_data.begin_headers_cb_called);
CU_ASSERT(1 == user_data.header_cb_called);
CU_ASSERT(0 == user_data.end_headers_cb_called);
CU_ASSERT(nghttp2_nv_equal(&nv2[1], &user_data.nv));
/* No input data, just call on_end_headers_callback */
/* No input data, frame_recv_callback is called */
user_data.begin_headers_cb_called = 0;
user_data.header_cb_called = 0;
user_data.end_headers_cb_called = 0;
user_data.frame_recv_cb_called = 0;
rv = nghttp2_session_mem_recv(session, bufp, buflen);
bufp += rv;
buflen -= rv;
CU_ASSERT(0 == user_data.begin_headers_cb_called);
CU_ASSERT(0 == user_data.header_cb_called);
CU_ASSERT(1 == user_data.end_headers_cb_called);
CU_ASSERT(1 == user_data.frame_recv_cb_called);
/* Receive DATA */
data_hd.length = 16;
@ -989,10 +990,8 @@ void test_nghttp2_session_on_request_headers_received(void)
size_t nvlen;
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
callbacks.on_frame_recv_callback = on_frame_recv_callback;
callbacks.on_begin_headers_callback = on_begin_headers_callback;
callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback;
user_data.frame_recv_cb_called = 0;
user_data.invalid_frame_recv_cb_called = 0;
nghttp2_session_server_new(&session, &callbacks, &user_data);
memset(session->iframe.buf, 0, 4);
@ -1001,8 +1000,11 @@ void test_nghttp2_session_on_request_headers_received(void)
NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY,
stream_id, 1 << 20, NULL, 0);
user_data.begin_headers_cb_called = 0;
user_data.invalid_frame_recv_cb_called = 0;
CU_ASSERT(0 == nghttp2_session_on_request_headers_received(session, &frame));
CU_ASSERT(1 == user_data.frame_recv_cb_called);
CU_ASSERT(1 == user_data.begin_headers_cb_called);
stream = nghttp2_session_get_stream(session, stream_id);
CU_ASSERT(NGHTTP2_STREAM_OPENING == stream->state);
CU_ASSERT(1 << 20 == stream->pri);
@ -1047,10 +1049,10 @@ void test_nghttp2_session_on_request_headers_received(void)
nghttp2_frame_headers_init(&frame.headers,
NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY,
1, 1 << 20, nva, nvlen);
user_data.frame_recv_cb_called = 0;
user_data.begin_headers_cb_called = 0;
user_data.invalid_frame_recv_cb_called = 0;
CU_ASSERT(0 == nghttp2_session_on_request_headers_received(session, &frame));
CU_ASSERT(1 == user_data.frame_recv_cb_called);
CU_ASSERT(1 == user_data.begin_headers_cb_called);
CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called);
nghttp2_frame_headers_free(&frame.headers);
@ -1067,10 +1069,8 @@ void test_nghttp2_session_on_response_headers_received(void)
nghttp2_stream *stream;
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
callbacks.on_frame_recv_callback = on_frame_recv_callback;
callbacks.on_begin_headers_callback = on_begin_headers_callback;
callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback;
user_data.frame_recv_cb_called = 0;
user_data.invalid_frame_recv_cb_called = 0;
nghttp2_session_client_new(&session, &callbacks, &user_data);
stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE,
@ -1079,9 +1079,12 @@ void test_nghttp2_session_on_response_headers_received(void)
nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1,
NGHTTP2_PRI_DEFAULT, NULL, 0);
user_data.begin_headers_cb_called = 0;
user_data.invalid_frame_recv_cb_called = 0;
CU_ASSERT(0 == nghttp2_session_on_response_headers_received
(session, &frame, stream));
CU_ASSERT(1 == user_data.frame_recv_cb_called);
CU_ASSERT(1 == user_data.begin_headers_cb_called);
CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state);
nghttp2_frame_headers_free(&frame.headers);
@ -1097,10 +1100,8 @@ void test_nghttp2_session_on_headers_received(void)
nghttp2_stream *stream;
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
callbacks.on_frame_recv_callback = on_frame_recv_callback;
callbacks.on_begin_headers_callback = on_begin_headers_callback;
callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback;
user_data.frame_recv_cb_called = 0;
user_data.invalid_frame_recv_cb_called = 0;
nghttp2_session_client_new(&session, &callbacks, &user_data);
stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE,
@ -1110,15 +1111,18 @@ void test_nghttp2_session_on_headers_received(void)
nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1,
NGHTTP2_PRI_DEFAULT, NULL, 0);
user_data.begin_headers_cb_called = 0;
user_data.invalid_frame_recv_cb_called = 0;
CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame, stream));
CU_ASSERT(1 == user_data.frame_recv_cb_called);
CU_ASSERT(1 == user_data.begin_headers_cb_called);
CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state);
/* stream closed */
frame.hd.flags |= NGHTTP2_FLAG_END_STREAM;
CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame, stream));
CU_ASSERT(2 == user_data.frame_recv_cb_called);
CU_ASSERT(2 == user_data.begin_headers_cb_called);
/* Check to see when NGHTTP2_STREAM_CLOSING, incoming HEADERS is
discarded. */
@ -1130,7 +1134,7 @@ void test_nghttp2_session_on_headers_received(void)
CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
nghttp2_session_on_headers_received(session, &frame, stream));
/* See no counters are updated */
CU_ASSERT(2 == user_data.frame_recv_cb_called);
CU_ASSERT(2 == user_data.begin_headers_cb_called);
CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called);
/* Server initiated stream */
@ -1143,7 +1147,7 @@ void test_nghttp2_session_on_headers_received(void)
frame.hd.stream_id = 2;
CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame, stream));
CU_ASSERT(3 == user_data.frame_recv_cb_called);
CU_ASSERT(3 == user_data.begin_headers_cb_called);
CU_ASSERT(NGHTTP2_STREAM_OPENING == stream->state);
nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
@ -1168,10 +1172,8 @@ void test_nghttp2_session_on_push_response_headers_received(void)
nghttp2_outbound_item *item;
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
callbacks.on_frame_recv_callback = on_frame_recv_callback;
callbacks.on_begin_headers_callback = on_begin_headers_callback;
callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback;
user_data.frame_recv_cb_called = 0;
user_data.invalid_frame_recv_cb_called = 0;
nghttp2_session_client_new(&session, &callbacks, &user_data);
stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE,
@ -1182,9 +1184,13 @@ void test_nghttp2_session_on_push_response_headers_received(void)
/* nghttp2_session_on_push_response_headers_received assumes
stream's state is NGHTTP2_STREAM_RESERVED and session->server is
0. */
user_data.begin_headers_cb_called = 0;
user_data.invalid_frame_recv_cb_called = 0;
CU_ASSERT(0 == nghttp2_session_on_push_response_headers_received
(session, &frame, stream));
CU_ASSERT(1 == user_data.frame_recv_cb_called);
CU_ASSERT(1 == user_data.begin_headers_cb_called);
CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state);
CU_ASSERT(1 == session->num_incoming_streams);
@ -1407,10 +1413,8 @@ void test_nghttp2_session_on_push_promise_received(void)
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
callbacks.send_callback = null_send_callback;
callbacks.on_frame_recv_callback = on_frame_recv_callback;
callbacks.on_begin_headers_callback = on_begin_headers_callback;
callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback;
user_data.frame_recv_cb_called = 0;
user_data.invalid_frame_recv_cb_called = 0;
nghttp2_session_client_new(&session, &callbacks, &user_data);
memset(session->iframe.buf, 0, 4);
@ -1422,9 +1426,12 @@ void test_nghttp2_session_on_push_promise_received(void)
NGHTTP2_FLAG_END_PUSH_PROMISE, 1, 2,
NULL, 0);
user_data.begin_headers_cb_called = 0;
user_data.invalid_frame_recv_cb_called = 0;
CU_ASSERT(0 == nghttp2_session_on_push_promise_received(session, &frame));
CU_ASSERT(1 == user_data.frame_recv_cb_called);
CU_ASSERT(1 == user_data.begin_headers_cb_called);
promised_stream = nghttp2_session_get_stream(session, 2);
CU_ASSERT(NGHTTP2_STREAM_RESERVED == promised_stream->state);
CU_ASSERT(2 == session->last_recv_stream_id);
@ -1433,12 +1440,12 @@ void test_nghttp2_session_on_push_promise_received(void)
nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
frame.push_promise.promised_stream_id = 4;
user_data.frame_recv_cb_called = 0;
user_data.begin_headers_cb_called = 0;
user_data.invalid_frame_recv_cb_called = 0;
CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
nghttp2_session_on_push_promise_received(session, &frame));
CU_ASSERT(0 == user_data.frame_recv_cb_called);
CU_ASSERT(0 == user_data.begin_headers_cb_called);
CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called);
CU_ASSERT(NULL == nghttp2_session_get_stream(session, 4));
item = nghttp2_session_get_next_ob_item(session);
@ -1453,12 +1460,12 @@ void test_nghttp2_session_on_push_promise_received(void)
stream->state = NGHTTP2_STREAM_CLOSING;
frame.push_promise.promised_stream_id = 6;
user_data.frame_recv_cb_called = 0;
user_data.begin_headers_cb_called = 0;
user_data.invalid_frame_recv_cb_called = 0;
CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
nghttp2_session_on_push_promise_received(session, &frame));
CU_ASSERT(0 == user_data.frame_recv_cb_called);
CU_ASSERT(0 == user_data.begin_headers_cb_called);
CU_ASSERT(NULL == nghttp2_session_get_stream(session, 6));
item = nghttp2_session_get_next_ob_item(session);
CU_ASSERT(NGHTTP2_RST_STREAM == OB_CTRL_TYPE(item));
@ -1470,12 +1477,12 @@ void test_nghttp2_session_on_push_promise_received(void)
frame.hd.stream_id = 3;
frame.push_promise.promised_stream_id = 8;
user_data.frame_recv_cb_called = 0;
user_data.begin_headers_cb_called = 0;
user_data.invalid_frame_recv_cb_called = 0;
CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
nghttp2_session_on_push_promise_received(session, &frame));
CU_ASSERT(0 == user_data.frame_recv_cb_called);
CU_ASSERT(0 == user_data.begin_headers_cb_called);
CU_ASSERT(NULL == nghttp2_session_get_stream(session, 8));
item = nghttp2_session_get_next_ob_item(session);
CU_ASSERT(NGHTTP2_RST_STREAM == OB_CTRL_TYPE(item));
@ -1486,12 +1493,12 @@ void test_nghttp2_session_on_push_promise_received(void)
/* Same ID twice */
stream->state = NGHTTP2_STREAM_OPENING;
user_data.frame_recv_cb_called = 0;
user_data.begin_headers_cb_called = 0;
user_data.invalid_frame_recv_cb_called = 0;
CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
nghttp2_session_on_push_promise_received(session, &frame));
CU_ASSERT(0 == user_data.frame_recv_cb_called);
CU_ASSERT(0 == user_data.begin_headers_cb_called);
CU_ASSERT(NULL == nghttp2_session_get_stream(session, 8));
item = nghttp2_session_get_next_ob_item(session);
CU_ASSERT(NGHTTP2_GOAWAY == OB_CTRL_TYPE(item));
@ -1501,12 +1508,12 @@ void test_nghttp2_session_on_push_promise_received(void)
/* After GOAWAY, PUSH_PROMISE will be discarded */
frame.push_promise.promised_stream_id = 10;
user_data.frame_recv_cb_called = 0;
user_data.begin_headers_cb_called = 0;
user_data.invalid_frame_recv_cb_called = 0;
CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
nghttp2_session_on_push_promise_received(session, &frame));
CU_ASSERT(0 == user_data.frame_recv_cb_called);
CU_ASSERT(0 == user_data.begin_headers_cb_called);
CU_ASSERT(NULL == nghttp2_session_get_stream(session, 10));
CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
@ -1524,12 +1531,12 @@ void test_nghttp2_session_on_push_promise_received(void)
NGHTTP2_FLAG_END_PUSH_PROMISE, 2, 4,
NULL, 0);
user_data.frame_recv_cb_called = 0;
user_data.begin_headers_cb_called = 0;
user_data.invalid_frame_recv_cb_called = 0;
CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
nghttp2_session_on_push_promise_received(session, &frame));
CU_ASSERT(0 == user_data.frame_recv_cb_called);
CU_ASSERT(0 == user_data.begin_headers_cb_called);
CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called);
nghttp2_frame_push_promise_free(&frame.push_promise);
@ -1549,12 +1556,12 @@ void test_nghttp2_session_on_push_promise_received(void)
NGHTTP2_FLAG_END_PUSH_PROMISE, 1, 2,
NULL, 0);
user_data.frame_recv_cb_called = 0;
user_data.begin_headers_cb_called = 0;
user_data.invalid_frame_recv_cb_called = 0;
CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK ==
nghttp2_session_on_push_promise_received(session, &frame));
CU_ASSERT(0 == user_data.frame_recv_cb_called);
CU_ASSERT(0 == user_data.begin_headers_cb_called);
CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called);
nghttp2_frame_push_promise_free(&frame.push_promise);
@ -1571,11 +1578,11 @@ void test_nghttp2_session_on_push_promise_received(void)
nghttp2_frame_push_promise_init(&frame.push_promise,
NGHTTP2_FLAG_END_PUSH_PROMISE, 1, 2,
nva, nvlen);
user_data.frame_recv_cb_called = 0;
user_data.begin_headers_cb_called = 0;
user_data.invalid_frame_recv_cb_called = 0;
CU_ASSERT(0 == nghttp2_session_on_push_promise_received(session, &frame));
CU_ASSERT(1 == user_data.frame_recv_cb_called);
CU_ASSERT(1 == user_data.begin_headers_cb_called);
CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called);
nghttp2_frame_push_promise_free(&frame.push_promise);
@ -3635,43 +3642,43 @@ void test_nghttp2_session_on_request_recv_callback(void)
nghttp2_session_callbacks callbacks;
my_user_data user_data;
nghttp2_frame frame;
nghttp2_stream *stream;
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
callbacks.on_request_recv_callback = on_request_recv_callback;
user_data.stream_id = 0;
nghttp2_session_server_new(&session, &callbacks, &user_data);
nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS,
1, NGHTTP2_PRI_DEFAULT, NULL, 0);
CU_ASSERT(0 == nghttp2_session_end_request_headers_received(session, &frame));
CU_ASSERT(0 == user_data.stream_id);
nghttp2_frame_headers_init(&frame.headers,
NGHTTP2_FLAG_END_HEADERS |
NGHTTP2_FLAG_END_STREAM,
3, NGHTTP2_PRI_DEFAULT, NULL, 0);
/* nghttp2_session_end_* does not open stream, so we do it here */
nghttp2_session_open_stream(session, 3, NGHTTP2_STREAM_FLAG_NONE,
stream = nghttp2_session_open_stream(session, 3, NGHTTP2_STREAM_FLAG_NONE,
NGHTTP2_PRI_DEFAULT,
NGHTTP2_STREAM_OPENING, NULL);
frame.hd.stream_id = 3;
frame.hd.flags |= NGHTTP2_FLAG_END_STREAM;
CU_ASSERT(0 == nghttp2_session_end_request_headers_received(session, &frame));
user_data.stream_id = 0;
CU_ASSERT(0 == nghttp2_session_end_request_headers_received
(session, &frame, stream));
CU_ASSERT(3 == user_data.stream_id);
nghttp2_frame_headers_free(&frame.headers);
user_data.stream_id = 0;
nghttp2_session_open_stream(session, 5, NGHTTP2_STREAM_FLAG_NONE,
stream = nghttp2_session_open_stream(session, 5, NGHTTP2_STREAM_FLAG_NONE,
NGHTTP2_PRI_DEFAULT,
NGHTTP2_STREAM_OPENING, NULL);
nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS,
5, NGHTTP2_PRI_DEFAULT, NULL, 0);
CU_ASSERT(0 == nghttp2_session_end_headers_received(session, &frame));
CU_ASSERT(0 == nghttp2_session_end_headers_received(session, &frame, stream));
CU_ASSERT(0 == user_data.stream_id);
frame.headers.hd.flags |= NGHTTP2_FLAG_END_STREAM;
CU_ASSERT(0 == nghttp2_session_end_headers_received(session, &frame));
CU_ASSERT(0 == nghttp2_session_end_headers_received(session, &frame, stream));
CU_ASSERT(5 == user_data.stream_id);
nghttp2_frame_headers_free(&frame.headers);