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
|
#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
|
* @enum
|
||||||
*
|
*
|
||||||
|
@ -599,6 +607,11 @@ typedef struct {
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
nghttp2_frame_hd hd;
|
nghttp2_frame_hd hd;
|
||||||
|
/**
|
||||||
|
* The length of the padding in this frame. This includes PAD_HIGH
|
||||||
|
* and PAD_LOW.
|
||||||
|
*/
|
||||||
|
size_t padlen;
|
||||||
} nghttp2_data;
|
} nghttp2_data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1309,7 +1322,15 @@ typedef enum {
|
||||||
* will be overwritten if the local endpoint receives
|
* will be overwritten if the local endpoint receives
|
||||||
* SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint.
|
* 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;
|
} nghttp2_opt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1330,6 +1351,10 @@ typedef struct {
|
||||||
* :enum:`NGHTTP2_OPT_NO_AUTO_CONNECTION_WINDOW_UPDATE`
|
* :enum:`NGHTTP2_OPT_NO_AUTO_CONNECTION_WINDOW_UPDATE`
|
||||||
*/
|
*/
|
||||||
uint8_t 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;
|
} 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)
|
void nghttp2_frame_data_init(nghttp2_data *frame, nghttp2_private_data *pdata)
|
||||||
{
|
{
|
||||||
frame->hd = pdata->hd;
|
frame->hd = pdata->hd;
|
||||||
|
frame->padlen = pdata->padlen;
|
||||||
/* flags may have NGHTTP2_FLAG_END_STREAM even if the sent chunk
|
/* flags may have NGHTTP2_FLAG_END_STREAM even if the sent chunk
|
||||||
is not the end of the stream */
|
is not the end of the stream */
|
||||||
if(!pdata->eof) {
|
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,
|
void nghttp2_frame_private_data_init(nghttp2_private_data *frame,
|
||||||
uint8_t flags,
|
uint8_t flags,
|
||||||
int32_t stream_id,
|
int32_t stream_id,
|
||||||
|
|
|
@ -71,6 +71,11 @@ typedef struct {
|
||||||
* The data to be sent for this DATA frame.
|
* The data to be sent for this DATA frame.
|
||||||
*/
|
*/
|
||||||
nghttp2_data_provider data_prd;
|
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
|
* 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
|
* |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);
|
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,
|
void nghttp2_frame_private_data_init(nghttp2_private_data *frame,
|
||||||
uint8_t flags,
|
uint8_t flags,
|
||||||
int32_t stream_id,
|
int32_t stream_id,
|
||||||
|
|
|
@ -223,6 +223,12 @@ static int nghttp2_session_new(nghttp2_session **session_ptr,
|
||||||
(*session_ptr)->opt_flags |=
|
(*session_ptr)->opt_flags |=
|
||||||
NGHTTP2_OPTMASK_NO_AUTO_CONNECTION_WINDOW_UPDATE;
|
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)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
|
||||||
(*session_ptr)->recv_window_size = 0;
|
(*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,
|
framebuflen = nghttp2_session_pack_data(session,
|
||||||
&session->aob.framebuf,
|
&session->aob.framebuf,
|
||||||
&session->aob.framebufmax,
|
&session->aob.framebufmax,
|
||||||
|
&session->aob.framebufoff,
|
||||||
next_readmax,
|
next_readmax,
|
||||||
data_frame);
|
data_frame);
|
||||||
if(framebuflen == NGHTTP2_ERR_DEFERRED) {
|
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) {
|
} else if(item->frame_cat == NGHTTP2_CAT_DATA) {
|
||||||
nghttp2_private_data *data_frame;
|
nghttp2_private_data *data_frame;
|
||||||
nghttp2_outbound_item* next_item;
|
nghttp2_outbound_item* next_item;
|
||||||
|
nghttp2_stream *stream;
|
||||||
|
size_t effective_payloadlen;
|
||||||
|
|
||||||
data_frame = nghttp2_outbound_item_get_data_frame(session->aob.item);
|
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) {
|
if(session->callbacks.on_frame_send_callback) {
|
||||||
nghttp2_frame public_data_frame;
|
nghttp2_frame public_data_frame;
|
||||||
nghttp2_frame_data_init(&public_data_frame.data, data_frame);
|
nghttp2_frame_data_init(&public_data_frame.data, data_frame);
|
||||||
|
@ -1599,15 +1618,13 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session)
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(data_frame->eof && (data_frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
|
|
||||||
nghttp2_stream *stream =
|
if(stream && data_frame->eof &&
|
||||||
nghttp2_session_get_stream(session, data_frame->hd.stream_id);
|
(data_frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
|
||||||
if(stream) {
|
nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
|
||||||
nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
|
rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
|
||||||
rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
|
if(nghttp2_is_fatal(rv)) {
|
||||||
if(nghttp2_is_fatal(rv)) {
|
return rv;
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* If session is closed or RST_STREAM was queued, we won't send
|
/* If session is closed or RST_STREAM was queued, we won't send
|
||||||
|
@ -1618,16 +1635,14 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session)
|
||||||
nghttp2_active_outbound_item_reset(&session->aob);
|
nghttp2_active_outbound_item_reset(&session->aob);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
/* Assuming stream is not NULL */
|
||||||
|
assert(stream);
|
||||||
next_item = nghttp2_session_get_next_ob_item(session);
|
next_item = nghttp2_session_get_next_ob_item(session);
|
||||||
/* If priority of this stream is higher or equal to other stream
|
/* If priority of this stream is higher or equal to other stream
|
||||||
waiting at the top of the queue, we continue to send this
|
waiting at the top of the queue, we continue to send this
|
||||||
data. */
|
data. */
|
||||||
if(next_item == NULL || session->aob.item->pri < next_item->pri) {
|
if(next_item == NULL || session->aob.item->pri < next_item->pri) {
|
||||||
size_t next_readmax;
|
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);
|
next_readmax = nghttp2_session_next_data_read(session, stream);
|
||||||
if(next_readmax == 0) {
|
if(next_readmax == 0) {
|
||||||
nghttp2_stream_defer_data(stream, session->aob.item,
|
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,
|
rv = nghttp2_session_pack_data(session,
|
||||||
&session->aob.framebuf,
|
&session->aob.framebuf,
|
||||||
&session->aob.framebufmax,
|
&session->aob.framebufmax,
|
||||||
|
&session->aob.framebufoff,
|
||||||
next_readmax,
|
next_readmax,
|
||||||
data_frame);
|
data_frame);
|
||||||
if(nghttp2_is_fatal(rv)) {
|
if(nghttp2_is_fatal(rv)) {
|
||||||
|
@ -1666,7 +1682,6 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session)
|
||||||
}
|
}
|
||||||
assert(rv >= 0);
|
assert(rv >= 0);
|
||||||
session->aob.framebuflen = session->aob.framebufmark = rv;
|
session->aob.framebuflen = session->aob.framebufmark = rv;
|
||||||
session->aob.framebufoff = 0;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/* Update seq to interleave other streams with the same
|
/* 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) {
|
if(item->frame_cat == NGHTTP2_CAT_CTRL) {
|
||||||
nghttp2_frame *frame = nghttp2_outbound_item_get_ctrl_frame(item);
|
nghttp2_frame *frame = nghttp2_outbound_item_get_ctrl_frame(item);
|
||||||
session->aob.framebufmark =
|
session->aob.framebufmark =
|
||||||
|
session->aob.framebufoff +
|
||||||
frame->hd.length + NGHTTP2_FRAME_HEAD_LENGTH;
|
frame->hd.length + NGHTTP2_FRAME_HEAD_LENGTH;
|
||||||
r = session_call_before_frame_send(session, frame);
|
r = session_call_before_frame_send(session, frame);
|
||||||
if(nghttp2_is_fatal(r)) {
|
if(nghttp2_is_fatal(r)) {
|
||||||
|
@ -1748,10 +1764,13 @@ int nghttp2_session_send(nghttp2_session *session)
|
||||||
} else {
|
} else {
|
||||||
nghttp2_private_data *frame;
|
nghttp2_private_data *frame;
|
||||||
frame = nghttp2_outbound_item_get_data_frame(session->aob.item);
|
frame = nghttp2_outbound_item_get_data_frame(session->aob.item);
|
||||||
|
/* session->aob.framebufmark = session->aob.framebuflen; */
|
||||||
session->aob.framebufmark =
|
session->aob.framebufmark =
|
||||||
|
session->aob.framebufoff +
|
||||||
frame->hd.length + NGHTTP2_FRAME_HEAD_LENGTH;
|
frame->hd.length + NGHTTP2_FRAME_HEAD_LENGTH;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data = session->aob.framebuf + session->aob.framebufoff;
|
data = session->aob.framebuf + session->aob.framebufoff;
|
||||||
datalen = session->aob.framebufmark - session->aob.framebufoff;
|
datalen = session->aob.framebufmark - session->aob.framebufoff;
|
||||||
sentlen = session->callbacks.send_callback(session, data, datalen, 0,
|
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;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
}
|
}
|
||||||
} else {
|
} 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;
|
session->aob.framebufoff += sentlen;
|
||||||
if(session->aob.framebufoff == session->aob.framebufmark) {
|
if(session->aob.framebufoff == session->aob.framebufmark) {
|
||||||
/* Frame has completely sent */
|
/* Frame has completely sent */
|
||||||
|
@ -3338,6 +3340,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
||||||
switch(iframe->frame.hd.type) {
|
switch(iframe->frame.hd.type) {
|
||||||
case NGHTTP2_DATA: {
|
case NGHTTP2_DATA: {
|
||||||
DEBUGF(fprintf(stderr, "DATA\n"));
|
DEBUGF(fprintf(stderr, "DATA\n"));
|
||||||
|
iframe->frame.data.padlen = 0;
|
||||||
/* Check stream is open. If it is not open or closing,
|
/* Check stream is open. If it is not open or closing,
|
||||||
ignore payload. */
|
ignore payload. */
|
||||||
busy = 1;
|
busy = 1;
|
||||||
|
@ -3351,6 +3354,25 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
||||||
if(nghttp2_is_fatal(rv)) {
|
if(nghttp2_is_fatal(rv)) {
|
||||||
return 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;
|
iframe->state = NGHTTP2_IB_READ_DATA;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -3454,7 +3476,26 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
||||||
}
|
}
|
||||||
switch(iframe->frame.hd.type) {
|
switch(iframe->frame.hd.type) {
|
||||||
case NGHTTP2_DATA:
|
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;
|
break;
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
rv = session_process_headers_frame(session);
|
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",
|
DEBUGF(fprintf(stderr, "readlen=%zu, payloadleft=%zu\n",
|
||||||
readlen, iframe->payloadleft));
|
readlen, iframe->payloadleft));
|
||||||
if(readlen > 0) {
|
if(readlen > 0) {
|
||||||
|
size_t data_readlen = readlen;
|
||||||
rv = nghttp2_session_update_recv_connection_window_size
|
rv = nghttp2_session_update_recv_connection_window_size
|
||||||
(session, readlen);
|
(session, readlen);
|
||||||
if(nghttp2_is_fatal(rv)) {
|
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
|
rv = session->callbacks.on_data_chunk_recv_callback
|
||||||
(session,
|
(session,
|
||||||
iframe->frame.hd.flags,
|
iframe->frame.hd.flags,
|
||||||
iframe->frame.hd.stream_id,
|
iframe->frame.hd.stream_id,
|
||||||
in - readlen,
|
in - readlen,
|
||||||
readlen,
|
data_readlen,
|
||||||
session->user_data);
|
session->user_data);
|
||||||
if(rv == NGHTTP2_ERR_PAUSE) {
|
if(rv == NGHTTP2_ERR_PAUSE) {
|
||||||
/* Set type to NGHTTP2_DATA, so that we can see what was
|
/* 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,
|
ssize_t nghttp2_session_pack_data(nghttp2_session *session,
|
||||||
uint8_t **buf_ptr, size_t *buflen_ptr,
|
uint8_t **buf_ptr, size_t *buflen_ptr,
|
||||||
|
size_t *bufoff_ptr,
|
||||||
size_t datamax,
|
size_t datamax,
|
||||||
nghttp2_private_data *frame)
|
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;
|
int eof_flags;
|
||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
r = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, framelen);
|
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;
|
eof_flags = 0;
|
||||||
r = frame->data_prd.read_callback
|
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);
|
&eof_flags, &frame->data_prd.source, session->user_data);
|
||||||
if(r == NGHTTP2_ERR_DEFERRED || r == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
|
if(r == NGHTTP2_ERR_DEFERRED || r == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
|
||||||
return r;
|
return r;
|
||||||
|
@ -3957,19 +4019,56 @@ ssize_t nghttp2_session_pack_data(nghttp2_session *session,
|
||||||
/* This is the error code when callback is failed. */
|
/* This is the error code when callback is failed. */
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
}
|
}
|
||||||
frame->hd.length = r;
|
|
||||||
memset(*buf_ptr, 0, NGHTTP2_FRAME_HEAD_LENGTH);
|
/* Clear flags, because this may contain previous flags of previous
|
||||||
nghttp2_put_uint16be(&(*buf_ptr)[0], r);
|
DATA */
|
||||||
|
frame->hd.flags &= ~(NGHTTP2_FLAG_PAD_HIGH | NGHTTP2_FLAG_PAD_LOW);
|
||||||
flags = 0;
|
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) {
|
if(eof_flags) {
|
||||||
frame->eof = 1;
|
frame->eof = 1;
|
||||||
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||||
flags |= NGHTTP2_FLAG_END_STREAM;
|
flags |= NGHTTP2_FLAG_END_STREAM;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(*buf_ptr)[3] = flags;
|
(*buf_ptr)[headoff + 3] = flags;
|
||||||
nghttp2_put_uint32be(&(*buf_ptr)[4], frame->hd.stream_id);
|
nghttp2_put_uint32be(&(*buf_ptr)[headoff + 4], frame->hd.stream_id);
|
||||||
return r+8;
|
*bufoff_ptr = headoff;
|
||||||
|
|
||||||
|
return frame->hd.length + NGHTTP2_FRAME_HEAD_LENGTH + headoff;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* nghttp2_session_get_stream_user_data(nghttp2_session *session,
|
void* nghttp2_session_get_stream_user_data(nghttp2_session *session,
|
||||||
|
|
|
@ -44,7 +44,10 @@
|
||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
NGHTTP2_OPTMASK_NO_AUTO_STREAM_WINDOW_UPDATE = 1 << 0,
|
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;
|
} nghttp2_optmask;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -149,6 +152,8 @@ struct nghttp2_session {
|
||||||
size_t num_incoming_streams;
|
size_t num_incoming_streams;
|
||||||
/* The number of bytes allocated for nvbuf */
|
/* The number of bytes allocated for nvbuf */
|
||||||
size_t nvbuflen;
|
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). */
|
/* Next Stream ID. Made unsigned int to detect >= (1 << 31). */
|
||||||
uint32_t next_stream_id;
|
uint32_t next_stream_id;
|
||||||
/* The largest stream ID received so far */
|
/* 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
|
* Packs DATA frame |frame| in wire frame format and stores it in
|
||||||
* |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr|
|
* |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr|
|
||||||
* length. This function expands |*buf_ptr| as necessary to store
|
* length. This function expands |*buf_ptr| as necessary to store
|
||||||
* given |frame|. It packs header in first 8 bytes. Remaining bytes
|
* given |frame|. It packs header in first 8 bytes starting
|
||||||
* are the DATA apyload and are filled using |frame->data_prd|. The
|
* |*bufoff_ptr| offset. The |*bufoff_ptr| is calculated based on
|
||||||
* length of payload is at most |datamax| bytes.
|
* 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
|
* This function returns the size of packed frame if it succeeds, or
|
||||||
* one of the following negative error codes:
|
* 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,
|
ssize_t nghttp2_session_pack_data(nghttp2_session *session,
|
||||||
uint8_t **buf_ptr, size_t *buflen_ptr,
|
uint8_t **buf_ptr, size_t *buflen_ptr,
|
||||||
|
size_t *bufoff_ptr,
|
||||||
size_t datamax,
|
size_t datamax,
|
||||||
nghttp2_private_data *frame);
|
nghttp2_private_data *frame);
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,7 @@ const std::string NGHTTPD_SERVER = "nghttpd nghttp2/" NGHTTP2_VERSION;
|
||||||
Config::Config()
|
Config::Config()
|
||||||
: data_ptr(nullptr),
|
: data_ptr(nullptr),
|
||||||
output_upper_thres(1024*1024),
|
output_upper_thres(1024*1024),
|
||||||
|
data_pad_alignment(NGHTTP2_DATA_PAD_ALIGNMENT),
|
||||||
header_table_size(-1),
|
header_table_size(-1),
|
||||||
port(0),
|
port(0),
|
||||||
verbose(false),
|
verbose(false),
|
||||||
|
@ -361,8 +362,14 @@ int Http2Handler::on_connect()
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
nghttp2_session_callbacks callbacks;
|
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());
|
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) {
|
if(r != 0) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ struct Config {
|
||||||
std::string cert_file;
|
std::string cert_file;
|
||||||
void *data_ptr;
|
void *data_ptr;
|
||||||
size_t output_upper_thres;
|
size_t output_upper_thres;
|
||||||
|
size_t data_pad_alignment;
|
||||||
ssize_t header_table_size;
|
ssize_t header_table_size;
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
bool verbose;
|
bool verbose;
|
||||||
|
|
|
@ -77,6 +77,8 @@ const char* strstatus(nghttp2_error_code error_code)
|
||||||
return "CONNECT_ERROR";
|
return "CONNECT_ERROR";
|
||||||
case NGHTTP2_ENHANCE_YOUR_CALM:
|
case NGHTTP2_ENHANCE_YOUR_CALM:
|
||||||
return "ENHANCE_YOUR_CALM";
|
return "ENHANCE_YOUR_CALM";
|
||||||
|
case NGHTTP2_INADEQUATE_SECURITY:
|
||||||
|
return "INADEQUATE_SECURITY";
|
||||||
default:
|
default:
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
|
@ -201,11 +203,35 @@ void print_flags(const nghttp2_frame_hd& hd)
|
||||||
if(hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
if(hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||||
s += "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;
|
break;
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
if(hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
if(hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||||
s += "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(hd.flags & NGHTTP2_FLAG_END_HEADERS) {
|
||||||
if(!s.empty()) {
|
if(!s.empty()) {
|
||||||
s += " | ";
|
s += " | ";
|
||||||
|
@ -265,6 +291,10 @@ void print_frame(print_type ptype, const nghttp2_frame *frame)
|
||||||
}
|
}
|
||||||
switch(frame->hd.type) {
|
switch(frame->hd.type) {
|
||||||
case NGHTTP2_DATA:
|
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;
|
break;
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
|
if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
|
||||||
|
|
|
@ -82,6 +82,7 @@ struct Config {
|
||||||
std::string keyfile;
|
std::string keyfile;
|
||||||
std::string datafile;
|
std::string datafile;
|
||||||
size_t output_upper_thres;
|
size_t output_upper_thres;
|
||||||
|
size_t data_pad_alignment;
|
||||||
ssize_t peer_max_concurrent_streams;
|
ssize_t peer_max_concurrent_streams;
|
||||||
ssize_t header_table_size;
|
ssize_t header_table_size;
|
||||||
int32_t pri;
|
int32_t pri;
|
||||||
|
@ -98,6 +99,7 @@ struct Config {
|
||||||
bool upgrade;
|
bool upgrade;
|
||||||
Config()
|
Config()
|
||||||
: output_upper_thres(1024*1024),
|
: output_upper_thres(1024*1024),
|
||||||
|
data_pad_alignment(NGHTTP2_DATA_PAD_ALIGNMENT),
|
||||||
peer_max_concurrent_streams(NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS),
|
peer_max_concurrent_streams(NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS),
|
||||||
header_table_size(-1),
|
header_table_size(-1),
|
||||||
pri(NGHTTP2_PRI_DEFAULT),
|
pri(NGHTTP2_PRI_DEFAULT),
|
||||||
|
@ -712,8 +714,10 @@ struct HttpClient {
|
||||||
}
|
}
|
||||||
nghttp2_opt_set opt_set;
|
nghttp2_opt_set opt_set;
|
||||||
opt_set.peer_max_concurrent_streams = config.peer_max_concurrent_streams;
|
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,
|
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);
|
&opt_set);
|
||||||
if(rv != 0) {
|
if(rv != 0) {
|
||||||
return -1;
|
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"
|
out << "Usage: nghttp [-Oansuv] [-t <SECONDS>] [-w <WINDOW_BITS>] [-W <WINDOW_BITS>]\n"
|
||||||
<< " [--cert=<CERT>] [--key=<KEY>] [-d <FILE>] [-m <N>]\n"
|
<< " [--cert=<CERT>] [--key=<KEY>] [-d <FILE>] [-m <N>]\n"
|
||||||
<< " [-p <PRIORITY>] [-M <N>]\n"
|
<< " [-p <PRIORITY>] [-M <N>] [-b <ALIGNMENT>]\n"
|
||||||
<< " <URI>..."
|
<< " <URI>..."
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
|
@ -1694,6 +1698,8 @@ void print_help(std::ostream& out)
|
||||||
<< " is large enough as it is seen as unlimited.\n"
|
<< " is large enough as it is seen as unlimited.\n"
|
||||||
<< " -c, --header-table-size=<N>\n"
|
<< " -c, --header-table-size=<N>\n"
|
||||||
<< " Specify decoder header table size.\n"
|
<< " Specify decoder header table size.\n"
|
||||||
|
<< " -b, --data-pad=<ALIGNMENT>\n"
|
||||||
|
<< " Alignment of DATA frame padding.\n"
|
||||||
<< " --color Force colored log output.\n"
|
<< " --color Force colored log output.\n"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
|
@ -1721,13 +1727,14 @@ int main(int argc, char **argv)
|
||||||
{"pri", required_argument, nullptr, 'p'},
|
{"pri", required_argument, nullptr, 'p'},
|
||||||
{"peer-max-concurrent-streams", required_argument, nullptr, 'M'},
|
{"peer-max-concurrent-streams", required_argument, nullptr, 'M'},
|
||||||
{"header-table-size", required_argument, nullptr, 'c'},
|
{"header-table-size", required_argument, nullptr, 'c'},
|
||||||
|
{"data-pad", required_argument, nullptr, 'b'},
|
||||||
{"cert", required_argument, &flag, 1},
|
{"cert", required_argument, &flag, 1},
|
||||||
{"key", required_argument, &flag, 2},
|
{"key", required_argument, &flag, 2},
|
||||||
{"color", no_argument, &flag, 3},
|
{"color", no_argument, &flag, 3},
|
||||||
{nullptr, 0, nullptr, 0 }
|
{nullptr, 0, nullptr, 0 }
|
||||||
};
|
};
|
||||||
int option_index = 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);
|
&option_index);
|
||||||
char *end;
|
char *end;
|
||||||
if(c == -1) {
|
if(c == -1) {
|
||||||
|
@ -1744,6 +1751,9 @@ int main(int argc, char **argv)
|
||||||
case 'h':
|
case 'h':
|
||||||
print_help(std::cout);
|
print_help(std::cout);
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
|
case 'b':
|
||||||
|
config.data_pad_alignment = strtol(optarg, nullptr, 10);
|
||||||
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
config.null_out = true;
|
config.null_out = true;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -75,7 +75,8 @@ int parse_push_config(Config& config, const char *optarg)
|
||||||
namespace {
|
namespace {
|
||||||
void print_usage(std::ostream& out)
|
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;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -114,6 +115,8 @@ void print_help(std::ostream& out)
|
||||||
<< " -p/=/foo.png -p/doc=/bar.css\n"
|
<< " -p/=/foo.png -p/doc=/bar.css\n"
|
||||||
<< " PATH and PUSH_PATHs are relative to document\n"
|
<< " PATH and PUSH_PATHs are relative to document\n"
|
||||||
<< " root. See --htdocs option.\n"
|
<< " root. See --htdocs option.\n"
|
||||||
|
<< " -b, --data-pad=<ALIGNMENT>\n"
|
||||||
|
<< " Alignment of DATA frame padding.\n"
|
||||||
<< " -h, --help Print this help.\n"
|
<< " -h, --help Print this help.\n"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
|
@ -133,12 +136,13 @@ int main(int argc, char **argv)
|
||||||
{"verify-client", no_argument, nullptr, 'V'},
|
{"verify-client", no_argument, nullptr, 'V'},
|
||||||
{"header-table-size", required_argument, nullptr, 'c'},
|
{"header-table-size", required_argument, nullptr, 'c'},
|
||||||
{"push", required_argument, nullptr, 'p'},
|
{"push", required_argument, nullptr, 'p'},
|
||||||
|
{"data-pad", required_argument, nullptr, 'b'},
|
||||||
{"no-tls", no_argument, &flag, 1},
|
{"no-tls", no_argument, &flag, 1},
|
||||||
{"color", no_argument, &flag, 2},
|
{"color", no_argument, &flag, 2},
|
||||||
{nullptr, 0, nullptr, 0}
|
{nullptr, 0, nullptr, 0}
|
||||||
};
|
};
|
||||||
int option_index = 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;
|
char *end;
|
||||||
if(c == -1) {
|
if(c == -1) {
|
||||||
break;
|
break;
|
||||||
|
@ -150,6 +154,9 @@ int main(int argc, char **argv)
|
||||||
case 'V':
|
case 'V':
|
||||||
config.verify_client = true;
|
config.verify_client = true;
|
||||||
break;
|
break;
|
||||||
|
case 'b':
|
||||||
|
config.data_pad_alignment = strtol(optarg, nullptr, 10);
|
||||||
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
config.htdocs = optarg;
|
config.htdocs = optarg;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -193,6 +193,8 @@ int main(int argc, char* argv[])
|
||||||
test_nghttp2_session_set_option) ||
|
test_nghttp2_session_set_option) ||
|
||||||
!CU_add_test(pSuite, "session_data_backoff_by_high_pri_frame",
|
!CU_add_test(pSuite, "session_data_backoff_by_high_pri_frame",
|
||||||
test_nghttp2_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",
|
!CU_add_test(pSuite, "pack_settings_payload",
|
||||||
test_nghttp2_pack_settings_payload) ||
|
test_nghttp2_pack_settings_payload) ||
|
||||||
!CU_add_test(pSuite, "frame_pack_headers",
|
!CU_add_test(pSuite, "frame_pack_headers",
|
||||||
|
|
|
@ -71,6 +71,7 @@ typedef struct {
|
||||||
int header_cb_called;
|
int header_cb_called;
|
||||||
int begin_headers_cb_called;
|
int begin_headers_cb_called;
|
||||||
nghttp2_nv nv;
|
nghttp2_nv nv;
|
||||||
|
size_t data_chunk_len;
|
||||||
} my_user_data;
|
} my_user_data;
|
||||||
|
|
||||||
static void scripted_data_feed_init(scripted_data_feed *df,
|
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;
|
my_user_data *ud = (my_user_data*)user_data;
|
||||||
++ud->data_chunk_recv_cb_called;
|
++ud->data_chunk_recv_cb_called;
|
||||||
|
ud->data_chunk_len = len;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3814,6 +3816,96 @@ void test_nghttp2_session_data_backoff_by_high_pri_frame(void)
|
||||||
nghttp2_session_del(session);
|
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)
|
void test_nghttp2_pack_settings_payload(void)
|
||||||
{
|
{
|
||||||
nghttp2_settings_entry iv[2];
|
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_get_effective_local_window_size(void);
|
||||||
void test_nghttp2_session_set_option(void);
|
void test_nghttp2_session_set_option(void);
|
||||||
void test_nghttp2_session_data_backoff_by_high_pri_frame(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);
|
void test_nghttp2_pack_settings_payload(void);
|
||||||
|
|
||||||
#endif /* NGHTTP2_SESSION_TEST_H */
|
#endif /* NGHTTP2_SESSION_TEST_H */
|
||||||
|
|
Loading…
Reference in New Issue