Separate extension frames from core frames

ALTSVC and BLOCKED frames are now extension frames.  To add new
extension frame without modifying nghttp2_frame union, which causes so
name bump, we separated extension frames from core frames.
nghttp2_frame includes generic nghttp2_extension.  The payload member
of nghttp2_extension will point to the structure of extension frame
payload.  The frame types of extension frames are defined in
nghttp2_ext_frame_type.
This commit is contained in:
Tatsuhiro Tsujikawa 2014-06-09 23:16:54 +09:00
parent 317b8baa4f
commit dacc9b2f1c
11 changed files with 267 additions and 148 deletions

View File

@ -396,7 +396,8 @@ typedef struct {
/** /**
* @enum * @enum
* The control frame types in HTTP/2. *
* The frame types in HTTP/2 specification.
*/ */
typedef enum { typedef enum {
/** /**
@ -438,17 +439,28 @@ typedef enum {
/** /**
* The CONTINUATION frame. * The CONTINUATION frame.
*/ */
NGHTTP2_CONTINUATION = 0x09, NGHTTP2_CONTINUATION = 0x09
/**
* The ALTSVC frame.
*/
NGHTTP2_ALTSVC = 0x0a,
/**
* The BLOCKED frame.
*/
NGHTTP2_BLOCKED = 0x0b
} nghttp2_frame_type; } nghttp2_frame_type;
/**
* @enum
*
* The extension frame types.
*
* TODO: The assigned frame types were carried from draft-12, and now
* actually TBD.
*/
typedef enum {
/**
* The ALTSVC extension frame.
*/
NGHTTP2_EXT_ALTSVC = 0x0a,
/**
* The BLOCKED extension frame.
*/
NGHTTP2_EXT_BLOCKED = 0x0b
} nghttp2_ext_frame_type;
/** /**
* @enum * @enum
* *
@ -938,16 +950,30 @@ typedef struct {
int32_t window_size_increment; int32_t window_size_increment;
} nghttp2_window_update; } nghttp2_window_update;
/**
* @struct
*
* The ALTSVC frame. It has following members:
*/
typedef struct { typedef struct {
/** /**
* The frame header. * The frame header.
*/ */
nghttp2_frame_hd hd; nghttp2_frame_hd hd;
/**
* The pointer to extension payload. The exact pointer type is
* determined by hd.type.
*
* If hd.type == :enum:`NGHTTP2_EXT_ALTSVC`, it is a pointer to
* :type:`nghttp2_ext_altsvc`.
*
* If hd.type == :enum:`NGHTTP2_EXT_BLOCKED`, it points to ``NULL``,
* since BLOCKED extension frame has no payload.
*/
void *payload;
} nghttp2_extension;
/**
* @struct
*
* The ALTSVC extension frame payload. It has following members:
*/
typedef struct {
/** /**
* Protocol ID * Protocol ID
*/ */
@ -980,19 +1006,7 @@ typedef struct {
* Port * Port
*/ */
uint16_t port; uint16_t port;
} nghttp2_altsvc; } nghttp2_ext_altsvc;
/**
* @struct
*
* The BLOCKED frame. It has following members:
*/
typedef struct {
/**
* The frame header.
*/
nghttp2_frame_hd hd;
} nghttp2_blocked;
/** /**
* @union * @union
@ -1043,13 +1057,9 @@ typedef union {
*/ */
nghttp2_window_update window_update; nghttp2_window_update window_update;
/** /**
* The ALTSVC frame. * The extension frame.
*/ */
nghttp2_altsvc altsvc; nghttp2_extension ext;
/**
* The BLOCKED frame.
*/
nghttp2_blocked blocked;
} nghttp2_frame; } nghttp2_frame;
/** /**

View File

@ -186,7 +186,7 @@ void nghttp2_frame_window_update_init(nghttp2_window_update *frame,
void nghttp2_frame_window_update_free(nghttp2_window_update *frame) void nghttp2_frame_window_update_free(nghttp2_window_update *frame)
{} {}
void nghttp2_frame_altsvc_init(nghttp2_altsvc *frame, int32_t stream_id, void nghttp2_frame_altsvc_init(nghttp2_extension *frame, int32_t stream_id,
uint32_t max_age, uint32_t max_age,
uint16_t port, uint16_t port,
uint8_t *protocol_id, uint8_t *protocol_id,
@ -195,34 +195,45 @@ void nghttp2_frame_altsvc_init(nghttp2_altsvc *frame, int32_t stream_id,
uint8_t *origin, size_t origin_len) uint8_t *origin, size_t origin_len)
{ {
size_t payloadlen; size_t payloadlen;
nghttp2_ext_altsvc *altsvc;
altsvc = frame->payload;
payloadlen = NGHTTP2_ALTSVC_MINLEN + protocol_id_len + host_len + origin_len; payloadlen = NGHTTP2_ALTSVC_MINLEN + protocol_id_len + host_len + origin_len;
frame_set_hd(&frame->hd, payloadlen, NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, frame_set_hd(&frame->hd, payloadlen, NGHTTP2_EXT_ALTSVC, NGHTTP2_FLAG_NONE,
stream_id); stream_id);
frame->max_age = max_age; altsvc->max_age = max_age;
frame->port = port; altsvc->port = port;
frame->protocol_id = protocol_id; altsvc->protocol_id = protocol_id;
frame->protocol_id_len = protocol_id_len; altsvc->protocol_id_len = protocol_id_len;
frame->host = host; altsvc->host = host;
frame->host_len = host_len; altsvc->host_len = host_len;
frame->origin = origin; altsvc->origin = origin;
frame->origin_len = origin_len; altsvc->origin_len = origin_len;
} }
void nghttp2_frame_altsvc_free(nghttp2_altsvc *frame) void nghttp2_frame_altsvc_free(nghttp2_extension *frame)
{ {
free(frame->protocol_id); nghttp2_ext_altsvc *altsvc;
altsvc = frame->payload;
if(altsvc == NULL) {
return;
}
free(altsvc->protocol_id);
} }
void nghttp2_frame_blocked_init(nghttp2_blocked *frame, int32_t stream_id) void nghttp2_frame_blocked_init(nghttp2_extension *frame, int32_t stream_id)
{ {
frame_set_hd(&frame->hd, 0, NGHTTP2_BLOCKED, NGHTTP2_FLAG_NONE, frame_set_hd(&frame->hd, 0, NGHTTP2_EXT_BLOCKED, NGHTTP2_FLAG_NONE,
stream_id); stream_id);
} }
void nghttp2_frame_blocked_free(nghttp2_blocked *frame) void nghttp2_frame_blocked_free(nghttp2_extension *frame)
{} {}
void nghttp2_frame_data_init(nghttp2_data *frame, nghttp2_private_data *pdata) void nghttp2_frame_data_init(nghttp2_data *frame, nghttp2_private_data *pdata)
@ -757,44 +768,47 @@ void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame,
NGHTTP2_WINDOW_SIZE_INCREMENT_MASK; NGHTTP2_WINDOW_SIZE_INCREMENT_MASK;
} }
int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_altsvc *frame) int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *frame)
{ {
int rv; int rv;
nghttp2_buf *buf; nghttp2_buf *buf;
nghttp2_ext_altsvc *altsvc;
assert(bufs->head == bufs->cur); assert(bufs->head == bufs->cur);
altsvc = frame->payload;
buf = &bufs->head->buf; buf = &bufs->head->buf;
buf->pos -= NGHTTP2_FRAME_HDLEN; buf->pos -= NGHTTP2_FRAME_HDLEN;
nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
nghttp2_put_uint32be(buf->last, frame->max_age); nghttp2_put_uint32be(buf->last, altsvc->max_age);
buf->last += 4; buf->last += 4;
nghttp2_put_uint16be(buf->last, frame->port); nghttp2_put_uint16be(buf->last, altsvc->port);
buf->last += 2; buf->last += 2;
buf->last[0] = frame->protocol_id_len; buf->last[0] = altsvc->protocol_id_len;
++buf->last; ++buf->last;
rv = nghttp2_bufs_add(bufs, frame->protocol_id, frame->protocol_id_len); rv = nghttp2_bufs_add(bufs, altsvc->protocol_id, altsvc->protocol_id_len);
if(rv != 0) { if(rv != 0) {
goto fail; goto fail;
} }
rv = nghttp2_bufs_addb(bufs, frame->host_len); rv = nghttp2_bufs_addb(bufs, altsvc->host_len);
if(rv != 0) { if(rv != 0) {
goto fail; goto fail;
} }
rv = nghttp2_bufs_add(bufs, frame->host, frame->host_len); rv = nghttp2_bufs_add(bufs, altsvc->host, altsvc->host_len);
if(rv != 0) { if(rv != 0) {
goto fail; goto fail;
} }
rv = nghttp2_bufs_add(bufs, frame->origin, frame->origin_len); rv = nghttp2_bufs_add(bufs, altsvc->origin, altsvc->origin_len);
if(rv != 0) { if(rv != 0) {
goto fail; goto fail;
} }
@ -810,50 +824,53 @@ int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_altsvc *frame)
return rv; return rv;
} }
int nghttp2_frame_unpack_altsvc_payload(nghttp2_altsvc *frame, int nghttp2_frame_unpack_altsvc_payload(nghttp2_extension *frame,
const uint8_t *payload, const uint8_t *payload,
size_t payloadlen, size_t payloadlen,
uint8_t *var_gift_payload, uint8_t *var_gift_payload,
size_t var_gift_payloadlen) size_t var_gift_payloadlen)
{ {
nghttp2_buf buf; nghttp2_buf buf;
nghttp2_ext_altsvc *altsvc;
frame->max_age = nghttp2_get_uint32(payload); altsvc = frame->payload;
altsvc->max_age = nghttp2_get_uint32(payload);
payload += 4; payload += 4;
frame->port = nghttp2_get_uint16(payload); altsvc->port = nghttp2_get_uint16(payload);
payload += 2; payload += 2;
frame->protocol_id_len = *payload; altsvc->protocol_id_len = *payload;
nghttp2_buf_wrap_init(&buf, var_gift_payload, var_gift_payloadlen); nghttp2_buf_wrap_init(&buf, var_gift_payload, var_gift_payloadlen);
buf.last += var_gift_payloadlen; buf.last += var_gift_payloadlen;
/* 1 for Host-Len */ /* 1 for Host-Len */
if(nghttp2_buf_len(&buf) < 1 + (ssize_t)frame->protocol_id_len) { if(nghttp2_buf_len(&buf) < 1 + (ssize_t)altsvc->protocol_id_len) {
return NGHTTP2_ERR_FRAME_SIZE_ERROR; return NGHTTP2_ERR_FRAME_SIZE_ERROR;
} }
frame->protocol_id = buf.pos; altsvc->protocol_id = buf.pos;
buf.pos += frame->protocol_id_len; buf.pos += altsvc->protocol_id_len;
frame->host_len = *buf.pos; altsvc->host_len = *buf.pos;
++buf.pos; ++buf.pos;
if(nghttp2_buf_len(&buf) < (ssize_t)frame->host_len) { if(nghttp2_buf_len(&buf) < (ssize_t)altsvc->host_len) {
return NGHTTP2_ERR_FRAME_SIZE_ERROR; return NGHTTP2_ERR_FRAME_SIZE_ERROR;
} }
frame->host = buf.pos; altsvc->host = buf.pos;
buf.pos += frame->host_len; buf.pos += altsvc->host_len;
frame->origin = buf.pos; altsvc->origin = buf.pos;
frame->origin_len = nghttp2_buf_len(&buf); altsvc->origin_len = nghttp2_buf_len(&buf);
return 0; return 0;
} }
int nghttp2_frame_pack_blocked(nghttp2_bufs *bufs, nghttp2_blocked *frame) int nghttp2_frame_pack_blocked(nghttp2_bufs *bufs, nghttp2_extension *frame)
{ {
nghttp2_buf *buf; nghttp2_buf *buf;

View File

@ -66,11 +66,10 @@
Max-Age, Port and Proto-Len. */ Max-Age, Port and Proto-Len. */
#define NGHTTP2_ALTSVC_FIXED_PARTLEN 7 #define NGHTTP2_ALTSVC_FIXED_PARTLEN 7
/* Minimum length of ALTSVC frame. NGHTTP2_ALTSVC_FIXED_PARTLEN + /* Minimum length of ALTSVC extension frame payload.
Host-Len. */ NGHTTP2_ALTSVC_FIXED_PARTLEN + Host-Len. */
#define NGHTTP2_ALTSVC_MINLEN 8 #define NGHTTP2_ALTSVC_MINLEN 8
/* Category of frames. */ /* Category of frames. */
typedef enum { typedef enum {
/* non-DATA frame */ /* non-DATA frame */
@ -79,6 +78,11 @@ typedef enum {
NGHTTP2_CAT_DATA NGHTTP2_CAT_DATA
} nghttp2_frame_category; } nghttp2_frame_category;
/* Union of extension frame payload */
typedef union {
nghttp2_ext_altsvc altsvc;
} nghttp2_ext_frame_payload;
/** /**
* @struct * @struct
* *
@ -402,6 +406,9 @@ void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame,
* The caller must make sure that nghttp2_bufs_reset(bufs) is called * The caller must make sure that nghttp2_bufs_reset(bufs) is called
* before calling this function. * before calling this function.
* *
* The caller must make sure that frame->payload points to
* nghttp2_ext_altsvc object.
*
* This function returns 0 if it succeeds or one of the following * This function returns 0 if it succeeds or one of the following
* negative error codes: * negative error codes:
* *
@ -410,7 +417,7 @@ void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame,
* NGHTTP2_ERR_FRAME_SIZE_ERROR * NGHTTP2_ERR_FRAME_SIZE_ERROR
* The length of the frame is too large. * The length of the frame is too large.
*/ */
int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_altsvc *frame); int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *frame);
/* /*
@ -421,13 +428,16 @@ int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_altsvc *frame);
* and then |frame|. The |var_gift_payloadlen| must be freed by * and then |frame|. The |var_gift_payloadlen| must be freed by
* nghttp2_frame_altsvc_free(). * nghttp2_frame_altsvc_free().
* *
* The caller must make sure that frame->payload points to
* nghttp2_ext_altsvc object.
*
* This function returns 0 if it succeeds or one of the following * This function returns 0 if it succeeds or one of the following
* negative error codes: * negative error codes:
* *
* NGHTTP2_ERR_FRAME_SIZE_ERROR * NGHTTP2_ERR_FRAME_SIZE_ERROR
* The |var_gift_payload| does not contain required data. * The |var_gift_payload| does not contain required data.
*/ */
int nghttp2_frame_unpack_altsvc_payload(nghttp2_altsvc *frame, int nghttp2_frame_unpack_altsvc_payload(nghttp2_extension *frame,
const uint8_t *payload, const uint8_t *payload,
size_t payloadlen, size_t payloadlen,
uint8_t *var_gift_payload, uint8_t *var_gift_payload,
@ -441,7 +451,7 @@ int nghttp2_frame_unpack_altsvc_payload(nghttp2_altsvc *frame,
* *
* This function always returns 0. * This function always returns 0.
*/ */
int nghttp2_frame_pack_blocked(nghttp2_bufs *bufs, nghttp2_blocked *frame); int nghttp2_frame_pack_blocked(nghttp2_bufs *bufs, nghttp2_extension *frame);
/* /*
* Initializes HEADERS frame |frame| with given values. |frame| takes * Initializes HEADERS frame |frame| with given values. |frame| takes
@ -524,7 +534,7 @@ void nghttp2_frame_window_update_free(nghttp2_window_update *frame);
|protocol_id_len| == 0 and |host_len| + |origin_len| > 0. If |protocol_id_len| == 0 and |host_len| + |origin_len| > 0. If
|protocol_id_len|, |host_len| and |origin_len| are all zero, |protocol_id_len|, |host_len| and |origin_len| are all zero,
|protocol_id| can be NULL. */ |protocol_id| can be NULL. */
void nghttp2_frame_altsvc_init(nghttp2_altsvc *frame, int32_t stream_id, void nghttp2_frame_altsvc_init(nghttp2_extension *frame, int32_t stream_id,
uint32_t max_age, uint32_t max_age,
uint16_t port, uint16_t port,
uint8_t *protocol_id, uint8_t *protocol_id,
@ -532,11 +542,15 @@ void nghttp2_frame_altsvc_init(nghttp2_altsvc *frame, int32_t stream_id,
uint8_t *host, size_t host_len, uint8_t *host, size_t host_len,
uint8_t *origin, size_t origin_len); uint8_t *origin, size_t origin_len);
void nghttp2_frame_altsvc_free(nghttp2_altsvc *frame); /*
* Frees resources used by |frame|. This function does not free
* frame->payload itself.
*/
void nghttp2_frame_altsvc_free(nghttp2_extension *frame);
void nghttp2_frame_blocked_init(nghttp2_blocked *frame, int32_t stream_id); void nghttp2_frame_blocked_init(nghttp2_extension *frame, int32_t stream_id);
void nghttp2_frame_blocked_free(nghttp2_blocked *frame); void nghttp2_frame_blocked_free(nghttp2_extension *frame);
void nghttp2_frame_data_init(nghttp2_data *frame, nghttp2_private_data *pdata); void nghttp2_frame_data_init(nghttp2_data *frame, nghttp2_private_data *pdata);

View File

@ -62,8 +62,9 @@ void nghttp2_outbound_item_free(nghttp2_outbound_item *item)
case NGHTTP2_WINDOW_UPDATE: case NGHTTP2_WINDOW_UPDATE:
nghttp2_frame_window_update_free(&frame->window_update); nghttp2_frame_window_update_free(&frame->window_update);
break; break;
case NGHTTP2_ALTSVC: case NGHTTP2_EXT_ALTSVC:
nghttp2_frame_altsvc_free(&frame->altsvc); nghttp2_frame_altsvc_free(&frame->ext);
free(frame->ext.payload);
break; break;
} }
} else if(item->frame_cat == NGHTTP2_CAT_DATA) { } else if(item->frame_cat == NGHTTP2_CAT_DATA) {

View File

@ -230,14 +230,16 @@ static void session_inbound_frame_reset(nghttp2_session *session)
case NGHTTP2_WINDOW_UPDATE: case NGHTTP2_WINDOW_UPDATE:
nghttp2_frame_window_update_free(&iframe->frame.window_update); nghttp2_frame_window_update_free(&iframe->frame.window_update);
break; break;
case NGHTTP2_ALTSVC: case NGHTTP2_EXT_ALTSVC:
nghttp2_frame_altsvc_free(&iframe->frame.altsvc); nghttp2_frame_altsvc_free(&iframe->frame.ext);
break; break;
case NGHTTP2_BLOCKED: case NGHTTP2_EXT_BLOCKED:
nghttp2_frame_blocked_free(&iframe->frame.blocked); nghttp2_frame_blocked_free(&iframe->frame.ext);
break; break;
} }
memset(&iframe->frame, 0, sizeof(nghttp2_frame)); memset(&iframe->frame, 0, sizeof(nghttp2_frame));
memset(&iframe->ext_frame_payload, 0, sizeof(nghttp2_ext_frame_payload));
iframe->state = NGHTTP2_IB_READ_HEAD; iframe->state = NGHTTP2_IB_READ_HEAD;
@ -1504,12 +1506,14 @@ static int session_add_blocked(nghttp2_session *session, int32_t stream_id)
return NGHTTP2_ERR_NOMEM; return NGHTTP2_ERR_NOMEM;
} }
nghttp2_frame_blocked_init(&frame->blocked, stream_id); frame->ext.payload = NULL;
nghttp2_frame_blocked_init(&frame->ext, stream_id);
rv = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, NULL); rv = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, NULL);
if(rv != 0) { if(rv != 0) {
nghttp2_frame_blocked_free(&frame->blocked); nghttp2_frame_blocked_free(&frame->ext);
free(frame); free(frame);
return rv; return rv;
@ -1751,28 +1755,28 @@ static int session_prep_frame(nghttp2_session *session,
return framerv; return framerv;
} }
break; break;
case NGHTTP2_ALTSVC: case NGHTTP2_EXT_ALTSVC:
rv = session_predicate_altsvc_send(session, frame->hd.stream_id); rv = session_predicate_altsvc_send(session, frame->hd.stream_id);
if(rv != 0) { if(rv != 0) {
return rv; return rv;
} }
framerv = nghttp2_frame_pack_altsvc(&session->aob.framebufs, framerv = nghttp2_frame_pack_altsvc(&session->aob.framebufs,
&frame->altsvc); &frame->ext);
if(framerv < 0) { if(framerv < 0) {
return framerv; return framerv;
} }
break; break;
case NGHTTP2_BLOCKED: case NGHTTP2_EXT_BLOCKED:
rv = session_predicate_blocked_send(session, frame->hd.stream_id); rv = session_predicate_blocked_send(session, frame->hd.stream_id);
if(rv != 0) { if(rv != 0) {
return rv; return rv;
} }
framerv = nghttp2_frame_pack_blocked(&session->aob.framebufs, framerv = nghttp2_frame_pack_blocked(&session->aob.framebufs,
&frame->blocked); &frame->ext);
if(framerv < 0) { if(framerv < 0) {
return framerv; return framerv;
@ -3650,7 +3654,9 @@ static int session_process_altsvc_frame(nghttp2_session *session)
nghttp2_frame *frame = &iframe->frame; nghttp2_frame *frame = &iframe->frame;
int rv; int rv;
rv = nghttp2_frame_unpack_altsvc_payload(&frame->altsvc, frame->ext.payload = &iframe->ext_frame_payload;
rv = nghttp2_frame_unpack_altsvc_payload(&frame->ext,
iframe->sbuf.pos, iframe->sbuf.pos,
nghttp2_buf_len(&iframe->sbuf), nghttp2_buf_len(&iframe->sbuf),
iframe->lbuf.pos, iframe->lbuf.pos,
@ -3677,6 +3683,8 @@ static int session_process_blocked_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;
frame->ext.payload = NULL;
return nghttp2_session_on_blocked_received(session, frame); return nghttp2_session_on_blocked_received(session, frame);
} }
@ -4463,7 +4471,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
iframe->state = NGHTTP2_IB_IGN_PAYLOAD; iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
break; break;
case NGHTTP2_ALTSVC: case NGHTTP2_EXT_ALTSVC:
DEBUGF(fprintf(stderr, "recv: ALTSVC\n")); DEBUGF(fprintf(stderr, "recv: ALTSVC\n"));
iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
@ -4494,7 +4502,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
inbound_frame_set_mark(iframe, NGHTTP2_ALTSVC_FIXED_PARTLEN); inbound_frame_set_mark(iframe, NGHTTP2_ALTSVC_FIXED_PARTLEN);
break; break;
case NGHTTP2_BLOCKED: case NGHTTP2_EXT_BLOCKED:
DEBUGF(fprintf(stderr, "recv: BLOCKED\n")); DEBUGF(fprintf(stderr, "recv: BLOCKED\n"));
iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
@ -4694,7 +4702,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
session_inbound_frame_reset(session); session_inbound_frame_reset(session);
break; break;
case NGHTTP2_ALTSVC: { case NGHTTP2_EXT_ALTSVC: {
size_t varlen; size_t varlen;
varlen = iframe->frame.hd.length - NGHTTP2_ALTSVC_FIXED_PARTLEN; varlen = iframe->frame.hd.length - NGHTTP2_ALTSVC_FIXED_PARTLEN;

View File

@ -83,6 +83,9 @@ typedef enum {
typedef struct { typedef struct {
nghttp2_frame frame; nghttp2_frame frame;
/* Storage for extension frame payload. frame->ext.payload points
to this structure to avoid frequent memory allocation. */
nghttp2_ext_frame_payload ext_frame_payload;
/* The received SETTINGS entry. The protocol says that we only cares /* The received SETTINGS entry. The protocol says that we only cares
about the defined settings ID. If unknown ID is received, it is about the defined settings ID. If unknown ID is received, it is
subject to connection error */ subject to connection error */

View File

@ -379,6 +379,7 @@ int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags,
size_t varlen; size_t varlen;
uint8_t *var, *varp; uint8_t *var, *varp;
nghttp2_frame *frame; nghttp2_frame *frame;
nghttp2_ext_altsvc *altsvc;
uint8_t *copy_protocol_id, *copy_host, *copy_origin; uint8_t *copy_protocol_id, *copy_host, *copy_origin;
if(!session->server) { if(!session->server) {
@ -392,6 +393,14 @@ int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags,
return NGHTTP2_ERR_INVALID_ARGUMENT; return NGHTTP2_ERR_INVALID_ARGUMENT;
} }
altsvc = malloc(sizeof(nghttp2_ext_altsvc));
if(altsvc == NULL) {
rv = NGHTTP2_ERR_NOMEM;
goto fail;
}
if(varlen == 0) { if(varlen == 0) {
var = NULL; var = NULL;
copy_protocol_id = NULL; copy_protocol_id = NULL;
@ -401,7 +410,9 @@ int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags,
var = malloc(varlen); var = malloc(varlen);
if(var == NULL) { if(var == NULL) {
return NGHTTP2_ERR_NOMEM; rv = NGHTTP2_ERR_NOMEM;
goto var_alloc_fail;
} }
varp = var; varp = var;
@ -424,25 +435,37 @@ int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags,
frame = malloc(sizeof(nghttp2_frame)); frame = malloc(sizeof(nghttp2_frame));
if(frame == NULL) { if(frame == NULL) {
free(var); rv = NGHTTP2_ERR_NOMEM;
return NGHTTP2_ERR_NOMEM; goto frame_alloc_fail;
} }
nghttp2_frame_altsvc_init(&frame->altsvc, stream_id, max_age, port, frame->ext.payload = altsvc;
nghttp2_frame_altsvc_init(&frame->ext, stream_id, max_age, port,
copy_protocol_id, protocol_id_len, copy_protocol_id, protocol_id_len,
copy_host, host_len, copy_origin, origin_len); copy_host, host_len, copy_origin, origin_len);
rv = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, NULL); rv = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, NULL);
if(rv != 0) { if(rv != 0) {
nghttp2_frame_altsvc_free(&frame->altsvc); nghttp2_frame_altsvc_free(&frame->ext);
free(frame); free(frame);
free(altsvc);
return rv; return rv;
} }
return 0; return 0;
frame_alloc_fail:
free(var);
var_alloc_fail:
free(altsvc);
fail:
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,

View File

@ -127,9 +127,9 @@ const char* strframetype(uint8_t type)
return "GOAWAY"; return "GOAWAY";
case NGHTTP2_WINDOW_UPDATE: case NGHTTP2_WINDOW_UPDATE:
return "WINDOW_UPDATE"; return "WINDOW_UPDATE";
case NGHTTP2_ALTSVC: case NGHTTP2_EXT_ALTSVC:
return "ALTSVC"; return "ALTSVC";
case NGHTTP2_BLOCKED: case NGHTTP2_EXT_BLOCKED:
return "BLOCKED"; return "BLOCKED";
default: default:
return "UNKNOWN"; return "UNKNOWN";
@ -427,25 +427,34 @@ void print_frame(print_type ptype, const nghttp2_frame *frame)
fprintf(outfile, "(window_size_increment=%d)\n", fprintf(outfile, "(window_size_increment=%d)\n",
frame->window_update.window_size_increment); frame->window_update.window_size_increment);
break; break;
case NGHTTP2_ALTSVC: case NGHTTP2_EXT_ALTSVC: {
print_frame_attr_indent(); print_frame_attr_indent();
auto altsvc = static_cast<const nghttp2_ext_altsvc*>(frame->ext.payload);
fprintf(outfile, "(max-age=%u, port=%u, protocol_id=", fprintf(outfile, "(max-age=%u, port=%u, protocol_id=",
frame->altsvc.max_age, frame->altsvc.port); altsvc->max_age, altsvc->port);
if(frame->altsvc.protocol_id_len) {
fwrite(frame->altsvc.protocol_id, frame->altsvc.protocol_id_len, 1, if(altsvc->protocol_id_len) {
outfile); fwrite(altsvc->protocol_id, altsvc->protocol_id_len, 1, outfile);
} }
fprintf(outfile, ", host="); fprintf(outfile, ", host=");
if(frame->altsvc.host_len) {
fwrite(frame->altsvc.host, frame->altsvc.host_len, 1, outfile); if(altsvc->host_len) {
fwrite(altsvc->host, altsvc->host_len, 1, outfile);
} }
fprintf(outfile, ", origin="); fprintf(outfile, ", origin=");
if(frame->altsvc.origin_len) {
fwrite(frame->altsvc.origin, frame->altsvc.origin_len, 1, outfile); if(altsvc->origin_len) {
fwrite(altsvc->origin, altsvc->origin_len, 1, outfile);
} }
fprintf(outfile, ")\n"); fprintf(outfile, ")\n");
break; break;
}
default: default:
break; break;
} }

View File

@ -435,7 +435,8 @@ void test_nghttp2_frame_pack_window_update(void)
void test_nghttp2_frame_pack_altsvc(void) void test_nghttp2_frame_pack_altsvc(void)
{ {
nghttp2_altsvc frame, oframe; nghttp2_extension frame, oframe;
nghttp2_ext_altsvc altsvc, oaltsvc;
nghttp2_bufs bufs; nghttp2_bufs bufs;
nghttp2_buf *buf; nghttp2_buf *buf;
size_t protocol_id_len, host_len, origin_len; size_t protocol_id_len, host_len, origin_len;
@ -464,6 +465,8 @@ void test_nghttp2_frame_pack_altsvc(void)
frame_pack_bufs_init(&bufs); frame_pack_bufs_init(&bufs);
frame.payload = &altsvc;
nghttp2_frame_altsvc_init(&frame, 1000000007, 1u << 31, 4000, nghttp2_frame_altsvc_init(&frame, 1000000007, 1u << 31, 4000,
protocol_id, protocol_id_len, protocol_id, protocol_id_len,
host, host_len, origin, origin_len); host, host_len, origin, origin_len);
@ -475,27 +478,30 @@ void test_nghttp2_frame_pack_altsvc(void)
CU_ASSERT((ssize_t)(NGHTTP2_FRAME_HDLEN + NGHTTP2_ALTSVC_MINLEN + datalen) == CU_ASSERT((ssize_t)(NGHTTP2_FRAME_HDLEN + NGHTTP2_ALTSVC_MINLEN + datalen) ==
nghttp2_bufs_len(&bufs)); nghttp2_bufs_len(&bufs));
oframe.payload = &oaltsvc;
CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &bufs)); CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &bufs));
check_frame_header(NGHTTP2_ALTSVC_MINLEN + datalen, check_frame_header(NGHTTP2_ALTSVC_MINLEN + datalen,
NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, NGHTTP2_EXT_ALTSVC, NGHTTP2_FLAG_NONE,
1000000007, &oframe.hd); 1000000007, &oframe.hd);
CU_ASSERT(1u << 31 == oframe.max_age); CU_ASSERT(1u << 31 == oaltsvc.max_age);
CU_ASSERT(4000 == oframe.port); CU_ASSERT(4000 == oaltsvc.port);
CU_ASSERT(protocol_id_len == oframe.protocol_id_len); CU_ASSERT(protocol_id_len == oaltsvc.protocol_id_len);
CU_ASSERT(memcmp(protocol_id, oframe.protocol_id, protocol_id_len) == 0); CU_ASSERT(memcmp(protocol_id, oaltsvc.protocol_id, protocol_id_len) == 0);
CU_ASSERT(host_len == oframe.host_len); CU_ASSERT(host_len == oaltsvc.host_len);
CU_ASSERT(memcmp(host, oframe.host, host_len) == 0); CU_ASSERT(memcmp(host, oaltsvc.host, host_len) == 0);
CU_ASSERT(origin_len == oframe.origin_len); CU_ASSERT(origin_len == oaltsvc.origin_len);
CU_ASSERT(memcmp(origin, oframe.origin, origin_len) == 0); CU_ASSERT(memcmp(origin, oaltsvc.origin, origin_len) == 0);
nghttp2_frame_altsvc_free(&oframe); nghttp2_frame_altsvc_free(&oframe);
nghttp2_frame_altsvc_free(&frame); nghttp2_frame_altsvc_free(&frame);
memset(&oframe, 0, sizeof(oframe)); memset(&oframe, 0, sizeof(oframe));
memset(&oaltsvc, 0, sizeof(oaltsvc));
buf = &bufs.head->buf; buf = &bufs.head->buf;
@ -506,6 +512,8 @@ void test_nghttp2_frame_pack_altsvc(void)
payloadlen = NGHTTP2_ALTSVC_MINLEN + protocol_id_len + host_len; payloadlen = NGHTTP2_ALTSVC_MINLEN + protocol_id_len + host_len;
nghttp2_put_uint16be(buf->pos, payloadlen); nghttp2_put_uint16be(buf->pos, payloadlen);
oframe.payload = &oaltsvc;
CU_ASSERT(0 == CU_ASSERT(0 ==
nghttp2_frame_unpack_altsvc_payload nghttp2_frame_unpack_altsvc_payload
(&oframe, (&oframe,
@ -514,13 +522,19 @@ void test_nghttp2_frame_pack_altsvc(void)
buf->pos + NGHTTP2_FRAME_HDLEN + NGHTTP2_ALTSVC_FIXED_PARTLEN, buf->pos + NGHTTP2_FRAME_HDLEN + NGHTTP2_ALTSVC_FIXED_PARTLEN,
payloadlen - NGHTTP2_ALTSVC_FIXED_PARTLEN)); payloadlen - NGHTTP2_ALTSVC_FIXED_PARTLEN));
CU_ASSERT(host_len == oframe.host_len); CU_ASSERT(protocol_id_len == oaltsvc.protocol_id_len);
CU_ASSERT(0 == oframe.origin_len); CU_ASSERT(host_len == oaltsvc.host_len);
CU_ASSERT(0 == oaltsvc.origin_len);
memset(&oframe, 0, sizeof(oframe));
memset(&oaltsvc, 0, sizeof(oaltsvc));
/* Check insufficient payload length for host */ /* Check insufficient payload length for host */
payloadlen = NGHTTP2_ALTSVC_MINLEN + protocol_id_len + host_len - 1; payloadlen = NGHTTP2_ALTSVC_MINLEN + protocol_id_len + host_len - 1;
nghttp2_put_uint16be(buf->pos, payloadlen); nghttp2_put_uint16be(buf->pos, payloadlen);
oframe.payload = &oaltsvc;
CU_ASSERT(NGHTTP2_ERR_FRAME_SIZE_ERROR == CU_ASSERT(NGHTTP2_ERR_FRAME_SIZE_ERROR ==
nghttp2_frame_unpack_altsvc_payload nghttp2_frame_unpack_altsvc_payload
(&oframe, (&oframe,
@ -529,12 +543,17 @@ void test_nghttp2_frame_pack_altsvc(void)
buf->pos + NGHTTP2_FRAME_HDLEN + NGHTTP2_ALTSVC_FIXED_PARTLEN, buf->pos + NGHTTP2_FRAME_HDLEN + NGHTTP2_ALTSVC_FIXED_PARTLEN,
payloadlen - NGHTTP2_ALTSVC_FIXED_PARTLEN)); payloadlen - NGHTTP2_ALTSVC_FIXED_PARTLEN));
memset(&oframe, 0, sizeof(oframe));
memset(&oaltsvc, 0, sizeof(oaltsvc));
/* Check no host case */ /* Check no host case */
payloadlen = NGHTTP2_ALTSVC_MINLEN + protocol_id_len; payloadlen = NGHTTP2_ALTSVC_MINLEN + protocol_id_len;
nghttp2_put_uint16be(buf->pos, payloadlen); nghttp2_put_uint16be(buf->pos, payloadlen);
buf->pos[NGHTTP2_FRAME_HDLEN + NGHTTP2_ALTSVC_FIXED_PARTLEN buf->pos[NGHTTP2_FRAME_HDLEN + NGHTTP2_ALTSVC_FIXED_PARTLEN
+ protocol_id_len] = 0; + protocol_id_len] = 0;
oframe.payload = &oaltsvc;
CU_ASSERT(0 == CU_ASSERT(0 ==
nghttp2_frame_unpack_altsvc_payload nghttp2_frame_unpack_altsvc_payload
(&oframe, (&oframe,
@ -543,13 +562,19 @@ void test_nghttp2_frame_pack_altsvc(void)
buf->pos + NGHTTP2_FRAME_HDLEN + NGHTTP2_ALTSVC_FIXED_PARTLEN, buf->pos + NGHTTP2_FRAME_HDLEN + NGHTTP2_ALTSVC_FIXED_PARTLEN,
payloadlen - NGHTTP2_ALTSVC_FIXED_PARTLEN)); payloadlen - NGHTTP2_ALTSVC_FIXED_PARTLEN));
CU_ASSERT(0 == oframe.host_len); CU_ASSERT(protocol_id_len == oaltsvc.protocol_id_len);
CU_ASSERT(0 == oframe.origin_len); CU_ASSERT(0 == oaltsvc.host_len);
CU_ASSERT(0 == oaltsvc.origin_len);
memset(&oframe, 0, sizeof(oframe));
memset(&oaltsvc, 0, sizeof(oaltsvc));
/* Check missing Host-Len */ /* Check missing Host-Len */
payloadlen = NGHTTP2_ALTSVC_FIXED_PARTLEN + protocol_id_len; payloadlen = NGHTTP2_ALTSVC_FIXED_PARTLEN + protocol_id_len;
nghttp2_put_uint16be(buf->pos, payloadlen); nghttp2_put_uint16be(buf->pos, payloadlen);
oframe.payload = &oaltsvc;
CU_ASSERT(NGHTTP2_ERR_FRAME_SIZE_ERROR == CU_ASSERT(NGHTTP2_ERR_FRAME_SIZE_ERROR ==
nghttp2_frame_unpack_altsvc_payload nghttp2_frame_unpack_altsvc_payload
(&oframe, (&oframe,
@ -558,6 +583,9 @@ void test_nghttp2_frame_pack_altsvc(void)
buf->pos + NGHTTP2_FRAME_HDLEN + NGHTTP2_ALTSVC_FIXED_PARTLEN, buf->pos + NGHTTP2_FRAME_HDLEN + NGHTTP2_ALTSVC_FIXED_PARTLEN,
payloadlen - NGHTTP2_ALTSVC_FIXED_PARTLEN)); payloadlen - NGHTTP2_ALTSVC_FIXED_PARTLEN));
memset(&oframe, 0, sizeof(oframe));
memset(&oaltsvc, 0, sizeof(oaltsvc));
nghttp2_bufs_free(&bufs); nghttp2_bufs_free(&bufs);
} }

View File

@ -57,11 +57,11 @@ typedef struct {
accumulator *acc; accumulator *acc;
scripted_data_feed *df; scripted_data_feed *df;
int frame_recv_cb_called, invalid_frame_recv_cb_called; int frame_recv_cb_called, invalid_frame_recv_cb_called;
nghttp2_frame_type recv_frame_type; uint8_t recv_frame_type;
int frame_send_cb_called; int frame_send_cb_called;
nghttp2_frame_type sent_frame_type; uint8_t sent_frame_type;
int frame_not_send_cb_called; int frame_not_send_cb_called;
nghttp2_frame_type not_sent_frame_type; uint8_t not_sent_frame_type;
int not_sent_error; int not_sent_error;
int stream_close_cb_called; int stream_close_cb_called;
size_t data_source_length; size_t data_source_length;
@ -1055,6 +1055,7 @@ void test_nghttp2_session_recv_altsvc(void)
nghttp2_session *session; nghttp2_session *session;
nghttp2_session_callbacks callbacks; nghttp2_session_callbacks callbacks;
nghttp2_frame frame; nghttp2_frame frame;
nghttp2_ext_altsvc altsvc;
size_t protocol_id_len, host_len, origin_len; size_t protocol_id_len, host_len, origin_len;
uint8_t *protocol_id, *host, *origin; uint8_t *protocol_id, *host, *origin;
uint8_t *data; uint8_t *data;
@ -1087,16 +1088,18 @@ void test_nghttp2_session_recv_altsvc(void)
"http://www.example.org", origin_len); "http://www.example.org", origin_len);
origin = data + protocol_id_len + host_len; origin = data + protocol_id_len + host_len;
nghttp2_frame_altsvc_init(&frame.altsvc, 1000000007, 1u << 31, 4000, frame.ext.payload = &altsvc;
nghttp2_frame_altsvc_init(&frame.ext, 1000000007, 1u << 31, 4000,
protocol_id, protocol_id_len, protocol_id, protocol_id_len,
host, host_len, origin, origin_len); host, host_len, origin, origin_len);
rv = nghttp2_frame_pack_altsvc(&bufs, &frame.altsvc); rv = nghttp2_frame_pack_altsvc(&bufs, &frame.ext);
CU_ASSERT(0 == rv); CU_ASSERT(0 == rv);
CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); CU_ASSERT(nghttp2_bufs_len(&bufs) > 0);
nghttp2_frame_altsvc_free(&frame.altsvc); nghttp2_frame_altsvc_free(&frame.ext);
buf = &bufs.head->buf; buf = &bufs.head->buf;
assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf));
@ -1124,7 +1127,7 @@ void test_nghttp2_session_recv_altsvc(void)
CU_ASSERT(rv == nghttp2_buf_len(buf)); CU_ASSERT(rv == nghttp2_buf_len(buf));
CU_ASSERT(1 == ud.frame_recv_cb_called); CU_ASSERT(1 == ud.frame_recv_cb_called);
CU_ASSERT(NGHTTP2_ALTSVC == ud.recv_frame_type); CU_ASSERT(NGHTTP2_EXT_ALTSVC == ud.recv_frame_type);
/* premature payload */ /* premature payload */
nghttp2_put_uint16be(buf->pos, 8); nghttp2_put_uint16be(buf->pos, 8);
@ -3529,6 +3532,7 @@ void test_nghttp2_submit_altsvc(void)
const char host[] = "localhost"; const char host[] = "localhost";
const char origin[] = "http://localhost/"; const char origin[] = "http://localhost/";
nghttp2_frame *frame; nghttp2_frame *frame;
nghttp2_ext_altsvc *altsvc;
nghttp2_outbound_item *item; nghttp2_outbound_item *item;
my_user_data ud; my_user_data ud;
@ -3564,28 +3568,30 @@ void test_nghttp2_submit_altsvc(void)
frame = OB_CTRL(item); frame = OB_CTRL(item);
CU_ASSERT(NGHTTP2_ALTSVC == frame->hd.type); CU_ASSERT(NGHTTP2_EXT_ALTSVC == frame->hd.type);
CU_ASSERT(9 == frame->hd.stream_id); CU_ASSERT(9 == frame->hd.stream_id);
CU_ASSERT(12345 == frame->altsvc.max_age); altsvc = frame->ext.payload;
CU_ASSERT(3000 == frame->altsvc.port);
CU_ASSERT(strlen(protocol_id) == frame->altsvc.protocol_id_len); CU_ASSERT(12345 == altsvc->max_age);
CU_ASSERT(strlen(host) == frame->altsvc.host_len); CU_ASSERT(3000 == altsvc->port);
CU_ASSERT(strlen(origin) == frame->altsvc.origin_len);
CU_ASSERT(0 == memcmp(protocol_id, frame->altsvc.protocol_id, CU_ASSERT(strlen(protocol_id) == altsvc->protocol_id_len);
frame->altsvc.protocol_id_len)); CU_ASSERT(strlen(host) == altsvc->host_len);
CU_ASSERT(0 == memcmp(host, frame->altsvc.host, frame->altsvc.host_len)); CU_ASSERT(strlen(origin) == altsvc->origin_len);
CU_ASSERT(0 == memcmp(origin, frame->altsvc.origin,
frame->altsvc.origin_len)); CU_ASSERT(0 == memcmp(protocol_id, altsvc->protocol_id,
altsvc->protocol_id_len));
CU_ASSERT(0 == memcmp(host, altsvc->host, altsvc->host_len));
CU_ASSERT(0 == memcmp(origin, altsvc->origin,
altsvc->origin_len));
ud.frame_send_cb_called = 0; ud.frame_send_cb_called = 0;
CU_ASSERT(0 == nghttp2_session_send(session)); CU_ASSERT(0 == nghttp2_session_send(session));
CU_ASSERT(1 == ud.frame_send_cb_called); CU_ASSERT(1 == ud.frame_send_cb_called);
CU_ASSERT(NGHTTP2_ALTSVC == ud.sent_frame_type); CU_ASSERT(NGHTTP2_EXT_ALTSVC == ud.sent_frame_type);
nghttp2_session_del(session); nghttp2_session_del(session);
} }

View File

@ -86,7 +86,7 @@ int unpack_frame(nghttp2_frame *frame, const uint8_t *in, size_t len)
nghttp2_frame_unpack_window_update_payload nghttp2_frame_unpack_window_update_payload
(&frame->window_update, payload, payloadlen); (&frame->window_update, payload, payloadlen);
break; break;
case NGHTTP2_ALTSVC: case NGHTTP2_EXT_ALTSVC:
gift_payloadlen = payloadlen - NGHTTP2_ALTSVC_FIXED_PARTLEN; gift_payloadlen = payloadlen - NGHTTP2_ALTSVC_FIXED_PARTLEN;
gift_payload = malloc(gift_payloadlen); gift_payload = malloc(gift_payloadlen);
@ -95,7 +95,7 @@ int unpack_frame(nghttp2_frame *frame, const uint8_t *in, size_t len)
payloadlen -= NGHTTP2_ALTSVC_FIXED_PARTLEN; payloadlen -= NGHTTP2_ALTSVC_FIXED_PARTLEN;
rv = nghttp2_frame_unpack_altsvc_payload(&frame->altsvc, rv = nghttp2_frame_unpack_altsvc_payload(&frame->ext,
payload, payloadlen, payload, payloadlen,
gift_payload, gift_payloadlen); gift_payload, gift_payloadlen);
break; break;