Check request/response submission error based side of session
Disallow request from server, and response from client respectively. When the violation is detected, return NGHTTP2_ERR_PROTO from nghttp2_submit_request, nghttp2_submit_response, nghttp2_submit_headers. We also did some refactoring, and now self-dependency detection is placed where it is only required.
This commit is contained in:
parent
8f225ae88d
commit
bb6f842b37
|
@ -3164,6 +3164,8 @@ nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec);
|
||||||
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
|
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
|
||||||
* Trying to depend on itself (new stream ID equals
|
* Trying to depend on itself (new stream ID equals
|
||||||
* ``pri_spec->stream_id``).
|
* ``pri_spec->stream_id``).
|
||||||
|
* :enum:`NGHTTP2_ERR_PROTO`
|
||||||
|
* The |session| is server session.
|
||||||
*
|
*
|
||||||
* .. warning::
|
* .. warning::
|
||||||
*
|
*
|
||||||
|
@ -3236,6 +3238,8 @@ nghttp2_submit_request(nghttp2_session *session,
|
||||||
* processed yet. Normally, this does not happen, but when
|
* processed yet. Normally, this does not happen, but when
|
||||||
* application wrongly calls `nghttp2_submit_response()` twice,
|
* application wrongly calls `nghttp2_submit_response()` twice,
|
||||||
* this may happen.
|
* this may happen.
|
||||||
|
* :enum:`NGHTTP2_ERR_PROTO`
|
||||||
|
* The |session| is client session.
|
||||||
*
|
*
|
||||||
* .. warning::
|
* .. warning::
|
||||||
*
|
*
|
||||||
|
@ -3380,6 +3384,8 @@ NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session,
|
||||||
* DATA or HEADERS has been already submitted and not fully
|
* DATA or HEADERS has been already submitted and not fully
|
||||||
* processed yet. This happens if stream denoted by |stream_id|
|
* processed yet. This happens if stream denoted by |stream_id|
|
||||||
* is in reserved state.
|
* is in reserved state.
|
||||||
|
* :enum:`NGHTTP2_ERR_PROTO`
|
||||||
|
* The |stream_id| is -1, and |session| is server session.
|
||||||
*
|
*
|
||||||
* .. warning::
|
* .. warning::
|
||||||
*
|
*
|
||||||
|
|
|
@ -32,6 +32,35 @@
|
||||||
#include "nghttp2_helper.h"
|
#include "nghttp2_helper.h"
|
||||||
#include "nghttp2_priority_spec.h"
|
#include "nghttp2_priority_spec.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Detects the dependency error, that is stream attempted to depend on
|
||||||
|
* itself. If |stream_id| is -1, we use session->next_stream_id as
|
||||||
|
* stream ID.
|
||||||
|
*
|
||||||
|
* This function returns 0 if it succeeds, or one of the following
|
||||||
|
* error codes:
|
||||||
|
*
|
||||||
|
* NGHTTP2_ERR_INVALID_ARGUMENT
|
||||||
|
* Stream attempted to depend on itself.
|
||||||
|
*/
|
||||||
|
static int detect_self_dependency(nghttp2_session *session, int32_t stream_id,
|
||||||
|
const nghttp2_priority_spec *pri_spec) {
|
||||||
|
assert(pri_spec);
|
||||||
|
|
||||||
|
if (stream_id == -1) {
|
||||||
|
if ((int32_t)session->next_stream_id == pri_spec->stream_id) {
|
||||||
|
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream_id == pri_spec->stream_id) {
|
||||||
|
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* This function takes ownership of |nva_copy|. Regardless of the
|
/* This function takes ownership of |nva_copy|. Regardless of the
|
||||||
return value, the caller must not free |nva_copy| after this
|
return value, the caller must not free |nva_copy| after this
|
||||||
function returns. */
|
function returns. */
|
||||||
|
@ -50,21 +79,6 @@ static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags,
|
||||||
|
|
||||||
mem = &session->mem;
|
mem = &session->mem;
|
||||||
|
|
||||||
if (stream_id == 0) {
|
|
||||||
rv = NGHTTP2_ERR_INVALID_ARGUMENT;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stream_id == -1) {
|
|
||||||
if ((int32_t)session->next_stream_id == pri_spec->stream_id) {
|
|
||||||
rv = NGHTTP2_ERR_INVALID_ARGUMENT;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
} else if (stream_id == pri_spec->stream_id) {
|
|
||||||
rv = NGHTTP2_ERR_INVALID_ARGUMENT;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
|
item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
|
||||||
if (item == NULL) {
|
if (item == NULL) {
|
||||||
rv = NGHTTP2_ERR_NOMEM;
|
rv = NGHTTP2_ERR_NOMEM;
|
||||||
|
@ -156,6 +170,10 @@ static int32_t submit_headers_shared_nva(nghttp2_session *session,
|
||||||
|
|
||||||
int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id,
|
int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id,
|
||||||
const nghttp2_nv *nva, size_t nvlen) {
|
const nghttp2_nv *nva, size_t nvlen) {
|
||||||
|
if (stream_id <= 0) {
|
||||||
|
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
return (int)submit_headers_shared_nva(session, NGHTTP2_FLAG_END_STREAM,
|
return (int)submit_headers_shared_nva(session, NGHTTP2_FLAG_END_STREAM,
|
||||||
stream_id, NULL, nva, nvlen, NULL,
|
stream_id, NULL, nva, nvlen, NULL,
|
||||||
NULL);
|
NULL);
|
||||||
|
@ -166,9 +184,24 @@ int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
|
||||||
const nghttp2_priority_spec *pri_spec,
|
const nghttp2_priority_spec *pri_spec,
|
||||||
const nghttp2_nv *nva, size_t nvlen,
|
const nghttp2_nv *nva, size_t nvlen,
|
||||||
void *stream_user_data) {
|
void *stream_user_data) {
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
if (stream_id == -1) {
|
||||||
|
if (session->server) {
|
||||||
|
return NGHTTP2_ERR_PROTO;
|
||||||
|
}
|
||||||
|
} else if (stream_id <= 0) {
|
||||||
|
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
flags &= NGHTTP2_FLAG_END_STREAM;
|
flags &= NGHTTP2_FLAG_END_STREAM;
|
||||||
|
|
||||||
if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) {
|
if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) {
|
||||||
|
rv = detect_self_dependency(session, stream_id, pri_spec);
|
||||||
|
if (rv != 0) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
flags |= NGHTTP2_FLAG_PRIORITY;
|
flags |= NGHTTP2_FLAG_PRIORITY;
|
||||||
} else {
|
} else {
|
||||||
pri_spec = NULL;
|
pri_spec = NULL;
|
||||||
|
@ -281,7 +314,7 @@ int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags _U_,
|
||||||
|
|
||||||
mem = &session->mem;
|
mem = &session->mem;
|
||||||
|
|
||||||
if (stream_id == 0 || nghttp2_session_is_my_stream_id(session, stream_id)) {
|
if (stream_id <= 0 || nghttp2_session_is_my_stream_id(session, stream_id)) {
|
||||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,8 +429,18 @@ int32_t nghttp2_submit_request(nghttp2_session *session,
|
||||||
const nghttp2_data_provider *data_prd,
|
const nghttp2_data_provider *data_prd,
|
||||||
void *stream_user_data) {
|
void *stream_user_data) {
|
||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
|
int rv;
|
||||||
|
|
||||||
if (pri_spec && nghttp2_priority_spec_check_default(pri_spec)) {
|
if (session->server) {
|
||||||
|
return NGHTTP2_ERR_PROTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) {
|
||||||
|
rv = detect_self_dependency(session, -1, pri_spec);
|
||||||
|
if (rv != 0) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
pri_spec = NULL;
|
pri_spec = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,7 +461,17 @@ static uint8_t set_response_flags(const nghttp2_data_provider *data_prd) {
|
||||||
int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,
|
int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,
|
||||||
const nghttp2_nv *nva, size_t nvlen,
|
const nghttp2_nv *nva, size_t nvlen,
|
||||||
const nghttp2_data_provider *data_prd) {
|
const nghttp2_data_provider *data_prd) {
|
||||||
uint8_t flags = set_response_flags(data_prd);
|
uint8_t flags;
|
||||||
|
|
||||||
|
if (stream_id <= 0) {
|
||||||
|
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!session->server) {
|
||||||
|
return NGHTTP2_ERR_PROTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
flags = set_response_flags(data_prd);
|
||||||
return submit_headers_shared_nva(session, flags, stream_id, NULL, nva, nvlen,
|
return submit_headers_shared_nva(session, flags, stream_id, NULL, nva, nvlen,
|
||||||
data_prd, NULL);
|
data_prd, NULL);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3897,6 +3897,15 @@ void test_nghttp2_submit_request_with_data(void) {
|
||||||
CU_ASSERT(0 == ud.data_source_length);
|
CU_ASSERT(0 == ud.data_source_length);
|
||||||
|
|
||||||
nghttp2_session_del(session);
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
|
/* nghttp2_submit_request() with server session is error */
|
||||||
|
nghttp2_session_server_new(&session, &callbacks, NULL);
|
||||||
|
|
||||||
|
CU_ASSERT(NGHTTP2_ERR_PROTO == nghttp2_submit_request(session, NULL, reqnv,
|
||||||
|
ARRLEN(reqnv), NULL,
|
||||||
|
NULL));
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_nghttp2_submit_request_without_data(void) {
|
void test_nghttp2_submit_request_without_data(void) {
|
||||||
|
@ -3984,6 +3993,19 @@ void test_nghttp2_submit_response_with_data(void) {
|
||||||
CU_ASSERT(0 == ud.data_source_length);
|
CU_ASSERT(0 == ud.data_source_length);
|
||||||
|
|
||||||
nghttp2_session_del(session);
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
|
/* Various error cases */
|
||||||
|
nghttp2_session_client_new(&session, &callbacks, NULL);
|
||||||
|
|
||||||
|
/* Calling nghttp2_submit_response() with client session is error */
|
||||||
|
CU_ASSERT(NGHTTP2_ERR_PROTO ==
|
||||||
|
nghttp2_submit_response(session, 1, resnv, ARRLEN(resnv), NULL));
|
||||||
|
|
||||||
|
/* Stream ID <= 0 is error */
|
||||||
|
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
|
||||||
|
nghttp2_submit_response(session, 0, resnv, ARRLEN(resnv), NULL));
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_nghttp2_submit_response_without_data(void) {
|
void test_nghttp2_submit_response_without_data(void) {
|
||||||
|
@ -4115,6 +4137,18 @@ void test_nghttp2_submit_trailer(void) {
|
||||||
nghttp2_frame_headers_free(&frame.headers, mem);
|
nghttp2_frame_headers_free(&frame.headers, mem);
|
||||||
nghttp2_hd_inflate_free(&inflater);
|
nghttp2_hd_inflate_free(&inflater);
|
||||||
nghttp2_session_del(session);
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
|
/* Specifying stream ID <= 0 is error */
|
||||||
|
nghttp2_session_server_new(&session, &callbacks, NULL);
|
||||||
|
open_recv_stream(session, 1);
|
||||||
|
|
||||||
|
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
|
||||||
|
nghttp2_submit_trailer(session, 0, trailernv, ARRLEN(trailernv)));
|
||||||
|
|
||||||
|
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
|
||||||
|
nghttp2_submit_trailer(session, -1, trailernv, ARRLEN(trailernv)));
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_nghttp2_submit_headers_start_stream(void) {
|
void test_nghttp2_submit_headers_start_stream(void) {
|
||||||
|
@ -4305,6 +4339,22 @@ void test_nghttp2_submit_headers(void) {
|
||||||
reqnv, ARRLEN(reqnv), NULL));
|
reqnv, ARRLEN(reqnv), NULL));
|
||||||
|
|
||||||
nghttp2_session_del(session);
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
|
/* Error cases with invalid stream ID */
|
||||||
|
nghttp2_session_server_new(&session, &callbacks, NULL);
|
||||||
|
|
||||||
|
/* Sending nghttp2_submit_headers() with stream_id == 1 and server
|
||||||
|
session is error */
|
||||||
|
CU_ASSERT(NGHTTP2_ERR_PROTO ==
|
||||||
|
nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, -1, NULL, reqnv,
|
||||||
|
ARRLEN(reqnv), NULL));
|
||||||
|
|
||||||
|
/* Sending stream ID <= 0 is error */
|
||||||
|
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
|
||||||
|
nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 0, NULL, resnv,
|
||||||
|
ARRLEN(resnv), NULL));
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_nghttp2_submit_headers_continuation(void) {
|
void test_nghttp2_submit_headers_continuation(void) {
|
||||||
|
@ -4642,7 +4692,12 @@ void test_nghttp2_submit_push_promise(void) {
|
||||||
/* submit PUSH_PROMISE while associated stream is not opened */
|
/* submit PUSH_PROMISE while associated stream is not opened */
|
||||||
CU_ASSERT(NGHTTP2_ERR_STREAM_CLOSED ==
|
CU_ASSERT(NGHTTP2_ERR_STREAM_CLOSED ==
|
||||||
nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 3, reqnv,
|
nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 3, reqnv,
|
||||||
ARRLEN(reqnv), &ud));
|
ARRLEN(reqnv), NULL));
|
||||||
|
|
||||||
|
/* Stream ID <= 0 is error */
|
||||||
|
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
|
||||||
|
nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 0, reqnv,
|
||||||
|
ARRLEN(reqnv), NULL));
|
||||||
|
|
||||||
nghttp2_session_del(session);
|
nghttp2_session_del(session);
|
||||||
}
|
}
|
||||||
|
@ -4850,19 +4905,10 @@ void test_nghttp2_submit_invalid_nv(void) {
|
||||||
|
|
||||||
CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, NULL));
|
CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, NULL));
|
||||||
|
|
||||||
/* nghttp2_submit_request */
|
|
||||||
CU_ASSERT(0 < nghttp2_submit_request(session, NULL, empty_name_nv,
|
|
||||||
ARRLEN(empty_name_nv), NULL, NULL));
|
|
||||||
|
|
||||||
/* nghttp2_submit_response */
|
/* nghttp2_submit_response */
|
||||||
CU_ASSERT(0 == nghttp2_submit_response(session, 2, empty_name_nv,
|
CU_ASSERT(0 == nghttp2_submit_response(session, 2, empty_name_nv,
|
||||||
ARRLEN(empty_name_nv), NULL));
|
ARRLEN(empty_name_nv), NULL));
|
||||||
|
|
||||||
/* nghttp2_submit_headers */
|
|
||||||
CU_ASSERT(0 < nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, -1, NULL,
|
|
||||||
empty_name_nv, ARRLEN(empty_name_nv),
|
|
||||||
NULL));
|
|
||||||
|
|
||||||
/* nghttp2_submit_push_promise */
|
/* nghttp2_submit_push_promise */
|
||||||
open_recv_stream(session, 1);
|
open_recv_stream(session, 1);
|
||||||
|
|
||||||
|
@ -4871,6 +4917,19 @@ void test_nghttp2_submit_invalid_nv(void) {
|
||||||
ARRLEN(empty_name_nv), NULL));
|
ARRLEN(empty_name_nv), NULL));
|
||||||
|
|
||||||
nghttp2_session_del(session);
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, NULL));
|
||||||
|
|
||||||
|
/* nghttp2_submit_request */
|
||||||
|
CU_ASSERT(0 < nghttp2_submit_request(session, NULL, empty_name_nv,
|
||||||
|
ARRLEN(empty_name_nv), NULL, NULL));
|
||||||
|
|
||||||
|
/* nghttp2_submit_headers */
|
||||||
|
CU_ASSERT(0 < nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, -1, NULL,
|
||||||
|
empty_name_nv, ARRLEN(empty_name_nv),
|
||||||
|
NULL));
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_nghttp2_session_open_stream(void) {
|
void test_nghttp2_session_open_stream(void) {
|
||||||
|
|
Loading…
Reference in New Issue