Implement dependency based priority

This commit is contained in:
Tatsuhiro Tsujikawa 2014-03-26 02:04:24 +09:00
parent 8ccb6e463d
commit f7162ab702
27 changed files with 3319 additions and 521 deletions

View File

@ -521,7 +521,6 @@ static void ctl_poll(struct pollfd *pollfd, struct Connection *connection)
*/
static void submit_request(struct Connection *connection, struct Request *req)
{
int pri = 0;
int rv;
const nghttp2_nv nva[] = {
/* Make sure that the last item is NULL */
@ -532,7 +531,7 @@ static void submit_request(struct Connection *connection, struct Request *req)
MAKE_NV("accept", "*/*"),
MAKE_NV("user-agent", "nghttp2/"NGHTTP2_VERSION)
};
rv = nghttp2_submit_request(connection->session, pri,
rv = nghttp2_submit_request(connection->session, NULL,
nva, sizeof(nva)/sizeof(nva[0]), NULL, req);
if(rv != 0) {
diec("nghttp2_submit_request", rv);

View File

@ -402,7 +402,7 @@ static void submit_request(http2_session_data *session_data)
};
fprintf(stderr, "Request headers:\n");
print_headers(stderr, hdrs, ARRLEN(hdrs));
rv = nghttp2_submit_request(session_data->session, NGHTTP2_PRI_DEFAULT,
rv = nghttp2_submit_request(session_data->session, NULL,
hdrs, ARRLEN(hdrs), NULL, stream_data);
if(rv != 0) {
errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(rv));

View File

@ -39,7 +39,8 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \
nghttp2_helper.c \
nghttp2_npn.c nghttp2_gzip.c \
nghttp2_hd.c nghttp2_hd_huffman.c nghttp2_hd_huffman_data.c \
nghttp2_version.c
nghttp2_version.c \
nghttp2_priority_spec.c
HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
nghttp2_frame.h \
@ -48,7 +49,8 @@ HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
nghttp2_npn.h nghttp2_gzip.h \
nghttp2_submit.h nghttp2_outbound_item.h \
nghttp2_net.h \
nghttp2_hd.h nghttp2_hd_huffman.h
nghttp2_hd.h nghttp2_hd_huffman.h \
nghttp2_priority_spec.h
libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS)
libnghttp2_la_LDFLAGS = -no-undefined \

View File

@ -97,15 +97,16 @@ typedef struct {
/**
* @macro
*
* The default priority value
* The default weight of priority group.
*/
#define NGHTTP2_PRI_DEFAULT (1 << 30)
#define NGHTTP2_DEFAULT_WEIGHT 16
/**
* @macro
*
* The lowest priority value
* The maximum weight of priority group.
*/
#define NGHTTP2_PRI_LOWEST ((1U << 31) - 1)
#define NGHTTP2_MAX_WEIGHT 255
/**
* @macro
@ -278,6 +279,11 @@ typedef enum {
* The server push is disabled.
*/
NGHTTP2_ERR_PUSH_DISABLED = -528,
/**
* DATA frame for a given stream has been already submitted and has
* not been fully processed yet.
*/
NGHTTP2_ERR_DATA_EXIST = -529,
/**
* The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is
* under unexpected condition and cannot process any further data
@ -390,10 +396,6 @@ typedef enum {
* The END_HEADERS flag.
*/
NGHTTP2_FLAG_END_HEADERS = 0x4,
/**
* The PRIORITY flag.
*/
NGHTTP2_FLAG_PRIORITY = 0x8,
/**
* The ACK flag.
*/
@ -405,11 +407,19 @@ typedef enum {
/**
* The PAD_LOW flag.
*/
NGHTTP2_FLAG_PAD_LOW = 0x10,
NGHTTP2_FLAG_PAD_LOW = 0x08,
/**
* The PAD_HIGH flag.
*/
NGHTTP2_FLAG_PAD_HIGH = 0x20
NGHTTP2_FLAG_PAD_HIGH = 0x10,
/**
* The PRIORITY_GROUP flag.
*/
NGHTTP2_FLAG_PRIORITY_GROUP = 0x20,
/**
* The PRIORITY_DEPENDENCY flag.
*/
NGHTTP2_FLAG_PRIORITY_DEPENDENCY = 0x40
} nghttp2_flag;
/**
@ -634,6 +644,85 @@ typedef enum {
NGHTTP2_HCAT_HEADERS = 3
} nghttp2_headers_category;
/**
* @enum
*
* The type of priority specified in :type:`nghttp2_priority_spec`.
*/
typedef enum {
/**
* No priority is given.
*/
NGHTTP2_PRIORITY_TYPE_NONE,
/**
* Priority group ID and its weight are specified.
*/
NGHTTP2_PRIORITY_TYPE_GROUP,
/**
* The stream ID of a stream to depend on and its exclusive flag is
* specified.
*/
NGHTTP2_PRIORITY_TYPE_DEP
} nghttp2_priority_type;
/**
* @struct
*
* This structure stores priority group ID and its weight.
*/
typedef struct {
/**
* The priority group ID
*/
int32_t pri_group_id;
/**
* The weight of the priority group
*/
uint8_t weight;
} nghttp2_priority_group;
/**
* @struct
*
* This structure stores stream ID of the stream to depend on and its
* dependency is exclusive or not.
*/
typedef struct {
/**
* The stream ID of the stream to depend on.
*/
int32_t stream_id;
/**
* nonzero means exclusive dependency
*/
uint8_t exclusive;
} nghttp2_priority_dep;
/**
* @struct
*
* The structure to specify stream dependency. To specify stream
* dependency, specify |pri_type| and fill the |group| or |dep| member
* according to |pri_type|.
*/
typedef struct {
/**
* Type of priority specification. If |pri_type| is
* :enum:`NGHTTP2_PRIORITY_TYPE_GROUP`, fill |group|. If |pri_type|
* is :enum:`NGHTTP2_PRIORITY_TYPE_DEP`, fill |dep|. If |pri_type|
* is :enum:`NGHTTP2_PRIORITY_TYPE_NONE`, the other data members are
* ignored and it means that default priority group ID (which is
* same as the stream ID) and default weight
* :macro:`NGHTTP2_DEFAULT_WEIGHT` are specified.
*/
nghttp2_priority_type pri_type;
union {
nghttp2_priority_group group;
nghttp2_priority_dep dep;
};
} nghttp2_priority_spec;
/**
* @struct
* The HEADERS frame. It has the following members:
@ -648,6 +737,10 @@ typedef struct {
* and PAD_LOW.
*/
size_t padlen;
/**
* The priority specification
*/
nghttp2_priority_spec pri_spec;
/**
* The name/value pairs.
*/
@ -660,10 +753,6 @@ typedef struct {
* The category of this HEADERS frame.
*/
nghttp2_headers_category cat;
/**
* The priority.
*/
int32_t pri;
} nghttp2_headers;
/**
@ -676,9 +765,9 @@ typedef struct {
*/
nghttp2_frame_hd hd;
/**
* The priority.
* The priority specification.
*/
int32_t pri;
nghttp2_priority_spec pri_spec;
} nghttp2_priority;
/**
@ -1883,13 +1972,39 @@ ssize_t nghttp2_pack_settings_payload(uint8_t *buf,
*/
const char* nghttp2_strerror(int lib_error_code);
/**
* @function
*
* Initializes |pri_spec| with priority group ID |pri_group_id| and
* its weight |weight|. To specify weight for the default priority
* group (which is the same as the stream ID of the stream) in
* `nghttp2_submit_request()` and `nghttp2_submit_headers()` and its
* stream ID is not known in advance, specify -1 to |pri_group_id|.
*/
void nghttp2_priority_spec_group_init(nghttp2_priority_spec *pri_spec,
int32_t pri_group_id, uint8_t weight);
/**
* @function
*
* Initializes |pri_spec| with the |stream_id| of the stream to depend
* on and its exclusive flag. If |exclusive| is nonzero, exclusive
* flag is set.
*/
void nghttp2_priority_spec_dep_init(nghttp2_priority_spec *pri_spec,
int32_t stream_id, int exclusive);
/**
* @function
*
* Submits HEADERS frame and optionally one or more DATA frames.
*
* The |pri| is priority of this request. 0 is the highest priority
* value and :macro:`NGHTTP2_PRI_LOWEST` is the lowest value.
* The |pri_spec| is priority specification of this request. ``NULL``
* means the default priority (priority group ID becomes its stream ID
* and weight is :macro:`NGHTTP2_DEFAULT_WEIGHT). To specify the
* priority, use either `nghttp2_priority_spec_group_init()` or
* `nghttp2_priority_spec_dep_init()`. If |pri_spec| is not ``NULL``,
* this function will copy its data members.
*
* The |nva| is an array of name/value pair :type:`nghttp2_nv` with
* |nvlen| elements. The value is opaque sequence of bytes and
@ -1931,12 +2046,11 @@ const char* nghttp2_strerror(int lib_error_code);
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
* The |pri| is invalid
* :enum:`NGHTTP2_ERR_NOMEM`
* Out of memory.
*/
int nghttp2_submit_request(nghttp2_session *session, int32_t pri,
int nghttp2_submit_request(nghttp2_session *session,
const nghttp2_priority_spec *pri_spec,
const nghttp2_nv *nva, size_t nvlen,
const nghttp2_data_provider *data_prd,
void *stream_user_data);
@ -1990,7 +2104,6 @@ int nghttp2_submit_response(nghttp2_session *session,
* following values:
*
* * :enum:`NGHTTP2_FLAG_END_STREAM`
* * :enum:`NGHTTP2_FLAG_PRIORITY`
*
* If |flags| includes :enum:`NGHTTP2_FLAG_END_STREAM`, this frame has
* END_STREAM flag set.
@ -2004,7 +2117,12 @@ int nghttp2_submit_response(nghttp2_session *session,
* actual stream ID is assigned just before the frame is sent. For
* response, specify stream ID in |stream_id|.
*
* The |pri| is priority of this request.
* The |pri_spec| is priority specification of this request. ``NULL``
* means the default priority (priority group ID becomes its stream ID
* and weight is :macro:`NGHTTP2_DEFAULT_WEIGHT). To specify the
* priority, use either `nghttp2_priority_spec_group_init()` or
* `nghttp2_priority_spec_dep_init()`. If |pri_spec| is not ``NULL``,
* this function will copy its data members.
*
* The |nva| is an array of name/value pair :type:`nghttp2_nv` with
* |nvlen| elements. The value is opaque sequence of bytes and
@ -2028,13 +2146,12 @@ int nghttp2_submit_response(nghttp2_session *session,
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
* The |pri| is invalid
* :enum:`NGHTTP2_ERR_NOMEM`
* Out of memory.
*/
int nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
int32_t stream_id, int32_t pri,
int32_t stream_id,
const nghttp2_priority_spec *pri_spec,
const nghttp2_nv *nva, size_t nvlen,
void *stream_user_data);
@ -2064,21 +2181,28 @@ int nghttp2_submit_data(nghttp2_session *session, uint8_t flags,
* @function
*
* Submits PRIORITY frame to change the priority of stream |stream_id|
* to the priority value |pri|.
* to the priority specification |pri_spec|.
*
* The |flags| is currently ignored and should be
* :enum:`NGHTTP2_FLAG_NONE`.
*
* The |pri_spec| is priority specification of this request. ``NULL``
* is not allowed for this function. To specify the priority, use
* either `nghttp2_priority_spec_group_init()` or
* `nghttp2_priority_spec_dep_init()`. This function will copy its
* data members.
*
* 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_ARGUMENT`
* The |pri| is negative.
* The |pri_spec| is NULL; or trying to depend on itself.
*/
int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags,
int32_t stream_id, int32_t pri);
int32_t stream_id,
const nghttp2_priority_spec *pri_spec);
/**
* @function

View File

@ -31,6 +31,7 @@
#include "nghttp2_helper.h"
#include "nghttp2_net.h"
#include "nghttp2_priority_spec.h"
int nghttp2_frame_is_data_frame(uint8_t *head)
{
@ -64,7 +65,8 @@ static void nghttp2_frame_set_hd(nghttp2_frame_hd *hd, uint16_t length,
}
void nghttp2_frame_headers_init(nghttp2_headers *frame,
uint8_t flags, int32_t stream_id, int32_t pri,
uint8_t flags, int32_t stream_id,
const nghttp2_priority_spec *pri_spec,
nghttp2_nv *nva, size_t nvlen)
{
nghttp2_frame_set_hd(&frame->hd, 0, NGHTTP2_HEADERS, flags, stream_id);
@ -72,7 +74,7 @@ void nghttp2_frame_headers_init(nghttp2_headers *frame,
frame->nva = nva;
frame->nvlen = nvlen;
frame->cat = NGHTTP2_HCAT_REQUEST;
frame->pri = pri;
frame->pri_spec = *pri_spec;
}
void nghttp2_frame_headers_free(nghttp2_headers *frame)
@ -81,11 +83,25 @@ void nghttp2_frame_headers_free(nghttp2_headers *frame)
}
void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id,
int32_t pri)
const nghttp2_priority_spec *pri_spec)
{
nghttp2_frame_set_hd(&frame->hd, 4, NGHTTP2_PRIORITY, NGHTTP2_FLAG_NONE,
stream_id);
frame->pri = pri;
uint8_t flags;
switch(pri_spec->pri_type) {
case NGHTTP2_PRIORITY_TYPE_GROUP:
flags = NGHTTP2_FLAG_PRIORITY_GROUP;
break;
case NGHTTP2_PRIORITY_TYPE_DEP:
flags = NGHTTP2_FLAG_PRIORITY_DEPENDENCY;
break;
default:
assert(0);
}
nghttp2_frame_set_hd(&frame->hd, 4, NGHTTP2_PRIORITY, flags, stream_id);
frame->pri_spec = *pri_spec;
}
void nghttp2_frame_priority_free(nghttp2_priority *frame)
@ -212,13 +228,22 @@ void nghttp2_frame_private_data_init(nghttp2_private_data *frame,
void nghttp2_frame_private_data_free(nghttp2_private_data *frame)
{}
size_t nghttp2_frame_priority_len(uint8_t flags)
{
if(flags & NGHTTP2_FLAG_PRIORITY_GROUP) {
return 5;
}
if(flags & NGHTTP2_FLAG_PRIORITY_DEPENDENCY) {
return 4;
}
return 0;
}
size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame)
{
if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
return 4;
} else {
return 0;
}
return nghttp2_frame_priority_len(frame->hd.flags);
}
/*
@ -319,9 +344,7 @@ int nghttp2_frame_pack_headers(nghttp2_bufs *bufs,
return rv;
}
if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
nghttp2_put_uint32be(buf->pos, frame->pri);
}
nghttp2_frame_pack_priority_spec(buf->pos, &frame->pri_spec);
frame->padlen = 0;
frame->hd.length = nghttp2_bufs_len(bufs);
@ -329,17 +352,64 @@ int nghttp2_frame_pack_headers(nghttp2_bufs *bufs,
return frame_pack_headers_shared(bufs, &frame->hd);
}
void nghttp2_frame_pack_priority_spec(uint8_t *buf,
const nghttp2_priority_spec *pri_spec)
{
switch(pri_spec->pri_type) {
case NGHTTP2_PRIORITY_TYPE_GROUP:
nghttp2_put_uint32be(buf, pri_spec->group.pri_group_id);
buf[4] = pri_spec->group.weight;
return;
case NGHTTP2_PRIORITY_TYPE_DEP:
nghttp2_put_uint32be(buf, pri_spec->dep.stream_id);
if(pri_spec->dep.exclusive) {
buf[0] |= 0x80;
}
return;
default:
return;
}
}
void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec,
uint8_t flags,
const uint8_t *payload,
size_t payloadlen)
{
if(flags & NGHTTP2_FLAG_PRIORITY_GROUP) {
int32_t pri_group_id;
uint8_t weight;
pri_group_id = nghttp2_get_uint32(payload) & NGHTTP2_PRI_GROUP_ID_MASK;
weight = payload[4];
nghttp2_priority_spec_group_init(pri_spec, pri_group_id, weight);
} else if(flags & NGHTTP2_FLAG_PRIORITY_DEPENDENCY) {
int32_t dep_stream_id;
uint8_t exclusive;
dep_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK;
exclusive = (payload[0] & 0x80) > 0;
nghttp2_priority_spec_dep_init(pri_spec, dep_stream_id, exclusive);
} else {
pri_spec->pri_type = NGHTTP2_PRIORITY_TYPE_NONE;
}
}
int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame,
const uint8_t *payload,
size_t payloadlen)
{
if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
frame->pri = nghttp2_get_uint32(payload) & NGHTTP2_PRIORITY_MASK;
} else {
frame->pri = NGHTTP2_PRI_DEFAULT;
}
nghttp2_frame_unpack_priority_spec(&frame->pri_spec, frame->hd.flags,
payload, payloadlen);
frame->nva = NULL;
frame->nvlen = 0;
return 0;
}
@ -354,8 +424,9 @@ int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame)
nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
nghttp2_put_uint32be(buf->last, frame->pri);
buf->last += 4;
nghttp2_frame_pack_priority_spec(buf->last, &frame->pri_spec);
buf->last += nghttp2_frame_priority_len(frame->hd.flags);
return 0;
}
@ -364,7 +435,8 @@ void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame,
const uint8_t *payload,
size_t payloadlen)
{
frame->pri = nghttp2_get_uint32(payload) & NGHTTP2_PRIORITY_MASK;
nghttp2_frame_unpack_priority_spec(&frame->pri_spec, frame->hd.flags,
payload, payloadlen);
}
int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs,

View File

@ -35,6 +35,7 @@
#define NGHTTP2_FRAME_LENGTH_MASK ((1 << 14) - 1)
#define NGHTTP2_STREAM_ID_MASK ((1u << 31) - 1)
#define NGHTTP2_PRI_GROUP_ID_MASK ((1u << 31) - 1)
#define NGHTTP2_PRIORITY_MASK ((1u << 31) - 1)
#define NGHTTP2_WINDOW_SIZE_INCREMENT_MASK ((1u << 31) - 1)
#define NGHTTP2_SETTINGS_ID_MASK ((1 << 24) - 1)
@ -97,6 +98,31 @@ void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd);
void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t* buf);
/**
* Returns the number of priority field depending on the |flags|. If
* |flags| has neither NGHTTP2_FLAG_PRIORITY_GROUP nor
* NGHTTP2_FLAG_PRIORITY_DEPENDENCY set, return 0.
*/
size_t nghttp2_frame_priority_len(uint8_t flags);
/**
* Packs the |pri_spec| in |buf|. This function assumes |buf| has
* enough space for serialization.
*/
void nghttp2_frame_pack_priority_spec(uint8_t *buf,
const nghttp2_priority_spec *pri_spec);
/**
* Unpacks the priority specification from payload |payload| of length
* |payloadlen| to |pri_spec|. The |flags| is used to determine what
* kind of priority specification is in |payload|. This function
* assumes the |payload| contains whole priority specification.
*/
void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec,
uint8_t flags,
const uint8_t *payload,
size_t payloadlen);
/*
* Returns the offset from the HEADERS frame payload where the
* compressed header block starts. The frame payload does not include
@ -387,14 +413,15 @@ void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame,
* not assigned yet, it must be -1.
*/
void nghttp2_frame_headers_init(nghttp2_headers *frame,
uint8_t flags, int32_t stream_id, int32_t pri,
uint8_t flags, int32_t stream_id,
const nghttp2_priority_spec *pri_spec,
nghttp2_nv *nva, size_t nvlen);
void nghttp2_frame_headers_free(nghttp2_headers *frame);
void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id,
int32_t pri);
const nghttp2_priority_spec *pri_spec);
void nghttp2_frame_priority_free(nghttp2_priority *frame);

View File

@ -32,10 +32,12 @@
#include <nghttp2/nghttp2.h>
#include "nghttp2_frame.h"
/* Priority for PING */
#define NGHTTP2_OB_PRI_PING -10
/* Priority for SETTINGS */
#define NGHTTP2_OB_PRI_SETTINGS -9
/* A bit higher weight for non-DATA frames */
#define NGHTTP2_OB_EX_WEIGHT 256
/* Higher weight for SETTINGS */
#define NGHTTP2_OB_SETTINGS_WEIGHT 257
/* Highest weight for PING */
#define NGHTTP2_OB_PING_WEIGHT 258
typedef struct {
nghttp2_data_provider *data_prd;
@ -49,8 +51,11 @@ typedef struct {
/* Type of |frame|. NGHTTP2_CTRL: nghttp2_frame*, NGHTTP2_DATA:
nghttp2_private_data* */
nghttp2_frame_category frame_cat;
/* The priority used in priority comparion */
int32_t pri;
/* The priority used in priority comparion. Larger is served
ealier. */
int32_t weight;
/* nonzero if this object is queued. */
uint8_t queued;
} nghttp2_outbound_item;
/*

View File

@ -0,0 +1,41 @@
/*
* nghttp2 - HTTP/2.0 C Library
*
* Copyright (c) 2014 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "nghttp2_priority_spec.h"
void nghttp2_priority_spec_group_init(nghttp2_priority_spec *pri_spec,
int32_t pri_group_id, uint8_t weight)
{
pri_spec->pri_type = NGHTTP2_PRIORITY_TYPE_GROUP;
pri_spec->group.pri_group_id = pri_group_id;
pri_spec->group.weight = weight;
}
void nghttp2_priority_spec_dep_init(nghttp2_priority_spec *pri_spec,
int32_t stream_id, int exclusive)
{
pri_spec->pri_type = NGHTTP2_PRIORITY_TYPE_DEP;
pri_spec->dep.stream_id = stream_id;
pri_spec->dep.exclusive = exclusive != 0;
}

View File

@ -0,0 +1,34 @@
/*
* nghttp2 - HTTP/2.0 C Library
*
* Copyright (c) 2014 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef NGHTTP2_PRIORITY_SPEC_H
#define NGHTTP2_PRIORITY_SPEC_H
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
#endif /* NGHTTP2_PRIORITY_SPEC_H */

File diff suppressed because it is too large Load Diff

View File

@ -116,6 +116,7 @@ typedef enum {
struct nghttp2_session {
nghttp2_map /* <nghttp2_stream*> */ streams;
nghttp2_map /* <nghttp2_stream_group*> */ stream_groups;
/* Queue for outbound frames other than stream-creating HEADERS */
nghttp2_pq /* <nghttp2_outbound_item*> */ ob_pq;
/* Queue for outbound stream-creating HEADERS frame */
@ -307,9 +308,8 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
/*
* Creates new stream in |session| with stream ID |stream_id|,
* priority |pri| and flags |flags|. NGHTTP2_FLAG_END_STREAM flag is
* set in |flags|, the sender of HEADERS will not send any further
* data in this stream. Since this function is called when initial
* priority |pri_spec| and flags |flags|. The |flags| is bitwise OR
* of nghttp2_stream_flag. Since this function is called when initial
* HEADERS is sent or received, these flags are taken from it. The
* state of stream is set to |initial_state|. The |stream_user_data|
* is a pointer to the arbitrary user supplied data to be associated
@ -320,7 +320,8 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
*/
nghttp2_stream* nghttp2_session_open_stream(nghttp2_session *session,
int32_t stream_id,
uint8_t flags, int32_t pri,
uint8_t flags,
nghttp2_priority_spec *pri_spec,
nghttp2_stream_state initial_state,
void *stream_user_data);
@ -332,8 +333,12 @@ nghttp2_stream* nghttp2_session_open_stream(nghttp2_session *session,
* This function returns 0 if it succeeds, or one the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
* NGHTTP2_ERR_INVALID_ARGUMENT
* The specified stream does not exist.
* NGHTTP2_ERR_CALLBACK_FAILURE
* The callback function failed.
*/
int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id,
nghttp2_error_code error_code);
@ -506,6 +511,14 @@ int nghttp2_session_on_data_received(nghttp2_session *session,
nghttp2_stream* nghttp2_session_get_stream(nghttp2_session *session,
int32_t stream_id);
/*
* Returns nghttp2_stream_group* object whose priority group ID is
* |pri_group_id|. It could be NULL if such priority group does not
* exist.
*/
nghttp2_stream_group* nghttp2_session_get_stream_group
(nghttp2_session *session, int32_t pri_group_id);
/*
* Packs DATA frame |frame| in wire frame format and stores it in
* |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr|
@ -580,9 +593,32 @@ int nghttp2_session_update_local_settings(nghttp2_session *session,
size_t niv);
/*
* Re-prioritize |stream|. The new priority is |pri|.
* Re-prioritize |stream|. The new priority specification is
* |pri_spec|.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
void nghttp2_session_reprioritize_stream
(nghttp2_session *session, nghttp2_stream *stream, int32_t pri);
int nghttp2_session_reprioritize_stream
(nghttp2_session *session, nghttp2_stream *stream,
const nghttp2_priority_spec *pri_spec);
/*
* Creates new priority group using given values.
*
* This function returns created priority group if it succeeds, or
* NULL.
*/
nghttp2_stream_group* nghttp2_session_open_stream_group
(nghttp2_session *session, int32_t pri_group_id, int32_t weight);
/*
* Closes priority group if it does not include any streams.
*/
void nghttp2_session_close_stream_group_if_empty
(nghttp2_session *session, nghttp2_stream_group *stream_group);
#endif /* NGHTTP2_SESSION_H */

View File

@ -25,9 +25,10 @@
#include "nghttp2_stream.h"
#include <assert.h>
#include <stdio.h>
void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
uint8_t flags, int32_t pri,
uint8_t flags,
nghttp2_stream_state initial_state,
int32_t remote_initial_window_size,
int32_t local_initial_window_size,
@ -36,22 +37,33 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
nghttp2_map_entry_init(&stream->map_entry, stream_id);
stream->stream_id = stream_id;
stream->flags = flags;
stream->pri = pri;
stream->state = initial_state;
stream->shut_flags = NGHTTP2_SHUT_NONE;
stream->stream_user_data = stream_user_data;
stream->data = NULL;
stream->deferred_data = NULL;
stream->deferred_flags = NGHTTP2_DEFERRED_NONE;
stream->remote_window_size = remote_initial_window_size;
stream->local_window_size = local_initial_window_size;
stream->recv_window_size = 0;
stream->recv_reduction = 0;
stream->dep_prev = NULL;
stream->dep_next = NULL;
stream->sib_prev = NULL;
stream->sib_next = NULL;
stream->stream_group = NULL;
stream->dpri = NGHTTP2_STREAM_DPRI_NO_DATA;
stream->num_substreams = 1;
}
void nghttp2_stream_free(nghttp2_stream *stream)
{
nghttp2_outbound_item_free(stream->deferred_data);
free(stream->deferred_data);
/* We don't free stream->data. */
}
void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag)
@ -59,19 +71,216 @@ void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag)
stream->shut_flags |= flag;
}
static nghttp2_stream* stream_first_sib(nghttp2_stream *stream)
{
for(; stream->sib_prev; stream = stream->sib_prev);
return stream;
}
static nghttp2_stream* stream_last_sib(nghttp2_stream *stream)
{
for(; stream->sib_next; stream = stream->sib_next);
return stream;
}
static nghttp2_stream* stream_update_dep_length(nghttp2_stream *stream,
ssize_t delta)
{
stream->num_substreams += delta;
stream = stream_first_sib(stream);
if(stream->dep_prev) {
return stream_update_dep_length(stream->dep_prev, delta);
}
return stream;
}
static void stream_update_dep_set_rest_stream_group
(nghttp2_stream *stream, nghttp2_stream_group *stream_group)
{
if(stream == NULL) {
return;
}
nghttp2_stream_group_remove_stream(stream->stream_group, stream);
nghttp2_stream_group_add_stream(stream_group, stream);
if(stream->dpri == NGHTTP2_STREAM_DPRI_TOP) {
stream->dpri = NGHTTP2_STREAM_DPRI_REST;
}
stream_update_dep_set_rest_stream_group(stream->sib_next, stream_group);
stream_update_dep_set_rest_stream_group(stream->dep_next, stream_group);
}
static void stream_update_dep_set_rest(nghttp2_stream *stream)
{
if(stream == NULL) {
return;
}
if(stream->dpri == NGHTTP2_STREAM_DPRI_REST) {
return;
}
if(stream->dpri == NGHTTP2_STREAM_DPRI_TOP) {
stream->dpri = NGHTTP2_STREAM_DPRI_REST;
stream_update_dep_set_rest(stream->sib_next);
return;
}
stream_update_dep_set_rest(stream->sib_next);
stream_update_dep_set_rest(stream->dep_next);
}
/*
* Performs dfs starting |stream|, search stream which can become
* NGHTTP2_STREAM_DPRI_TOP and queues its data.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory.
*/
static int stream_update_dep_set_top(nghttp2_stream *stream, nghttp2_pq *pq)
{
int rv;
if(stream == NULL) {
return 0;
}
if(stream->dpri == NGHTTP2_STREAM_DPRI_TOP) {
return stream_update_dep_set_top(stream->sib_next, pq);
}
if(stream->dpri == NGHTTP2_STREAM_DPRI_REST) {
DEBUGF(fprintf(stderr, "stream: stream=%d data is top\n",
stream->stream_id));
if(!stream->data->queued) {
rv = nghttp2_pq_push(pq, stream->data);
if(rv != 0) {
return rv;
}
stream->data->queued = 1;
}
stream->dpri = NGHTTP2_STREAM_DPRI_TOP;
return stream_update_dep_set_top(stream->sib_next, pq);
}
assert(stream->dpri == NGHTTP2_STREAM_DPRI_NO_DATA);
rv = stream_update_dep_set_top(stream->sib_next, pq);
if(rv != 0) {
return rv;
}
return stream_update_dep_set_top(stream->dep_next, pq);
}
static int stream_update_dep_on_attach_data(nghttp2_stream *stream,
nghttp2_pq *pq)
{
int rv;
nghttp2_stream *root_stream;
stream->dpri = NGHTTP2_STREAM_DPRI_REST;
stream_update_dep_set_rest(stream->dep_next);
root_stream = nghttp2_stream_get_dep_root(stream);
DEBUGF(fprintf(stderr, "root=%p, stream=%p\n", root_stream, stream));
rv = stream_update_dep_set_top(root_stream, pq);
if(rv != 0) {
return rv;
}
return 0;
}
static int stream_update_dep_on_detach_data(nghttp2_stream *stream,
nghttp2_pq *pq)
{
if(stream->dpri != NGHTTP2_STREAM_DPRI_TOP) {
stream->dpri = NGHTTP2_STREAM_DPRI_NO_DATA;
return 0;
}
stream->dpri = NGHTTP2_STREAM_DPRI_NO_DATA;
return stream_update_dep_set_top(stream->dep_next, pq);
}
int nghttp2_stream_attach_data(nghttp2_stream *stream,
nghttp2_outbound_item *data,
nghttp2_pq *pq)
{
assert(stream->data == NULL);
assert(stream->deferred_data == NULL);
stream->data = data;
DEBUGF(fprintf(stderr, "stream: stream=%d attach data=%p\n",
stream->stream_id, data));
return stream_update_dep_on_attach_data(stream, pq);
}
int nghttp2_stream_detach_data(nghttp2_stream *stream, nghttp2_pq *pq)
{
DEBUGF(fprintf(stderr, "stream: stream=%d detach data=%p\n",
stream->stream_id, stream->data));
stream->data = NULL;
return stream_update_dep_on_detach_data(stream, pq);
}
void nghttp2_stream_defer_data(nghttp2_stream *stream,
nghttp2_outbound_item *data,
uint8_t flags)
{
assert(stream->data);
assert(stream->data == data);
assert(stream->deferred_data == NULL);
stream->deferred_data = data;
stream->deferred_flags = flags;
stream->data = NULL;
}
void nghttp2_stream_detach_deferred_data(nghttp2_stream *stream)
int nghttp2_stream_detach_deferred_data(nghttp2_stream *stream,
nghttp2_pq *pq)
{
nghttp2_outbound_item *data;
assert(stream->data == NULL);
assert(stream->deferred_data);
data = stream->deferred_data;
stream->deferred_data = NULL;
stream->deferred_flags = NGHTTP2_DEFERRED_NONE;
return nghttp2_stream_attach_data(stream, data, pq);
}
static int update_initial_window_size
@ -113,3 +322,344 @@ void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream)
{
stream->state = NGHTTP2_STREAM_OPENED;
}
nghttp2_stream* nghttp2_stream_get_dep_root(nghttp2_stream *stream)
{
for(;;) {
if(stream->sib_prev) {
stream = stream->sib_prev;
continue;
}
if(stream->dep_prev) {
stream = stream->dep_prev;
continue;
}
break;
}
return stream;
}
int nghttp2_stream_dep_subtree_find(nghttp2_stream *stream,
nghttp2_stream *target)
{
if(stream == NULL) {
return 0;
}
if(stream == target) {
return 1;
}
if(nghttp2_stream_dep_subtree_find(stream->sib_next, target)) {
return 1;
}
return nghttp2_stream_dep_subtree_find(stream->dep_next, target);
}
void nghttp2_stream_dep_insert(nghttp2_stream *dep_stream,
nghttp2_stream *stream)
{
nghttp2_stream *si;
assert(stream->data == NULL);
DEBUGF(fprintf(stderr,
"stream: dep_insert dep_stream(%p)=%d, stream(%p)=%d\n",
dep_stream, dep_stream->stream_id,
stream, stream->stream_id));
if(dep_stream->dep_next) {
for(si = dep_stream->dep_next; si; si = si->sib_next) {
stream->num_substreams += si->num_substreams;
}
stream->dep_next = dep_stream->dep_next;
stream->dep_next->dep_prev = stream;
}
dep_stream->dep_next = stream;
stream->dep_prev = dep_stream;
stream_update_dep_length(dep_stream, 1);
}
void nghttp2_stream_dep_add(nghttp2_stream *dep_stream,
nghttp2_stream *stream)
{
nghttp2_stream *last_sib;
assert(stream->data == NULL);
DEBUGF(fprintf(stderr,
"stream: dep_add dep_stream(%p)=%d, stream(%p)=%d\n",
dep_stream, dep_stream->stream_id,
stream, stream->stream_id));
stream_update_dep_length(dep_stream, 1);
if(dep_stream->dep_next == NULL) {
dep_stream->dep_next = stream;
stream->dep_prev = dep_stream;
return;
}
last_sib = stream_last_sib(dep_stream->dep_next);
last_sib->sib_next = stream;
stream->sib_prev = last_sib;
}
void nghttp2_stream_dep_remove(nghttp2_stream *stream)
{
nghttp2_stream *prev, *next, *dep_next;
DEBUGF(fprintf(stderr, "stream: dep_remove stream(%p)=%d\n",
stream, stream->stream_id));
prev = stream_first_sib(stream);
if(prev->dep_prev) {
stream_update_dep_length(prev->dep_prev, -1);
}
if(stream->sib_prev) {
prev = stream->sib_prev;
dep_next = stream->dep_next;
if(dep_next) {
dep_next->dep_prev = NULL;
prev->sib_next = dep_next;
dep_next->sib_prev = prev;
} else {
next = stream->sib_next;
prev->sib_next = next;
if(next) {
next->sib_prev = prev;
}
}
} else if(stream->dep_prev) {
prev = stream->dep_prev;
dep_next = stream->dep_next;
if(dep_next) {
prev->dep_next = dep_next;
dep_next->dep_prev = prev;
} else if(stream->sib_next) {
next = stream->sib_next;
prev->dep_next = next;
next->dep_prev = prev;
next->sib_prev = NULL;
} else {
prev->dep_next = NULL;
dep_next = NULL;
}
} else {
nghttp2_stream *si;
dep_next = NULL;
/* stream is a root of tree. Removing stream makes its
descendants a root of its own subtree. */
for(si = stream->dep_next; si;) {
next = si->sib_next;
si->dep_prev = NULL;
si->sib_prev = NULL;
si->sib_next = NULL;
si = next;
}
}
if(dep_next && stream->sib_next) {
prev = stream_last_sib(dep_next);
next = stream->sib_next;
prev->sib_next = next;
next->sib_prev = prev;
}
stream->num_substreams = 1;
stream->dep_prev = NULL;
stream->dep_next = NULL;
stream->sib_prev = NULL;
stream->sib_next = NULL;
}
int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream,
nghttp2_stream *stream,
nghttp2_pq *pq)
{
nghttp2_stream *last_sib;
nghttp2_stream *dep_next;
nghttp2_stream *root_stream;
nghttp2_stream *si;
size_t delta_substreams;
DEBUGF(fprintf(stderr, "stream: dep_insert_subtree dep_stream(%p)=%d "
"stream(%p)=%d\n",
dep_stream, dep_stream->stream_id,
stream, stream->stream_id));
delta_substreams = stream->num_substreams;
stream_update_dep_set_rest_stream_group(stream, dep_stream->stream_group);
if(dep_stream->dep_next) {
dep_next = dep_stream->dep_next;
for(si = dep_stream->dep_next; si; si = si->sib_next) {
stream->num_substreams += si->num_substreams;
}
stream_update_dep_set_rest(dep_next);
dep_stream->dep_next = stream;
stream->dep_prev = dep_stream;
if(stream->dep_next) {
last_sib = stream_last_sib(stream->dep_next);
last_sib->sib_next = dep_next;
dep_next->sib_prev = last_sib;
dep_next->dep_prev = NULL;
} else {
stream->dep_next = dep_next;
dep_next->dep_prev = stream;
}
} else {
dep_stream->dep_next = stream;
stream->dep_prev = dep_stream;
}
root_stream = stream_update_dep_length(dep_stream, delta_substreams);
return stream_update_dep_set_top(root_stream, pq);
}
int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream,
nghttp2_stream *stream,
nghttp2_pq *pq)
{
nghttp2_stream *last_sib;
nghttp2_stream *root_stream;
DEBUGF(fprintf(stderr, "stream: dep_add_subtree dep_stream(%p)=%d "
"stream(%p)=%d\n",
dep_stream, dep_stream->stream_id,
stream, stream->stream_id));
stream_update_dep_set_rest_stream_group(stream, dep_stream->stream_group);
if(dep_stream->dep_next) {
last_sib = stream_last_sib(dep_stream->dep_next);
last_sib->sib_next = stream;
stream->sib_prev = last_sib;
} else {
dep_stream->dep_next = stream;
stream->dep_prev = dep_stream;
}
root_stream = stream_update_dep_length(dep_stream, stream->num_substreams);
return stream_update_dep_set_top(root_stream, pq);
}
void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream)
{
nghttp2_stream *prev, *next;
if(stream->sib_prev) {
prev = stream->sib_prev;
prev->sib_next = stream->sib_next;
if(prev->sib_next) {
prev->sib_next->sib_prev = prev;
}
prev = stream_first_sib(prev);
if(prev->dep_prev) {
stream_update_dep_length(prev->dep_prev, -stream->num_substreams);
}
} else if(stream->dep_prev) {
prev = stream->dep_prev;
next = stream->sib_next;
prev->dep_next = next;
if(next) {
next->dep_prev = prev;
next->sib_prev = NULL;
}
stream_update_dep_length(prev, -stream->num_substreams);
}
stream->sib_prev = NULL;
stream->sib_next = NULL;
stream->dep_prev = NULL;
}
int nghttp2_stream_dep_make_root(nghttp2_stream_group *stream_group,
nghttp2_stream *stream,
nghttp2_pq *pq)
{
stream_update_dep_set_rest_stream_group(stream, stream_group);
return stream_update_dep_set_top(stream, pq);
}
void nghttp2_stream_group_init(nghttp2_stream_group *stream_group,
int32_t pri_group_id,
int32_t weight)
{
nghttp2_map_entry_init(&stream_group->map_entry, pri_group_id);
stream_group->num_streams = 0;
stream_group->pri_group_id = pri_group_id;
stream_group->weight = weight;
}
void nghttp2_stream_group_free(nghttp2_stream_group *stream_group)
{}
void nghttp2_stream_group_add_stream(nghttp2_stream_group *stream_group,
nghttp2_stream *stream)
{
DEBUGF(fprintf(stderr, "stream_group: stream_group(%p)=%d "
"add stream(%p)=%d\n",
stream_group, stream_group->pri_group_id,
stream, stream->stream_id));
stream->stream_group = stream_group;
++stream_group->num_streams;
}
void nghttp2_stream_group_remove_stream(nghttp2_stream_group *stream_group,
nghttp2_stream *stream)
{
DEBUGF(fprintf(stderr, "stream_group: stream_group(%p)=%d "
"remove stream(%p)=%d\n",
stream_group, stream_group->pri_group_id,
stream, stream->stream_id));
stream->stream_group = NULL;
--stream_group->num_streams;
}

View File

@ -32,6 +32,8 @@
#include <nghttp2/nghttp2.h>
#include "nghttp2_outbound_item.h"
#include "nghttp2_map.h"
#include "nghttp2_pq.h"
#include "nghttp2_int.h"
/*
* If local peer is stream initiator:
@ -78,23 +80,54 @@ typedef enum {
NGHTTP2_STREAM_FLAG_PUSH = 0x01
} nghttp2_stream_flag;
typedef enum {
NGHTTP2_STREAM_DPRI_NONE = 0,
NGHTTP2_STREAM_DPRI_NO_DATA = 0x01,
NGHTTP2_STREAM_DPRI_TOP = 0x02,
NGHTTP2_STREAM_DPRI_REST = 0x04
} nghttp2_stream_dpri;
typedef enum {
NGHTTP2_DEFERRED_NONE = 0,
/* Indicates the DATA is deferred due to flow control. */
NGHTTP2_DEFERRED_FLOW_CONTROL = 0x01
} nghttp2_deferred_flag;
typedef struct {
struct nghttp2_stream_group;
typedef struct nghttp2_stream_group nghttp2_stream_group;
struct nghttp2_stream;
typedef struct nghttp2_stream nghttp2_stream;
struct nghttp2_stream {
/* Intrusive Map */
nghttp2_map_entry map_entry;
/* pointers to form dependency tree. If multiple streams depend on
a stream, only one stream (left most) has non-NULL dep_prev which
points to the stream it depends on. The remaining streams are
linked using sib_prev and sib_next. The stream which has
non-NULL dep_prev always NULL sib_prev. The right most stream
has NULL sib_next. If this stream is a root of dependency tree,
dep_prev and sib_prev are NULL. */
nghttp2_stream *dep_prev, *dep_next;
nghttp2_stream *sib_prev, *sib_next;
/* The arbitrary data provided by user for this stream. */
void *stream_user_data;
/* Active DATA frame */
nghttp2_outbound_item *data;
/* Deferred DATA frame */
nghttp2_outbound_item *deferred_data;
/* stream ID */
int32_t stream_id;
/* Use same value in request HEADERS frame */
int32_t pri;
/* priority group this stream belongs to */
nghttp2_stream_group *stream_group;
/* categorized priority of this stream. Only stream bearing
NGHTTP2_STREAM_DPRI_TOP can send DATA frame. */
nghttp2_stream_dpri dpri;
/* the number of nodes in subtree */
size_t num_substreams;
/* Current remote window size. This value is computed against the
current initial window size of remote endpoint. */
int32_t remote_window_size;
@ -117,10 +150,10 @@ typedef struct {
/* The flags for defered DATA. Bitwise OR of zero or more
nghttp2_deferred_flag values */
uint8_t deferred_flags;
} nghttp2_stream;
};
void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
uint8_t flags, int32_t pri,
uint8_t flags,
nghttp2_stream_state initial_state,
int32_t remote_initial_window_size,
int32_t local_initial_window_size,
@ -147,7 +180,8 @@ void nghttp2_stream_defer_data(nghttp2_stream *stream,
* Detaches deferred data from this stream. This function does not
* free deferred data.
*/
void nghttp2_stream_detach_deferred_data(nghttp2_stream *stream);
int nghttp2_stream_detach_deferred_data(nghttp2_stream *stream,
nghttp2_pq *pq);
/*
* Updates the remote window size with the new value
@ -182,4 +216,154 @@ int nghttp2_stream_update_local_initial_window_size
*/
void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream);
/*
* Returns the stream positioned in root of the dependency tree the
* |stream| belongs to.
*/
nghttp2_stream* nghttp2_stream_get_dep_root(nghttp2_stream *stream);
/*
* Returns nonzero if |target| is found in subtree of |stream|.
*/
int nghttp2_stream_dep_subtree_find(nghttp2_stream *stream,
nghttp2_stream *target);
/*
* Makes the |stream| depend on the |dep_stream|. This dependency is
* exclusive. All existing direct descendants of |dep_stream| become
* the descendants of the |stream|. This function assumes
* |stream->data| is NULL and no dpri members are changed in this
* dependency tree. It also does not change stream->stream_group.
*/
void nghttp2_stream_dep_insert(nghttp2_stream *dep_stream,
nghttp2_stream *stream);
/*
* Makes the |stream| depend on the |dep_stream|. This dependency is
* not exclusive. This function assumes |stream->data| is NULL and no
* dpri members are changed in this dependency tree. It also does not
* change stream->stream_group.
*/
void nghttp2_stream_dep_add(nghttp2_stream *dep_stream,
nghttp2_stream *stream);
/*
* Removes the |stream| from the current dependency tree. This
* function assumes |stream->data| is NULL.
*/
void nghttp2_stream_dep_remove(nghttp2_stream *stream);
/*
* Attaches |data| to |stream|. Updates dpri members in this
* dependency tree.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
int nghttp2_stream_attach_data(nghttp2_stream *stream,
nghttp2_outbound_item *data,
nghttp2_pq *pq);
/*
* Detaches |data| from |stream|. Updates dpri members in this
* dependency tree.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
int nghttp2_stream_detach_data(nghttp2_stream *stream, nghttp2_pq *pq);
/*
* Makes the |stream| depend on the |dep_stream|. This dependency is
* exclusive. Updates dpri members in this dependency tree.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream,
nghttp2_stream *stream,
nghttp2_pq *pq);
/*
* Makes the |stream| depend on the |dep_stream|. This dependency is
* not exclusive. Updates dpri members in this dependency tree.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream,
nghttp2_stream *stream,
nghttp2_pq *pq);
/*
* Removes subtree whose root stream is |stream|. Removing subtree
* does not change dpri values.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream);
/*
* Makes the |stream| as root for |stream_group|. Updates dpri
* members in this dependency tree.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
int nghttp2_stream_dep_make_root(nghttp2_stream_group *stream_group,
nghttp2_stream *stream,
nghttp2_pq *pq);
/*
* Priority group of streams.
*/
struct nghttp2_stream_group {
/* Intrusive Map */
nghttp2_map_entry map_entry;
/* The number of streams this priority group contains */
size_t num_streams;
/* The priority group ID */
int32_t pri_group_id;
/* The weight of this group */
int32_t weight;
};
void nghttp2_stream_group_init(nghttp2_stream_group *stream_group,
int32_t pri_group_id,
int32_t weight);
void nghttp2_stream_group_free(nghttp2_stream_group *stream_group);
/*
* Adds |stream| to |stream_group|.
*/
void nghttp2_stream_group_add_stream(nghttp2_stream_group *stream_group,
nghttp2_stream *stream);
/*
* Removes |stream| from |stream_group|.
*/
void nghttp2_stream_group_remove_stream(nghttp2_stream_group *stream_group,
nghttp2_stream *stream);
#endif /* NGHTTP2_STREAM */

View File

@ -30,6 +30,7 @@
#include "nghttp2_session.h"
#include "nghttp2_frame.h"
#include "nghttp2_helper.h"
#include "nghttp2_priority_spec.h"
/* This function takes ownership of |nva_copy|. Regardless of the
return value, the caller must not free |nva_copy| after this
@ -38,7 +39,7 @@ static int nghttp2_submit_headers_shared
(nghttp2_session *session,
uint8_t flags,
int32_t stream_id,
int32_t pri,
const nghttp2_priority_spec *pri_spec,
nghttp2_nv *nva_copy,
size_t nvlen,
const nghttp2_data_provider *data_prd,
@ -49,10 +50,7 @@ static int nghttp2_submit_headers_shared
nghttp2_frame *frame = NULL;
nghttp2_data_provider *data_prd_copy = NULL;
nghttp2_headers_aux_data *aux_data = NULL;
if(pri < 0) {
rv = NGHTTP2_ERR_INVALID_ARGUMENT;
goto fail;
}
if(data_prd != NULL && data_prd->read_callback != NULL) {
data_prd_copy = malloc(sizeof(nghttp2_data_provider));
if(data_prd_copy == NULL) {
@ -75,15 +73,20 @@ static int nghttp2_submit_headers_shared
rv = NGHTTP2_ERR_NOMEM;
goto fail;
}
flags_copy =
(flags & (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PRIORITY |
NGHTTP2_FLAG_END_SEGMENT)) |
(flags & (NGHTTP2_FLAG_END_STREAM |
NGHTTP2_FLAG_END_SEGMENT |
NGHTTP2_FLAG_PRIORITY_GROUP |
NGHTTP2_FLAG_PRIORITY_DEPENDENCY)) |
NGHTTP2_FLAG_END_HEADERS;
nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id, pri,
nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id, pri_spec,
nva_copy, nvlen);
rv = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame,
aux_data);
if(rv != 0) {
nghttp2_frame_headers_free(&frame->headers);
goto fail2;
@ -103,7 +106,7 @@ static int nghttp2_submit_headers_shared_nva
(nghttp2_session *session,
uint8_t flags,
int32_t stream_id,
int32_t pri,
const nghttp2_priority_spec *pri_spec,
const nghttp2_nv *nva,
size_t nvlen,
const nghttp2_data_provider *data_prd,
@ -111,21 +114,52 @@ static int nghttp2_submit_headers_shared_nva
{
ssize_t rv;
nghttp2_nv *nva_copy;
nghttp2_priority_spec pri_spec_none = {
NGHTTP2_PRIORITY_TYPE_NONE
};
const nghttp2_priority_spec *pri_spec_ptr;
rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen);
if(rv < 0) {
return rv;
}
if(pri_spec == NULL) {
pri_spec_ptr = &pri_spec_none;
} else {
pri_spec_ptr = pri_spec;
}
return nghttp2_submit_headers_shared(session, flags, stream_id,
pri, nva_copy, rv, data_prd,
pri_spec_ptr, nva_copy, rv, data_prd,
stream_user_data);
}
int nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
int32_t stream_id, int32_t pri,
int32_t stream_id,
const nghttp2_priority_spec *pri_spec,
const nghttp2_nv *nva, size_t nvlen,
void *stream_user_data)
{
return nghttp2_submit_headers_shared_nva(session, flags, stream_id, pri,
flags &= NGHTTP2_FLAG_END_STREAM;
if(pri_spec) {
switch(pri_spec->pri_type) {
case NGHTTP2_PRIORITY_TYPE_GROUP:
flags |= NGHTTP2_FLAG_PRIORITY_GROUP;
break;
case NGHTTP2_PRIORITY_TYPE_DEP:
flags |= NGHTTP2_FLAG_PRIORITY_DEPENDENCY;
break;
default:
/* Default weight */
pri_spec = NULL;
break;
}
}
return nghttp2_submit_headers_shared_nva(session, flags, stream_id, pri_spec,
nva, nvlen, NULL, stream_user_data);
}
@ -137,24 +171,46 @@ int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags,
}
int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags,
int32_t stream_id, int32_t pri)
int32_t stream_id,
const nghttp2_priority_spec *pri_spec)
{
int rv;
nghttp2_frame *frame;
if(pri < 0) {
if(pri_spec == NULL) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
switch(pri_spec->pri_type) {
case NGHTTP2_PRIORITY_TYPE_GROUP:
break;
case NGHTTP2_PRIORITY_TYPE_DEP:
if(stream_id == pri_spec->dep.stream_id) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
break;
default:
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
frame = malloc(sizeof(nghttp2_frame));
if(frame == NULL) {
return NGHTTP2_ERR_NOMEM;
}
nghttp2_frame_priority_init(&frame->priority, stream_id, pri);
nghttp2_frame_priority_init(&frame->priority, stream_id, pri_spec);
rv = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, NULL);
if(rv != 0) {
nghttp2_frame_priority_free(&frame->priority);
free(frame);
return rv;
}
return 0;
}
@ -260,26 +316,43 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
return 0;
}
static uint8_t set_request_flags(int32_t pri,
static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec,
const nghttp2_data_provider *data_prd)
{
uint8_t flags = NGHTTP2_FLAG_NONE;
if(data_prd == NULL || data_prd->read_callback == NULL) {
flags |= NGHTTP2_FLAG_END_STREAM;
}
if(pri != NGHTTP2_PRI_DEFAULT) {
flags |= NGHTTP2_FLAG_PRIORITY;
if(pri_spec) {
switch(pri_spec->pri_type) {
case NGHTTP2_PRIORITY_TYPE_GROUP:
flags |= NGHTTP2_FLAG_PRIORITY_GROUP;
break;
case NGHTTP2_PRIORITY_TYPE_DEP:
flags |= NGHTTP2_FLAG_PRIORITY_DEPENDENCY;
break;
default:
/* Default weight */
break;
}
}
return flags;
}
int nghttp2_submit_request(nghttp2_session *session, int32_t pri,
int nghttp2_submit_request(nghttp2_session *session,
const nghttp2_priority_spec *pri_spec,
const nghttp2_nv *nva, size_t nvlen,
const nghttp2_data_provider *data_prd,
void *stream_user_data)
{
uint8_t flags = set_request_flags(pri, data_prd);
return nghttp2_submit_headers_shared_nva(session, flags, -1, pri, nva, nvlen,
uint8_t flags;
flags = set_request_flags(pri_spec, data_prd);
return nghttp2_submit_headers_shared_nva(session, flags, -1, pri_spec,
nva, nvlen,
data_prd, stream_user_data);
}
@ -299,7 +372,7 @@ int nghttp2_submit_response(nghttp2_session *session,
{
uint8_t flags = set_response_flags(data_prd);
return nghttp2_submit_headers_shared_nva(session, flags, stream_id,
NGHTTP2_PRI_DEFAULT, nva, nvlen,
NULL, nva, nvlen,
data_prd, NULL);
}

View File

@ -103,7 +103,7 @@ void start_element_func
if(!src_attr) {
return;
}
add_link(parser_data, src_attr, REQ_PRI_MEDIUM);
add_link(parser_data, src_attr, REQ_PRI_LOW);
}
}
} // namespace

View File

@ -247,12 +247,6 @@ void print_flags(const nghttp2_frame_hd& hd)
}
s += "END_HEADERS";
}
if(hd.flags & NGHTTP2_FLAG_PRIORITY) {
if(!s.empty()) {
s += " | ";
}
s += "PRIORITY";
}
if(hd.flags & NGHTTP2_FLAG_PAD_LOW) {
if(!s.empty()) {
s += " | ";
@ -265,6 +259,27 @@ void print_flags(const nghttp2_frame_hd& hd)
}
s += "PAD_HIGH";
}
if(hd.flags & NGHTTP2_FLAG_PRIORITY_GROUP) {
if(!s.empty()) {
s += " | ";
}
s += "PRIORITY_GROUP";
}
if(hd.flags & NGHTTP2_FLAG_PRIORITY_DEPENDENCY) {
if(!s.empty()) {
s += " | ";
}
s += "PRIORITY_DEPENDENCY";
}
break;
case NGHTTP2_PRIORITY:
if(hd.flags & NGHTTP2_FLAG_PRIORITY_GROUP) {
s += "PRIORITY_GROUP";
} else if(hd.flags & NGHTTP2_FLAG_PRIORITY_DEPENDENCY) {
s += "PRIORITY_DEPENDENCY";
}
break;
case NGHTTP2_SETTINGS:
if(hd.flags & NGHTTP2_FLAG_ACK) {
@ -332,8 +347,14 @@ void print_frame(print_type ptype, const nghttp2_frame *frame)
case NGHTTP2_HEADERS:
print_frame_attr_indent();
fprintf(outfile, "(");
if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
fprintf(outfile, "pri=%d, ", frame->headers.pri);
if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY_GROUP) {
fprintf(outfile, "pri_group_id=%d, weight=%u, ",
frame->headers.pri_spec.group.pri_group_id,
frame->headers.pri_spec.group.weight);
} else if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY_DEPENDENCY) {
fprintf(outfile, "stream_id=%d, exclusive=%d, ",
frame->headers.pri_spec.dep.stream_id,
frame->headers.pri_spec.dep.exclusive);
}
fprintf(outfile, "padlen=%zu)\n", frame->headers.padlen);
switch(frame->headers.cat) {
@ -356,7 +377,21 @@ void print_frame(print_type ptype, const nghttp2_frame *frame)
break;
case NGHTTP2_PRIORITY:
print_frame_attr_indent();
fprintf(outfile, "(pri=%d)\n", frame->priority.pri);
fprintf(outfile, "(");
if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY_GROUP) {
fprintf(outfile, "pri_group_id=%d, weight=%u",
frame->priority.pri_spec.group.pri_group_id,
frame->priority.pri_spec.group.weight);
} else if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY_DEPENDENCY) {
fprintf(outfile, "stream_id=%d, exclusive=%d",
frame->priority.pri_spec.dep.stream_id,
frame->priority.pri_spec.dep.exclusive);
}
fprintf(outfile, ")\n");
break;
case NGHTTP2_RST_STREAM:
print_frame_attr_indent();

View File

@ -148,7 +148,7 @@ void Http2Session::submit_request()
client_->reqidx = 0;
}
nghttp2_submit_request(session_, 0, nva.data(), nva.size(),
nghttp2_submit_request(session_, nullptr, nva.data(), nva.size(),
nullptr, nullptr);
}

View File

@ -85,7 +85,7 @@ struct Config {
size_t padding;
ssize_t peer_max_concurrent_streams;
ssize_t header_table_size;
int32_t pri;
int32_t weight;
int multiply;
// milliseconds
int timeout;
@ -103,7 +103,7 @@ struct Config {
padding(0),
peer_max_concurrent_streams(NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS),
header_table_size(-1),
pri(NGHTTP2_PRI_DEFAULT),
weight(NGHTTP2_DEFAULT_WEIGHT),
multiply(1),
timeout(-1),
window_bits(-1),
@ -146,6 +146,16 @@ std::string strip_fragment(const char *raw_uri)
}
} // namespace
namespace {
struct Request;
} // namespace
namespace {
struct Dependency {
std::vector<std::vector<Request*>> deps;
};
} // namespace
namespace {
struct Request {
Headers res_nva;
@ -154,28 +164,37 @@ struct Request {
std::string uri;
std::string status;
http_parser_url u;
std::shared_ptr<Dependency> dep;
nghttp2_priority_spec pri_spec;
RequestStat stat;
int64_t data_length;
int64_t data_offset;
nghttp2_gzip *inflater;
HtmlParser *html_parser;
const nghttp2_data_provider *data_prd;
int32_t pri;
int32_t stream_id;
// Recursion level: 0: first entity, 1: entity linked from first entity
int level;
// RequestPriority value defined in HtmlParser.h
int pri;
// For pushed request, |uri| is empty and |u| is zero-cleared.
Request(const std::string& uri, const http_parser_url &u,
const nghttp2_data_provider *data_prd, int64_t data_length,
int32_t pri, int level = 0)
const nghttp2_priority_spec& pri_spec,
std::shared_ptr<Dependency> dep, int level = 0)
: uri(uri),
u(u),
dep(std::move(dep)),
pri_spec(pri_spec),
data_length(data_length),
data_offset(0),
inflater(nullptr),
html_parser(nullptr),
data_prd(data_prd),
pri(pri),
level(level)
stream_id(-1),
level(level),
pri(0)
{}
~Request()
@ -219,6 +238,55 @@ struct Request {
return path;
}
int32_t find_dep_stream_id(int start)
{
for(auto i = start; i >= 0; --i) {
for(auto req : dep->deps[i]) {
if(req->stat.stage != STAT_ON_COMPLETE) {
return req->stream_id;
}
}
}
return -1;
}
nghttp2_priority_spec resolve_dep(int32_t pri)
{
nghttp2_priority_spec pri_spec = { NGHTTP2_PRIORITY_TYPE_NONE };
int exclusive = 0;
int32_t stream_id = -1;
if(pri == 0) {
return pri_spec;
}
auto start = std::min(pri, (int)dep->deps.size() - 1);
for(auto i = start; i >= 0; --i) {
if(dep->deps[i][0]->pri < pri) {
stream_id = find_dep_stream_id(i);
if(i != (int)dep->deps.size() - 1) {
exclusive = 1;
}
break;
} else if(dep->deps[i][0]->pri == pri) {
stream_id = find_dep_stream_id(i - 1);
break;
}
}
if(stream_id == -1) {
return pri_spec;
}
nghttp2_priority_spec_dep_init(&pri_spec, stream_id, exclusive);
return pri_spec;
}
bool is_ipv6_literal_addr() const
{
if(util::has_uri_field(u, UF_HOST)) {
@ -773,7 +841,8 @@ struct HttpClient {
bool add_request(const std::string& uri,
const nghttp2_data_provider *data_prd,
int64_t data_length,
int32_t pri,
const nghttp2_priority_spec& pri_spec,
std::shared_ptr<Dependency> dep,
int level = 0)
{
http_parser_url u;
@ -787,8 +856,11 @@ struct HttpClient {
if(config.multiply == 1) {
path_cache.insert(uri);
}
reqvec.push_back(util::make_unique<Request>(uri, u, data_prd,
data_length, pri, level));
data_length,
pri_spec, std::move(dep),
level));
return true;
}
}
@ -892,8 +964,9 @@ int submit_request
for(auto& kv : build_headers) {
nva.push_back(http2::make_nv(kv.first, kv.second));
}
int rv = nghttp2_submit_request(client->session, req->pri,
nva.data(), nva.size(), req->data_prd, req);
auto rv = nghttp2_submit_request(client->session, &req->pri_spec,
nva.data(), nva.size(), req->data_prd, req);
if(rv != 0) {
std::cerr << "nghttp2_submit_request() returned error: "
<< nghttp2_strerror(rv) << std::endl;
@ -903,17 +976,6 @@ int submit_request
}
} // namespace
namespace {
int32_t adjust_pri(int32_t base_pri, int32_t rel_pri)
{
if((int32_t)NGHTTP2_PRI_LOWEST - rel_pri < base_pri) {
return NGHTTP2_PRI_LOWEST;
} else {
return base_pri + rel_pri;
}
}
} // namespace
namespace {
void update_html_parser(HttpClient *client, Request *req,
const uint8_t *data, size_t len, int fin)
@ -931,9 +993,23 @@ void update_html_parser(HttpClient *client, Request *req,
util::fieldeq(uri.c_str(), u, req->uri.c_str(), req->u, UF_SCHEMA) &&
util::fieldeq(uri.c_str(), u, req->uri.c_str(), req->u, UF_HOST) &&
util::porteq(uri.c_str(), u, req->uri.c_str(), req->u)) {
int32_t pri = adjust_pri(req->pri, p.second);
// No POST data for assets
if ( client->add_request(uri, nullptr, 0, pri, req->level+1) ) {
nghttp2_priority_spec pri_spec;
// We always specify priority group of parent stream, so that
// even if the parent stream is closed, the dependent stream is
// in the same priority group. We adjust the priority using
// separate PRIORITY frame after stream ID becomes known.
nghttp2_priority_spec_group_init(&pri_spec, req->stream_id,
config.weight);
if ( client->add_request(uri, nullptr, 0, pri_spec, req->dep,
req->level+1) ) {
auto& req = client->reqvec.back();
req->pri = p.second;
submit_request(client, config.headers,
client->reqvec.back().get());
}
@ -997,8 +1073,48 @@ void check_stream_id(nghttp2_session *session, int32_t stream_id,
auto req = (Request*)nghttp2_session_get_stream_user_data(session,
stream_id);
assert(req);
req->stream_id = stream_id;
client->streams[stream_id] = req;
req->record_request_time();
if(req->pri == 0) {
assert(req->dep->deps.empty());
req->dep->deps.push_back(std::vector<Request*>{req});
return;
}
if(stream_id % 2 == 0) {
return;
}
auto pri_spec = req->resolve_dep(req->pri);
if(pri_spec.pri_type == NGHTTP2_PRIORITY_TYPE_DEP) {
nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, stream_id,
&pri_spec);
}
auto itr = std::begin(req->dep->deps);
for(; itr != std::end(req->dep->deps); ++itr) {
if((*itr)[0]->pri == req->pri) {
(*itr).push_back(req);
break;
}
if((*itr)[0]->pri > req->pri) {
auto v = std::vector<Request*>{req};
req->dep->deps.insert(itr, std::move(v));
break;
}
}
if(itr == std::end(req->dep->deps)) {
req->dep->deps.push_back(std::vector<Request*>{req});
}
}
} // namespace
@ -1075,7 +1191,10 @@ int on_begin_headers_callback(nghttp2_session *session,
http_parser_url u;
memset(&u, 0, sizeof(u));
// TODO Set pri and level
auto req = util::make_unique<Request>("", u, nullptr, 0, 0, 0);
nghttp2_priority_spec pri_spec = { NGHTTP2_PRIORITY_TYPE_NONE };
auto req = util::make_unique<Request>("", u, nullptr, 0, pri_spec,
nullptr);
nghttp2_session_set_stream_user_data(session, stream_id, req.get());
client->reqvec.push_back(std::move(req));
check_stream_id(session, stream_id, user_data);
@ -1484,10 +1603,18 @@ int communicate(const std::string& scheme, const std::string& host,
}
{
HttpClient client{callbacks, evbase, ssl_ctx};
nghttp2_priority_spec pri_spec = { NGHTTP2_PRIORITY_TYPE_NONE };
if(config.weight != NGHTTP2_DEFAULT_WEIGHT) {
nghttp2_priority_spec_group_init(&pri_spec, -1, config.weight);
}
for(auto req : requests) {
for(int i = 0; i < config.multiply; ++i) {
auto dep = std::make_shared<Dependency>();
client.add_request(std::get<0>(req), std::get<1>(req),
std::get<2>(req), config.pri);
std::get<2>(req), pri_spec, std::move(dep));
}
}
client.update_hostport();
@ -1682,9 +1809,9 @@ Options:
is ignored if the request URI has https scheme.
If -d is used, the HTTP upgrade request is
performed with OPTIONS method.
-p, --pri=<PRIORITY>
Sets stream priority. Default: )"
<< NGHTTP2_PRI_DEFAULT << R"(
-p, --weight=<WEIGHT>
Sets priority group weight. Default: )"
<< NGHTTP2_DEFAULT_WEIGHT << R"(
-M, --peer-max-concurrent-streams=<N>
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value
of remote endpoint as if it is received in
@ -1721,7 +1848,7 @@ int main(int argc, char **argv)
{"data", required_argument, nullptr, 'd'},
{"multiply", required_argument, nullptr, 'm'},
{"upgrade", no_argument, nullptr, 'u'},
{"pri", required_argument, nullptr, 'p'},
{"weight", required_argument, nullptr, 'p'},
{"peer-max-concurrent-streams", required_argument, nullptr, 'M'},
{"header-table-size", required_argument, nullptr, 'c'},
{"padding", required_argument, nullptr, 'b'},
@ -1758,11 +1885,11 @@ int main(int argc, char **argv)
break;
case 'p': {
auto n = strtoul(optarg, nullptr, 10);
if(n <= NGHTTP2_PRI_LOWEST) {
config.pri = n;
if(n <= NGHTTP2_MAX_WEIGHT) {
config.weight = n;
} else {
std::cerr << "-p: specify the integer in the range [0, "
<< NGHTTP2_PRI_LOWEST << "], inclusive"
<< NGHTTP2_MAX_WEIGHT << "], inclusive"
<< std::endl;
exit(EXIT_FAILURE);
}

View File

@ -578,7 +578,8 @@ int Http2Session::submit_request(Http2DownstreamConnection *dconn,
{
assert(state_ == CONNECTED);
auto sd = util::make_unique<StreamData>();
int rv = nghttp2_submit_request(session_, pri, nva, nvlen,
// TODO Specify nullptr to pri_spec for now
int rv = nghttp2_submit_request(session_, nullptr, nva, nvlen,
data_prd, sd.get());
if(rv == 0) {
dconn->attach_stream_data(sd.get());
@ -640,9 +641,15 @@ int Http2Session::submit_priority(Http2DownstreamConnection *dconn,
return 0;
}
int rv;
rv = nghttp2_submit_priority(session_, NGHTTP2_FLAG_NONE,
dconn->get_downstream()->
get_downstream_stream_id(), pri);
// TODO Disabled temporarily
// rv = nghttp2_submit_priority(session_, NGHTTP2_FLAG_NONE,
// dconn->get_downstream()->
// get_downstream_stream_id(), pri);
rv = 0;
if(rv < NGHTTP2_ERR_FATAL) {
SSLOG(FATAL, this) << "nghttp2_submit_priority() failed: "
<< nghttp2_strerror(rv);

View File

@ -223,9 +223,12 @@ int on_begin_headers_callback(nghttp2_session *session,
ULOG(INFO, upstream) << "Received upstream request HEADERS stream_id="
<< frame->hd.stream_id;
}
// TODO Use priority 0 for now
auto downstream = new Downstream(upstream,
frame->hd.stream_id,
frame->headers.pri);
0);
upstream->add_downstream(downstream);
downstream->init_response_body_buf();
@ -367,10 +370,11 @@ int on_frame_recv_callback
if(!downstream) {
break;
}
rv = downstream->change_priority(frame->priority.pri);
if(rv != 0) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
// TODO comment out for now
// rv = downstream->change_priority(frame->priority.pri);
// if(rv != 0) {
// return NGHTTP2_ERR_CALLBACK_FAILURE;
// }
break;
}
case NGHTTP2_SETTINGS:

View File

@ -75,7 +75,8 @@ int htp_msg_begin(http_parser *htp)
ULOG(INFO, upstream) << "HTTP request started";
}
upstream->reset_current_header_length();
auto downstream = new Downstream(upstream, 0, NGHTTP2_PRI_DEFAULT);
// TODO specify 0 as priority for now
auto downstream = new Downstream(upstream, 0, 0);
upstream->attach_downstream(downstream);
return 0;
}

View File

@ -84,6 +84,8 @@ int main(int argc, char* argv[])
test_nghttp2_session_recv_data) ||
!CU_add_test(pSuite, "session_recv_continuation",
test_nghttp2_session_recv_continuation) ||
!CU_add_test(pSuite, "session_recv_headers_with_priority",
test_nghttp2_session_recv_headers_with_priority) ||
!CU_add_test(pSuite, "session_recv_premature_headers",
test_nghttp2_session_recv_premature_headers) ||
!CU_add_test(pSuite, "session_continue", test_nghttp2_session_continue) ||
@ -209,6 +211,18 @@ int main(int argc, char* argv[])
test_nghttp2_session_pack_headers_with_padding4) ||
!CU_add_test(pSuite, "pack_settings_payload",
test_nghttp2_pack_settings_payload) ||
!CU_add_test(pSuite, "session_stream_dep_add",
test_nghttp2_session_stream_dep_add) ||
!CU_add_test(pSuite, "session_stream_dep_remove",
test_nghttp2_session_stream_dep_remove) ||
!CU_add_test(pSuite, "session_stream_dep_add_subtree",
test_nghttp2_session_stream_dep_add_subtree) ||
!CU_add_test(pSuite, "session_stream_dep_remove_subtree",
test_nghttp2_session_stream_dep_remove_subtree) ||
!CU_add_test(pSuite, "session_stream_attach_data",
test_nghttp2_session_stream_attach_data) ||
!CU_add_test(pSuite, "session_stream_attach_data_subtree",
test_nghttp2_session_stream_attach_data_subtree) ||
!CU_add_test(pSuite, "frame_pack_headers",
test_nghttp2_frame_pack_headers) ||
!CU_add_test(pSuite, "frame_pack_headers_frame_too_large",

View File

@ -32,6 +32,7 @@
#include "nghttp2_frame.h"
#include "nghttp2_helper.h"
#include "nghttp2_test_helper.h"
#include "nghttp2_priority_spec.h"
static nghttp2_nv make_nv(const char *name, const char *value)
{
@ -74,6 +75,7 @@ void test_nghttp2_frame_pack_headers()
nghttp2_headers frame, oframe;
nghttp2_bufs bufs;
nghttp2_nv *nva;
nghttp2_priority_spec pri_spec;
ssize_t nvlen;
nva_out out;
ssize_t hdblocklen;
@ -87,10 +89,13 @@ void test_nghttp2_frame_pack_headers()
nva = headers();
nvlen = HEADERS_LENGTH;
pri_spec.pri_type = NGHTTP2_PRIORITY_TYPE_NONE;
nghttp2_frame_headers_init(&frame,
NGHTTP2_FLAG_END_STREAM|NGHTTP2_FLAG_END_HEADERS,
1000000007,
1 << 20, nva, nvlen);
NGHTTP2_FLAG_END_STREAM |
NGHTTP2_FLAG_END_HEADERS,
1000000007, &pri_spec, nva, nvlen);
rv = nghttp2_frame_pack_headers(&bufs, &frame, &deflater);
nghttp2_bufs_rewind(&bufs);
@ -103,8 +108,8 @@ void test_nghttp2_frame_pack_headers()
NGHTTP2_HEADERS,
NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS,
1000000007, &oframe.hd);
/* We didn't include PRIORITY flag so priority is not packed */
CU_ASSERT(1 << 30 == oframe.pri);
/* We include NGHTTP2_PRIORITY_TYPE_NONE */
CU_ASSERT(NGHTTP2_PRIORITY_TYPE_NONE == oframe.pri_spec.pri_type);
hdblocklen = nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN;
CU_ASSERT(hdblocklen ==
@ -119,8 +124,10 @@ void test_nghttp2_frame_pack_headers()
nghttp2_bufs_reset(&bufs);
memset(&oframe, 0, sizeof(oframe));
/* Next, include PRIORITY flag */
frame.hd.flags |= NGHTTP2_FLAG_PRIORITY;
/* Next, include NGHTTP2_PRIORITY_TYPE_GROUP */
nghttp2_priority_spec_group_init(&frame.pri_spec, 1000000009, 12);
frame.hd.flags |= NGHTTP2_FLAG_PRIORITY_GROUP;
rv = nghttp2_frame_pack_headers(&bufs, &frame, &deflater);
CU_ASSERT(0 == rv);
@ -130,20 +137,64 @@ void test_nghttp2_frame_pack_headers()
check_frame_header(nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN,
NGHTTP2_HEADERS,
NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS |
NGHTTP2_FLAG_PRIORITY,
NGHTTP2_FLAG_PRIORITY_GROUP,
1000000007, &oframe.hd);
CU_ASSERT(1 << 20 == oframe.pri);
hdblocklen = nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN - 4;
CU_ASSERT(NGHTTP2_PRIORITY_TYPE_GROUP == oframe.pri_spec.pri_type);
CU_ASSERT(1000000009 == oframe.pri_spec.group.pri_group_id);
CU_ASSERT(12 == oframe.pri_spec.group.weight);
hdblocklen = nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN
- nghttp2_frame_priority_len(oframe.hd.flags);
CU_ASSERT(hdblocklen ==
inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN + 4));
inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN
+ nghttp2_frame_priority_len(oframe.hd.flags)));
nghttp2_nv_array_sort(out.nva, out.nvlen);
CU_ASSERT(nvnameeq("method", &out.nva[0]));
nghttp2_frame_headers_free(&oframe);
nva_out_reset(&out);
nghttp2_bufs_reset(&bufs);
memset(&oframe, 0, sizeof(oframe));
/* Next, include NGHTTP2_PRIORITY_TYPE_DEP */
nghttp2_priority_spec_dep_init(&frame.pri_spec, 123, 1);
frame.hd.flags =
NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS |
NGHTTP2_FLAG_PRIORITY_DEPENDENCY;
rv = nghttp2_frame_pack_headers(&bufs, &frame, &deflater);
CU_ASSERT(0 == rv);
CU_ASSERT(nghttp2_bufs_len(&bufs) > 0);
CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &bufs));
check_frame_header(nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN,
NGHTTP2_HEADERS,
NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS |
NGHTTP2_FLAG_PRIORITY_DEPENDENCY,
1000000007, &oframe.hd);
CU_ASSERT(NGHTTP2_PRIORITY_TYPE_DEP == oframe.pri_spec.pri_type);
CU_ASSERT(123 == oframe.pri_spec.dep.stream_id);
CU_ASSERT(1 == oframe.pri_spec.dep.exclusive);
hdblocklen = nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN
- nghttp2_frame_priority_len(oframe.hd.flags);
CU_ASSERT(hdblocklen ==
inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN
+ nghttp2_frame_priority_len(oframe.hd.flags)));
nghttp2_nv_array_sort(out.nva, out.nvlen);
CU_ASSERT(nvnameeq("method", &out.nva[0]));
nghttp2_frame_headers_free(&oframe);
nva_out_reset(&out);
nghttp2_bufs_free(&bufs);
nghttp2_frame_headers_free(&oframe);
nghttp2_frame_headers_free(&frame);
nghttp2_hd_inflate_free(&inflater);
nghttp2_hd_deflate_free(&deflater);
@ -155,6 +206,7 @@ void test_nghttp2_frame_pack_headers_frame_too_large(void)
nghttp2_headers frame;
nghttp2_bufs bufs;
nghttp2_nv *nva;
nghttp2_priority_spec pri_spec;
ssize_t nvlen;
size_t big_vallen = NGHTTP2_HD_MAX_VALUE;
nghttp2_nv big_hds[16];
@ -173,12 +225,13 @@ void test_nghttp2_frame_pack_headers_frame_too_large(void)
big_hds[i].valuelen = big_vallen;
}
pri_spec.pri_type = NGHTTP2_PRIORITY_TYPE_NONE;
nvlen = nghttp2_nv_array_copy(&nva, big_hds, big_hdslen);
nghttp2_hd_deflate_init(&deflater);
nghttp2_frame_headers_init(&frame,
NGHTTP2_FLAG_END_STREAM|NGHTTP2_FLAG_END_HEADERS,
1000000007,
0, nva, nvlen);
1000000007, &pri_spec, nva, nvlen);
rv = nghttp2_frame_pack_headers(&bufs, &frame, &deflater);
CU_ASSERT(NGHTTP2_ERR_HEADER_COMP == rv);
@ -194,22 +247,53 @@ void test_nghttp2_frame_pack_priority(void)
{
nghttp2_priority frame, oframe;
nghttp2_bufs bufs;
nghttp2_priority_spec pri_spec;
int rv;
frame_pack_bufs_init(&bufs);
nghttp2_frame_priority_init(&frame, 1000000007, 1 << 30);
/* First, pack priority with priority group and weight */
nghttp2_priority_spec_group_init(&pri_spec, 1000000009, 12);
nghttp2_frame_priority_init(&frame, 1000000007, &pri_spec);
rv = nghttp2_frame_pack_priority(&bufs, &frame);
CU_ASSERT(0 == rv);
CU_ASSERT(13 == nghttp2_bufs_len(&bufs));
CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &bufs));
check_frame_header(4, NGHTTP2_PRIORITY, NGHTTP2_FLAG_PRIORITY_GROUP,
1000000007, &oframe.hd);
CU_ASSERT(NGHTTP2_PRIORITY_TYPE_GROUP == oframe.pri_spec.pri_type);
CU_ASSERT(1000000009 == oframe.pri_spec.group.pri_group_id);
CU_ASSERT(12 == oframe.pri_spec.group.weight);
nghttp2_frame_priority_free(&oframe);
nghttp2_bufs_reset(&bufs);
memset(&oframe, 0, sizeof(oframe));
/* Next, pack priority with stream dependency */
nghttp2_priority_spec_dep_init(&pri_spec, 79, 1);
nghttp2_frame_priority_init(&frame, 1000000007, &pri_spec);
rv = nghttp2_frame_pack_priority(&bufs, &frame);
CU_ASSERT(0 == rv);
CU_ASSERT(12 == nghttp2_bufs_len(&bufs));
CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &bufs));
check_frame_header(4, NGHTTP2_PRIORITY, NGHTTP2_FLAG_NONE, 1000000007,
&oframe.hd);
CU_ASSERT(1 << 30 == oframe.pri);
check_frame_header(4, NGHTTP2_PRIORITY, NGHTTP2_FLAG_PRIORITY_DEPENDENCY,
1000000007, &oframe.hd);
CU_ASSERT(NGHTTP2_PRIORITY_TYPE_DEP == oframe.pri_spec.pri_type);
CU_ASSERT(79 == oframe.pri_spec.dep.stream_id);
CU_ASSERT(1 == oframe.pri_spec.dep.exclusive);
nghttp2_frame_priority_free(&oframe);
nghttp2_bufs_reset(&bufs);
nghttp2_bufs_free(&bufs);
nghttp2_frame_priority_free(&oframe);
nghttp2_frame_priority_free(&frame);
}

File diff suppressed because it is too large Load Diff

View File

@ -31,6 +31,7 @@ void test_nghttp2_session_recv_invalid_frame(void);
void test_nghttp2_session_recv_eof(void);
void test_nghttp2_session_recv_data(void);
void test_nghttp2_session_recv_continuation(void);
void test_nghttp2_session_recv_headers_with_priority(void);
void test_nghttp2_session_recv_premature_headers(void);
void test_nghttp2_session_continue(void);
void test_nghttp2_session_add_frame(void);
@ -96,5 +97,11 @@ void test_nghttp2_session_pack_headers_with_padding2(void);
void test_nghttp2_session_pack_headers_with_padding3(void);
void test_nghttp2_session_pack_headers_with_padding4(void);
void test_nghttp2_pack_settings_payload(void);
void test_nghttp2_session_stream_dep_add(void);
void test_nghttp2_session_stream_dep_remove(void);
void test_nghttp2_session_stream_dep_add_subtree(void);
void test_nghttp2_session_stream_dep_remove_subtree(void);
void test_nghttp2_session_stream_attach_data(void);
void test_nghttp2_session_stream_attach_data_subtree(void);
#endif /* NGHTTP2_SESSION_TEST_H */

View File

@ -29,6 +29,7 @@
#include <CUnit/CUnit.h>
#include "nghttp2_helper.h"
#include "nghttp2_priority_spec.h"
int unpack_framebuf(nghttp2_frame *frame, nghttp2_bufs *bufs)
{
@ -213,3 +214,47 @@ void bufs_large_init(nghttp2_bufs *bufs, size_t chunk_size)
/* 2 for PAD_HIGH and PAD_LOW */
nghttp2_bufs_init2(bufs, chunk_size, 16, NGHTTP2_FRAME_HDLEN + 2);
}
nghttp2_stream* open_stream(nghttp2_session *session, int32_t stream_id)
{
nghttp2_priority_spec pri_spec;
nghttp2_priority_spec_group_init(&pri_spec, stream_id,
NGHTTP2_DEFAULT_WEIGHT);
return nghttp2_session_open_stream(session, stream_id,
NGHTTP2_STREAM_FLAG_NONE,
&pri_spec,
NGHTTP2_STREAM_OPENED,
NULL);
}
nghttp2_stream* open_stream_with_dep(nghttp2_session *session,
int32_t stream_id,
nghttp2_stream *dep_stream)
{
nghttp2_priority_spec pri_spec;
nghttp2_priority_spec_dep_init(&pri_spec, dep_stream->stream_id, 0);
return nghttp2_session_open_stream(session, stream_id,
NGHTTP2_STREAM_FLAG_NONE,
&pri_spec,
NGHTTP2_STREAM_OPENED,
NULL);
}
nghttp2_stream* open_stream_with_dep_excl(nghttp2_session *session,
int32_t stream_id,
nghttp2_stream *dep_stream)
{
nghttp2_priority_spec pri_spec;
nghttp2_priority_spec_dep_init(&pri_spec, dep_stream->stream_id, 1);
return nghttp2_session_open_stream(session, stream_id,
NGHTTP2_STREAM_FLAG_NONE,
&pri_spec,
NGHTTP2_STREAM_OPENED,
NULL);
}

View File

@ -31,6 +31,7 @@
#include "nghttp2_frame.h"
#include "nghttp2_hd.h"
#include "nghttp2_session.h"
#define MAKE_NV(NAME, VALUE) \
{ (uint8_t*)NAME, (uint8_t*)VALUE, strlen(NAME), strlen(VALUE) }
@ -81,4 +82,14 @@ void frame_pack_bufs_init(nghttp2_bufs *bufs);
void bufs_large_init(nghttp2_bufs *bufs, size_t chunk_size);
nghttp2_stream* open_stream(nghttp2_session *session, int32_t stream_id);
nghttp2_stream* open_stream_with_dep(nghttp2_session *session,
int32_t stream_id,
nghttp2_stream *dep_stream);
nghttp2_stream* open_stream_with_dep_excl(nghttp2_session *session,
int32_t stream_id,
nghttp2_stream *dep_stream);
#endif /* NGHTTP2_TEST_HELPER_H */