altsvc: Add ALTSVC frame support
This commit is contained in:
parent
4a6fc6cede
commit
ecabef2dc7
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -72,10 +72,26 @@ 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:
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,8 +2123,14 @@ 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 */
|
||||
|
||||
aux_data = &item->aux_data.ext;
|
||||
|
||||
if (aux_data->builtin == 0) {
|
||||
if (session_is_closing(session)) {
|
||||
return NGHTTP2_ERR_SESSION_CLOSING;
|
||||
}
|
||||
|
@ -2116,6 +2142,26 @@ static int session_prep_frame(nghttp2_session *session,
|
|||
|
||||
break;
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue