Remove nghttp2_session_continue

nghttp2_session_continue is removed. Now just call
nghttp2_session_mem_recv to continue after NGHTTP2_ERR_PAUSE.
This commit is contained in:
Tatsuhiro Tsujikawa 2014-01-17 22:52:30 +09:00
parent b2bf18d40f
commit 22a4e3eab8
3 changed files with 209 additions and 89 deletions

View File

@ -913,13 +913,12 @@ typedef int (*nghttp2_on_invalid_frame_recv_callback)
* *
* If the application uses `nghttp2_session_mem_recv()`, it can return * If the application uses `nghttp2_session_mem_recv()`, it can return
* :enum:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()` * :enum:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()`
* return without processing further input bytes. The |frame| * return without processing further input bytes. The memory by
* parameter is retained until `nghttp2_session_continue()` is * pointed by the |data| is retained until
* `nghttp2_session_mem_recv()` or `nghttp2_session_recv()` is
* called. The application must retain the input bytes which was used * called. The application must retain the input bytes which was used
* to produce the |frame| parameter, because it may refer to the * to produce the |data| parameter, because it may refer to the memory
* memory region included in the input bytes. The application which * region included in the input bytes.
* returns :enum:`NGHTTP2_ERR_PAUSE` must call
* `nghttp2_session_continue()` before `nghttp2_session_mem_recv()`.
* *
* The implementation of this function must return 0 if it * The implementation of this function must return 0 if it
* succeeds. If nonzero is returned, it is treated as fatal error and * succeeds. If nonzero is returned, it is treated as fatal error and
@ -1124,14 +1123,12 @@ typedef int (*nghttp2_on_unknown_frame_recv_callback)
* *
* If the application uses `nghttp2_session_mem_recv()`, it can return * If the application uses `nghttp2_session_mem_recv()`, it can return
* :enum:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()` * :enum:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()`
* return without processing further input bytes. The |frame|, * return without processing further input bytes. The memory pointed
* |name|, |namelen|, |value| and |valuelen| parameters are retained * by |frame|, |name| and |value| parameters are retained until
* until `nghttp2_session_continue()` is called. The application must * `nghttp2_session_mem_recv()` or `nghttp2_session_recv()` is
* retain the input bytes which was used to produce the |frame| * called. The application must retain the input bytes which was used
* parameter, because it may refer to the memory region included in * to produce these parameters, because it may refer to the memory
* the input bytes. The application which returns * region included in the input bytes.
* :enum:`NGHTTP2_ERR_PAUSE` must call `nghttp2_session_continue()`
* before `nghttp2_session_mem_recv()`.
* *
* The implementation of this function must return 0 if it * The implementation of this function must return 0 if it
* succeeds. It may return :enum:`NGHTTP2_ERR_PAUSE`. If the other * succeeds. It may return :enum:`NGHTTP2_ERR_PAUSE`. If the other
@ -1548,7 +1545,7 @@ int nghttp2_session_recv(nghttp2_session *session);
* In the current implementation, this function always tries to * In the current implementation, this function always tries to
* processes all input data unless either an error occurs or * processes all input data unless either an error occurs or
* :enum:`NGHTTP2_ERR_PAUSE` is returned from * :enum:`NGHTTP2_ERR_PAUSE` is returned from
* :member:`nghttp2_session_callbacks.on_frame_recv_callback` or * :member:`nghttp2_session_callbacks.on_header_callback` or
* :member:`nghttp2_session_callbacks.on_data_chunk_recv_callback`. * :member:`nghttp2_session_callbacks.on_data_chunk_recv_callback`.
* If :enum:`NGHTTP2_ERR_PAUSE` is used, the return value includes the * If :enum:`NGHTTP2_ERR_PAUSE` is used, the return value includes the
* number of bytes which was used to produce the data or frame for the * number of bytes which was used to produce the data or frame for the
@ -1565,33 +1562,6 @@ int nghttp2_session_recv(nghttp2_session *session);
ssize_t nghttp2_session_mem_recv(nghttp2_session *session, ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
const uint8_t *in, size_t inlen); const uint8_t *in, size_t inlen);
/**
* @function
*
* Perform post-processing after `nghttp2_session_mem_recv()` was
* paused by :enum:`NGHTTP2_ERR_PAUSE` from
* :member:`nghttp2_session_callbacks.on_frame_recv_callback` or
* :member:`nghttp2_session_callbacks.on_data_chunk_recv_callback`.
*
* This function frees resources associated with paused frames. It
* may also call additional callbacks, such as
* :member:`nghttp2_session_callbacks.on_stream_close_callback`.
*
* If this function succeeds, the application can call
* `nghttp2_session_mem_recv()` again.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
*
* :enum:`NGHTTP2_ERR_NOMEM`
* Out of memory.
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`
* The callback function failed.
*
*/
int nghttp2_session_continue(nghttp2_session *session);
/** /**
* @function * @function
* *

View File

@ -2046,6 +2046,21 @@ static int nghttp2_session_inflate_handle_invalid_connection
return nghttp2_session_handle_invalid_connection(session, frame, error_code); return nghttp2_session_handle_invalid_connection(session, frame, error_code);
} }
/*
* Decompress header blocks of incoming request HEADERS and also call
* additional callbacks. This function can be called again if this
* function returns NGHTTP2_ERR_PAUSE.
*
* This function returns 0 if it succeeds, or one of negative error
* codes:
*
* NGHTTP2_ERR_CALLBACK_FAILURE
* The callback function failed.
* NGHTTP2_ERR_NOMEM
* Out of memory.
* NGHTTP2_ERR_PAUSE
* The callback function returned NGHTTP2_ERR_PAUSE
*/
static int session_end_request_headers_received(nghttp2_session *session, static int session_end_request_headers_received(nghttp2_session *session,
nghttp2_frame *frame) nghttp2_frame *frame)
{ {
@ -2067,6 +2082,21 @@ static int session_end_request_headers_received(nghttp2_session *session,
return 0; return 0;
} }
/*
* Decompress header blocks of incoming (push-)response HEADERS and
* also call additional callbacks. This function can be called again
* if this function returns NGHTTP2_ERR_PAUSE.
*
* This function returns 0 if it succeeds, or one of negative error
* codes:
*
* NGHTTP2_ERR_CALLBACK_FAILURE
* The callback function failed.
* NGHTTP2_ERR_NOMEM
* Out of memory.
* NGHTTP2_ERR_PAUSE
* The callback function returned NGHTTP2_ERR_PAUSE
*/
static int session_end_response_headers_received(nghttp2_session *session, static int session_end_response_headers_received(nghttp2_session *session,
nghttp2_frame *frame) nghttp2_frame *frame)
{ {
@ -2089,6 +2119,21 @@ static int session_end_response_headers_received(nghttp2_session *session,
return 0; return 0;
} }
/*
* Decompress header blocks of incoming HEADERS and also call
* additional callbacks. This function can be called again if this
* function returns NGHTTP2_ERR_PAUSE.
*
* This function returns 0 if it succeeds, or one of negative error
* codes:
*
* NGHTTP2_ERR_CALLBACK_FAILURE
* The callback function failed.
* NGHTTP2_ERR_NOMEM
* Out of memory.
* NGHTTP2_ERR_PAUSE
* The callback function returned NGHTTP2_ERR_PAUSE
*/
static int session_end_headers_received(nghttp2_session *session, static int session_end_headers_received(nghttp2_session *session,
nghttp2_frame *frame) nghttp2_frame *frame)
{ {
@ -3390,40 +3435,46 @@ static int nghttp2_session_check_data_recv_allowed(nghttp2_session *session,
return 0; return 0;
} }
int nghttp2_session_continue(nghttp2_session *session) /*
* Handles paused header emission callbacks. This function may return
* NGHTTP2_ERR_PAUSE again if the application just do so in a
* callback.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* :enum:`NGHTTP2_ERR_NOMEM`
* Out of memory.
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`
* The callback function failed.
* :enum:`NGHTTP2_ERR_PAUSE`
* The callback function returned NGHTTP2_ERR_PAUSE
*/
static int session_continue(nghttp2_session *session)
{ {
nghttp2_frame *frame = &session->iframe.frame; nghttp2_frame *frame = &session->iframe.frame;
int rv = 0; int rv = 0;
if(session->iframe.error_code != NGHTTP2_ERR_PAUSE) {
return 0;
}
switch(frame->hd.type) { switch(frame->hd.type) {
case NGHTTP2_DATA:
/* To call on_data_recv_callback */
return nghttp2_session_mem_recv(session, NULL, 0);
case NGHTTP2_HEADERS: case NGHTTP2_HEADERS:
switch(session->iframe.frame.headers.cat) { switch(frame->headers.cat) {
case NGHTTP2_HCAT_REQUEST: case NGHTTP2_HCAT_REQUEST:
rv = session_end_request_headers_received(session, rv = session_end_request_headers_received(session, frame);
&session->iframe.frame);
break; break;
case NGHTTP2_HCAT_RESPONSE: case NGHTTP2_HCAT_RESPONSE:
case NGHTTP2_HCAT_PUSH_RESPONSE: case NGHTTP2_HCAT_PUSH_RESPONSE:
rv = session_end_response_headers_received(session, rv = session_end_response_headers_received(session, frame);
&session->iframe.frame);
break; break;
case NGHTTP2_HCAT_HEADERS: case NGHTTP2_HCAT_HEADERS:
rv = session_end_headers_received(session, &session->iframe.frame); rv = session_end_headers_received(session, frame);
break; break;
} }
break; break;
case NGHTTP2_PUSH_PROMISE: case NGHTTP2_PUSH_PROMISE:
rv = inflate_header_block(session, &session->iframe.frame, 1); rv = inflate_header_block(session, frame, 1);
break; break;
default: default:
break; break;
} }
nghttp2_inbound_frame_reset(session);
return rv; return rv;
} }
@ -3431,6 +3482,21 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
const uint8_t *in, size_t inlen) const uint8_t *in, size_t inlen)
{ {
const uint8_t *inmark, *inlimit; const uint8_t *inmark, *inlimit;
if(session->iframe.error_code == NGHTTP2_ERR_PAUSE) {
int rv;
rv = session_continue(session);
if(rv == NGHTTP2_ERR_PAUSE) {
return 0;
}
if(rv != 0) {
return rv;
}
/* For DATA, we have to go without resetting in order to call
on_data_recv_callback. */
if(session->iframe.frame.hd.type != NGHTTP2_DATA) {
nghttp2_inbound_frame_reset(session);
}
}
inmark = in; inmark = in;
inlimit = in+inlen; inlimit = in+inlen;
while(1) { while(1) {

View File

@ -69,6 +69,9 @@ typedef struct {
int data_recv_cb_called; int data_recv_cb_called;
const nghttp2_frame *frame; const nghttp2_frame *frame;
size_t fixed_sendlen; size_t fixed_sendlen;
int header_cb_called;
int end_headers_cb_called;
nghttp2_nv nv;
} my_user_data; } my_user_data;
static void scripted_data_feed_init(scripted_data_feed *df, static void scripted_data_feed_init(scripted_data_feed *df,
@ -146,16 +149,6 @@ static int on_frame_recv_callback(nghttp2_session *session,
return 0; return 0;
} }
static int pause_on_frame_recv_callback(nghttp2_session *session,
const nghttp2_frame *frame,
void *user_data)
{
my_user_data *ud = (my_user_data*)user_data;
++ud->frame_recv_cb_called;
ud->frame = frame;
return NGHTTP2_ERR_PAUSE;
}
static int on_invalid_frame_recv_callback(nghttp2_session *session, static int on_invalid_frame_recv_callback(nghttp2_session *session,
const nghttp2_frame *frame, const nghttp2_frame *frame,
nghttp2_error_code error_code, nghttp2_error_code error_code,
@ -287,6 +280,44 @@ static ssize_t block_count_send_callback(nghttp2_session* session,
return r; return r;
} }
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)
{
my_user_data *ud = (my_user_data*)user_data;
++ud->header_cb_called;
ud->nv.name = (uint8_t*)name;
ud->nv.namelen = namelen;
ud->nv.value = (uint8_t*)value;
ud->nv.valuelen = valuelen;
ud->frame = frame;
return 0;
}
static int pause_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)
{
on_header_callback(session, frame, name, namelen, value, valuelen,
user_data);
return NGHTTP2_ERR_PAUSE;
}
static int on_end_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;
return 0;
}
static ssize_t defer_data_source_read_callback static ssize_t defer_data_source_read_callback
(nghttp2_session *session, int32_t stream_id, (nghttp2_session *session, int32_t stream_id,
uint8_t *buf, size_t len, int *eof, uint8_t *buf, size_t len, int *eof,
@ -639,16 +670,19 @@ void test_nghttp2_session_continue(void)
nghttp2_session_callbacks callbacks; nghttp2_session_callbacks callbacks;
my_user_data user_data; my_user_data user_data;
const nghttp2_nv nv1[] = { const nghttp2_nv nv1[] = {
MAKE_NV("url", "/") MAKE_NV(":method", "GET"),
MAKE_NV(":path", "/")
}; };
const nghttp2_nv nv2[] = { const nghttp2_nv nv2[] = {
MAKE_NV("user-agent", "nghttp2/1.0.0") MAKE_NV("user-agent", "nghttp2/1.0.0"),
MAKE_NV("alpha", "bravo")
}; };
uint8_t *framedata = NULL; uint8_t *framedata = NULL;
size_t framedatalen = 0; size_t framedatalen = 0;
ssize_t framelen1, framelen2; ssize_t framelen1, framelen2;
ssize_t rv; ssize_t rv;
uint8_t buffer[4096]; uint8_t buffer[4096];
uint8_t *bufp = buffer;
size_t buflen; size_t buflen;
nghttp2_frame frame; nghttp2_frame frame;
nghttp2_nv *nva; nghttp2_nv *nva;
@ -657,14 +691,13 @@ void test_nghttp2_session_continue(void)
nghttp2_frame_hd data_hd; nghttp2_frame_hd data_hd;
nghttp2_hd_context deflater; nghttp2_hd_context deflater;
/* TODO TBD */
return;
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
callbacks.send_callback = null_send_callback; callbacks.send_callback = null_send_callback;
callbacks.on_frame_recv_callback = pause_on_frame_recv_callback; callbacks.on_frame_recv_callback = on_frame_recv_callback;
callbacks.on_data_chunk_recv_callback = pause_on_data_chunk_recv_callback; callbacks.on_data_chunk_recv_callback = pause_on_data_chunk_recv_callback;
callbacks.on_data_recv_callback = on_data_recv_callback; callbacks.on_data_recv_callback = on_data_recv_callback;
callbacks.on_header_callback = pause_on_header_callback;
callbacks.on_end_headers_callback = on_end_headers_callback;
nghttp2_session_server_new(&session, &callbacks, &user_data); nghttp2_session_server_new(&session, &callbacks, &user_data);
@ -675,8 +708,8 @@ void test_nghttp2_session_continue(void)
nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS,
1, NGHTTP2_PRI_DEFAULT, nva, nvlen); 1, NGHTTP2_PRI_DEFAULT, nva, nvlen);
framelen1 = nghttp2_frame_pack_headers(&framedata, &framedatalen, framelen1 = nghttp2_frame_pack_headers(&framedata, &framedatalen,
&frame.headers, &frame.headers,
&deflater); &deflater);
nghttp2_frame_headers_free(&frame.headers); nghttp2_frame_headers_free(&frame.headers);
memcpy(buffer, framedata, framelen1); memcpy(buffer, framedata, framelen1);
@ -693,31 +726,69 @@ void test_nghttp2_session_continue(void)
buflen = framelen1 + framelen2; buflen = framelen1 + framelen2;
/* Receive 1st HEADERS and pause */ /* Receive 1st HEADERS and pause */
rv = nghttp2_session_mem_recv(session, buffer, buflen); user_data.header_cb_called = 0;
user_data.end_headers_cb_called = 0;
rv = nghttp2_session_mem_recv(session, bufp, buflen);
CU_ASSERT(rv == framelen1); CU_ASSERT(rv == framelen1);
bufp += framelen1;
buflen -= framelen1;
recv_frame = user_data.frame; recv_frame = user_data.frame;
CU_ASSERT(NGHTTP2_HEADERS == recv_frame->hd.type); CU_ASSERT(NGHTTP2_HEADERS == recv_frame->hd.type);
CU_ASSERT(framelen1 - NGHTTP2_FRAME_HEAD_LENGTH == recv_frame->hd.length); CU_ASSERT(framelen1 - NGHTTP2_FRAME_HEAD_LENGTH == recv_frame->hd.length);
CU_ASSERT(1 == recv_frame->headers.nvlen);
CU_ASSERT(nghttp2_nv_equal(&nv1[0], recv_frame->headers.nva));
rv = nghttp2_session_continue(session); CU_ASSERT(1 == user_data.header_cb_called);
CU_ASSERT(rv == 0); CU_ASSERT(0 == user_data.end_headers_cb_called);
/* Receive 2nd HEADERS and pause */ CU_ASSERT(nghttp2_nv_equal(&nv1[0], &user_data.nv));
rv = nghttp2_session_mem_recv(session, buffer + framelen1,
buflen - framelen1); /* get 2nd header field */
user_data.header_cb_called = 0;
user_data.end_headers_cb_called = 0;
rv = nghttp2_session_mem_recv(session, bufp, buflen);
CU_ASSERT(0 == rv);
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.header_cb_called = 0;
user_data.end_headers_cb_called = 0;
rv = nghttp2_session_mem_recv(session, bufp, buflen);
CU_ASSERT(rv == framelen2); CU_ASSERT(rv == framelen2);
bufp += framelen2;
buflen -= framelen2;
recv_frame = user_data.frame; recv_frame = user_data.frame;
CU_ASSERT(NGHTTP2_HEADERS == recv_frame->hd.type); CU_ASSERT(NGHTTP2_HEADERS == recv_frame->hd.type);
CU_ASSERT(framelen2 - NGHTTP2_FRAME_HEAD_LENGTH == recv_frame->hd.length); CU_ASSERT(framelen2 - NGHTTP2_FRAME_HEAD_LENGTH == recv_frame->hd.length);
CU_ASSERT(1 == recv_frame->headers.nvlen);
CU_ASSERT(nghttp2_nv_equal(&nv2[0], recv_frame->headers.nva));
rv = nghttp2_session_continue(session); CU_ASSERT(1 == user_data.header_cb_called);
CU_ASSERT(rv == 0); 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.header_cb_called = 0;
user_data.end_headers_cb_called = 0;
rv = nghttp2_session_mem_recv(session, bufp, buflen);
CU_ASSERT(0 == rv);
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 */
user_data.header_cb_called = 0;
user_data.end_headers_cb_called = 0;
rv = nghttp2_session_mem_recv(session, bufp, buflen);
CU_ASSERT(0 == rv);
CU_ASSERT(0 == user_data.header_cb_called);
CU_ASSERT(1 == user_data.end_headers_cb_called);
/* Receive DATA */ /* Receive DATA */
data_hd.length = 16; data_hd.length = 16;
@ -725,14 +796,27 @@ void test_nghttp2_session_continue(void)
data_hd.flags = NGHTTP2_FLAG_NONE; data_hd.flags = NGHTTP2_FLAG_NONE;
data_hd.stream_id = 1; data_hd.stream_id = 1;
nghttp2_frame_pack_frame_hd(buffer, &data_hd); nghttp2_frame_pack_frame_hd(buffer, &data_hd);
bufp = buffer;
buflen = sizeof(buffer);
/* Intentionally specify larger buffer size to see pause is kicked /* Intentionally specify larger buffer size to see pause is kicked
in. */ in. */
user_data.data_recv_cb_called = 0;
rv = nghttp2_session_mem_recv(session, buffer, sizeof(buffer)); rv = nghttp2_session_mem_recv(session, buffer, sizeof(buffer));
CU_ASSERT(16 + NGHTTP2_FRAME_HEAD_LENGTH == rv); CU_ASSERT(16 + NGHTTP2_FRAME_HEAD_LENGTH == rv);
CU_ASSERT(0 == user_data.data_recv_cb_called);
/* Next nghttp2_session_mem_recv invokes on_data_recv_callback and
pause again in on_data_chunk_recv_callback since we pass same
DATA frame. */
user_data.data_recv_cb_called = 0; user_data.data_recv_cb_called = 0;
rv = nghttp2_session_continue(session); rv = nghttp2_session_mem_recv(session, buffer, sizeof(buffer));
CU_ASSERT(rv == 0); CU_ASSERT(16 + NGHTTP2_FRAME_HEAD_LENGTH == rv);
CU_ASSERT(1 == user_data.data_recv_cb_called);
/* And finally call on_data_recv_callback with 0 size input */
user_data.data_recv_cb_called = 0;
rv = nghttp2_session_mem_recv(session, NULL, 0);
CU_ASSERT(0 == rv);
CU_ASSERT(1 == user_data.data_recv_cb_called); CU_ASSERT(1 == user_data.data_recv_cb_called);
free(framedata); free(framedata);