Implement DATA frame padding
This commit is contained in:
parent
f26270b5b4
commit
814d0f76f3
|
@ -147,6 +147,14 @@ typedef struct {
|
|||
*/
|
||||
#define NGHTTP2_CLIENT_CONNECTION_HEADER_LEN 24
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* The default value of DATA padding alignment. See
|
||||
* :member:`NGHTTP2_OPT_DATA_PAD_ALIGNMENT`.
|
||||
*/
|
||||
#define NGHTTP2_DATA_PAD_ALIGNMENT 256
|
||||
|
||||
/**
|
||||
* @enum
|
||||
*
|
||||
|
@ -599,6 +607,11 @@ typedef struct {
|
|||
*/
|
||||
typedef struct {
|
||||
nghttp2_frame_hd hd;
|
||||
/**
|
||||
* The length of the padding in this frame. This includes PAD_HIGH
|
||||
* and PAD_LOW.
|
||||
*/
|
||||
size_t padlen;
|
||||
} nghttp2_data;
|
||||
|
||||
/**
|
||||
|
@ -1309,7 +1322,15 @@ typedef enum {
|
|||
* will be overwritten if the local endpoint receives
|
||||
* SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint.
|
||||
*/
|
||||
NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 2
|
||||
NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 2,
|
||||
/**
|
||||
* This option specifies the alignment of padding in DATA frame. If
|
||||
* this option is set to N, padding is added to DATA payload so that
|
||||
* its payload length is divisible by N. Due to flow control,
|
||||
* padding is not always added according to this alignment. The
|
||||
* option value must be greater than or equal to 8.
|
||||
*/
|
||||
NGHTTP2_OPT_DATA_PAD_ALIGNMENT = 1 << 3
|
||||
} nghttp2_opt;
|
||||
|
||||
/**
|
||||
|
@ -1330,6 +1351,10 @@ typedef struct {
|
|||
* :enum:`NGHTTP2_OPT_NO_AUTO_CONNECTION_WINDOW_UPDATE`
|
||||
*/
|
||||
uint8_t no_auto_connection_window_update;
|
||||
/**
|
||||
* :enum:`NGHTTP2_OPT_DATA_PAD_ALIGNMENT`
|
||||
*/
|
||||
uint16_t data_pad_alignment;
|
||||
} nghttp2_opt_set;
|
||||
|
||||
/**
|
||||
|
|
|
@ -185,6 +185,7 @@ void nghttp2_frame_window_update_free(nghttp2_window_update *frame)
|
|||
void nghttp2_frame_data_init(nghttp2_data *frame, nghttp2_private_data *pdata)
|
||||
{
|
||||
frame->hd = pdata->hd;
|
||||
frame->padlen = pdata->padlen;
|
||||
/* flags may have NGHTTP2_FLAG_END_STREAM even if the sent chunk
|
||||
is not the end of the stream */
|
||||
if(!pdata->eof) {
|
||||
|
@ -192,6 +193,13 @@ void nghttp2_frame_data_init(nghttp2_data *frame, nghttp2_private_data *pdata)
|
|||
}
|
||||
}
|
||||
|
||||
size_t nghttp2_frame_data_trail_padlen(nghttp2_data *frame)
|
||||
{
|
||||
return frame->padlen
|
||||
- ((frame->hd.flags & NGHTTP2_FLAG_PAD_HIGH) > 0)
|
||||
- ((frame->hd.flags & NGHTTP2_FLAG_PAD_LOW) > 0);
|
||||
}
|
||||
|
||||
void nghttp2_frame_private_data_init(nghttp2_private_data *frame,
|
||||
uint8_t flags,
|
||||
int32_t stream_id,
|
||||
|
|
|
@ -71,6 +71,11 @@ typedef struct {
|
|||
* The data to be sent for this DATA frame.
|
||||
*/
|
||||
nghttp2_data_provider data_prd;
|
||||
/**
|
||||
* The number of bytes added as padding. This includes PAD_HIGH and
|
||||
* PAD_LOW.
|
||||
*/
|
||||
size_t padlen;
|
||||
/**
|
||||
* The flag to indicate whether EOF was reached or not. Initially
|
||||
* |eof| is 0. It becomes 1 after all data were read. This is used
|
||||
|
@ -421,6 +426,12 @@ void nghttp2_frame_window_update_free(nghttp2_window_update *frame);
|
|||
|
||||
void nghttp2_frame_data_init(nghttp2_data *frame, nghttp2_private_data *pdata);
|
||||
|
||||
/*
|
||||
* Returns the number of padding data after application data
|
||||
* payload. Thus this does not include the PAD_HIGH and PAD_LOW.
|
||||
*/
|
||||
size_t nghttp2_frame_data_trail_padlen(nghttp2_data *frame);
|
||||
|
||||
void nghttp2_frame_private_data_init(nghttp2_private_data *frame,
|
||||
uint8_t flags,
|
||||
int32_t stream_id,
|
||||
|
|
|
@ -223,6 +223,12 @@ static int nghttp2_session_new(nghttp2_session **session_ptr,
|
|||
(*session_ptr)->opt_flags |=
|
||||
NGHTTP2_OPTMASK_NO_AUTO_CONNECTION_WINDOW_UPDATE;
|
||||
}
|
||||
if((opt_set_mask & NGHTTP2_OPT_DATA_PAD_ALIGNMENT) &&
|
||||
opt_set->data_pad_alignment >= 8) {
|
||||
(*session_ptr)->data_pad_alignment = opt_set->data_pad_alignment;
|
||||
} else {
|
||||
(*session_ptr)->data_pad_alignment = NGHTTP2_DATA_PAD_ALIGNMENT;
|
||||
}
|
||||
|
||||
(*session_ptr)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
|
||||
(*session_ptr)->recv_window_size = 0;
|
||||
|
@ -1271,6 +1277,7 @@ static ssize_t nghttp2_session_prep_frame(nghttp2_session *session,
|
|||
framebuflen = nghttp2_session_pack_data(session,
|
||||
&session->aob.framebuf,
|
||||
&session->aob.framebufmax,
|
||||
&session->aob.framebufoff,
|
||||
next_readmax,
|
||||
data_frame);
|
||||
if(framebuflen == NGHTTP2_ERR_DEFERRED) {
|
||||
|
@ -1589,8 +1596,20 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session)
|
|||
} else if(item->frame_cat == NGHTTP2_CAT_DATA) {
|
||||
nghttp2_private_data *data_frame;
|
||||
nghttp2_outbound_item* next_item;
|
||||
nghttp2_stream *stream;
|
||||
size_t effective_payloadlen;
|
||||
|
||||
data_frame = nghttp2_outbound_item_get_data_frame(session->aob.item);
|
||||
stream = nghttp2_session_get_stream(session, data_frame->hd.stream_id);
|
||||
/* We update flow control window after a frame was completely
|
||||
sent. This is possible because we choose payload length not to
|
||||
exceed the window */
|
||||
effective_payloadlen = data_frame->hd.length - data_frame->padlen;
|
||||
session->remote_window_size -= effective_payloadlen;
|
||||
if(stream) {
|
||||
stream->remote_window_size -= effective_payloadlen;
|
||||
}
|
||||
|
||||
if(session->callbacks.on_frame_send_callback) {
|
||||
nghttp2_frame public_data_frame;
|
||||
nghttp2_frame_data_init(&public_data_frame.data, data_frame);
|
||||
|
@ -1599,17 +1618,15 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session)
|
|||
return rv;
|
||||
}
|
||||
}
|
||||
if(data_frame->eof && (data_frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
|
||||
nghttp2_stream *stream =
|
||||
nghttp2_session_get_stream(session, data_frame->hd.stream_id);
|
||||
if(stream) {
|
||||
|
||||
if(stream && data_frame->eof &&
|
||||
(data_frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
|
||||
nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
|
||||
rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
|
||||
if(nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* If session is closed or RST_STREAM was queued, we won't send
|
||||
further data. */
|
||||
if(data_frame->eof ||
|
||||
|
@ -1618,16 +1635,14 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session)
|
|||
nghttp2_active_outbound_item_reset(&session->aob);
|
||||
return 0;
|
||||
}
|
||||
/* Assuming stream is not NULL */
|
||||
assert(stream);
|
||||
next_item = nghttp2_session_get_next_ob_item(session);
|
||||
/* If priority of this stream is higher or equal to other stream
|
||||
waiting at the top of the queue, we continue to send this
|
||||
data. */
|
||||
if(next_item == NULL || session->aob.item->pri < next_item->pri) {
|
||||
size_t next_readmax;
|
||||
nghttp2_stream *stream;
|
||||
stream = nghttp2_session_get_stream(session, data_frame->hd.stream_id);
|
||||
/* Assuming stream is not NULL */
|
||||
assert(stream);
|
||||
next_readmax = nghttp2_session_next_data_read(session, stream);
|
||||
if(next_readmax == 0) {
|
||||
nghttp2_stream_defer_data(stream, session->aob.item,
|
||||
|
@ -1639,6 +1654,7 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session)
|
|||
rv = nghttp2_session_pack_data(session,
|
||||
&session->aob.framebuf,
|
||||
&session->aob.framebufmax,
|
||||
&session->aob.framebufoff,
|
||||
next_readmax,
|
||||
data_frame);
|
||||
if(nghttp2_is_fatal(rv)) {
|
||||
|
@ -1666,7 +1682,6 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session)
|
|||
}
|
||||
assert(rv >= 0);
|
||||
session->aob.framebuflen = session->aob.framebufmark = rv;
|
||||
session->aob.framebufoff = 0;
|
||||
return 0;
|
||||
}
|
||||
/* Update seq to interleave other streams with the same
|
||||
|
@ -1740,6 +1755,7 @@ int nghttp2_session_send(nghttp2_session *session)
|
|||
if(item->frame_cat == NGHTTP2_CAT_CTRL) {
|
||||
nghttp2_frame *frame = nghttp2_outbound_item_get_ctrl_frame(item);
|
||||
session->aob.framebufmark =
|
||||
session->aob.framebufoff +
|
||||
frame->hd.length + NGHTTP2_FRAME_HEAD_LENGTH;
|
||||
r = session_call_before_frame_send(session, frame);
|
||||
if(nghttp2_is_fatal(r)) {
|
||||
|
@ -1748,10 +1764,13 @@ int nghttp2_session_send(nghttp2_session *session)
|
|||
} else {
|
||||
nghttp2_private_data *frame;
|
||||
frame = nghttp2_outbound_item_get_data_frame(session->aob.item);
|
||||
/* session->aob.framebufmark = session->aob.framebuflen; */
|
||||
session->aob.framebufmark =
|
||||
session->aob.framebufoff +
|
||||
frame->hd.length + NGHTTP2_FRAME_HEAD_LENGTH;
|
||||
}
|
||||
}
|
||||
|
||||
data = session->aob.framebuf + session->aob.framebufoff;
|
||||
datalen = session->aob.framebufmark - session->aob.framebufoff;
|
||||
sentlen = session->callbacks.send_callback(session, data, datalen, 0,
|
||||
|
@ -1763,23 +1782,6 @@ int nghttp2_session_send(nghttp2_session *session)
|
|||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
} else {
|
||||
if(session->aob.item->frame_cat == NGHTTP2_CAT_DATA &&
|
||||
session->aob.framebufoff + sentlen > NGHTTP2_FRAME_HEAD_LENGTH) {
|
||||
nghttp2_private_data *frame;
|
||||
nghttp2_stream *stream;
|
||||
uint16_t len;
|
||||
if(session->aob.framebufoff < NGHTTP2_FRAME_HEAD_LENGTH) {
|
||||
len = session->aob.framebufoff + sentlen - NGHTTP2_FRAME_HEAD_LENGTH;
|
||||
} else {
|
||||
len = sentlen;
|
||||
}
|
||||
frame = nghttp2_outbound_item_get_data_frame(session->aob.item);
|
||||
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
|
||||
if(stream) {
|
||||
stream->remote_window_size -= len;
|
||||
}
|
||||
session->remote_window_size -= len;
|
||||
}
|
||||
session->aob.framebufoff += sentlen;
|
||||
if(session->aob.framebufoff == session->aob.framebufmark) {
|
||||
/* Frame has completely sent */
|
||||
|
@ -3338,6 +3340,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
|||
switch(iframe->frame.hd.type) {
|
||||
case NGHTTP2_DATA: {
|
||||
DEBUGF(fprintf(stderr, "DATA\n"));
|
||||
iframe->frame.data.padlen = 0;
|
||||
/* Check stream is open. If it is not open or closing,
|
||||
ignore payload. */
|
||||
busy = 1;
|
||||
|
@ -3351,6 +3354,25 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
|||
if(nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if(iframe->frame.hd.flags & NGHTTP2_FLAG_PAD_HIGH) {
|
||||
if((iframe->frame.hd.flags & NGHTTP2_FLAG_PAD_LOW) == 0) {
|
||||
iframe->state = NGHTTP2_IB_IGN_DATA;
|
||||
rv = nghttp2_session_terminate_session(session,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
if(nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
break;
|
||||
}
|
||||
iframe->state = NGHTTP2_IB_READ_NBYTE;
|
||||
iframe->left = 2;
|
||||
break;
|
||||
}
|
||||
if(iframe->frame.hd.flags & NGHTTP2_FLAG_PAD_LOW) {
|
||||
iframe->state = NGHTTP2_IB_READ_NBYTE;
|
||||
iframe->left = 1;
|
||||
break;
|
||||
}
|
||||
iframe->state = NGHTTP2_IB_READ_DATA;
|
||||
break;
|
||||
}
|
||||
|
@ -3454,7 +3476,26 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
|||
}
|
||||
switch(iframe->frame.hd.type) {
|
||||
case NGHTTP2_DATA:
|
||||
assert(0);
|
||||
busy = 1;
|
||||
iframe->frame.data.padlen = iframe->buf[0];
|
||||
if(iframe->frame.hd.flags & NGHTTP2_FLAG_PAD_HIGH) {
|
||||
iframe->frame.data.padlen <<= 8;
|
||||
iframe->frame.data.padlen |= iframe->buf[1];
|
||||
++iframe->frame.data.padlen;
|
||||
}
|
||||
++iframe->frame.data.padlen;
|
||||
|
||||
DEBUGF(fprintf(stderr, "padlen=%zu\n", iframe->frame.data.padlen));
|
||||
if(iframe->frame.data.padlen > iframe->frame.hd.length) {
|
||||
rv = nghttp2_session_terminate_session(session,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
if(nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
iframe->state = NGHTTP2_IB_IGN_DATA;
|
||||
break;
|
||||
}
|
||||
iframe->state = NGHTTP2_IB_READ_DATA;
|
||||
break;
|
||||
case NGHTTP2_HEADERS:
|
||||
rv = session_process_headers_frame(session);
|
||||
|
@ -3703,6 +3744,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
|||
DEBUGF(fprintf(stderr, "readlen=%zu, payloadleft=%zu\n",
|
||||
readlen, iframe->payloadleft));
|
||||
if(readlen > 0) {
|
||||
size_t data_readlen = readlen;
|
||||
rv = nghttp2_session_update_recv_connection_window_size
|
||||
(session, readlen);
|
||||
if(nghttp2_is_fatal(rv)) {
|
||||
|
@ -3721,13 +3763,25 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
|||
}
|
||||
}
|
||||
}
|
||||
if(session->callbacks.on_data_chunk_recv_callback) {
|
||||
if(nghttp2_frame_data_trail_padlen(&iframe->frame.data) >
|
||||
iframe->payloadleft) {
|
||||
size_t trail_padlen;
|
||||
trail_padlen = nghttp2_frame_data_trail_padlen(&iframe->frame.data)
|
||||
- iframe->payloadleft;
|
||||
if(readlen < trail_padlen) {
|
||||
data_readlen = 0;
|
||||
} else {
|
||||
data_readlen -= trail_padlen;
|
||||
}
|
||||
}
|
||||
DEBUGF(fprintf(stderr, "data_readlen=%zu\n", data_readlen));
|
||||
if(data_readlen > 0 && session->callbacks.on_data_chunk_recv_callback) {
|
||||
rv = session->callbacks.on_data_chunk_recv_callback
|
||||
(session,
|
||||
iframe->frame.hd.flags,
|
||||
iframe->frame.hd.stream_id,
|
||||
in - readlen,
|
||||
readlen,
|
||||
data_readlen,
|
||||
session->user_data);
|
||||
if(rv == NGHTTP2_ERR_PAUSE) {
|
||||
/* Set type to NGHTTP2_DATA, so that we can see what was
|
||||
|
@ -3937,10 +3991,18 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
|
|||
|
||||
ssize_t nghttp2_session_pack_data(nghttp2_session *session,
|
||||
uint8_t **buf_ptr, size_t *buflen_ptr,
|
||||
size_t *bufoff_ptr,
|
||||
size_t datamax,
|
||||
nghttp2_private_data *frame)
|
||||
{
|
||||
ssize_t framelen = datamax+8, r;
|
||||
/* extra 2 bytes for PAD_HIGH and PAD_LOW. We allocate extra 2 bytes
|
||||
for padding. Based on the padding length, we adjust the starting
|
||||
offset of frame data. The starting offset is assigned into
|
||||
|*bufoff_ptr|. */
|
||||
size_t headoff = 2;
|
||||
size_t dataoff = NGHTTP2_FRAME_HEAD_LENGTH + headoff;
|
||||
ssize_t framelen = dataoff + datamax;
|
||||
ssize_t r;
|
||||
int eof_flags;
|
||||
uint8_t flags;
|
||||
r = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, framelen);
|
||||
|
@ -3949,7 +4011,7 @@ ssize_t nghttp2_session_pack_data(nghttp2_session *session,
|
|||
}
|
||||
eof_flags = 0;
|
||||
r = frame->data_prd.read_callback
|
||||
(session, frame->hd.stream_id, (*buf_ptr)+8, datamax,
|
||||
(session, frame->hd.stream_id, (*buf_ptr) + dataoff, datamax,
|
||||
&eof_flags, &frame->data_prd.source, session->user_data);
|
||||
if(r == NGHTTP2_ERR_DEFERRED || r == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
|
||||
return r;
|
||||
|
@ -3957,19 +4019,56 @@ ssize_t nghttp2_session_pack_data(nghttp2_session *session,
|
|||
/* This is the error code when callback is failed. */
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
frame->hd.length = r;
|
||||
memset(*buf_ptr, 0, NGHTTP2_FRAME_HEAD_LENGTH);
|
||||
nghttp2_put_uint16be(&(*buf_ptr)[0], r);
|
||||
|
||||
/* Clear flags, because this may contain previous flags of previous
|
||||
DATA */
|
||||
frame->hd.flags &= ~(NGHTTP2_FLAG_PAD_HIGH | NGHTTP2_FLAG_PAD_LOW);
|
||||
flags = 0;
|
||||
|
||||
if((session->opt_flags & NGHTTP2_OPTMASK_NO_DATA_PADDING) == 0 &&
|
||||
r > 0 && (size_t)r < datamax) {
|
||||
const size_t align = session->data_pad_alignment;
|
||||
size_t nextlen = nghttp2_min((r + align - 1) / align * align, datamax);
|
||||
size_t padlen = nextlen - r;
|
||||
size_t trail_padlen = 0;
|
||||
if(padlen > 257) {
|
||||
headoff = 0;
|
||||
trail_padlen = padlen - 2;
|
||||
flags |= NGHTTP2_FLAG_PAD_HIGH | NGHTTP2_FLAG_PAD_LOW;
|
||||
(*buf_ptr)[NGHTTP2_FRAME_HEAD_LENGTH] = trail_padlen >> 8;
|
||||
(*buf_ptr)[NGHTTP2_FRAME_HEAD_LENGTH + 1] = trail_padlen & 0xff;
|
||||
} else if(padlen > 0) {
|
||||
headoff = 1;
|
||||
trail_padlen = padlen - 1;
|
||||
flags |= NGHTTP2_FLAG_PAD_LOW;
|
||||
(*buf_ptr)[NGHTTP2_FRAME_HEAD_LENGTH + 1] = trail_padlen;
|
||||
}
|
||||
frame->padlen = padlen;
|
||||
memset((*buf_ptr) + dataoff + r, 0, trail_padlen);
|
||||
frame->hd.length = nextlen;
|
||||
} else {
|
||||
frame->padlen = 0;
|
||||
frame->hd.length = r;
|
||||
}
|
||||
|
||||
/* Set PAD flags so that we can supply frame to the callback with
|
||||
the correct flags */
|
||||
frame->hd.flags |= flags;
|
||||
|
||||
memset(*buf_ptr + headoff, 0, NGHTTP2_FRAME_HEAD_LENGTH);
|
||||
nghttp2_put_uint16be(&(*buf_ptr)[headoff], frame->hd.length);
|
||||
|
||||
if(eof_flags) {
|
||||
frame->eof = 1;
|
||||
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
flags |= NGHTTP2_FLAG_END_STREAM;
|
||||
}
|
||||
}
|
||||
(*buf_ptr)[3] = flags;
|
||||
nghttp2_put_uint32be(&(*buf_ptr)[4], frame->hd.stream_id);
|
||||
return r+8;
|
||||
(*buf_ptr)[headoff + 3] = flags;
|
||||
nghttp2_put_uint32be(&(*buf_ptr)[headoff + 4], frame->hd.stream_id);
|
||||
*bufoff_ptr = headoff;
|
||||
|
||||
return frame->hd.length + NGHTTP2_FRAME_HEAD_LENGTH + headoff;
|
||||
}
|
||||
|
||||
void* nghttp2_session_get_stream_user_data(nghttp2_session *session,
|
||||
|
|
|
@ -44,7 +44,10 @@
|
|||
*/
|
||||
typedef enum {
|
||||
NGHTTP2_OPTMASK_NO_AUTO_STREAM_WINDOW_UPDATE = 1 << 0,
|
||||
NGHTTP2_OPTMASK_NO_AUTO_CONNECTION_WINDOW_UPDATE = 1 << 1
|
||||
NGHTTP2_OPTMASK_NO_AUTO_CONNECTION_WINDOW_UPDATE = 1 << 1,
|
||||
/* Option to disable DATA frame padding, which is currently hidden
|
||||
from outside, but provided for ease of testing */
|
||||
NGHTTP2_OPTMASK_NO_DATA_PADDING = 1 << 2,
|
||||
} nghttp2_optmask;
|
||||
|
||||
typedef struct {
|
||||
|
@ -149,6 +152,8 @@ struct nghttp2_session {
|
|||
size_t num_incoming_streams;
|
||||
/* The number of bytes allocated for nvbuf */
|
||||
size_t nvbuflen;
|
||||
/* DATA padding alignemnt. See NGHTTP2_OPT_DATA_PAD_ALIGNMENT. */
|
||||
size_t data_pad_alignment;
|
||||
/* Next Stream ID. Made unsigned int to detect >= (1 << 31). */
|
||||
uint32_t next_stream_id;
|
||||
/* The largest stream ID received so far */
|
||||
|
@ -508,9 +513,11 @@ nghttp2_stream* nghttp2_session_get_stream(nghttp2_session *session,
|
|||
* Packs DATA frame |frame| in wire frame format and stores it in
|
||||
* |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr|
|
||||
* length. This function expands |*buf_ptr| as necessary to store
|
||||
* given |frame|. It packs header in first 8 bytes. Remaining bytes
|
||||
* are the DATA apyload and are filled using |frame->data_prd|. The
|
||||
* length of payload is at most |datamax| bytes.
|
||||
* given |frame|. It packs header in first 8 bytes starting
|
||||
* |*bufoff_ptr| offset. The |*bufoff_ptr| is calculated based on
|
||||
* usage of padding. Remaining bytes are the DATA apyload and are
|
||||
* filled using |frame->data_prd|. The length of payload is at most
|
||||
* |datamax| bytes.
|
||||
*
|
||||
* This function returns the size of packed frame if it succeeds, or
|
||||
* one of the following negative error codes:
|
||||
|
@ -526,6 +533,7 @@ nghttp2_stream* nghttp2_session_get_stream(nghttp2_session *session,
|
|||
*/
|
||||
ssize_t nghttp2_session_pack_data(nghttp2_session *session,
|
||||
uint8_t **buf_ptr, size_t *buflen_ptr,
|
||||
size_t *bufoff_ptr,
|
||||
size_t datamax,
|
||||
nghttp2_private_data *frame);
|
||||
|
||||
|
|
|
@ -66,6 +66,7 @@ const std::string NGHTTPD_SERVER = "nghttpd nghttp2/" NGHTTP2_VERSION;
|
|||
Config::Config()
|
||||
: data_ptr(nullptr),
|
||||
output_upper_thres(1024*1024),
|
||||
data_pad_alignment(NGHTTP2_DATA_PAD_ALIGNMENT),
|
||||
header_table_size(-1),
|
||||
port(0),
|
||||
verbose(false),
|
||||
|
@ -361,8 +362,14 @@ int Http2Handler::on_connect()
|
|||
{
|
||||
int r;
|
||||
nghttp2_session_callbacks callbacks;
|
||||
nghttp2_opt_set opt_set;
|
||||
|
||||
memset(&opt_set, 0, sizeof(opt_set));
|
||||
opt_set.data_pad_alignment = sessions_->get_config()->data_pad_alignment;
|
||||
|
||||
fill_callback(callbacks, sessions_->get_config());
|
||||
r = nghttp2_session_server_new(&session_, &callbacks, this);
|
||||
r = nghttp2_session_server_new2(&session_, &callbacks, this,
|
||||
NGHTTP2_OPT_DATA_PAD_ALIGNMENT, &opt_set);
|
||||
if(r != 0) {
|
||||
return r;
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ struct Config {
|
|||
std::string cert_file;
|
||||
void *data_ptr;
|
||||
size_t output_upper_thres;
|
||||
size_t data_pad_alignment;
|
||||
ssize_t header_table_size;
|
||||
uint16_t port;
|
||||
bool verbose;
|
||||
|
|
|
@ -77,6 +77,8 @@ const char* strstatus(nghttp2_error_code error_code)
|
|||
return "CONNECT_ERROR";
|
||||
case NGHTTP2_ENHANCE_YOUR_CALM:
|
||||
return "ENHANCE_YOUR_CALM";
|
||||
case NGHTTP2_INADEQUATE_SECURITY:
|
||||
return "INADEQUATE_SECURITY";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
@ -201,11 +203,35 @@ void print_flags(const nghttp2_frame_hd& hd)
|
|||
if(hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
s += "END_STREAM";
|
||||
}
|
||||
if(hd.flags & NGHTTP2_FLAG_END_SEGMENT) {
|
||||
if(!s.empty()) {
|
||||
s += " | ";
|
||||
}
|
||||
s += "END_SEGMENT";
|
||||
}
|
||||
if(hd.flags & NGHTTP2_FLAG_PAD_LOW) {
|
||||
if(!s.empty()) {
|
||||
s += " | ";
|
||||
}
|
||||
s += "PAD_LOW";
|
||||
}
|
||||
if(hd.flags & NGHTTP2_FLAG_PAD_HIGH) {
|
||||
if(!s.empty()) {
|
||||
s += " | ";
|
||||
}
|
||||
s += "PAD_HIGH";
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_HEADERS:
|
||||
if(hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
s += "END_STREAM";
|
||||
}
|
||||
if(hd.flags & NGHTTP2_FLAG_END_SEGMENT) {
|
||||
if(!s.empty()) {
|
||||
s += " | ";
|
||||
}
|
||||
s += "END_SEGMENT";
|
||||
}
|
||||
if(hd.flags & NGHTTP2_FLAG_END_HEADERS) {
|
||||
if(!s.empty()) {
|
||||
s += " | ";
|
||||
|
@ -265,6 +291,10 @@ void print_frame(print_type ptype, const nghttp2_frame *frame)
|
|||
}
|
||||
switch(frame->hd.type) {
|
||||
case NGHTTP2_DATA:
|
||||
if(frame->hd.flags & (NGHTTP2_FLAG_PAD_HIGH | NGHTTP2_FLAG_PAD_LOW)) {
|
||||
print_frame_attr_indent();
|
||||
printf("(padlen=%zu)\n", frame->data.padlen);
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_HEADERS:
|
||||
if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
|
||||
|
|
|
@ -82,6 +82,7 @@ struct Config {
|
|||
std::string keyfile;
|
||||
std::string datafile;
|
||||
size_t output_upper_thres;
|
||||
size_t data_pad_alignment;
|
||||
ssize_t peer_max_concurrent_streams;
|
||||
ssize_t header_table_size;
|
||||
int32_t pri;
|
||||
|
@ -98,6 +99,7 @@ struct Config {
|
|||
bool upgrade;
|
||||
Config()
|
||||
: output_upper_thres(1024*1024),
|
||||
data_pad_alignment(NGHTTP2_DATA_PAD_ALIGNMENT),
|
||||
peer_max_concurrent_streams(NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS),
|
||||
header_table_size(-1),
|
||||
pri(NGHTTP2_PRI_DEFAULT),
|
||||
|
@ -712,8 +714,10 @@ struct HttpClient {
|
|||
}
|
||||
nghttp2_opt_set opt_set;
|
||||
opt_set.peer_max_concurrent_streams = config.peer_max_concurrent_streams;
|
||||
opt_set.data_pad_alignment = config.data_pad_alignment;
|
||||
rv = nghttp2_session_client_new2(&session, callbacks, this,
|
||||
NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS,
|
||||
NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS |
|
||||
NGHTTP2_OPT_DATA_PAD_ALIGNMENT,
|
||||
&opt_set);
|
||||
if(rv != 0) {
|
||||
return -1;
|
||||
|
@ -1637,7 +1641,7 @@ void print_usage(std::ostream& out)
|
|||
{
|
||||
out << "Usage: nghttp [-Oansuv] [-t <SECONDS>] [-w <WINDOW_BITS>] [-W <WINDOW_BITS>]\n"
|
||||
<< " [--cert=<CERT>] [--key=<KEY>] [-d <FILE>] [-m <N>]\n"
|
||||
<< " [-p <PRIORITY>] [-M <N>]\n"
|
||||
<< " [-p <PRIORITY>] [-M <N>] [-b <ALIGNMENT>]\n"
|
||||
<< " <URI>..."
|
||||
<< std::endl;
|
||||
}
|
||||
|
@ -1694,6 +1698,8 @@ void print_help(std::ostream& out)
|
|||
<< " is large enough as it is seen as unlimited.\n"
|
||||
<< " -c, --header-table-size=<N>\n"
|
||||
<< " Specify decoder header table size.\n"
|
||||
<< " -b, --data-pad=<ALIGNMENT>\n"
|
||||
<< " Alignment of DATA frame padding.\n"
|
||||
<< " --color Force colored log output.\n"
|
||||
<< std::endl;
|
||||
}
|
||||
|
@ -1721,13 +1727,14 @@ int main(int argc, char **argv)
|
|||
{"pri", required_argument, nullptr, 'p'},
|
||||
{"peer-max-concurrent-streams", required_argument, nullptr, 'M'},
|
||||
{"header-table-size", required_argument, nullptr, 'c'},
|
||||
{"data-pad", required_argument, nullptr, 'b'},
|
||||
{"cert", required_argument, &flag, 1},
|
||||
{"key", required_argument, &flag, 2},
|
||||
{"color", no_argument, &flag, 3},
|
||||
{nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
int option_index = 0;
|
||||
int c = getopt_long(argc, argv, "M:Oac:d:m:np:hH:vst:uw:W:", long_options,
|
||||
int c = getopt_long(argc, argv, "M:Oab:c:d:m:np:hH:vst:uw:W:", long_options,
|
||||
&option_index);
|
||||
char *end;
|
||||
if(c == -1) {
|
||||
|
@ -1744,6 +1751,9 @@ int main(int argc, char **argv)
|
|||
case 'h':
|
||||
print_help(std::cout);
|
||||
exit(EXIT_SUCCESS);
|
||||
case 'b':
|
||||
config.data_pad_alignment = strtol(optarg, nullptr, 10);
|
||||
break;
|
||||
case 'n':
|
||||
config.null_out = true;
|
||||
break;
|
||||
|
|
|
@ -75,7 +75,8 @@ int parse_push_config(Config& config, const char *optarg)
|
|||
namespace {
|
||||
void print_usage(std::ostream& out)
|
||||
{
|
||||
out << "Usage: nghttpd [-DVhv] [-d <PATH>] [--no-tls] <PORT> [<PRIVATE_KEY> <CERT>]"
|
||||
out << "Usage: nghttpd [-DVhpv] [-d <PATH>] [--no-tls] [-b <ALIGNMENT>]\n"
|
||||
<< " <PORT> [<PRIVATE_KEY> <CERT>]"
|
||||
<< std::endl;
|
||||
}
|
||||
} // namespace
|
||||
|
@ -114,6 +115,8 @@ void print_help(std::ostream& out)
|
|||
<< " -p/=/foo.png -p/doc=/bar.css\n"
|
||||
<< " PATH and PUSH_PATHs are relative to document\n"
|
||||
<< " root. See --htdocs option.\n"
|
||||
<< " -b, --data-pad=<ALIGNMENT>\n"
|
||||
<< " Alignment of DATA frame padding.\n"
|
||||
<< " -h, --help Print this help.\n"
|
||||
<< std::endl;
|
||||
}
|
||||
|
@ -133,12 +136,13 @@ int main(int argc, char **argv)
|
|||
{"verify-client", no_argument, nullptr, 'V'},
|
||||
{"header-table-size", required_argument, nullptr, 'c'},
|
||||
{"push", required_argument, nullptr, 'p'},
|
||||
{"data-pad", required_argument, nullptr, 'b'},
|
||||
{"no-tls", no_argument, &flag, 1},
|
||||
{"color", no_argument, &flag, 2},
|
||||
{nullptr, 0, nullptr, 0}
|
||||
};
|
||||
int option_index = 0;
|
||||
int c = getopt_long(argc, argv, "DVc:d:hp:v", long_options, &option_index);
|
||||
int c = getopt_long(argc, argv, "DVb:c:d:hp:v", long_options, &option_index);
|
||||
char *end;
|
||||
if(c == -1) {
|
||||
break;
|
||||
|
@ -150,6 +154,9 @@ int main(int argc, char **argv)
|
|||
case 'V':
|
||||
config.verify_client = true;
|
||||
break;
|
||||
case 'b':
|
||||
config.data_pad_alignment = strtol(optarg, nullptr, 10);
|
||||
break;
|
||||
case 'd':
|
||||
config.htdocs = optarg;
|
||||
break;
|
||||
|
|
|
@ -193,6 +193,8 @@ int main(int argc, char* argv[])
|
|||
test_nghttp2_session_set_option) ||
|
||||
!CU_add_test(pSuite, "session_data_backoff_by_high_pri_frame",
|
||||
test_nghttp2_session_data_backoff_by_high_pri_frame) ||
|
||||
!CU_add_test(pSuite, "session_pack_data_with_padding",
|
||||
test_nghttp2_session_pack_data_with_padding) ||
|
||||
!CU_add_test(pSuite, "pack_settings_payload",
|
||||
test_nghttp2_pack_settings_payload) ||
|
||||
!CU_add_test(pSuite, "frame_pack_headers",
|
||||
|
|
|
@ -71,6 +71,7 @@ typedef struct {
|
|||
int header_cb_called;
|
||||
int begin_headers_cb_called;
|
||||
nghttp2_nv nv;
|
||||
size_t data_chunk_len;
|
||||
} my_user_data;
|
||||
|
||||
static void scripted_data_feed_init(scripted_data_feed *df,
|
||||
|
@ -187,6 +188,7 @@ static int on_data_chunk_recv_callback(nghttp2_session *session,
|
|||
{
|
||||
my_user_data *ud = (my_user_data*)user_data;
|
||||
++ud->data_chunk_recv_cb_called;
|
||||
ud->data_chunk_len = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -3814,6 +3816,96 @@ void test_nghttp2_session_data_backoff_by_high_pri_frame(void)
|
|||
nghttp2_session_del(session);
|
||||
}
|
||||
|
||||
static void check_session_recv_data_with_padding(const uint8_t *in,
|
||||
size_t inlen,
|
||||
size_t datalen)
|
||||
{
|
||||
nghttp2_session *session;
|
||||
my_user_data ud;
|
||||
nghttp2_session_callbacks callbacks;
|
||||
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
callbacks.on_frame_recv_callback = on_frame_recv_callback;
|
||||
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
|
||||
nghttp2_session_server_new(&session, &callbacks, &ud);
|
||||
|
||||
nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE,
|
||||
NGHTTP2_PRI_DEFAULT, NGHTTP2_STREAM_OPENING,
|
||||
NULL);
|
||||
|
||||
ud.frame_recv_cb_called = 0;
|
||||
ud.data_chunk_len = 0;
|
||||
CU_ASSERT((ssize_t)inlen == nghttp2_session_mem_recv(session, in, inlen));
|
||||
|
||||
CU_ASSERT(1 == ud.frame_recv_cb_called);
|
||||
CU_ASSERT(datalen == ud.data_chunk_len);
|
||||
|
||||
nghttp2_session_del(session);
|
||||
}
|
||||
|
||||
void test_nghttp2_session_pack_data_with_padding(void)
|
||||
{
|
||||
nghttp2_session *session;
|
||||
my_user_data ud;
|
||||
nghttp2_session_callbacks callbacks;
|
||||
nghttp2_data_provider data_prd;
|
||||
nghttp2_private_data *frame;
|
||||
size_t datalen = 55;
|
||||
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
callbacks.send_callback = block_count_send_callback;
|
||||
callbacks.on_frame_send_callback = on_frame_send_callback;
|
||||
data_prd.read_callback = fixed_length_data_source_read_callback;
|
||||
|
||||
nghttp2_session_client_new(&session, &callbacks, &ud);
|
||||
session->data_pad_alignment = 512;
|
||||
|
||||
nghttp2_submit_request(session, NGHTTP2_PRI_DEFAULT, NULL, 0, &data_prd,
|
||||
NULL);
|
||||
ud.block_count = 1;
|
||||
ud.data_source_length = datalen;
|
||||
/* Sends HEADERS */
|
||||
CU_ASSERT(0 == nghttp2_session_send(session));
|
||||
CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type);
|
||||
|
||||
frame = OB_DATA(session->aob.item);
|
||||
CU_ASSERT(session->data_pad_alignment - datalen == frame->padlen);
|
||||
CU_ASSERT(frame->hd.flags & NGHTTP2_FLAG_PAD_LOW);
|
||||
CU_ASSERT(frame->hd.flags & NGHTTP2_FLAG_PAD_HIGH);
|
||||
|
||||
/* Check reception of this DATA frame */
|
||||
check_session_recv_data_with_padding
|
||||
(session->aob.framebuf + session->aob.framebufoff,
|
||||
session->aob.framebufmark - session->aob.framebufoff,
|
||||
datalen);
|
||||
|
||||
nghttp2_session_del(session);
|
||||
|
||||
/* Check without PAD_HIGH */
|
||||
nghttp2_session_client_new(&session, &callbacks, &ud);
|
||||
|
||||
nghttp2_submit_request(session, NGHTTP2_PRI_DEFAULT, NULL, 0, &data_prd,
|
||||
NULL);
|
||||
ud.block_count = 1;
|
||||
ud.data_source_length = datalen;
|
||||
/* Sends HEADERS */
|
||||
CU_ASSERT(0 == nghttp2_session_send(session));
|
||||
CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type);
|
||||
|
||||
frame = OB_DATA(session->aob.item);
|
||||
CU_ASSERT(session->data_pad_alignment - datalen == frame->padlen);
|
||||
CU_ASSERT(frame->hd.flags & NGHTTP2_FLAG_PAD_LOW);
|
||||
CU_ASSERT(0 == (frame->hd.flags & NGHTTP2_FLAG_PAD_HIGH));
|
||||
|
||||
/* Check reception of this DATA frame */
|
||||
check_session_recv_data_with_padding
|
||||
(session->aob.framebuf + session->aob.framebufoff,
|
||||
session->aob.framebufmark - session->aob.framebufoff,
|
||||
datalen);
|
||||
|
||||
nghttp2_session_del(session);
|
||||
}
|
||||
|
||||
void test_nghttp2_pack_settings_payload(void)
|
||||
{
|
||||
nghttp2_settings_entry iv[2];
|
||||
|
|
|
@ -87,6 +87,7 @@ void test_nghttp2_session_get_outbound_queue_size(void);
|
|||
void test_nghttp2_session_get_effective_local_window_size(void);
|
||||
void test_nghttp2_session_set_option(void);
|
||||
void test_nghttp2_session_data_backoff_by_high_pri_frame(void);
|
||||
void test_nghttp2_session_pack_data_with_padding(void);
|
||||
void test_nghttp2_pack_settings_payload(void);
|
||||
|
||||
#endif /* NGHTTP2_SESSION_TEST_H */
|
||||
|
|
Loading…
Reference in New Issue