commit
a7e38724e7
|
@ -164,6 +164,7 @@ APIDOCS= \
|
||||||
nghttp2_submit_extension.rst \
|
nghttp2_submit_extension.rst \
|
||||||
nghttp2_submit_goaway.rst \
|
nghttp2_submit_goaway.rst \
|
||||||
nghttp2_submit_headers.rst \
|
nghttp2_submit_headers.rst \
|
||||||
|
nghttp2_submit_origin.rst \
|
||||||
nghttp2_submit_ping.rst \
|
nghttp2_submit_ping.rst \
|
||||||
nghttp2_submit_priority.rst \
|
nghttp2_submit_priority.rst \
|
||||||
nghttp2_submit_push_promise.rst \
|
nghttp2_submit_push_promise.rst \
|
||||||
|
|
|
@ -611,7 +611,12 @@ typedef enum {
|
||||||
* The ALTSVC frame, which is defined in `RFC 7383
|
* The ALTSVC frame, which is defined in `RFC 7383
|
||||||
* <https://tools.ietf.org/html/rfc7838#section-4>`_.
|
* <https://tools.ietf.org/html/rfc7838#section-4>`_.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_ALTSVC = 0x0a
|
NGHTTP2_ALTSVC = 0x0a,
|
||||||
|
/**
|
||||||
|
* The ORIGIN frame, which is defined by `RFC 8336
|
||||||
|
* <https://tools.ietf.org/html/rfc8336>`_.
|
||||||
|
*/
|
||||||
|
NGHTTP2_ORIGIN = 0x0c
|
||||||
} nghttp2_frame_type;
|
} nghttp2_frame_type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4551,6 +4556,80 @@ NGHTTP2_EXTERN int nghttp2_submit_altsvc(nghttp2_session *session,
|
||||||
const uint8_t *field_value,
|
const uint8_t *field_value,
|
||||||
size_t field_value_len);
|
size_t field_value_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @struct
|
||||||
|
*
|
||||||
|
* The single entry of an origin.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
/**
|
||||||
|
* The pointer to origin. No validation is made against this field
|
||||||
|
* by the library. This is not necessarily NULL-terminated.
|
||||||
|
*/
|
||||||
|
uint8_t *origin;
|
||||||
|
/**
|
||||||
|
* The length of the |origin|.
|
||||||
|
*/
|
||||||
|
size_t origin_len;
|
||||||
|
} nghttp2_origin_entry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @struct
|
||||||
|
*
|
||||||
|
* The payload of ORIGIN frame. ORIGIN frame is a non-critical
|
||||||
|
* extension to HTTP/2 and defined by `RFC 8336
|
||||||
|
* <https://tools.ietf.org/html/rfc8336>`_.
|
||||||
|
*
|
||||||
|
* If this frame is received, and
|
||||||
|
* `nghttp2_option_set_user_recv_extension_type()` is not set, and
|
||||||
|
* `nghttp2_option_set_builtin_recv_extension_type()` is set for
|
||||||
|
* :enum:`NGHTTP2_ORIGIN`, ``nghttp2_extension.payload`` will point to
|
||||||
|
* this struct.
|
||||||
|
*
|
||||||
|
* It has the following members:
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
/**
|
||||||
|
* The number of origins contained in |ov|.
|
||||||
|
*/
|
||||||
|
size_t nov;
|
||||||
|
/**
|
||||||
|
* The pointer to the array of origins contained in ORIGIN frame.
|
||||||
|
*/
|
||||||
|
nghttp2_origin_entry *ov;
|
||||||
|
} nghttp2_ext_origin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @function
|
||||||
|
*
|
||||||
|
* Submits ORIGIN frame.
|
||||||
|
*
|
||||||
|
* ORIGIN frame is a non-critical extension to HTTP/2 and defined by
|
||||||
|
* `RFC 8336 <https://tools.ietf.org/html/rfc8336>`_.
|
||||||
|
*
|
||||||
|
* The |flags| is currently ignored and should be
|
||||||
|
* :enum:`NGHTTP2_FLAG_NONE`.
|
||||||
|
*
|
||||||
|
* The |ov| points to the array of origins. The |nov| specifies the
|
||||||
|
* number of origins included in |ov|.
|
||||||
|
*
|
||||||
|
* The ORIGIN frame is only usable by a server. If this function is
|
||||||
|
* invoked with client side session, this function returns
|
||||||
|
* :enum:`NGHTTP2_ERR_INVALID_STATE`.
|
||||||
|
*
|
||||||
|
* :enum:`NGHTTP2_ERR_NOMEM`
|
||||||
|
* Out of memory
|
||||||
|
* :enum:`NGHTTP2_ERR_INVALID_STATE`
|
||||||
|
* The function is called from client side session.
|
||||||
|
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
|
||||||
|
* There are too many origins, or an origin is too large to fit
|
||||||
|
* into a default frame payload.
|
||||||
|
*/
|
||||||
|
NGHTTP2_EXTERN int nghttp2_submit_origin(nghttp2_session *session,
|
||||||
|
uint8_t flags,
|
||||||
|
const nghttp2_origin_entry *ov,
|
||||||
|
size_t nov);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function
|
* @function
|
||||||
*
|
*
|
||||||
|
|
|
@ -223,6 +223,36 @@ void nghttp2_frame_altsvc_free(nghttp2_extension *frame, nghttp2_mem *mem) {
|
||||||
nghttp2_mem_free(mem, altsvc->origin);
|
nghttp2_mem_free(mem, altsvc->origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nghttp2_frame_origin_init(nghttp2_extension *frame,
|
||||||
|
nghttp2_origin_entry *ov, size_t nov) {
|
||||||
|
nghttp2_ext_origin *origin;
|
||||||
|
size_t payloadlen = 0;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < nov; ++i) {
|
||||||
|
payloadlen += 2 + ov[i].origin_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
nghttp2_frame_hd_init(&frame->hd, payloadlen, NGHTTP2_ORIGIN,
|
||||||
|
NGHTTP2_FLAG_NONE, 0);
|
||||||
|
|
||||||
|
origin = frame->payload;
|
||||||
|
origin->ov = ov;
|
||||||
|
origin->nov = nov;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nghttp2_frame_origin_free(nghttp2_extension *frame, nghttp2_mem *mem) {
|
||||||
|
nghttp2_ext_origin *origin;
|
||||||
|
|
||||||
|
origin = frame->payload;
|
||||||
|
if (origin == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* We use the same buffer for all resources pointed by the field of
|
||||||
|
origin directly or indirectly. */
|
||||||
|
nghttp2_mem_free(mem, origin->ov);
|
||||||
|
}
|
||||||
|
|
||||||
size_t nghttp2_frame_priority_len(uint8_t flags) {
|
size_t nghttp2_frame_priority_len(uint8_t flags) {
|
||||||
if (flags & NGHTTP2_FLAG_PRIORITY) {
|
if (flags & NGHTTP2_FLAG_PRIORITY) {
|
||||||
return NGHTTP2_PRIORITY_SPECLEN;
|
return NGHTTP2_PRIORITY_SPECLEN;
|
||||||
|
@ -746,6 +776,106 @@ int nghttp2_frame_unpack_altsvc_payload2(nghttp2_extension *frame,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int nghttp2_frame_pack_origin(nghttp2_bufs *bufs, nghttp2_extension *frame) {
|
||||||
|
nghttp2_buf *buf;
|
||||||
|
nghttp2_ext_origin *origin;
|
||||||
|
nghttp2_origin_entry *orig;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
origin = frame->payload;
|
||||||
|
|
||||||
|
buf = &bufs->head->buf;
|
||||||
|
|
||||||
|
if (nghttp2_buf_avail(buf) < frame->hd.length) {
|
||||||
|
return NGHTTP2_ERR_FRAME_SIZE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf->pos -= NGHTTP2_FRAME_HDLEN;
|
||||||
|
|
||||||
|
nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
|
||||||
|
|
||||||
|
for (i = 0; i < origin->nov; ++i) {
|
||||||
|
orig = &origin->ov[i];
|
||||||
|
nghttp2_put_uint16be(buf->last, (uint16_t)orig->origin_len);
|
||||||
|
buf->last += 2;
|
||||||
|
buf->last = nghttp2_cpymem(buf->last, orig->origin, orig->origin_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(nghttp2_buf_len(buf) == NGHTTP2_FRAME_HDLEN + frame->hd.length);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame,
|
||||||
|
const uint8_t *payload,
|
||||||
|
size_t payloadlen, nghttp2_mem *mem) {
|
||||||
|
nghttp2_ext_origin *origin;
|
||||||
|
const uint8_t *p, *end;
|
||||||
|
uint8_t *dst;
|
||||||
|
size_t originlen;
|
||||||
|
nghttp2_origin_entry *ov;
|
||||||
|
size_t nov = 0;
|
||||||
|
size_t len = 0;
|
||||||
|
|
||||||
|
origin = frame->payload;
|
||||||
|
p = payload;
|
||||||
|
end = p + payloadlen;
|
||||||
|
|
||||||
|
for (; p != end;) {
|
||||||
|
if (end - p < 2) {
|
||||||
|
return NGHTTP2_ERR_FRAME_SIZE_ERROR;
|
||||||
|
}
|
||||||
|
originlen = nghttp2_get_uint16(p);
|
||||||
|
p += 2;
|
||||||
|
if (originlen == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (originlen > (size_t)(end - p)) {
|
||||||
|
return NGHTTP2_ERR_FRAME_SIZE_ERROR;
|
||||||
|
}
|
||||||
|
p += originlen;
|
||||||
|
/* 1 for terminal NULL */
|
||||||
|
len += originlen + 1;
|
||||||
|
++nov;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nov == 0) {
|
||||||
|
origin->ov = NULL;
|
||||||
|
origin->nov = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
len += nov * sizeof(nghttp2_origin_entry);
|
||||||
|
|
||||||
|
ov = nghttp2_mem_malloc(mem, len);
|
||||||
|
if (ov == NULL) {
|
||||||
|
return NGHTTP2_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
origin->ov = ov;
|
||||||
|
origin->nov = nov;
|
||||||
|
|
||||||
|
dst = (uint8_t *)ov + nov * sizeof(nghttp2_origin_entry);
|
||||||
|
p = payload;
|
||||||
|
|
||||||
|
for (; p != end;) {
|
||||||
|
originlen = nghttp2_get_uint16(p);
|
||||||
|
p += 2;
|
||||||
|
if (originlen == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ov->origin = dst;
|
||||||
|
ov->origin_len = originlen;
|
||||||
|
dst = nghttp2_cpymem(dst, p, originlen);
|
||||||
|
*dst++ = '\0';
|
||||||
|
p += originlen;
|
||||||
|
++ov;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv,
|
nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv,
|
||||||
size_t niv, nghttp2_mem *mem) {
|
size_t niv, nghttp2_mem *mem) {
|
||||||
nghttp2_settings_entry *iv_copy;
|
nghttp2_settings_entry *iv_copy;
|
||||||
|
|
|
@ -72,6 +72,7 @@
|
||||||
/* Union of extension frame payload */
|
/* Union of extension frame payload */
|
||||||
typedef union {
|
typedef union {
|
||||||
nghttp2_ext_altsvc altsvc;
|
nghttp2_ext_altsvc altsvc;
|
||||||
|
nghttp2_ext_origin origin;
|
||||||
} nghttp2_ext_frame_payload;
|
} nghttp2_ext_frame_payload;
|
||||||
|
|
||||||
void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd);
|
void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd);
|
||||||
|
@ -392,6 +393,36 @@ int nghttp2_frame_unpack_altsvc_payload2(nghttp2_extension *frame,
|
||||||
const uint8_t *payload,
|
const uint8_t *payload,
|
||||||
size_t payloadlen, nghttp2_mem *mem);
|
size_t payloadlen, nghttp2_mem *mem);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Packs ORIGIN frame |frame| in wire frame format and store it in
|
||||||
|
* |bufs|.
|
||||||
|
*
|
||||||
|
* The caller must make sure that nghttp2_bufs_reset(bufs) is called
|
||||||
|
* before calling this function.
|
||||||
|
*
|
||||||
|
* This function returns 0 if it succeeds, or one of the following
|
||||||
|
* negative error codes:
|
||||||
|
*
|
||||||
|
* NGHTTP2_ERR_FRAME_SIZE_ERROR
|
||||||
|
* The length of the frame is too large.
|
||||||
|
*/
|
||||||
|
int nghttp2_frame_pack_origin(nghttp2_bufs *bufs, nghttp2_extension *ext);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unpacks ORIGIN wire format into |frame|. The |payload| of length
|
||||||
|
* |payloadlen| contains the frame payload.
|
||||||
|
*
|
||||||
|
* This function returns 0 if it succeeds, or one of the following
|
||||||
|
* negative error codes:
|
||||||
|
*
|
||||||
|
* NGHTTP2_ERR_NOMEM
|
||||||
|
* Out of memory.
|
||||||
|
* NGHTTP2_ERR_FRAME_SIZE_ERROR
|
||||||
|
* The payload is too small.
|
||||||
|
*/
|
||||||
|
int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame,
|
||||||
|
const uint8_t *payload,
|
||||||
|
size_t payloadlen, nghttp2_mem *mem);
|
||||||
/*
|
/*
|
||||||
* Initializes HEADERS frame |frame| with given values. |frame| takes
|
* Initializes HEADERS frame |frame| with given values. |frame| takes
|
||||||
* ownership of |nva|, so caller must not free it. If |stream_id| is
|
* ownership of |nva|, so caller must not free it. If |stream_id| is
|
||||||
|
@ -489,6 +520,24 @@ void nghttp2_frame_altsvc_init(nghttp2_extension *frame, int32_t stream_id,
|
||||||
*/
|
*/
|
||||||
void nghttp2_frame_altsvc_free(nghttp2_extension *frame, nghttp2_mem *mem);
|
void nghttp2_frame_altsvc_free(nghttp2_extension *frame, nghttp2_mem *mem);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initializes ORIGIN frame |frame| with given values. This function
|
||||||
|
* assumes that frame->payload points to nghttp2_ext_origin object.
|
||||||
|
* Also |ov| and the memory pointed by the field of its elements are
|
||||||
|
* allocated in single buffer, starting with |ov|. On success, this
|
||||||
|
* function takes ownership of |ov|, so caller must not free it.
|
||||||
|
*/
|
||||||
|
void nghttp2_frame_origin_init(nghttp2_extension *frame,
|
||||||
|
nghttp2_origin_entry *ov, size_t nov);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Frees up resources under |frame|. This function does not free
|
||||||
|
* nghttp2_ext_origin object pointed by frame->payload. This function
|
||||||
|
* only frees nghttp2_ext_origin.ov. Therefore, other fields must be
|
||||||
|
* allocated in the same buffer with ov.
|
||||||
|
*/
|
||||||
|
void nghttp2_frame_origin_free(nghttp2_extension *frame, nghttp2_mem *mem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns the number of padding bytes after payload. The total
|
* Returns the number of padding bytes after payload. The total
|
||||||
* padding length is given in the |padlen|. The returned value does
|
* padding length is given in the |padlen|. The returned value does
|
||||||
|
|
|
@ -86,6 +86,10 @@ void nghttp2_option_set_builtin_recv_extension_type(nghttp2_option *option,
|
||||||
option->opt_set_mask |= NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES;
|
option->opt_set_mask |= NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES;
|
||||||
option->builtin_recv_ext_types |= NGHTTP2_TYPEMASK_ALTSVC;
|
option->builtin_recv_ext_types |= NGHTTP2_TYPEMASK_ALTSVC;
|
||||||
return;
|
return;
|
||||||
|
case NGHTTP2_ORIGIN:
|
||||||
|
option->opt_set_mask |= NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES;
|
||||||
|
option->builtin_recv_ext_types |= NGHTTP2_TYPEMASK_ORIGIN;
|
||||||
|
return;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,6 +86,9 @@ void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) {
|
||||||
case NGHTTP2_ALTSVC:
|
case NGHTTP2_ALTSVC:
|
||||||
nghttp2_frame_altsvc_free(&frame->ext, mem);
|
nghttp2_frame_altsvc_free(&frame->ext, mem);
|
||||||
break;
|
break;
|
||||||
|
case NGHTTP2_ORIGIN:
|
||||||
|
nghttp2_frame_origin_free(&frame->ext, mem);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
assert(0);
|
assert(0);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -348,6 +348,12 @@ static void session_inbound_frame_reset(nghttp2_session *session) {
|
||||||
}
|
}
|
||||||
nghttp2_frame_altsvc_free(&iframe->frame.ext, mem);
|
nghttp2_frame_altsvc_free(&iframe->frame.ext, mem);
|
||||||
break;
|
break;
|
||||||
|
case NGHTTP2_ORIGIN:
|
||||||
|
if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ORIGIN) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nghttp2_frame_origin_free(&iframe->frame.ext, mem);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1749,6 +1755,13 @@ static int session_predicate_altsvc_send(nghttp2_session *session,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int session_predicate_origin_send(nghttp2_session *session) {
|
||||||
|
if (session_is_closing(session)) {
|
||||||
|
return NGHTTP2_ERR_SESSION_CLOSING;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Take into account settings max frame size and both connection-level
|
/* Take into account settings max frame size and both connection-level
|
||||||
flow control here */
|
flow control here */
|
||||||
static ssize_t
|
static ssize_t
|
||||||
|
@ -2280,6 +2293,18 @@ static int session_prep_frame(nghttp2_session *session,
|
||||||
|
|
||||||
nghttp2_frame_pack_altsvc(&session->aob.framebufs, &frame->ext);
|
nghttp2_frame_pack_altsvc(&session->aob.framebufs, &frame->ext);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
case NGHTTP2_ORIGIN:
|
||||||
|
rv = session_predicate_origin_send(session);
|
||||||
|
if (rv != 0) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = nghttp2_frame_pack_origin(&session->aob.framebufs, &frame->ext);
|
||||||
|
if (rv != 0) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
/* Unreachable here */
|
/* Unreachable here */
|
||||||
|
@ -4821,6 +4846,11 @@ int nghttp2_session_on_altsvc_received(nghttp2_session *session,
|
||||||
return session_call_on_frame_received(session, frame);
|
return session_call_on_frame_received(session, frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int nghttp2_session_on_origin_received(nghttp2_session *session,
|
||||||
|
nghttp2_frame *frame) {
|
||||||
|
return session_call_on_frame_received(session, frame);
|
||||||
|
}
|
||||||
|
|
||||||
static int session_process_altsvc_frame(nghttp2_session *session) {
|
static int session_process_altsvc_frame(nghttp2_session *session) {
|
||||||
nghttp2_inbound_frame *iframe = &session->iframe;
|
nghttp2_inbound_frame *iframe = &session->iframe;
|
||||||
nghttp2_frame *frame = &iframe->frame;
|
nghttp2_frame *frame = &iframe->frame;
|
||||||
|
@ -4836,6 +4866,25 @@ static int session_process_altsvc_frame(nghttp2_session *session) {
|
||||||
return nghttp2_session_on_altsvc_received(session, frame);
|
return nghttp2_session_on_altsvc_received(session, frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int session_process_origin_frame(nghttp2_session *session) {
|
||||||
|
nghttp2_inbound_frame *iframe = &session->iframe;
|
||||||
|
nghttp2_frame *frame = &iframe->frame;
|
||||||
|
nghttp2_mem *mem = &session->mem;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
rv = nghttp2_frame_unpack_origin_payload(&frame->ext, iframe->lbuf.pos,
|
||||||
|
nghttp2_buf_len(&iframe->lbuf), mem);
|
||||||
|
if (rv != 0) {
|
||||||
|
if (nghttp2_is_fatal(rv)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
/* Ignore ORIGIN frame which cannot be parsed. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nghttp2_session_on_origin_received(session, frame);
|
||||||
|
}
|
||||||
|
|
||||||
static int session_process_extension_frame(nghttp2_session *session) {
|
static int session_process_extension_frame(nghttp2_session *session) {
|
||||||
int rv;
|
int rv;
|
||||||
nghttp2_inbound_frame *iframe = &session->iframe;
|
nghttp2_inbound_frame *iframe = &session->iframe;
|
||||||
|
@ -5746,6 +5795,42 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
||||||
iframe->state = NGHTTP2_IB_READ_NBYTE;
|
iframe->state = NGHTTP2_IB_READ_NBYTE;
|
||||||
inbound_frame_set_mark(iframe, 2);
|
inbound_frame_set_mark(iframe, 2);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case NGHTTP2_ORIGIN:
|
||||||
|
if (!(session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ORIGIN)) {
|
||||||
|
busy = 1;
|
||||||
|
iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUGF("recv: ORIGIN\n");
|
||||||
|
|
||||||
|
iframe->frame.ext.payload = &iframe->ext_frame_payload.origin;
|
||||||
|
|
||||||
|
if (session->server || iframe->frame.hd.stream_id ||
|
||||||
|
(iframe->frame.hd.flags & 0xf0)) {
|
||||||
|
busy = 1;
|
||||||
|
iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
|
||||||
|
|
||||||
|
if (iframe->payloadleft) {
|
||||||
|
iframe->raw_lbuf = nghttp2_mem_malloc(mem, iframe->payloadleft);
|
||||||
|
|
||||||
|
if (iframe->raw_lbuf == NULL) {
|
||||||
|
return NGHTTP2_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf,
|
||||||
|
iframe->payloadleft);
|
||||||
|
} else {
|
||||||
|
busy = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe->state = NGHTTP2_IB_READ_ORIGIN_PAYLOAD;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
busy = 1;
|
busy = 1;
|
||||||
|
@ -6583,7 +6668,6 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
||||||
DEBUGF("recv: [IB_READ_ALTSVC_PAYLOAD]\n");
|
DEBUGF("recv: [IB_READ_ALTSVC_PAYLOAD]\n");
|
||||||
|
|
||||||
readlen = inbound_frame_payload_readlen(iframe, in, last);
|
readlen = inbound_frame_payload_readlen(iframe, in, last);
|
||||||
|
|
||||||
if (readlen > 0) {
|
if (readlen > 0) {
|
||||||
iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen);
|
iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen);
|
||||||
|
|
||||||
|
@ -6601,11 +6685,44 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
||||||
}
|
}
|
||||||
|
|
||||||
rv = session_process_altsvc_frame(session);
|
rv = session_process_altsvc_frame(session);
|
||||||
|
if (nghttp2_is_fatal(rv)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
session_inbound_frame_reset(session);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case NGHTTP2_IB_READ_ORIGIN_PAYLOAD:
|
||||||
|
DEBUGF("recv: [IB_READ_ORIGIN_PAYLOAD]\n");
|
||||||
|
|
||||||
|
readlen = inbound_frame_payload_readlen(iframe, in, last);
|
||||||
|
|
||||||
|
if (readlen > 0) {
|
||||||
|
iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen);
|
||||||
|
|
||||||
|
iframe->payloadleft -= readlen;
|
||||||
|
in += readlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
|
||||||
|
iframe->payloadleft);
|
||||||
|
|
||||||
|
if (iframe->payloadleft) {
|
||||||
|
assert(nghttp2_buf_avail(&iframe->lbuf) > 0);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = session_process_origin_frame(session);
|
||||||
|
|
||||||
if (nghttp2_is_fatal(rv)) {
|
if (nghttp2_is_fatal(rv)) {
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (iframe->state == NGHTTP2_IB_IGN_ALL) {
|
||||||
|
return (ssize_t)inlen;
|
||||||
|
}
|
||||||
|
|
||||||
session_inbound_frame_reset(session);
|
session_inbound_frame_reset(session);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -61,7 +61,8 @@ typedef enum {
|
||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
NGHTTP2_TYPEMASK_NONE = 0,
|
NGHTTP2_TYPEMASK_NONE = 0,
|
||||||
NGHTTP2_TYPEMASK_ALTSVC = 1 << 0
|
NGHTTP2_TYPEMASK_ALTSVC = 1 << 0,
|
||||||
|
NGHTTP2_TYPEMASK_ORIGIN = 1 << 1
|
||||||
} nghttp2_typemask;
|
} nghttp2_typemask;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -121,6 +122,7 @@ typedef enum {
|
||||||
NGHTTP2_IB_IGN_DATA,
|
NGHTTP2_IB_IGN_DATA,
|
||||||
NGHTTP2_IB_IGN_ALL,
|
NGHTTP2_IB_IGN_ALL,
|
||||||
NGHTTP2_IB_READ_ALTSVC_PAYLOAD,
|
NGHTTP2_IB_READ_ALTSVC_PAYLOAD,
|
||||||
|
NGHTTP2_IB_READ_ORIGIN_PAYLOAD,
|
||||||
NGHTTP2_IB_READ_EXTENSION_PAYLOAD
|
NGHTTP2_IB_READ_EXTENSION_PAYLOAD
|
||||||
} nghttp2_inbound_state;
|
} nghttp2_inbound_state;
|
||||||
|
|
||||||
|
@ -749,6 +751,19 @@ int nghttp2_session_on_window_update_received(nghttp2_session *session,
|
||||||
int nghttp2_session_on_altsvc_received(nghttp2_session *session,
|
int nghttp2_session_on_altsvc_received(nghttp2_session *session,
|
||||||
nghttp2_frame *frame);
|
nghttp2_frame *frame);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called when ORIGIN is received, assuming |frame| is properly
|
||||||
|
* initialized.
|
||||||
|
*
|
||||||
|
* This function returns 0 if it succeeds, or one of the following
|
||||||
|
* negative error codes:
|
||||||
|
*
|
||||||
|
* NGHTTP2_ERR_CALLBACK_FAILURE
|
||||||
|
* The callback function failed.
|
||||||
|
*/
|
||||||
|
int nghttp2_session_on_origin_received(nghttp2_session *session,
|
||||||
|
nghttp2_frame *frame);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Called when DATA is received, assuming |frame| is properly
|
* Called when DATA is received, assuming |frame| is properly
|
||||||
* initialized.
|
* initialized.
|
||||||
|
|
|
@ -571,6 +571,89 @@ fail_item_malloc:
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int nghttp2_submit_origin(nghttp2_session *session, uint8_t flags,
|
||||||
|
const nghttp2_origin_entry *ov, size_t nov) {
|
||||||
|
nghttp2_mem *mem;
|
||||||
|
uint8_t *p;
|
||||||
|
nghttp2_outbound_item *item;
|
||||||
|
nghttp2_frame *frame;
|
||||||
|
nghttp2_ext_origin *origin;
|
||||||
|
nghttp2_origin_entry *ov_copy;
|
||||||
|
size_t len = 0;
|
||||||
|
size_t i;
|
||||||
|
int rv;
|
||||||
|
(void)flags;
|
||||||
|
|
||||||
|
mem = &session->mem;
|
||||||
|
|
||||||
|
if (!session->server) {
|
||||||
|
return NGHTTP2_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nov) {
|
||||||
|
for (i = 0; i < nov; ++i) {
|
||||||
|
len += ov[i].origin_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (2 * nov + len > NGHTTP2_MAX_PAYLOADLEN) {
|
||||||
|
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The last nov is added for terminal NULL character. */
|
||||||
|
ov_copy =
|
||||||
|
nghttp2_mem_malloc(mem, nov * sizeof(nghttp2_origin_entry) + len + nov);
|
||||||
|
if (ov_copy == NULL) {
|
||||||
|
return NGHTTP2_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = (uint8_t *)ov_copy + nov * sizeof(nghttp2_origin_entry);
|
||||||
|
|
||||||
|
for (i = 0; i < nov; ++i) {
|
||||||
|
ov_copy[i].origin = p;
|
||||||
|
ov_copy[i].origin_len = ov[i].origin_len;
|
||||||
|
p = nghttp2_cpymem(p, ov[i].origin, ov[i].origin_len);
|
||||||
|
*p++ = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
assert((size_t)(p - (uint8_t *)ov_copy) ==
|
||||||
|
nov * sizeof(nghttp2_origin_entry) + len + nov);
|
||||||
|
} else {
|
||||||
|
ov_copy = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
|
||||||
|
if (item == NULL) {
|
||||||
|
rv = NGHTTP2_ERR_NOMEM;
|
||||||
|
goto fail_item_malloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
nghttp2_outbound_item_init(item);
|
||||||
|
|
||||||
|
item->aux_data.ext.builtin = 1;
|
||||||
|
|
||||||
|
origin = &item->ext_frame_payload.origin;
|
||||||
|
|
||||||
|
frame = &item->frame;
|
||||||
|
frame->ext.payload = origin;
|
||||||
|
|
||||||
|
nghttp2_frame_origin_init(&frame->ext, ov_copy, nov);
|
||||||
|
|
||||||
|
rv = nghttp2_session_add_item(session, item);
|
||||||
|
if (rv != 0) {
|
||||||
|
nghttp2_frame_origin_free(&frame->ext, mem);
|
||||||
|
nghttp2_mem_free(mem, item);
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail_item_malloc:
|
||||||
|
free(ov_copy);
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec,
|
static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec,
|
||||||
const nghttp2_data_provider *data_prd) {
|
const nghttp2_data_provider *data_prd) {
|
||||||
uint8_t flags = NGHTTP2_FLAG_NONE;
|
uint8_t flags = NGHTTP2_FLAG_NONE;
|
||||||
|
|
|
@ -106,6 +106,8 @@ std::string strframetype(uint8_t type) {
|
||||||
return "WINDOW_UPDATE";
|
return "WINDOW_UPDATE";
|
||||||
case NGHTTP2_ALTSVC:
|
case NGHTTP2_ALTSVC:
|
||||||
return "ALTSVC";
|
return "ALTSVC";
|
||||||
|
case NGHTTP2_ORIGIN:
|
||||||
|
return "ORIGIN";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string s = "extension(0x";
|
std::string s = "extension(0x";
|
||||||
|
@ -351,6 +353,15 @@ void print_frame(print_type ptype, const nghttp2_frame *frame) {
|
||||||
static_cast<int>(altsvc->field_value_len), altsvc->field_value);
|
static_cast<int>(altsvc->field_value_len), altsvc->field_value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case NGHTTP2_ORIGIN: {
|
||||||
|
auto origin = static_cast<nghttp2_ext_origin *>(frame->ext.payload);
|
||||||
|
for (size_t i = 0; i < origin->nov; ++i) {
|
||||||
|
auto ent = &origin->ov[i];
|
||||||
|
print_frame_attr_indent();
|
||||||
|
fprintf(outfile, "[%.*s]\n", (int)ent->origin_len, ent->origin);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,6 +127,7 @@ Config::Config()
|
||||||
nghttp2_option_set_peer_max_concurrent_streams(http2_option,
|
nghttp2_option_set_peer_max_concurrent_streams(http2_option,
|
||||||
peer_max_concurrent_streams);
|
peer_max_concurrent_streams);
|
||||||
nghttp2_option_set_builtin_recv_extension_type(http2_option, NGHTTP2_ALTSVC);
|
nghttp2_option_set_builtin_recv_extension_type(http2_option, NGHTTP2_ALTSVC);
|
||||||
|
nghttp2_option_set_builtin_recv_extension_type(http2_option, NGHTTP2_ORIGIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
Config::~Config() { nghttp2_option_del(http2_option); }
|
Config::~Config() { nghttp2_option_del(http2_option); }
|
||||||
|
|
|
@ -224,6 +224,11 @@ static void run_nghttp2_session_send_server(void) {
|
||||||
ssize_t txdatalen;
|
ssize_t txdatalen;
|
||||||
const uint8_t origin[] = "nghttp2.org";
|
const uint8_t origin[] = "nghttp2.org";
|
||||||
const uint8_t altsvc_field_value[] = "h2=\":443\"";
|
const uint8_t altsvc_field_value[] = "h2=\":443\"";
|
||||||
|
static const uint8_t nghttp2[] = "https://nghttp2.org";
|
||||||
|
static const nghttp2_origin_entry ov = {
|
||||||
|
(uint8_t *)nghttp2,
|
||||||
|
sizeof(nghttp2) - 1,
|
||||||
|
};
|
||||||
|
|
||||||
rv = nghttp2_session_callbacks_new(&callbacks);
|
rv = nghttp2_session_callbacks_new(&callbacks);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
|
@ -246,6 +251,11 @@ static void run_nghttp2_session_send_server(void) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rv = nghttp2_submit_origin(session, NGHTTP2_FLAG_NONE, &ov, 1);
|
||||||
|
if (rv != 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
txdatalen = nghttp2_session_mem_send(session, &txdata);
|
txdatalen = nghttp2_session_mem_send(session, &txdata);
|
||||||
|
|
||||||
if (txdatalen < 0) {
|
if (txdatalen < 0) {
|
||||||
|
|
|
@ -107,6 +107,8 @@ int main() {
|
||||||
test_nghttp2_session_recv_extension) ||
|
test_nghttp2_session_recv_extension) ||
|
||||||
!CU_add_test(pSuite, "session_recv_altsvc",
|
!CU_add_test(pSuite, "session_recv_altsvc",
|
||||||
test_nghttp2_session_recv_altsvc) ||
|
test_nghttp2_session_recv_altsvc) ||
|
||||||
|
!CU_add_test(pSuite, "session_recv_origin",
|
||||||
|
test_nghttp2_session_recv_origin) ||
|
||||||
!CU_add_test(pSuite, "session_continue", test_nghttp2_session_continue) ||
|
!CU_add_test(pSuite, "session_continue", test_nghttp2_session_continue) ||
|
||||||
!CU_add_test(pSuite, "session_add_frame",
|
!CU_add_test(pSuite, "session_add_frame",
|
||||||
test_nghttp2_session_add_frame) ||
|
test_nghttp2_session_add_frame) ||
|
||||||
|
@ -206,6 +208,7 @@ int main() {
|
||||||
test_nghttp2_submit_invalid_nv) ||
|
test_nghttp2_submit_invalid_nv) ||
|
||||||
!CU_add_test(pSuite, "submit_extension", test_nghttp2_submit_extension) ||
|
!CU_add_test(pSuite, "submit_extension", test_nghttp2_submit_extension) ||
|
||||||
!CU_add_test(pSuite, "submit_altsvc", test_nghttp2_submit_altsvc) ||
|
!CU_add_test(pSuite, "submit_altsvc", test_nghttp2_submit_altsvc) ||
|
||||||
|
!CU_add_test(pSuite, "submit_origin", test_nghttp2_submit_origin) ||
|
||||||
!CU_add_test(pSuite, "session_open_stream",
|
!CU_add_test(pSuite, "session_open_stream",
|
||||||
test_nghttp2_session_open_stream) ||
|
test_nghttp2_session_open_stream) ||
|
||||||
!CU_add_test(pSuite, "session_open_stream_with_idle_stream_dep",
|
!CU_add_test(pSuite, "session_open_stream_with_idle_stream_dep",
|
||||||
|
@ -355,6 +358,8 @@ int main() {
|
||||||
test_nghttp2_frame_pack_window_update) ||
|
test_nghttp2_frame_pack_window_update) ||
|
||||||
!CU_add_test(pSuite, "frame_pack_altsvc",
|
!CU_add_test(pSuite, "frame_pack_altsvc",
|
||||||
test_nghttp2_frame_pack_altsvc) ||
|
test_nghttp2_frame_pack_altsvc) ||
|
||||||
|
!CU_add_test(pSuite, "frame_pack_origin",
|
||||||
|
test_nghttp2_frame_pack_origin) ||
|
||||||
!CU_add_test(pSuite, "nv_array_copy", test_nghttp2_nv_array_copy) ||
|
!CU_add_test(pSuite, "nv_array_copy", test_nghttp2_nv_array_copy) ||
|
||||||
!CU_add_test(pSuite, "iv_check", test_nghttp2_iv_check) ||
|
!CU_add_test(pSuite, "iv_check", test_nghttp2_iv_check) ||
|
||||||
!CU_add_test(pSuite, "hd_deflate", test_nghttp2_hd_deflate) ||
|
!CU_add_test(pSuite, "hd_deflate", test_nghttp2_hd_deflate) ||
|
||||||
|
|
|
@ -508,6 +508,98 @@ void test_nghttp2_frame_pack_altsvc(void) {
|
||||||
nghttp2_bufs_free(&bufs);
|
nghttp2_bufs_free(&bufs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_nghttp2_frame_pack_origin(void) {
|
||||||
|
nghttp2_extension frame, oframe;
|
||||||
|
nghttp2_ext_origin origin, oorigin;
|
||||||
|
nghttp2_bufs bufs;
|
||||||
|
nghttp2_buf *buf;
|
||||||
|
int rv;
|
||||||
|
size_t payloadlen;
|
||||||
|
static const uint8_t example[] = "https://example.com";
|
||||||
|
static const uint8_t nghttp2[] = "https://nghttp2.org";
|
||||||
|
nghttp2_origin_entry ov[] = {
|
||||||
|
{
|
||||||
|
(uint8_t *)example,
|
||||||
|
sizeof(example) - 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
(uint8_t *)nghttp2,
|
||||||
|
sizeof(nghttp2) - 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
nghttp2_mem *mem;
|
||||||
|
|
||||||
|
mem = nghttp2_mem_default();
|
||||||
|
|
||||||
|
frame_pack_bufs_init(&bufs);
|
||||||
|
|
||||||
|
frame.payload = &origin;
|
||||||
|
oframe.payload = &oorigin;
|
||||||
|
|
||||||
|
nghttp2_frame_origin_init(&frame, ov, 3);
|
||||||
|
|
||||||
|
payloadlen = 2 + sizeof(example) - 1 + 2 + 2 + sizeof(nghttp2) - 1;
|
||||||
|
|
||||||
|
rv = nghttp2_frame_pack_origin(&bufs, &frame);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == rv);
|
||||||
|
CU_ASSERT(NGHTTP2_FRAME_HDLEN + payloadlen == nghttp2_bufs_len(&bufs));
|
||||||
|
|
||||||
|
rv = unpack_framebuf((nghttp2_frame *)&oframe, &bufs);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == rv);
|
||||||
|
|
||||||
|
check_frame_header(payloadlen, NGHTTP2_ORIGIN, NGHTTP2_FLAG_NONE, 0,
|
||||||
|
&oframe.hd);
|
||||||
|
|
||||||
|
CU_ASSERT(2 == oorigin.nov);
|
||||||
|
CU_ASSERT(sizeof(example) - 1 == oorigin.ov[0].origin_len);
|
||||||
|
CU_ASSERT(0 == memcmp(example, oorigin.ov[0].origin, sizeof(example) - 1));
|
||||||
|
CU_ASSERT(sizeof(nghttp2) - 1 == oorigin.ov[1].origin_len);
|
||||||
|
CU_ASSERT(0 == memcmp(nghttp2, oorigin.ov[1].origin, sizeof(nghttp2) - 1));
|
||||||
|
|
||||||
|
nghttp2_frame_origin_free(&oframe, mem);
|
||||||
|
|
||||||
|
/* Check the case where origin length is too large */
|
||||||
|
buf = &bufs.head->buf;
|
||||||
|
nghttp2_put_uint16be(buf->pos + NGHTTP2_FRAME_HDLEN,
|
||||||
|
(uint16_t)(payloadlen - 1));
|
||||||
|
|
||||||
|
rv = unpack_framebuf((nghttp2_frame *)&oframe, &bufs);
|
||||||
|
|
||||||
|
CU_ASSERT(NGHTTP2_ERR_FRAME_SIZE_ERROR == rv);
|
||||||
|
|
||||||
|
nghttp2_bufs_reset(&bufs);
|
||||||
|
memset(&oframe, 0, sizeof(oframe));
|
||||||
|
memset(&oorigin, 0, sizeof(oorigin));
|
||||||
|
oframe.payload = &oorigin;
|
||||||
|
|
||||||
|
/* Empty ORIGIN frame */
|
||||||
|
nghttp2_frame_origin_init(&frame, NULL, 0);
|
||||||
|
|
||||||
|
rv = nghttp2_frame_pack_origin(&bufs, &frame);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == rv);
|
||||||
|
CU_ASSERT(NGHTTP2_FRAME_HDLEN == nghttp2_bufs_len(&bufs));
|
||||||
|
|
||||||
|
rv = unpack_framebuf((nghttp2_frame *)&oframe, &bufs);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == rv);
|
||||||
|
|
||||||
|
check_frame_header(0, NGHTTP2_ORIGIN, NGHTTP2_FLAG_NONE, 0, &oframe.hd);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == oorigin.nov);
|
||||||
|
CU_ASSERT(NULL == oorigin.ov);
|
||||||
|
|
||||||
|
nghttp2_frame_origin_free(&oframe, mem);
|
||||||
|
|
||||||
|
nghttp2_bufs_free(&bufs);
|
||||||
|
}
|
||||||
|
|
||||||
void test_nghttp2_nv_array_copy(void) {
|
void test_nghttp2_nv_array_copy(void) {
|
||||||
nghttp2_nv *nva;
|
nghttp2_nv *nva;
|
||||||
ssize_t rv;
|
ssize_t rv;
|
||||||
|
|
|
@ -39,6 +39,7 @@ void test_nghttp2_frame_pack_ping(void);
|
||||||
void test_nghttp2_frame_pack_goaway(void);
|
void test_nghttp2_frame_pack_goaway(void);
|
||||||
void test_nghttp2_frame_pack_window_update(void);
|
void test_nghttp2_frame_pack_window_update(void);
|
||||||
void test_nghttp2_frame_pack_altsvc(void);
|
void test_nghttp2_frame_pack_altsvc(void);
|
||||||
|
void test_nghttp2_frame_pack_origin(void);
|
||||||
void test_nghttp2_nv_array_copy(void);
|
void test_nghttp2_nv_array_copy(void);
|
||||||
void test_nghttp2_iv_check(void);
|
void test_nghttp2_iv_check(void);
|
||||||
|
|
||||||
|
|
|
@ -2436,6 +2436,153 @@ void test_nghttp2_session_recv_altsvc(void) {
|
||||||
nghttp2_option_del(option);
|
nghttp2_option_del(option);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_nghttp2_session_recv_origin(void) {
|
||||||
|
nghttp2_session *session;
|
||||||
|
nghttp2_session_callbacks callbacks;
|
||||||
|
my_user_data ud;
|
||||||
|
nghttp2_bufs bufs;
|
||||||
|
ssize_t rv;
|
||||||
|
nghttp2_option *option;
|
||||||
|
nghttp2_extension frame;
|
||||||
|
nghttp2_ext_origin origin;
|
||||||
|
nghttp2_origin_entry ov;
|
||||||
|
static const uint8_t nghttp2[] = "https://nghttp2.org";
|
||||||
|
|
||||||
|
frame_pack_bufs_init(&bufs);
|
||||||
|
|
||||||
|
frame.payload = &origin;
|
||||||
|
|
||||||
|
ov.origin = (uint8_t *)nghttp2;
|
||||||
|
ov.origin_len = sizeof(nghttp2) - 1;
|
||||||
|
|
||||||
|
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
|
||||||
|
|
||||||
|
callbacks.on_frame_recv_callback = on_frame_recv_callback;
|
||||||
|
|
||||||
|
nghttp2_option_new(&option);
|
||||||
|
nghttp2_option_set_builtin_recv_extension_type(option, NGHTTP2_ORIGIN);
|
||||||
|
|
||||||
|
nghttp2_session_client_new2(&session, &callbacks, &ud, option);
|
||||||
|
|
||||||
|
nghttp2_frame_origin_init(&frame, &ov, 1);
|
||||||
|
|
||||||
|
rv = nghttp2_frame_pack_origin(&bufs, &frame);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == rv);
|
||||||
|
|
||||||
|
ud.frame_recv_cb_called = 0;
|
||||||
|
rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
|
||||||
|
nghttp2_bufs_len(&bufs));
|
||||||
|
|
||||||
|
CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == rv);
|
||||||
|
CU_ASSERT(1 == ud.frame_recv_cb_called);
|
||||||
|
CU_ASSERT(NGHTTP2_ORIGIN == ud.recv_frame_hd.type);
|
||||||
|
CU_ASSERT(NGHTTP2_FLAG_NONE == ud.recv_frame_hd.flags);
|
||||||
|
CU_ASSERT(0 == ud.recv_frame_hd.stream_id);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
nghttp2_bufs_reset(&bufs);
|
||||||
|
|
||||||
|
/* The length of origin is larger than payload length. */
|
||||||
|
nghttp2_session_client_new2(&session, &callbacks, &ud, option);
|
||||||
|
|
||||||
|
nghttp2_frame_origin_init(&frame, &ov, 1);
|
||||||
|
rv = nghttp2_frame_pack_origin(&bufs, &frame);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == rv);
|
||||||
|
|
||||||
|
nghttp2_put_uint16be(bufs.head->buf.pos + NGHTTP2_FRAME_HDLEN,
|
||||||
|
(uint16_t)sizeof(nghttp2));
|
||||||
|
|
||||||
|
ud.frame_recv_cb_called = 0;
|
||||||
|
rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
|
||||||
|
nghttp2_bufs_len(&bufs));
|
||||||
|
|
||||||
|
CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == rv);
|
||||||
|
CU_ASSERT(0 == ud.frame_recv_cb_called);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
nghttp2_bufs_reset(&bufs);
|
||||||
|
|
||||||
|
/* A frame should be ignored if it is sent to a stream other than
|
||||||
|
stream 0. */
|
||||||
|
nghttp2_session_client_new2(&session, &callbacks, &ud, option);
|
||||||
|
|
||||||
|
nghttp2_frame_origin_init(&frame, &ov, 1);
|
||||||
|
frame.hd.stream_id = 1;
|
||||||
|
rv = nghttp2_frame_pack_origin(&bufs, &frame);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == rv);
|
||||||
|
|
||||||
|
ud.frame_recv_cb_called = 0;
|
||||||
|
rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
|
||||||
|
nghttp2_bufs_len(&bufs));
|
||||||
|
|
||||||
|
CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == rv);
|
||||||
|
CU_ASSERT(0 == ud.frame_recv_cb_called);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
nghttp2_bufs_reset(&bufs);
|
||||||
|
|
||||||
|
/* A frame should be ignored if the reserved flag is set */
|
||||||
|
nghttp2_session_client_new2(&session, &callbacks, &ud, option);
|
||||||
|
|
||||||
|
nghttp2_frame_origin_init(&frame, &ov, 1);
|
||||||
|
frame.hd.flags = 0xf0;
|
||||||
|
rv = nghttp2_frame_pack_origin(&bufs, &frame);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == rv);
|
||||||
|
|
||||||
|
ud.frame_recv_cb_called = 0;
|
||||||
|
rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
|
||||||
|
nghttp2_bufs_len(&bufs));
|
||||||
|
|
||||||
|
CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == rv);
|
||||||
|
CU_ASSERT(0 == ud.frame_recv_cb_called);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
nghttp2_bufs_reset(&bufs);
|
||||||
|
|
||||||
|
/* A frame should be ignored if it is received by a server. */
|
||||||
|
nghttp2_session_server_new2(&session, &callbacks, &ud, option);
|
||||||
|
|
||||||
|
nghttp2_frame_origin_init(&frame, &ov, 1);
|
||||||
|
rv = nghttp2_frame_pack_origin(&bufs, &frame);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == rv);
|
||||||
|
|
||||||
|
ud.frame_recv_cb_called = 0;
|
||||||
|
rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
|
||||||
|
nghttp2_bufs_len(&bufs));
|
||||||
|
|
||||||
|
CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == rv);
|
||||||
|
CU_ASSERT(0 == ud.frame_recv_cb_called);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
nghttp2_bufs_reset(&bufs);
|
||||||
|
|
||||||
|
/* Receiving empty ORIGIN frame */
|
||||||
|
nghttp2_session_client_new2(&session, &callbacks, &ud, option);
|
||||||
|
|
||||||
|
nghttp2_frame_origin_init(&frame, NULL, 0);
|
||||||
|
rv = nghttp2_frame_pack_origin(&bufs, &frame);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == rv);
|
||||||
|
|
||||||
|
ud.frame_recv_cb_called = 0;
|
||||||
|
rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
|
||||||
|
nghttp2_bufs_len(&bufs));
|
||||||
|
|
||||||
|
CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == rv);
|
||||||
|
CU_ASSERT(1 == ud.frame_recv_cb_called);
|
||||||
|
CU_ASSERT(NGHTTP2_ORIGIN == ud.recv_frame_hd.type);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
|
nghttp2_option_del(option);
|
||||||
|
nghttp2_bufs_free(&bufs);
|
||||||
|
}
|
||||||
|
|
||||||
void test_nghttp2_session_continue(void) {
|
void test_nghttp2_session_continue(void) {
|
||||||
nghttp2_session *session;
|
nghttp2_session *session;
|
||||||
nghttp2_session_callbacks callbacks;
|
nghttp2_session_callbacks callbacks;
|
||||||
|
@ -6036,6 +6183,95 @@ void test_nghttp2_submit_altsvc(void) {
|
||||||
nghttp2_session_del(session);
|
nghttp2_session_del(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_nghttp2_submit_origin(void) {
|
||||||
|
nghttp2_session *session;
|
||||||
|
nghttp2_session_callbacks callbacks;
|
||||||
|
my_user_data ud;
|
||||||
|
int rv;
|
||||||
|
ssize_t len;
|
||||||
|
const uint8_t *data;
|
||||||
|
static const uint8_t nghttp2[] = "https://nghttp2.org";
|
||||||
|
static const uint8_t examples[] = "https://examples.com";
|
||||||
|
static const nghttp2_origin_entry ov[] = {
|
||||||
|
{
|
||||||
|
(uint8_t *)nghttp2,
|
||||||
|
sizeof(nghttp2) - 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
(uint8_t *)examples,
|
||||||
|
sizeof(examples) - 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
nghttp2_frame frame;
|
||||||
|
nghttp2_ext_origin origin;
|
||||||
|
nghttp2_mem *mem;
|
||||||
|
|
||||||
|
mem = nghttp2_mem_default();
|
||||||
|
|
||||||
|
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
|
||||||
|
callbacks.on_frame_send_callback = on_frame_send_callback;
|
||||||
|
|
||||||
|
frame.ext.payload = &origin;
|
||||||
|
|
||||||
|
nghttp2_session_server_new(&session, &callbacks, &ud);
|
||||||
|
|
||||||
|
rv = nghttp2_submit_origin(session, NGHTTP2_FLAG_NONE, ov, 2);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == rv);
|
||||||
|
|
||||||
|
ud.frame_send_cb_called = 0;
|
||||||
|
len = nghttp2_session_mem_send(session, &data);
|
||||||
|
|
||||||
|
CU_ASSERT(len > 0);
|
||||||
|
CU_ASSERT(1 == ud.frame_send_cb_called);
|
||||||
|
|
||||||
|
nghttp2_frame_unpack_frame_hd(&frame.hd, data);
|
||||||
|
rv = nghttp2_frame_unpack_origin_payload(
|
||||||
|
&frame.ext, data + NGHTTP2_FRAME_HDLEN, (size_t)len - NGHTTP2_FRAME_HDLEN,
|
||||||
|
mem);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == rv);
|
||||||
|
CU_ASSERT(0 == frame.hd.stream_id);
|
||||||
|
CU_ASSERT(NGHTTP2_ORIGIN == frame.hd.type);
|
||||||
|
CU_ASSERT(2 == origin.nov);
|
||||||
|
CU_ASSERT(0 == memcmp(nghttp2, origin.ov[0].origin, sizeof(nghttp2) - 1));
|
||||||
|
CU_ASSERT(sizeof(nghttp2) - 1 == origin.ov[0].origin_len);
|
||||||
|
CU_ASSERT(0 == memcmp(examples, origin.ov[1].origin, sizeof(examples) - 1));
|
||||||
|
CU_ASSERT(sizeof(examples) - 1 == origin.ov[1].origin_len);
|
||||||
|
|
||||||
|
nghttp2_frame_origin_free(&frame.ext, mem);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
|
/* Submitting ORIGIN frame from client session is error */
|
||||||
|
nghttp2_session_client_new(&session, &callbacks, NULL);
|
||||||
|
|
||||||
|
rv = nghttp2_submit_origin(session, NGHTTP2_FLAG_NONE, ov, 1);
|
||||||
|
|
||||||
|
CU_ASSERT(NGHTTP2_ERR_INVALID_STATE == rv);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
|
/* Submitting empty ORIGIN frame */
|
||||||
|
nghttp2_session_server_new(&session, &callbacks, &ud);
|
||||||
|
|
||||||
|
rv = nghttp2_submit_origin(session, NGHTTP2_FLAG_NONE, NULL, 0);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == rv);
|
||||||
|
|
||||||
|
ud.frame_send_cb_called = 0;
|
||||||
|
len = nghttp2_session_mem_send(session, &data);
|
||||||
|
|
||||||
|
CU_ASSERT(len == NGHTTP2_FRAME_HDLEN);
|
||||||
|
CU_ASSERT(1 == ud.frame_send_cb_called);
|
||||||
|
|
||||||
|
nghttp2_frame_unpack_frame_hd(&frame.hd, data);
|
||||||
|
|
||||||
|
CU_ASSERT(NGHTTP2_ORIGIN == frame.hd.type);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
}
|
||||||
|
|
||||||
void test_nghttp2_session_open_stream(void) {
|
void test_nghttp2_session_open_stream(void) {
|
||||||
nghttp2_session *session;
|
nghttp2_session *session;
|
||||||
nghttp2_session_callbacks callbacks;
|
nghttp2_session_callbacks callbacks;
|
||||||
|
|
|
@ -47,6 +47,7 @@ void test_nghttp2_session_recv_settings_header_table_size(void);
|
||||||
void test_nghttp2_session_recv_too_large_frame_length(void);
|
void test_nghttp2_session_recv_too_large_frame_length(void);
|
||||||
void test_nghttp2_session_recv_extension(void);
|
void test_nghttp2_session_recv_extension(void);
|
||||||
void test_nghttp2_session_recv_altsvc(void);
|
void test_nghttp2_session_recv_altsvc(void);
|
||||||
|
void test_nghttp2_session_recv_origin(void);
|
||||||
void test_nghttp2_session_continue(void);
|
void test_nghttp2_session_continue(void);
|
||||||
void test_nghttp2_session_add_frame(void);
|
void test_nghttp2_session_add_frame(void);
|
||||||
void test_nghttp2_session_on_request_headers_received(void);
|
void test_nghttp2_session_on_request_headers_received(void);
|
||||||
|
@ -100,6 +101,7 @@ void test_nghttp2_submit_shutdown_notice(void);
|
||||||
void test_nghttp2_submit_invalid_nv(void);
|
void test_nghttp2_submit_invalid_nv(void);
|
||||||
void test_nghttp2_submit_extension(void);
|
void test_nghttp2_submit_extension(void);
|
||||||
void test_nghttp2_submit_altsvc(void);
|
void test_nghttp2_submit_altsvc(void);
|
||||||
|
void test_nghttp2_submit_origin(void);
|
||||||
void test_nghttp2_session_open_stream(void);
|
void test_nghttp2_session_open_stream(void);
|
||||||
void test_nghttp2_session_open_stream_with_idle_stream_dep(void);
|
void test_nghttp2_session_open_stream_with_idle_stream_dep(void);
|
||||||
void test_nghttp2_session_get_next_ob_item(void);
|
void test_nghttp2_session_get_next_ob_item(void);
|
||||||
|
|
|
@ -84,6 +84,10 @@ int unpack_frame(nghttp2_frame *frame, const uint8_t *in, size_t len) {
|
||||||
assert(payloadlen > 2);
|
assert(payloadlen > 2);
|
||||||
nghttp2_frame_unpack_altsvc_payload2(&frame->ext, payload, payloadlen, mem);
|
nghttp2_frame_unpack_altsvc_payload2(&frame->ext, payload, payloadlen, mem);
|
||||||
break;
|
break;
|
||||||
|
case NGHTTP2_ORIGIN:
|
||||||
|
rv = nghttp2_frame_unpack_origin_payload(&frame->ext, payload, payloadlen,
|
||||||
|
mem);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
/* Must not be reachable */
|
/* Must not be reachable */
|
||||||
assert(0);
|
assert(0);
|
||||||
|
|
Loading…
Reference in New Issue