ORIGIN frame support

This commit is contained in:
Lucas Pardue 2017-04-24 18:09:16 +01:00
parent cc289972fc
commit 2a0cf7c8d8
7 changed files with 218 additions and 3 deletions

View File

@ -597,7 +597,12 @@ typedef enum {
* The ALTSVC frame, which is defined in `RFC 7383
* <https://tools.ietf.org/html/rfc7838#section-4>`_.
*/
NGHTTP2_ALTSVC = 0x0a
NGHTTP2_ALTSVC = 0x0a,
/**
* The ORIGIN frame, whic is defined in `RFC XXXX
* https://tools.ietf.org/html/draft-ietf-httpbis-origin-frame-03#section-2`_.
*/
NGHTTP2_ORIGIN = 0x0c
} nghttp2_frame_type;
/**
@ -4477,6 +4482,66 @@ NGHTTP2_EXTERN int nghttp2_submit_altsvc(nghttp2_session *session,
const uint8_t *field_value,
size_t field_value_len);
/**
* @struct
*
* The payload of ORIGIN frame. ORIGIN frame is a non-critical
* extension to HTTP/2. 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 pointer to an origin which the sender believes this connection is or
* could be
* authoritative for. This is not necessarily NULL-terminated.
*/
uint8_t *origin;
/**
* The length of the |origin|.
*/
size_t origin_len;
} nghttp2_ext_origin;
/**
* @function
*
* Submits ORIGIN frame.
*
* ORIGIN frame is a non-critical extension to HTTP/2, and defined in
* `RFC XXX
* <https://tools.ietf.org/html/draft-ietf-httpbis-origin-frame-03#section-2>`_.
*
* The |flags| is currently ignored and should be
* :enum:`NGHTTP2_FLAG_NONE`.
*
* The |origin| points to the origin which the sender believes this connection
* is or could be
* authoritative for
*
* The ORIGIN frame is only usable from server side. If this function
* is invoked with client side session, this function returns
* :enum:`NGHTTP2_ERR_INVALID_STATE`.
*
* 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_INVALID_STATE`
* The function is called from client side session
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
* The |origin_len| is larger than
* 16382.
*/
NGHTTP2_EXTERN int nghttp2_submit_origin(nghttp2_session *session,
uint8_t flags, const uint8_t *origin,
size_t origin_len);
/**
* @function
*

View File

@ -196,6 +196,26 @@ void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type,
void nghttp2_frame_extension_free(nghttp2_extension *frame) { (void)frame; }
void nghttp2_frame_origin_init(nghttp2_extension *frame, uint8_t *origin,
size_t origin_len) {
nghttp2_ext_origin *origin_frame;
/* Always send ORIGIN frame on stream 0 */
nghttp2_frame_hd_init(&frame->hd, 2 + origin_len, NGHTTP2_ORIGIN,
NGHTTP2_FLAG_NONE, 0);
origin_frame = frame->payload;
origin_frame->origin = origin;
origin_frame->origin_len = origin_len;
}
void nghttp2_frame_origin_free(nghttp2_extension *frame, nghttp2_mem *mem) {
nghttp2_ext_origin *origin_frame;
origin_frame = frame->payload;
nghttp2_mem_free(mem, origin_frame->origin);
}
void nghttp2_frame_altsvc_init(nghttp2_extension *frame, int32_t stream_id,
uint8_t *origin, size_t origin_len,
uint8_t *field_value, size_t field_value_len) {

View File

@ -70,7 +70,10 @@
#define NGHTTP2_MAX_PADLEN 256
/* Union of extension frame payload */
typedef union { nghttp2_ext_altsvc altsvc; } nghttp2_ext_frame_payload;
typedef union {
nghttp2_ext_altsvc altsvc;
nghttp2_ext_origin origin;
} nghttp2_ext_frame_payload;
void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd);
@ -487,6 +490,22 @@ void nghttp2_frame_altsvc_init(nghttp2_extension *frame, int32_t stream_id,
*/
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.
* On success, this function takes ownership of
* |origin|, so caller must not free it.
*/
void nghttp2_frame_origin_init(nghttp2_extension *frame, uint8_t *origin,
size_t origin_len);
/*
* Frees up resources under |frame|. This function does not free
* nghttp2_ext_origin object pointed by frame->payload. This function
* only frees origin pointed by nghttp2_ext_origin.origin.
*/
void nghttp2_frame_origin_free(nghttp2_extension *frame, nghttp2_mem *mem);
/*
* Returns the number of padding bytes after payload. The total
* padding length is given in the |padlen|. The returned value does

View File

@ -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->builtin_recv_ext_types |= NGHTTP2_TYPEMASK_ALTSVC;
return;
case NGHTTP2_ORIGIN:
option->opt_set_mask |= NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES;
option->builtin_recv_ext_types |= NGHTTP2_TYPEMASK_ORIGIN;
return;
default:
return;
}

View File

@ -337,6 +337,12 @@ static void session_inbound_frame_reset(nghttp2_session *session) {
}
nghttp2_frame_altsvc_free(&iframe->frame.ext, mem);
break;
case NGHTTP2_ORIGIN:
if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ORIGIN) == 0) {
break;
}
nghttp2_frame_origin_free(&iframe->frame.ext, mem);
break;
}
}
@ -5746,7 +5752,37 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
iframe->state = NGHTTP2_IB_READ_NBYTE;
inbound_frame_set_mark(iframe, 2);
case NGHTTP2_ORIGIN:
if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ORIGIN) ==
0) {
busy = 1;
iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
break;
}
DEBUGF("recv: ORIGIN\n");
iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
iframe->frame.ext.payload = &iframe->ext_frame_payload.origin;
if (session->server) {
busy = 1;
iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
break;
}
if (iframe->payloadleft < 2) {
busy = 1;
iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
break;
}
busy = 1;
iframe->state = NGHTTP2_IB_READ_NBYTE;
inbound_frame_set_mark(iframe, 2);
break;
break;
default:
busy = 1;

View File

@ -61,7 +61,8 @@ typedef enum {
*/
typedef enum {
NGHTTP2_TYPEMASK_NONE = 0,
NGHTTP2_TYPEMASK_ALTSVC = 1 << 0
NGHTTP2_TYPEMASK_ALTSVC = 1 << 0,
NGHTTP2_TYPEMASK_ORIGIN = 2 << 0
} nghttp2_typemask;
typedef enum {

View File

@ -486,6 +486,76 @@ int nghttp2_session_set_local_window_size(nghttp2_session *session,
return 0;
}
int nghttp2_submit_origin(nghttp2_session *session, uint8_t flags,
const uint8_t *origin, size_t origin_len) {
nghttp2_mem *mem;
uint8_t *buf, *p;
uint8_t *origin_copy;
nghttp2_outbound_item *item;
nghttp2_frame *frame;
nghttp2_ext_origin *origin_frame;
int rv;
(void)flags;
mem = &session->mem;
if (!session->server) {
return NGHTTP2_ERR_INVALID_STATE;
}
if (2 + origin_len > NGHTTP2_MAX_PAYLOADLEN) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
buf = nghttp2_mem_malloc(mem, origin_len + 2);
if (buf == NULL) {
return NGHTTP2_ERR_NOMEM;
}
p = buf;
origin_copy = p;
if (origin_len) {
p = nghttp2_cpymem(p, origin, origin_len);
}
*p++ = '\0';
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_frame = &item->ext_frame_payload.origin;
frame = &item->frame;
frame->ext.payload = origin_frame;
//
// nghttp2_frame_altsvc_init(&frame->ext, stream_id, origin_copy, origin_len,
// field_value_copy, field_value_len);
nghttp2_frame_origin_init(&frame->ext, origin_copy, origin_len);
rv = nghttp2_session_add_item(session, item);
if (rv != 0) {
nghttp2_frame_altsvc_free(&frame->ext, mem);
nghttp2_mem_free(mem, item);
return rv;
}
return 0;
fail_item_malloc:
free(buf);
return rv;
}
int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags,
int32_t stream_id, const uint8_t *origin,
size_t origin_len, const uint8_t *field_value,