Merge branch 'h2-14'
This commit is contained in:
commit
a5ec5c1a1d
30
README.rst
30
README.rst
|
@ -17,10 +17,12 @@ everything yet.
|
|||
Development Status
|
||||
------------------
|
||||
|
||||
We started to implement h2-13
|
||||
(http://tools.ietf.org/html/draft-ietf-httpbis-http2-13) and the
|
||||
header compression
|
||||
(http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-08).
|
||||
We started to implement h2-14
|
||||
(http://tools.ietf.org/html/draft-ietf-httpbis-http2-14), the header
|
||||
compression
|
||||
(http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09)
|
||||
and HTTP Alternative Services
|
||||
(http://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-02).
|
||||
|
||||
The nghttp2 code base was forked from spdylay project.
|
||||
|
||||
|
@ -30,15 +32,9 @@ HTTP/2 Features Support
|
|||
Core frames handling Yes
|
||||
Dependency Tree Yes
|
||||
Large header (CONTINUATION) Yes
|
||||
ALTSVC extension Yes \*1
|
||||
ALTSVC extension Yes
|
||||
=========================== =======
|
||||
|
||||
* \*1 As described in draft-12, but reserved byte was removed. ALTSVC
|
||||
may be removed from nghttp2 public API since it is not stabilized
|
||||
yet.
|
||||
|
||||
BLOCKED frame, which once existed in h2-12, was removed in h2-13.
|
||||
|
||||
Public Test Server
|
||||
------------------
|
||||
|
||||
|
@ -47,7 +43,7 @@ implementation.
|
|||
|
||||
* https://nghttp2.org/ (TLS + NPN)
|
||||
|
||||
NPN offer ``h2-13``, ``spdy/3.1`` and ``http/1.1``.
|
||||
NPN offer ``h2-14``, ``spdy/3.1`` and ``http/1.1``.
|
||||
ALPN is currently disabled.
|
||||
|
||||
This endpoint requires TLSv1.2 and DHE or EDCHE with GCM cipher
|
||||
|
@ -186,10 +182,10 @@ output from ``nghttp`` client::
|
|||
|
||||
$ src/nghttp -nv https://nghttp2.org
|
||||
[ 0.033][NPN] server offers:
|
||||
* h2-13
|
||||
* h2-14
|
||||
* spdy/3.1
|
||||
* http/1.1
|
||||
The negotiated protocol: h2-13
|
||||
The negotiated protocol: h2-14
|
||||
[ 0.068] send SETTINGS frame <length=15, flags=0x00, stream_id=0>
|
||||
(niv=3)
|
||||
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
||||
|
@ -281,7 +277,7 @@ The HTTP Upgrade is performed like this::
|
|||
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
|
||||
[SETTINGS_INITIAL_WINDOW_SIZE(4):65535]
|
||||
[ 0.024] recv ALTSVC frame <length=43, flags=0x00, stream_id=0>
|
||||
(max-age=86400, port=443, protocol_id=h2-13, host=nghttp2.org, origin=http://nghttp2.org)
|
||||
(max-age=86400, port=443, protocol_id=h2-14, host=nghttp2.org, origin=http://nghttp2.org)
|
||||
[ 0.024] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
|
||||
; ACK
|
||||
(niv=0)
|
||||
|
@ -389,7 +385,7 @@ information. Here is sample output from ``nghttpd`` server::
|
|||
nghttpx - proxy
|
||||
+++++++++++++++
|
||||
|
||||
``nghttpx`` is a multi-threaded reverse proxy for ``h2-13``, SPDY and
|
||||
``nghttpx`` is a multi-threaded reverse proxy for ``h2-14``, SPDY and
|
||||
HTTP/1.1 and powers nghttp2.org site. It has several operation modes:
|
||||
|
||||
================== ============================ ============== =============
|
||||
|
@ -403,7 +399,7 @@ default mode HTTP/2, SPDY, HTTP/1.1 (TLS) HTTP/1.1 Reverse proxy
|
|||
================== ============================ ============== =============
|
||||
|
||||
The interesting mode at the moment is the default mode. It works like
|
||||
a reverse proxy and listens for ``h2-13``, SPDY and HTTP/1.1 and can
|
||||
a reverse proxy and listens for ``h2-14``, SPDY and HTTP/1.1 and can
|
||||
be deployed SSL/TLS terminator for existing web server.
|
||||
|
||||
The default mode, ``--http2-proxy`` and ``--http2-bridge`` modes use
|
||||
|
|
|
@ -46,5 +46,6 @@ Released Versions
|
|||
Resources
|
||||
---------
|
||||
|
||||
* http://tools.ietf.org/html/draft-ietf-httpbis-http2-13
|
||||
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-08
|
||||
* http://tools.ietf.org/html/draft-ietf-httpbis-http2-14
|
||||
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09
|
||||
* http://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-02
|
||||
|
|
|
@ -41,7 +41,7 @@ extern "C" {
|
|||
* The protocol version identification string of this library
|
||||
* supports. This identifier is used if HTTP/2 is used over TLS.
|
||||
*/
|
||||
#define NGHTTP2_PROTO_VERSION_ID "h2-13"
|
||||
#define NGHTTP2_PROTO_VERSION_ID "h2-14"
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
|
@ -56,7 +56,7 @@ extern "C" {
|
|||
* supports. This identifier is used if HTTP/2 is used over cleartext
|
||||
* TCP.
|
||||
*/
|
||||
#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "h2c-13"
|
||||
#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "h2c-14"
|
||||
|
||||
/**
|
||||
* @macro
|
||||
|
@ -476,10 +476,6 @@ typedef enum {
|
|||
* The ACK flag.
|
||||
*/
|
||||
NGHTTP2_FLAG_ACK = 0x01,
|
||||
/**
|
||||
* The END_SEGMENT flag.
|
||||
*/
|
||||
NGHTTP2_FLAG_END_SEGMENT = 0x02,
|
||||
/**
|
||||
* The PADDED flag.
|
||||
*/
|
||||
|
@ -510,7 +506,15 @@ typedef enum {
|
|||
/**
|
||||
* SETTINGS_INITIAL_WINDOW_SIZE
|
||||
*/
|
||||
NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 0x04
|
||||
NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 0x04,
|
||||
/**
|
||||
* SETTINGS_MAX_FRAME_SIZE
|
||||
*/
|
||||
NGHTTP2_SETTINGS_MAX_FRAME_SIZE = 0x05,
|
||||
/**
|
||||
* SETTINGS_MAX_HEADER_LIST_SIZE
|
||||
*/
|
||||
NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x06
|
||||
} nghttp2_settings_id;
|
||||
/* Note: If we add SETTINGS, update the capacity of
|
||||
NGHTTP2_INBOUND_NUM_IV as well */
|
||||
|
@ -724,7 +728,9 @@ typedef enum {
|
|||
NGHTTP2_HCAT_PUSH_RESPONSE = 2,
|
||||
/**
|
||||
* The HEADERS frame which does not apply for the above categories,
|
||||
* which is analogous to HEADERS in SPDY.
|
||||
* which is analogous to HEADERS in SPDY. If non-final response
|
||||
* (e.g., status 1xx) is used, final response HEADERS frame will be
|
||||
* categorized here.
|
||||
*/
|
||||
NGHTTP2_HCAT_HEADERS = 3
|
||||
} nghttp2_headers_category;
|
||||
|
@ -1505,31 +1511,17 @@ int nghttp2_option_new(nghttp2_option **option_ptr);
|
|||
*/
|
||||
void nghttp2_option_del(nghttp2_option *option);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* This option prevents the library from sending WINDOW_UPDATE for a
|
||||
* stream automatically. If this option is set to nonzero, the
|
||||
* library won't send WINDOW_UPDATE for a stream and the application
|
||||
* is responsible for sending WINDOW_UPDATE using
|
||||
* `nghttp2_submit_window_update`. By default, this option is set to
|
||||
* zero.
|
||||
*/
|
||||
void nghttp2_option_set_no_auto_stream_window_update(nghttp2_option *option,
|
||||
int val);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* This option prevents the library from sending WINDOW_UPDATE for a
|
||||
* connection automatically. If this option is set to nonzero, the
|
||||
* library won't send WINDOW_UPDATE for a connection and the
|
||||
* application is responsible for sending WINDOW_UPDATE with stream ID
|
||||
* 0 using `nghttp2_submit_window_update`. By default, this option is
|
||||
* set to zero.
|
||||
* library won't send WINDOW_UPDATE for DATA until application calls
|
||||
* `nghttp2_session_consume()` to indicate the consumed amount of
|
||||
* data. Don't use `nghttp2_submit_window_update()` for this purpose.
|
||||
* By default, this option is set to zero.
|
||||
*/
|
||||
void nghttp2_option_set_no_auto_connection_window_update
|
||||
(nghttp2_option *option, int val);
|
||||
void nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val);
|
||||
|
||||
/**
|
||||
* @function
|
||||
|
@ -2067,6 +2059,28 @@ int nghttp2_session_terminate_session2(nghttp2_session *session,
|
|||
uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session,
|
||||
nghttp2_settings_id id);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Tells the |session| that |size| bytes for a stream denoted by
|
||||
* |stream_id| were consumed by application and are ready to
|
||||
* WINDOW_UPDATE. This function is intended to be used without
|
||||
* automatic window update (see
|
||||
* `nghttp2_option_set_no_auto_window_update()`).
|
||||
*
|
||||
* 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 |stream_id| is 0.
|
||||
* :enum:`NGHTTP2_ERR_INVALID_STATE`
|
||||
* Automatic WINDOW_UPDATE is not disabled.
|
||||
*/
|
||||
int nghttp2_session_consume(nghttp2_session *session, int32_t stream_id,
|
||||
size_t size);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
|
@ -2366,8 +2380,7 @@ int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
|
|||
* Submits one or more DATA frames to the stream |stream_id|. The
|
||||
* data to be sent are provided by |data_prd|. If |flags| contains
|
||||
* :enum:`NGHTTP2_FLAG_END_STREAM`, the last DATA frame has END_STREAM
|
||||
* flag set. If |flags| contains :enum:`NGHTTP2_FLAG_END_SEGMENT`,
|
||||
* the last DATA frame has END_SEGMENT flag set.
|
||||
* flag set.
|
||||
*
|
||||
* This function does not take ownership of the |data_prd|. The
|
||||
* function copies the members of the |data_prd|.
|
||||
|
@ -2609,12 +2622,11 @@ int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags,
|
|||
* difference.
|
||||
*
|
||||
* If the |window_size_increment| is negative, the local window size
|
||||
* is decreased by -|window_size_increment|. If
|
||||
* :enum:`NGHTTP2_OPT_NO_AUTO_STREAM_WINDOW_UPDATE` (or
|
||||
* :enum:`NGHTTP2_OPT_NO_AUTO_CONNECTION_WINDOW_UPDATE` if |stream_id|
|
||||
* is 0) is not set and the library decided that the WINDOW_UPDATE
|
||||
* should be submitted, then WINDOW_UPDATE is queued with the current
|
||||
* received bytes count.
|
||||
* is decreased by -|window_size_increment|. If automatic
|
||||
* WINDOW_UPDATE is enabled
|
||||
* (`nghttp2_option_set_no_auto_window_update()`), and the library
|
||||
* decided that the WINDOW_UPDATE should be submitted, then
|
||||
* WINDOW_UPDATE is queued with the current received bytes count.
|
||||
*
|
||||
* If the |window_size_increment| is 0, the function does nothing and
|
||||
* returns 0.
|
||||
|
@ -2641,7 +2653,7 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
|
|||
*
|
||||
* Only the server can send the ALTSVC frame. If |session| is
|
||||
* initialized as client, this function fails and returns
|
||||
* :enum:`NGHTTP2_ERR_INVALID_STATE`.
|
||||
* :enum:`NGHTTP2_ERR_PROTO`.
|
||||
*
|
||||
* If the |protocol_id_len| is 0, the |protocol_id| could be ``NULL``.
|
||||
*
|
||||
|
@ -2819,18 +2831,6 @@ int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr,
|
|||
*/
|
||||
void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Sets the availability of reference set in the |deflater|. If
|
||||
* |no_refset| is nonzero, the deflater will first emit "Reference Set
|
||||
* Emptying" in the each subsequent invocation of
|
||||
* `nghttp2_hd_deflate_hd()` to clear up reference set. By default,
|
||||
* the deflater uses reference set.
|
||||
*/
|
||||
void nghttp2_hd_deflate_set_no_refset(nghttp2_hd_deflater *deflater,
|
||||
uint8_t no_refset);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
|
|
|
@ -40,21 +40,21 @@ int nghttp2_frame_is_data_frame(uint8_t *head)
|
|||
|
||||
void nghttp2_frame_pack_frame_hd(uint8_t* buf, const nghttp2_frame_hd *hd)
|
||||
{
|
||||
nghttp2_put_uint16be(&buf[0], hd->length);
|
||||
buf[2]= hd->type;
|
||||
buf[3] = hd->flags;
|
||||
nghttp2_put_uint32be(&buf[4], hd->stream_id);
|
||||
nghttp2_put_uint32be(&buf[0], (uint32_t)(hd->length << 8));
|
||||
buf[3]= hd->type;
|
||||
buf[4] = hd->flags;
|
||||
nghttp2_put_uint32be(&buf[5], hd->stream_id);
|
||||
}
|
||||
|
||||
void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t* buf)
|
||||
{
|
||||
hd->length = nghttp2_get_uint16(&buf[0]) & NGHTTP2_FRAME_LENGTH_MASK;
|
||||
hd->type = buf[2];
|
||||
hd->flags = buf[3];
|
||||
hd->stream_id = nghttp2_get_uint32(&buf[4]) & NGHTTP2_STREAM_ID_MASK;
|
||||
hd->length = nghttp2_get_uint32(&buf[0]) >> 8;
|
||||
hd->type = buf[3];
|
||||
hd->flags = buf[4];
|
||||
hd->stream_id = nghttp2_get_uint32(&buf[5]) & NGHTTP2_STREAM_ID_MASK;
|
||||
}
|
||||
|
||||
static void frame_set_hd(nghttp2_frame_hd *hd, uint16_t length,
|
||||
static void frame_set_hd(nghttp2_frame_hd *hd, size_t length,
|
||||
uint8_t type, uint8_t flags,
|
||||
int32_t stream_id)
|
||||
{
|
||||
|
@ -231,11 +231,10 @@ 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 or
|
||||
NGHTTP2_FLAG_END_SEGMENT even if the sent chunk is not the end of
|
||||
the stream */
|
||||
/* flags may have NGHTTP2_FLAG_END_STREAM even if the sent chunk is
|
||||
not the end of the stream */
|
||||
if(!pdata->eof) {
|
||||
frame->hd.flags &= ~(NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_SEGMENT);
|
||||
frame->hd.flags &= ~NGHTTP2_FLAG_END_STREAM;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1037,9 +1036,15 @@ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv)
|
|||
return 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
|
||||
if(iv[i].value < NGHTTP2_MAX_FRAME_SIZE_MIN ||
|
||||
iv[i].value > NGHTTP2_MAX_FRAME_SIZE_MAX) {
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
@ -1047,6 +1052,7 @@ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv)
|
|||
static void frame_set_pad(nghttp2_buf *buf, size_t padlen)
|
||||
{
|
||||
size_t trail_padlen;
|
||||
size_t newlen;
|
||||
|
||||
DEBUGF(fprintf(stderr, "send: padlen=%zu, shift left 1 bytes\n", padlen));
|
||||
|
||||
|
@ -1054,9 +1060,10 @@ static void frame_set_pad(nghttp2_buf *buf, size_t padlen)
|
|||
|
||||
--buf->pos;
|
||||
|
||||
buf->pos[3] |= NGHTTP2_FLAG_PADDED;
|
||||
buf->pos[4] |= NGHTTP2_FLAG_PADDED;
|
||||
|
||||
nghttp2_put_uint16be(buf->pos, nghttp2_get_uint16(buf->pos) + padlen);
|
||||
newlen = (nghttp2_get_uint32(buf->pos) >> 8) + padlen;
|
||||
nghttp2_put_uint32be(buf->pos, (uint32_t)((newlen << 8) + buf->pos[3]));
|
||||
|
||||
trail_padlen = padlen - 1;
|
||||
buf->pos[NGHTTP2_FRAME_HDLEN] = trail_padlen;
|
||||
|
@ -1088,11 +1095,11 @@ int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd,
|
|||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | |Frame header | Frame payload... :
|
||||
* +-+---------------+---------------------------------------------+
|
||||
* +-+-----------------+-------------------------------------------+
|
||||
* | |Frame header | Frame payload... :
|
||||
* +-+---------------+---------------------------------------------+
|
||||
* +-+-----------------+-------------------------------------------+
|
||||
* | |Frame header | Frame payload... :
|
||||
* +-+---------------+---------------------------------------------+
|
||||
* +-+-----------------+-------------------------------------------+
|
||||
*
|
||||
* We arranged padding so that it is included in the first frame
|
||||
* completely. For padded frame, we are going to adjust buf->pos of
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
#include "nghttp2_hd.h"
|
||||
#include "nghttp2_buf.h"
|
||||
|
||||
#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)
|
||||
|
@ -41,9 +40,12 @@
|
|||
#define NGHTTP2_SETTINGS_ID_MASK ((1 << 24) - 1)
|
||||
|
||||
/* The number of bytes of frame header. */
|
||||
#define NGHTTP2_FRAME_HDLEN 8
|
||||
#define NGHTTP2_FRAME_HDLEN 9
|
||||
|
||||
#define NGHTTP2_MAX_PAYLOADLEN 16383
|
||||
#define NGHTTP2_MAX_FRAME_SIZE_MAX ((1 << 24) - 1)
|
||||
#define NGHTTP2_MAX_FRAME_SIZE_MIN (1 << 14)
|
||||
|
||||
#define NGHTTP2_MAX_PAYLOADLEN 16384
|
||||
/* The one frame buffer length for tranmission. We may use several of
|
||||
them to support CONTINUATION. To account for Pad Length field, we
|
||||
allocate extra 1 byte, which saves extra large memcopying. */
|
||||
|
@ -57,7 +59,7 @@
|
|||
#define NGHTTP2_DATA_PAYLOADLEN 4096
|
||||
|
||||
/* Maximum headers payload length, calculated in compressed form.
|
||||
This applies to both transmission and reception. */
|
||||
This applies to transmission only. */
|
||||
#define NGHTTP2_MAX_HEADERSLEN 65536
|
||||
|
||||
/* The number of bytes for each SETTINGS entry */
|
||||
|
|
433
lib/nghttp2_hd.c
433
lib/nghttp2_hd.c
|
@ -100,7 +100,7 @@ static nghttp2_hd_entry static_table[] = {
|
|||
MAKE_ENT("www-authenticate", "", 4051929931u, 0u),
|
||||
};
|
||||
|
||||
static const size_t STATIC_TABLE_LENGTH =
|
||||
const size_t NGHTTP2_STATIC_TABLE_LENGTH =
|
||||
sizeof(static_table)/sizeof(static_table[0]);
|
||||
|
||||
static int memeq(const void *s1, const void *s2, size_t n)
|
||||
|
@ -281,11 +281,9 @@ static void hd_ringbuf_pop_back(nghttp2_hd_ringbuf *ringbuf)
|
|||
--ringbuf->len;
|
||||
}
|
||||
|
||||
static int hd_context_init(nghttp2_hd_context *context,
|
||||
nghttp2_hd_role role)
|
||||
static int hd_context_init(nghttp2_hd_context *context)
|
||||
{
|
||||
int rv;
|
||||
context->role = role;
|
||||
context->bad = 0;
|
||||
context->hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE;
|
||||
rv = hd_ringbuf_init
|
||||
|
@ -314,11 +312,10 @@ int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater,
|
|||
size_t deflate_hd_table_bufsize_max)
|
||||
{
|
||||
int rv;
|
||||
rv = hd_context_init(&deflater->ctx, NGHTTP2_HD_ROLE_DEFLATE);
|
||||
rv = hd_context_init(&deflater->ctx);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
deflater->no_refset = 0;
|
||||
|
||||
if(deflate_hd_table_bufsize_max < NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE) {
|
||||
deflater->notify_table_size_change = 1;
|
||||
|
@ -328,6 +325,7 @@ int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater,
|
|||
}
|
||||
|
||||
deflater->deflate_hd_table_bufsize_max = deflate_hd_table_bufsize_max;
|
||||
deflater->min_hd_table_bufsize_max = UINT32_MAX;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -336,7 +334,7 @@ int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater)
|
|||
{
|
||||
int rv;
|
||||
|
||||
rv = hd_context_init(&inflater->ctx, NGHTTP2_HD_ROLE_INFLATE);
|
||||
rv = hd_context_init(&inflater->ctx);
|
||||
if(rv != 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
@ -346,7 +344,6 @@ int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater)
|
|||
|
||||
inflater->ent_keep = NULL;
|
||||
inflater->nv_keep = NULL;
|
||||
inflater->end_headers_index = 0;
|
||||
|
||||
inflater->opcode = NGHTTP2_HD_OPCODE_NONE;
|
||||
inflater->state = NGHTTP2_HD_STATE_OPCODE;
|
||||
|
@ -364,7 +361,6 @@ int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater)
|
|||
inflater->newnamelen = 0;
|
||||
inflater->index_required = 0;
|
||||
inflater->no_index = 0;
|
||||
inflater->ent_name = NULL;
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -400,12 +396,6 @@ void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater)
|
|||
hd_context_free(&inflater->ctx);
|
||||
}
|
||||
|
||||
void nghttp2_hd_deflate_set_no_refset(nghttp2_hd_deflater *deflater,
|
||||
uint8_t no_refset)
|
||||
{
|
||||
deflater->no_refset = no_refset;
|
||||
}
|
||||
|
||||
static size_t entry_room(size_t namelen, size_t valuelen)
|
||||
{
|
||||
return NGHTTP2_HD_ENTRY_OVERHEAD + namelen + valuelen;
|
||||
|
@ -418,10 +408,8 @@ static int emit_indexed_header(nghttp2_nv *nv_out, nghttp2_hd_entry *ent)
|
|||
DEBUGF(fprintf(stderr, ": "));
|
||||
DEBUGF(fwrite(ent->nv.value, ent->nv.valuelen, 1, stderr));
|
||||
DEBUGF(fprintf(stderr, "\n"));
|
||||
/* ent->ref may be 0. This happens if the careless stupid encoder
|
||||
emits literal block larger than header table capacity with
|
||||
indexing. */
|
||||
ent->flags |= NGHTTP2_HD_FLAG_EMIT;
|
||||
/* ent->ref may be 0. This happens if the encoder emits literal
|
||||
block larger than header table capacity with indexing. */
|
||||
*nv_out = ent->nv;
|
||||
return 0;
|
||||
}
|
||||
|
@ -462,15 +450,19 @@ static size_t encode_length(uint8_t *buf, size_t n, size_t prefix)
|
|||
{
|
||||
size_t k = (1 << prefix) - 1;
|
||||
size_t len = 0;
|
||||
|
||||
*buf &= ~k;
|
||||
if(n >= k) {
|
||||
|
||||
if(n < k) {
|
||||
*buf++ |= n;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
*buf++ |= k;
|
||||
n -= k;
|
||||
++len;
|
||||
} else {
|
||||
*buf++ |= n;
|
||||
return 1;
|
||||
}
|
||||
|
||||
do {
|
||||
++len;
|
||||
if(n >= 128) {
|
||||
|
@ -561,20 +553,6 @@ static ssize_t decode_length(uint32_t *res, size_t *shift_ptr, int *final,
|
|||
return in + 1 - start;
|
||||
}
|
||||
|
||||
static int emit_clear_refset(nghttp2_bufs *bufs)
|
||||
{
|
||||
int rv;
|
||||
|
||||
DEBUGF(fprintf(stderr, "deflatehd: emit clear refset\n"));
|
||||
|
||||
rv = nghttp2_bufs_addb(bufs, 0x30u);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emit_table_size(nghttp2_bufs *bufs, size_t table_size)
|
||||
{
|
||||
int rv;
|
||||
|
@ -584,7 +562,7 @@ static int emit_table_size(nghttp2_bufs *bufs, size_t table_size)
|
|||
|
||||
DEBUGF(fprintf(stderr, "deflatehd: emit table_size=%zu\n", table_size));
|
||||
|
||||
blocklen = count_encoded_length(table_size, 4);
|
||||
blocklen = count_encoded_length(table_size, 5);
|
||||
|
||||
if(sizeof(sb) < blocklen) {
|
||||
return NGHTTP2_ERR_HEADER_COMP;
|
||||
|
@ -594,7 +572,7 @@ static int emit_table_size(nghttp2_bufs *bufs, size_t table_size)
|
|||
|
||||
*bufp = 0x20u;
|
||||
|
||||
encode_length(bufp, table_size, 4);
|
||||
encode_length(bufp, table_size, 5);
|
||||
|
||||
rv = nghttp2_bufs_add(bufs, sb, blocklen);
|
||||
if(rv != 0) {
|
||||
|
@ -632,14 +610,22 @@ static int emit_indexed_block(nghttp2_bufs *bufs, size_t idx)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int emit_string(nghttp2_bufs *bufs,
|
||||
size_t enclen, int huffman,
|
||||
const uint8_t *str, size_t len)
|
||||
static int emit_string(nghttp2_bufs *bufs, const uint8_t *str, size_t len)
|
||||
{
|
||||
int rv;
|
||||
uint8_t sb[16];
|
||||
uint8_t *bufp;
|
||||
size_t blocklen;
|
||||
size_t enclen;
|
||||
int huffman = 0;
|
||||
|
||||
enclen = nghttp2_hd_huff_encode_count(str, len);
|
||||
|
||||
if(enclen < len) {
|
||||
huffman = 1;
|
||||
} else {
|
||||
enclen = len;
|
||||
}
|
||||
|
||||
blocklen = count_encoded_length(enclen, 7);
|
||||
|
||||
|
@ -691,9 +677,7 @@ static int emit_indname_block(nghttp2_bufs *bufs, size_t idx,
|
|||
{
|
||||
int rv;
|
||||
uint8_t *bufp;
|
||||
size_t encvallen;
|
||||
size_t blocklen;
|
||||
int huffman;
|
||||
uint8_t sb[16];
|
||||
size_t prefixlen;
|
||||
int no_index;
|
||||
|
@ -711,13 +695,7 @@ static int emit_indname_block(nghttp2_bufs *bufs, size_t idx,
|
|||
"indexing=%d, no_index=%d\n",
|
||||
idx, nv->valuelen, inc_indexing, no_index));
|
||||
|
||||
encvallen = nghttp2_hd_huff_encode_count(nv->value, nv->valuelen);
|
||||
blocklen = count_encoded_length(idx + 1, prefixlen);
|
||||
huffman = encvallen < nv->valuelen;
|
||||
|
||||
if(!huffman) {
|
||||
encvallen = nv->valuelen;
|
||||
}
|
||||
|
||||
if(sizeof(sb) < blocklen) {
|
||||
return NGHTTP2_ERR_HEADER_COMP;
|
||||
|
@ -734,7 +712,7 @@ static int emit_indname_block(nghttp2_bufs *bufs, size_t idx,
|
|||
return rv;
|
||||
}
|
||||
|
||||
rv = emit_string(bufs, encvallen, huffman, nv->value, nv->valuelen);
|
||||
rv = emit_string(bufs, nv->value, nv->valuelen);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -746,10 +724,6 @@ static int emit_newname_block(nghttp2_bufs *bufs, const nghttp2_nv *nv,
|
|||
int inc_indexing)
|
||||
{
|
||||
int rv;
|
||||
size_t encnamelen;
|
||||
size_t encvallen;
|
||||
int name_huffman;
|
||||
int value_huffman;
|
||||
int no_index;
|
||||
|
||||
no_index = (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0;
|
||||
|
@ -759,29 +733,17 @@ static int emit_newname_block(nghttp2_bufs *bufs, const nghttp2_nv *nv,
|
|||
"indexing=%d, no_index=%d\n",
|
||||
nv->namelen, nv->valuelen, inc_indexing, no_index));
|
||||
|
||||
encnamelen = nghttp2_hd_huff_encode_count(nv->name, nv->namelen);
|
||||
encvallen = nghttp2_hd_huff_encode_count(nv->value, nv->valuelen);
|
||||
name_huffman = encnamelen < nv->namelen;
|
||||
value_huffman = encvallen < nv->valuelen;
|
||||
|
||||
if(!name_huffman) {
|
||||
encnamelen = nv->namelen;
|
||||
}
|
||||
if(!value_huffman) {
|
||||
encvallen = nv->valuelen;
|
||||
}
|
||||
|
||||
rv = nghttp2_bufs_addb(bufs, pack_first_byte(inc_indexing, no_index));
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = emit_string(bufs, encnamelen, name_huffman, nv->name, nv->namelen);
|
||||
rv = emit_string(bufs, nv->name, nv->namelen);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = emit_string(bufs, encvallen, value_huffman, nv->value, nv->valuelen);
|
||||
rv = emit_string(bufs, nv->value, nv->valuelen);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -789,23 +751,6 @@ static int emit_newname_block(nghttp2_bufs *bufs, const nghttp2_nv *nv,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit common header with |index| by toggle off and on (thus 2
|
||||
* indexed representation emissions).
|
||||
*/
|
||||
static int emit_implicit(nghttp2_bufs *bufs, size_t idx)
|
||||
{
|
||||
int i, rv;
|
||||
|
||||
for(i = 0; i < 2; ++i) {
|
||||
rv = emit_indexed_block(bufs, idx);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context,
|
||||
nghttp2_bufs *bufs,
|
||||
const nghttp2_nv *nv,
|
||||
|
@ -824,17 +769,7 @@ static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context,
|
|||
nghttp2_hd_entry* ent = hd_ringbuf_get(&context->hd_table, idx);
|
||||
|
||||
context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen);
|
||||
if(context->role == NGHTTP2_HD_ROLE_DEFLATE) {
|
||||
if(ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT) {
|
||||
/* Emit common header just before it slips away from the
|
||||
table. If we don't do this, we have to emit it in literal
|
||||
representation which hurts compression. */
|
||||
rv = emit_implicit(bufs, idx);
|
||||
if(rv != 0) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUGF(fprintf(stderr, "hpack: remove item from header table: "));
|
||||
DEBUGF(fwrite(ent->nv.name, ent->nv.namelen, 1, stderr));
|
||||
DEBUGF(fprintf(stderr, ": "));
|
||||
|
@ -882,8 +817,6 @@ static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context,
|
|||
}
|
||||
|
||||
context->hd_table_bufsize += room;
|
||||
|
||||
new_ent->flags |= NGHTTP2_HD_FLAG_REFSET;
|
||||
}
|
||||
return new_ent;
|
||||
}
|
||||
|
@ -913,39 +846,42 @@ static search_result search_hd_table(nghttp2_hd_context *context,
|
|||
uint32_t value_hash = hash(nv->value, nv->valuelen);
|
||||
int use_index = (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) == 0;
|
||||
|
||||
if(use_index) {
|
||||
for(i = 0; i < context->hd_table.len; ++i) {
|
||||
nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, i);
|
||||
if(ent->name_hash == name_hash && name_eq(&ent->nv, nv)) {
|
||||
if(res.index == -1) {
|
||||
res.index = (ssize_t)i;
|
||||
}
|
||||
if(ent->value_hash == value_hash && value_eq(&ent->nv, nv)) {
|
||||
res.index = (ssize_t)i;
|
||||
res.name_value_match = 1;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0; i < STATIC_TABLE_LENGTH; ++i) {
|
||||
for(i = 0; i < NGHTTP2_STATIC_TABLE_LENGTH; ++i) {
|
||||
nghttp2_hd_entry *ent = &static_table[i];
|
||||
if(ent->name_hash != name_hash || !name_eq(&ent->nv, nv)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(res.index == -1) {
|
||||
res.index = (ssize_t)(context->hd_table.len + i);
|
||||
res.index = (ssize_t)i;
|
||||
}
|
||||
|
||||
if(use_index &&
|
||||
ent->value_hash == value_hash && value_eq(&ent->nv, nv)) {
|
||||
res.index = (ssize_t)(context->hd_table.len + i);
|
||||
res.index = (ssize_t)i;
|
||||
res.name_value_match = 1;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
if(!use_index) {
|
||||
return res;
|
||||
}
|
||||
|
||||
for(i = 0; i < context->hd_table.len; ++i) {
|
||||
nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, i);
|
||||
if(ent->name_hash == name_hash && name_eq(&ent->nv, nv)) {
|
||||
if(res.index == -1) {
|
||||
res.index = (ssize_t)i + NGHTTP2_STATIC_TABLE_LENGTH;
|
||||
}
|
||||
if(ent->value_hash == value_hash && value_eq(&ent->nv, nv)) {
|
||||
res.index = (ssize_t)i + NGHTTP2_STATIC_TABLE_LENGTH;
|
||||
res.name_value_match = 1;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -972,6 +908,9 @@ int nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater,
|
|||
|
||||
deflater->ctx.hd_table_bufsize_max = next_bufsize;
|
||||
|
||||
deflater->min_hd_table_bufsize_max =
|
||||
nghttp2_min(deflater->min_hd_table_bufsize_max, next_bufsize);
|
||||
|
||||
deflater->notify_table_size_change = 1;
|
||||
|
||||
hd_context_shrink_table_size(&deflater->ctx);
|
||||
|
@ -987,31 +926,22 @@ int nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void clear_refset(nghttp2_hd_context *context)
|
||||
{
|
||||
size_t i;
|
||||
for(i = 0; i < context->hd_table.len; ++i) {
|
||||
nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, i);
|
||||
ent->flags &= ~NGHTTP2_HD_FLAG_REFSET;
|
||||
}
|
||||
}
|
||||
|
||||
#define INDEX_RANGE_VALID(context, idx) \
|
||||
((idx) < (context)->hd_table.len + STATIC_TABLE_LENGTH)
|
||||
((idx) < (context)->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH)
|
||||
|
||||
static size_t get_max_index(nghttp2_hd_context *context)
|
||||
{
|
||||
return context->hd_table.len + STATIC_TABLE_LENGTH - 1;
|
||||
return context->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH - 1;
|
||||
}
|
||||
|
||||
nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context,
|
||||
size_t idx)
|
||||
{
|
||||
assert(INDEX_RANGE_VALID(context, idx));
|
||||
if(idx < context->hd_table.len) {
|
||||
return hd_ringbuf_get(&context->hd_table, idx);
|
||||
if(idx >= NGHTTP2_STATIC_TABLE_LENGTH) {
|
||||
return hd_ringbuf_get(&context->hd_table, idx - NGHTTP2_STATIC_TABLE_LENGTH);
|
||||
} else {
|
||||
return &static_table[idx - context->hd_table.len];
|
||||
return &static_table[idx];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1042,8 +972,9 @@ static int deflate_nv(nghttp2_hd_deflater *deflater,
|
|||
nghttp2_bufs *bufs, const nghttp2_nv *nv)
|
||||
{
|
||||
int rv;
|
||||
nghttp2_hd_entry *ent;
|
||||
search_result res;
|
||||
ssize_t idx = -1;
|
||||
int incidx = 0;
|
||||
|
||||
DEBUGF(fprintf(stderr, "deflatehd: deflating "));
|
||||
DEBUGF(fwrite(nv->name, nv->namelen, 1, stderr));
|
||||
|
@ -1053,87 +984,28 @@ static int deflate_nv(nghttp2_hd_deflater *deflater,
|
|||
|
||||
res = search_hd_table(&deflater->ctx, nv);
|
||||
|
||||
if(res.index != -1 && res.name_value_match) {
|
||||
size_t idx = res.index;
|
||||
idx = res.index;
|
||||
|
||||
DEBUGF(fprintf(stderr, "deflatehd: name/value match index=%zd\n",
|
||||
res.index));
|
||||
if(res.name_value_match) {
|
||||
|
||||
ent = nghttp2_hd_table_get(&deflater->ctx, idx);
|
||||
if(idx >= deflater->ctx.hd_table.len) {
|
||||
nghttp2_hd_entry *new_ent;
|
||||
DEBUGF(fprintf(stderr, "deflatehd: name/value match index=%zd\n", idx));
|
||||
|
||||
/* It is important to first add entry to the header table and
|
||||
let eviction go. If NGHTTP2_HD_FLAG_IMPLICIT_EMIT entry is
|
||||
evicted, it must be emitted before the |nv|. */
|
||||
new_ent = add_hd_table_incremental(&deflater->ctx, bufs, &ent->nv,
|
||||
NGHTTP2_HD_FLAG_NONE);
|
||||
if(!new_ent) {
|
||||
return NGHTTP2_ERR_HEADER_COMP;
|
||||
}
|
||||
if(new_ent->ref == 0) {
|
||||
nghttp2_hd_entry_free(new_ent);
|
||||
free(new_ent);
|
||||
new_ent = NULL;
|
||||
} else {
|
||||
/* new_ent->ref > 0 means that new_ent is in the reference
|
||||
set */
|
||||
new_ent->flags |= NGHTTP2_HD_FLAG_EMIT;
|
||||
}
|
||||
rv = emit_indexed_block(bufs, idx);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
} else if((ent->flags & NGHTTP2_HD_FLAG_REFSET) == 0) {
|
||||
ent->flags |= NGHTTP2_HD_FLAG_REFSET | NGHTTP2_HD_FLAG_EMIT;
|
||||
rv = emit_indexed_block(bufs, idx);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
int num_emits = 0;
|
||||
if(ent->flags & NGHTTP2_HD_FLAG_EMIT) {
|
||||
/* occurrences of the same indexed representation. Emit index
|
||||
twice. */
|
||||
num_emits = 2;
|
||||
} else if(ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT) {
|
||||
/* ent was implicitly emitted because it is the common
|
||||
header field. To support occurrences of the same indexed
|
||||
representation, we have to emit 4 times. This is because
|
||||
"implicitly emitted" means actually not emitted at
|
||||
all. So first 2 emits performs 1st header appears in the
|
||||
reference set. And another 2 emits are done for 2nd
|
||||
(current) header. */
|
||||
ent->flags ^= NGHTTP2_HD_FLAG_IMPLICIT_EMIT;
|
||||
ent->flags |= NGHTTP2_HD_FLAG_EMIT;
|
||||
num_emits = 4;
|
||||
} else {
|
||||
/* This is common header and not emitted in the current
|
||||
run. Just mark IMPLICIT_EMIT, in the hope that we are not
|
||||
required to emit anything for this. We will emit toggle
|
||||
off/on for this entry if it is removed from the header
|
||||
table. */
|
||||
ent->flags |= NGHTTP2_HD_FLAG_IMPLICIT_EMIT;
|
||||
}
|
||||
for(; num_emits > 0; --num_emits) {
|
||||
rv = emit_indexed_block(bufs, idx);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ssize_t idx = -1;
|
||||
int incidx = 0;
|
||||
|
||||
if(res.index != -1) {
|
||||
DEBUGF(fprintf(stderr, "deflatehd: name match index=%zd\n",
|
||||
res.index));
|
||||
|
||||
idx = res.index;
|
||||
}
|
||||
|
||||
if(hd_deflate_should_indexing(deflater, nv)) {
|
||||
nghttp2_hd_entry *new_ent;
|
||||
if(idx >= (ssize_t)deflater->ctx.hd_table.len) {
|
||||
if(idx != -1 && idx < (ssize_t)NGHTTP2_STATIC_TABLE_LENGTH) {
|
||||
nghttp2_nv nv_indname;
|
||||
nv_indname = *nv;
|
||||
nv_indname.name = nghttp2_hd_table_get(&deflater->ctx, idx)->nv.name;
|
||||
|
@ -1150,10 +1022,6 @@ static int deflate_nv(nghttp2_hd_deflater *deflater,
|
|||
if(new_ent->ref == 0) {
|
||||
nghttp2_hd_entry_free(new_ent);
|
||||
free(new_ent);
|
||||
} else {
|
||||
/* new_ent->ref > 0 means that new_ent is in the reference
|
||||
set. */
|
||||
new_ent->flags |= NGHTTP2_HD_FLAG_EMIT;
|
||||
}
|
||||
incidx = 1;
|
||||
}
|
||||
|
@ -1165,30 +1033,6 @@ static int deflate_nv(nghttp2_hd_deflater *deflater,
|
|||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int deflate_post_process_hd_entry(nghttp2_hd_entry *ent,
|
||||
size_t idx,
|
||||
nghttp2_bufs *bufs)
|
||||
{
|
||||
int rv;
|
||||
|
||||
if((ent->flags & NGHTTP2_HD_FLAG_REFSET) &&
|
||||
(ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT) == 0 &&
|
||||
(ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0) {
|
||||
/* This entry is not present in the current header set and must
|
||||
be removed. */
|
||||
ent->flags ^= NGHTTP2_HD_FLAG_REFSET;
|
||||
|
||||
rv = emit_indexed_block(bufs, idx);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
ent->flags &= ~(NGHTTP2_HD_FLAG_EMIT | NGHTTP2_HD_FLAG_IMPLICIT_EMIT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1205,8 +1049,21 @@ int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater,
|
|||
}
|
||||
|
||||
if(deflater->notify_table_size_change) {
|
||||
size_t min_hd_table_bufsize_max;
|
||||
|
||||
min_hd_table_bufsize_max = deflater->min_hd_table_bufsize_max;
|
||||
|
||||
deflater->notify_table_size_change = 0;
|
||||
deflater->min_hd_table_bufsize_max = UINT32_MAX;
|
||||
|
||||
if(deflater->ctx.hd_table_bufsize_max > min_hd_table_bufsize_max) {
|
||||
|
||||
rv = emit_table_size(bufs, min_hd_table_bufsize_max);
|
||||
|
||||
if(rv != 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
rv = emit_table_size(bufs, deflater->ctx.hd_table_bufsize_max);
|
||||
|
||||
|
@ -1215,13 +1072,6 @@ int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater,
|
|||
}
|
||||
}
|
||||
|
||||
if(deflater->no_refset) {
|
||||
rv = emit_clear_refset(bufs);
|
||||
if(rv != 0) {
|
||||
goto fail;
|
||||
}
|
||||
clear_refset(&deflater->ctx);
|
||||
}
|
||||
for(i = 0; i < nvlen; ++i) {
|
||||
rv = deflate_nv(deflater, bufs, &nv[i]);
|
||||
if(rv != 0) {
|
||||
|
@ -1232,15 +1082,6 @@ int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater,
|
|||
DEBUGF(fprintf(stderr,
|
||||
"deflatehd: all input name/value pairs were deflated\n"));
|
||||
|
||||
for(i = 0; i < deflater->ctx.hd_table.len; ++i) {
|
||||
nghttp2_hd_entry *ent = hd_ringbuf_get(&deflater->ctx.hd_table, i);
|
||||
|
||||
rv = deflate_post_process_hd_entry(ent, i, bufs);
|
||||
if(rv != 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
DEBUGF(fprintf(stderr, "deflatehd: error return %d\n", rv));
|
||||
|
@ -1282,15 +1123,13 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater,
|
|||
size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater,
|
||||
const nghttp2_nv *nva, size_t nvlen)
|
||||
{
|
||||
size_t n;
|
||||
size_t n = 0;
|
||||
size_t i;
|
||||
|
||||
/* Possible Reference Set Emptying */
|
||||
n = 1;
|
||||
|
||||
/* Possible Maximum Header Table Size Change. Encoding (1u << 31) -
|
||||
1 using 4 bit prefix requires 6 bytes. */
|
||||
n += 6;
|
||||
1 using 4 bit prefix requires 6 bytes. We may emit this at most
|
||||
twice. */
|
||||
n += 12;
|
||||
|
||||
/* Use Literal Header Field without indexing - New Name, since it is
|
||||
most space consuming format. Also we choose the less one between
|
||||
|
@ -1305,9 +1144,6 @@ size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater,
|
|||
n += nva[i].namelen + nva[i].valuelen;
|
||||
}
|
||||
|
||||
/* Add possible reference set toggle off */
|
||||
n += deflater->ctx.hd_table.len;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
@ -1471,29 +1307,10 @@ static int hd_inflate_commit_indexed(nghttp2_hd_inflater *inflater,
|
|||
nghttp2_nv *nv_out)
|
||||
{
|
||||
nghttp2_hd_entry *ent = nghttp2_hd_table_get(&inflater->ctx, inflater->index);
|
||||
if(inflater->index >= inflater->ctx.hd_table.len) {
|
||||
nghttp2_hd_entry *new_ent;
|
||||
new_ent = add_hd_table_incremental(&inflater->ctx, NULL, &ent->nv,
|
||||
NGHTTP2_HD_FLAG_NONE);
|
||||
if(!new_ent) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
/* new_ent->ref == 0 may be hold */
|
||||
emit_indexed_header(nv_out, new_ent);
|
||||
inflater->ent_keep = new_ent;
|
||||
return 0;
|
||||
}
|
||||
ent->flags ^= NGHTTP2_HD_FLAG_REFSET;
|
||||
if(ent->flags & NGHTTP2_HD_FLAG_REFSET) {
|
||||
|
||||
emit_indexed_header(nv_out, ent);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEBUGF(fprintf(stderr, "inflatehd: toggle off item: "));
|
||||
DEBUGF(fwrite(ent->nv.name, ent->nv.namelen, 1, stderr));
|
||||
DEBUGF(fprintf(stderr, ": "));
|
||||
DEBUGF(fwrite(ent->nv.value, ent->nv.valuelen, 1, stderr));
|
||||
DEBUGF(fprintf(stderr, "\n"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int hd_inflate_remove_bufs(nghttp2_hd_inflater *inflater,
|
||||
|
@ -1599,6 +1416,7 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater,
|
|||
{
|
||||
int rv;
|
||||
nghttp2_nv nv;
|
||||
nghttp2_hd_entry *ent_name;
|
||||
|
||||
rv = hd_inflate_remove_bufs(inflater, &nv, 1 /* value only */);
|
||||
if(rv != 0) {
|
||||
|
@ -1611,8 +1429,10 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater,
|
|||
nv.flags = NGHTTP2_NV_FLAG_NONE;
|
||||
}
|
||||
|
||||
nv.name = inflater->ent_name->nv.name;
|
||||
nv.namelen = inflater->ent_name->nv.namelen;
|
||||
ent_name = nghttp2_hd_table_get(&inflater->ctx, inflater->index);
|
||||
|
||||
nv.name = ent_name->nv.name;
|
||||
nv.namelen = ent_name->nv.namelen;
|
||||
|
||||
if(inflater->index_required) {
|
||||
nghttp2_hd_entry *new_ent;
|
||||
|
@ -1620,24 +1440,22 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater,
|
|||
int static_name;
|
||||
|
||||
ent_flags = NGHTTP2_HD_FLAG_VALUE_ALLOC | NGHTTP2_HD_FLAG_VALUE_GIFT;
|
||||
static_name = inflater->index >= inflater->ctx.hd_table.len;
|
||||
static_name = inflater->index < NGHTTP2_STATIC_TABLE_LENGTH;
|
||||
|
||||
if(!static_name) {
|
||||
ent_flags |= NGHTTP2_HD_FLAG_NAME_ALLOC;
|
||||
/* For entry in static table, we must not touch ref, because it
|
||||
is shared by threads */
|
||||
++inflater->ent_name->ref;
|
||||
++ent_name->ref;
|
||||
}
|
||||
|
||||
new_ent = add_hd_table_incremental(&inflater->ctx, NULL, &nv, ent_flags);
|
||||
|
||||
if(!static_name && --inflater->ent_name->ref == 0) {
|
||||
nghttp2_hd_entry_free(inflater->ent_name);
|
||||
free(inflater->ent_name);
|
||||
if(!static_name && --ent_name->ref == 0) {
|
||||
nghttp2_hd_entry_free(ent_name);
|
||||
free(ent_name);
|
||||
}
|
||||
|
||||
inflater->ent_name = NULL;
|
||||
|
||||
if(new_ent) {
|
||||
emit_indexed_header(nv_out, new_ent);
|
||||
|
||||
|
@ -1678,20 +1496,10 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
|
|||
for(; in != last;) {
|
||||
switch(inflater->state) {
|
||||
case NGHTTP2_HD_STATE_OPCODE:
|
||||
if((*in & 0xf0u) == 0x20u) {
|
||||
if((*in & 0xe0u) == 0x20u) {
|
||||
DEBUGF(fprintf(stderr, "inflatehd: header table size change\n"));
|
||||
inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED;
|
||||
inflater->state = NGHTTP2_HD_STATE_READ_TABLE_SIZE;
|
||||
} else if((*in & 0xf0u) == 0x30u) {
|
||||
if(*in != 0x30u) {
|
||||
rv = NGHTTP2_ERR_HEADER_COMP;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
DEBUGF(fprintf(stderr, "inflatehd: clearing reference set\n"));
|
||||
inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED;
|
||||
inflater->state = NGHTTP2_HD_STATE_CLEAR_REFSET;
|
||||
++in;
|
||||
} else if(*in & 0x80u) {
|
||||
DEBUGF(fprintf(stderr, "inflatehd: indexed repr\n"));
|
||||
inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED;
|
||||
|
@ -1720,15 +1528,10 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
|
|||
}
|
||||
inflater->left = 0;
|
||||
inflater->shift = 0;
|
||||
break;
|
||||
case NGHTTP2_HD_STATE_CLEAR_REFSET:
|
||||
clear_refset(&inflater->ctx);
|
||||
inflater->state = NGHTTP2_HD_STATE_OPCODE;
|
||||
|
||||
break;
|
||||
case NGHTTP2_HD_STATE_READ_TABLE_SIZE:
|
||||
rfin = 0;
|
||||
rv = hd_inflate_read_len(inflater, &rfin, in, last, 4,
|
||||
rv = hd_inflate_read_len(inflater, &rfin, in, last, 5,
|
||||
inflater->settings_hd_table_bufsize_max);
|
||||
if(rv < 0) {
|
||||
goto fail;
|
||||
|
@ -1762,19 +1565,20 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
|
|||
|
||||
in += rv;
|
||||
|
||||
if(!rfin) {
|
||||
goto almost_ok;
|
||||
}
|
||||
|
||||
if(inflater->left == 0) {
|
||||
rv = NGHTTP2_ERR_HEADER_COMP;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if(!rfin) {
|
||||
goto almost_ok;
|
||||
}
|
||||
DEBUGF(fprintf(stderr, "inflatehd: index=%zu\n", inflater->left));
|
||||
if(inflater->opcode == NGHTTP2_HD_OPCODE_INDEXED) {
|
||||
inflater->index = inflater->left;
|
||||
assert(inflater->index > 0);
|
||||
--inflater->index;
|
||||
|
||||
rv = hd_inflate_commit_indexed(inflater, nv_out);
|
||||
if(rv < 0) {
|
||||
goto fail;
|
||||
|
@ -1787,10 +1591,8 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
|
|||
}
|
||||
} else {
|
||||
inflater->index = inflater->left;
|
||||
assert(inflater->index > 0);
|
||||
--inflater->index;
|
||||
inflater->ent_name = nghttp2_hd_table_get(&inflater->ctx,
|
||||
inflater->index);
|
||||
|
||||
inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN;
|
||||
}
|
||||
break;
|
||||
|
@ -1995,20 +1797,6 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
|
|||
|
||||
goto fail;
|
||||
}
|
||||
for(; inflater->end_headers_index < inflater->ctx.hd_table.len;
|
||||
++inflater->end_headers_index) {
|
||||
nghttp2_hd_entry *ent;
|
||||
ent = hd_ringbuf_get(&inflater->ctx.hd_table,
|
||||
inflater->end_headers_index);
|
||||
|
||||
if((ent->flags & NGHTTP2_HD_FLAG_REFSET) &&
|
||||
(ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0) {
|
||||
emit_indexed_header(nv_out, ent);
|
||||
*inflate_flags |= NGHTTP2_HD_INFLATE_EMIT;
|
||||
return (ssize_t)(in - first);
|
||||
}
|
||||
ent->flags &= ~NGHTTP2_HD_FLAG_EMIT;
|
||||
}
|
||||
*inflate_flags |= NGHTTP2_HD_INFLATE_FINAL;
|
||||
}
|
||||
return (ssize_t)(in - first);
|
||||
|
@ -2033,7 +1821,6 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
|
|||
int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater)
|
||||
{
|
||||
hd_inflate_keep_free(inflater);
|
||||
inflater->end_headers_index = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -47,10 +47,8 @@
|
|||
encoder only uses the memory up to this value. */
|
||||
#define NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE (1 << 12)
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_HD_ROLE_DEFLATE,
|
||||
NGHTTP2_HD_ROLE_INFLATE
|
||||
} nghttp2_hd_role;
|
||||
/* Exported for unit test */
|
||||
extern const size_t NGHTTP2_STATIC_TABLE_LENGTH;
|
||||
|
||||
typedef enum {
|
||||
NGHTTP2_HD_FLAG_NONE = 0,
|
||||
|
@ -58,18 +56,12 @@ typedef enum {
|
|||
NGHTTP2_HD_FLAG_NAME_ALLOC = 1,
|
||||
/* Indicates value was dynamically allocated and must be freed */
|
||||
NGHTTP2_HD_FLAG_VALUE_ALLOC = 1 << 1,
|
||||
/* Indicates that the entry is in the reference set */
|
||||
NGHTTP2_HD_FLAG_REFSET = 1 << 2,
|
||||
/* Indicates that the entry is emitted in the current header
|
||||
processing. */
|
||||
NGHTTP2_HD_FLAG_EMIT = 1 << 3,
|
||||
NGHTTP2_HD_FLAG_IMPLICIT_EMIT = 1 << 4,
|
||||
/* Indicates that the name was gifted to the entry and no copying
|
||||
necessary. */
|
||||
NGHTTP2_HD_FLAG_NAME_GIFT = 1 << 5,
|
||||
NGHTTP2_HD_FLAG_NAME_GIFT = 1 << 2,
|
||||
/* Indicates that the value was gifted to the entry and no copying
|
||||
necessary. */
|
||||
NGHTTP2_HD_FLAG_VALUE_GIFT = 1 << 6
|
||||
NGHTTP2_HD_FLAG_VALUE_GIFT = 1 << 3
|
||||
} nghttp2_hd_flags;
|
||||
|
||||
typedef struct {
|
||||
|
@ -97,7 +89,6 @@ typedef enum {
|
|||
|
||||
typedef enum {
|
||||
NGHTTP2_HD_STATE_OPCODE,
|
||||
NGHTTP2_HD_STATE_CLEAR_REFSET,
|
||||
NGHTTP2_HD_STATE_READ_TABLE_SIZE,
|
||||
NGHTTP2_HD_STATE_READ_INDEX,
|
||||
NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN,
|
||||
|
@ -119,8 +110,6 @@ typedef struct {
|
|||
size_t hd_table_bufsize;
|
||||
/* The effective header table size. */
|
||||
size_t hd_table_bufsize_max;
|
||||
/* Role of this context; deflate or infalte */
|
||||
nghttp2_hd_role role;
|
||||
/* If inflate/deflate error occurred, this value is set to 1 and
|
||||
further invocation of inflate/deflate will fail with
|
||||
NGHTTP2_ERR_HEADER_COMP. */
|
||||
|
@ -131,9 +120,8 @@ struct nghttp2_hd_deflater {
|
|||
nghttp2_hd_context ctx;
|
||||
/* The upper limit of the header table size the deflater accepts. */
|
||||
size_t deflate_hd_table_bufsize_max;
|
||||
/* Set to this nonzero to clear reference set on each deflation each
|
||||
time. */
|
||||
uint8_t no_refset;
|
||||
/* Minimum header table size notified in the next context update */
|
||||
size_t min_hd_table_bufsize_max;
|
||||
/* If nonzero, send header table size using encoding context update
|
||||
in the next deflate process */
|
||||
uint8_t notify_table_size_change;
|
||||
|
@ -152,16 +140,10 @@ struct nghttp2_hd_inflater {
|
|||
/* Pointer to the name/value pair buffer which is used in the
|
||||
current header emission. */
|
||||
uint8_t *nv_keep;
|
||||
/* Pointers to the name/value pair which is referred as indexed
|
||||
name. This entry must be in header table. */
|
||||
nghttp2_hd_entry *ent_name;
|
||||
/* The number of bytes to read */
|
||||
size_t left;
|
||||
/* The index in indexed repr or indexed name */
|
||||
size_t index;
|
||||
/* The index of header table to toggle off the entry from reference
|
||||
set at the end of decompression. */
|
||||
size_t end_headers_index;
|
||||
/* The length of new name encoded in literal. For huffman encoded
|
||||
string, this is the length after it is decoded. */
|
||||
size_t newnamelen;
|
||||
|
|
|
@ -40,18 +40,10 @@ void nghttp2_option_del(nghttp2_option *option)
|
|||
free(option);
|
||||
}
|
||||
|
||||
void nghttp2_option_set_no_auto_stream_window_update(nghttp2_option *option,
|
||||
int val)
|
||||
void nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val)
|
||||
{
|
||||
option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_STREAM_WINDOW_UPDATE;
|
||||
option->no_auto_stream_window_update = val;
|
||||
}
|
||||
|
||||
void nghttp2_option_set_no_auto_connection_window_update
|
||||
(nghttp2_option *option, int val)
|
||||
{
|
||||
option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_CONNECTION_WINDOW_UPDATE;
|
||||
option->no_auto_connection_window_update = val;
|
||||
option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE;
|
||||
option->no_auto_window_update = val;
|
||||
}
|
||||
|
||||
void nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option,
|
||||
|
|
|
@ -35,24 +35,14 @@
|
|||
* Configuration options
|
||||
*/
|
||||
typedef enum {
|
||||
/**
|
||||
* This option prevents the library from sending WINDOW_UPDATE for a
|
||||
* stream automatically. If this option is set to nonzero, the
|
||||
* library won't send WINDOW_UPDATE for a stream and the application
|
||||
* is responsible for sending WINDOW_UPDATE using
|
||||
* `nghttp2_submit_window_update`. By default, this option is set to
|
||||
* zero.
|
||||
*/
|
||||
NGHTTP2_OPT_NO_AUTO_STREAM_WINDOW_UPDATE = 1,
|
||||
/**
|
||||
* This option prevents the library from sending WINDOW_UPDATE for a
|
||||
* connection automatically. If this option is set to nonzero, the
|
||||
* library won't send WINDOW_UPDATE for a connection and the
|
||||
* application is responsible for sending WINDOW_UPDATE with stream
|
||||
* ID 0 using `nghttp2_submit_window_update`. By default, this
|
||||
* option is set to zero.
|
||||
* library won't send WINDOW_UPDATE for DATA until application calls
|
||||
* nghttp2_session_consume() to indicate the amount of consumed
|
||||
* DATA. By default, this option is set to zero.
|
||||
*/
|
||||
NGHTTP2_OPT_NO_AUTO_CONNECTION_WINDOW_UPDATE = 1 << 1,
|
||||
NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE = 1,
|
||||
/**
|
||||
* This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of
|
||||
* remote endpoint as if it is received in SETTINGS frame. Without
|
||||
|
@ -66,7 +56,7 @@ 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 << 1
|
||||
} nghttp2_option_flag;
|
||||
|
||||
/**
|
||||
|
@ -83,13 +73,9 @@ struct nghttp2_option {
|
|||
*/
|
||||
uint32_t peer_max_concurrent_streams;
|
||||
/**
|
||||
* NGHTTP2_OPT_NO_AUTO_STREAM_WINDOW_UPDATE
|
||||
* NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE
|
||||
*/
|
||||
uint8_t no_auto_stream_window_update;
|
||||
/**
|
||||
* NGHTTP2_OPT_NO_AUTO_CONNECTION_WINDOW_UPDATE
|
||||
*/
|
||||
uint8_t no_auto_connection_window_update;
|
||||
uint8_t no_auto_window_update;
|
||||
};
|
||||
|
||||
#endif /* NGHTTP2_OPTION_H */
|
||||
|
|
|
@ -281,7 +281,6 @@ static void session_inbound_frame_reset(nghttp2_session *session)
|
|||
iframe->niv = 0;
|
||||
iframe->payloadleft = 0;
|
||||
iframe->padlen = 0;
|
||||
iframe->headers_payload_length = 0;
|
||||
iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1].settings_id =
|
||||
NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
|
||||
iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1].value = UINT32_MAX;
|
||||
|
@ -293,6 +292,8 @@ static void init_settings(nghttp2_settings_storage *settings)
|
|||
settings->enable_push = 1;
|
||||
settings->max_concurrent_streams = NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS;
|
||||
settings->initial_window_size = NGHTTP2_INITIAL_WINDOW_SIZE;
|
||||
settings->max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MIN;
|
||||
settings->max_header_set_size = UINT32_MAX;
|
||||
}
|
||||
|
||||
static void active_outbound_item_reset(nghttp2_active_outbound_item *aob)
|
||||
|
@ -352,6 +353,7 @@ static int session_new(nghttp2_session **session_ptr,
|
|||
|
||||
(*session_ptr)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
|
||||
(*session_ptr)->recv_window_size = 0;
|
||||
(*session_ptr)->consumed_size = 0;
|
||||
(*session_ptr)->recv_reduction = 0;
|
||||
(*session_ptr)->local_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
|
||||
|
||||
|
@ -383,19 +385,10 @@ static int session_new(nghttp2_session **session_ptr,
|
|||
|
||||
|
||||
if(option) {
|
||||
if((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_STREAM_WINDOW_UPDATE) &&
|
||||
option->no_auto_stream_window_update) {
|
||||
if((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE) &&
|
||||
option->no_auto_window_update) {
|
||||
|
||||
(*session_ptr)->opt_flags |=
|
||||
NGHTTP2_OPTMASK_NO_AUTO_STREAM_WINDOW_UPDATE;
|
||||
|
||||
}
|
||||
|
||||
if((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_CONNECTION_WINDOW_UPDATE) &&
|
||||
option->no_auto_connection_window_update) {
|
||||
|
||||
(*session_ptr)->opt_flags |=
|
||||
NGHTTP2_OPTMASK_NO_AUTO_CONNECTION_WINDOW_UPDATE;
|
||||
(*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE;
|
||||
|
||||
}
|
||||
|
||||
|
@ -2031,6 +2024,8 @@ static int session_after_frame_sent(nghttp2_session *session)
|
|||
/* Fall through */
|
||||
case NGHTTP2_HCAT_RESPONSE:
|
||||
stream->state = NGHTTP2_STREAM_OPENED;
|
||||
/* Fall through */
|
||||
case NGHTTP2_HCAT_HEADERS:
|
||||
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
|
||||
}
|
||||
|
@ -2050,15 +2045,6 @@ static int session_after_frame_sent(nghttp2_session *session)
|
|||
DATA frame item. We might have to handle it here. */
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_HCAT_HEADERS:
|
||||
if(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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -3182,10 +3168,11 @@ static int update_local_initial_window_size_func
|
|||
return nghttp2_session_terminate_session(arg->session,
|
||||
NGHTTP2_FLOW_CONTROL_ERROR);
|
||||
}
|
||||
if(!(arg->session->opt_flags &
|
||||
NGHTTP2_OPTMASK_NO_AUTO_STREAM_WINDOW_UPDATE)) {
|
||||
if(!(arg->session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {
|
||||
|
||||
if(nghttp2_should_send_window_update(stream->local_window_size,
|
||||
stream->recv_window_size)) {
|
||||
|
||||
rv = nghttp2_session_add_window_update(arg->session,
|
||||
NGHTTP2_FLAG_NONE,
|
||||
stream->stream_id,
|
||||
|
@ -3288,6 +3275,12 @@ int nghttp2_session_update_local_settings(nghttp2_session *session,
|
|||
case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
|
||||
session->local_settings.initial_window_size = iv[i].value;
|
||||
break;
|
||||
case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
|
||||
session->local_settings.max_frame_size = iv[i].value;
|
||||
break;
|
||||
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
|
||||
session->local_settings.max_header_set_size = iv[i].value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3408,6 +3401,23 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
|
|||
|
||||
session->remote_settings.initial_window_size = entry->value;
|
||||
|
||||
break;
|
||||
case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
|
||||
|
||||
if(entry->value < NGHTTP2_MAX_FRAME_SIZE_MIN ||
|
||||
entry->value > NGHTTP2_MAX_FRAME_SIZE_MAX) {
|
||||
return session_handle_invalid_connection
|
||||
(session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
"SETTINGS: invalid SETTINGS_MAX_FRAME_SIZE");
|
||||
}
|
||||
|
||||
session->remote_settings.max_frame_size = entry->value;
|
||||
|
||||
break;
|
||||
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
|
||||
|
||||
session->remote_settings.max_header_set_size = entry->value;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -3722,7 +3732,8 @@ static int session_on_connection_window_update_received
|
|||
{
|
||||
int rv;
|
||||
/* Handle connection-level flow control */
|
||||
if(NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment <
|
||||
if(frame->window_update.window_size_increment == 0 ||
|
||||
NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment <
|
||||
session->remote_window_size) {
|
||||
return session_handle_invalid_connection
|
||||
(session, frame, NGHTTP2_FLOW_CONTROL_ERROR, NULL);
|
||||
|
@ -3761,7 +3772,8 @@ static int session_on_stream_window_update_received
|
|||
(session, frame, NGHTTP2_PROTOCOL_ERROR,
|
||||
"WINDOW_UPADATE to reserved stream");
|
||||
}
|
||||
if(NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment <
|
||||
if(frame->window_update.window_size_increment == 0 ||
|
||||
NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment <
|
||||
stream->remote_window_size) {
|
||||
return session_handle_invalid_stream(session, frame,
|
||||
NGHTTP2_FLOW_CONTROL_ERROR);
|
||||
|
@ -3883,9 +3895,9 @@ static int adjust_recv_window_size(int32_t *recv_window_size_ptr,
|
|||
|
||||
/*
|
||||
* Accumulates received bytes |delta_size| for stream-level flow
|
||||
* control and decides whether to send WINDOW_UPDATE to that
|
||||
* stream. If NGHTTP2_OPT_NO_AUTO_STREAM_WINDOW_UPDATE is set,
|
||||
* WINDOW_UPDATE will not be sent.
|
||||
* control and decides whether to send WINDOW_UPDATE to that stream.
|
||||
* If NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE is set, WINDOW_UPDATE will not
|
||||
* be sent.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
|
@ -3909,7 +3921,7 @@ static int session_update_recv_stream_window_size
|
|||
/* We don't have to send WINDOW_UPDATE if the data received is the
|
||||
last chunk in the incoming stream. */
|
||||
if(send_window_update &&
|
||||
!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_STREAM_WINDOW_UPDATE)) {
|
||||
!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {
|
||||
/* We have to use local_settings here because it is the constraint
|
||||
the remote endpoint should honor. */
|
||||
if(nghttp2_should_send_window_update(stream->local_window_size,
|
||||
|
@ -3931,8 +3943,8 @@ static int session_update_recv_stream_window_size
|
|||
/*
|
||||
* Accumulates received bytes |delta_size| for connection-level flow
|
||||
* control and decides whether to send WINDOW_UPDATE to the
|
||||
* connection. If NGHTTP2_OPT_NO_AUTO_CONNECTION_WINDOW_UPDATE is
|
||||
* set, WINDOW_UPDATE will not be sent.
|
||||
* connection. If NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE is set,
|
||||
* WINDOW_UPDATE will not be sent.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
|
@ -3951,30 +3963,92 @@ static int session_update_recv_connection_window_size
|
|||
return nghttp2_session_terminate_session(session,
|
||||
NGHTTP2_FLOW_CONTROL_ERROR);
|
||||
}
|
||||
if(!(session->opt_flags &
|
||||
NGHTTP2_OPTMASK_NO_AUTO_CONNECTION_WINDOW_UPDATE)) {
|
||||
if(!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {
|
||||
|
||||
if(nghttp2_should_send_window_update(session->local_window_size,
|
||||
session->recv_window_size)) {
|
||||
/* Use stream ID 0 to update connection-level flow control
|
||||
window */
|
||||
rv = nghttp2_session_add_window_update(session,
|
||||
NGHTTP2_FLAG_NONE,
|
||||
0,
|
||||
rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, 0,
|
||||
session->recv_window_size);
|
||||
if(rv == 0) {
|
||||
session->recv_window_size = 0;
|
||||
/* recv_ign_window_size keeps track of ignored DATA bytes
|
||||
before any connection-level WINDOW_UPDATE therefore, we can
|
||||
reset it here. */
|
||||
session->recv_ign_window_size = 0;
|
||||
} else {
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
session->recv_window_size = 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int session_update_stream_consumed_size
|
||||
(nghttp2_session *session, nghttp2_stream *stream, size_t delta_size)
|
||||
{
|
||||
int32_t recv_size;
|
||||
int rv;
|
||||
|
||||
if((size_t)stream->consumed_size > NGHTTP2_MAX_WINDOW_SIZE - delta_size) {
|
||||
return nghttp2_session_terminate_session(session,
|
||||
NGHTTP2_FLOW_CONTROL_ERROR);
|
||||
}
|
||||
|
||||
stream->consumed_size += delta_size;
|
||||
|
||||
/* recv_window_size may be smaller than consumed_size, because it
|
||||
may be decreased by negative value with
|
||||
nghttp2_submit_window_update(). */
|
||||
recv_size = nghttp2_min(stream->consumed_size, stream->recv_window_size);
|
||||
|
||||
if(nghttp2_should_send_window_update(stream->local_window_size, recv_size)) {
|
||||
rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE,
|
||||
stream->stream_id, recv_size);
|
||||
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
stream->recv_window_size -= recv_size;
|
||||
stream->consumed_size -= recv_size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int session_update_connection_consumed_size
|
||||
(nghttp2_session *session, size_t delta_size)
|
||||
{
|
||||
int32_t recv_size;
|
||||
int rv;
|
||||
|
||||
if((size_t)session->consumed_size > NGHTTP2_MAX_WINDOW_SIZE - delta_size) {
|
||||
return nghttp2_session_terminate_session(session,
|
||||
NGHTTP2_FLOW_CONTROL_ERROR);
|
||||
}
|
||||
|
||||
session->consumed_size += delta_size;
|
||||
|
||||
/* recv_window_size may be smaller than consumed_size, because it
|
||||
may be decreased by negative value with
|
||||
nghttp2_submit_window_update(). */
|
||||
recv_size = nghttp2_min(session->consumed_size, session->recv_window_size);
|
||||
|
||||
if(nghttp2_should_send_window_update(session->local_window_size,
|
||||
recv_size)) {
|
||||
|
||||
rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, 0,
|
||||
recv_size);
|
||||
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
session->recv_window_size -= recv_size;
|
||||
session->consumed_size -= recv_size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks that we can receive the DATA frame for stream, which is
|
||||
* indicated by |session->iframe.frame.hd.stream_id|. If it is a
|
||||
|
@ -4094,6 +4168,8 @@ static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe)
|
|||
case NGHTTP2_SETTINGS_ENABLE_PUSH:
|
||||
case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
|
||||
case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
|
||||
case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
|
||||
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
|
||||
break;
|
||||
default:
|
||||
DEBUGF(fprintf(stderr, "recv: ignore unknown settings id=0x%02x\n",
|
||||
|
@ -4226,12 +4302,31 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
|||
iframe->frame.hd.flags,
|
||||
iframe->frame.hd.stream_id));
|
||||
|
||||
if(iframe->frame.hd.length > session->local_settings.max_frame_size) {
|
||||
DEBUGF(fprintf(stderr,
|
||||
"recv: legnth is too large %u > %u\n",
|
||||
iframe->frame.hd.length,
|
||||
session->local_settings.max_frame_size));
|
||||
|
||||
busy = 1;
|
||||
|
||||
iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
|
||||
|
||||
rv = nghttp2_session_terminate_session_with_reason
|
||||
(session, NGHTTP2_PROTOCOL_ERROR, "too large frame size");
|
||||
|
||||
if(nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
switch(iframe->frame.hd.type) {
|
||||
case NGHTTP2_DATA: {
|
||||
DEBUGF(fprintf(stderr, "recv: DATA\n"));
|
||||
|
||||
iframe->frame.hd.flags &= (NGHTTP2_FLAG_END_STREAM |
|
||||
NGHTTP2_FLAG_END_SEGMENT |
|
||||
NGHTTP2_FLAG_PADDED);
|
||||
/* Check stream is open. If it is not open or closing,
|
||||
ignore payload. */
|
||||
|
@ -4275,13 +4370,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
|||
DEBUGF(fprintf(stderr, "recv: HEADERS\n"));
|
||||
|
||||
iframe->frame.hd.flags &= (NGHTTP2_FLAG_END_STREAM |
|
||||
NGHTTP2_FLAG_END_SEGMENT |
|
||||
NGHTTP2_FLAG_END_HEADERS |
|
||||
NGHTTP2_FLAG_PADDED |
|
||||
NGHTTP2_FLAG_PRIORITY);
|
||||
|
||||
iframe->headers_payload_length = iframe->frame.hd.length;
|
||||
|
||||
rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd);
|
||||
if(rv < 0) {
|
||||
busy = 1;
|
||||
|
@ -4408,8 +4500,6 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
|||
iframe->frame.hd.flags &= (NGHTTP2_FLAG_END_HEADERS |
|
||||
NGHTTP2_FLAG_PADDED);
|
||||
|
||||
iframe->headers_payload_length = iframe->frame.hd.length;
|
||||
|
||||
rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd);
|
||||
if(rv < 0) {
|
||||
busy = 1;
|
||||
|
@ -4988,27 +5078,6 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
|||
break;
|
||||
}
|
||||
|
||||
iframe->headers_payload_length += cont_hd.length;
|
||||
|
||||
if(iframe->headers_payload_length > NGHTTP2_MAX_HEADERSLEN) {
|
||||
|
||||
DEBUGF(fprintf(stderr,
|
||||
"recv: headers too large %zu\n",
|
||||
iframe->headers_payload_length));
|
||||
|
||||
rv = nghttp2_session_terminate_session_with_reason
|
||||
(session, NGHTTP2_INTERNAL_ERROR, "header is too large");
|
||||
if(nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
busy = 1;
|
||||
|
||||
iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* CONTINUATION won't bear NGHTTP2_PADDED flag */
|
||||
|
||||
iframe->frame.hd.flags |= cont_hd.flags & NGHTTP2_FLAG_END_HEADERS;
|
||||
|
@ -5040,6 +5109,14 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
|||
return rv;
|
||||
}
|
||||
|
||||
/* Pad Length field is consumed immediately */
|
||||
rv = nghttp2_session_consume(session, iframe->frame.hd.stream_id,
|
||||
readlen);
|
||||
|
||||
if(nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
stream = nghttp2_session_get_stream(session,
|
||||
iframe->frame.hd.stream_id);
|
||||
if(stream) {
|
||||
|
@ -5105,6 +5182,18 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
|||
data_readlen = inbound_frame_effective_readlen
|
||||
(iframe, iframe->payloadleft, readlen);
|
||||
|
||||
padlen = readlen - data_readlen;
|
||||
|
||||
if(padlen > 0) {
|
||||
/* Padding is considered as "consumed" immediately */
|
||||
rv = nghttp2_session_consume(session, iframe->frame.hd.stream_id,
|
||||
padlen);
|
||||
|
||||
if(nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUGF(fprintf(stderr, "recv: data_readlen=%zu\n", data_readlen));
|
||||
|
||||
if(stream && data_readlen > 0 &&
|
||||
|
@ -5149,8 +5238,6 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
|||
readlen, iframe->payloadleft));
|
||||
|
||||
if(readlen > 0) {
|
||||
session->recv_ign_window_size += readlen;
|
||||
|
||||
/* Update connection-level flow control window for ignored
|
||||
DATA frame too */
|
||||
rv = session_update_recv_connection_window_size(session, readlen);
|
||||
|
@ -5158,20 +5245,14 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
|
|||
return rv;
|
||||
}
|
||||
|
||||
if((session->opt_flags &
|
||||
NGHTTP2_OPTMASK_NO_AUTO_CONNECTION_WINDOW_UPDATE) &&
|
||||
nghttp2_should_send_window_update
|
||||
(session->local_window_size, session->recv_ign_window_size)) {
|
||||
if(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {
|
||||
|
||||
rv = nghttp2_session_add_window_update
|
||||
(session, NGHTTP2_FLAG_NONE, 0, session->recv_ign_window_size);
|
||||
/* Ignored DATA is considered as "consumed" immediately. */
|
||||
rv = session_update_connection_consumed_size(session, readlen);
|
||||
|
||||
if(nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
session->recv_window_size -= session->recv_ign_window_size;
|
||||
session->recv_ign_window_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5495,7 +5576,7 @@ int nghttp2_session_pack_data(nghttp2_session *session,
|
|||
|
||||
/* Clear flags, because this may contain previous flags of previous
|
||||
DATA */
|
||||
frame->hd.flags &= (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_SEGMENT);
|
||||
frame->hd.flags &= NGHTTP2_FLAG_END_STREAM;
|
||||
flags = NGHTTP2_FLAG_NONE;
|
||||
|
||||
if(data_flags & NGHTTP2_DATA_FLAG_EOF) {
|
||||
|
@ -5503,9 +5584,6 @@ int nghttp2_session_pack_data(nghttp2_session *session,
|
|||
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
flags |= NGHTTP2_FLAG_END_STREAM;
|
||||
}
|
||||
if(frame->hd.flags & NGHTTP2_FLAG_END_SEGMENT) {
|
||||
flags |= NGHTTP2_FLAG_END_SEGMENT;
|
||||
}
|
||||
}
|
||||
|
||||
/* The primary reason of data_frame is pass to the user callback */
|
||||
|
@ -5654,6 +5732,10 @@ uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session,
|
|||
return session->remote_settings.max_concurrent_streams;
|
||||
case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
|
||||
return session->remote_settings.initial_window_size;
|
||||
case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
|
||||
return session->remote_settings.max_frame_size;
|
||||
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
|
||||
return session->remote_settings.max_header_set_size;
|
||||
}
|
||||
|
||||
assert(0);
|
||||
|
@ -5744,3 +5826,36 @@ int nghttp2_session_get_stream_remote_close(nghttp2_session* session,
|
|||
|
||||
return (stream->shut_flags & NGHTTP2_SHUT_RD) != 0;
|
||||
}
|
||||
|
||||
int nghttp2_session_consume(nghttp2_session *session, int32_t stream_id,
|
||||
size_t size)
|
||||
{
|
||||
int rv;
|
||||
nghttp2_stream *stream;
|
||||
|
||||
if(stream_id == 0) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if(!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {
|
||||
return NGHTTP2_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
rv = session_update_connection_consumed_size(session, size);
|
||||
|
||||
if(nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
stream = nghttp2_session_get_stream(session, stream_id);
|
||||
|
||||
if(stream) {
|
||||
rv = session_update_stream_consumed_size(session, stream, size);
|
||||
|
||||
if(nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -43,8 +43,7 @@
|
|||
* Option flags.
|
||||
*/
|
||||
typedef enum {
|
||||
NGHTTP2_OPTMASK_NO_AUTO_STREAM_WINDOW_UPDATE = 1 << 0,
|
||||
NGHTTP2_OPTMASK_NO_AUTO_CONNECTION_WINDOW_UPDATE = 1 << 1
|
||||
NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE = 1 << 0,
|
||||
} nghttp2_optmask;
|
||||
|
||||
typedef enum {
|
||||
|
@ -81,7 +80,7 @@ typedef enum {
|
|||
NGHTTP2_IB_IGN_DATA
|
||||
} nghttp2_inbound_state;
|
||||
|
||||
#define NGHTTP2_INBOUND_NUM_IV 5
|
||||
#define NGHTTP2_INBOUND_NUM_IV 7
|
||||
|
||||
typedef struct {
|
||||
nghttp2_frame frame;
|
||||
|
@ -105,9 +104,6 @@ typedef struct {
|
|||
size_t payloadleft;
|
||||
/* padding length for the current frame */
|
||||
size_t padlen;
|
||||
/* Sum of payload of (HEADERS | PUSH_PROMISE) + possible
|
||||
CONTINUATION received so far. */
|
||||
size_t headers_payload_length;
|
||||
nghttp2_inbound_state state;
|
||||
uint8_t raw_sbuf[8];
|
||||
} nghttp2_inbound_frame;
|
||||
|
@ -117,6 +113,8 @@ typedef struct {
|
|||
uint32_t enable_push;
|
||||
uint32_t max_concurrent_streams;
|
||||
uint32_t initial_window_size;
|
||||
uint32_t max_frame_size;
|
||||
uint32_t max_header_set_size;
|
||||
} nghttp2_settings_storage;
|
||||
|
||||
typedef enum {
|
||||
|
@ -206,15 +204,10 @@ struct nghttp2_session {
|
|||
WINDOW_UPDATE. This could be negative after submitting negative
|
||||
value to WINDOW_UPDATE. */
|
||||
int32_t recv_window_size;
|
||||
/* The number of bytes in ignored DATA frame received without
|
||||
connection-level WINDOW_UPDATE. Since we do not call
|
||||
on_data_chunk_recv_callback for ignored DATA chunk, if
|
||||
nghttp2_option_set_no_auto_connection_window_update is used,
|
||||
application may not have a chance to send connection
|
||||
WINDOW_UPDATE. To fix this, we accumulate those received bytes,
|
||||
and if it exceeds certain number, we automatically send
|
||||
connection-level WINDOW_UPDATE. */
|
||||
int32_t recv_ign_window_size;
|
||||
/* The number of bytes consumed by the application and now is
|
||||
subject to WINDOW_UPDATE. This is only used when auto
|
||||
WINDOW_UPDATE is turned off. */
|
||||
int32_t consumed_size;
|
||||
/* The amount of recv_window_size cut using submitting negative
|
||||
value to WINDOW_UPDATE */
|
||||
int32_t recv_reduction;
|
||||
|
|
|
@ -48,6 +48,7 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
|
|||
stream->remote_window_size = remote_initial_window_size;
|
||||
stream->local_window_size = local_initial_window_size;
|
||||
stream->recv_window_size = 0;
|
||||
stream->consumed_size = 0;
|
||||
stream->recv_reduction = 0;
|
||||
stream->blocked_sent = 0;
|
||||
|
||||
|
|
|
@ -151,6 +151,10 @@ struct nghttp2_stream {
|
|||
WINDOW_UPDATE. This could be negative after submitting negative
|
||||
value to WINDOW_UPDATE */
|
||||
int32_t recv_window_size;
|
||||
/* The number of bytes consumed by the application and now is
|
||||
subject to WINDOW_UPDATE. This is only used when auto
|
||||
WINDOW_UPDATE is turned off. */
|
||||
int32_t consumed_size;
|
||||
/* The amount of recv_window_size cut using submitting negative
|
||||
value to WINDOW_UPDATE */
|
||||
int32_t recv_reduction;
|
||||
|
|
|
@ -81,9 +81,7 @@ static int32_t submit_headers_shared
|
|||
}
|
||||
|
||||
flags_copy =
|
||||
(flags & (NGHTTP2_FLAG_END_STREAM |
|
||||
NGHTTP2_FLAG_END_SEGMENT |
|
||||
NGHTTP2_FLAG_PRIORITY)) |
|
||||
(flags & (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PRIORITY)) |
|
||||
NGHTTP2_FLAG_END_HEADERS;
|
||||
|
||||
if(stream_id == -1) {
|
||||
|
@ -348,14 +346,12 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
|
|||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* recv_ign_window_size keeps track of ignored DATA bytes before
|
||||
any connection-level WINDOW_UPDATE therefore, we can reset it
|
||||
here. */
|
||||
session->recv_ign_window_size = 0;
|
||||
} else {
|
||||
stream = nghttp2_session_get_stream(session, stream_id);
|
||||
if(stream) {
|
||||
if(!stream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rv = nghttp2_adjust_local_window_size(&stream->local_window_size,
|
||||
&stream->recv_window_size,
|
||||
&stream->recv_reduction,
|
||||
|
@ -363,12 +359,17 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
|
|||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(window_size_increment > 0) {
|
||||
if(stream_id == 0) {
|
||||
session->consumed_size =
|
||||
nghttp2_max(0, session->consumed_size - window_size_increment);
|
||||
} else {
|
||||
stream->consumed_size =
|
||||
nghttp2_max(0, stream->consumed_size - window_size_increment);
|
||||
}
|
||||
|
||||
return nghttp2_session_add_window_update(session, flags, stream_id,
|
||||
window_size_increment);
|
||||
}
|
||||
|
@ -535,8 +536,7 @@ int nghttp2_submit_data(nghttp2_session *session, uint8_t flags,
|
|||
{
|
||||
int rv;
|
||||
nghttp2_private_data *data_frame;
|
||||
uint8_t nflags = flags & (NGHTTP2_FLAG_END_STREAM |
|
||||
NGHTTP2_FLAG_END_SEGMENT);
|
||||
uint8_t nflags = flags & NGHTTP2_FLAG_END_STREAM;
|
||||
|
||||
if(stream_id == 0) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
|
|
|
@ -252,9 +252,6 @@ cdef extern from 'nghttp2/nghttp2.h':
|
|||
|
||||
void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater)
|
||||
|
||||
void nghttp2_hd_deflate_set_no_refset(nghttp2_hd_deflater *deflater,
|
||||
uint8_t no_refset)
|
||||
|
||||
int nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater,
|
||||
size_t hd_table_bufsize_max)
|
||||
|
||||
|
@ -291,9 +288,6 @@ cdef extern from 'nghttp2_hd.h':
|
|||
# This is macro
|
||||
int NGHTTP2_HD_ENTRY_OVERHEAD
|
||||
|
||||
ctypedef enum nghttp2_hd_flags:
|
||||
NGHTTP2_HD_FLAG_REFSET
|
||||
|
||||
ctypedef enum nghttp2_hd_inflate_flag:
|
||||
NGHTTP2_HD_INFLATE_EMIT
|
||||
NGHTTP2_HD_INFLATE_FINAL
|
||||
|
|
|
@ -53,7 +53,7 @@ if __name__ == '__main__':
|
|||
|
||||
testdata = json.loads(input)
|
||||
|
||||
if 'draft' not in testdata or testdata['draft'] != 8:
|
||||
if 'draft' not in testdata or testdata['draft'] != 9:
|
||||
sys.stderr.write('Not supported\n')
|
||||
continue
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ import nghttp2
|
|||
def testsuite(testdata, filename, outdir, table_size, deflate_table_size,
|
||||
simulate_table_size_change):
|
||||
res = {
|
||||
'draft':8,
|
||||
'draft':9,
|
||||
'description': '''\
|
||||
Encoded by nghttp2. The basic encoding strategy is described in \
|
||||
http://lists.w3.org/Archives/Public/ietf-http-wg/2013JulSep/1135.html \
|
||||
|
|
|
@ -33,12 +33,11 @@ HD_ENTRY_OVERHEAD = cnghttp2.NGHTTP2_HD_ENTRY_OVERHEAD
|
|||
|
||||
class HDTableEntry:
|
||||
|
||||
def __init__(self, name, namelen, value, valuelen, ref):
|
||||
def __init__(self, name, namelen, value, valuelen):
|
||||
self.name = name
|
||||
self.namelen = namelen
|
||||
self.value = value
|
||||
self.valuelen = valuelen
|
||||
self.ref = ref
|
||||
|
||||
def space(self):
|
||||
return self.namelen + self.valuelen + HD_ENTRY_OVERHEAD
|
||||
|
@ -52,8 +51,7 @@ cdef _get_hd_table(cnghttp2.nghttp2_hd_context *ctx):
|
|||
k = _get_pybytes(entry.nv.name, entry.nv.namelen)
|
||||
v = _get_pybytes(entry.nv.value, entry.nv.valuelen)
|
||||
res.append(HDTableEntry(k, entry.nv.namelen,
|
||||
v, entry.nv.valuelen,
|
||||
(entry.flags & cnghttp2.NGHTTP2_HD_FLAG_REFSET) != 0))
|
||||
v, entry.nv.valuelen))
|
||||
return res
|
||||
|
||||
cdef _get_pybytes(uint8_t *b, uint16_t blen):
|
||||
|
@ -143,15 +141,6 @@ cdef class HDDeflater:
|
|||
|
||||
return res
|
||||
|
||||
def set_no_refset(self, no_refset):
|
||||
'''Tells the compressor not to use reference set if |no_refset| is
|
||||
nonzero. If |no_refset| is nonzero, on each invocation of
|
||||
deflate(), compressor first emits index=0 to clear up
|
||||
reference set.
|
||||
|
||||
'''
|
||||
cnghttp2.nghttp2_hd_deflate_set_no_refset(self._deflater, no_refset)
|
||||
|
||||
def change_table_size(self, hd_table_bufsize_max):
|
||||
'''Changes header table size to |hd_table_bufsize_max| byte.
|
||||
|
||||
|
@ -243,16 +232,14 @@ def print_hd_table(hdtable):
|
|||
function does not work if header name/value cannot be decoded using
|
||||
UTF-8 encoding.
|
||||
|
||||
s=N means the entry occupies N bytes in header table. if r=y, then
|
||||
the entry is in the reference set.
|
||||
s=N means the entry occupies N bytes in header table.
|
||||
|
||||
'''
|
||||
idx = 0
|
||||
for entry in hdtable:
|
||||
idx += 1
|
||||
print('[{}] (s={}) (r={}) {}: {}'\
|
||||
print('[{}] (s={}) {}: {}'\
|
||||
.format(idx, entry.space(),
|
||||
'y' if entry.ref else 'n',
|
||||
entry.name.decode('utf-8'),
|
||||
entry.value.decode('utf-8')))
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ namespace {
|
|||
void append_nv(Stream *stream, const std::vector<nghttp2_nv>& nva)
|
||||
{
|
||||
for(auto& nv : nva) {
|
||||
http2::split_add_header(stream->headers,
|
||||
http2::add_header(stream->headers,
|
||||
nv.name, nv.namelen, nv.value, nv.valuelen,
|
||||
nv.flags & NGHTTP2_NV_FLAG_NO_INDEX);
|
||||
}
|
||||
|
@ -843,6 +843,16 @@ int Http2Handler::submit_response(const std::string& status,
|
|||
data_prd);
|
||||
}
|
||||
|
||||
int Http2Handler::submit_non_final_response(const std::string& status,
|
||||
int32_t stream_id)
|
||||
{
|
||||
auto nva = std::vector<nghttp2_nv>{
|
||||
http2::make_nv_ls(":status", status)
|
||||
};
|
||||
return nghttp2_submit_headers(session_, NGHTTP2_FLAG_NONE, stream_id,
|
||||
nullptr, nva.data(), nva.size(), nullptr);
|
||||
}
|
||||
|
||||
int Http2Handler::submit_push_promise(Stream *stream,
|
||||
const std::string& push_path)
|
||||
{
|
||||
|
@ -1158,7 +1168,7 @@ int on_header_callback(nghttp2_session *session,
|
|||
if(!http2::check_nv(name, namelen, value, valuelen)) {
|
||||
return 0;
|
||||
}
|
||||
http2::split_add_header(stream->headers, name, namelen, value, valuelen,
|
||||
http2::add_header(stream->headers, name, namelen, value, valuelen,
|
||||
flags & NGHTTP2_NV_FLAG_NO_INDEX);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1247,7 +1257,7 @@ int hd_on_frame_recv_callback
|
|||
if(frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
|
||||
|
||||
http2::normalize_headers(stream->headers);
|
||||
if(!http2::check_http2_headers(stream->headers)) {
|
||||
if(!http2::check_http2_request_headers(stream->headers)) {
|
||||
hd->submit_rst_stream(stream, NGHTTP2_PROTOCOL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1266,6 +1276,12 @@ int hd_on_frame_recv_callback
|
|||
return 0;
|
||||
}
|
||||
|
||||
auto expect100 = http2::get_header(stream->headers, "expect");
|
||||
|
||||
if(expect100 && util::strieq("100-continue", expect100->value.c_str())) {
|
||||
hd->submit_non_final_response("100", frame->hd.stream_id);
|
||||
}
|
||||
|
||||
if(hd->get_config()->early_response) {
|
||||
prepare_response(stream, hd);
|
||||
}
|
||||
|
|
|
@ -126,6 +126,8 @@ public:
|
|||
const std::vector<std::pair<std::string, std::string>>& headers,
|
||||
nghttp2_data_provider *data_prd);
|
||||
|
||||
int submit_non_final_response(const std::string& status, int32_t stream_id);
|
||||
|
||||
int submit_push_promise(Stream *stream, const std::string& push_path);
|
||||
|
||||
int submit_rst_stream(Stream *stream, nghttp2_error_code error_code);
|
||||
|
|
|
@ -221,12 +221,6 @@ 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_PADDED) {
|
||||
if(!s.empty()) {
|
||||
s += " | ";
|
||||
|
@ -238,12 +232,6 @@ 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_END_HEADERS) {
|
||||
if(!s.empty()) {
|
||||
s += " | ";
|
||||
|
|
|
@ -43,8 +43,6 @@ json_t* dump_header_table(nghttp2_hd_context *context)
|
|||
json_object_set_new(outent, "index", json_integer(i + 1));
|
||||
dump_val(outent, "name", ent->nv.name, ent->nv.namelen);
|
||||
dump_val(outent, "value", ent->nv.value, ent->nv.valuelen);
|
||||
json_object_set_new(outent, "referenced",
|
||||
json_boolean(ent->flags & NGHTTP2_HD_FLAG_REFSET));
|
||||
json_object_set_new(outent, "size",
|
||||
json_integer(ent->nv.namelen + ent->nv.valuelen +
|
||||
NGHTTP2_HD_ENTRY_OVERHEAD));
|
||||
|
|
|
@ -53,7 +53,6 @@ typedef struct {
|
|||
size_t deflate_table_size;
|
||||
int http1text;
|
||||
int dump_header_table;
|
||||
int no_refset;
|
||||
} deflate_config;
|
||||
|
||||
static deflate_config config;
|
||||
|
@ -198,7 +197,6 @@ static int deflate_hd_json(json_t *obj, nghttp2_hd_deflater *deflater, int seq)
|
|||
static void init_deflater(nghttp2_hd_deflater *deflater)
|
||||
{
|
||||
nghttp2_hd_deflate_init2(deflater, config.deflate_table_size);
|
||||
nghttp2_hd_deflate_set_no_refset(deflater, config.no_refset);
|
||||
nghttp2_hd_deflate_change_table_size(deflater, config.table_size);
|
||||
}
|
||||
|
||||
|
@ -387,8 +385,7 @@ OPTIONS:
|
|||
buffer.
|
||||
Default: 4096
|
||||
-d, --dump-header-table
|
||||
Output dynamic header table.
|
||||
-c, --no-refset Don't use reference set.)"
|
||||
Output dynamic header table.)"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
|
@ -397,7 +394,6 @@ static struct option long_options[] = {
|
|||
{"table-size", required_argument, nullptr, 's'},
|
||||
{"deflate-table-size", required_argument, nullptr, 'S'},
|
||||
{"dump-header-table", no_argument, nullptr, 'd'},
|
||||
{"no-refset", no_argument, nullptr, 'c'},
|
||||
{nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
|
@ -409,10 +405,9 @@ int main(int argc, char **argv)
|
|||
config.deflate_table_size = NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE;
|
||||
config.http1text = 0;
|
||||
config.dump_header_table = 0;
|
||||
config.no_refset = 0;
|
||||
while(1) {
|
||||
int option_index = 0;
|
||||
int c = getopt_long(argc, argv, "S:cdhs:t", long_options, &option_index);
|
||||
int c = getopt_long(argc, argv, "S:dhs:t", long_options, &option_index);
|
||||
if(c == -1) {
|
||||
break;
|
||||
}
|
||||
|
@ -446,10 +441,6 @@ int main(int argc, char **argv)
|
|||
// --dump-header-table
|
||||
config.dump_header_table = 1;
|
||||
break;
|
||||
case 'c':
|
||||
// --no-refset
|
||||
config.no_refset = 1;
|
||||
break;
|
||||
case '?':
|
||||
exit(EXIT_FAILURE);
|
||||
default:
|
||||
|
|
126
src/http2.cc
126
src/http2.cc
|
@ -162,10 +162,34 @@ namespace {
|
|||
size_t DISALLOWED_HDLEN = sizeof(DISALLOWED_HD)/sizeof(DISALLOWED_HD[0]);
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
const char *REQUEST_PSEUDO_HD[] = {
|
||||
":authority",
|
||||
":method",
|
||||
":path",
|
||||
":scheme",
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
size_t REQUEST_PSEUDO_HDLEN =
|
||||
sizeof(REQUEST_PSEUDO_HD) / sizeof(REQUEST_PSEUDO_HD[0]);
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
const char *RESPONSE_PSEUDO_HD[] = {
|
||||
":status",
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
size_t RESPONSE_PSEUDO_HDLEN =
|
||||
sizeof(RESPONSE_PSEUDO_HD) / sizeof(RESPONSE_PSEUDO_HD[0]);
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
const char *IGN_HD[] = {
|
||||
"connection",
|
||||
"expect",
|
||||
"http2-settings",
|
||||
"keep-alive",
|
||||
"proxy-connection",
|
||||
|
@ -186,7 +210,6 @@ namespace {
|
|||
const char *HTTP1_IGN_HD[] = {
|
||||
"connection",
|
||||
"cookie",
|
||||
"expect",
|
||||
"http2-settings",
|
||||
"keep-alive",
|
||||
"proxy-connection",
|
||||
|
@ -225,6 +248,61 @@ bool check_http2_headers(const Headers& nva)
|
|||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
template<typename InputIterator>
|
||||
bool check_pseudo_headers(const Headers& nva,
|
||||
InputIterator allowed_first,
|
||||
InputIterator allowed_last)
|
||||
{
|
||||
// strict checking for pseudo headers.
|
||||
for(auto& hd : nva) {
|
||||
auto c = hd.name.c_str()[0];
|
||||
|
||||
if(c < ':') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(c > ':') {
|
||||
break;
|
||||
}
|
||||
|
||||
auto i = allowed_first;
|
||||
|
||||
for(; i != allowed_last; ++i) {
|
||||
if(hd.name == *i) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(i == allowed_last) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool check_http2_request_headers(const Headers& nva)
|
||||
{
|
||||
if(!check_http2_headers(nva)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return check_pseudo_headers(nva, REQUEST_PSEUDO_HD,
|
||||
REQUEST_PSEUDO_HD + REQUEST_PSEUDO_HDLEN);
|
||||
}
|
||||
|
||||
bool check_http2_response_headers(const Headers& nva)
|
||||
{
|
||||
if(!check_http2_headers(nva)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return check_pseudo_headers(nva, RESPONSE_PSEUDO_HD,
|
||||
RESPONSE_PSEUDO_HD + RESPONSE_PSEUDO_HDLEN);
|
||||
}
|
||||
|
||||
void normalize_headers(Headers& nva)
|
||||
{
|
||||
for(auto& kv : nva) {
|
||||
|
@ -275,31 +353,12 @@ Headers::value_type to_header(const uint8_t *name, size_t namelen,
|
|||
no_index);
|
||||
}
|
||||
|
||||
void split_add_header(Headers& nva,
|
||||
void add_header(Headers& nva,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
bool no_index)
|
||||
{
|
||||
if(valuelen == 0) {
|
||||
nva.push_back(to_header(name, namelen, value, valuelen, no_index));
|
||||
return;
|
||||
}
|
||||
auto j = value;
|
||||
auto end = value + valuelen;
|
||||
for(;;) {
|
||||
// Skip 0 length value
|
||||
j = std::find_if(j, end,
|
||||
[](uint8_t c)
|
||||
{
|
||||
return c != '\0';
|
||||
});
|
||||
if(j == end) {
|
||||
break;
|
||||
}
|
||||
auto l = std::find(j, end, '\0');
|
||||
nva.push_back(to_header(name, namelen, j, l-j, no_index));
|
||||
j = l;
|
||||
}
|
||||
}
|
||||
|
||||
const Headers::value_type* get_unique_header(const Headers& nva,
|
||||
|
@ -355,29 +414,6 @@ nghttp2_nv make_nv(const std::string& name, const std::string& value,
|
|||
name.size(), value.size(), flags};
|
||||
}
|
||||
|
||||
Headers concat_norm_headers(Headers headers)
|
||||
{
|
||||
auto res = Headers();
|
||||
res.reserve(headers.size());
|
||||
for(auto& kv : headers) {
|
||||
if(!res.empty() && res.back().name == kv.name &&
|
||||
kv.name != "cookie" && kv.name != "set-cookie") {
|
||||
|
||||
auto& last = res.back();
|
||||
|
||||
if(!kv.value.empty()) {
|
||||
last.value.append(1, '\0');
|
||||
last.value += kv.value;
|
||||
}
|
||||
// We do ORing nv flags. This is done even if value is empty.
|
||||
last.no_index |= kv.no_index;
|
||||
} else {
|
||||
res.push_back(std::move(kv));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void copy_norm_headers_to_nva
|
||||
(std::vector<nghttp2_nv>& nva, const Headers& headers)
|
||||
{
|
||||
|
|
26
src/http2.h
26
src/http2.h
|
@ -96,6 +96,16 @@ bool check_http2_allowed_header(const char *name);
|
|||
// contains such headers.
|
||||
bool check_http2_headers(const Headers& nva);
|
||||
|
||||
// Calls check_http2_headers() and also checks that |nva| only
|
||||
// contains pseudo headers allowed in request. Returns true if all
|
||||
// checks passed.
|
||||
bool check_http2_request_headers(const Headers& nva);
|
||||
|
||||
// Calls check_http2_headers() and also checks that |nva| only
|
||||
// contains pseudo headers allowed in response. Returns true if all
|
||||
// checks passed.
|
||||
bool check_http2_response_headers(const Headers& nva);
|
||||
|
||||
bool name_less(const Headers::value_type& lhs, const Headers::value_type& rhs);
|
||||
|
||||
void normalize_headers(Headers& nva);
|
||||
|
@ -104,12 +114,10 @@ Headers::value_type to_header(const uint8_t *name, size_t namelen,
|
|||
const uint8_t *value, size_t valuelen,
|
||||
bool no_index);
|
||||
|
||||
// Add name/value pairs to |nva|. The name is given in the |name| with
|
||||
// |namelen| bytes. This function inspects the |value| and split it
|
||||
// using '\0' as delimiter. Each token is added to the |nva| with the
|
||||
// name |name|. If |no_index| is true, this name/value pair won't be
|
||||
// indexed when it is forwarded to the next hop.
|
||||
void split_add_header(Headers& nva,
|
||||
// Add name/value pairs to |nva|. If |no_index| is true, this
|
||||
// name/value pair won't be indexed when it is forwarded to the next
|
||||
// hop.
|
||||
void add_header(Headers& nva,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
bool no_index);
|
||||
|
@ -143,12 +151,6 @@ bool value_lws(const Headers::value_type *nv);
|
|||
// and not contain illegal characters.
|
||||
bool non_empty_value(const Headers::value_type *nv);
|
||||
|
||||
// Concatenates field with same value by NULL as delimiter and returns
|
||||
// new vector containing the resulting header fields. cookie and
|
||||
// set-cookie header fields won't be concatenated. This function
|
||||
// assumes that the |headers| is sorted by name.
|
||||
Headers concat_norm_headers(Headers headers);
|
||||
|
||||
// Creates nghttp2_nv using |name| and |value| and returns it. The
|
||||
// returned value only references the data pointer to name.c_str() and
|
||||
// value.c_str(). If |no_index| is true, nghttp2_nv flags member has
|
||||
|
|
|
@ -72,26 +72,18 @@ void test_http2_sort_nva(void)
|
|||
check_nv({"delta", "5"}, &nva[5]);
|
||||
}
|
||||
|
||||
void test_http2_split_add_header(void)
|
||||
void test_http2_add_header(void)
|
||||
{
|
||||
const uint8_t concatval[] = { '4', 0x00, 0x00, '6', 0x00, '5', '9', 0x00 };
|
||||
auto nva = Headers();
|
||||
http2::split_add_header(nva, (const uint8_t*)"delta", 5,
|
||||
concatval, sizeof(concatval), false);
|
||||
CU_ASSERT(Headers::value_type("delta", "4") == nva[0]);
|
||||
CU_ASSERT(Headers::value_type("delta", "6") == nva[1]);
|
||||
CU_ASSERT(Headers::value_type("delta", "59") == nva[2]);
|
||||
|
||||
nva.clear();
|
||||
|
||||
http2::split_add_header(nva, (const uint8_t*)"alpha", 5,
|
||||
http2::add_header(nva, (const uint8_t*)"alpha", 5,
|
||||
(const uint8_t*)"123", 3, false);
|
||||
CU_ASSERT(Headers::value_type("alpha", "123") == nva[0]);
|
||||
CU_ASSERT(!nva[0].no_index);
|
||||
|
||||
nva.clear();
|
||||
|
||||
http2::split_add_header(nva, (const uint8_t*)"alpha", 5,
|
||||
http2::add_header(nva, (const uint8_t*)"alpha", 5,
|
||||
(const uint8_t*)"", 0, true);
|
||||
CU_ASSERT(Headers::value_type("alpha", "") == nva[0]);
|
||||
CU_ASSERT(nva[0].no_index);
|
||||
|
@ -119,6 +111,21 @@ void test_http2_check_http2_headers(void)
|
|||
{ "te2", "3" }
|
||||
};
|
||||
CU_ASSERT(http2::check_http2_headers(nva3));
|
||||
|
||||
auto nva4 = Headers{
|
||||
{ ":authority", "1" },
|
||||
{ ":method", "2" },
|
||||
{ ":path", "3" },
|
||||
{ ":scheme", "4" }
|
||||
};
|
||||
CU_ASSERT(http2::check_http2_request_headers(nva4));
|
||||
CU_ASSERT(!http2::check_http2_response_headers(nva4));
|
||||
|
||||
auto nva5 = Headers{
|
||||
{ ":status", "1" }
|
||||
};
|
||||
CU_ASSERT(!http2::check_http2_request_headers(nva5));
|
||||
CU_ASSERT(http2::check_http2_response_headers(nva5));
|
||||
}
|
||||
|
||||
void test_http2_get_unique_header(void)
|
||||
|
@ -199,24 +206,12 @@ auto headers = Headers
|
|||
{"zulu", "12"}};
|
||||
} // namespace
|
||||
|
||||
void test_http2_concat_norm_headers(void)
|
||||
{
|
||||
auto hds = headers;
|
||||
hds.emplace_back("cookie", "foo");
|
||||
hds.emplace_back("cookie", "bar");
|
||||
hds.emplace_back("set-cookie", "baz");
|
||||
hds.emplace_back("set-cookie", "buzz");
|
||||
auto res = http2::concat_norm_headers(hds);
|
||||
CU_ASSERT(14 == res.size());
|
||||
CU_ASSERT(std::string("2") + '\0' + std::string("3") == res[2].value);
|
||||
}
|
||||
|
||||
void test_http2_copy_norm_headers_to_nva(void)
|
||||
{
|
||||
std::vector<nghttp2_nv> nva;
|
||||
http2::copy_norm_headers_to_nva(nva, headers);
|
||||
CU_ASSERT(6 == nva.size());
|
||||
auto ans = std::vector<int>{0, 1, 4, 6, 7, 12};
|
||||
CU_ASSERT(7 == nva.size());
|
||||
auto ans = std::vector<int>{0, 1, 4, 5, 6, 7, 12};
|
||||
for(size_t i = 0; i < ans.size(); ++i) {
|
||||
check_nv(headers[ans[i]], &nva[i]);
|
||||
|
||||
|
@ -236,6 +231,7 @@ void test_http2_build_http1_headers_from_norm_headers(void)
|
|||
"Alpha: 0\r\n"
|
||||
"Bravo: 1\r\n"
|
||||
"Delta: 4\r\n"
|
||||
"Expect: 5\r\n"
|
||||
"Foxtrot: 6\r\n"
|
||||
"Tango: 7\r\n"
|
||||
"Te: 8\r\n"
|
||||
|
|
|
@ -27,13 +27,12 @@
|
|||
|
||||
namespace shrpx {
|
||||
|
||||
void test_http2_split_add_header(void);
|
||||
void test_http2_add_header(void);
|
||||
void test_http2_sort_nva(void);
|
||||
void test_http2_check_http2_headers(void);
|
||||
void test_http2_get_unique_header(void);
|
||||
void test_http2_get_header(void);
|
||||
void test_http2_value_lws(void);
|
||||
void test_http2_concat_norm_headers(void);
|
||||
void test_http2_copy_norm_headers_to_nva(void);
|
||||
void test_http2_build_http1_headers_from_norm_headers(void);
|
||||
void test_http2_lws(void);
|
||||
|
|
|
@ -1048,8 +1048,6 @@ int submit_request
|
|||
return lhs.name < rhs.name;
|
||||
});
|
||||
|
||||
build_headers = http2::concat_norm_headers(std::move(build_headers));
|
||||
|
||||
auto nva = std::vector<nghttp2_nv>();
|
||||
nva.reserve(build_headers.size());
|
||||
|
||||
|
@ -1264,7 +1262,7 @@ int on_header_callback(nghttp2_session *session,
|
|||
if(!req) {
|
||||
break;
|
||||
}
|
||||
http2::split_add_header(req->res_nva, name, namelen, value, valuelen,
|
||||
http2::add_header(req->res_nva, name, namelen, value, valuelen,
|
||||
flags & NGHTTP2_NV_FLAG_NO_INDEX);
|
||||
break;
|
||||
}
|
||||
|
@ -1274,7 +1272,7 @@ int on_header_callback(nghttp2_session *session,
|
|||
if(!req) {
|
||||
break;
|
||||
}
|
||||
http2::split_add_header(req->push_req_nva, name, namelen, value, valuelen,
|
||||
http2::add_header(req->push_req_nva, name, namelen, value, valuelen,
|
||||
flags & NGHTTP2_NV_FLAG_NO_INDEX);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -71,8 +71,7 @@ int main(int argc, char* argv[])
|
|||
shrpx::test_shrpx_ssl_create_lookup_tree) ||
|
||||
!CU_add_test(pSuite, "ssl_cert_lookup_tree_add_cert_from_file",
|
||||
shrpx::test_shrpx_ssl_cert_lookup_tree_add_cert_from_file) ||
|
||||
!CU_add_test(pSuite, "http2_split_add_header",
|
||||
shrpx::test_http2_split_add_header) ||
|
||||
!CU_add_test(pSuite, "http2_add_header", shrpx::test_http2_add_header) ||
|
||||
!CU_add_test(pSuite, "http2_sort_nva", shrpx::test_http2_sort_nva) ||
|
||||
!CU_add_test(pSuite, "http2_check_http2_headers",
|
||||
shrpx::test_http2_check_http2_headers) ||
|
||||
|
@ -82,8 +81,6 @@ int main(int argc, char* argv[])
|
|||
shrpx::test_http2_get_header) ||
|
||||
!CU_add_test(pSuite, "http2_value_lws",
|
||||
shrpx::test_http2_value_lws) ||
|
||||
!CU_add_test(pSuite, "http2_concat_norm_headers",
|
||||
shrpx::test_http2_concat_norm_headers) ||
|
||||
!CU_add_test(pSuite, "http2_copy_norm_headers_to_nva",
|
||||
shrpx::test_http2_copy_norm_headers_to_nva) ||
|
||||
!CU_add_test(pSuite, "http2_build_http1_headers_from_norm_headers",
|
||||
|
|
|
@ -562,9 +562,7 @@ void fill_default_config()
|
|||
|
||||
nghttp2_option_new(&mod_config()->http2_option);
|
||||
|
||||
nghttp2_option_set_no_auto_stream_window_update
|
||||
(mod_config()->http2_option, 1);
|
||||
nghttp2_option_set_no_auto_connection_window_update
|
||||
nghttp2_option_set_no_auto_window_update
|
||||
(mod_config()->http2_option, 1);
|
||||
|
||||
mod_config()->tls_proto_mask = 0;
|
||||
|
|
|
@ -46,6 +46,8 @@ Downstream::Downstream(Upstream *upstream, int stream_id, int priority)
|
|||
response_body_buf_(nullptr),
|
||||
request_headers_sum_(0),
|
||||
response_headers_sum_(0),
|
||||
request_datalen_(0),
|
||||
response_datalen_(0),
|
||||
stream_id_(stream_id),
|
||||
priority_(priority),
|
||||
downstream_stream_id_(-1),
|
||||
|
@ -63,12 +65,12 @@ Downstream::Downstream(Upstream *upstream, int stream_id, int priority)
|
|||
http2_settings_seen_(false),
|
||||
chunked_request_(false),
|
||||
request_connection_close_(false),
|
||||
request_expect_100_continue_(false),
|
||||
request_header_key_prev_(false),
|
||||
request_http2_expect_body_(false),
|
||||
chunked_response_(false),
|
||||
response_connection_close_(false),
|
||||
response_header_key_prev_(false)
|
||||
response_header_key_prev_(false),
|
||||
expect_final_response_(false)
|
||||
{}
|
||||
|
||||
Downstream::~Downstream()
|
||||
|
@ -230,11 +232,6 @@ Headers::const_iterator Downstream::get_norm_request_header
|
|||
return get_norm_header(request_headers_, name);
|
||||
}
|
||||
|
||||
void Downstream::concat_norm_request_headers()
|
||||
{
|
||||
request_headers_ = http2::concat_norm_headers(std::move(request_headers_));
|
||||
}
|
||||
|
||||
void Downstream::add_request_header(std::string name, std::string value)
|
||||
{
|
||||
request_header_key_prev_ = true;
|
||||
|
@ -256,7 +253,7 @@ void Downstream::split_add_request_header
|
|||
bool no_index)
|
||||
{
|
||||
request_headers_sum_ += namelen + valuelen;
|
||||
http2::split_add_header(request_headers_, name, namelen, value, valuelen,
|
||||
http2::add_header(request_headers_, name, namelen, value, valuelen,
|
||||
no_index);
|
||||
}
|
||||
|
||||
|
@ -429,11 +426,6 @@ void Downstream::set_request_http2_expect_body(bool f)
|
|||
request_http2_expect_body_ = f;
|
||||
}
|
||||
|
||||
bool Downstream::get_expect_100_continue() const
|
||||
{
|
||||
return request_expect_100_continue_;
|
||||
}
|
||||
|
||||
bool Downstream::get_output_buffer_full()
|
||||
{
|
||||
if(dconn_) {
|
||||
|
@ -463,7 +455,13 @@ int Downstream::push_upload_data_chunk(const uint8_t *data, size_t datalen)
|
|||
return -1;
|
||||
}
|
||||
request_bodylen_ += datalen;
|
||||
return dconn_->push_upload_data_chunk(data, datalen);
|
||||
if(dconn_->push_upload_data_chunk(data, datalen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
request_datalen_ += datalen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Downstream::end_upload_data()
|
||||
|
@ -485,11 +483,6 @@ void Downstream::normalize_response_headers()
|
|||
http2::normalize_headers(response_headers_);
|
||||
}
|
||||
|
||||
void Downstream::concat_norm_response_headers()
|
||||
{
|
||||
response_headers_ = http2::concat_norm_headers(std::move(response_headers_));
|
||||
}
|
||||
|
||||
Headers::const_iterator Downstream::get_norm_response_header
|
||||
(const std::string& name) const
|
||||
{
|
||||
|
@ -551,7 +544,7 @@ void Downstream::split_add_response_header
|
|||
bool no_index)
|
||||
{
|
||||
response_headers_sum_ += namelen + valuelen;
|
||||
http2::split_add_header(response_headers_, name, namelen, value, valuelen,
|
||||
http2::add_header(response_headers_, name, namelen, value, valuelen,
|
||||
no_index);
|
||||
}
|
||||
|
||||
|
@ -754,11 +747,6 @@ void Downstream::inspect_http1_request()
|
|||
if(util::strifind(hd.value.c_str(), "chunked")) {
|
||||
chunked_request_ = true;
|
||||
}
|
||||
} else if(!request_expect_100_continue_ &&
|
||||
util::strieq(hd.name.c_str(), "expect")) {
|
||||
if(util::strifind(hd.value.c_str(), "100-continue")) {
|
||||
request_expect_100_continue_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -775,6 +763,18 @@ void Downstream::inspect_http1_response()
|
|||
}
|
||||
}
|
||||
|
||||
void Downstream::reset_response()
|
||||
{
|
||||
response_http_status_ = 0;
|
||||
response_major_ = 1;
|
||||
response_minor_ = 1;
|
||||
}
|
||||
|
||||
bool Downstream::get_non_final_response() const
|
||||
{
|
||||
return response_http_status_ / 100 == 1;
|
||||
}
|
||||
|
||||
bool Downstream::get_upgraded() const
|
||||
{
|
||||
return upgraded_;
|
||||
|
@ -816,4 +816,47 @@ void Downstream::set_response_rst_stream_error_code
|
|||
response_rst_stream_error_code_ = error_code;
|
||||
}
|
||||
|
||||
void Downstream::set_expect_final_response(bool f)
|
||||
{
|
||||
expect_final_response_ = f;
|
||||
}
|
||||
|
||||
bool Downstream::get_expect_final_response() const
|
||||
{
|
||||
return expect_final_response_;
|
||||
}
|
||||
|
||||
size_t Downstream::get_request_datalen() const
|
||||
{
|
||||
return request_datalen_;
|
||||
}
|
||||
|
||||
void Downstream::reset_request_datalen()
|
||||
{
|
||||
request_datalen_ = 0;
|
||||
}
|
||||
|
||||
void Downstream::add_response_datalen(size_t len)
|
||||
{
|
||||
response_datalen_ += len;
|
||||
}
|
||||
|
||||
size_t Downstream::get_response_datalen() const
|
||||
{
|
||||
return response_datalen_;
|
||||
}
|
||||
|
||||
void Downstream::reset_response_datalen()
|
||||
{
|
||||
response_datalen_ = 0;
|
||||
}
|
||||
|
||||
bool Downstream::expect_response_body() const
|
||||
{
|
||||
return request_method_ != "HEAD" &&
|
||||
response_http_status_ / 100 != 1 &&
|
||||
response_http_status_ != 304 &&
|
||||
response_http_status_ != 204;
|
||||
}
|
||||
|
||||
} // namespace shrpx
|
||||
|
|
|
@ -99,10 +99,6 @@ public:
|
|||
// called after calling normalize_request_headers().
|
||||
Headers::const_iterator get_norm_request_header
|
||||
(const std::string& name) const;
|
||||
// Concatenates request header fields with same name by NULL as
|
||||
// delimiter. See http2::concat_norm_headers(). This function must
|
||||
// be called after calling normalize_request_headers().
|
||||
void concat_norm_request_headers();
|
||||
void add_request_header(std::string name, std::string value);
|
||||
void set_last_request_header_value(std::string value);
|
||||
|
||||
|
@ -144,9 +140,11 @@ public:
|
|||
const std::string& get_request_user_agent() const;
|
||||
bool get_request_http2_expect_body() const;
|
||||
void set_request_http2_expect_body(bool f);
|
||||
bool get_expect_100_continue() const;
|
||||
int push_upload_data_chunk(const uint8_t *data, size_t datalen);
|
||||
int end_upload_data();
|
||||
size_t get_request_datalen() const;
|
||||
void reset_request_datalen();
|
||||
bool expect_response_body() const;
|
||||
enum {
|
||||
INITIAL,
|
||||
HEADER_COMPLETE,
|
||||
|
@ -162,10 +160,6 @@ public:
|
|||
const Headers& get_response_headers() const;
|
||||
// Makes key lowercase and sort headers by name using <
|
||||
void normalize_response_headers();
|
||||
// Concatenates response header fields with same name by NULL as
|
||||
// delimiter. See http2::concat_norm_headers(). This function must
|
||||
// be called after calling normalize_response_headers().
|
||||
void concat_norm_response_headers();
|
||||
// Returns iterator pointing to the response header with the name
|
||||
// |name|. If multiple header have |name| as name, return first
|
||||
// occurrence from the beginning. If no such header is found,
|
||||
|
@ -215,6 +209,14 @@ public:
|
|||
void set_response_rst_stream_error_code(nghttp2_error_code error_code);
|
||||
// Inspects HTTP/1 response. This checks tranfer-encoding etc.
|
||||
void inspect_http1_response();
|
||||
// Clears some of member variables for response.
|
||||
void reset_response();
|
||||
bool get_non_final_response() const;
|
||||
void set_expect_final_response(bool f);
|
||||
bool get_expect_final_response() const;
|
||||
void add_response_datalen(size_t len);
|
||||
size_t get_response_datalen() const;
|
||||
void reset_response_datalen();
|
||||
|
||||
// Call this method when there is incoming data in downstream
|
||||
// connection.
|
||||
|
@ -254,6 +256,10 @@ private:
|
|||
size_t request_headers_sum_;
|
||||
size_t response_headers_sum_;
|
||||
|
||||
// The number of bytes not consumed by the application yet.
|
||||
size_t request_datalen_;
|
||||
size_t response_datalen_;
|
||||
|
||||
int32_t stream_id_;
|
||||
int32_t priority_;
|
||||
// stream ID in backend connection
|
||||
|
@ -282,13 +288,13 @@ private:
|
|||
|
||||
bool chunked_request_;
|
||||
bool request_connection_close_;
|
||||
bool request_expect_100_continue_;
|
||||
bool request_header_key_prev_;
|
||||
bool request_http2_expect_body_;
|
||||
|
||||
bool chunked_response_;
|
||||
bool response_connection_close_;
|
||||
bool response_header_key_prev_;
|
||||
bool expect_final_response_;
|
||||
};
|
||||
|
||||
} // namespace shrpx
|
||||
|
|
|
@ -67,6 +67,15 @@ Http2DownstreamConnection::~Http2DownstreamConnection()
|
|||
if(submit_rst_stream(downstream_) == 0) {
|
||||
http2session_->notify();
|
||||
}
|
||||
|
||||
if(downstream_->get_downstream_stream_id() != -1) {
|
||||
http2session_->consume(downstream_->get_downstream_stream_id(),
|
||||
downstream_->get_response_datalen());
|
||||
|
||||
downstream_->reset_response_datalen();
|
||||
|
||||
http2session_->notify();
|
||||
}
|
||||
}
|
||||
http2session_->remove_downstream_connection(this);
|
||||
// Downstream and DownstreamConnection may be deleted
|
||||
|
@ -122,6 +131,16 @@ void Http2DownstreamConnection::detach_downstream(Downstream *downstream)
|
|||
if(submit_rst_stream(downstream) == 0) {
|
||||
http2session_->notify();
|
||||
}
|
||||
|
||||
if(downstream_->get_downstream_stream_id() != -1) {
|
||||
http2session_->consume(downstream_->get_downstream_stream_id(),
|
||||
downstream_->get_response_datalen());
|
||||
|
||||
downstream_->reset_response_datalen();
|
||||
|
||||
http2session_->notify();
|
||||
}
|
||||
|
||||
downstream->set_downstream_connection(nullptr);
|
||||
downstream_ = nullptr;
|
||||
|
||||
|
@ -244,7 +263,7 @@ int Http2DownstreamConnection::push_request_headers()
|
|||
downstream_->crumble_request_cookie();
|
||||
}
|
||||
downstream_->normalize_request_headers();
|
||||
downstream_->concat_norm_request_headers();
|
||||
|
||||
auto end_headers = std::end(downstream_->get_request_headers());
|
||||
|
||||
// 7 means:
|
||||
|
@ -302,8 +321,13 @@ int Http2DownstreamConnection::push_request_headers()
|
|||
http2::copy_url_component(path, &u, UF_PATH, url);
|
||||
http2::copy_url_component(query, &u, UF_QUERY, url);
|
||||
if(path.empty()) {
|
||||
if(!authority.empty() &&
|
||||
downstream_->get_request_method() == "OPTIONS") {
|
||||
path = "*";
|
||||
} else {
|
||||
path = "/";
|
||||
}
|
||||
}
|
||||
if(!query.empty()) {
|
||||
path += "?";
|
||||
path += query;
|
||||
|
@ -469,34 +493,29 @@ int Http2DownstreamConnection::end_upload_data()
|
|||
|
||||
int Http2DownstreamConnection::resume_read(IOCtrlReason reason)
|
||||
{
|
||||
int rv1 = 0, rv2 = 0;
|
||||
if(http2session_->get_state() == Http2Session::CONNECTED &&
|
||||
http2session_->get_flow_control()) {
|
||||
int32_t window_size_increment;
|
||||
window_size_increment = http2::determine_window_update_transmission
|
||||
(http2session_->get_session(), 0);
|
||||
if(window_size_increment != -1) {
|
||||
rv1 = http2session_->submit_window_update(nullptr, window_size_increment);
|
||||
if(rv1 == 0) {
|
||||
http2session_->notify();
|
||||
}
|
||||
}
|
||||
if(downstream_ && downstream_->get_downstream_stream_id() != -1) {
|
||||
window_size_increment = http2::determine_window_update_transmission
|
||||
(http2session_->get_session(), downstream_->get_downstream_stream_id());
|
||||
if(window_size_increment != -1) {
|
||||
rv2 = http2session_->submit_window_update(this, window_size_increment);
|
||||
if(rv2 == 0) {
|
||||
http2session_->notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(rv1 == 0 && rv2 == 0) {
|
||||
int rv;
|
||||
|
||||
if(http2session_->get_state() != Http2Session::CONNECTED ||
|
||||
!http2session_->get_flow_control()) {
|
||||
return 0;
|
||||
}
|
||||
DLOG(WARNING, this) << "Sending WINDOW_UPDATE failed";
|
||||
|
||||
if(!downstream_ || downstream_->get_downstream_stream_id() == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rv = http2session_->consume(downstream_->get_downstream_stream_id(),
|
||||
downstream_->get_response_datalen());
|
||||
|
||||
if(rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
downstream_->reset_response_datalen();
|
||||
|
||||
http2session_->notify();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Http2DownstreamConnection::on_read()
|
||||
|
|
|
@ -58,7 +58,6 @@ Http2Session::Http2Session(event_base *evbase, SSL_CTX *ssl_ctx)
|
|||
wrbev_(nullptr),
|
||||
rdbev_(nullptr),
|
||||
settings_timerev_(nullptr),
|
||||
recv_ign_window_size_(0),
|
||||
fd_(-1),
|
||||
state_(DISCONNECTED),
|
||||
notified_(false),
|
||||
|
@ -661,28 +660,6 @@ int Http2Session::submit_rst_stream(int32_t stream_id,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int Http2Session::submit_window_update(Http2DownstreamConnection *dconn,
|
||||
int32_t amount)
|
||||
{
|
||||
assert(state_ == CONNECTED);
|
||||
int rv;
|
||||
int32_t stream_id;
|
||||
if(dconn) {
|
||||
stream_id = dconn->get_downstream()->get_downstream_stream_id();
|
||||
} else {
|
||||
stream_id = 0;
|
||||
recv_ign_window_size_ = 0;
|
||||
}
|
||||
rv = nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE,
|
||||
stream_id, amount);
|
||||
if(rv < NGHTTP2_ERR_FATAL) {
|
||||
SSLOG(FATAL, this) << "nghttp2_submit_window_update() failed: "
|
||||
<< nghttp2_strerror(rv);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Http2Session::submit_priority(Http2DownstreamConnection *dconn,
|
||||
int32_t pri)
|
||||
{
|
||||
|
@ -752,7 +729,6 @@ int on_stream_close_callback
|
|||
(nghttp2_session *session, int32_t stream_id, nghttp2_error_code error_code,
|
||||
void *user_data)
|
||||
{
|
||||
int rv;
|
||||
auto http2session = static_cast<Http2Session*>(user_data);
|
||||
if(LOG_ENABLED(INFO)) {
|
||||
SSLOG(INFO, http2session) << "Stream stream_id=" << stream_id
|
||||
|
@ -769,11 +745,14 @@ int on_stream_close_callback
|
|||
if(dconn) {
|
||||
auto downstream = dconn->get_downstream();
|
||||
if(downstream && downstream->get_downstream_stream_id() == stream_id) {
|
||||
auto upstream = downstream->get_upstream();
|
||||
|
||||
http2session->consume(downstream->get_downstream_stream_id(),
|
||||
downstream->get_response_datalen());
|
||||
|
||||
downstream->reset_response_datalen();
|
||||
|
||||
if(error_code == NGHTTP2_NO_ERROR) {
|
||||
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
||||
rv = upstream->on_downstream_body_complete(downstream);
|
||||
if(rv != 0) {
|
||||
if(downstream->get_response_state() != Downstream::MSG_COMPLETE) {
|
||||
downstream->set_response_state(Downstream::MSG_RESET);
|
||||
}
|
||||
} else {
|
||||
|
@ -841,10 +820,6 @@ int on_header_callback(nghttp2_session *session,
|
|||
uint8_t flags,
|
||||
void *user_data)
|
||||
{
|
||||
if(frame->hd.type != NGHTTP2_HEADERS ||
|
||||
frame->headers.cat != NGHTTP2_HCAT_RESPONSE) {
|
||||
return 0;
|
||||
}
|
||||
auto sd = static_cast<StreamData*>
|
||||
(nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
|
||||
if(!sd || !sd->dconn) {
|
||||
|
@ -854,6 +829,13 @@ int on_header_callback(nghttp2_session *session,
|
|||
if(!downstream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(frame->hd.type != NGHTTP2_HEADERS ||
|
||||
(frame->headers.cat != NGHTTP2_HCAT_RESPONSE &&
|
||||
!downstream->get_expect_final_response())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(downstream->get_response_headers_sum() > Downstream::MAX_HEADERS_SUM) {
|
||||
if(LOG_ENABLED(INFO)) {
|
||||
DLOG(INFO, downstream) << "Too large header block size="
|
||||
|
@ -905,23 +887,20 @@ int on_begin_headers_callback(nghttp2_session *session,
|
|||
|
||||
namespace {
|
||||
int on_response_headers(Http2Session *http2session,
|
||||
Downstream *downstream,
|
||||
nghttp2_session *session,
|
||||
const nghttp2_frame *frame)
|
||||
{
|
||||
int rv;
|
||||
auto sd = static_cast<StreamData*>
|
||||
(nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
|
||||
if(!sd || !sd->dconn) {
|
||||
return 0;
|
||||
}
|
||||
auto downstream = sd->dconn->get_downstream();
|
||||
if(!downstream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto upstream = downstream->get_upstream();
|
||||
|
||||
downstream->normalize_response_headers();
|
||||
auto& nva = downstream->get_response_headers();
|
||||
|
||||
if(!http2::check_http2_headers(nva)) {
|
||||
downstream->set_expect_final_response(false);
|
||||
|
||||
if(!http2::check_http2_response_headers(nva)) {
|
||||
http2session->submit_rst_stream(frame->hd.stream_id,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
downstream->set_response_state(Downstream::MSG_RESET);
|
||||
|
@ -935,8 +914,10 @@ int on_response_headers(Http2Session *http2session,
|
|||
NGHTTP2_PROTOCOL_ERROR);
|
||||
downstream->set_response_state(Downstream::MSG_RESET);
|
||||
call_downstream_readcb(http2session, downstream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
downstream->set_response_http_status(strtoul(status->value.c_str(),
|
||||
nullptr, 10));
|
||||
downstream->set_response_major(2);
|
||||
|
@ -952,6 +933,26 @@ int on_response_headers(Http2Session *http2session,
|
|||
<< "\n" << ss.str();
|
||||
}
|
||||
|
||||
if(downstream->get_non_final_response()) {
|
||||
|
||||
if(LOG_ENABLED(INFO)) {
|
||||
SSLOG(INFO, http2session) << "This is non-final response.";
|
||||
}
|
||||
|
||||
downstream->set_expect_final_response(true);
|
||||
rv = upstream->on_downstream_header_complete(downstream);
|
||||
|
||||
// Now Dowstream's response headers are erased.
|
||||
|
||||
if(rv != 0) {
|
||||
http2session->submit_rst_stream(frame->hd.stream_id,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
downstream->set_response_state(Downstream::MSG_RESET);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto content_length = http2::get_header(nva, "content-length");
|
||||
if(!content_length && downstream->get_request_method() != "HEAD" &&
|
||||
downstream->get_request_method() != "CONNECT") {
|
||||
|
@ -975,7 +976,6 @@ int on_response_headers(Http2Session *http2session,
|
|||
}
|
||||
}
|
||||
|
||||
auto upstream = downstream->get_upstream();
|
||||
downstream->set_response_state(Downstream::HEADER_COMPLETE);
|
||||
downstream->check_upgrade_fulfilled();
|
||||
if(downstream->get_upgraded()) {
|
||||
|
@ -1033,19 +1033,72 @@ int on_frame_recv_callback
|
|||
http2session->submit_rst_stream(frame->hd.stream_id,
|
||||
NGHTTP2_INTERNAL_ERROR);
|
||||
downstream->set_response_state(Downstream::MSG_RESET);
|
||||
|
||||
} else if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
|
||||
if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
||||
|
||||
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
||||
|
||||
rv = upstream->on_downstream_body_complete(downstream);
|
||||
|
||||
if(rv != 0) {
|
||||
downstream->set_response_state(Downstream::MSG_RESET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
call_downstream_readcb(http2session, downstream);
|
||||
break;
|
||||
}
|
||||
case NGHTTP2_HEADERS:
|
||||
case NGHTTP2_HEADERS: {
|
||||
auto sd = static_cast<StreamData*>
|
||||
(nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
|
||||
if(!sd || !sd->dconn) {
|
||||
break;
|
||||
}
|
||||
auto downstream = sd->dconn->get_downstream();
|
||||
|
||||
if(!downstream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE) {
|
||||
rv = on_response_headers(http2session, session, frame);
|
||||
rv = on_response_headers(http2session, downstream, session, frame);
|
||||
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
if(frame->headers.cat == NGHTTP2_HCAT_HEADERS) {
|
||||
if(downstream->get_expect_final_response()) {
|
||||
|
||||
rv = on_response_headers(http2session, downstream, session, frame);
|
||||
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
} else if((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
||||
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
||||
|
||||
auto upstream = downstream->get_upstream();
|
||||
|
||||
rv = upstream->on_downstream_body_complete(downstream);
|
||||
|
||||
if(rv != 0) {
|
||||
downstream->set_response_state(Downstream::MSG_RESET);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NGHTTP2_RST_STREAM: {
|
||||
auto sd = static_cast<StreamData*>
|
||||
(nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
|
||||
|
@ -1055,7 +1108,7 @@ int on_frame_recv_callback
|
|||
downstream->get_downstream_stream_id() == frame->hd.stream_id) {
|
||||
if(downstream->get_upgraded() &&
|
||||
downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
||||
// For tunneled connection, we has to submit RST_STREAM to
|
||||
// For tunneled connection, we have to submit RST_STREAM to
|
||||
// upstream *after* whole response body is sent. We just set
|
||||
// MSG_COMPLETE here. Upstream will take care of that.
|
||||
if(LOG_ENABLED(INFO)) {
|
||||
|
@ -1112,13 +1165,35 @@ int on_data_chunk_recv_callback(nghttp2_session *session,
|
|||
(nghttp2_session_get_stream_user_data(session, stream_id));
|
||||
if(!sd || !sd->dconn) {
|
||||
http2session->submit_rst_stream(stream_id, NGHTTP2_INTERNAL_ERROR);
|
||||
http2session->handle_ign_data_chunk(len);
|
||||
|
||||
if(http2session->consume(stream_id, len) != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
auto downstream = sd->dconn->get_downstream();
|
||||
if(!downstream || downstream->get_downstream_stream_id() != stream_id) {
|
||||
if(!downstream || downstream->get_downstream_stream_id() != stream_id ||
|
||||
!downstream->expect_response_body()) {
|
||||
|
||||
http2session->submit_rst_stream(stream_id, NGHTTP2_INTERNAL_ERROR);
|
||||
http2session->handle_ign_data_chunk(len);
|
||||
|
||||
if(http2session->consume(stream_id, len) != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We don't want DATA after non-final response, which is illegal in
|
||||
// HTTP.
|
||||
if(downstream->get_non_final_response()) {
|
||||
http2session->submit_rst_stream(stream_id, NGHTTP2_PROTOCOL_ERROR);
|
||||
|
||||
if(http2session->consume(stream_id, len) != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1128,9 +1203,16 @@ int on_data_chunk_recv_callback(nghttp2_session *session,
|
|||
rv = upstream->on_downstream_body(downstream, data, len, false);
|
||||
if(rv != 0) {
|
||||
http2session->submit_rst_stream(stream_id, NGHTTP2_INTERNAL_ERROR);
|
||||
http2session->handle_ign_data_chunk(len);
|
||||
|
||||
if(http2session->consume(stream_id, len) != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
downstream->set_response_state(Downstream::MSG_RESET);
|
||||
}
|
||||
|
||||
downstream->add_response_datalen(len);
|
||||
|
||||
call_downstream_readcb(http2session, downstream);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1470,16 +1552,21 @@ SSL* Http2Session::get_ssl() const
|
|||
return ssl_;
|
||||
}
|
||||
|
||||
int Http2Session::handle_ign_data_chunk(size_t len)
|
||||
int Http2Session::consume(int32_t stream_id, size_t len)
|
||||
{
|
||||
int32_t window_size;
|
||||
int rv;
|
||||
|
||||
recv_ign_window_size_ += len;
|
||||
if(!session_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
window_size = nghttp2_session_get_effective_local_window_size(session_);
|
||||
rv = nghttp2_session_consume(session_, stream_id, len);
|
||||
|
||||
if(recv_ign_window_size_ >= window_size / 2) {
|
||||
submit_window_update(nullptr, recv_ign_window_size_);
|
||||
if(rv != 0) {
|
||||
SSLOG(WARNING, this) << "nghttp2_session_consume() returned error: "
|
||||
<< nghttp2_strerror(rv);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -70,10 +70,6 @@ public:
|
|||
|
||||
int submit_rst_stream(int32_t stream_id, nghttp2_error_code error_code);
|
||||
|
||||
// To send WINDOW_UPDATE for a connection, specify nullptr to
|
||||
// |dconn|.
|
||||
int submit_window_update(Http2DownstreamConnection *dconn, int32_t amount);
|
||||
|
||||
int submit_priority(Http2DownstreamConnection *dconn, int32_t pri);
|
||||
|
||||
int terminate_session(nghttp2_error_code error_code);
|
||||
|
@ -108,7 +104,7 @@ public:
|
|||
|
||||
SSL* get_ssl() const;
|
||||
|
||||
int handle_ign_data_chunk(size_t len);
|
||||
int consume(int32_t stream_id, size_t len);
|
||||
|
||||
enum {
|
||||
// Disconnected
|
||||
|
@ -140,7 +136,6 @@ private:
|
|||
bufferevent *wrbev_;
|
||||
bufferevent *rdbev_;
|
||||
event *settings_timerev_;
|
||||
int32_t recv_ign_window_size_;
|
||||
// fd_ is used for proxy connection and no TLS connection. For
|
||||
// direct or TLS connection, it may be -1 even after connection is
|
||||
// established. Use bufferevent_getfd(bev_) to get file descriptor
|
||||
|
|
|
@ -60,12 +60,17 @@ int on_stream_close_callback
|
|||
ULOG(INFO, upstream) << "Stream stream_id=" << stream_id
|
||||
<< " is being closed";
|
||||
}
|
||||
|
||||
auto downstream = upstream->find_downstream(stream_id);
|
||||
|
||||
if(!downstream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
upstream->consume(stream_id, downstream->get_request_datalen());
|
||||
|
||||
downstream->reset_request_datalen();
|
||||
|
||||
if(downstream->get_request_state() == Downstream::CONNECT_FAIL) {
|
||||
upstream->remove_downstream(downstream);
|
||||
|
||||
|
@ -297,7 +302,7 @@ int on_request_headers(Http2Upstream *upstream,
|
|||
http2::dump_nv(get_config()->http2_upstream_dump_request_header, nva);
|
||||
}
|
||||
|
||||
if(!http2::check_http2_headers(nva)) {
|
||||
if(!http2::check_http2_request_headers(nva)) {
|
||||
if(upstream->error_reply(downstream, 400) != 0) {
|
||||
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
|
||||
}
|
||||
|
@ -421,6 +426,8 @@ int on_frame_recv_callback
|
|||
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
downstream->end_upload_data();
|
||||
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
||||
} else {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -476,19 +483,21 @@ int on_data_chunk_recv_callback(nghttp2_session *session,
|
|||
auto upstream = static_cast<Http2Upstream*>(user_data);
|
||||
auto downstream = upstream->find_downstream(stream_id);
|
||||
|
||||
if(!downstream) {
|
||||
upstream->handle_ign_data_chunk(len);
|
||||
return 0;
|
||||
if(!downstream || !downstream->get_downstream_connection()) {
|
||||
if(upstream->consume(stream_id, len) != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
if(!downstream->get_downstream_connection()) {
|
||||
upstream->handle_ign_data_chunk(len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(downstream->push_upload_data_chunk(data, len) != 0) {
|
||||
upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
||||
upstream->handle_ign_data_chunk(len);
|
||||
|
||||
if(upstream->consume(stream_id, len) != 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -586,8 +595,7 @@ nghttp2_error_code infer_upstream_rst_stream_error_code
|
|||
Http2Upstream::Http2Upstream(ClientHandler *handler)
|
||||
: handler_(handler),
|
||||
session_(nullptr),
|
||||
settings_timerev_(nullptr),
|
||||
recv_ign_window_size_(0)
|
||||
settings_timerev_(nullptr)
|
||||
{
|
||||
handler->set_upstream_timeouts(&get_config()->http2_upstream_read_timeout,
|
||||
&get_config()->upstream_write_timeout);
|
||||
|
@ -989,30 +997,6 @@ int Http2Upstream::rst_stream(Downstream *downstream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int Http2Upstream::window_update(Downstream *downstream,
|
||||
int32_t window_size_increment)
|
||||
{
|
||||
int rv;
|
||||
int32_t stream_id;
|
||||
|
||||
if(downstream) {
|
||||
stream_id = downstream->get_stream_id();
|
||||
} else {
|
||||
stream_id = 0;
|
||||
recv_ign_window_size_ = 0;
|
||||
}
|
||||
|
||||
rv = nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE,
|
||||
stream_id, window_size_increment);
|
||||
|
||||
if(rv < NGHTTP2_ERR_FATAL) {
|
||||
ULOG(FATAL, this) << "nghttp2_submit_window_update() failed: "
|
||||
<< nghttp2_strerror(rv);
|
||||
DIE();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Http2Upstream::terminate_session(nghttp2_error_code error_code)
|
||||
{
|
||||
int rv;
|
||||
|
@ -1153,15 +1137,22 @@ nghttp2_session* Http2Upstream::get_http2_session()
|
|||
// nghttp2_session_recv. These calls may delete downstream.
|
||||
int Http2Upstream::on_downstream_header_complete(Downstream *downstream)
|
||||
{
|
||||
int rv;
|
||||
|
||||
if(LOG_ENABLED(INFO)) {
|
||||
if(downstream->get_non_final_response()) {
|
||||
DLOG(INFO, downstream) << "HTTP non-final response header";
|
||||
} else {
|
||||
DLOG(INFO, downstream) << "HTTP response header completed";
|
||||
}
|
||||
}
|
||||
|
||||
downstream->normalize_response_headers();
|
||||
if(!get_config()->http2_proxy && !get_config()->client_proxy) {
|
||||
downstream->rewrite_norm_location_response_header
|
||||
(get_client_handler()->get_upstream_scheme(), get_config()->port);
|
||||
}
|
||||
downstream->concat_norm_response_headers();
|
||||
|
||||
auto end_headers = std::end(downstream->get_response_headers());
|
||||
size_t nheader = downstream->get_response_headers().size();
|
||||
auto nva = std::vector<nghttp2_nv>();
|
||||
|
@ -1172,6 +1163,26 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream)
|
|||
nva.push_back(http2::make_nv_ls(":status", response_status));
|
||||
|
||||
http2::copy_norm_headers_to_nva(nva, downstream->get_response_headers());
|
||||
|
||||
if(downstream->get_non_final_response()) {
|
||||
if(LOG_ENABLED(INFO)) {
|
||||
log_response_headers(downstream, nva);
|
||||
}
|
||||
|
||||
rv = nghttp2_submit_headers(session_, NGHTTP2_FLAG_NONE,
|
||||
downstream->get_stream_id(), nullptr,
|
||||
nva.data(), nva.size(), nullptr);
|
||||
|
||||
downstream->clear_response_headers();
|
||||
|
||||
if(rv != 0) {
|
||||
ULOG(FATAL, this) << "nghttp2_submit_headers() failed";
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto via = downstream->get_norm_response_header("via");
|
||||
if(get_config()->no_via) {
|
||||
if(via != end_headers) {
|
||||
|
@ -1192,17 +1203,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream)
|
|||
}
|
||||
|
||||
if(LOG_ENABLED(INFO)) {
|
||||
std::stringstream ss;
|
||||
for(auto& nv : nva) {
|
||||
ss << TTY_HTTP_HD;
|
||||
ss.write(reinterpret_cast<const char*>(nv.name), nv.namelen);
|
||||
ss << TTY_RST << ": ";
|
||||
ss.write(reinterpret_cast<const char*>(nv.value), nv.valuelen);
|
||||
ss << "\n";
|
||||
}
|
||||
ULOG(INFO, this) << "HTTP response headers. stream_id="
|
||||
<< downstream->get_stream_id() << "\n"
|
||||
<< ss.str();
|
||||
log_response_headers(downstream, nva);
|
||||
}
|
||||
|
||||
if(get_config()->http2_upstream_dump_response_header) {
|
||||
|
@ -1214,9 +1215,16 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream)
|
|||
data_prd.source.ptr = downstream;
|
||||
data_prd.read_callback = downstream_data_read_callback;
|
||||
|
||||
int rv;
|
||||
nghttp2_data_provider *data_prdptr;
|
||||
|
||||
if(downstream->expect_response_body()) {
|
||||
data_prdptr = &data_prd;
|
||||
} else {
|
||||
data_prdptr = nullptr;
|
||||
}
|
||||
|
||||
rv = nghttp2_submit_response(session_, downstream->get_stream_id(),
|
||||
nva.data(), nva.size(), &data_prd);
|
||||
nva.data(), nva.size(),data_prdptr);
|
||||
if(rv != 0) {
|
||||
ULOG(FATAL, this) << "nghttp2_submit_response() failed";
|
||||
return -1;
|
||||
|
@ -1282,18 +1290,14 @@ void Http2Upstream::pause_read(IOCtrlReason reason)
|
|||
int Http2Upstream::resume_read(IOCtrlReason reason, Downstream *downstream)
|
||||
{
|
||||
if(get_flow_control()) {
|
||||
int32_t window_size_increment;
|
||||
window_size_increment = http2::determine_window_update_transmission
|
||||
(session_, 0);
|
||||
if(window_size_increment != -1) {
|
||||
window_update(nullptr, window_size_increment);
|
||||
}
|
||||
window_size_increment = http2::determine_window_update_transmission
|
||||
(session_, downstream->get_stream_id());
|
||||
if(window_size_increment != -1) {
|
||||
window_update(downstream, window_size_increment);
|
||||
if(consume(downstream->get_stream_id(),
|
||||
downstream->get_request_datalen()) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
downstream->reset_request_datalen();
|
||||
}
|
||||
|
||||
return send();
|
||||
}
|
||||
|
||||
|
@ -1311,19 +1315,35 @@ int Http2Upstream::on_downstream_abort_request(Downstream *downstream,
|
|||
return send();
|
||||
}
|
||||
|
||||
int Http2Upstream::handle_ign_data_chunk(size_t len)
|
||||
int Http2Upstream::consume(int32_t stream_id, size_t len)
|
||||
{
|
||||
int32_t window_size;
|
||||
int rv;
|
||||
|
||||
recv_ign_window_size_ += len;
|
||||
rv = nghttp2_session_consume(session_, stream_id, len);
|
||||
|
||||
window_size = nghttp2_session_get_effective_local_window_size(session_);
|
||||
|
||||
if(recv_ign_window_size_ >= window_size / 2) {
|
||||
window_update(nullptr, recv_ign_window_size_);
|
||||
if(rv != 0) {
|
||||
ULOG(WARNING, this) << "nghttp2_session_consume() returned error: "
|
||||
<< nghttp2_strerror(rv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Http2Upstream::log_response_headers
|
||||
(Downstream *downstream, const std::vector<nghttp2_nv>& nva) const
|
||||
{
|
||||
std::stringstream ss;
|
||||
for(auto& nv : nva) {
|
||||
ss << TTY_HTTP_HD;
|
||||
ss.write(reinterpret_cast<const char*>(nv.name), nv.namelen);
|
||||
ss << TTY_RST << ": ";
|
||||
ss.write(reinterpret_cast<const char*>(nv.value), nv.valuelen);
|
||||
ss << "\n";
|
||||
}
|
||||
ULOG(INFO, this) << "HTTP response headers. stream_id="
|
||||
<< downstream->get_stream_id() << "\n"
|
||||
<< ss.str();
|
||||
}
|
||||
|
||||
} // namespace shrpx
|
||||
|
|
|
@ -60,9 +60,6 @@ public:
|
|||
nghttp2_session* get_http2_session();
|
||||
|
||||
int rst_stream(Downstream *downstream, nghttp2_error_code error_code);
|
||||
// To send WINDOW_UPDATE for a connection, specify nullptr to
|
||||
// |downstream|.
|
||||
int window_update(Downstream *downstream, int32_t window_size_increment);
|
||||
int terminate_session(nghttp2_error_code error_code);
|
||||
int error_reply(Downstream *downstream, unsigned int status_code);
|
||||
|
||||
|
@ -81,16 +78,15 @@ public:
|
|||
int upgrade_upstream(HttpsUpstream *upstream);
|
||||
int start_settings_timer();
|
||||
void stop_settings_timer();
|
||||
int handle_ign_data_chunk(size_t len);
|
||||
int consume(int32_t stream_id, size_t len);
|
||||
void log_response_headers(Downstream *downstream,
|
||||
const std::vector<nghttp2_nv>& nva) const;
|
||||
private:
|
||||
DownstreamQueue downstream_queue_;
|
||||
std::unique_ptr<HttpsUpstream> pre_upstream_;
|
||||
ClientHandler *handler_;
|
||||
nghttp2_session *session_;
|
||||
event *settings_timerev_;
|
||||
// Received DATA frame size while it is not sent to backend before
|
||||
// any connection-level WINDOW_UPDATE
|
||||
int32_t recv_ign_window_size_;
|
||||
bool flow_control_;
|
||||
};
|
||||
|
||||
|
|
|
@ -137,13 +137,23 @@ int HttpDownstreamConnection::push_request_headers()
|
|||
} else if(get_config()->http2_proxy &&
|
||||
!downstream_->get_request_http2_scheme().empty() &&
|
||||
!downstream_->get_request_http2_authority().empty() &&
|
||||
downstream_->get_request_path().c_str()[0] == '/') {
|
||||
(downstream_->get_request_path().c_str()[0] == '/' ||
|
||||
downstream_->get_request_path() == "*")) {
|
||||
// Construct absolute-form request target because we are going to
|
||||
// send a request to a HTTP/1 proxy.
|
||||
hdrs += downstream_->get_request_http2_scheme();
|
||||
hdrs += "://";
|
||||
hdrs += downstream_->get_request_http2_authority();
|
||||
|
||||
// Server-wide OPTIONS takes following form in proxy request:
|
||||
//
|
||||
// OPTIONS http://example.org HTTP/1.1
|
||||
//
|
||||
// Notice that no slash after authority. See
|
||||
// http://tools.ietf.org/html/rfc7230#section-5.3.4
|
||||
if(downstream_->get_request_path() != "*") {
|
||||
hdrs += downstream_->get_request_path();
|
||||
}
|
||||
} else {
|
||||
// No proxy case. get_request_path() may be absolute-form but we
|
||||
// don't care.
|
||||
|
@ -408,9 +418,26 @@ namespace {
|
|||
int htp_hdrs_completecb(http_parser *htp)
|
||||
{
|
||||
auto downstream = static_cast<Downstream*>(htp->data);
|
||||
auto upstream = downstream->get_upstream();
|
||||
int rv;
|
||||
|
||||
downstream->set_response_http_status(htp->status_code);
|
||||
downstream->set_response_major(htp->http_major);
|
||||
downstream->set_response_minor(htp->http_minor);
|
||||
|
||||
if(downstream->get_non_final_response()) {
|
||||
// For non-final response code, we just call
|
||||
// on_downstream_header_complete() without changing response
|
||||
// state.
|
||||
rv = upstream->on_downstream_header_complete(downstream);
|
||||
|
||||
if(rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
downstream->set_response_connection_close(!http_should_keep_alive(htp));
|
||||
downstream->set_response_state(Downstream::HEADER_COMPLETE);
|
||||
downstream->inspect_http1_response();
|
||||
|
@ -418,15 +445,13 @@ int htp_hdrs_completecb(http_parser *htp)
|
|||
if(downstream->get_upgraded()) {
|
||||
downstream->set_response_connection_close(true);
|
||||
}
|
||||
if(downstream->get_upstream()->on_downstream_header_complete(downstream)
|
||||
!= 0) {
|
||||
if(upstream->on_downstream_header_complete(downstream) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(downstream->get_upgraded()) {
|
||||
// Upgrade complete, read until EOF in both ends
|
||||
if(downstream->get_upstream()->resume_read(SHRPX_MSG_BLOCK,
|
||||
downstream) != 0) {
|
||||
if(upstream->resume_read(SHRPX_MSG_BLOCK, downstream) != 0) {
|
||||
return -1;
|
||||
}
|
||||
downstream->set_request_state(Downstream::HEADER_COMPLETE);
|
||||
|
@ -443,6 +468,9 @@ int htp_hdrs_completecb(http_parser *htp)
|
|||
// 304 status code with nonzero Content-Length, but without response
|
||||
// body. See
|
||||
// http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-20#section-3.3
|
||||
|
||||
// TODO It seems that the cases other than HEAD are handled by
|
||||
// http-parser. Need test.
|
||||
return downstream->get_request_method() == "HEAD" ||
|
||||
(100 <= status && status <= 199) || status == 204 ||
|
||||
status == 304 ? 1 : 0;
|
||||
|
@ -505,6 +533,13 @@ namespace {
|
|||
int htp_msg_completecb(http_parser *htp)
|
||||
{
|
||||
auto downstream = static_cast<Downstream*>(htp->data);
|
||||
|
||||
if(downstream->get_non_final_response()) {
|
||||
downstream->reset_response();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
||||
// Block reading another response message from (broken?)
|
||||
// server. This callback is not called if the connection is
|
||||
|
|
|
@ -190,16 +190,6 @@ int htp_hdrs_completecb(http_parser *htp)
|
|||
|
||||
auto dconn = upstream->get_client_handler()->get_downstream_connection();
|
||||
|
||||
if(downstream->get_expect_100_continue()) {
|
||||
static const char reply_100[] = "HTTP/1.1 100 Continue\r\n\r\n";
|
||||
if(bufferevent_write(upstream->get_client_handler()->get_bev(),
|
||||
reply_100, sizeof(reply_100)-1) != 0) {
|
||||
ULOG(FATAL, upstream) << "bufferevent_write() faild";
|
||||
delete dconn;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
rv = dconn->attach_downstream(downstream);
|
||||
|
||||
if(rv != 0) {
|
||||
|
@ -416,6 +406,23 @@ int HttpsUpstream::on_write()
|
|||
int rv = 0;
|
||||
auto downstream = get_downstream();
|
||||
if(downstream) {
|
||||
// We need to postpone detachment until all data are sent so that
|
||||
// we can notify nghttp2 library all data consumed.
|
||||
if(downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
||||
auto dconn = downstream->get_downstream_connection();
|
||||
|
||||
if(downstream->get_response_connection_close()) {
|
||||
|
||||
// Connection close
|
||||
downstream->set_downstream_connection(nullptr);
|
||||
|
||||
delete dconn;
|
||||
} else {
|
||||
// Keep-alive
|
||||
dconn->detach_downstream(downstream);
|
||||
}
|
||||
}
|
||||
|
||||
rv = downstream->resume_read(SHRPX_NO_BUFFER);
|
||||
}
|
||||
return rv;
|
||||
|
@ -503,7 +510,9 @@ void https_downstream_readcb(bufferevent *bev, void *ptr)
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
// If pending data exist, we defer detachment to correctly notify
|
||||
// the all consumed data to nghttp2 library.
|
||||
if(handler->get_outbuf_length() == 0) {
|
||||
if(downstream->get_response_connection_close()) {
|
||||
// Connection close
|
||||
downstream->set_downstream_connection(nullptr);
|
||||
|
@ -515,6 +524,7 @@ void https_downstream_readcb(bufferevent *bev, void *ptr)
|
|||
// Keep-alive
|
||||
dconn->detach_downstream(downstream);
|
||||
}
|
||||
}
|
||||
|
||||
if(downstream->get_request_state() == Downstream::MSG_COMPLETE) {
|
||||
if(handler->get_should_close_after_write() &&
|
||||
|
@ -740,8 +750,12 @@ Downstream* HttpsUpstream::pop_downstream()
|
|||
int HttpsUpstream::on_downstream_header_complete(Downstream *downstream)
|
||||
{
|
||||
if(LOG_ENABLED(INFO)) {
|
||||
if(downstream->get_non_final_response()) {
|
||||
DLOG(INFO, downstream) << "HTTP non-final response header";
|
||||
} else {
|
||||
DLOG(INFO, downstream) << "HTTP response header completed";
|
||||
}
|
||||
}
|
||||
|
||||
std::string hdrs = "HTTP/";
|
||||
hdrs += util::utos(downstream->get_request_major());
|
||||
|
@ -759,6 +773,24 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream)
|
|||
http2::build_http1_headers_from_norm_headers
|
||||
(hdrs, downstream->get_response_headers());
|
||||
|
||||
if(downstream->get_non_final_response()) {
|
||||
hdrs += "\r\n";
|
||||
|
||||
if(LOG_ENABLED(INFO)) {
|
||||
log_response_headers(hdrs);
|
||||
}
|
||||
|
||||
auto output = bufferevent_get_output(handler_->get_bev());
|
||||
if(evbuffer_add(output, hdrs.c_str(), hdrs.size()) != 0) {
|
||||
ULOG(FATAL, this) << "evbuffer_add() failed";
|
||||
return -1;
|
||||
}
|
||||
|
||||
downstream->clear_response_headers();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We check downstream->get_response_connection_close() in case when
|
||||
// the Content-Length is not available.
|
||||
if(!downstream->get_request_connection_close() &&
|
||||
|
@ -818,17 +850,11 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream)
|
|||
}
|
||||
|
||||
hdrs += "\r\n";
|
||||
|
||||
if(LOG_ENABLED(INFO)) {
|
||||
const char *hdrp;
|
||||
std::string nhdrs;
|
||||
if(worker_config.errorlog_tty) {
|
||||
nhdrs = http::colorizeHeaders(hdrs.c_str());
|
||||
hdrp = nhdrs.c_str();
|
||||
} else {
|
||||
hdrp = hdrs.c_str();
|
||||
}
|
||||
ULOG(INFO, this) << "HTTP response headers\n" << hdrp;
|
||||
log_response_headers(hdrs);
|
||||
}
|
||||
|
||||
auto output = bufferevent_get_output(handler_->get_bev());
|
||||
if(evbuffer_add(output, hdrs.c_str(), hdrs.size()) != 0) {
|
||||
ULOG(FATAL, this) << "evbuffer_add() failed";
|
||||
|
@ -911,4 +937,17 @@ int HttpsUpstream::on_downstream_abort_request(Downstream *downstream,
|
|||
return error_reply(status_code);
|
||||
}
|
||||
|
||||
void HttpsUpstream::log_response_headers(const std::string& hdrs) const
|
||||
{
|
||||
const char *hdrp;
|
||||
std::string nhdrs;
|
||||
if(worker_config.errorlog_tty) {
|
||||
nhdrs = http::colorizeHeaders(hdrs.c_str());
|
||||
hdrp = nhdrs.c_str();
|
||||
} else {
|
||||
hdrp = hdrs.c_str();
|
||||
}
|
||||
ULOG(INFO, this) << "HTTP response headers\n" << hdrp;
|
||||
}
|
||||
|
||||
} // namespace shrpx
|
||||
|
|
|
@ -65,6 +65,7 @@ public:
|
|||
virtual int on_downstream_body_complete(Downstream *downstream);
|
||||
|
||||
void reset_current_header_length();
|
||||
void log_response_headers(const std::string& hdrs) const;
|
||||
private:
|
||||
ClientHandler *handler_;
|
||||
http_parser htp_;
|
||||
|
|
|
@ -886,6 +886,15 @@ spdylay_session* SpdyUpstream::get_http2_session()
|
|||
// spdylay_session_recv. These calls may delete downstream.
|
||||
int SpdyUpstream::on_downstream_header_complete(Downstream *downstream)
|
||||
{
|
||||
if(downstream->get_non_final_response()) {
|
||||
// SPDY does not support non-final response. We could send it
|
||||
// with HEADERS and final response in SYN_REPLY, but it is not
|
||||
// official way.
|
||||
downstream->clear_response_headers();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(LOG_ENABLED(INFO)) {
|
||||
DLOG(INFO, downstream) << "HTTP response header completed";
|
||||
}
|
||||
|
|
|
@ -95,6 +95,8 @@ int main(int argc, char* argv[])
|
|||
test_nghttp2_session_recv_unexpected_continuation) ||
|
||||
!CU_add_test(pSuite, "session_recv_settings_header_table_size",
|
||||
test_nghttp2_session_recv_settings_header_table_size) ||
|
||||
!CU_add_test(pSuite, "session_recv_too_large_frame_length",
|
||||
test_nghttp2_session_recv_too_large_frame_length) ||
|
||||
!CU_add_test(pSuite, "session_continue", test_nghttp2_session_continue) ||
|
||||
!CU_add_test(pSuite, "session_add_frame",
|
||||
test_nghttp2_session_add_frame) ||
|
||||
|
@ -255,10 +257,6 @@ int main(int argc, char* argv[])
|
|||
!CU_add_test(pSuite, "hd_deflate", test_nghttp2_hd_deflate) ||
|
||||
!CU_add_test(pSuite, "hd_deflate_same_indexed_repr",
|
||||
test_nghttp2_hd_deflate_same_indexed_repr) ||
|
||||
!CU_add_test(pSuite, "hd_deflate_common_header_eviction",
|
||||
test_nghttp2_hd_deflate_common_header_eviction) ||
|
||||
!CU_add_test(pSuite, "hd_deflate_clear_refset",
|
||||
test_nghttp2_hd_deflate_clear_refset) ||
|
||||
!CU_add_test(pSuite, "hd_inflate_indexed",
|
||||
test_nghttp2_hd_inflate_indexed) ||
|
||||
!CU_add_test(pSuite, "hd_inflate_indname_noinc",
|
||||
|
|
|
@ -61,7 +61,7 @@ static nghttp2_nv* headers(void)
|
|||
return nva;
|
||||
}
|
||||
|
||||
static void check_frame_header(uint16_t length, uint8_t type, uint8_t flags,
|
||||
static void check_frame_header(size_t length, uint8_t type, uint8_t flags,
|
||||
int32_t stream_id, nghttp2_frame_hd *hd)
|
||||
{
|
||||
CU_ASSERT(length == hd->length);
|
||||
|
@ -223,7 +223,7 @@ void test_nghttp2_frame_pack_priority(void)
|
|||
rv = nghttp2_frame_pack_priority(&bufs, &frame);
|
||||
|
||||
CU_ASSERT(0 == rv);
|
||||
CU_ASSERT(13 == nghttp2_bufs_len(&bufs));
|
||||
CU_ASSERT(NGHTTP2_FRAME_HDLEN + 5 == nghttp2_bufs_len(&bufs));
|
||||
CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &bufs));
|
||||
check_frame_header(5, NGHTTP2_PRIORITY, NGHTTP2_FLAG_NONE,
|
||||
1000000007, &oframe.hd);
|
||||
|
@ -251,7 +251,7 @@ void test_nghttp2_frame_pack_rst_stream(void)
|
|||
rv = nghttp2_frame_pack_rst_stream(&bufs, &frame);
|
||||
|
||||
CU_ASSERT(0 == rv);
|
||||
CU_ASSERT(12 == nghttp2_bufs_len(&bufs));
|
||||
CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4 == nghttp2_bufs_len(&bufs));
|
||||
CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &bufs));
|
||||
check_frame_header(4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, 1000000007,
|
||||
&oframe.hd);
|
||||
|
@ -384,7 +384,7 @@ void test_nghttp2_frame_pack_ping(void)
|
|||
rv = nghttp2_frame_pack_ping(&bufs, &frame);
|
||||
|
||||
CU_ASSERT(0 == rv);
|
||||
CU_ASSERT(16 == nghttp2_bufs_len(&bufs));
|
||||
CU_ASSERT(NGHTTP2_FRAME_HDLEN + 8 == nghttp2_bufs_len(&bufs));
|
||||
CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &bufs));
|
||||
check_frame_header(8, NGHTTP2_PING, NGHTTP2_FLAG_ACK, 0, &oframe.hd);
|
||||
CU_ASSERT(memcmp(opaque_data, oframe.opaque_data, sizeof(opaque_data) - 1)
|
||||
|
@ -411,7 +411,8 @@ void test_nghttp2_frame_pack_goaway()
|
|||
rv = nghttp2_frame_pack_goaway(&bufs, &frame);
|
||||
|
||||
CU_ASSERT(0 == rv);
|
||||
CU_ASSERT((ssize_t)(16 + opaque_data_len) == nghttp2_bufs_len(&bufs));
|
||||
CU_ASSERT((ssize_t)(NGHTTP2_FRAME_HDLEN + 8 + opaque_data_len) ==
|
||||
nghttp2_bufs_len(&bufs));
|
||||
CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &bufs));
|
||||
check_frame_header(24, NGHTTP2_GOAWAY, NGHTTP2_FLAG_NONE, 0, &oframe.hd);
|
||||
CU_ASSERT(1000000007 == oframe.last_stream_id);
|
||||
|
@ -453,7 +454,7 @@ void test_nghttp2_frame_pack_window_update(void)
|
|||
rv = nghttp2_frame_pack_window_update(&bufs, &frame);
|
||||
|
||||
CU_ASSERT(0 == rv);
|
||||
CU_ASSERT(12 == nghttp2_bufs_len(&bufs));
|
||||
CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4 == nghttp2_bufs_len(&bufs));
|
||||
CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &bufs));
|
||||
check_frame_header(4, NGHTTP2_WINDOW_UPDATE, NGHTTP2_FLAG_NONE,
|
||||
1000000007, &oframe.hd);
|
||||
|
@ -541,7 +542,7 @@ void test_nghttp2_frame_pack_altsvc(void)
|
|||
/* Check no origin case */
|
||||
|
||||
payloadlen = NGHTTP2_ALTSVC_MINLEN + protocol_id_len + host_len;
|
||||
nghttp2_put_uint16be(buf->pos, payloadlen);
|
||||
nghttp2_put_uint32be(buf->pos, (uint32_t)((payloadlen << 8) + buf->pos[3]));
|
||||
|
||||
oframe.payload = &oaltsvc;
|
||||
|
||||
|
@ -562,7 +563,7 @@ void test_nghttp2_frame_pack_altsvc(void)
|
|||
|
||||
/* Check insufficient payload length for host */
|
||||
payloadlen = NGHTTP2_ALTSVC_MINLEN + protocol_id_len + host_len - 1;
|
||||
nghttp2_put_uint16be(buf->pos, payloadlen);
|
||||
nghttp2_put_uint32be(buf->pos, (uint32_t)((payloadlen << 8) + buf->pos[3]));
|
||||
|
||||
oframe.payload = &oaltsvc;
|
||||
|
||||
|
@ -579,7 +580,7 @@ void test_nghttp2_frame_pack_altsvc(void)
|
|||
|
||||
/* Check no host case */
|
||||
payloadlen = NGHTTP2_ALTSVC_MINLEN + protocol_id_len;
|
||||
nghttp2_put_uint16be(buf->pos, payloadlen);
|
||||
nghttp2_put_uint32be(buf->pos, (uint32_t)((payloadlen << 8) + buf->pos[3]));
|
||||
buf->pos[NGHTTP2_FRAME_HDLEN + NGHTTP2_ALTSVC_FIXED_PARTLEN
|
||||
+ protocol_id_len] = 0;
|
||||
|
||||
|
@ -602,7 +603,7 @@ void test_nghttp2_frame_pack_altsvc(void)
|
|||
|
||||
/* Check missing Host-Len */
|
||||
payloadlen = NGHTTP2_ALTSVC_FIXED_PARTLEN + protocol_id_len;
|
||||
nghttp2_put_uint16be(buf->pos, payloadlen);
|
||||
nghttp2_put_uint32be(buf->pos, (uint32_t)((payloadlen << 8) + buf->pos[3]));
|
||||
|
||||
oframe.payload = &oaltsvc;
|
||||
|
||||
|
@ -699,13 +700,30 @@ void test_nghttp2_iv_check(void)
|
|||
iv[1].value = 3;
|
||||
CU_ASSERT(!nghttp2_iv_check(iv, 2));
|
||||
|
||||
/* Undefined SETTINGS ID */
|
||||
/* Undefined SETTINGS ID is allowed */
|
||||
iv[1].settings_id = 1000000009;
|
||||
iv[1].value = 0;
|
||||
CU_ASSERT(!nghttp2_iv_check(iv, 2));
|
||||
CU_ASSERT(nghttp2_iv_check(iv, 2));
|
||||
|
||||
/* Too large SETTINGS_HEADER_TABLE_SIZE */
|
||||
iv[1].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
|
||||
iv[1].value = UINT32_MAX;
|
||||
CU_ASSERT(!nghttp2_iv_check(iv, 2));
|
||||
|
||||
/* Too small SETTINGS_MAX_FRAME_SIZE */
|
||||
iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE;
|
||||
iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MIN - 1;
|
||||
CU_ASSERT(!nghttp2_iv_check(iv, 1));
|
||||
|
||||
/* Too large SETTINGS_MAX_FRAME_SIZE */
|
||||
iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE;
|
||||
iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MAX + 1;
|
||||
CU_ASSERT(!nghttp2_iv_check(iv, 1));
|
||||
|
||||
/* Max and min SETTINGS_MAX_FRAME_SIZE */
|
||||
iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE;
|
||||
iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MIN;
|
||||
iv[1].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE;
|
||||
iv[1].value = NGHTTP2_MAX_FRAME_SIZE_MAX;
|
||||
CU_ASSERT(nghttp2_iv_check(iv, 2));
|
||||
}
|
||||
|
|
|
@ -159,8 +159,7 @@ void test_nghttp2_hd_deflate_same_indexed_repr(void)
|
|||
CU_ASSERT(0 == nghttp2_hd_deflate_init(&deflater));
|
||||
CU_ASSERT(0 == nghttp2_hd_inflate_init(&inflater));
|
||||
|
||||
/* Encode 2 same headers. cookie:alpha is not in the reference set,
|
||||
so first emit literal repr and then 2 emits of indexed repr. */
|
||||
/* Encode 2 same headers. Emit 1 literal reprs and 1 index repr. */
|
||||
rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva1, ARRLEN(nva1));
|
||||
blocklen = nghttp2_bufs_len(&bufs);
|
||||
|
||||
|
@ -174,13 +173,12 @@ void test_nghttp2_hd_deflate_same_indexed_repr(void)
|
|||
nva_out_reset(&out);
|
||||
nghttp2_bufs_reset(&bufs);
|
||||
|
||||
/* Encode 3 same headers. This time, cookie:alpha is in the
|
||||
reference set, so the encoder emits indexed repr 6 times */
|
||||
/* Encode 3 same headers. This time, emits 3 index reprs. */
|
||||
rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva2, ARRLEN(nva2));
|
||||
blocklen = nghttp2_bufs_len(&bufs);
|
||||
|
||||
CU_ASSERT(0 == rv);
|
||||
CU_ASSERT(blocklen == 6);
|
||||
CU_ASSERT(blocklen == 3);
|
||||
CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0));
|
||||
|
||||
CU_ASSERT(3 == out.nvlen);
|
||||
|
@ -195,120 +193,6 @@ void test_nghttp2_hd_deflate_same_indexed_repr(void)
|
|||
nghttp2_hd_deflate_free(&deflater);
|
||||
}
|
||||
|
||||
void test_nghttp2_hd_deflate_common_header_eviction(void)
|
||||
{
|
||||
nghttp2_hd_deflater deflater;
|
||||
nghttp2_hd_inflater inflater;
|
||||
nghttp2_nv nva[] = {MAKE_NV("h1", ""),
|
||||
MAKE_NV("h2", "")};
|
||||
nghttp2_bufs bufs;
|
||||
ssize_t blocklen;
|
||||
/* Default header table capacity is 4096. Adding 2 byte header name
|
||||
and 4060 byte value, which is 4094 bytes including overhead, to
|
||||
the table evicts first entry. */
|
||||
uint8_t value[3038];
|
||||
nva_out out;
|
||||
size_t i;
|
||||
int rv;
|
||||
|
||||
frame_pack_bufs_init(&bufs);
|
||||
|
||||
nva_out_init(&out);
|
||||
memset(value, '0', sizeof(value));
|
||||
for(i = 0; i < 2; ++i) {
|
||||
nva[i].value = value;
|
||||
nva[i].valuelen = sizeof(value);
|
||||
}
|
||||
|
||||
nghttp2_hd_deflate_init(&deflater);
|
||||
nghttp2_hd_inflate_init(&inflater);
|
||||
|
||||
/* First emit "h1: ..." to put it in the reference set (index
|
||||
= 0). */
|
||||
rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 1);
|
||||
blocklen = nghttp2_bufs_len(&bufs);
|
||||
|
||||
CU_ASSERT(0 == rv);
|
||||
CU_ASSERT(blocklen > 0);
|
||||
CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0));
|
||||
|
||||
CU_ASSERT(1 == out.nvlen);
|
||||
nghttp2_nv_array_sort(nva, 1);
|
||||
assert_nv_equal(nva, out.nva, 1);
|
||||
|
||||
nva_out_reset(&out);
|
||||
nghttp2_bufs_reset(&bufs);
|
||||
|
||||
/* Encode with second header */
|
||||
|
||||
rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2);
|
||||
blocklen = nghttp2_bufs_len(&bufs);
|
||||
|
||||
CU_ASSERT(0 == rv);
|
||||
CU_ASSERT(blocklen > 0);
|
||||
|
||||
/* Check common header "h1: ...:, which is removed from the
|
||||
header table because of eviction, is still emitted by the
|
||||
inflater */
|
||||
CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0));
|
||||
|
||||
CU_ASSERT(2 == out.nvlen);
|
||||
nghttp2_nv_array_sort(nva, 2);
|
||||
assert_nv_equal(nva, out.nva, 2);
|
||||
|
||||
nva_out_reset(&out);
|
||||
nghttp2_bufs_reset(&bufs);
|
||||
|
||||
CU_ASSERT(1 == deflater.ctx.hd_table.len);
|
||||
CU_ASSERT(1 == inflater.ctx.hd_table.len);
|
||||
|
||||
nghttp2_bufs_free(&bufs);
|
||||
nghttp2_hd_inflate_free(&inflater);
|
||||
nghttp2_hd_deflate_free(&deflater);
|
||||
}
|
||||
|
||||
void test_nghttp2_hd_deflate_clear_refset(void)
|
||||
{
|
||||
nghttp2_hd_deflater deflater;
|
||||
nghttp2_hd_inflater inflater;
|
||||
nghttp2_bufs bufs;
|
||||
ssize_t blocklen;
|
||||
nghttp2_nv nv[] = {
|
||||
MAKE_NV(":path", "/"),
|
||||
MAKE_NV(":scheme", "http")
|
||||
};
|
||||
size_t i;
|
||||
nva_out out;
|
||||
int rv;
|
||||
|
||||
frame_pack_bufs_init(&bufs);
|
||||
|
||||
nva_out_init(&out);
|
||||
nghttp2_hd_deflate_init2(&deflater,
|
||||
NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
|
||||
nghttp2_hd_deflate_set_no_refset(&deflater, 1);
|
||||
nghttp2_hd_inflate_init(&inflater);
|
||||
|
||||
for(i = 0; i < 2; ++i) {
|
||||
rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nv, ARRLEN(nv));
|
||||
blocklen = nghttp2_bufs_len(&bufs);
|
||||
|
||||
CU_ASSERT(0 == rv);
|
||||
CU_ASSERT(blocklen > 1);
|
||||
CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0));
|
||||
|
||||
CU_ASSERT(ARRLEN(nv) == out.nvlen);
|
||||
assert_nv_equal(nv, out.nva, ARRLEN(nv));
|
||||
|
||||
nva_out_reset(&out);
|
||||
nghttp2_bufs_reset(&bufs);
|
||||
}
|
||||
|
||||
nghttp2_bufs_free(&bufs);
|
||||
nghttp2_hd_inflate_free(&inflater);
|
||||
nghttp2_hd_deflate_free(&deflater);
|
||||
}
|
||||
|
||||
void test_nghttp2_hd_inflate_indexed(void)
|
||||
{
|
||||
nghttp2_hd_inflater inflater;
|
||||
|
@ -410,9 +294,11 @@ void test_nghttp2_hd_inflate_indname_inc(void)
|
|||
CU_ASSERT(1 == out.nvlen);
|
||||
assert_nv_equal(&nv, out.nva, 1);
|
||||
CU_ASSERT(1 == inflater.ctx.hd_table.len);
|
||||
assert_nv_equal(&nv,
|
||||
&GET_TABLE_ENT(&inflater.ctx,
|
||||
inflater.ctx.hd_table.len-1)->nv, 1);
|
||||
assert_nv_equal
|
||||
(&nv,
|
||||
&GET_TABLE_ENT
|
||||
(&inflater.ctx,
|
||||
NGHTTP2_STATIC_TABLE_LENGTH + inflater.ctx.hd_table.len-1)->nv, 1);
|
||||
|
||||
nva_out_reset(&out);
|
||||
nghttp2_bufs_free(&bufs);
|
||||
|
@ -459,7 +345,6 @@ void test_nghttp2_hd_inflate_indname_inc_eviction(void)
|
|||
nghttp2_bufs_reset(&bufs);
|
||||
|
||||
CU_ASSERT(3 == inflater.ctx.hd_table.len);
|
||||
CU_ASSERT(GET_TABLE_ENT(&inflater.ctx, 0)->flags & NGHTTP2_HD_FLAG_REFSET);
|
||||
|
||||
nghttp2_bufs_free(&bufs);
|
||||
nghttp2_hd_inflate_free(&inflater);
|
||||
|
@ -530,9 +415,11 @@ void test_nghttp2_hd_inflate_newname_inc(void)
|
|||
CU_ASSERT(1 == out.nvlen);
|
||||
assert_nv_equal(&nv, out.nva, 1);
|
||||
CU_ASSERT(1 == inflater.ctx.hd_table.len);
|
||||
assert_nv_equal(&nv,
|
||||
&GET_TABLE_ENT(&inflater.ctx,
|
||||
inflater.ctx.hd_table.len-1)->nv, 1);
|
||||
assert_nv_equal
|
||||
(&nv,
|
||||
&GET_TABLE_ENT
|
||||
(&inflater.ctx,
|
||||
NGHTTP2_STATIC_TABLE_LENGTH + inflater.ctx.hd_table.len-1)->nv, 1);
|
||||
|
||||
nva_out_reset(&out);
|
||||
nghttp2_bufs_free(&bufs);
|
||||
|
@ -696,8 +583,9 @@ void test_nghttp2_hd_change_table_size(void)
|
|||
{
|
||||
nghttp2_hd_deflater deflater;
|
||||
nghttp2_hd_inflater inflater;
|
||||
nghttp2_nv nva[] = { MAKE_NV(":method", "GET"),
|
||||
MAKE_NV(":path", "/") };
|
||||
nghttp2_nv nva[] = { MAKE_NV("alpha", "bravo"),
|
||||
MAKE_NV("charlie", "delta") };
|
||||
nghttp2_nv nva2[] = { MAKE_NV(":path", "/") };
|
||||
nghttp2_bufs bufs;
|
||||
ssize_t rv;
|
||||
nva_out out;
|
||||
|
@ -909,6 +797,36 @@ void test_nghttp2_hd_change_table_size(void)
|
|||
nghttp2_hd_inflate_free(&inflater);
|
||||
nghttp2_hd_deflate_free(&deflater);
|
||||
|
||||
/* Check that context update emitted twice */
|
||||
nghttp2_hd_deflate_init2(&deflater, 4096);
|
||||
nghttp2_hd_inflate_init(&inflater);
|
||||
|
||||
CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 0));
|
||||
CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 3000));
|
||||
CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 0));
|
||||
CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 3000));
|
||||
|
||||
CU_ASSERT(0 == deflater.min_hd_table_bufsize_max);
|
||||
CU_ASSERT(3000 == deflater.ctx.hd_table_bufsize_max);
|
||||
|
||||
rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva2, 1);
|
||||
blocklen = nghttp2_bufs_len(&bufs);
|
||||
|
||||
CU_ASSERT(0 == rv);
|
||||
CU_ASSERT(3 < blocklen);
|
||||
CU_ASSERT(3000 == deflater.ctx.hd_table_bufsize_max);
|
||||
CU_ASSERT(UINT32_MAX == deflater.min_hd_table_bufsize_max);
|
||||
|
||||
CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0));
|
||||
CU_ASSERT(3000 == inflater.ctx.hd_table_bufsize_max);
|
||||
CU_ASSERT(3000 == inflater.settings_hd_table_bufsize_max);
|
||||
|
||||
nva_out_reset(&out);
|
||||
nghttp2_bufs_reset(&bufs);
|
||||
|
||||
nghttp2_hd_inflate_free(&inflater);
|
||||
nghttp2_hd_deflate_free(&deflater);
|
||||
|
||||
nghttp2_bufs_free(&bufs);
|
||||
}
|
||||
|
||||
|
@ -1163,7 +1081,7 @@ void test_nghttp2_hd_deflate_bound(void)
|
|||
|
||||
bound = nghttp2_hd_deflate_bound(&deflater, nva, ARRLEN(nva));
|
||||
|
||||
CU_ASSERT(1 + 6 + 6 * 2 * 2 +
|
||||
CU_ASSERT(12 + 6 * 2 * 2 +
|
||||
nva[0].namelen + nva[0].valuelen +
|
||||
nva[1].namelen + nva[1].valuelen
|
||||
== bound);
|
||||
|
@ -1174,7 +1092,7 @@ void test_nghttp2_hd_deflate_bound(void)
|
|||
|
||||
bound2 = nghttp2_hd_deflate_bound(&deflater, nva, ARRLEN(nva));
|
||||
|
||||
CU_ASSERT(bound + 2 == bound2);
|
||||
CU_ASSERT(bound == bound2);
|
||||
|
||||
nghttp2_bufs_free(&bufs);
|
||||
nghttp2_hd_deflate_free(&deflater);
|
||||
|
|
|
@ -27,8 +27,6 @@
|
|||
|
||||
void test_nghttp2_hd_deflate(void);
|
||||
void test_nghttp2_hd_deflate_same_indexed_repr(void);
|
||||
void test_nghttp2_hd_deflate_common_header_eviction(void);
|
||||
void test_nghttp2_hd_deflate_clear_refset(void);
|
||||
void test_nghttp2_hd_inflate_indexed(void);
|
||||
void test_nghttp2_hd_inflate_indname_noinc(void);
|
||||
void test_nghttp2_hd_inflate_indname_inc(void);
|
||||
|
|
|
@ -33,7 +33,7 @@ static void http2(void)
|
|||
{
|
||||
const unsigned char p[] = {
|
||||
8, 'h', 't', 't', 'p', '/', '1', '.', '1',
|
||||
5, 'h', '2', '-', '1', '3',
|
||||
5, 'h', '2', '-', '1', '4',
|
||||
6, 's', 'p', 'd', 'y', '/', '3'
|
||||
};
|
||||
unsigned char outlen;
|
||||
|
|
|
@ -450,7 +450,9 @@ void test_nghttp2_session_recv(void)
|
|||
assert(nghttp2_buf_len(&bufs.cur->buf) >= 16);
|
||||
|
||||
bufs.cur->buf.last += 16;
|
||||
nghttp2_put_uint16be(bufs.cur->buf.pos, frame.hd.length + 16);
|
||||
nghttp2_put_uint32be(bufs.cur->buf.pos,
|
||||
(uint32_t)(((frame.hd.length + 16) << 8) +
|
||||
bufs.cur->buf.pos[3]));
|
||||
|
||||
nghttp2_frame_ping_free(&frame.ping);
|
||||
|
||||
|
@ -610,8 +612,8 @@ void test_nghttp2_session_recv_data(void)
|
|||
error. This is not mandated by the spec */
|
||||
ud.data_chunk_recv_cb_called = 0;
|
||||
ud.frame_recv_cb_called = 0;
|
||||
rv = nghttp2_session_mem_recv(session, data, 8+4096);
|
||||
CU_ASSERT(8+4096 == rv);
|
||||
rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096);
|
||||
CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv);
|
||||
|
||||
CU_ASSERT(0 == ud.data_chunk_recv_cb_called);
|
||||
CU_ASSERT(0 == ud.frame_recv_cb_called);
|
||||
|
@ -633,8 +635,8 @@ void test_nghttp2_session_recv_data(void)
|
|||
|
||||
ud.data_chunk_recv_cb_called = 0;
|
||||
ud.frame_recv_cb_called = 0;
|
||||
rv = nghttp2_session_mem_recv(session, data, 8+4096);
|
||||
CU_ASSERT(8+4096 == rv);
|
||||
rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096);
|
||||
CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv);
|
||||
|
||||
CU_ASSERT(0 == ud.data_chunk_recv_cb_called);
|
||||
CU_ASSERT(0 == ud.frame_recv_cb_called);
|
||||
|
@ -646,8 +648,8 @@ void test_nghttp2_session_recv_data(void)
|
|||
|
||||
ud.data_chunk_recv_cb_called = 0;
|
||||
ud.frame_recv_cb_called = 0;
|
||||
rv = nghttp2_session_mem_recv(session, data, 8+4096);
|
||||
CU_ASSERT(8+4096 == rv);
|
||||
rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096);
|
||||
CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv);
|
||||
|
||||
CU_ASSERT(1 == ud.data_chunk_recv_cb_called);
|
||||
CU_ASSERT(1 == ud.frame_recv_cb_called);
|
||||
|
@ -656,8 +658,8 @@ void test_nghttp2_session_recv_data(void)
|
|||
|
||||
ud.data_chunk_recv_cb_called = 0;
|
||||
ud.frame_recv_cb_called = 0;
|
||||
rv = nghttp2_session_mem_recv(session, data, 8+4096);
|
||||
CU_ASSERT(8+4096 == rv);
|
||||
rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096);
|
||||
CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv);
|
||||
|
||||
/* Now we got data more than initial-window-size / 2, WINDOW_UPDATE
|
||||
must be queued */
|
||||
|
@ -676,8 +678,8 @@ void test_nghttp2_session_recv_data(void)
|
|||
DATA. Additional 4 DATA frames, connection flow control will kick
|
||||
in. */
|
||||
for(i = 0; i < 5; ++i) {
|
||||
rv = nghttp2_session_mem_recv(session, data, 8+4096);
|
||||
CU_ASSERT(8+4096 == rv);
|
||||
rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096);
|
||||
CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv);
|
||||
}
|
||||
item = nghttp2_session_get_next_ob_item(session);
|
||||
CU_ASSERT(NGHTTP2_WINDOW_UPDATE == OB_CTRL_TYPE(item));
|
||||
|
@ -693,8 +695,8 @@ void test_nghttp2_session_recv_data(void)
|
|||
|
||||
ud.data_chunk_recv_cb_called = 0;
|
||||
ud.frame_recv_cb_called = 0;
|
||||
rv = nghttp2_session_mem_recv(session, data, 8+4096);
|
||||
CU_ASSERT(8+4096 == rv);
|
||||
rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096);
|
||||
CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv);
|
||||
|
||||
CU_ASSERT(0 == ud.data_chunk_recv_cb_called);
|
||||
CU_ASSERT(0 == ud.frame_recv_cb_called);
|
||||
|
@ -753,11 +755,11 @@ void test_nghttp2_session_recv_continuation(void)
|
|||
nghttp2_frame_headers_free(&frame.headers);
|
||||
|
||||
/* HEADERS's payload is 1 byte */
|
||||
memcpy(data, buf->pos, 9);
|
||||
datalen = 9;
|
||||
buf->pos += 9;
|
||||
memcpy(data, buf->pos, NGHTTP2_FRAME_HDLEN + 1);
|
||||
datalen = NGHTTP2_FRAME_HDLEN + 1;
|
||||
buf->pos += NGHTTP2_FRAME_HDLEN + 1;
|
||||
|
||||
nghttp2_put_uint16be(data, 1);
|
||||
nghttp2_put_uint32be(data, (1 << 8) + data[3]);
|
||||
|
||||
/* First CONTINUATION, 2 bytes */
|
||||
cont_hd.length = 2;
|
||||
|
@ -925,14 +927,14 @@ void test_nghttp2_session_recv_headers_with_priority(void)
|
|||
rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
|
||||
|
||||
CU_ASSERT(0 == rv);
|
||||
CU_ASSERT(13 == nghttp2_bufs_len(&bufs));
|
||||
CU_ASSERT(NGHTTP2_FRAME_HDLEN + 5 + 2 == nghttp2_bufs_len(&bufs));
|
||||
|
||||
nghttp2_frame_headers_free(&frame.headers);
|
||||
|
||||
buf = &bufs.head->buf;
|
||||
/* Make payload shorter than required length to store priroty
|
||||
group */
|
||||
nghttp2_put_uint16be(buf->pos, 4);
|
||||
nghttp2_put_uint32be(buf->pos, (4 << 8) + buf->pos[3]);
|
||||
|
||||
ud.frame_recv_cb_called = 0;
|
||||
|
||||
|
@ -1043,7 +1045,8 @@ void test_nghttp2_session_recv_premature_headers(void)
|
|||
assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf));
|
||||
|
||||
/* Intentionally feed payload cutting last 1 byte off */
|
||||
nghttp2_put_uint16be(buf->pos, frame.hd.length - 1);
|
||||
nghttp2_put_uint32be(buf->pos,
|
||||
(uint32_t)(((frame.hd.length - 1) << 8) + buf->pos[3]));
|
||||
rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf) - 1);
|
||||
|
||||
CU_ASSERT((ssize_t)(nghttp2_buf_len(buf) - 1) == rv);
|
||||
|
@ -1138,7 +1141,7 @@ void test_nghttp2_session_recv_altsvc(void)
|
|||
CU_ASSERT(NGHTTP2_EXT_ALTSVC == ud.recv_frame_type);
|
||||
|
||||
/* premature payload */
|
||||
nghttp2_put_uint16be(buf->pos, 8);
|
||||
nghttp2_put_uint32be(buf->pos, (8 << 8) + buf->pos[3]);
|
||||
|
||||
ud.frame_recv_cb_called = 0;
|
||||
|
||||
|
@ -1407,6 +1410,36 @@ void test_nghttp2_session_recv_settings_header_table_size(void)
|
|||
nghttp2_session_del(session);
|
||||
}
|
||||
|
||||
void test_nghttp2_session_recv_too_large_frame_length(void)
|
||||
{
|
||||
nghttp2_session *session;
|
||||
nghttp2_session_callbacks callbacks;
|
||||
uint8_t buf[NGHTTP2_FRAME_HDLEN];
|
||||
nghttp2_outbound_item *item;
|
||||
nghttp2_frame_hd hd = {
|
||||
/* Initial max frame size is NGHTTP2_MAX_FRAME_SIZE_MIN */
|
||||
NGHTTP2_MAX_FRAME_SIZE_MIN + 1,
|
||||
1,
|
||||
NGHTTP2_HEADERS,
|
||||
NGHTTP2_FLAG_NONE
|
||||
};
|
||||
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
|
||||
|
||||
nghttp2_session_server_new(&session, &callbacks, NULL);
|
||||
|
||||
nghttp2_frame_pack_frame_hd(buf, &hd);
|
||||
|
||||
CU_ASSERT(sizeof(buf) ==
|
||||
nghttp2_session_mem_recv(session, buf, sizeof(buf)));
|
||||
|
||||
item = nghttp2_session_get_next_ob_item(session);
|
||||
|
||||
CU_ASSERT(item != NULL);
|
||||
CU_ASSERT(NGHTTP2_GOAWAY == OB_CTRL_TYPE(item));
|
||||
|
||||
nghttp2_session_del(session);
|
||||
}
|
||||
|
||||
void test_nghttp2_session_continue(void)
|
||||
{
|
||||
nghttp2_session *session;
|
||||
|
@ -1642,11 +1675,11 @@ void test_nghttp2_session_add_frame(void)
|
|||
aux_data));
|
||||
CU_ASSERT(0 == nghttp2_pq_empty(&session->ob_ss_pq));
|
||||
CU_ASSERT(0 == nghttp2_session_send(session));
|
||||
CU_ASSERT(NGHTTP2_HEADERS == acc.buf[2]);
|
||||
CU_ASSERT(NGHTTP2_HEADERS == acc.buf[3]);
|
||||
CU_ASSERT((NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY) ==
|
||||
acc.buf[3]);
|
||||
acc.buf[4]);
|
||||
/* check stream id */
|
||||
CU_ASSERT(1 == nghttp2_get_uint32(&acc.buf[4]));
|
||||
CU_ASSERT(1 == nghttp2_get_uint32(&acc.buf[5]));
|
||||
|
||||
nghttp2_session_del(session);
|
||||
}
|
||||
|
@ -2159,6 +2192,25 @@ void test_nghttp2_session_on_settings_received(void)
|
|||
|
||||
nghttp2_frame_settings_free(&frame.settings);
|
||||
nghttp2_session_del(session);
|
||||
|
||||
/* Check too large SETTINGS_MAX_FRAME_SIZE */
|
||||
nghttp2_session_server_new(&session, &callbacks, NULL);
|
||||
|
||||
iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE;
|
||||
iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MAX + 1;
|
||||
|
||||
nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE,
|
||||
dup_iv(iv, 1), 1);
|
||||
|
||||
CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0));
|
||||
|
||||
item = nghttp2_session_get_next_ob_item(session);
|
||||
|
||||
CU_ASSERT(item != NULL);
|
||||
CU_ASSERT(NGHTTP2_GOAWAY == OB_CTRL_TYPE(item));
|
||||
|
||||
nghttp2_frame_settings_free(&frame.settings);
|
||||
nghttp2_session_del(session);
|
||||
}
|
||||
|
||||
void test_nghttp2_session_on_push_promise_received(void)
|
||||
|
@ -2937,8 +2989,7 @@ void test_nghttp2_submit_data(void)
|
|||
&pri_spec_default, NGHTTP2_STREAM_OPENING,
|
||||
NULL);
|
||||
CU_ASSERT(0 == nghttp2_submit_data(session,
|
||||
NGHTTP2_FLAG_END_STREAM |
|
||||
NGHTTP2_FLAG_END_SEGMENT, 1, &data_prd));
|
||||
NGHTTP2_FLAG_END_STREAM, 1, &data_prd));
|
||||
|
||||
ud.block_count = 0;
|
||||
CU_ASSERT(0 == nghttp2_session_send(session));
|
||||
|
@ -2949,8 +3000,7 @@ void test_nghttp2_submit_data(void)
|
|||
|
||||
CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags);
|
||||
/* frame->hd.flags has these flags */
|
||||
CU_ASSERT((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_SEGMENT) ==
|
||||
data_frame->hd.flags);
|
||||
CU_ASSERT(NGHTTP2_FLAG_END_STREAM == data_frame->hd.flags);
|
||||
|
||||
nghttp2_session_del(session);
|
||||
}
|
||||
|
@ -3433,8 +3483,9 @@ void test_nghttp2_submit_settings(void)
|
|||
my_user_data ud;
|
||||
nghttp2_outbound_item *item;
|
||||
nghttp2_frame *frame;
|
||||
nghttp2_settings_entry iv[6];
|
||||
nghttp2_settings_entry iv[7];
|
||||
nghttp2_frame ack_frame;
|
||||
const int32_t UNKNOWN_ID = 1000000007;
|
||||
|
||||
iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
|
||||
iv[0].value = 5;
|
||||
|
@ -3448,8 +3499,11 @@ void test_nghttp2_submit_settings(void)
|
|||
iv[3].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
|
||||
iv[3].value = 0;
|
||||
|
||||
iv[4].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
|
||||
iv[4].value = (uint32_t)NGHTTP2_MAX_WINDOW_SIZE + 1;
|
||||
iv[4].settings_id = UNKNOWN_ID;
|
||||
iv[4].value = 999;
|
||||
|
||||
iv[5].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
|
||||
iv[5].value = (uint32_t)NGHTTP2_MAX_WINDOW_SIZE + 1;
|
||||
|
||||
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
|
||||
callbacks.send_callback = null_send_callback;
|
||||
|
@ -3457,7 +3511,7 @@ void test_nghttp2_submit_settings(void)
|
|||
nghttp2_session_server_new(&session, &callbacks, &ud);
|
||||
|
||||
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
|
||||
nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 5));
|
||||
nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 6));
|
||||
|
||||
/* Make sure that local settings are not changed */
|
||||
CU_ASSERT(NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS ==
|
||||
|
@ -3465,15 +3519,15 @@ void test_nghttp2_submit_settings(void)
|
|||
CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE ==
|
||||
session->local_settings.initial_window_size);
|
||||
|
||||
/* Now sends without 5th one */
|
||||
CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 4));
|
||||
/* Now sends without 6th one */
|
||||
CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 5));
|
||||
|
||||
item = nghttp2_session_get_next_ob_item(session);
|
||||
|
||||
CU_ASSERT(NGHTTP2_SETTINGS == OB_CTRL_TYPE(item));
|
||||
|
||||
frame = item->frame;
|
||||
CU_ASSERT(4 == frame->settings.niv);
|
||||
CU_ASSERT(5 == frame->settings.niv);
|
||||
CU_ASSERT(5 == frame->settings.iv[0].value);
|
||||
CU_ASSERT(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS ==
|
||||
frame->settings.iv[0].settings_id);
|
||||
|
@ -3482,6 +3536,9 @@ void test_nghttp2_submit_settings(void)
|
|||
CU_ASSERT(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE ==
|
||||
frame->settings.iv[1].settings_id);
|
||||
|
||||
CU_ASSERT(UNKNOWN_ID == frame->settings.iv[4].settings_id);
|
||||
CU_ASSERT(999 == frame->settings.iv[4].value);
|
||||
|
||||
ud.frame_send_cb_called = 0;
|
||||
CU_ASSERT(0 == nghttp2_session_send(session));
|
||||
CU_ASSERT(1 == ud.frame_send_cb_called);
|
||||
|
@ -4759,25 +4816,13 @@ void test_nghttp2_session_set_option(void)
|
|||
|
||||
nghttp2_option_new(&option);
|
||||
|
||||
nghttp2_option_set_no_auto_stream_window_update(option, 1);
|
||||
nghttp2_option_set_no_auto_window_update(option, 1);
|
||||
|
||||
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
|
||||
nghttp2_session_client_new2(&session, &callbacks, NULL, option);
|
||||
|
||||
CU_ASSERT(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_STREAM_WINDOW_UPDATE);
|
||||
CU_ASSERT(!(session->opt_flags &
|
||||
NGHTTP2_OPTMASK_NO_AUTO_CONNECTION_WINDOW_UPDATE));
|
||||
nghttp2_session_del(session);
|
||||
CU_ASSERT(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE);
|
||||
|
||||
nghttp2_option_set_no_auto_stream_window_update(option, 0);
|
||||
nghttp2_option_set_no_auto_connection_window_update(option, 1);
|
||||
|
||||
nghttp2_session_server_new2(&session, &callbacks, NULL, option);
|
||||
|
||||
CU_ASSERT(!(session->opt_flags &
|
||||
NGHTTP2_OPTMASK_NO_AUTO_STREAM_WINDOW_UPDATE));
|
||||
CU_ASSERT(session->opt_flags &
|
||||
NGHTTP2_OPTMASK_NO_AUTO_CONNECTION_WINDOW_UPDATE);
|
||||
nghttp2_session_del(session);
|
||||
|
||||
nghttp2_option_set_peer_max_concurrent_streams(option, 100);
|
||||
|
|
|
@ -37,6 +37,7 @@ void test_nghttp2_session_recv_altsvc(void);
|
|||
void test_nghttp2_session_recv_unknown_frame(void);
|
||||
void test_nghttp2_session_recv_unexpected_continuation(void);
|
||||
void test_nghttp2_session_recv_settings_header_table_size(void);
|
||||
void test_nghttp2_session_recv_too_large_frame_length(void);
|
||||
void test_nghttp2_session_continue(void);
|
||||
void test_nghttp2_session_add_frame(void);
|
||||
void test_nghttp2_session_on_request_headers_received(void);
|
||||
|
|
Loading…
Reference in New Issue