Merge branch 'altsvc'
This commit is contained in:
commit
9028512a5f
|
@ -51,3 +51,4 @@ Resources
|
||||||
|
|
||||||
* HTTP/2 https://tools.ietf.org/html/rfc7540
|
* HTTP/2 https://tools.ietf.org/html/rfc7540
|
||||||
* HPACK https://tools.ietf.org/html/rfc7541
|
* HPACK https://tools.ietf.org/html/rfc7541
|
||||||
|
* HTTP Alternative Services https://tools.ietf.org/html/rfc7838
|
||||||
|
|
|
@ -591,7 +591,12 @@ typedef enum {
|
||||||
* callbacks because the library processes this frame type and its
|
* callbacks because the library processes this frame type and its
|
||||||
* preceding HEADERS/PUSH_PROMISE as a single frame.
|
* preceding HEADERS/PUSH_PROMISE as a single frame.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_CONTINUATION = 0x09
|
NGHTTP2_CONTINUATION = 0x09,
|
||||||
|
/**
|
||||||
|
* The ALTSVC frame, which is defined in `RFC 7383
|
||||||
|
* <https://tools.ietf.org/html/rfc7838#section-4>`_.
|
||||||
|
*/
|
||||||
|
NGHTTP2_ALTSVC = 0x0a
|
||||||
} nghttp2_frame_type;
|
} nghttp2_frame_type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2373,6 +2378,26 @@ NGHTTP2_EXTERN void
|
||||||
nghttp2_option_set_user_recv_extension_type(nghttp2_option *option,
|
nghttp2_option_set_user_recv_extension_type(nghttp2_option *option,
|
||||||
uint8_t type);
|
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
|
* @function
|
||||||
*
|
*
|
||||||
|
@ -4113,6 +4138,80 @@ NGHTTP2_EXTERN int nghttp2_submit_extension(nghttp2_session *session,
|
||||||
uint8_t type, uint8_t flags,
|
uint8_t type, uint8_t flags,
|
||||||
int32_t stream_id, void *payload);
|
int32_t stream_id, void *payload);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @struct
|
||||||
|
*
|
||||||
|
* The payload of ALTSVC frame. ALTSVC 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_ALTSVC`, ``nghttp2_extension.payload`` will point to
|
||||||
|
* this struct.
|
||||||
|
*
|
||||||
|
* It has the following members:
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
/**
|
||||||
|
* The pointer to origin which this alternative service is
|
||||||
|
* associated with. This is not necessarily NULL-terminated.
|
||||||
|
*/
|
||||||
|
uint8_t *origin;
|
||||||
|
/**
|
||||||
|
* The length of the |origin|.
|
||||||
|
*/
|
||||||
|
size_t origin_len;
|
||||||
|
/**
|
||||||
|
* The pointer to Alt-Svc field value contained in ALTSVC frame.
|
||||||
|
* This is not necessarily NULL-terminated.
|
||||||
|
*/
|
||||||
|
uint8_t *field_value;
|
||||||
|
/**
|
||||||
|
* The length of the |field_value|.
|
||||||
|
*/
|
||||||
|
size_t field_value_len;
|
||||||
|
} nghttp2_ext_altsvc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @function
|
||||||
|
*
|
||||||
|
* Submits ALTSVC frame.
|
||||||
|
*
|
||||||
|
* ALTSVC frame is a non-critical extension to HTTP/2, and defined in
|
||||||
|
* is defined in `RFC 7383
|
||||||
|
* <https://tools.ietf.org/html/rfc7838#section-4>`_.
|
||||||
|
*
|
||||||
|
* The |flags| is currently ignored and should be
|
||||||
|
* :enum:`NGHTTP2_FLAG_NONE`.
|
||||||
|
*
|
||||||
|
* The |origin| points to the origin this alternative service is
|
||||||
|
* associated with. The |origin_len| is the length of the origin. If
|
||||||
|
* |stream_id| is 0, the origin must be specified. If |stream_id| is
|
||||||
|
* not zero, the origin must be empty (in other words, |origin_len|
|
||||||
|
* must be 0).
|
||||||
|
*
|
||||||
|
* The ALTSVC 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 sum of |origin_len| and |field_value_len| is larger than
|
||||||
|
* 16382; or |origin_len| is 0 while |stream_id| is 0; or
|
||||||
|
* |origin_len| is not 0 while |stream_id| is not 0.
|
||||||
|
*/
|
||||||
|
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
|
* @function
|
||||||
*
|
*
|
||||||
|
|
|
@ -193,6 +193,30 @@ void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type,
|
||||||
|
|
||||||
void nghttp2_frame_extension_free(nghttp2_extension *frame _U_) {}
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
size_t nghttp2_frame_priority_len(uint8_t flags) {
|
size_t nghttp2_frame_priority_len(uint8_t flags) {
|
||||||
if (flags & NGHTTP2_FLAG_PRIORITY) {
|
if (flags & NGHTTP2_FLAG_PRIORITY) {
|
||||||
return NGHTTP2_PRIORITY_SPECLEN;
|
return NGHTTP2_PRIORITY_SPECLEN;
|
||||||
|
@ -668,6 +692,55 @@ void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame,
|
||||||
nghttp2_get_uint32(payload) & NGHTTP2_WINDOW_SIZE_INCREMENT_MASK;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv,
|
||||||
size_t niv, nghttp2_mem *mem) {
|
size_t niv, nghttp2_mem *mem) {
|
||||||
nghttp2_settings_entry *iv_copy;
|
nghttp2_settings_entry *iv_copy;
|
||||||
|
|
|
@ -72,7 +72,7 @@
|
||||||
#define NGHTTP2_MAX_PADLEN 256
|
#define NGHTTP2_MAX_PADLEN 256
|
||||||
|
|
||||||
/* Union of extension frame payload */
|
/* 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);
|
void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd);
|
||||||
|
|
||||||
|
@ -367,6 +367,27 @@ void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame,
|
||||||
const uint8_t *payload,
|
const uint8_t *payload,
|
||||||
size_t payloadlen);
|
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);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
* Initializes HEADERS frame |frame| with given values. |frame| takes
|
||||||
* ownership of |nva|, so caller must not free it. If |stream_id| is
|
* ownership of |nva|, so caller must not free it. If |stream_id| is
|
||||||
|
@ -445,6 +466,25 @@ void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type,
|
||||||
|
|
||||||
void nghttp2_frame_extension_free(nghttp2_extension *frame);
|
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);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns the number of padding bytes after payload. The total
|
* Returns the number of padding bytes after payload. The total
|
||||||
* padding length is given in the |padlen|. The returned value does
|
* padding length is given in the |padlen|. The returned value does
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
*/
|
*/
|
||||||
#include "nghttp2_option.h"
|
#include "nghttp2_option.h"
|
||||||
|
|
||||||
|
#include "nghttp2_session.h"
|
||||||
|
|
||||||
int nghttp2_option_new(nghttp2_option **option_ptr) {
|
int nghttp2_option_new(nghttp2_option **option_ptr) {
|
||||||
*option_ptr = calloc(1, sizeof(nghttp2_option));
|
*option_ptr = calloc(1, sizeof(nghttp2_option));
|
||||||
|
|
||||||
|
@ -63,6 +65,10 @@ void nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option,
|
||||||
option->max_reserved_remote_streams = val;
|
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,
|
void nghttp2_option_set_user_recv_extension_type(nghttp2_option *option,
|
||||||
uint8_t type) {
|
uint8_t type) {
|
||||||
if (type < 10) {
|
if (type < 10) {
|
||||||
|
@ -70,8 +76,19 @@ void nghttp2_option_set_user_recv_extension_type(nghttp2_option *option,
|
||||||
}
|
}
|
||||||
|
|
||||||
option->opt_set_mask |= NGHTTP2_OPT_USER_RECV_EXT_TYPES;
|
option->opt_set_mask |= NGHTTP2_OPT_USER_RECV_EXT_TYPES;
|
||||||
option->user_recv_ext_types[type / 8] =
|
set_ext_type(option->user_recv_ext_types, type);
|
||||||
(uint8_t)(option->user_recv_ext_types[type / 8] | (1 << (type & 0x7)));
|
}
|
||||||
|
|
||||||
|
void nghttp2_option_set_builtin_recv_extension_type(nghttp2_option *option,
|
||||||
|
uint8_t type) {
|
||||||
|
switch (type) {
|
||||||
|
case NGHTTP2_ALTSVC:
|
||||||
|
option->opt_set_mask |= NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES;
|
||||||
|
option->builtin_recv_ext_types |= NGHTTP2_TYPEMASK_ALTSVC;
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_option_set_no_auto_ping_ack(nghttp2_option *option, int val) {
|
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_NO_HTTP_MESSAGING = 1 << 3,
|
||||||
NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS = 1 << 4,
|
NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS = 1 << 4,
|
||||||
NGHTTP2_OPT_USER_RECV_EXT_TYPES = 1 << 5,
|
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;
|
} nghttp2_option_flag;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -81,6 +82,10 @@ struct nghttp2_option {
|
||||||
* NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS
|
* NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS
|
||||||
*/
|
*/
|
||||||
uint32_t max_reserved_remote_streams;
|
uint32_t max_reserved_remote_streams;
|
||||||
|
/**
|
||||||
|
* NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES
|
||||||
|
*/
|
||||||
|
uint32_t builtin_recv_ext_types;
|
||||||
/**
|
/**
|
||||||
* NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE
|
* NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -72,9 +72,25 @@ void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) {
|
||||||
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;
|
||||||
default:
|
default: {
|
||||||
nghttp2_frame_extension_free(&frame->ext);
|
nghttp2_ext_aux_data *aux_data;
|
||||||
break;
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -87,11 +87,19 @@ typedef struct {
|
||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
} nghttp2_goaway_aux_data;
|
} 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 */
|
/* Additional data which cannot be stored in nghttp2_frame struct */
|
||||||
typedef union {
|
typedef union {
|
||||||
nghttp2_data_aux_data data;
|
nghttp2_data_aux_data data;
|
||||||
nghttp2_headers_aux_data headers;
|
nghttp2_headers_aux_data headers;
|
||||||
nghttp2_goaway_aux_data goaway;
|
nghttp2_goaway_aux_data goaway;
|
||||||
|
nghttp2_ext_aux_data ext;
|
||||||
} nghttp2_aux_data;
|
} nghttp2_aux_data;
|
||||||
|
|
||||||
struct nghttp2_outbound_item;
|
struct nghttp2_outbound_item;
|
||||||
|
@ -99,6 +107,9 @@ typedef struct nghttp2_outbound_item nghttp2_outbound_item;
|
||||||
|
|
||||||
struct nghttp2_outbound_item {
|
struct nghttp2_outbound_item {
|
||||||
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;
|
||||||
nghttp2_aux_data aux_data;
|
nghttp2_aux_data aux_data;
|
||||||
/* The priority used in priority comparion. Smaller is served
|
/* The priority used in priority comparion. Smaller is served
|
||||||
ealier. For PING, SETTINGS and non-DATA frames (excluding
|
ealier. For PING, SETTINGS and non-DATA frames (excluding
|
||||||
|
|
|
@ -142,6 +142,10 @@ static int session_detect_idle_stream(nghttp2_session *session,
|
||||||
return 0;
|
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,
|
static int session_call_error_callback(nghttp2_session *session,
|
||||||
const char *fmt, ...) {
|
const char *fmt, ...) {
|
||||||
size_t bufsize;
|
size_t bufsize;
|
||||||
|
@ -316,7 +320,20 @@ static void session_inbound_frame_reset(nghttp2_session *session) {
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* extension frame */
|
/* extension frame */
|
||||||
nghttp2_frame_extension_free(&iframe->frame.ext);
|
if (check_ext_type_set(session->user_recv_ext_types,
|
||||||
|
iframe->frame.hd.type)) {
|
||||||
|
nghttp2_frame_extension_free(&iframe->frame.ext);
|
||||||
|
} else {
|
||||||
|
switch (iframe->frame.hd.type) {
|
||||||
|
case NGHTTP2_ALTSVC:
|
||||||
|
if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ALTSVC) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nghttp2_frame_altsvc_free(&iframe->frame.ext, mem);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,6 +349,8 @@ static void session_inbound_frame_reset(nghttp2_session *session) {
|
||||||
nghttp2_buf_free(&iframe->lbuf, mem);
|
nghttp2_buf_free(&iframe->lbuf, mem);
|
||||||
nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0);
|
nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0);
|
||||||
|
|
||||||
|
iframe->raw_lbuf = NULL;
|
||||||
|
|
||||||
iframe->niv = 0;
|
iframe->niv = 0;
|
||||||
iframe->payloadleft = 0;
|
iframe->payloadleft = 0;
|
||||||
iframe->padlen = 0;
|
iframe->padlen = 0;
|
||||||
|
@ -474,6 +493,10 @@ static int session_new(nghttp2_session **session_ptr,
|
||||||
sizeof((*session_ptr)->user_recv_ext_types));
|
sizeof((*session_ptr)->user_recv_ext_types));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (option->opt_set_mask & NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES) {
|
||||||
|
(*session_ptr)->builtin_recv_ext_types = option->builtin_recv_ext_types;
|
||||||
|
}
|
||||||
|
|
||||||
if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_PING_ACK) &&
|
if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_PING_ACK) &&
|
||||||
option->no_auto_ping_ack) {
|
option->no_auto_ping_ack) {
|
||||||
(*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_PING_ACK;
|
(*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_PING_ACK;
|
||||||
|
@ -1664,6 +1687,26 @@ static int session_predicate_window_update_send(nghttp2_session *session,
|
||||||
return 0;
|
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
|
/* Take into account settings max frame size and both connection-level
|
||||||
flow control here */
|
flow control here */
|
||||||
static ssize_t
|
static ssize_t
|
||||||
|
@ -2103,19 +2146,45 @@ static int session_prep_frame(nghttp2_session *session,
|
||||||
/* We never handle CONTINUATION here. */
|
/* We never handle CONTINUATION here. */
|
||||||
assert(0);
|
assert(0);
|
||||||
break;
|
break;
|
||||||
default:
|
default: {
|
||||||
|
nghttp2_ext_aux_data *aux_data;
|
||||||
|
|
||||||
/* extension frame */
|
/* 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);
|
switch (frame->hd.type) {
|
||||||
if (rv != 0) {
|
case NGHTTP2_ALTSVC:
|
||||||
return rv;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
size_t next_readmax;
|
size_t next_readmax;
|
||||||
|
@ -4591,6 +4660,52 @@ static int session_process_window_update_frame(nghttp2_session *session) {
|
||||||
return nghttp2_session_on_window_update_received(session, frame);
|
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;
|
||||||
|
nghttp2_stream *stream;
|
||||||
|
|
||||||
|
altsvc = frame->ext.payload;
|
||||||
|
|
||||||
|
/* session->server case has been excluded */
|
||||||
|
|
||||||
|
if (frame->hd.stream_id == 0) {
|
||||||
|
if (altsvc->origin_len == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (altsvc->origin_len > 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
|
||||||
|
if (!stream) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream->state == NGHTTP2_STREAM_CLOSING) {
|
||||||
|
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;
|
||||||
|
|
||||||
|
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) {
|
static int session_process_extension_frame(nghttp2_session *session) {
|
||||||
int rv;
|
int rv;
|
||||||
nghttp2_inbound_frame *iframe = &session->iframe;
|
nghttp2_inbound_frame *iframe = &session->iframe;
|
||||||
|
@ -5434,25 +5549,66 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
DEBUGF(fprintf(stderr, "recv: unknown frame\n"));
|
DEBUGF(fprintf(stderr, "recv: extension frame\n"));
|
||||||
|
|
||||||
if (!session->callbacks.unpack_extension_callback ||
|
if (check_ext_type_set(session->user_recv_ext_types,
|
||||||
(session->user_recv_ext_types[iframe->frame.hd.type / 8] &
|
iframe->frame.hd.type)) {
|
||||||
(1 << (iframe->frame.hd.type & 0x7))) == 0) {
|
if (!session->callbacks.unpack_extension_callback) {
|
||||||
/* Silently ignore unknown frame type. */
|
/* Silently ignore unknown frame type. */
|
||||||
|
|
||||||
|
busy = 1;
|
||||||
|
|
||||||
|
iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
busy = 1;
|
busy = 1;
|
||||||
|
|
||||||
iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
|
iframe->state = NGHTTP2_IB_READ_EXTENSION_PAYLOAD;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
} else {
|
||||||
|
switch (iframe->frame.hd.type) {
|
||||||
|
case NGHTTP2_ALTSVC:
|
||||||
|
if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ALTSVC) ==
|
||||||
|
0) {
|
||||||
|
busy = 1;
|
||||||
|
iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUGF(fprintf(stderr, "recv: ALTSVC\n"));
|
||||||
|
|
||||||
|
iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
|
||||||
|
iframe->frame.ext.payload = &iframe->ext_frame_payload.altsvc;
|
||||||
|
|
||||||
|
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;
|
||||||
|
default:
|
||||||
|
busy = 1;
|
||||||
|
|
||||||
|
iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
busy = 1;
|
|
||||||
|
|
||||||
iframe->state = NGHTTP2_IB_READ_EXTENSION_PAYLOAD;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!on_begin_frame_called) {
|
if (!on_begin_frame_called) {
|
||||||
|
@ -5662,6 +5818,37 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
||||||
session_inbound_frame_reset(session);
|
session_inbound_frame_reset(session);
|
||||||
|
|
||||||
break;
|
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:
|
default:
|
||||||
/* This is unknown frame */
|
/* This is unknown frame */
|
||||||
session_inbound_frame_reset(session);
|
session_inbound_frame_reset(session);
|
||||||
|
@ -6187,6 +6374,36 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
|
||||||
|
|
||||||
session_inbound_frame_reset(session);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,15 @@ typedef enum {
|
||||||
NGHTTP2_OPTMASK_NO_AUTO_PING_ACK = 1 << 3
|
NGHTTP2_OPTMASK_NO_AUTO_PING_ACK = 1 << 3
|
||||||
} nghttp2_optmask;
|
} nghttp2_optmask;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* bitmask for built-in type to enable the default handling for that
|
||||||
|
* type of the frame.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
NGHTTP2_TYPEMASK_NONE = 0,
|
||||||
|
NGHTTP2_TYPEMASK_ALTSVC = 1 << 0
|
||||||
|
} nghttp2_typemask;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
NGHTTP2_OB_POP_ITEM,
|
NGHTTP2_OB_POP_ITEM,
|
||||||
NGHTTP2_OB_SEND_DATA,
|
NGHTTP2_OB_SEND_DATA,
|
||||||
|
@ -107,6 +116,7 @@ typedef enum {
|
||||||
NGHTTP2_IB_READ_DATA,
|
NGHTTP2_IB_READ_DATA,
|
||||||
NGHTTP2_IB_IGN_DATA,
|
NGHTTP2_IB_IGN_DATA,
|
||||||
NGHTTP2_IB_IGN_ALL,
|
NGHTTP2_IB_IGN_ALL,
|
||||||
|
NGHTTP2_IB_READ_ALTSVC_PAYLOAD,
|
||||||
NGHTTP2_IB_READ_EXTENSION_PAYLOAD
|
NGHTTP2_IB_READ_EXTENSION_PAYLOAD
|
||||||
} nghttp2_inbound_state;
|
} nghttp2_inbound_state;
|
||||||
|
|
||||||
|
@ -294,6 +304,9 @@ struct nghttp2_session {
|
||||||
/* Unacked local SETTINGS_MAX_CONCURRENT_STREAMS value. We use this
|
/* Unacked local SETTINGS_MAX_CONCURRENT_STREAMS value. We use this
|
||||||
to refuse the incoming stream if it exceeds this value. */
|
to refuse the incoming stream if it exceeds this value. */
|
||||||
uint32_t pending_local_max_concurrent_stream;
|
uint32_t pending_local_max_concurrent_stream;
|
||||||
|
/* The bitwose OR of zero or more of nghttp2_typemask to indicate
|
||||||
|
that the default handling of extension frame is enabled. */
|
||||||
|
uint32_t builtin_recv_ext_types;
|
||||||
/* Unacked local ENABLE_PUSH value. We use this to refuse
|
/* Unacked local ENABLE_PUSH value. We use this to refuse
|
||||||
PUSH_PROMISE before SETTINGS ACK is received. */
|
PUSH_PROMISE before SETTINGS ACK is received. */
|
||||||
uint8_t pending_enable_push;
|
uint8_t pending_enable_push;
|
||||||
|
@ -716,6 +729,19 @@ int nghttp2_session_on_goaway_received(nghttp2_session *session,
|
||||||
int nghttp2_session_on_window_update_received(nghttp2_session *session,
|
int nghttp2_session_on_window_update_received(nghttp2_session *session,
|
||||||
nghttp2_frame *frame);
|
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
|
* Called when DATA is received, assuming |frame| is properly
|
||||||
* initialized.
|
* initialized.
|
||||||
|
|
|
@ -410,6 +410,90 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
|
||||||
return 0;
|
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 (!session->server) {
|
||||||
|
return NGHTTP2_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (2 + origin_len + field_value_len > NGHTTP2_MAX_PAYLOADLEN) {
|
||||||
|
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream_id == 0) {
|
||||||
|
if (origin_len == 0) {
|
||||||
|
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
} else if (origin_len != 0) {
|
||||||
|
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;
|
||||||
|
if (origin_len) {
|
||||||
|
p = nghttp2_cpymem(p, origin, origin_len);
|
||||||
|
}
|
||||||
|
*p++ = '\0';
|
||||||
|
|
||||||
|
field_value_copy = p;
|
||||||
|
if (field_value_len) {
|
||||||
|
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 = &item->ext_frame_payload.altsvc;
|
||||||
|
|
||||||
|
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 rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail_item_malloc:
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
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,
|
||||||
const nghttp2_data_provider *data_prd) {
|
const nghttp2_data_provider *data_prd) {
|
||||||
uint8_t flags = NGHTTP2_FLAG_NONE;
|
uint8_t flags = NGHTTP2_FLAG_NONE;
|
||||||
|
|
|
@ -104,6 +104,8 @@ std::string 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:
|
||||||
|
return "ALTSVC";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string s = "extension(0x";
|
std::string s = "extension(0x";
|
||||||
|
@ -339,6 +341,14 @@ 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: {
|
||||||
|
auto altsvc = static_cast<nghttp2_ext_altsvc *>(frame->ext.payload);
|
||||||
|
print_frame_attr_indent();
|
||||||
|
fprintf(outfile, "(origin=[%.*s], altsvc_field_value=[%.*s])\n",
|
||||||
|
static_cast<int>(altsvc->origin_len), altsvc->origin,
|
||||||
|
static_cast<int>(altsvc->field_value_len), altsvc->field_value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,6 +118,7 @@ Config::Config()
|
||||||
nghttp2_option_new(&http2_option);
|
nghttp2_option_new(&http2_option);
|
||||||
nghttp2_option_set_peer_max_concurrent_streams(http2_option,
|
nghttp2_option_set_peer_max_concurrent_streams(http2_option,
|
||||||
peer_max_concurrent_streams);
|
peer_max_concurrent_streams);
|
||||||
|
nghttp2_option_set_builtin_recv_extension_type(http2_option, NGHTTP2_ALTSVC);
|
||||||
}
|
}
|
||||||
|
|
||||||
Config::~Config() { nghttp2_option_del(http2_option); }
|
Config::~Config() { nghttp2_option_del(http2_option); }
|
||||||
|
|
|
@ -55,6 +55,8 @@ int main(int argc _U_, char *argv[] _U_) {
|
||||||
/* add the tests to the suite */
|
/* add the tests to the suite */
|
||||||
if (!CU_add_test(pSuite, "failmalloc_session_send",
|
if (!CU_add_test(pSuite, "failmalloc_session_send",
|
||||||
test_nghttp2_session_send) ||
|
test_nghttp2_session_send) ||
|
||||||
|
!CU_add_test(pSuite, "failmalloc_session_send_server",
|
||||||
|
test_nghttp2_session_send_server) ||
|
||||||
!CU_add_test(pSuite, "failmalloc_session_recv",
|
!CU_add_test(pSuite, "failmalloc_session_recv",
|
||||||
test_nghttp2_session_recv) ||
|
test_nghttp2_session_recv) ||
|
||||||
!CU_add_test(pSuite, "failmalloc_frame", test_nghttp2_frame) ||
|
!CU_add_test(pSuite, "failmalloc_frame", test_nghttp2_frame) ||
|
||||||
|
|
|
@ -217,6 +217,50 @@ void test_nghttp2_session_send(void) {
|
||||||
TEST_FAILMALLOC_RUN(run_nghttp2_session_send);
|
TEST_FAILMALLOC_RUN(run_nghttp2_session_send);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void run_nghttp2_session_send_server(void) {
|
||||||
|
nghttp2_session *session;
|
||||||
|
nghttp2_session_callbacks *callbacks;
|
||||||
|
int rv;
|
||||||
|
const uint8_t *txdata;
|
||||||
|
ssize_t txdatalen;
|
||||||
|
rv = nghttp2_session_callbacks_new(&callbacks);
|
||||||
|
if (rv != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = nghttp2_session_server_new3(&session, callbacks, NULL, NULL,
|
||||||
|
nghttp2_mem_fm());
|
||||||
|
|
||||||
|
nghttp2_session_callbacks_del(callbacks);
|
||||||
|
|
||||||
|
if (rv != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t origin[] = "nghttp2.org";
|
||||||
|
const uint8_t altsvc_field_value[] = "h2=\":443\"";
|
||||||
|
|
||||||
|
rv = nghttp2_submit_altsvc(session, NGHTTP2_FLAG_NONE, 0, origin,
|
||||||
|
sizeof(origin) - 1, altsvc_field_value,
|
||||||
|
sizeof(altsvc_field_value) - 1);
|
||||||
|
if (rv != 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
txdatalen = nghttp2_session_mem_send(session, &txdata);
|
||||||
|
|
||||||
|
if (txdatalen < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
fail:
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_nghttp2_session_send_server(void) {
|
||||||
|
TEST_FAILMALLOC_RUN(run_nghttp2_session_send_server);
|
||||||
|
}
|
||||||
|
|
||||||
static void run_nghttp2_session_recv(void) {
|
static void run_nghttp2_session_recv(void) {
|
||||||
nghttp2_session *session;
|
nghttp2_session *session;
|
||||||
nghttp2_session_callbacks callbacks;
|
nghttp2_session_callbacks callbacks;
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#endif /* HAVE_CONFIG_H */
|
#endif /* HAVE_CONFIG_H */
|
||||||
|
|
||||||
void test_nghttp2_session_send(void);
|
void test_nghttp2_session_send(void);
|
||||||
|
void test_nghttp2_session_send_server(void);
|
||||||
void test_nghttp2_session_recv(void);
|
void test_nghttp2_session_recv(void);
|
||||||
void test_nghttp2_frame(void);
|
void test_nghttp2_frame(void);
|
||||||
void test_nghttp2_hd(void);
|
void test_nghttp2_hd(void);
|
||||||
|
|
|
@ -103,6 +103,8 @@ int main(int argc _U_, char *argv[] _U_) {
|
||||||
test_nghttp2_session_recv_too_large_frame_length) ||
|
test_nghttp2_session_recv_too_large_frame_length) ||
|
||||||
!CU_add_test(pSuite, "session_recv_extension",
|
!CU_add_test(pSuite, "session_recv_extension",
|
||||||
test_nghttp2_session_recv_extension) ||
|
test_nghttp2_session_recv_extension) ||
|
||||||
|
!CU_add_test(pSuite, "session_recv_altsvc",
|
||||||
|
test_nghttp2_session_recv_altsvc) ||
|
||||||
!CU_add_test(pSuite, "session_continue", test_nghttp2_session_continue) ||
|
!CU_add_test(pSuite, "session_continue", test_nghttp2_session_continue) ||
|
||||||
!CU_add_test(pSuite, "session_add_frame",
|
!CU_add_test(pSuite, "session_add_frame",
|
||||||
test_nghttp2_session_add_frame) ||
|
test_nghttp2_session_add_frame) ||
|
||||||
|
@ -132,6 +134,8 @@ int main(int argc _U_, char *argv[] _U_) {
|
||||||
test_nghttp2_session_on_data_received) ||
|
test_nghttp2_session_on_data_received) ||
|
||||||
!CU_add_test(pSuite, "session_on_data_received_fail_fast",
|
!CU_add_test(pSuite, "session_on_data_received_fail_fast",
|
||||||
test_nghttp2_session_on_data_received_fail_fast) ||
|
test_nghttp2_session_on_data_received_fail_fast) ||
|
||||||
|
!CU_add_test(pSuite, "session_on_altsvc_received",
|
||||||
|
test_nghttp2_session_on_altsvc_received) ||
|
||||||
!CU_add_test(pSuite, "session_send_headers_start_stream",
|
!CU_add_test(pSuite, "session_send_headers_start_stream",
|
||||||
test_nghttp2_session_send_headers_start_stream) ||
|
test_nghttp2_session_send_headers_start_stream) ||
|
||||||
!CU_add_test(pSuite, "session_send_headers_reply",
|
!CU_add_test(pSuite, "session_send_headers_reply",
|
||||||
|
@ -197,6 +201,7 @@ int main(int argc _U_, char *argv[] _U_) {
|
||||||
!CU_add_test(pSuite, "submit_invalid_nv",
|
!CU_add_test(pSuite, "submit_invalid_nv",
|
||||||
test_nghttp2_submit_invalid_nv) ||
|
test_nghttp2_submit_invalid_nv) ||
|
||||||
!CU_add_test(pSuite, "submit_extension", test_nghttp2_submit_extension) ||
|
!CU_add_test(pSuite, "submit_extension", test_nghttp2_submit_extension) ||
|
||||||
|
!CU_add_test(pSuite, "submit_altsvc", test_nghttp2_submit_altsvc) ||
|
||||||
!CU_add_test(pSuite, "session_open_stream",
|
!CU_add_test(pSuite, "session_open_stream",
|
||||||
test_nghttp2_session_open_stream) ||
|
test_nghttp2_session_open_stream) ||
|
||||||
!CU_add_test(pSuite, "session_open_stream_with_idle_stream_dep",
|
!CU_add_test(pSuite, "session_open_stream_with_idle_stream_dep",
|
||||||
|
|
|
@ -1955,6 +1955,124 @@ void test_nghttp2_session_recv_extension(void) {
|
||||||
nghttp2_option_del(option);
|
nghttp2_option_del(option);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_nghttp2_session_recv_altsvc(void) {
|
||||||
|
nghttp2_session *session;
|
||||||
|
nghttp2_session_callbacks callbacks;
|
||||||
|
my_user_data ud;
|
||||||
|
nghttp2_buf buf;
|
||||||
|
nghttp2_frame_hd hd;
|
||||||
|
nghttp2_mem *mem;
|
||||||
|
ssize_t rv;
|
||||||
|
nghttp2_option *option;
|
||||||
|
static const uint8_t origin[] = "nghttp2.org";
|
||||||
|
static const uint8_t field_value[] = "h2=\":443\"";
|
||||||
|
|
||||||
|
mem = nghttp2_mem_default();
|
||||||
|
|
||||||
|
nghttp2_buf_init2(&buf, NGHTTP2_FRAME_HDLEN + NGHTTP2_MAX_FRAME_SIZE_MIN,
|
||||||
|
mem);
|
||||||
|
|
||||||
|
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
|
||||||
|
|
||||||
|
callbacks.on_frame_recv_callback = on_frame_recv_callback;
|
||||||
|
|
||||||
|
nghttp2_option_new(&option);
|
||||||
|
nghttp2_option_set_builtin_recv_extension_type(option, NGHTTP2_ALTSVC);
|
||||||
|
|
||||||
|
nghttp2_session_client_new2(&session, &callbacks, &ud, option);
|
||||||
|
|
||||||
|
nghttp2_frame_hd_init(&hd, 2 + sizeof(origin) - 1 + sizeof(field_value) - 1,
|
||||||
|
NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, 0);
|
||||||
|
nghttp2_frame_pack_frame_hd(buf.last, &hd);
|
||||||
|
buf.last += NGHTTP2_FRAME_HDLEN;
|
||||||
|
nghttp2_put_uint16be(buf.last, sizeof(origin) - 1);
|
||||||
|
buf.last += 2;
|
||||||
|
buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1);
|
||||||
|
buf.last = nghttp2_cpymem(buf.last, field_value, sizeof(field_value) - 1);
|
||||||
|
|
||||||
|
ud.frame_recv_cb_called = 0;
|
||||||
|
rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf));
|
||||||
|
|
||||||
|
CU_ASSERT((ssize_t)nghttp2_buf_len(&buf) == rv);
|
||||||
|
CU_ASSERT(1 == ud.frame_recv_cb_called);
|
||||||
|
CU_ASSERT(NGHTTP2_ALTSVC == ud.recv_frame_hd.type);
|
||||||
|
CU_ASSERT(NGHTTP2_FLAG_NONE == ud.recv_frame_hd.flags);
|
||||||
|
CU_ASSERT(0 == ud.recv_frame_hd.stream_id);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
|
/* size of origin is larger than frame length */
|
||||||
|
nghttp2_buf_reset(&buf);
|
||||||
|
|
||||||
|
nghttp2_session_client_new2(&session, &callbacks, &ud, option);
|
||||||
|
|
||||||
|
nghttp2_frame_hd_init(&hd, 2 + sizeof(origin) - 1 - 1, NGHTTP2_ALTSVC,
|
||||||
|
NGHTTP2_FLAG_NONE, 0);
|
||||||
|
nghttp2_frame_pack_frame_hd(buf.last, &hd);
|
||||||
|
buf.last += NGHTTP2_FRAME_HDLEN;
|
||||||
|
nghttp2_put_uint16be(buf.last, sizeof(origin) - 1);
|
||||||
|
buf.last += 2;
|
||||||
|
buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1 - 1);
|
||||||
|
|
||||||
|
ud.frame_recv_cb_called = 0;
|
||||||
|
rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf));
|
||||||
|
|
||||||
|
CU_ASSERT((ssize_t)nghttp2_buf_len(&buf) == rv);
|
||||||
|
CU_ASSERT(0 == ud.frame_recv_cb_called);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
|
/* send large frame (16KiB) */
|
||||||
|
nghttp2_buf_reset(&buf);
|
||||||
|
|
||||||
|
nghttp2_session_client_new2(&session, &callbacks, &ud, option);
|
||||||
|
|
||||||
|
nghttp2_frame_hd_init(&hd, NGHTTP2_MAX_FRAME_SIZE_MIN, NGHTTP2_ALTSVC,
|
||||||
|
NGHTTP2_FLAG_NONE, 0);
|
||||||
|
nghttp2_frame_pack_frame_hd(buf.last, &hd);
|
||||||
|
buf.last += NGHTTP2_FRAME_HDLEN;
|
||||||
|
nghttp2_put_uint16be(buf.last, sizeof(origin) - 1);
|
||||||
|
buf.last += 2;
|
||||||
|
buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1);
|
||||||
|
memset(buf.last, 0, nghttp2_buf_avail(&buf));
|
||||||
|
buf.last += nghttp2_buf_avail(&buf);
|
||||||
|
|
||||||
|
ud.frame_recv_cb_called = 0;
|
||||||
|
rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf));
|
||||||
|
|
||||||
|
CU_ASSERT((ssize_t)nghttp2_buf_len(&buf) == rv);
|
||||||
|
CU_ASSERT(1 == ud.frame_recv_cb_called);
|
||||||
|
CU_ASSERT(NGHTTP2_ALTSVC == ud.recv_frame_hd.type);
|
||||||
|
CU_ASSERT(NGHTTP2_MAX_FRAME_SIZE_MIN == ud.recv_frame_hd.length);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
|
/* received by server */
|
||||||
|
nghttp2_buf_reset(&buf);
|
||||||
|
|
||||||
|
nghttp2_session_server_new2(&session, &callbacks, &ud, option);
|
||||||
|
|
||||||
|
nghttp2_frame_hd_init(&hd, 2 + sizeof(origin) - 1 + sizeof(field_value) - 1,
|
||||||
|
NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, 0);
|
||||||
|
nghttp2_frame_pack_frame_hd(buf.last, &hd);
|
||||||
|
buf.last += NGHTTP2_FRAME_HDLEN;
|
||||||
|
nghttp2_put_uint16be(buf.last, sizeof(origin) - 1);
|
||||||
|
buf.last += 2;
|
||||||
|
buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1);
|
||||||
|
buf.last = nghttp2_cpymem(buf.last, field_value, sizeof(field_value) - 1);
|
||||||
|
|
||||||
|
ud.frame_recv_cb_called = 0;
|
||||||
|
rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf));
|
||||||
|
|
||||||
|
CU_ASSERT((ssize_t)nghttp2_buf_len(&buf) == rv);
|
||||||
|
CU_ASSERT(0 == ud.frame_recv_cb_called);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
|
nghttp2_buf_free(&buf, mem);
|
||||||
|
nghttp2_option_del(option);
|
||||||
|
}
|
||||||
|
|
||||||
void test_nghttp2_session_continue(void) {
|
void test_nghttp2_session_continue(void) {
|
||||||
nghttp2_session *session;
|
nghttp2_session *session;
|
||||||
nghttp2_session_callbacks callbacks;
|
nghttp2_session_callbacks callbacks;
|
||||||
|
@ -3364,6 +3482,113 @@ void test_nghttp2_session_on_data_received_fail_fast(void) {
|
||||||
nghttp2_session_del(session);
|
nghttp2_session_del(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_nghttp2_session_on_altsvc_received(void) {
|
||||||
|
nghttp2_session *session;
|
||||||
|
nghttp2_session_callbacks callbacks;
|
||||||
|
my_user_data ud;
|
||||||
|
nghttp2_frame frame;
|
||||||
|
nghttp2_mem *mem;
|
||||||
|
nghttp2_option *option;
|
||||||
|
uint8_t origin[] = "nghttp2.org";
|
||||||
|
uint8_t field_value[] = "h2=\":443\"";
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
mem = nghttp2_mem_default();
|
||||||
|
|
||||||
|
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
|
||||||
|
callbacks.on_frame_recv_callback = on_frame_recv_callback;
|
||||||
|
|
||||||
|
nghttp2_option_new(&option);
|
||||||
|
nghttp2_option_set_builtin_recv_extension_type(option, NGHTTP2_ALTSVC);
|
||||||
|
|
||||||
|
nghttp2_session_client_new2(&session, &callbacks, &ud, option);
|
||||||
|
|
||||||
|
frame.ext.payload = &session->iframe.ext_frame_payload;
|
||||||
|
|
||||||
|
/* We just pass the strings without making a copy. This is OK,
|
||||||
|
since we never call nghttp2_frame_altsvc_free(). */
|
||||||
|
nghttp2_frame_altsvc_init(&frame.ext, 0, origin, sizeof(origin) - 1,
|
||||||
|
field_value, sizeof(field_value) - 1);
|
||||||
|
|
||||||
|
ud.frame_recv_cb_called = 0;
|
||||||
|
rv = nghttp2_session_on_altsvc_received(session, &frame);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == rv);
|
||||||
|
CU_ASSERT(1 == ud.frame_recv_cb_called);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
|
/* Receiving empty origin with stream ID == 0 */
|
||||||
|
nghttp2_session_client_new2(&session, &callbacks, &ud, option);
|
||||||
|
|
||||||
|
frame.ext.payload = &session->iframe.ext_frame_payload;
|
||||||
|
|
||||||
|
nghttp2_frame_altsvc_init(&frame.ext, 0, origin, 0, field_value,
|
||||||
|
sizeof(field_value) - 1);
|
||||||
|
|
||||||
|
ud.frame_recv_cb_called = 0;
|
||||||
|
rv = nghttp2_session_on_altsvc_received(session, &frame);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == rv);
|
||||||
|
CU_ASSERT(0 == ud.frame_recv_cb_called);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
|
/* Receiving non-empty origin with stream ID != 0 */
|
||||||
|
nghttp2_session_client_new2(&session, &callbacks, &ud, option);
|
||||||
|
|
||||||
|
frame.ext.payload = &session->iframe.ext_frame_payload;
|
||||||
|
|
||||||
|
open_sent_stream(session, 1);
|
||||||
|
|
||||||
|
nghttp2_frame_altsvc_init(&frame.ext, 1, origin, sizeof(origin) - 1,
|
||||||
|
field_value, sizeof(field_value) - 1);
|
||||||
|
|
||||||
|
ud.frame_recv_cb_called = 0;
|
||||||
|
rv = nghttp2_session_on_altsvc_received(session, &frame);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == rv);
|
||||||
|
CU_ASSERT(0 == ud.frame_recv_cb_called);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
|
/* Receiving empty origin with stream ID != 0; this is OK */
|
||||||
|
nghttp2_session_client_new2(&session, &callbacks, &ud, option);
|
||||||
|
|
||||||
|
frame.ext.payload = &session->iframe.ext_frame_payload;
|
||||||
|
|
||||||
|
open_sent_stream(session, 1);
|
||||||
|
|
||||||
|
nghttp2_frame_altsvc_init(&frame.ext, 1, origin, 0, field_value,
|
||||||
|
sizeof(field_value) - 1);
|
||||||
|
|
||||||
|
ud.frame_recv_cb_called = 0;
|
||||||
|
rv = nghttp2_session_on_altsvc_received(session, &frame);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == rv);
|
||||||
|
CU_ASSERT(1 == ud.frame_recv_cb_called);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
|
/* Stream does not exist; ALTSVC will be ignored. */
|
||||||
|
nghttp2_session_client_new2(&session, &callbacks, &ud, option);
|
||||||
|
|
||||||
|
frame.ext.payload = &session->iframe.ext_frame_payload;
|
||||||
|
|
||||||
|
nghttp2_frame_altsvc_init(&frame.ext, 1, origin, 0, field_value,
|
||||||
|
sizeof(field_value) - 1);
|
||||||
|
|
||||||
|
ud.frame_recv_cb_called = 0;
|
||||||
|
rv = nghttp2_session_on_altsvc_received(session, &frame);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == rv);
|
||||||
|
CU_ASSERT(0 == ud.frame_recv_cb_called);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
|
nghttp2_option_del(option);
|
||||||
|
}
|
||||||
|
|
||||||
void test_nghttp2_session_send_headers_start_stream(void) {
|
void test_nghttp2_session_send_headers_start_stream(void) {
|
||||||
nghttp2_session *session;
|
nghttp2_session *session;
|
||||||
nghttp2_session_callbacks callbacks;
|
nghttp2_session_callbacks callbacks;
|
||||||
|
@ -5217,6 +5442,82 @@ void test_nghttp2_submit_extension(void) {
|
||||||
nghttp2_buf_free(&ud.scratchbuf, mem);
|
nghttp2_buf_free(&ud.scratchbuf, mem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_nghttp2_submit_altsvc(void) {
|
||||||
|
nghttp2_session *session;
|
||||||
|
nghttp2_session_callbacks callbacks;
|
||||||
|
nghttp2_mem *mem;
|
||||||
|
my_user_data ud;
|
||||||
|
int rv;
|
||||||
|
ssize_t len;
|
||||||
|
const uint8_t *data;
|
||||||
|
nghttp2_frame_hd hd;
|
||||||
|
size_t origin_len;
|
||||||
|
|
||||||
|
mem = nghttp2_mem_default();
|
||||||
|
|
||||||
|
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
|
||||||
|
|
||||||
|
nghttp2_session_server_new(&session, &callbacks, &ud);
|
||||||
|
|
||||||
|
const uint8_t origin[] = "nghttp2.org";
|
||||||
|
const uint8_t field_value[] = "h2=\":443\"";
|
||||||
|
|
||||||
|
rv = nghttp2_submit_altsvc(session, NGHTTP2_FLAG_NONE, 0, origin,
|
||||||
|
sizeof(origin) - 1, field_value,
|
||||||
|
sizeof(field_value) - 1);
|
||||||
|
|
||||||
|
CU_ASSERT(0 == rv);
|
||||||
|
|
||||||
|
ud.frame_send_cb_called = 0;
|
||||||
|
|
||||||
|
len = nghttp2_session_mem_send(session, &data);
|
||||||
|
|
||||||
|
CU_ASSERT(len ==
|
||||||
|
NGHTTP2_FRAME_HDLEN + 2 + sizeof(origin) - 1 + sizeof(field_value) -
|
||||||
|
1);
|
||||||
|
|
||||||
|
nghttp2_frame_unpack_frame_hd(&hd, data);
|
||||||
|
|
||||||
|
CU_ASSERT(2 + sizeof(origin) - 1 + sizeof(field_value) - 1 == hd.length);
|
||||||
|
CU_ASSERT(NGHTTP2_ALTSVC == hd.type);
|
||||||
|
CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags);
|
||||||
|
|
||||||
|
origin_len = nghttp2_get_uint16(data + NGHTTP2_FRAME_HDLEN);
|
||||||
|
|
||||||
|
CU_ASSERT(sizeof(origin) - 1 == origin_len);
|
||||||
|
CU_ASSERT(0 ==
|
||||||
|
memcmp(origin, data + NGHTTP2_FRAME_HDLEN + 2, sizeof(origin) - 1));
|
||||||
|
CU_ASSERT(0 == memcmp(field_value,
|
||||||
|
data + NGHTTP2_FRAME_HDLEN + 2 + sizeof(origin) - 1,
|
||||||
|
hd.length - (sizeof(origin) - 1) - 2));
|
||||||
|
|
||||||
|
/* submitting empty origin with stream_id == 0 is error */
|
||||||
|
rv = nghttp2_submit_altsvc(session, NGHTTP2_FLAG_NONE, 0, NULL, 0,
|
||||||
|
field_value, sizeof(field_value) - 1);
|
||||||
|
|
||||||
|
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
|
||||||
|
|
||||||
|
/* submitting non-empty origin with stream_id != 0 is error */
|
||||||
|
rv = nghttp2_submit_altsvc(session, NGHTTP2_FLAG_NONE, 1, origin,
|
||||||
|
sizeof(origin) - 1, field_value,
|
||||||
|
sizeof(field_value) - 1);
|
||||||
|
|
||||||
|
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
|
||||||
|
/* submitting from client side session is error */
|
||||||
|
nghttp2_session_client_new(&session, &callbacks, NULL);
|
||||||
|
|
||||||
|
rv = nghttp2_submit_altsvc(session, NGHTTP2_FLAG_NONE, 0, origin,
|
||||||
|
sizeof(origin) - 1, field_value,
|
||||||
|
sizeof(field_value) - 1);
|
||||||
|
|
||||||
|
CU_ASSERT(NGHTTP2_ERR_INVALID_STATE == rv);
|
||||||
|
|
||||||
|
nghttp2_session_del(session);
|
||||||
|
}
|
||||||
|
|
||||||
void test_nghttp2_session_open_stream(void) {
|
void test_nghttp2_session_open_stream(void) {
|
||||||
nghttp2_session *session;
|
nghttp2_session *session;
|
||||||
nghttp2_session_callbacks callbacks;
|
nghttp2_session_callbacks callbacks;
|
||||||
|
|
|
@ -45,6 +45,7 @@ void test_nghttp2_session_recv_unexpected_continuation(void);
|
||||||
void test_nghttp2_session_recv_settings_header_table_size(void);
|
void test_nghttp2_session_recv_settings_header_table_size(void);
|
||||||
void test_nghttp2_session_recv_too_large_frame_length(void);
|
void test_nghttp2_session_recv_too_large_frame_length(void);
|
||||||
void test_nghttp2_session_recv_extension(void);
|
void test_nghttp2_session_recv_extension(void);
|
||||||
|
void test_nghttp2_session_recv_altsvc(void);
|
||||||
void test_nghttp2_session_continue(void);
|
void test_nghttp2_session_continue(void);
|
||||||
void test_nghttp2_session_add_frame(void);
|
void test_nghttp2_session_add_frame(void);
|
||||||
void test_nghttp2_session_on_request_headers_received(void);
|
void test_nghttp2_session_on_request_headers_received(void);
|
||||||
|
@ -60,6 +61,7 @@ void test_nghttp2_session_on_goaway_received(void);
|
||||||
void test_nghttp2_session_on_window_update_received(void);
|
void test_nghttp2_session_on_window_update_received(void);
|
||||||
void test_nghttp2_session_on_data_received(void);
|
void test_nghttp2_session_on_data_received(void);
|
||||||
void test_nghttp2_session_on_data_received_fail_fast(void);
|
void test_nghttp2_session_on_data_received_fail_fast(void);
|
||||||
|
void test_nghttp2_session_on_altsvc_received(void);
|
||||||
void test_nghttp2_session_send_headers_start_stream(void);
|
void test_nghttp2_session_send_headers_start_stream(void);
|
||||||
void test_nghttp2_session_send_headers_reply(void);
|
void test_nghttp2_session_send_headers_reply(void);
|
||||||
void test_nghttp2_session_send_headers_frame_size_error(void);
|
void test_nghttp2_session_send_headers_frame_size_error(void);
|
||||||
|
@ -95,6 +97,7 @@ void test_nghttp2_submit_window_update_local_window_size(void);
|
||||||
void test_nghttp2_submit_shutdown_notice(void);
|
void test_nghttp2_submit_shutdown_notice(void);
|
||||||
void test_nghttp2_submit_invalid_nv(void);
|
void test_nghttp2_submit_invalid_nv(void);
|
||||||
void test_nghttp2_submit_extension(void);
|
void test_nghttp2_submit_extension(void);
|
||||||
|
void test_nghttp2_submit_altsvc(void);
|
||||||
void test_nghttp2_session_open_stream(void);
|
void test_nghttp2_session_open_stream(void);
|
||||||
void test_nghttp2_session_open_stream_with_idle_stream_dep(void);
|
void test_nghttp2_session_open_stream_with_idle_stream_dep(void);
|
||||||
void test_nghttp2_session_get_next_ob_item(void);
|
void test_nghttp2_session_get_next_ob_item(void);
|
||||||
|
|
Loading…
Reference in New Issue