Allow NGHTTP2_ERR_PAUSE from nghttp2_data_source_read_callback

This commit is contained in:
Tatsuhiro Tsujikawa 2016-08-28 18:50:19 +09:00
parent 1064e017c6
commit 581e0938a9
5 changed files with 66 additions and 3 deletions

View File

@ -860,8 +860,13 @@ typedef enum {
* achieved by returning :enum:`NGHTTP2_ERR_DEFERRED` without reading * achieved by returning :enum:`NGHTTP2_ERR_DEFERRED` without reading
* any data in this invocation. The library removes DATA frame from * any data in this invocation. The library removes DATA frame from
* the outgoing queue temporarily. To move back deferred DATA frame * the outgoing queue temporarily. To move back deferred DATA frame
* to outgoing queue, call `nghttp2_session_resume_data()`. In case * to outgoing queue, call `nghttp2_session_resume_data()`.
* of error, there are 2 choices. Returning *
* If the application just wants to return from
* `nghttp2_session_send()` or `nghttp2_session_mem_send()` without
* sending anything, return :enum:`NGHTTP2_ERR_PAUSE`.
*
* In case of error, there are 2 choices. Returning
* :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close the stream * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close the stream
* by issuing RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`. If a * by issuing RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`. If a
* different error code is desirable, use * different error code is desirable, use

View File

@ -2263,6 +2263,9 @@ static int session_prep_frame(nghttp2_session *session,
rv = nghttp2_session_pack_data(session, &session->aob.framebufs, rv = nghttp2_session_pack_data(session, &session->aob.framebufs,
next_readmax, frame, &item->aux_data.data, next_readmax, frame, &item->aux_data.data,
stream); stream);
if (rv == NGHTTP2_ERR_PAUSE) {
return rv;
}
if (rv == NGHTTP2_ERR_DEFERRED) { if (rv == NGHTTP2_ERR_DEFERRED) {
rv = nghttp2_stream_defer_item(stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER); rv = nghttp2_stream_defer_item(stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER);
@ -2918,6 +2921,9 @@ static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session,
} }
rv = session_prep_frame(session, item); rv = session_prep_frame(session, item);
if (rv == NGHTTP2_ERR_PAUSE) {
return 0;
}
if (rv == NGHTTP2_ERR_DEFERRED) { if (rv == NGHTTP2_ERR_DEFERRED) {
DEBUGF(fprintf(stderr, "send: frame transmission deferred\n")); DEBUGF(fprintf(stderr, "send: frame transmission deferred\n"));
break; break;
@ -7020,7 +7026,8 @@ int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
&aux_data->data_prd.source, session->user_data); &aux_data->data_prd.source, session->user_data);
if (payloadlen == NGHTTP2_ERR_DEFERRED || if (payloadlen == NGHTTP2_ERR_DEFERRED ||
payloadlen == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { payloadlen == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE ||
payloadlen == NGHTTP2_ERR_PAUSE) {
DEBUGF(fprintf(stderr, "send: DATA postponed due to %s\n", DEBUGF(fprintf(stderr, "send: DATA postponed due to %s\n",
nghttp2_strerror((int)payloadlen))); nghttp2_strerror((int)payloadlen)));

View File

@ -310,6 +310,8 @@ int main(int argc _U_, char *argv[] _U_) {
test_nghttp2_session_cancel_from_before_frame_send) || test_nghttp2_session_cancel_from_before_frame_send) ||
!CU_add_test(pSuite, "session_removed_closed_stream", !CU_add_test(pSuite, "session_removed_closed_stream",
test_nghttp2_session_removed_closed_stream) || test_nghttp2_session_removed_closed_stream) ||
!CU_add_test(pSuite, "session_pause_data",
test_nghttp2_session_pause_data) ||
!CU_add_test(pSuite, "http_mandatory_headers", !CU_add_test(pSuite, "http_mandatory_headers",
test_nghttp2_http_mandatory_headers) || test_nghttp2_http_mandatory_headers) ||
!CU_add_test(pSuite, "http_content_length", !CU_add_test(pSuite, "http_content_length",

View File

@ -77,6 +77,7 @@ typedef struct {
size_t padlen; size_t padlen;
int begin_frame_cb_called; int begin_frame_cb_called;
nghttp2_buf scratchbuf; nghttp2_buf scratchbuf;
size_t data_source_read_cb_paused;
} my_user_data; } my_user_data;
static const nghttp2_nv reqnv[] = { static const nghttp2_nv reqnv[] = {
@ -9934,6 +9935,53 @@ void test_nghttp2_session_removed_closed_stream(void) {
nghttp2_bufs_free(&bufs); nghttp2_bufs_free(&bufs);
} }
static ssize_t pause_once_data_source_read_callback(
nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len,
uint32_t *data_flags, nghttp2_data_source *source, void *user_data) {
my_user_data *ud = user_data;
if (ud->data_source_read_cb_paused == 0) {
++ud->data_source_read_cb_paused;
return NGHTTP2_ERR_PAUSE;
}
return fixed_length_data_source_read_callback(session, stream_id, buf, len,
data_flags, source, user_data);
}
void test_nghttp2_session_pause_data(void) {
nghttp2_session *session;
nghttp2_session_callbacks callbacks;
nghttp2_data_provider data_prd;
my_user_data ud;
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
callbacks.send_callback = null_send_callback;
callbacks.on_frame_send_callback = on_frame_send_callback;
data_prd.read_callback = pause_once_data_source_read_callback;
ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN;
nghttp2_session_server_new(&session, &callbacks, &ud);
open_recv_stream(session, 1);
CU_ASSERT(
0 == nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd));
ud.frame_send_cb_called = 0;
ud.data_source_read_cb_paused = 0;
CU_ASSERT(0 == nghttp2_session_send(session));
CU_ASSERT(0 == ud.frame_send_cb_called);
CU_ASSERT(NULL == session->aob.item);
CU_ASSERT(0 == nghttp2_session_send(session));
CU_ASSERT(1 == ud.frame_send_cb_called);
CU_ASSERT(NGHTTP2_DATA == ud.sent_frame_type);
CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
nghttp2_session_del(session);
}
static void check_nghttp2_http_recv_headers_fail( static void check_nghttp2_http_recv_headers_fail(
nghttp2_session *session, nghttp2_hd_deflater *deflater, int32_t stream_id, nghttp2_session *session, nghttp2_hd_deflater *deflater, int32_t stream_id,
int stream_state, const nghttp2_nv *nva, size_t nvlen) { int stream_state, const nghttp2_nv *nva, size_t nvlen) {

View File

@ -153,6 +153,7 @@ void test_nghttp2_session_repeated_priority_submission(void);
void test_nghttp2_session_set_local_window_size(void); void test_nghttp2_session_set_local_window_size(void);
void test_nghttp2_session_cancel_from_before_frame_send(void); void test_nghttp2_session_cancel_from_before_frame_send(void);
void test_nghttp2_session_removed_closed_stream(void); void test_nghttp2_session_removed_closed_stream(void);
void test_nghttp2_session_pause_data(void);
void test_nghttp2_http_mandatory_headers(void); void test_nghttp2_http_mandatory_headers(void);
void test_nghttp2_http_content_length(void); void test_nghttp2_http_content_length(void);
void test_nghttp2_http_content_length_mismatch(void); void test_nghttp2_http_content_length_mismatch(void);