altsvc: Add ALTSVC frame support

This commit is contained in:
Tatsuhiro Tsujikawa 2016-03-29 23:29:26 +09:00
parent 4a6fc6cede
commit ecabef2dc7
7 changed files with 254 additions and 10 deletions

View File

@ -591,7 +591,11 @@ typedef enum {
* callbacks because the library processes this frame type and its
* preceding HEADERS/PUSH_PROMISE as a single frame.
*/
NGHTTP2_CONTINUATION = 0x09
NGHTTP2_CONTINUATION = 0x09,
/**
* The ALTSVC frame.
*/
NGHTTP2_ALTSVC = 0x0a
} nghttp2_frame_type;
/**
@ -4113,6 +4117,32 @@ NGHTTP2_EXTERN int nghttp2_submit_extension(nghttp2_session *session,
uint8_t type, uint8_t flags,
int32_t stream_id, void *payload);
/**
* @struct
*
* The payload of ALTSVC frame. ALTSVC frame is one of extension
* frame. If this frame is received, and
* `nghttp2_option_set_user_recv_extension_type()` is not set, and
* `nghttp2_option_set_recv_extension_type()` is set for
* :enum:`NGHTTP2_ALTSVC`, ``nghttp2_extension.payload`` will point to
* this struct.
*
* It has the following members:
*/
typedef struct {
uint8_t *origin;
size_t origin_len;
uint8_t *field_value;
size_t field_value_len;
} nghttp2_ext_altsvc;
NGHTTP2_EXTERN 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,
size_t field_value_len);
/**
* @function
*

View File

@ -193,6 +193,31 @@ void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type,
void nghttp2_frame_extension_free(nghttp2_extension *frame _U_) {}
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) {
nghttp2_ext_altsvc *altsvc;
nghttp2_frame_hd_init(&frame->hd, 2 + origin_len + field_value_len,
NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, stream_id);
altsvc = frame->payload;
altsvc->origin = origin;
altsvc->origin_len = origin_len;
altsvc->field_value = field_value;
altsvc->field_value_len = field_value_len;
}
void nghttp2_frame_altsvc_free(nghttp2_extension *frame, nghttp2_mem *mem) {
nghttp2_ext_altsvc *altsvc;
altsvc = frame->payload;
/* We use the same buffer for altsvc->origin and
altsvc->field_value. */
nghttp2_mem_free(mem, altsvc->origin);
nghttp2_mem_free(mem, altsvc);
}
size_t nghttp2_frame_priority_len(uint8_t flags) {
if (flags & NGHTTP2_FLAG_PRIORITY) {
return NGHTTP2_PRIORITY_SPECLEN;
@ -668,6 +693,36 @@ void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame,
nghttp2_get_uint32(payload) & NGHTTP2_WINDOW_SIZE_INCREMENT_MASK;
}
int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *frame) {
int rv;
nghttp2_buf *buf;
nghttp2_ext_altsvc *altsvc;
altsvc = frame->payload;
buf = &bufs->head->buf;
assert(nghttp2_buf_avail(buf) >=
2 + altsvc->origin_len + altsvc->field_value_len);
buf->pos -= NGHTTP2_FRAME_HDLEN;
nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
nghttp2_put_uint16be(buf->last, (uint16_t)altsvc->origin_len);
buf->last += 2;
rv = nghttp2_bufs_add(bufs, altsvc->origin, altsvc->origin_len);
assert(rv == 0);
rv = nghttp2_bufs_add(bufs, altsvc->field_value, altsvc->field_value_len);
assert(rv == 0);
return 0;
}
nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv,
size_t niv, nghttp2_mem *mem) {
nghttp2_settings_entry *iv_copy;

View File

@ -367,6 +367,17 @@ void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame,
const uint8_t *payload,
size_t payloadlen);
/*
* Packs ALTSVC 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 always succeeds and returns 0.
*/
int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *ext);
/*
* Initializes HEADERS frame |frame| with given values. |frame| takes
* ownership of |nva|, so caller must not free it. If |stream_id| is
@ -445,6 +456,12 @@ void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type,
void nghttp2_frame_extension_free(nghttp2_extension *frame);
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);
void nghttp2_frame_altsvc_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

@ -72,9 +72,25 @@ void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) {
case NGHTTP2_WINDOW_UPDATE:
nghttp2_frame_window_update_free(&frame->window_update);
break;
default:
nghttp2_frame_extension_free(&frame->ext);
break;
default: {
nghttp2_ext_aux_data *aux_data;
aux_data = &item->aux_data.ext;
if (aux_data->builtin == 0) {
nghttp2_frame_extension_free(&frame->ext);
break;
}
switch (frame->hd.type) {
case NGHTTP2_ALTSVC:
nghttp2_frame_altsvc_free(&frame->ext, mem);
break;
default:
assert(0);
break;
}
}
}
}

View File

@ -87,11 +87,19 @@ typedef struct {
uint8_t flags;
} nghttp2_goaway_aux_data;
/* struct used for extension frame */
typedef struct {
/* nonzero if this extension frame is serialized by library
function, instead of user-defined callbacks. */
uint8_t builtin;
} nghttp2_ext_aux_data;
/* Additional data which cannot be stored in nghttp2_frame struct */
typedef union {
nghttp2_data_aux_data data;
nghttp2_headers_aux_data headers;
nghttp2_goaway_aux_data goaway;
nghttp2_ext_aux_data ext;
} nghttp2_aux_data;
struct nghttp2_outbound_item;

View File

@ -1664,6 +1664,26 @@ static int session_predicate_window_update_send(nghttp2_session *session,
return 0;
}
static int session_predicate_altsvc_send(nghttp2_session *session,
int32_t stream_id) {
nghttp2_stream *stream;
if (session_is_closing(session)) {
return NGHTTP2_ERR_SESSION_CLOSING;
}
if (stream_id == 0) {
return 0;
}
stream = nghttp2_session_get_stream(session, stream_id);
if (stream == NULL) {
return NGHTTP2_ERR_STREAM_CLOSED;
}
return 0;
}
/* Take into account settings max frame size and both connection-level
flow control here */
static ssize_t
@ -2103,19 +2123,45 @@ static int session_prep_frame(nghttp2_session *session,
/* We never handle CONTINUATION here. */
assert(0);
break;
default:
default: {
nghttp2_ext_aux_data *aux_data;
/* extension frame */
if (session_is_closing(session)) {
return NGHTTP2_ERR_SESSION_CLOSING;
aux_data = &item->aux_data.ext;
if (aux_data->builtin == 0) {
if (session_is_closing(session)) {
return NGHTTP2_ERR_SESSION_CLOSING;
}
rv = session_pack_extension(session, &session->aob.framebufs, frame);
if (rv != 0) {
return rv;
}
break;
}
rv = session_pack_extension(session, &session->aob.framebufs, frame);
if (rv != 0) {
return rv;
switch (frame->hd.type) {
case NGHTTP2_ALTSVC:
rv = session_predicate_altsvc_send(session, frame->hd.stream_id);
if (rv != 0) {
return rv;
}
nghttp2_frame_pack_altsvc(&session->aob.framebufs, &frame->ext);
break;
default:
/* Unreachable here */
assert(0);
break;
}
break;
}
}
return 0;
} else {
size_t next_readmax;

View File

@ -410,6 +410,78 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
return 0;
}
int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags _U_,
int32_t stream_id, const uint8_t *origin,
size_t origin_len, const uint8_t *field_value,
size_t field_value_len) {
nghttp2_mem *mem;
uint8_t *buf, *p;
uint8_t *origin_copy;
uint8_t *field_value_copy;
nghttp2_outbound_item *item;
nghttp2_frame *frame;
nghttp2_ext_altsvc *altsvc;
int rv;
mem = &session->mem;
if (2 + origin_len + field_value_len > NGHTTP2_MAX_PAYLOADLEN) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
buf = nghttp2_mem_malloc(mem, origin_len + field_value_len + 2);
if (buf == NULL) {
return NGHTTP2_ERR_NOMEM;
}
p = buf;
origin_copy = p;
p = nghttp2_cpymem(p, origin, origin_len);
*p++ = '\0';
field_value_copy = p;
p = nghttp2_cpymem(p, field_value, field_value_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;
altsvc = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
if (item == NULL) {
rv = NGHTTP2_ERR_NOMEM;
goto fail_altsvc_malloc;
}
frame = &item->frame;
frame->ext.payload = altsvc;
nghttp2_frame_altsvc_init(&frame->ext, stream_id, origin_copy, origin_len,
field_value_copy, field_value_len);
rv = nghttp2_session_add_item(session, item);
if (rv != 0) {
nghttp2_frame_altsvc_free(&frame->ext, mem);
nghttp2_mem_free(mem, item);
}
return 0;
fail_altsvc_malloc:
free(item);
fail_item_malloc:
free(buf);
return rv;
}
static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec,
const nghttp2_data_provider *data_prd) {
uint8_t flags = NGHTTP2_FLAG_NONE;