altsvc: Receive ALTSVC frame
This commit is contained in:
parent
efbd48b122
commit
795ee8c20f
|
@ -2377,6 +2377,26 @@ NGHTTP2_EXTERN void
|
|||
nghttp2_option_set_user_recv_extension_type(nghttp2_option *option,
|
||||
uint8_t type);
|
||||
|
||||
/**
|
||||
* @ function
|
||||
*
|
||||
* Sets extension frame type the application is willing to receive
|
||||
* using builtin handler. The |type| is the extension frame type to
|
||||
* receive, and must be strictly greater than 0x9. Otherwise, this
|
||||
* function does nothing. The application can call this function
|
||||
* multiple times to set more than one frame type to receive. The
|
||||
* application does not have to call this function if it just sends
|
||||
* extension frames.
|
||||
*
|
||||
* If same frame type is passed to both
|
||||
* `nghttp2_option_set_builtin_recv_extension_type()` and
|
||||
* `nghttp2_option_set_user_recv_extension_type()`, the latter takes
|
||||
* precedence.
|
||||
*/
|
||||
NGHTTP2_EXTERN void
|
||||
nghttp2_option_set_builtin_recv_extension_type(nghttp2_option *option,
|
||||
uint8_t type);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
|
@ -4123,7 +4143,7 @@ NGHTTP2_EXTERN int nghttp2_submit_extension(nghttp2_session *session,
|
|||
* 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
|
||||
* `nghttp2_option_set_builtin_recv_extension_type()` is set for
|
||||
* :enum:`NGHTTP2_ALTSVC`, ``nghttp2_extension.payload`` will point to
|
||||
* this struct.
|
||||
*
|
||||
|
|
|
@ -215,7 +215,6 @@ void nghttp2_frame_altsvc_free(nghttp2_extension *frame, nghttp2_mem *mem) {
|
|||
/* 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) {
|
||||
|
@ -723,6 +722,25 @@ int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *frame) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void nghttp2_frame_unpack_altsvc_payload(nghttp2_extension *frame,
|
||||
size_t origin_len, uint8_t *payload,
|
||||
size_t payloadlen) {
|
||||
nghttp2_ext_altsvc *altsvc;
|
||||
uint8_t *p;
|
||||
|
||||
altsvc = frame->payload;
|
||||
p = payload;
|
||||
|
||||
altsvc->origin = p;
|
||||
|
||||
p += origin_len;
|
||||
|
||||
altsvc->origin_len = origin_len;
|
||||
|
||||
altsvc->field_value = p;
|
||||
altsvc->field_value_len = (size_t)(payload + payloadlen - p);
|
||||
}
|
||||
|
||||
nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv,
|
||||
size_t niv, nghttp2_mem *mem) {
|
||||
nghttp2_settings_entry *iv_copy;
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
#define NGHTTP2_MAX_PADLEN 256
|
||||
|
||||
/* Union of extension frame payload */
|
||||
typedef union { int dummy; } nghttp2_ext_frame_payload;
|
||||
typedef union { nghttp2_ext_altsvc altsvc; } nghttp2_ext_frame_payload;
|
||||
|
||||
void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd);
|
||||
|
||||
|
@ -378,6 +378,16 @@ void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame,
|
|||
*/
|
||||
int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *ext);
|
||||
|
||||
/*
|
||||
* Unpacks ALTSVC wire format into |frame|. The |payload| of
|
||||
* |payloadlen| bytes contains frame payload. This function assumes
|
||||
* that frame->payload points to the nghttp2_ext_altsvc object.
|
||||
*
|
||||
* This function always succeeds and returns 0.
|
||||
*/
|
||||
void nghttp2_frame_unpack_altsvc_payload(nghttp2_extension *frame,
|
||||
size_t origin_len, uint8_t *payload,
|
||||
size_t payloadlen);
|
||||
/*
|
||||
* Initializes HEADERS frame |frame| with given values. |frame| takes
|
||||
* ownership of |nva|, so caller must not free it. If |stream_id| is
|
||||
|
@ -456,10 +466,23 @@ void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type,
|
|||
|
||||
void nghttp2_frame_extension_free(nghttp2_extension *frame);
|
||||
|
||||
/*
|
||||
* Initializes ALTSVC frame |frame| with given values. This function
|
||||
* assumes that frame->payload points to nghttp2_ext_altsvc object.
|
||||
* Also |origin| and |field_value| are allocated in single buffer,
|
||||
* starting |origin|. On success, this function takes ownership of
|
||||
* |origin|, so caller must not free it.
|
||||
*/
|
||||
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);
|
||||
|
||||
/*
|
||||
* Frees up resources under |frame|. This function does not free
|
||||
* nghttp2_ext_altsvc object pointed by frame->payload. This function
|
||||
* only frees origin pointed by nghttp2_ext_altsvc.origin. Therefore,
|
||||
* other fields must be allocated in the same buffer with origin.
|
||||
*/
|
||||
void nghttp2_frame_altsvc_free(nghttp2_extension *frame, nghttp2_mem *mem);
|
||||
|
||||
/*
|
||||
|
|
|
@ -63,6 +63,10 @@ void nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option,
|
|||
option->max_reserved_remote_streams = val;
|
||||
}
|
||||
|
||||
static void set_ext_type(uint8_t *ext_types, uint8_t type) {
|
||||
ext_types[type / 8] = (uint8_t)(ext_types[type / 8] | (1 << (type & 0x7)));
|
||||
}
|
||||
|
||||
void nghttp2_option_set_user_recv_extension_type(nghttp2_option *option,
|
||||
uint8_t type) {
|
||||
if (type < 10) {
|
||||
|
@ -70,8 +74,17 @@ void nghttp2_option_set_user_recv_extension_type(nghttp2_option *option,
|
|||
}
|
||||
|
||||
option->opt_set_mask |= NGHTTP2_OPT_USER_RECV_EXT_TYPES;
|
||||
option->user_recv_ext_types[type / 8] =
|
||||
(uint8_t)(option->user_recv_ext_types[type / 8] | (1 << (type & 0x7)));
|
||||
set_ext_type(option->user_recv_ext_types, type);
|
||||
}
|
||||
|
||||
void nghttp2_option_set_builtin_recv_extension_type(nghttp2_option *option,
|
||||
uint8_t type) {
|
||||
if (type < 10) {
|
||||
return;
|
||||
}
|
||||
|
||||
option->opt_set_mask |= NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES;
|
||||
set_ext_type(option->builtin_recv_ext_types, type);
|
||||
}
|
||||
|
||||
void nghttp2_option_set_no_auto_ping_ack(nghttp2_option *option, int val) {
|
||||
|
|
|
@ -61,7 +61,8 @@ typedef enum {
|
|||
NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3,
|
||||
NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS = 1 << 4,
|
||||
NGHTTP2_OPT_USER_RECV_EXT_TYPES = 1 << 5,
|
||||
NGHTTP2_OPT_NO_AUTO_PING_ACK = 1 << 6
|
||||
NGHTTP2_OPT_NO_AUTO_PING_ACK = 1 << 6,
|
||||
NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES = 1 << 7
|
||||
} nghttp2_option_flag;
|
||||
|
||||
/**
|
||||
|
@ -101,6 +102,10 @@ struct nghttp2_option {
|
|||
* NGHTTP2_OPT_USER_RECV_EXT_TYPES
|
||||
*/
|
||||
uint8_t user_recv_ext_types[32];
|
||||
/**
|
||||
* NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES
|
||||
*/
|
||||
uint8_t builtin_recv_ext_types[32];
|
||||
};
|
||||
|
||||
#endif /* NGHTTP2_OPTION_H */
|
||||
|
|
|
@ -90,6 +90,8 @@ void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) {
|
|||
assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
nghttp2_mem_free(mem, frame->ext.payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -142,6 +142,10 @@ static int session_detect_idle_stream(nghttp2_session *session,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int check_ext_type_set(const uint8_t *ext_types, uint8_t type) {
|
||||
return (ext_types[type / 8] & (1 << (type & 0x7))) > 0;
|
||||
}
|
||||
|
||||
static int session_call_error_callback(nghttp2_session *session,
|
||||
const char *fmt, ...) {
|
||||
size_t bufsize;
|
||||
|
@ -316,7 +320,18 @@ static void session_inbound_frame_reset(nghttp2_session *session) {
|
|||
break;
|
||||
default:
|
||||
/* extension frame */
|
||||
if (check_ext_type_set(session->user_recv_ext_types,
|
||||
iframe->frame.hd.type)) {
|
||||
nghttp2_frame_extension_free(&iframe->frame.ext);
|
||||
} else if (check_ext_type_set(session->builtin_recv_ext_types,
|
||||
iframe->frame.hd.type)) {
|
||||
switch (iframe->frame.hd.type) {
|
||||
case NGHTTP2_ALTSVC:
|
||||
nghttp2_frame_altsvc_free(&iframe->frame.ext, mem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -332,6 +347,8 @@ static void session_inbound_frame_reset(nghttp2_session *session) {
|
|||
nghttp2_buf_free(&iframe->lbuf, mem);
|
||||
nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0);
|
||||
|
||||
iframe->raw_lbuf = NULL;
|
||||
|
||||
iframe->niv = 0;
|
||||
iframe->payloadleft = 0;
|
||||
iframe->padlen = 0;
|
||||
|
@ -474,6 +491,12 @@ static int session_new(nghttp2_session **session_ptr,
|
|||
sizeof((*session_ptr)->user_recv_ext_types));
|
||||
}
|
||||
|
||||
if (option->opt_set_mask & NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES) {
|
||||
memcpy((*session_ptr)->builtin_recv_ext_types,
|
||||
option->builtin_recv_ext_types,
|
||||
sizeof((*session_ptr)->builtin_recv_ext_types));
|
||||
}
|
||||
|
||||
if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_PING_ACK) &&
|
||||
option->no_auto_ping_ack) {
|
||||
(*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_PING_ACK;
|
||||
|
@ -4637,6 +4660,44 @@ static int session_process_window_update_frame(nghttp2_session *session) {
|
|||
return nghttp2_session_on_window_update_received(session, frame);
|
||||
}
|
||||
|
||||
int nghttp2_session_on_altsvc_received(nghttp2_session *session,
|
||||
nghttp2_frame *frame) {
|
||||
nghttp2_ext_altsvc *altsvc;
|
||||
|
||||
altsvc = frame->ext.payload;
|
||||
|
||||
if (session->server) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (frame->hd.stream_id == 0) {
|
||||
if (altsvc->origin_len == 0) {
|
||||
return 0;
|
||||
}
|
||||
} else if (altsvc->origin_len > 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return session_call_on_frame_received(session, frame);
|
||||
}
|
||||
|
||||
static int session_process_altsvc_frame(nghttp2_session *session) {
|
||||
nghttp2_inbound_frame *iframe = &session->iframe;
|
||||
nghttp2_frame *frame = &iframe->frame;
|
||||
|
||||
frame->ext.payload = &iframe->ext_frame_payload.altsvc;
|
||||
|
||||
nghttp2_frame_unpack_altsvc_payload(
|
||||
&frame->ext, nghttp2_get_uint16(iframe->sbuf.pos), iframe->lbuf.pos,
|
||||
nghttp2_buf_len(&iframe->lbuf));
|
||||
|
||||
/* nghttp2_frame_unpack_altsvc_payload steals buffer from
|
||||
iframe->lbuf */
|
||||
nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0);
|
||||
|
||||
return nghttp2_session_on_altsvc_received(session, frame);
|
||||
}
|
||||
|
||||
static int session_process_extension_frame(nghttp2_session *session) {
|
||||
int rv;
|
||||
nghttp2_inbound_frame *iframe = &session->iframe;
|
||||
|
@ -5480,11 +5541,11 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
|||
|
||||
break;
|
||||
default:
|
||||
DEBUGF(fprintf(stderr, "recv: unknown frame\n"));
|
||||
DEBUGF(fprintf(stderr, "recv: extension frame\n"));
|
||||
|
||||
if (!session->callbacks.unpack_extension_callback ||
|
||||
(session->user_recv_ext_types[iframe->frame.hd.type / 8] &
|
||||
(1 << (iframe->frame.hd.type & 0x7))) == 0) {
|
||||
if (check_ext_type_set(session->user_recv_ext_types,
|
||||
iframe->frame.hd.type)) {
|
||||
if (!session->callbacks.unpack_extension_callback) {
|
||||
/* Silently ignore unknown frame type. */
|
||||
|
||||
busy = 1;
|
||||
|
@ -5499,6 +5560,40 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
|||
iframe->state = NGHTTP2_IB_READ_EXTENSION_PAYLOAD;
|
||||
|
||||
break;
|
||||
} else if (check_ext_type_set(session->builtin_recv_ext_types,
|
||||
iframe->frame.hd.type)) {
|
||||
switch (iframe->frame.hd.type) {
|
||||
case NGHTTP2_ALTSVC:
|
||||
DEBUGF(fprintf(stderr, "recv: ALTSVC\n"));
|
||||
|
||||
iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
|
||||
|
||||
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;
|
||||
default:
|
||||
busy = 1;
|
||||
|
||||
iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
|
||||
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
busy = 1;
|
||||
|
||||
iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!on_begin_frame_called) {
|
||||
|
@ -5708,6 +5803,37 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
|||
session_inbound_frame_reset(session);
|
||||
|
||||
break;
|
||||
case NGHTTP2_ALTSVC: {
|
||||
size_t origin_len;
|
||||
|
||||
origin_len = nghttp2_get_uint16(iframe->sbuf.pos);
|
||||
|
||||
DEBUGF(fprintf(stderr, "recv: origin_len=%zu\n", origin_len));
|
||||
|
||||
if (2 + origin_len > iframe->payloadleft) {
|
||||
busy = 1;
|
||||
iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (iframe->frame.hd.length > 2) {
|
||||
iframe->raw_lbuf =
|
||||
nghttp2_mem_malloc(mem, iframe->frame.hd.length - 2);
|
||||
|
||||
if (iframe->raw_lbuf == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
|
||||
nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf,
|
||||
iframe->frame.hd.length);
|
||||
}
|
||||
|
||||
busy = 1;
|
||||
|
||||
iframe->state = NGHTTP2_IB_READ_ALTSVC_PAYLOAD;
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* This is unknown frame */
|
||||
session_inbound_frame_reset(session);
|
||||
|
@ -6233,6 +6359,36 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
|||
|
||||
session_inbound_frame_reset(session);
|
||||
|
||||
break;
|
||||
case NGHTTP2_IB_READ_ALTSVC_PAYLOAD:
|
||||
DEBUGF(fprintf(stderr, "recv: [IB_READ_ALTSVC_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(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen,
|
||||
iframe->payloadleft));
|
||||
|
||||
if (iframe->payloadleft) {
|
||||
assert(nghttp2_buf_avail(&iframe->lbuf) > 0);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
rv = session_process_altsvc_frame(session);
|
||||
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
session_inbound_frame_reset(session);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -107,6 +107,7 @@ typedef enum {
|
|||
NGHTTP2_IB_READ_DATA,
|
||||
NGHTTP2_IB_IGN_DATA,
|
||||
NGHTTP2_IB_IGN_ALL,
|
||||
NGHTTP2_IB_READ_ALTSVC_PAYLOAD,
|
||||
NGHTTP2_IB_READ_EXTENSION_PAYLOAD
|
||||
} nghttp2_inbound_state;
|
||||
|
||||
|
@ -313,6 +314,7 @@ struct nghttp2_session {
|
|||
bit is set, it indicates that incoming frame with that type is
|
||||
passed to user defined callbacks, otherwise they are ignored. */
|
||||
uint8_t user_recv_ext_types[32];
|
||||
uint8_t builtin_recv_ext_types[32];
|
||||
};
|
||||
|
||||
/* Struct used when updating initial window size of each active
|
||||
|
@ -716,6 +718,19 @@ int nghttp2_session_on_goaway_received(nghttp2_session *session,
|
|||
int nghttp2_session_on_window_update_received(nghttp2_session *session,
|
||||
nghttp2_frame *frame);
|
||||
|
||||
/*
|
||||
* Called when ALTSVC is recieved, 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_altsvc_received(nghttp2_session *session,
|
||||
nghttp2_frame *frame);
|
||||
|
||||
/*
|
||||
* Called when DATA is received, assuming |frame| is properly
|
||||
* initialized.
|
||||
|
|
|
@ -485,6 +485,7 @@ int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags _U_,
|
|||
rv = nghttp2_session_add_item(session, item);
|
||||
if (rv != 0) {
|
||||
nghttp2_frame_altsvc_free(&frame->ext, mem);
|
||||
nghttp2_mem_free(mem, frame->ext.payload);
|
||||
nghttp2_mem_free(mem, item);
|
||||
|
||||
return rv;
|
||||
|
|
|
@ -118,6 +118,7 @@ Config::Config()
|
|||
nghttp2_option_new(&http2_option);
|
||||
nghttp2_option_set_peer_max_concurrent_streams(http2_option,
|
||||
peer_max_concurrent_streams);
|
||||
nghttp2_option_set_builtin_recv_extension_type(http2_option, NGHTTP2_ALTSVC);
|
||||
}
|
||||
|
||||
Config::~Config() { nghttp2_option_del(http2_option); }
|
||||
|
|
Loading…
Reference in New Issue