Merge branch 'draft-10'

This commit is contained in:
Tatsuhiro Tsujikawa 2014-02-16 15:29:21 +09:00
commit 5cc24cb7c2
45 changed files with 2263 additions and 6654 deletions

View File

@ -7,25 +7,22 @@ version 2.0.
Development Status
------------------
We started to implement HTTP-draft-09/2.0
(http://tools.ietf.org/html/draft-ietf-httpbis-http2-09) and the
We started to implement h2-10
(http://tools.ietf.org/html/draft-ietf-httpbis-http2-10) and the
header compression
(http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05).
(http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-06).
The nghttp2 code base was forked from spdylay project.
========================== =================
Features HTTP-draft-09/2.0
========================== =================
:authority Done
HPACK-draft-05 Done
SETTINGS_HEADER_TABLE_SIZE Done
SETTINGS_ENABLE_PUSH Done
FRAME_SIZE_ERROR Done
SETTINGS with ACK Done
Header Continuation Done
ALPN Done
========================== =================
========================== =====
Features h2-10
========================== =====
HPACK-draft-06 Done
Strict SETTINGS validation Done
Disallow client to push Done
Padding Done
END_SEGMENT
========================== =====
Public Test Server
------------------
@ -150,19 +147,21 @@ with prior knowledge, HTTP Upgrade and NPN/ALPN TLS extension.
It has verbose output mode for framing information. Here is sample
output from ``nghttp`` client::
$ src/nghttp -vn https://localhost:8443
[ 0.003] NPN select next protocol: the remote server offers:
* HTTP-draft-09/2.0
$ src/nghttp -nv https://localhost:8443
[ 0.004][NPN] server offers:
* h2-10
* spdy/3.1
* spdy/3
* spdy/2
* http/1.1
NPN selected the protocol: HTTP-draft-09/2.0
[ 0.005] send SETTINGS frame <length=16, flags=0x00, stream_id=0>
The negotiated protocol: h2-10
[ 0.006] send SETTINGS frame <length=10, flags=0x00, stream_id=0>
(niv=2)
[SETTINGS_MAX_CONCURRENT_STREAMS(4):100]
[SETTINGS_INITIAL_WINDOW_SIZE(7):65535]
[ 0.006] send HEADERS frame <length=47, flags=0x05, stream_id=1>
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
[SETTINGS_INITIAL_WINDOW_SIZE(4):65535]
[ 0.007] send HEADERS frame <length=48, flags=0x05, stream_id=1>
; END_STREAM | END_HEADERS
(padlen=0)
; Open new stream
:authority: localhost:8443
:method: GET
@ -170,91 +169,91 @@ output from ``nghttp`` client::
:scheme: https
accept: */*
accept-encoding: gzip, deflate
user-agent: nghttp2/0.1.0-DEV
[ 0.006] recv SETTINGS frame <length=16, flags=0x00, stream_id=0>
(niv=2)
[SETTINGS_MAX_CONCURRENT_STREAMS(4):100]
[SETTINGS_INITIAL_WINDOW_SIZE(7):65535]
[ 0.006] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
user-agent: nghttp2/0.4.0-DEV
[ 0.007] recv SETTINGS frame <length=15, flags=0x00, stream_id=0>
(niv=3)
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
[SETTINGS_INITIAL_WINDOW_SIZE(4):65535]
[SETTINGS_ENABLE_PUSH(2):0]
[ 0.007] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
; ACK
(niv=0)
[ 0.006] recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
(window_size_increment=1000000007)
[ 0.006] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
[ 0.007] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
; ACK
(niv=0)
[ 0.006] recv HEADERS frame <length=132, flags=0x04, stream_id=1>
[ 0.008] (stream_id=1) :status: 200
[ 0.008] (stream_id=1) accept-ranges: bytes
[ 0.008] (stream_id=1) content-encoding: gzip
[ 0.008] (stream_id=1) content-length: 146
[ 0.008] (stream_id=1) content-type: text/html
[ 0.008] (stream_id=1) date: Sat, 15 Feb 2014 08:14:12 GMT
[ 0.008] (stream_id=1) etag: "b1-4e5535a027780-gzip"
[ 0.008] (stream_id=1) last-modified: Sun, 01 Sep 2013 14:34:22 GMT
[ 0.008] (stream_id=1) server: Apache/2.4.6 (Debian)
[ 0.008] (stream_id=1) vary: Accept-Encoding
[ 0.008] (stream_id=1) via: 1.1 nghttpx
[ 0.008] recv HEADERS frame <length=141, flags=0x04, stream_id=1>
; END_HEADERS
(padlen=0)
; First response header
:status: 200
accept-ranges: bytes
content-encoding: gzip
content-length: 146
content-type: text/html
date: Sun, 27 Oct 2013 14:23:54 GMT
etag: "b1-4e5535a027780-gzip"
last-modified: Sun, 01 Sep 2013 14:34:22 GMT
server: Apache/2.4.6 (Debian)
vary: Accept-Encoding
via: 1.1 nghttpx
[ 0.006] recv DATA frame <length=146, flags=0x00, stream_id=1>
[ 0.006] recv DATA frame <length=0, flags=0x01, stream_id=1>
[ 0.008] recv DATA frame <length=146, flags=0x00, stream_id=1>
[ 0.008] recv DATA frame <length=0, flags=0x01, stream_id=1>
; END_STREAM
[ 0.007] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
[ 0.008] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
(last_stream_id=0, error_code=NO_ERROR(0), opaque_data(0)=[])
The HTTP Upgrade is performed like this::
$ src/nghttp -vnu http://localhost:8080
$ src/nghttp -nvu http://localhost:8080
[ 0.000] HTTP Upgrade request
GET / HTTP/1.1
Host: localhost:8080
Connection: Upgrade, HTTP2-Settings
Upgrade: HTTP-draft-09/2.0
HTTP2-Settings: AAAABAAAAGQAAAAHAAD__w
Upgrade: h2-10
HTTP2-Settings: AwAAAGQEAAD__w
Accept: */*
User-Agent: nghttp2/0.1.0-DEV
User-Agent: nghttp2/0.4.0-DEV
[ 0.000] HTTP Upgrade response
[ 0.001] HTTP Upgrade response
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: HTTP-draft-09/2.0
Upgrade: h2-10
[ 0.001] HTTP Upgrade success
[ 0.001] send SETTINGS frame <length=16, flags=0x00, stream_id=0>
[ 0.001] send SETTINGS frame <length=10, flags=0x00, stream_id=0>
(niv=2)
[SETTINGS_MAX_CONCURRENT_STREAMS(4):100]
[SETTINGS_INITIAL_WINDOW_SIZE(7):65535]
[ 0.001] recv SETTINGS frame <length=16, flags=0x00, stream_id=0>
(niv=2)
[SETTINGS_MAX_CONCURRENT_STREAMS(4):100]
[SETTINGS_INITIAL_WINDOW_SIZE(7):65535]
[ 0.001] recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
(window_size_increment=1000000007)
[ 0.001] recv HEADERS frame <length=121, flags=0x04, stream_id=1>
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
[SETTINGS_INITIAL_WINDOW_SIZE(4):65535]
[ 0.001] recv SETTINGS frame <length=15, flags=0x00, stream_id=0>
(niv=3)
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
[SETTINGS_INITIAL_WINDOW_SIZE(4):65535]
[SETTINGS_ENABLE_PUSH(2):0]
[ 0.001] (stream_id=1) :status: 200
[ 0.001] (stream_id=1) accept-ranges: bytes
[ 0.001] (stream_id=1) content-length: 177
[ 0.001] (stream_id=1) content-type: text/html
[ 0.001] (stream_id=1) date: Sat, 15 Feb 2014 08:16:23 GMT
[ 0.001] (stream_id=1) etag: "b1-4e5535a027780"
[ 0.001] (stream_id=1) last-modified: Sun, 01 Sep 2013 14:34:22 GMT
[ 0.001] (stream_id=1) server: Apache/2.4.6 (Debian)
[ 0.001] (stream_id=1) vary: Accept-Encoding
[ 0.001] (stream_id=1) via: 1.1 nghttpx
[ 0.001] recv HEADERS frame <length=132, flags=0x04, stream_id=1>
; END_HEADERS
(padlen=0)
; First response header
:status: 200
accept-ranges: bytes
content-length: 177
content-type: text/html
date: Sun, 27 Oct 2013 14:26:04 GMT
etag: "b1-4e5535a027780"
last-modified: Sun, 01 Sep 2013 14:34:22 GMT
server: Apache/2.4.6 (Debian)
vary: Accept-Encoding
via: 1.1 nghttpx
[ 0.001] recv DATA frame <length=177, flags=0x00, stream_id=1>
[ 0.001] recv DATA frame <length=0, flags=0x01, stream_id=1>
; END_STREAM
[ 0.001] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
[ 0.002] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
; ACK
(niv=0)
[ 0.001] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
[ 0.002] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
(last_stream_id=0, error_code=NO_ERROR(0), opaque_data(0)=[])
[ 0.001] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
[ 0.002] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
; ACK
(niv=0)
@ -278,50 +277,53 @@ information. Here is sample output from ``nghttpd`` server::
$ src/nghttpd --no-tls -v 8080
IPv4: listen on port 8080
IPv6: listen on port 8080
[id=1] [ 1.189] send SETTINGS frame <length=8, flags=0x00, stream_id=0>
(niv=1)
[SETTINGS_MAX_CONCURRENT_STREAMS(4):100]
[id=1] [ 1.191] recv SETTINGS frame <length=16, flags=0x00, stream_id=0>
[id=1] [ 1.027] send SETTINGS frame <length=10, flags=0x00, stream_id=0>
(niv=2)
[SETTINGS_MAX_CONCURRENT_STREAMS(4):100]
[SETTINGS_INITIAL_WINDOW_SIZE(7):65535]
[id=1] [ 1.191] recv HEADERS frame <length=47, flags=0x05, stream_id=1>
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
[SETTINGS_ENABLE_PUSH(2):0]
[id=1] [ 1.027] recv SETTINGS frame <length=10, flags=0x00, stream_id=0>
(niv=2)
[SETTINGS_MAX_CONCURRENT_STREAMS(3):100]
[SETTINGS_INITIAL_WINDOW_SIZE(4):65535]
[id=1] [ 1.027] (stream_id=1) :authority: localhost:8080
[id=1] [ 1.027] (stream_id=1) :method: GET
[id=1] [ 1.027] (stream_id=1) :path: /
[id=1] [ 1.027] (stream_id=1) :scheme: http
[id=1] [ 1.027] (stream_id=1) accept: */*
[id=1] [ 1.027] (stream_id=1) accept-encoding: gzip, deflate
[id=1] [ 1.027] (stream_id=1) user-agent: nghttp2/0.4.0-DEV
[id=1] [ 1.027] recv HEADERS frame <length=48, flags=0x05, stream_id=1>
; END_STREAM | END_HEADERS
(padlen=0)
; Open new stream
:authority: localhost:8080
:method: GET
:path: /
:scheme: http
accept: */*
accept-encoding: gzip, deflate
user-agent: nghttp2/0.1.0-DEV
[id=1] [ 1.192] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
[id=1] [ 1.027] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
; ACK
(niv=0)
[id=1] [ 1.192] send HEADERS frame <length=70, flags=0x04, stream_id=1>
[id=1] [ 1.027] send HEADERS frame <length=72, flags=0x04, stream_id=1>
; END_HEADERS
(padlen=0)
; First response header
:status: 404
content-encoding: gzip
content-type: text/html; charset=UTF-8
date: Sun, 27 Oct 2013 14:27:53 GMT
server: nghttpd nghttp2/0.1.0-DEV
[id=1] [ 1.192] send DATA frame <length=117, flags=0x00, stream_id=1>
[id=1] [ 1.192] send DATA frame <length=0, flags=0x01, stream_id=1>
date: Sat, 15 Feb 2014 08:18:53 GMT
server: nghttpd nghttp2/0.4.0-DEV
[id=1] [ 1.028] send DATA frame <length=118, flags=0x00, stream_id=1>
[id=1] [ 1.028] send DATA frame <length=0, flags=0x01, stream_id=1>
; END_STREAM
[id=1] [ 1.192] stream_id=1 closed
[id=1] [ 1.192] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
[id=1] [ 1.028] stream_id=1 closed
[id=1] [ 1.028] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
; ACK
(niv=0)
[id=1] [ 1.192] recv GOAWAY frame <length=8, flags=0x00, stream_id=0>
[id=1] [ 1.028] recv GOAWAY frame <length=8, flags=0x00, stream_id=0>
(last_stream_id=0, error_code=NO_ERROR(0), opaque_data(0)=[])
[id=1] [ 1.192] closed
[id=1] [ 1.028] closed
nghttpx - proxy
+++++++++++++++
The ``nghttpx`` is a multi-threaded reverse proxy for
HTTP-draft-09/2.0, SPDY and HTTP/1.1. It has several operation modes:
h2-10, SPDY and HTTP/1.1. It has several operation modes:
================== ============================== ============== =============
Mode option Frontend Backend Note
@ -334,7 +336,7 @@ default mode HTTP/2.0, 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 HTTP-draft-09/2.0, SPDY and HTTP/1.1 and
a reverse proxy and listens h2-10, 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
@ -440,21 +442,18 @@ deflatehd - header compressor
The ``deflatehd`` reads JSON data or HTTP/1-style header fields from
stdin and outputs compressed header block in JSON.
For the JSON input, the root JSON object must contain ``context`` key,
which indicates which compression context is used. If it is
``request``, request compression context is used. Otherwise, response
compression context is used. The value of ``cases`` key contains the
sequence of input header set. They share the same compression context
and are processed in the order they appear. Each item in the sequence
is a JSON object and it must have at least ``headers`` key. Its value
is an array of a JSON object containing exactly one name/value pair.
For the JSON input, the root JSON object must include ``cases``
key. Its value has to include the sequence of input header set. They
share the same compression context and are processed in the order they
appear. Each item in the sequence is a JSON object and it must
include ``headers`` key. Its value is an array of a JSON object ,
which includes exactly one name/value pair.
Example:
.. code-block:: json
{
"context": "request",
"cases":
[
{
@ -485,9 +484,7 @@ Example::
:method: POST
user-agent: nghttp2
The output is JSON object. It contains ``context`` key and its value
is ``request`` if the compression context is request, otherwise
``response``. The root JSON object also contains ``cases`` key and its
The output is JSON object. It should include ``cases`` key and its
value is an array of JSON object, which has at least following keys:
seq
@ -516,7 +513,6 @@ Examples:
.. code-block:: json
{
"context": "request",
"cases":
[
{
@ -578,9 +574,9 @@ The output can be used as the input for ``inflatehd`` and
``deflatehd``.
With ``-d`` option, the extra ``header_table`` key is added and its
associated value contains the state of dyanmic header table after the
corresponding header set was processed. The value contains following
keys:
associated value includes the state of dyanmic header table after the
corresponding header set was processed. The value includes at least
following keys:
entries
The entry in the header table. If ``referenced`` is ``true``, it
@ -616,7 +612,6 @@ Example:
.. code-block:: json
{
"context": "request",
"cases":
[
{
@ -772,21 +767,17 @@ inflatehd - header decompressor
The ``inflatehd`` reads JSON data from stdin and outputs decompressed
name/value pairs in JSON.
The root JSON object must contain ``context`` key, which indicates
which compression context is used. If it is ``request``, request
compression context is used. Otherwise, response compression context
is used. The value of ``cases`` key contains the sequence of
compressed header block. They share the same compression context and
are processed in the order they appear. Each item in the sequence is a
JSON object and it must have at least ``wire`` key. Its value is a
string containing compressed header block in hex string.
The root JSON object must include ``cases`` key. Its value has to
include the sequence of compressed header block. They share the same
compression context and are processed in the order they appear. Each
item in the sequence is a JSON object and it must have at least
``wire`` key. Its value is a compressed header block in hex string.
Example:
.. code-block:: json
{
"context": "request",
"cases":
[
{ "wire": "8285" },
@ -794,16 +785,14 @@ Example:
]
}
The output is JSON object. It contains ``context`` key and its value
is ``request`` if the compression context is request, otherwise
``response``. The root JSON object also contains ``cases`` key and its
The output is JSON object. It should include ``cases`` key and its
value is an array of JSON object, which has at least following keys:
seq
The index of header set in the input.
headers
The JSON array contains decompressed name/value pairs.
The JSON array includes decompressed name/value pairs.
wire
The compressed header block in hex string.
@ -817,7 +806,6 @@ Example:
.. code-block:: json
{
"context": "request",
"cases":
[
{
@ -872,7 +860,7 @@ The output can be used as the input for ``deflatehd`` and
``inflatehd``.
With ``-d`` option, the extra ``header_table`` key is added and its
associated value contains the state of dyanmic header table after the
associated value includes the state of dyanmic header table after the
corresponding header set was processed. The format is the same as
``deflatehd``.

View File

@ -40,13 +40,13 @@ extern "C" {
*
* The protocol version identification of this library supports.
*/
#define NGHTTP2_PROTO_VERSION_ID "HTTP-draft-09/2.0"
#define NGHTTP2_PROTO_VERSION_ID "h2-10"
/**
* @macro
*
* The length of :macro:`NGHTTP2_PROTO_VERSION_ID`.
*/
#define NGHTTP2_PROTO_VERSION_ID_LEN 17
#define NGHTTP2_PROTO_VERSION_ID_LEN 5
struct nghttp2_session;
/**
@ -364,11 +364,11 @@ typedef enum {
/**
* The WINDOW_UPDATE frame.
*/
NGHTTP2_WINDOW_UPDATE = 9,
NGHTTP2_WINDOW_UPDATE = 8,
/**
* The CONTINUATION frame.
*/
NGHTTP2_CONTINUATION = 10
NGHTTP2_CONTINUATION = 9
} nghttp2_frame_type;
/**
@ -401,7 +401,19 @@ typedef enum {
/**
* The ACK flag.
*/
NGHTTP2_FLAG_ACK = 0x1
NGHTTP2_FLAG_ACK = 0x1,
/**
* The END_SEGMENT flag.
*/
NGHTTP2_FLAG_END_SEGMENT = 0x2,
/**
* The PAD_LOW flag.
*/
NGHTTP2_FLAG_PAD_LOW = 0x10,
/**
* The PAD_HIGH flag.
*/
NGHTTP2_FLAG_PAD_HIGH = 0x20
} nghttp2_flag;
/**
@ -420,19 +432,15 @@ typedef enum {
/**
* SETTINGS_MAX_CONCURRENT_STREAMS
*/
NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 4,
NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 3,
/**
* SETTINGS_INITIAL_WINDOW_SIZE
*/
NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 7,
/**
* SETTINGS_FLOW_CONTROL_OPTIONS
*/
NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS = 10,
NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 4,
/**
* Maximum ID of :type:`nghttp2_settings_id`.
*/
NGHTTP2_SETTINGS_MAX = 10
NGHTTP2_SETTINGS_MAX = 4
} nghttp2_settings_id;
/**
@ -493,7 +501,11 @@ typedef enum {
/**
* ENHANCE_YOUR_CALM
*/
NGHTTP2_ENHANCE_YOUR_CALM = 420
NGHTTP2_ENHANCE_YOUR_CALM = 11,
/**
* INADEQUATE_SECURITY
*/
NGHTTP2_INADEQUATE_SECURITY = 12
} nghttp2_error_code;
/**
@ -587,6 +599,11 @@ typedef struct {
*/
typedef struct {
nghttp2_frame_hd hd;
/**
* The length of the padding in this frame. This includes PAD_HIGH
* and PAD_LOW.
*/
size_t padlen;
} nghttp2_data;
/**
@ -630,6 +647,11 @@ typedef struct {
* The frame header.
*/
nghttp2_frame_hd hd;
/**
* The length of the padding in this frame. This includes PAD_HIGH
* and PAD_LOW.
*/
size_t padlen;
/**
* The name/value pairs.
*/
@ -721,6 +743,11 @@ typedef struct {
* The frame header.
*/
nghttp2_frame_hd hd;
/**
* The length of the padding in this frame. This includes PAD_HIGH
* and PAD_LOW.
*/
size_t padlen;
/**
* The name/value pairs.
*/
@ -1149,6 +1176,26 @@ typedef int (*nghttp2_on_header_callback)
const uint8_t *value, size_t valuelen,
void *user_data);
/**
* @functypedef
*
* Callback function invoked when the library asks application how
* much padding is required for the transmission of the |frame|. The
* application must choose the total length of payload including
* padded bytes in range [frame->hd.length, max_payloadlen],
* inclusive. Choosing number not in this range will be treated as
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. Returning
* ``frame->hd.length`` means no padding is added. Returning
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` will make
* `nghttp2_session_send()` function immediately return
* :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.
*/
typedef ssize_t (*nghttp2_select_padding_callback)
(nghttp2_session *session,
const nghttp2_frame *frame,
size_t max_payloadlen,
void *user_data);
/**
* @struct
*
@ -1212,6 +1259,11 @@ typedef struct {
* received.
*/
nghttp2_on_header_callback on_header_callback;
/**
* Callback function invoked when the library asks application how
* much padding is required for the transmission of the given frame.
*/
nghttp2_select_padding_callback select_padding_callback;
} nghttp2_session_callbacks;
/**
@ -1623,9 +1675,6 @@ size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session);
* window_size_increment with `nghttp2_submit_window_update()`, this
* function returns the number of bytes less than actually received.
*
* If flow control is disabled for that stream, this function returns
* 0.
*
* This function returns -1 if it fails.
*/
int32_t nghttp2_session_get_stream_effective_recv_data_length
@ -1656,9 +1705,6 @@ int32_t nghttp2_session_get_stream_effective_local_window_size
* with `nghttp2_submit_window_update()`, this function returns the
* number of bytes less than actually received.
*
* If flow control is disabled for a connection, this function returns
* 0.
*
* This function returns -1 if it fails.
*/
int32_t nghttp2_session_get_effective_recv_data_length
@ -2021,8 +2067,8 @@ int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags,
* negative error codes:
*
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
* The |iv| contains invalid value (e.g., attempting to re-enable
* flow control).
* The |iv| contains invalid value (e.g., initial window size
* strictly greater than (1 << 31) - 1.
* :enum:`NGHTTP2_ERR_NOMEM`
* Out of memory.
*/

View File

@ -110,7 +110,8 @@ void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags,
nghttp2_settings_entry *iv, size_t niv)
{
memset(frame, 0, sizeof(nghttp2_settings));
nghttp2_frame_set_hd(&frame->hd, niv*8, NGHTTP2_SETTINGS, flags, 0);
nghttp2_frame_set_hd(&frame->hd, niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH,
NGHTTP2_SETTINGS, flags, 0);
frame->niv = niv;
frame->iv = iv;
}
@ -184,6 +185,7 @@ void nghttp2_frame_window_update_free(nghttp2_window_update *frame)
void nghttp2_frame_data_init(nghttp2_data *frame, nghttp2_private_data *pdata)
{
frame->hd = pdata->hd;
frame->padlen = pdata->padlen;
/* flags may have NGHTTP2_FLAG_END_STREAM even if the sent chunk
is not the end of the stream */
if(!pdata->eof) {
@ -191,6 +193,13 @@ void nghttp2_frame_data_init(nghttp2_data *frame, nghttp2_private_data *pdata)
}
}
size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen)
{
return padlen
- ((frame->hd.flags & NGHTTP2_FLAG_PAD_HIGH) > 0)
- ((frame->hd.flags & NGHTTP2_FLAG_PAD_LOW) > 0);
}
void nghttp2_frame_private_data_init(nghttp2_private_data *frame,
uint8_t flags,
int32_t stream_id,
@ -205,19 +214,6 @@ void nghttp2_frame_private_data_init(nghttp2_private_data *frame,
void nghttp2_frame_private_data_free(nghttp2_private_data *frame)
{}
/*
* Returns the offset of the name/header block in the HEADERS frame,
* including frame header length.
*/
static size_t headers_nv_offset(nghttp2_headers *frame)
{
if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
return NGHTTP2_FRAME_HEAD_LENGTH + 4;
} else {
return NGHTTP2_FRAME_HEAD_LENGTH;
}
}
size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame)
{
if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
@ -229,37 +225,48 @@ size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame)
ssize_t nghttp2_frame_pack_headers(uint8_t **buf_ptr,
size_t *buflen_ptr,
size_t *bufoff_ptr,
nghttp2_headers *frame,
nghttp2_hd_deflater *deflater)
{
ssize_t framelen;
size_t nv_offset = headers_nv_offset(frame);
size_t payloadoff = NGHTTP2_FRAME_HEAD_LENGTH + 2;
size_t nv_offset =
payloadoff + nghttp2_frame_headers_payload_nv_offset(frame);
ssize_t rv;
size_t payloadlen;
rv = nghttp2_hd_deflate_hd(deflater, buf_ptr, buflen_ptr, nv_offset,
frame->nva, frame->nvlen);
if(rv < 0) {
return rv;
}
framelen = rv + nv_offset;
if(NGHTTP2_FRAME_HEAD_LENGTH + NGHTTP2_MAX_FRAME_LENGTH < rv + nv_offset) {
frame->hd.length = NGHTTP2_MAX_FRAME_LENGTH;
frame->hd.flags &= ~NGHTTP2_FLAG_END_HEADERS;
} else {
frame->hd.length = framelen - NGHTTP2_FRAME_HEAD_LENGTH;
}
payloadlen = nghttp2_frame_headers_payload_nv_offset(frame) + rv;
*bufoff_ptr = 2;
frame->padlen = 0;
frame->hd.length = payloadlen;
/* If frame->nvlen == 0, *buflen_ptr may be smaller than
nv_offset */
rv = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, nv_offset);
if(rv < 0) {
return rv;
}
memset(*buf_ptr, 0, nv_offset);
memset(*buf_ptr + *bufoff_ptr, 0, NGHTTP2_FRAME_HEAD_LENGTH);
/* pack ctrl header after length is determined */
nghttp2_frame_pack_frame_hd(*buf_ptr, &frame->hd);
if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
nghttp2_put_uint32be(&(*buf_ptr)[8], frame->pri);
if(NGHTTP2_MAX_FRAME_LENGTH < payloadlen) {
/* Needs CONTINUATION */
nghttp2_frame_hd hd = frame->hd;
hd.flags &= ~NGHTTP2_FLAG_END_HEADERS;
hd.length = NGHTTP2_MAX_FRAME_LENGTH;
nghttp2_frame_pack_frame_hd(*buf_ptr + *bufoff_ptr, &hd);
} else {
nghttp2_frame_pack_frame_hd(*buf_ptr + *bufoff_ptr, &frame->hd);
}
return framelen;
if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
nghttp2_put_uint32be(&(*buf_ptr)[payloadoff], frame->pri);
}
return *bufoff_ptr + NGHTTP2_FRAME_HEAD_LENGTH + frame->hd.length;
}
int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame,
@ -267,7 +274,6 @@ int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame,
size_t payloadlen)
{
if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
assert(payloadlen == 4);
frame->pri = nghttp2_get_uint32(payload) & NGHTTP2_PRIORITY_MASK;
} else {
frame->pri = NGHTTP2_PRI_DEFAULT;
@ -341,11 +347,11 @@ size_t nghttp2_frame_pack_settings_payload(uint8_t *buf,
size_t niv)
{
size_t i;
for(i = 0; i < niv; ++i, buf += 8) {
nghttp2_put_uint32be(buf, iv[i].settings_id);
nghttp2_put_uint32be(buf + 4, iv[i].value);
for(i = 0; i < niv; ++i, buf += NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) {
buf[0] = iv[i].settings_id;
nghttp2_put_uint32be(buf + 1, iv[i].value);
}
return 8 * niv;
return NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH * niv;
}
int nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame,
@ -366,9 +372,8 @@ int nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame,
void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv,
const uint8_t *payload)
{
iv->settings_id = nghttp2_get_uint32(&payload[0]) &
NGHTTP2_SETTINGS_ID_MASK;
iv->value = nghttp2_get_uint32(&payload[4]);
iv->settings_id = payload[0];
iv->value = nghttp2_get_uint32(&payload[1]);
}
int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr,
@ -377,13 +382,13 @@ int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr,
size_t payloadlen)
{
size_t i;
*niv_ptr = payloadlen / 8;
*niv_ptr = payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH;
*iv_ptr = malloc((*niv_ptr)*sizeof(nghttp2_settings_entry));
if(*iv_ptr == NULL) {
return NGHTTP2_ERR_NOMEM;
}
for(i = 0; i < *niv_ptr; ++i) {
size_t off = i*8;
size_t off = i * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH;
nghttp2_frame_unpack_settings_entry(&(*iv_ptr)[i], &payload[off]);
}
return 0;
@ -391,35 +396,45 @@ int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr,
ssize_t nghttp2_frame_pack_push_promise(uint8_t **buf_ptr,
size_t *buflen_ptr,
size_t *bufoff_ptr,
nghttp2_push_promise *frame,
nghttp2_hd_deflater *deflater)
{
ssize_t framelen;
size_t nv_offset = NGHTTP2_FRAME_HEAD_LENGTH + 4;
size_t payloadoff = NGHTTP2_FRAME_HEAD_LENGTH + 2;
size_t nv_offset = payloadoff + 4;
ssize_t rv;
size_t payloadlen;
rv = nghttp2_hd_deflate_hd(deflater, buf_ptr, buflen_ptr, nv_offset,
frame->nva, frame->nvlen);
if(rv < 0) {
return rv;
}
framelen = rv + nv_offset;
if(NGHTTP2_FRAME_HEAD_LENGTH + NGHTTP2_MAX_FRAME_LENGTH < rv + nv_offset) {
frame->hd.length = NGHTTP2_MAX_FRAME_LENGTH;
frame->hd.flags &= ~NGHTTP2_FLAG_END_HEADERS;
} else {
frame->hd.length = framelen - NGHTTP2_FRAME_HEAD_LENGTH;
}
payloadlen = 4 + rv;
*bufoff_ptr = 2;
frame->padlen = 0;
frame->hd.length = payloadlen;
/* If frame->nvlen == 0, *buflen_ptr may be smaller than
nv_offset */
rv = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, nv_offset);
if(rv < 0) {
return rv;
}
memset(*buf_ptr, 0, nv_offset);
memset(*buf_ptr + *bufoff_ptr, 0, NGHTTP2_FRAME_HEAD_LENGTH);
/* pack ctrl header after length is determined */
nghttp2_frame_pack_frame_hd(*buf_ptr, &frame->hd);
nghttp2_put_uint32be(&(*buf_ptr)[8], frame->promised_stream_id);
return framelen;
if(NGHTTP2_MAX_FRAME_LENGTH < payloadlen) {
/* Needs CONTINUATION */
nghttp2_frame_hd hd = frame->hd;
hd.flags &= ~NGHTTP2_FLAG_END_HEADERS;
hd.length = NGHTTP2_MAX_FRAME_LENGTH;
nghttp2_frame_pack_frame_hd(*buf_ptr + *bufoff_ptr, &hd);
} else {
nghttp2_frame_pack_frame_hd(*buf_ptr + *bufoff_ptr, &frame->hd);
}
nghttp2_put_uint32be(&(*buf_ptr)[payloadoff], frame->promised_stream_id);
return *bufoff_ptr + NGHTTP2_FRAME_HEAD_LENGTH + frame->hd.length;
}
int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame,
@ -617,29 +632,67 @@ ssize_t nghttp2_nv_array_copy(nghttp2_nv **nva_ptr,
return nvlen;
}
int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv,
int32_t flow_control_opt)
int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv)
{
size_t i;
for(i = 0; i < niv; ++i) {
switch(iv[i].settings_id) {
case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
break;
case NGHTTP2_SETTINGS_ENABLE_PUSH:
if(iv[i].value != 0 && iv[i].value != 1) {
return 0;
}
break;
case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
if(iv[i].value > (uint32_t)NGHTTP2_MAX_WINDOW_SIZE) {
return 0;
}
break;
case NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS:
if(flow_control_opt) {
if((iv[i].value & 0x1) == 0) {
/* Attempt to re-enabling flow-control is error */
return 0;
}
} else {
flow_control_opt = iv[i].value & 0x1;
}
default:
break;
return 0;
}
}
return 1;
}
int nghttp2_frame_add_pad(uint8_t **buf_ptr, size_t *buflen_ptr,
size_t *bufoff_ptr,
uint8_t *flags_ptr,
size_t payloadlen,
size_t padlen)
{
int rv;
size_t trail_padlen = 0;
/* extra 2 bytes for PAD_HIGH and PAD_LOW. */
size_t trail_padoff = *bufoff_ptr + NGHTTP2_FRAME_HEAD_LENGTH + payloadlen;
if(padlen > 257) {
uint8_t *p;
*bufoff_ptr -= 2;
trail_padlen = padlen - 2;
*flags_ptr |= NGHTTP2_FLAG_PAD_HIGH | NGHTTP2_FLAG_PAD_LOW;
p = *buf_ptr + *bufoff_ptr + NGHTTP2_FRAME_HEAD_LENGTH;
*p++ = trail_padlen >> 8;
*p = trail_padlen & 0xff;
} else if(padlen > 0) {
--*bufoff_ptr;
trail_padlen = padlen - 1;
*flags_ptr |= NGHTTP2_FLAG_PAD_LOW;
(*buf_ptr)[*bufoff_ptr + NGHTTP2_FRAME_HEAD_LENGTH] = trail_padlen;
} else {
return 0;
}
rv = nghttp2_reserve_buffer(buf_ptr, buflen_ptr,
trail_padoff + trail_padlen);
if(rv != 0) {
return rv;
}
/* We have to zero out padding bytes so that we won't reveal the
possible internal data to the remote peer */
memset((*buf_ptr) + trail_padoff, 0, trail_padlen);
return 0;
}

View File

@ -48,6 +48,9 @@
/* The number of bytes of frame header. */
#define NGHTTP2_FRAME_HEAD_LENGTH 8
/* The number of bytes for each SETTINGS entry */
#define NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH 5
/* Category of frames. */
typedef enum {
/* non-DATA frame */
@ -68,6 +71,11 @@ typedef struct {
* The data to be sent for this DATA frame.
*/
nghttp2_data_provider data_prd;
/**
* The number of bytes added as padding. This includes PAD_HIGH and
* PAD_LOW.
*/
size_t padlen;
/**
* The flag to indicate whether EOF was reached or not. Initially
* |eof| is 0. It becomes 1 after all data were read. This is used
@ -96,14 +104,19 @@ size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame);
* expansion occurred, memory previously pointed by |*buf_ptr| may
* change. |*buf_ptr| and |*buflen_ptr| are updated accordingly.
*
* The first byte the frame is serialized is returned in the
* |*bufoff_ptr|. Currently, it is always 2 to account for possible
* PAD_HIGH and PAD_LOW.
*
* frame->hd.length is assigned after length is determined during
* packing process. If payload length is strictly larger than
* NGHTTP2_MAX_FRAME_LENGTH, payload data is still serialized as is,
* but frame->hd.length is set to NGHTTP2_MAX_FRAME_LENGTH and
* NGHTTP2_FLAG_END_HEADERS flag is cleared from frame->hd.flags.
*
* This function returns the size of packed frame if it succeeds, or
* returns one of the following negative error codes:
* This function returns the size of packed frame (which includes
* |*bufoff_ptr| bytes) if it succeeds, or returns one of the
* following negative error codes:
*
* NGHTTP2_ERR_HEADER_COMP
* The deflate operation failed.
@ -114,6 +127,7 @@ size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame);
*/
ssize_t nghttp2_frame_pack_headers(uint8_t **buf_ptr,
size_t *buflen_ptr,
size_t *bufoff_ptr,
nghttp2_headers *frame,
nghttp2_hd_deflater *deflater);
@ -242,12 +256,20 @@ int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr,
* expansion occurred, memory previously pointed by |*buf_ptr| may
* change. |*buf_ptr| and |*buflen_ptr| are updated accordingly.
*
* The first byte the frame is serialized is returned in the
* |*bufoff_ptr|. Currently, it is always 2 to account for possible
* PAD_HIGH and PAD_LOW.
*
* frame->hd.length is assigned after length is determined during
* packing process. If payload length is strictly larger than
* NGHTTP2_MAX_FRAME_LENGTH, payload data is still serialized as is,
* but frame->hd.length is set to NGHTTP2_MAX_FRAME_LENGTH and
* NGHTTP2_FLAG_END_HEADERS flag is cleared from frame->hd.flags.
*
* This function returns the size of packed frame (which includes
* |*bufoff_ptr| bytes) if it succeeds, or returns one of the
* following negative error codes:
*
* This function returns the size of packed frame if it succeeds, or
* returns one of the following negative error codes:
*
@ -260,6 +282,7 @@ int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr,
*/
ssize_t nghttp2_frame_pack_push_promise(uint8_t **buf_ptr,
size_t *buflen_ptr,
size_t *bufoff_ptr,
nghttp2_push_promise *frame,
nghttp2_hd_deflater *deflater);
@ -418,6 +441,13 @@ void nghttp2_frame_window_update_free(nghttp2_window_update *frame);
void nghttp2_frame_data_init(nghttp2_data *frame, nghttp2_private_data *pdata);
/*
* Returns the number of padding bytes after payload. The total
* padding length is given in the |padlen|. The returned value does
* not include the PAD_HIGH and PAD_LOW.
*/
size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen);
void nghttp2_frame_private_data_init(nghttp2_private_data *frame,
uint8_t flags,
int32_t stream_id,
@ -472,12 +502,87 @@ void nghttp2_nv_array_del(nghttp2_nv *nva);
/*
* Checks that the |iv|, which includes |niv| entries, does not have
* invalid values. The |flow_control_opt| is current flow control
* option value.
* invalid values.
*
* This function returns nonzero if it succeeds, or 0.
*/
int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv,
int32_t flow_control_opt);
int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv);
/*
* Add padding to the payload in the |*buf_ptr| of length
* |*buflen_ptr|. The payload length is given in |payloadlen|. The
* frame header starts at offset |*bufoff_ptr|. Therefore, the payload
* must start at offset *bufoff_ptr + NGHTTP2_FRAME_HEAD_LENGTH from
* |*buf_ptr| to account for PAD_HIGH and PAD_LOW. The padding is
* given in the |padlen|.
*
* The |*flags_ptr| is updated to include NGHTTP2_FLAG_PAD_LOW and
* NGHTTP2_FLAG_PAD_HIGH based on the padding length. The
* |*bufoff_ptr| will have the offset starting the frame header in
* |*buf_ptr|.
*
* The |*buf_ptr| and |*buflen_ptr| may be extended to include padding
* bytes.
*
* The padding specifier PAD_HIGH and PAD_LOW are located right after
* the frame header. But they may not be there depending of the length
* of the padding. To save the additional buffer copy, we allocate
* buffer size as if these 2 bytes always exist. Depending of the
* length of the padding, we move the location of frame header and
* adjust |*bufoff_ptr|. If more than or equal to 256 padding is made,
* the |*bufoff_ptr| is 0 and the content of the |*buf_ptr| looks like
* this:
*
* 0 1 2 3
* 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 header |
* +---------------+---------------+-------------------------------+
* | Pad high | Pad low | Payload ...
* +---------------+---------------+-------------------------------+
*
*
* If padding is less than 256 but strictly more than 0, the
* |*bufoff_ptr| is 1 and the |*buf_ptr| looks like this:
*
* 0 1 2 3
* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Unused | Frame header ...
* +---------------+-----------------------------------------------+
* . Frame header ...
* +---------------+---------------+-------------------------------+
* . Frame Header | Pad low | Payload ...
* +---------------+---------------+-------------------------------+
*
* If no padding is added, the |*bufoff_ptr| is 2 and the |*buf_ptr|
* looks like this:
*
* 0 1 2 3
* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Unused | Frame header ...
* +-------------------------------+-------------------------------+
* . Frame header ...
* +-------------------------------+-------------------------------+
* . Frame Header | Payload ...
* +-------------------------------+-------------------------------+
*
* Notice that the position of payload does not change. This way, we
* can set PAD_HIGH and PAD_LOW after payload was serialized and no
* additional copy operation is required (if the |*buf_ptr| is large
* enough to account the additional padding, of course).
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory.
*/
int nghttp2_frame_add_pad(uint8_t **buf_ptr, size_t *buflen_ptr,
size_t *bufoff_ptr, uint8_t *flags_ptr,
size_t payloadlen, size_t padlen);
#endif /* NGHTTP2_FRAME_H */

View File

@ -286,13 +286,10 @@ static void nghttp2_hd_ringbuf_pop_back(nghttp2_hd_ringbuf *ringbuf)
}
static int nghttp2_hd_context_init(nghttp2_hd_context *context,
nghttp2_hd_role role,
nghttp2_hd_side side,
size_t deflate_hd_table_bufsize_max)
nghttp2_hd_role role)
{
int rv;
context->role = role;
context->side = side;
context->bad = 0;
context->hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE;
rv = nghttp2_hd_ringbuf_init
@ -302,41 +299,41 @@ static int nghttp2_hd_context_init(nghttp2_hd_context *context,
return rv;
}
context->deflate_hd_table_bufsize_max = deflate_hd_table_bufsize_max;
context->deflate_hd_table_bufsize = 0;
context->deflate_hd_tablelen = 0;
context->hd_table_bufsize = 0;
return 0;
}
int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater, nghttp2_hd_side side)
int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater)
{
return nghttp2_hd_deflate_init2(deflater, side,
return nghttp2_hd_deflate_init2(deflater,
NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
}
int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater,
nghttp2_hd_side side,
size_t deflate_hd_table_bufsize_max)
{
int rv;
rv = nghttp2_hd_context_init(&deflater->ctx, NGHTTP2_HD_ROLE_DEFLATE, side,
deflate_hd_table_bufsize_max);
rv = nghttp2_hd_context_init(&deflater->ctx, NGHTTP2_HD_ROLE_DEFLATE);
if(rv != 0) {
return rv;
}
deflater->no_refset = 0;
deflater->deflate_hd_table_bufsize_max = deflate_hd_table_bufsize_max;
return 0;
}
int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_hd_side side)
int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater)
{
int rv;
rv = nghttp2_hd_context_init(&inflater->ctx, NGHTTP2_HD_ROLE_INFLATE, side,
NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE);
rv = nghttp2_hd_context_init(&inflater->ctx, NGHTTP2_HD_ROLE_INFLATE);
if(rv != 0) {
return rv;
}
inflater->settings_hd_table_bufsize_max =
NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE;
inflater->ent_keep = NULL;
inflater->name_keep = NULL;
inflater->value_keep = NULL;
@ -559,16 +556,38 @@ static uint8_t* decode_length(ssize_t *res, int *final, ssize_t initial,
return in + 1;
}
static int emit_indexed0(uint8_t **buf_ptr, size_t *buflen_ptr,
static int emit_clear_refset(uint8_t **buf_ptr, size_t *buflen_ptr,
size_t *offset_ptr)
{
int rv;
rv = ensure_write_buffer(buf_ptr, buflen_ptr, *offset_ptr, 1);
uint8_t *bufp;
rv = ensure_write_buffer(buf_ptr, buflen_ptr, *offset_ptr, 2);
if(rv != 0) {
return rv;
}
*(*buf_ptr + *offset_ptr) = 0x80u;
++*offset_ptr;
bufp = *buf_ptr + *offset_ptr;
*bufp++ = 0x80u;
*bufp = 0x80u;
*offset_ptr += 2;
return 0;
}
static int emit_table_size(uint8_t **buf_ptr, size_t *buflen_ptr,
size_t *offset_ptr, size_t table_size)
{
int rv;
uint8_t *bufp;
size_t blocklen = 1 + count_encoded_length(table_size, 7);
rv = ensure_write_buffer(buf_ptr, buflen_ptr, *offset_ptr, blocklen);
if(rv != 0) {
return rv;
}
DEBUGF(fprintf(stderr, "emit table_size=%zu\n", table_size));
bufp = *buf_ptr + *offset_ptr;
*bufp++ = 0x80u;
*bufp = 0;
encode_length(bufp, table_size, 7);
*offset_ptr += blocklen;
return 0;
}
@ -591,15 +610,14 @@ static int emit_indexed_block(uint8_t **buf_ptr, size_t *buflen_ptr,
static size_t emit_string(uint8_t *buf, size_t buflen,
size_t enclen, int huffman,
const uint8_t *str, size_t len,
nghttp2_hd_side side)
const uint8_t *str, size_t len)
{
size_t rv;
*buf = huffman ? 1 << 7 : 0;
rv = encode_length(buf, enclen, 7);
buf += rv;
if(huffman) {
nghttp2_hd_huff_encode(buf, buflen - rv, str, len, side);
nghttp2_hd_huff_encode(buf, buflen - rv, str, len);
} else {
assert(enclen == len);
memcpy(buf, str, len);
@ -610,12 +628,11 @@ static size_t emit_string(uint8_t *buf, size_t buflen,
static int emit_indname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
size_t *offset_ptr, size_t index,
const uint8_t *value, size_t valuelen,
int inc_indexing,
nghttp2_hd_side side)
int inc_indexing)
{
int rv;
uint8_t *bufp;
size_t encvallen = nghttp2_hd_huff_encode_count(value, valuelen, side);
size_t encvallen = nghttp2_hd_huff_encode_count(value, valuelen);
size_t blocklen = count_encoded_length(index + 1, 6);
int huffman = encvallen < valuelen;
if(!huffman) {
@ -630,7 +647,7 @@ static int emit_indname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
*bufp = inc_indexing ? 0 : 0x40u;
bufp += encode_length(bufp, index + 1, 6);
bufp += emit_string(bufp, *buflen_ptr - (bufp - *buf_ptr),
encvallen, huffman, value, valuelen, side);
encvallen, huffman, value, valuelen);
assert(bufp - (*buf_ptr + *offset_ptr) == (ssize_t)blocklen);
*offset_ptr += blocklen;
return 0;
@ -638,15 +655,14 @@ static int emit_indname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
static int emit_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
size_t *offset_ptr, nghttp2_nv *nv,
int inc_indexing,
nghttp2_hd_side side)
int inc_indexing)
{
int rv;
uint8_t *bufp;
size_t encnamelen =
nghttp2_hd_huff_encode_count(nv->name, nv->namelen, side);
nghttp2_hd_huff_encode_count(nv->name, nv->namelen);
size_t encvallen =
nghttp2_hd_huff_encode_count(nv->value, nv->valuelen, side);
nghttp2_hd_huff_encode_count(nv->value, nv->valuelen);
size_t blocklen = 1;
int name_huffman = encnamelen < nv->namelen;
int value_huffman = encvallen < nv->valuelen;
@ -665,9 +681,9 @@ static int emit_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
bufp = *buf_ptr + *offset_ptr;
*bufp++ = inc_indexing ? 0 : 0x40u;
bufp += emit_string(bufp, *buflen_ptr - (bufp - *buf_ptr),
encnamelen, name_huffman, nv->name, nv->namelen, side);
encnamelen, name_huffman, nv->name, nv->namelen);
bufp += emit_string(bufp, *buflen_ptr - (bufp - *buf_ptr),
encvallen, value_huffman, nv->value, nv->valuelen, side);
encvallen, value_huffman, nv->value, nv->valuelen);
*offset_ptr += blocklen;
return 0;
}
@ -707,11 +723,6 @@ static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context,
size_t index = context->hd_table.len - 1;
nghttp2_hd_entry* ent = nghttp2_hd_ringbuf_get(&context->hd_table, index);
context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen);
if(context->hd_table_bufsize < context->deflate_hd_table_bufsize) {
context->deflate_hd_table_bufsize -= entry_room(ent->nv.namelen,
ent->nv.valuelen);
--context->deflate_hd_tablelen;
}
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
@ -734,82 +745,19 @@ static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context,
free(ent);
}
}
while(context->deflate_hd_table_bufsize + room >
context->deflate_hd_table_bufsize_max
&& context->deflate_hd_tablelen > 0) {
size_t index = context->deflate_hd_tablelen - 1;
nghttp2_hd_entry *ent =
nghttp2_hd_ringbuf_get(&context->hd_table, index);
context->deflate_hd_table_bufsize -= entry_room(ent->nv.namelen,
ent->nv.valuelen);
--context->deflate_hd_tablelen;
if(ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT) {
/* Just like a normal eviction, implicit header must be
emitted twice. */
rv = emit_implicit(buf_ptr, buflen_ptr, offset_ptr, index);
if(rv != 0) {
return NULL;
}
ent->flags ^= NGHTTP2_HD_FLAG_IMPLICIT_EMIT;
}
if(ent->flags & NGHTTP2_HD_FLAG_REFSET) {
/* We need to drop entry from reference set. */
rv = emit_indexed_block(buf_ptr, buflen_ptr, offset_ptr, index);
if(rv != 0) {
return NULL;
}
ent->flags ^= NGHTTP2_HD_FLAG_REFSET;
}
/* Release memory. We don't remove entry from the header table
at this moment. */
if(ent->flags & NGHTTP2_HD_FLAG_NAME_ALLOC) {
free(ent->nv.name);
ent->nv.name = NULL;
ent->flags ^= NGHTTP2_HD_FLAG_NAME_ALLOC;
}
if(ent->flags & NGHTTP2_HD_FLAG_VALUE_ALLOC) {
free(ent->nv.value);
ent->nv.value = NULL;
ent->flags ^= NGHTTP2_HD_FLAG_VALUE_ALLOC;
}
}
new_ent = malloc(sizeof(nghttp2_hd_entry));
if(new_ent == NULL) {
return NULL;
}
if(context->role == NGHTTP2_HD_ROLE_DEFLATE &&
room > context->deflate_hd_table_bufsize_max) {
uint8_t flags = entry_flags &
~(NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_VALUE_ALLOC |
NGHTTP2_HD_FLAG_NAME_GIFT | NGHTTP2_HD_FLAG_VALUE_GIFT);
rv = nghttp2_hd_entry_init(new_ent, flags,
NULL, nv->namelen, NULL, nv->valuelen);
if(rv != 0) {
free(new_ent);
return NULL;
}
if(flags & NGHTTP2_HD_FLAG_NAME_GIFT) {
free(nv->name);
nv->name = NULL;
}
if(flags & NGHTTP2_HD_FLAG_VALUE_GIFT) {
free(nv->value);
nv->value = NULL;
}
/* caller must emit indexed repr to toggle off new_ent from
reference set. We cannot do it here because it may break the
indexing. */
} else {
rv = nghttp2_hd_entry_init(new_ent,
entry_flags,
rv = nghttp2_hd_entry_init(new_ent, entry_flags,
nv->name, nv->namelen, nv->value, nv->valuelen);
if(rv != 0) {
free(new_ent);
return NULL;
}
}
if(room > context->hd_table_bufsize_max) {
/* The entry taking more than NGHTTP2_HD_MAX_BUFFER_SIZE is
immediately evicted. */
@ -817,11 +765,8 @@ static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context,
} else {
context->hd_table_bufsize += room;
nghttp2_hd_ringbuf_push_front(&context->hd_table, new_ent);
if(room <= context->deflate_hd_table_bufsize_max) {
new_ent->flags |= NGHTTP2_HD_FLAG_REFSET;
context->deflate_hd_table_bufsize += room;
++context->deflate_hd_tablelen;
}
}
return new_ent;
}
@ -851,7 +796,7 @@ static search_result search_hd_table(nghttp2_hd_context *context,
uint32_t value_hash = hash(nv->value, nv->valuelen);
ssize_t left = -1, right = STATIC_TABLE_LENGTH;
for(i = 0; i < context->deflate_hd_tablelen; ++i) {
for(i = 0; i < context->hd_table.len; ++i) {
nghttp2_hd_entry *ent = nghttp2_hd_ringbuf_get(&context->hd_table, i);
if(ent->name_hash == name_hash && name_eq(&ent->nv, nv)) {
if(res.index == -1) {
@ -893,35 +838,59 @@ static search_result search_hd_table(nghttp2_hd_context *context,
return res;
}
int nghttp2_hd_change_table_size(nghttp2_hd_context *context,
size_t hd_table_bufsize_max)
static void hd_context_shrink_table_size(nghttp2_hd_context *context)
{
int rv;
rv = nghttp2_hd_ringbuf_reserve
(&context->hd_table, hd_table_bufsize_max / NGHTTP2_HD_ENTRY_OVERHEAD);
if(rv != 0) {
return rv;
}
context->hd_table_bufsize_max = hd_table_bufsize_max;
if(context->role == NGHTTP2_HD_ROLE_INFLATE) {
context->deflate_hd_table_bufsize_max = hd_table_bufsize_max;
}
while(context->hd_table_bufsize > context->hd_table_bufsize_max &&
context->hd_table.len > 0) {
size_t index = context->hd_table.len - 1;
nghttp2_hd_entry* ent = nghttp2_hd_ringbuf_get(&context->hd_table, index);
context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen);
if(context->hd_table_bufsize < context->deflate_hd_table_bufsize) {
context->deflate_hd_table_bufsize -= entry_room(ent->nv.namelen,
ent->nv.valuelen);
--context->deflate_hd_tablelen;
}
nghttp2_hd_ringbuf_pop_back(&context->hd_table);
if(--ent->ref == 0) {
nghttp2_hd_entry_free(ent);
free(ent);
}
}
}
int nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater,
size_t settings_hd_table_bufsize_max)
{
int rv;
size_t next_bufsize = nghttp2_min(settings_hd_table_bufsize_max,
deflater->deflate_hd_table_bufsize_max);
rv = nghttp2_hd_ringbuf_reserve
(&deflater->ctx.hd_table, next_bufsize / NGHTTP2_HD_ENTRY_OVERHEAD);
if(rv != 0) {
return rv;
}
deflater->ctx.hd_table_bufsize_max = settings_hd_table_bufsize_max;
if(settings_hd_table_bufsize_max >= deflater->deflate_hd_table_bufsize_max) {
/* On the next encoding, we sends encoding context update with
deflater->deflate_hd_table_bufsize_max if it is strictly
smaller than settings_hd_table_bufsize_max. */
return 0;
}
hd_context_shrink_table_size(&deflater->ctx);
return 0;
}
int nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater,
size_t settings_hd_table_bufsize_max)
{
int rv;
rv = nghttp2_hd_ringbuf_reserve
(&inflater->ctx.hd_table,
settings_hd_table_bufsize_max / NGHTTP2_HD_ENTRY_OVERHEAD);
if(rv != 0) {
return rv;
}
inflater->settings_hd_table_bufsize_max = settings_hd_table_bufsize_max;
inflater->ctx.hd_table_bufsize_max = settings_hd_table_bufsize_max;
hd_context_shrink_table_size(&inflater->ctx);
return 0;
}
@ -962,9 +931,8 @@ nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context,
static int hd_deflate_should_indexing(nghttp2_hd_deflater *deflater,
const nghttp2_nv *nv)
{
size_t table_size = nghttp2_min(deflater->ctx.deflate_hd_table_bufsize_max,
deflater->ctx.hd_table_bufsize_max);
if(entry_room(nv->namelen, nv->valuelen) > table_size * 3 / 4) {
if(entry_room(nv->namelen, nv->valuelen) >
deflater->ctx.hd_table_bufsize_max * 3 / 4) {
return 0;
}
#ifdef NGHTTP2_XHD
@ -1006,9 +974,9 @@ static int deflate_nv(nghttp2_hd_deflater *deflater,
nghttp2_hd_entry_free(new_ent);
free(new_ent);
new_ent = NULL;
} else if(new_ent->nv.name != NULL) {
/* new_ent->ref > 0 and nv.name is not NULL means that new_ent is
in the reference set and in deflate_hd_table_bufsize */
} 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(buf_ptr, buflen_ptr, offset_ptr, index);
@ -1080,20 +1048,18 @@ static int deflate_nv(nghttp2_hd_deflater *deflater,
if(new_ent->ref == 0) {
nghttp2_hd_entry_free(new_ent);
free(new_ent);
} else if(new_ent->nv.name != NULL) {
/* new_ent->ref > 0 and nv.name is not NULL means that new_ent is
in the reference set and in deflate_hd_table_bufsize */
} else {
/* new_ent->ref > 0 means that new_ent is in the reference
set. */
new_ent->flags |= NGHTTP2_HD_FLAG_EMIT;
}
incidx = 1;
}
if(index == -1) {
rv = emit_newname_block(buf_ptr, buflen_ptr, offset_ptr, nv, incidx,
deflater->ctx.side);
rv = emit_newname_block(buf_ptr, buflen_ptr, offset_ptr, nv, incidx);
} else {
rv = emit_indname_block(buf_ptr, buflen_ptr, offset_ptr, index,
nv->value, nv->valuelen, incidx,
deflater->ctx.side);
nv->value, nv->valuelen, incidx);
}
if(rv != 0) {
return rv;
@ -1135,8 +1101,20 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater,
return NGHTTP2_ERR_HEADER_COMP;
}
offset = nv_offset;
if(deflater->ctx.hd_table_bufsize_max >
deflater->deflate_hd_table_bufsize_max) {
rv = emit_table_size(buf_ptr, buflen_ptr, &offset,
deflater->deflate_hd_table_bufsize_max);
if(rv != 0) {
goto fail;
}
deflater->ctx.hd_table_bufsize_max =
deflater->deflate_hd_table_bufsize_max;
}
if(deflater->no_refset) {
rv = emit_indexed0(buf_ptr, buflen_ptr, &offset);
rv = emit_clear_refset(buf_ptr, buflen_ptr, &offset);
if(rv != 0) {
goto fail;
}
@ -1148,7 +1126,7 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater,
goto fail;
}
}
for(i = 0; i < deflater->ctx.deflate_hd_tablelen; ++i) {
for(i = 0; i < deflater->ctx.hd_table.len; ++i) {
nghttp2_hd_entry *ent = nghttp2_hd_ringbuf_get(&deflater->ctx.hd_table, i);
rv = deflate_post_process_hd_entry(ent, i, buf_ptr, buflen_ptr, &offset);
if(rv != 0) {
@ -1419,7 +1397,12 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
for(; in != last;) {
switch(inflater->state) {
case NGHTTP2_HD_STATE_OPCODE:
if(*in & 0x80u) {
if(*in == 0x80u) {
DEBUGF(fprintf(stderr, "Encoding context update\n"));
inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED;
inflater->state = NGHTTP2_HD_STATE_CONTEXT_UPDATE;
++in;
} else if(*in & 0x80u) {
DEBUGF(fprintf(stderr, "Indexed repr\n"));
inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED;
inflater->state = NGHTTP2_HD_STATE_READ_INDEX;
@ -1442,6 +1425,38 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
}
inflater->left = 0;
break;
case NGHTTP2_HD_STATE_CONTEXT_UPDATE:
if(*in & 0x80u) {
if(*in != 0x80u) {
rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
}
++in;
DEBUGF(fprintf(stderr, "Clearing reference set\n"));
clear_refset(&inflater->ctx);
inflater->state = NGHTTP2_HD_STATE_OPCODE;
break;
}
/* Header table size change */
DEBUGF(fprintf(stderr, "Header table size change\n"));
inflater->state = NGHTTP2_HD_STATE_READ_TABLE_SIZE;
break;
case NGHTTP2_HD_STATE_READ_TABLE_SIZE:
rfin = 0;
rv = hd_inflate_read_len(inflater, &rfin, in, last, 7,
inflater->settings_hd_table_bufsize_max);
if(rv < 0) {
goto fail;
}
in += rv;
if(!rfin) {
return in - first;
}
DEBUGF(fprintf(stderr, "table_size=%zd\n", inflater->left));
inflater->ctx.hd_table_bufsize_max = inflater->left;
hd_context_shrink_table_size(&inflater->ctx);
inflater->state = NGHTTP2_HD_STATE_OPCODE;
break;
case NGHTTP2_HD_STATE_READ_INDEX:
rfin = 0;
rv = hd_inflate_read_len(inflater, &rfin, in, last,
@ -1458,12 +1473,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
DEBUGF(fprintf(stderr, "index=%zd\n", inflater->left));
if(inflater->opcode == NGHTTP2_HD_OPCODE_INDEXED) {
inflater->index = inflater->left;
if(inflater->index == 0) {
DEBUGF(fprintf(stderr, "Clearing reference set\n"));
clear_refset(&inflater->ctx);
inflater->state = NGHTTP2_HD_STATE_OPCODE;
break;
}
assert(inflater->index > 0);
--inflater->index;
rv = hd_inflate_commit_indexed(inflater, nv_out);
if(rv < 0) {
@ -1505,8 +1515,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
}
rv = 0;
if(inflater->huffman_encoded) {
nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx,
inflater->ctx.side);
nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx);
rv = nghttp2_buffer_reserve(&inflater->namebuf,
guess_huff_decode_len(inflater->left));
if(rv != 0) {
@ -1580,8 +1589,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
return in - first;
}
if(inflater->huffman_encoded) {
nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx,
inflater->ctx.side);
nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx);
rv = nghttp2_buffer_reserve(&inflater->valuebuf,
guess_huff_decode_len(inflater->left));
inflater->state = NGHTTP2_HD_STATE_READ_VALUEHUFF;
@ -1679,19 +1687,21 @@ int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater)
int nghttp2_hd_emit_indname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
size_t *offset_ptr, size_t index,
const uint8_t *value, size_t valuelen,
int inc_indexing,
nghttp2_hd_side side)
int inc_indexing)
{
return emit_indname_block(buf_ptr, buflen_ptr, offset_ptr,
index, value, valuelen, inc_indexing,
side);
index, value, valuelen, inc_indexing);
}
int nghttp2_hd_emit_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
size_t *offset_ptr, nghttp2_nv *nv,
int inc_indexing,
nghttp2_hd_side side)
int inc_indexing)
{
return emit_newname_block(buf_ptr, buflen_ptr, offset_ptr, nv, inc_indexing,
side);
return emit_newname_block(buf_ptr, buflen_ptr, offset_ptr, nv, inc_indexing);
}
int nghttp2_hd_emit_table_size(uint8_t **buf_ptr, size_t *buflen_ptr,
size_t *offset_ptr, size_t table_size)
{
return emit_table_size(buf_ptr, buflen_ptr, offset_ptr, table_size);
}

View File

@ -22,8 +22,8 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef NGHTTP2_HD_COMP_H
#define NGHTTP2_HD_COMP_H
#ifndef NGHTTP2_HD_H
#define NGHTTP2_HD_H
#ifdef HAVE_CONFIG_H
# include <config.h>
@ -48,11 +48,6 @@
encoder only uses the memory up to this value. */
#define NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE (1 << 12)
typedef enum {
NGHTTP2_HD_SIDE_REQUEST = 0,
NGHTTP2_HD_SIDE_RESPONSE = 1
} nghttp2_hd_side;
typedef enum {
NGHTTP2_HD_ROLE_DEFLATE,
NGHTTP2_HD_ROLE_INFLATE
@ -108,6 +103,8 @@ typedef enum {
typedef enum {
NGHTTP2_HD_STATE_OPCODE,
NGHTTP2_HD_STATE_CONTEXT_UPDATE,
NGHTTP2_HD_STATE_READ_TABLE_SIZE,
NGHTTP2_HD_STATE_READ_INDEX,
NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN,
NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN,
@ -126,30 +123,10 @@ typedef struct {
is the sum of length of name/value in hd_table +
NGHTTP2_HD_ENTRY_OVERHEAD bytes overhead per each entry. */
size_t hd_table_bufsize;
/* The header table size for decoding. If the context is initialized
as encoder, this value is advertised by remote endpoint
decoder. */
/* The effective header table size. */
size_t hd_table_bufsize_max;
/* The current effective header table size for encoding. This value
is always equal to |hd_table_bufsize| on decoder
context. |deflate_hd_table_bufsize| <= |hd_table_bufsize| must be
hold. */
size_t deflate_hd_table_bufsize;
/* The maximum effective header table for encoding. Although header
table size is bounded by |hd_table_bufsize_max|, the encoder can
use smaller buffer by not retaining the header name/values beyond
the |deflate_hd_table_bufsize_max| and not referencing those
entries. This value is always equal to |hd_table_bufsize_max| on
decoder context. */
size_t deflate_hd_table_bufsize_max;
/* The number of effective entry in |hd_table|. This value is always
equal to hd_table.len on decoder side. */
size_t deflate_hd_tablelen;
/* Role of this context; deflate or infalte */
nghttp2_hd_role role;
/* NGHTTP2_HD_SIDE_REQUEST for processing request, otherwise
response. */
nghttp2_hd_side side;
/* If inflate/deflate error occurred, this value is set to 1 and
further invocation of inflate/deflate will fail with
NGHTTP2_ERR_HEADER_COMP. */
@ -158,6 +135,8 @@ typedef struct {
typedef struct {
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;
@ -189,6 +168,9 @@ typedef struct {
/* The index of header table to toggle off the entry from reference
set at the end of decompression. */
size_t end_headers_index;
/* The maximum header table size the inflater supports. This is the
same value transmitted in SETTINGS_HEADER_TABLE_SIZE */
size_t settings_hd_table_bufsize_max;
nghttp2_hd_opcode opcode;
nghttp2_hd_inflate_state state;
/* nonzero if string is huffman encoded */
@ -230,8 +212,7 @@ void nghttp2_hd_entry_free(nghttp2_hd_entry *ent);
* NGHTTP2_ERR_NOMEM
* Out of memory.
*/
int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater,
nghttp2_hd_side side);
int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater);
/*
* Initializes |deflater| for deflating name/values pairs.
@ -247,7 +228,6 @@ int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater,
* Out of memory.
*/
int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater,
nghttp2_hd_side side,
size_t deflate_hd_table_bufsize_max);
/*
@ -259,8 +239,7 @@ int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater,
* NGHTTP2_ERR_NOMEM
* Out of memory.
*/
int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater,
nghttp2_hd_side side);
int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater);
/*
* Deallocates any resources allocated for |deflater|.
@ -282,10 +261,11 @@ void nghttp2_hd_deflate_set_no_refset(nghttp2_hd_deflater *deflater,
uint8_t no_refset);
/*
* Changes header table size in |context|. This may trigger eviction
* in the dynamic table.
* Changes header table size of the |deflater|. This may trigger
* eviction in the dynamic table.
*
* This function can be used for deflater and inflater.
* The |settings_hd_table_bufsize_max| should be the value received in
* SETTINGS_HEADER_TABLE_SIZE.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
@ -293,8 +273,25 @@ void nghttp2_hd_deflate_set_no_refset(nghttp2_hd_deflater *deflater,
* NGHTTP2_ERR_NOMEM
* Out of memory.
*/
int nghttp2_hd_change_table_size(nghttp2_hd_context *context,
size_t hd_table_bufsize_max);
int nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater,
size_t settings_hd_table_bufsize_max);
/*
* Changes header table size in the |inflater|. This may trigger
* eviction in the dynamic table.
*
* The |settings_hd_table_bufsize_max| should be the value transmitted
* in SETTINGS_HEADER_TABLE_SIZE.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory.
*/
int nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater,
size_t settings_hd_table_bufsize_max);
/*
* Deflates the |nva|, which has the |nvlen| name/value pairs, into
@ -379,14 +376,16 @@ int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater);
int nghttp2_hd_emit_indname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
size_t *offset_ptr, size_t index,
const uint8_t *value, size_t valuelen,
int inc_indexing,
nghttp2_hd_side side);
int inc_indexing);
/* For unittesting purpose */
int nghttp2_hd_emit_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
size_t *offset_ptr, nghttp2_nv *nv,
int inc_indexing,
nghttp2_hd_side side);
int inc_indexing);
/* For unittesting purpose */
int nghttp2_hd_emit_table_size(uint8_t **buf_ptr, size_t *buflen_ptr,
size_t *offset_ptr, size_t table_size);
/* For unittesting purpose */
nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context,
@ -395,37 +394,30 @@ nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context,
/* Huffman encoding/decoding functions */
/*
* Counts the required bytes to encode |src| with length |len|. If
* |side| is NGHTTP2_HD_SIDE_REQUEST, the request huffman code table
* is used. Otherwise, the response code table is used.
* Counts the required bytes to encode |src| with length |len|.
*
* This function returns the number of required bytes to encode given
* data, including padding of prefix of terminal symbol code. This
* function always succeeds.
*/
size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len,
nghttp2_hd_side side);
size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len);
/*
* Encodes the given data |src| with length |srclen| to the given
* memory location pointed by |dest|, allocated at lest |destlen|
* bytes. The caller is responsible to specify |destlen| at least the
* length that nghttp2_hd_huff_encode_count() returns. If |side| is
* NGHTTP2_HD_SIDE_REQUEST, the request huffman code table is
* used. Otherwise, the response code table is used.
* length that nghttp2_hd_huff_encode_count() returns.
*
* This function returns the number of written bytes, including
* padding of prefix of terminal symbol code. This return value is
* exactly the same with the return value of
* nghttp2_hd_huff_encode_count() if it is given with the same |src|,
* |srclen|, and |side|. This function always succeeds.
* nghttp2_hd_huff_encode_count() if it is given with the same |src|
* and |srclen|. This function always succeeds.
*/
ssize_t nghttp2_hd_huff_encode(uint8_t *dest, size_t destlen,
const uint8_t *src, size_t srclen,
nghttp2_hd_side side);
const uint8_t *src, size_t srclen);
void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx,
nghttp2_hd_side side);
void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx);
/*
* Decodes the given data |src| with length |srclen|. The |ctx| must
@ -453,4 +445,4 @@ ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
nghttp2_buffer *dest,
const uint8_t *src, size_t srclen, int final);
#endif /* NGHTTP2_HD_COMP_H */
#endif /* NGHTTP2_HD_H */

View File

@ -30,11 +30,8 @@
#include "nghttp2_hd.h"
extern const nghttp2_huff_sym req_huff_sym_table[];
extern const nghttp2_huff_decode req_huff_decode_table[][16];
extern const nghttp2_huff_sym res_huff_sym_table[];
extern const nghttp2_huff_decode res_huff_decode_table[][16];
extern const nghttp2_huff_sym huff_sym_table[];
extern const nghttp2_huff_decode huff_decode_table[][16];
/*
* Encodes huffman code |sym| into |*dest_ptr|, whose least |rembits|
@ -65,18 +62,11 @@ static size_t huff_encode_sym(uint8_t **dest_ptr, size_t rembits,
return rembits;
}
size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len,
nghttp2_hd_side side)
size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len)
{
size_t i;
size_t nbits = 0;
const nghttp2_huff_sym *huff_sym_table;
if(side == NGHTTP2_HD_SIDE_REQUEST) {
huff_sym_table = req_huff_sym_table;
} else {
huff_sym_table = res_huff_sym_table;
}
for(i = 0; i < len; ++i) {
nbits += huff_sym_table[src[i]].nbits;
}
@ -85,19 +75,12 @@ size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len,
}
ssize_t nghttp2_hd_huff_encode(uint8_t *dest, size_t destlen,
const uint8_t *src, size_t srclen,
nghttp2_hd_side side)
const uint8_t *src, size_t srclen)
{
int rembits = 8;
uint8_t *dest_first = dest;
size_t i;
const nghttp2_huff_sym *huff_sym_table;
if(side == NGHTTP2_HD_SIDE_REQUEST) {
huff_sym_table = req_huff_sym_table;
} else {
huff_sym_table = res_huff_sym_table;
}
for(i = 0; i < srclen; ++i) {
const nghttp2_huff_sym *sym = &huff_sym_table[src[i]];
if(rembits == 8) {
@ -114,14 +97,8 @@ ssize_t nghttp2_hd_huff_encode(uint8_t *dest, size_t destlen,
return dest - dest_first;
}
void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx,
nghttp2_hd_side side)
void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx)
{
if(side == NGHTTP2_HD_SIDE_REQUEST) {
ctx->huff_decode_table = req_huff_decode_table;
} else {
ctx->huff_decode_table = res_huff_decode_table;
}
ctx->state = 0;
ctx->accept = 1;
}
@ -137,7 +114,7 @@ ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
for(i = 0; i < srclen; ++i) {
uint8_t in = src[i] >> 4;
for(j = 0; j < 2; ++j) {
const nghttp2_huff_decode *t = &ctx->huff_decode_table[ctx->state][in];
const nghttp2_huff_decode *t = &huff_decode_table[ctx->state][in];
if(t->state == -1) {
return NGHTTP2_ERR_HEADER_COMP;
}

View File

@ -52,7 +52,6 @@ typedef struct {
typedef nghttp2_huff_decode huff_decode_table_type[16];
typedef struct {
const huff_decode_table_type *huff_decode_table;
/* Current huffman decoding state. We stripped leaf nodes, so the
value range is [0..255], inclusive. */
uint8_t state;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -86,6 +86,9 @@ typedef enum {
NGHTTP2_IB_READ_GOAWAY_DEBUG,
NGHTTP2_IB_EXPECT_CONTINUATION,
NGHTTP2_IB_IGN_CONTINUATION,
NGHTTP2_IB_READ_PAD_CONTINUATION,
NGHTTP2_IB_IGN_PAD_CONTINUATION,
NGHTTP2_IB_READ_PAD_DATA,
NGHTTP2_IB_READ_DATA,
NGHTTP2_IB_IGN_DATA
} nghttp2_inbound_state;
@ -102,9 +105,9 @@ typedef struct {
size_t left;
/* How many bytes we still need to receive for current frame */
size_t payloadleft;
/* padding length for the current frame */
size_t padlen;
nghttp2_inbound_state state;
/* TODO, remove this. Error code */
int error_code;
uint8_t buf[8];
/* How many bytes have been written to |buf| */
uint8_t buflen;
@ -189,14 +192,6 @@ struct nghttp2_session {
/* Flags indicating GOAWAY is sent and/or recieved. The flags are
composed by bitwise OR-ing nghttp2_goaway_flag. */
uint8_t goaway_flags;
/* Non-zero indicates connection-level flow control on remote side
is in effect. This will be disabled when WINDOW_UPDATE with
END_FLOW_CONTROL bit set is received. */
uint8_t remote_flow_control;
/* Non-zero indicates connection-level flow control on local side is
in effect. This will be disabled when WINDOW_UPDATE with
END_FLOW_CONTROL bit set is sent. */
uint8_t local_flow_control;
};
/* Struct used when updating initial window size of each active
@ -516,9 +511,11 @@ nghttp2_stream* nghttp2_session_get_stream(nghttp2_session *session,
* Packs DATA frame |frame| in wire frame format and stores it in
* |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr|
* length. This function expands |*buf_ptr| as necessary to store
* given |frame|. It packs header in first 8 bytes. Remaining bytes
* are the DATA apyload and are filled using |frame->data_prd|. The
* length of payload is at most |datamax| bytes.
* given |frame|. It packs header in first 8 bytes starting
* |*bufoff_ptr| offset. The |*bufoff_ptr| is calculated based on
* usage of padding. Remaining bytes are the DATA apyload and are
* filled using |frame->data_prd|. The length of payload is at most
* |datamax| bytes.
*
* This function returns the size of packed frame if it succeeds, or
* one of the following negative error codes:
@ -534,6 +531,7 @@ nghttp2_stream* nghttp2_session_get_stream(nghttp2_session *session,
*/
ssize_t nghttp2_session_pack_data(nghttp2_session *session,
uint8_t **buf_ptr, size_t *buflen_ptr,
size_t *bufoff_ptr,
size_t datamax,
nghttp2_private_data *frame);

View File

@ -29,8 +29,6 @@
void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
uint8_t flags, int32_t pri,
nghttp2_stream_state initial_state,
uint8_t remote_flow_control,
uint8_t local_flow_control,
int32_t remote_initial_window_size,
int32_t local_initial_window_size,
void *stream_user_data)
@ -44,8 +42,6 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
stream->stream_user_data = stream_user_data;
stream->deferred_data = NULL;
stream->deferred_flags = NGHTTP2_DEFERRED_NONE;
stream->remote_flow_control = remote_flow_control;
stream->local_flow_control = local_flow_control;
stream->remote_window_size = remote_initial_window_size;
stream->local_window_size = local_initial_window_size;
stream->recv_window_size = 0;

View File

@ -117,25 +117,11 @@ typedef struct {
/* The flags for defered DATA. Bitwise OR of zero or more
nghttp2_deferred_flag values */
uint8_t deferred_flags;
/* Flag to indicate whether the remote side has flow control
enabled. If it is enabled, we have to enforces flow control to
send data to the other side. This could be disabled when
receiving SETTINGS with flow control options off or receiving
WINDOW_UPDATE with END_FLOW_CONTROL bit set. */
uint8_t remote_flow_control;
/* Flag to indicate whether the local side has flow control
enabled. If it is enabled, the received data are subject to the
flow control. This could be disabled by sending SETTINGS with
flow control options off or sending WINDOW_UPDATE with
END_FLOW_CONTROL bit set. */
uint8_t local_flow_control;
} nghttp2_stream;
void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
uint8_t flags, int32_t pri,
nghttp2_stream_state initial_state,
uint8_t remote_flow_control,
uint8_t local_flow_control,
int32_t remote_initial_window_size,
int32_t local_initial_window_size,
void *stream_user_data);

View File

@ -75,8 +75,9 @@ static int nghttp2_submit_headers_shared
rv = NGHTTP2_ERR_NOMEM;
goto fail;
}
/* TODO Implement header continuation */
flags_copy = (flags & (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PRIORITY)) |
flags_copy =
(flags & (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PRIORITY |
NGHTTP2_FLAG_END_SEGMENT)) |
NGHTTP2_FLAG_END_HEADERS;
nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id, pri,
@ -196,7 +197,6 @@ int nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,
free(frame);
return rv;
}
/* TODO Implement header continuation */
flags_copy = NGHTTP2_FLAG_END_PUSH_PROMISE;
nghttp2_frame_push_promise_init(&frame->push_promise, flags_copy,
stream_id, -1, nva_copy, nvlen);
@ -219,9 +219,6 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
}
flags = 0;
if(stream_id == 0) {
if(!session->local_flow_control) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
rv = nghttp2_adjust_local_window_size(&session->local_window_size,
&session->recv_window_size,
&session->recv_reduction,
@ -232,9 +229,6 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
} else {
stream = nghttp2_session_get_stream(session, stream_id);
if(stream) {
if(!stream->local_flow_control) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
rv = nghttp2_adjust_local_window_size(&stream->local_window_size,
&stream->recv_window_size,
&stream->recv_reduction,
@ -325,14 +319,13 @@ ssize_t nghttp2_pack_settings_payload(uint8_t *buf,
const nghttp2_settings_entry *iv,
size_t niv)
{
/* Assume that current flow_control_option is 0 (which means that
flow control is enabled) */
if(!nghttp2_iv_check(iv, niv, 0)) {
if(!nghttp2_iv_check(iv, niv)) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
if(buflen < (niv * 8))
if(buflen < (niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH)) {
return NGHTTP2_ERR_INSUFF_BUFSIZE;
}
return nghttp2_frame_pack_settings_payload(buf, iv, niv);
}

View File

@ -45,10 +45,6 @@ cdef extern from 'nghttp2_hd.h':
# This is macro
int NGHTTP2_HD_ENTRY_OVERHEAD
ctypedef enum nghttp2_hd_side:
NGHTTP2_HD_SIDE_REQUEST
NGHTTP2_HD_SIDE_RESPONSE
ctypedef enum nghttp2_hd_flags:
NGHTTP2_HD_FLAG_REFSET
@ -73,12 +69,9 @@ cdef extern from 'nghttp2_hd.h':
nghttp2_hd_context ctx
int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater,
nghttp2_hd_side side,
size_t deflate_hd_table_bufsize_max)
int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater,
nghttp2_hd_side side)
int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater)
void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater)
@ -87,7 +80,10 @@ cdef extern from 'nghttp2_hd.h':
void nghttp2_hd_deflate_set_no_refset(nghttp2_hd_deflater *deflater,
uint8_t no_refset)
int nghttp2_hd_change_table_size(nghttp2_hd_context *context,
int nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater,
size_t hd_table_bufsize_max)
int nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater,
size_t hd_table_bufsize_max)
ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater,

View File

@ -13,12 +13,7 @@ from binascii import a2b_hex
import nghttp2
def testsuite(testdata):
if testdata['context'] == 'request':
side = nghttp2.HD_SIDE_REQUEST
else:
side = nghttp2.HD_SIDE_RESPONSE
inflater = nghttp2.HDInflater(side)
inflater = nghttp2.HDInflater()
for casenum, item in enumerate(testdata['cases']):
if 'header_table_size' in item:
@ -47,7 +42,7 @@ def testsuite(testdata):
if __name__ == '__main__':
for filename in sys.argv[1:]:
sys.stderr.write('{}\n'.format(filename))
sys.stderr.write('{}: '.format(filename))
with open(filename) as f:
input = f.read()
testsuite(json.loads(input))

View File

@ -14,11 +14,6 @@ from binascii import b2a_hex
import nghttp2
def testsuite(testdata, filename, outdir, table_size, deflate_table_size):
if testdata['context'] == 'request':
side = nghttp2.HD_SIDE_REQUEST
else:
side = nghttp2.HD_SIDE_RESPONSE
res = {
'draft':5, 'context': testdata['context'],
'description': '''\
@ -29,7 +24,7 @@ original. We make some headers not indexing at all, but this does not always \
result in less bits on the wire.'''
}
cases = []
deflater = nghttp2.HDDeflater(side, deflate_table_size)
deflater = nghttp2.HDDeflater(deflate_table_size)
deflater.change_table_size(table_size)
for casenum, item in enumerate(testdata['cases']):
outitem = {

View File

@ -26,9 +26,6 @@ from libc.stdlib cimport malloc, free
from libc.string cimport memcpy, memset
from libc.stdint cimport uint8_t, uint16_t, uint32_t, int32_t
HD_SIDE_REQUEST = cnghttp2.NGHTTP2_HD_SIDE_REQUEST
HD_SIDE_RESPONSE = cnghttp2.NGHTTP2_HD_SIDE_RESPONSE
HD_DEFLATE_HD_TABLE_BUFSIZE_MAX = 4096
HD_ENTRY_OVERHEAD = cnghttp2.NGHTTP2_HD_ENTRY_OVERHEAD
@ -45,12 +42,6 @@ class HDTableEntry:
def space(self):
return self.namelen + self.valuelen + HD_ENTRY_OVERHEAD
cdef _change_table_size(cnghttp2.nghttp2_hd_context *ctx, hd_table_bufsize_max):
cdef int rv
rv = cnghttp2.nghttp2_hd_change_table_size(ctx, hd_table_bufsize_max)
if rv != 0:
raise Exception(_strerror(rv))
cdef _get_hd_table(cnghttp2.nghttp2_hd_context *ctx):
cdef int length = ctx.hd_table.len
cdef cnghttp2.nghttp2_hd_entry *entry
@ -65,35 +56,25 @@ cdef _get_hd_table(cnghttp2.nghttp2_hd_context *ctx):
return res
cdef _get_pybytes(uint8_t *b, uint16_t blen):
# While the |blen| is positive, the |b| could be NULL. This is
# because deflater may deallocate the byte strings its local table
# space.
if b == NULL and blen > 0:
val = None
else:
val = b[:blen]
return val
return b[:blen]
cdef class HDDeflater:
'''Performs header compression. The header compression algorithm has
to know the header set to be compressed is request headers or
response headers. It is indicated by |side| parameter in the
constructor. The constructor also takes |hd_table_bufsize_max|
parameter, which limits the usage of header table in the given
amount of bytes. This is necessary because the header compressor
and decompressor has to share the same amount of header table and
the decompressor decides that number. The compressor may not want
to use all header table size because of limited memory
availability. In that case, the |hd_table_bufsize_max| can be used
to cap the upper limit of talbe size whatever the header table
size is chosen. The default value of |hd_table_bufsize_max| is
4096 bytes.
'''Performs header compression. The constructor takes
|hd_table_bufsize_max| parameter, which limits the usage of header
table in the given amount of bytes. This is necessary because the
header compressor and decompressor has to share the same amount of
header table and the decompressor decides that number. The
compressor may not want to use all header table size because of
limited memory availability. In that case, the
|hd_table_bufsize_max| can be used to cap the upper limit of table
size whatever the header table size is chosen by the decompressor.
The default value of |hd_table_bufsize_max| is 4096 bytes.
The following example shows how to compress request header sets:
import binascii, nghttp2
deflater = nghttp2.HDDeflater(nghttp2.HD_SIDE_REQUEST)
deflater = nghttp2.HDDeflater()
res = deflater.deflate([(b'foo', b'bar'),
(b'baz', b'buz')])
print(binascii.b2a_hex(res))
@ -102,17 +83,13 @@ cdef class HDDeflater:
cdef cnghttp2.nghttp2_hd_deflater _deflater
def __cinit__(self, side,
def __cinit__(self,
hd_table_bufsize_max = HD_DEFLATE_HD_TABLE_BUFSIZE_MAX):
rv = cnghttp2.nghttp2_hd_deflate_init2(&self._deflater, side,
rv = cnghttp2.nghttp2_hd_deflate_init2(&self._deflater,
hd_table_bufsize_max)
if rv != 0:
raise Exception(_strerror(rv))
def __init__(self, side,
hd_table_bufsize_max = HD_DEFLATE_HD_TABLE_BUFSIZE_MAX):
super(HDDeflater, self).__init__()
def __dealloc__(self):
cnghttp2.nghttp2_hd_deflate_free(&self._deflater)
@ -165,7 +142,11 @@ cdef class HDDeflater:
An exception will be raised on error.
'''
_change_table_size(&self._deflater.ctx, hd_table_bufsize_max)
cdef int rv
rv = cnghttp2.nghttp2_hd_deflate_change_table_size(&self._deflater,
hd_table_bufsize_max)
if rv != 0:
raise Exception(_strerror(rv))
def get_hd_table(self,):
'''Returns copy of current dynamic header table.'''
@ -177,7 +158,7 @@ cdef class HDInflater:
The following example shows how to compress request header sets:
data = b'0082c5ad82bd0f000362617a0362757a'
inflater = nghttp2.HDInflater(nghttp2.HD_SIDE_REQUEST)
inflater = nghttp2.HDInflater()
hdrs = inflater.inflate(data)
print(hdrs)
@ -185,14 +166,11 @@ cdef class HDInflater:
cdef cnghttp2.nghttp2_hd_inflater _inflater
def __cinit__(self, side):
rv = cnghttp2.nghttp2_hd_inflate_init(&self._inflater, side)
def __cinit__(self):
rv = cnghttp2.nghttp2_hd_inflate_init(&self._inflater)
if rv != 0:
raise Exception(_strerror(rv))
def __init__(self, side):
super(HDInflater, self).__init__()
def __dealloc__(self):
cnghttp2.nghttp2_hd_inflate_free(&self._inflater)
@ -231,7 +209,11 @@ cdef class HDInflater:
An exception will be raised on error.
'''
_change_table_size(&self._inflater.ctx, hd_table_bufsize_max)
cdef int rv
rv = cnghttp2.nghttp2_hd_inflate_change_table_size(&self._inflater,
hd_table_bufsize_max)
if rv != 0:
raise Exception(_strerror(rv))
def get_hd_table(self):
'''Returns copy of current dynamic header table.'''
@ -255,5 +237,5 @@ def print_hd_table(hdtable):
print('[{}] (s={}) (r={}) {}: {}'\
.format(idx, entry.space(),
'y' if entry.ref else 'n',
'**DEALLOCATED**' if entry.name is None else entry.name.decode('utf-8'),
'**DEALLOCATED**' if entry.value is None else entry.value.decode('utf-8')))
entry.name.decode('utf-8'),
entry.value.decode('utf-8')))

View File

@ -66,13 +66,13 @@ const std::string NGHTTPD_SERVER = "nghttpd nghttp2/" NGHTTP2_VERSION;
Config::Config()
: data_ptr(nullptr),
output_upper_thres(1024*1024),
padding(0),
header_table_size(-1),
port(0),
verbose(false),
daemon(false),
verify_client(false),
no_tls(false),
no_flow_control(false)
no_tls(false)
{}
Request::Request(int32_t stream_id)
@ -373,11 +373,6 @@ int Http2Handler::on_connect()
entry[0].value = 100;
entry[1].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
entry[1].value = 0;
if(sessions_->get_config()->no_flow_control) {
entry[niv].settings_id = NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS;
entry[niv].value = 1;
++niv;
}
if(sessions_->get_config()->header_table_size >= 0) {
entry[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
entry[niv].value = sessions_->get_config()->header_table_size;
@ -930,6 +925,16 @@ int hd_on_frame_send_callback
}
} // namespace
namespace {
ssize_t select_padding_callback
(nghttp2_session *session, const nghttp2_frame *frame, size_t max_payload,
void *user_data)
{
auto hd = static_cast<Http2Handler*>(user_data);
return std::min(max_payload, frame->hd.length + hd->get_config()->padding);
}
} // namespace
namespace {
int on_data_chunk_recv_callback
(nghttp2_session *session, uint8_t flags, int32_t stream_id,
@ -976,6 +981,9 @@ void fill_callback(nghttp2_session_callbacks& callbacks, const Config *config)
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
callbacks.on_header_callback = on_header_callback;
callbacks.on_begin_headers_callback = on_begin_headers_callback;
if(config->padding) {
callbacks.select_padding_callback = select_padding_callback;
}
}
} // namespace

View File

@ -56,13 +56,13 @@ struct Config {
std::string cert_file;
void *data_ptr;
size_t output_upper_thres;
size_t padding;
ssize_t header_table_size;
uint16_t port;
bool verbose;
bool daemon;
bool verify_client;
bool no_tls;
bool no_flow_control;
Config();
};

View File

@ -71,6 +71,7 @@ nghttpd_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttpd.cc \
NGHTTPX_SRCS = \
util.cc util.h http2.cc http2.h timegm.c timegm.h base64.h \
app_helper.cc app_helper.h \
shrpx_config.cc shrpx_config.h \
shrpx_error.h \
shrpx_listen_handler.cc shrpx_listen_handler.h \

View File

@ -77,6 +77,8 @@ const char* strstatus(nghttp2_error_code error_code)
return "CONNECT_ERROR";
case NGHTTP2_ENHANCE_YOUR_CALM:
return "ENHANCE_YOUR_CALM";
case NGHTTP2_INADEQUATE_SECURITY:
return "INADEQUATE_SECURITY";
default:
return "UNKNOWN";
}
@ -95,8 +97,6 @@ const char* strsettingsid(int32_t id)
return "SETTINGS_MAX_CONCURRENT_STREAMS";
case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
return "SETTINGS_INITIAL_WINDOW_SIZE";
case NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS:
return "SETTINGS_FLOW_CONTROL_OPTIONS";
default:
return "UNKNOWN";
}
@ -131,13 +131,6 @@ const char* strframetype(uint8_t type)
};
} // namespace
namespace {
void print_frame_attr_indent()
{
printf(" ");
}
} // namespace
namespace {
bool color_output = false;
} // namespace
@ -147,6 +140,22 @@ void set_color_output(bool f)
color_output = f;
}
namespace {
FILE *outfile = stdout;
} // namespace
void set_output(FILE *file)
{
outfile = file;
}
namespace {
void print_frame_attr_indent()
{
fprintf(outfile, " ");
}
} // namespace
namespace {
const char* ansi_esc(const char *code)
{
@ -168,11 +177,11 @@ void print_nv(nghttp2_nv *nva, size_t nvlen, bool indent = true)
if(indent) {
print_frame_attr_indent();
}
printf("%s", ansi_esc("\033[1;34m"));
fwrite(nv.name, nv.namelen, 1, stdout);
printf("%s: ", ansi_escend());
fwrite(nv.value, nv.valuelen, 1, stdout);
printf("\n");
fprintf(outfile, "%s", ansi_esc("\033[1;34m"));
fwrite(nv.name, nv.namelen, 1, outfile);
fprintf(outfile, "%s: ", ansi_escend());
fwrite(nv.value, nv.valuelen, 1, outfile);
fprintf(outfile, "\n");
}
}
} // namelen
@ -180,7 +189,7 @@ void print_nv(nghttp2_nv *nva, size_t nvlen, bool indent = true)
void print_timer()
{
auto millis = get_timer();
printf("%s[%3ld.%03ld]%s",
fprintf(outfile, "%s[%3ld.%03ld]%s",
ansi_esc("\033[33m"),
(long int)(millis.count()/1000), (long int)(millis.count()%1000),
ansi_escend());
@ -189,7 +198,7 @@ void print_timer()
namespace {
void print_frame_hd(const nghttp2_frame_hd& hd)
{
printf("<length=%zu, flags=0x%02x, stream_id=%d>\n",
fprintf(outfile, "<length=%zu, flags=0x%02x, stream_id=%d>\n",
hd.length, hd.flags, hd.stream_id);
}
} // namespace
@ -203,11 +212,35 @@ void print_flags(const nghttp2_frame_hd& hd)
if(hd.flags & NGHTTP2_FLAG_END_STREAM) {
s += "END_STREAM";
}
if(hd.flags & NGHTTP2_FLAG_END_SEGMENT) {
if(!s.empty()) {
s += " | ";
}
s += "END_SEGMENT";
}
if(hd.flags & NGHTTP2_FLAG_PAD_LOW) {
if(!s.empty()) {
s += " | ";
}
s += "PAD_LOW";
}
if(hd.flags & NGHTTP2_FLAG_PAD_HIGH) {
if(!s.empty()) {
s += " | ";
}
s += "PAD_HIGH";
}
break;
case NGHTTP2_HEADERS:
if(hd.flags & NGHTTP2_FLAG_END_STREAM) {
s += "END_STREAM";
}
if(hd.flags & NGHTTP2_FLAG_END_SEGMENT) {
if(!s.empty()) {
s += " | ";
}
s += "END_SEGMENT";
}
if(hd.flags & NGHTTP2_FLAG_END_HEADERS) {
if(!s.empty()) {
s += " | ";
@ -220,6 +253,18 @@ void print_flags(const nghttp2_frame_hd& hd)
}
s += "PRIORITY";
}
if(hd.flags & NGHTTP2_FLAG_PAD_LOW) {
if(!s.empty()) {
s += " | ";
}
s += "PAD_LOW";
}
if(hd.flags & NGHTTP2_FLAG_PAD_HIGH) {
if(!s.empty()) {
s += " | ";
}
s += "PAD_HIGH";
}
break;
case NGHTTP2_SETTINGS:
if(hd.flags & NGHTTP2_FLAG_ACK) {
@ -230,6 +275,18 @@ void print_flags(const nghttp2_frame_hd& hd)
if(hd.flags & NGHTTP2_FLAG_END_PUSH_PROMISE) {
s += "END_PUSH_PROMISE";
}
if(hd.flags & NGHTTP2_FLAG_PAD_LOW) {
if(!s.empty()) {
s += " | ";
}
s += "PAD_LOW";
}
if(hd.flags & NGHTTP2_FLAG_PAD_HIGH) {
if(!s.empty()) {
s += " | ";
}
s += "PAD_HIGH";
}
break;
case NGHTTP2_PING:
if(hd.flags & NGHTTP2_FLAG_ACK) {
@ -237,7 +294,7 @@ void print_flags(const nghttp2_frame_hd& hd)
}
break;
}
printf("; %s\n", s.c_str());
fprintf(outfile, "; %s\n", s.c_str());
}
} // namespace
@ -256,7 +313,7 @@ const char* frame_name_ansi_esc(print_type ptype)
namespace {
void print_frame(print_type ptype, const nghttp2_frame *frame)
{
printf("%s%s%s frame ",
fprintf(outfile, "%s%s%s frame ",
frame_name_ansi_esc(ptype),
strframetype(frame->hd.type),
ansi_escend());
@ -267,24 +324,30 @@ void print_frame(print_type ptype, const nghttp2_frame *frame)
}
switch(frame->hd.type) {
case NGHTTP2_DATA:
if(frame->hd.flags & (NGHTTP2_FLAG_PAD_HIGH | NGHTTP2_FLAG_PAD_LOW)) {
print_frame_attr_indent();
fprintf(outfile, "(padlen=%zu)\n", frame->data.padlen);
}
break;
case NGHTTP2_HEADERS:
if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
print_frame_attr_indent();
printf("(pri=%d)\n", frame->headers.pri);
fprintf(outfile, "(");
if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
fprintf(outfile, "pri=%d, ", frame->headers.pri);
}
fprintf(outfile, "padlen=%zu)\n", frame->headers.padlen);
switch(frame->headers.cat) {
case NGHTTP2_HCAT_REQUEST:
print_frame_attr_indent();
printf("; Open new stream\n");
fprintf(outfile, "; Open new stream\n");
break;
case NGHTTP2_HCAT_RESPONSE:
print_frame_attr_indent();
printf("; First response header\n");
fprintf(outfile, "; First response header\n");
break;
case NGHTTP2_HCAT_PUSH_RESPONSE:
print_frame_attr_indent();
printf("; First push response header\n");
fprintf(outfile, "; First push response header\n");
break;
default:
break;
@ -293,20 +356,21 @@ void print_frame(print_type ptype, const nghttp2_frame *frame)
break;
case NGHTTP2_PRIORITY:
print_frame_attr_indent();
printf("(pri=%d)\n", frame->priority.pri);
fprintf(outfile, "(pri=%d)\n", frame->priority.pri);
break;
case NGHTTP2_RST_STREAM:
print_frame_attr_indent();
printf("(error_code=%s(%u))\n",
fprintf(outfile, "(error_code=%s(%u))\n",
strstatus(frame->rst_stream.error_code),
frame->rst_stream.error_code);
break;
case NGHTTP2_SETTINGS:
print_frame_attr_indent();
printf("(niv=%lu)\n", static_cast<unsigned long>(frame->settings.niv));
fprintf(outfile, "(niv=%lu)\n",
static_cast<unsigned long>(frame->settings.niv));
for(size_t i = 0; i < frame->settings.niv; ++i) {
print_frame_attr_indent();
printf("[%s(%d):%u]\n",
fprintf(outfile, "[%s(%d):%u]\n",
strsettingsid(frame->settings.iv[i].settings_id),
frame->settings.iv[i].settings_id,
frame->settings.iv[i].value);
@ -314,18 +378,20 @@ void print_frame(print_type ptype, const nghttp2_frame *frame)
break;
case NGHTTP2_PUSH_PROMISE:
print_frame_attr_indent();
printf("(promised_stream_id=%d)\n",
frame->push_promise.promised_stream_id);
fprintf(outfile, "(promised_stream_id=%d, padlen=%zu)\n",
frame->push_promise.promised_stream_id,
frame->push_promise.padlen);
print_nv(frame->push_promise.nva, frame->push_promise.nvlen);
break;
case NGHTTP2_PING:
print_frame_attr_indent();
printf("(opaque_data=%s)\n",
fprintf(outfile, "(opaque_data=%s)\n",
util::format_hex(frame->ping.opaque_data, 8).c_str());
break;
case NGHTTP2_GOAWAY:
print_frame_attr_indent();
printf("(last_stream_id=%d, error_code=%s(%u), opaque_data(%u)=[%s])\n",
fprintf(outfile,
"(last_stream_id=%d, error_code=%s(%u), opaque_data(%u)=[%s])\n",
frame->goaway.last_stream_id,
strstatus(frame->goaway.error_code),
frame->goaway.error_code,
@ -335,11 +401,11 @@ void print_frame(print_type ptype, const nghttp2_frame *frame)
break;
case NGHTTP2_WINDOW_UPDATE:
print_frame_attr_indent();
printf("(window_size_increment=%d)\n",
fprintf(outfile, "(window_size_increment=%d)\n",
frame->window_update.window_size_increment);
break;
default:
printf("\n");
fprintf(outfile, "\n");
break;
}
}
@ -356,7 +422,7 @@ int verbose_on_header_callback(nghttp2_session *session,
static_cast<uint16_t>(namelen), static_cast<uint16_t>(valuelen)
};
print_timer();
printf(" (stream_id=%d) ", frame->hd.stream_id);
fprintf(outfile, " (stream_id=%d) ", frame->hd.stream_id);
print_nv(&nv, 1, false /* no indent */);
return 0;
}
@ -365,9 +431,9 @@ int verbose_on_frame_recv_callback
(nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
{
print_timer();
printf(" recv ");
fprintf(outfile, " recv ");
print_frame(PRINT_RECV, frame);
fflush(stdout);
fflush(outfile);
return 0;
}
@ -376,9 +442,9 @@ int verbose_on_invalid_frame_recv_callback
nghttp2_error_code error_code, void *user_data)
{
print_timer();
printf(" [INVALID; status=%s] recv ", strstatus(error_code));
fprintf(outfile, " [INVALID; status=%s] recv ", strstatus(error_code));
print_frame(PRINT_RECV, frame);
fflush(stdout);
fflush(outfile);
return 0;
}
@ -387,11 +453,11 @@ void dump_header(const uint8_t *head, size_t headlen)
{
size_t i;
print_frame_attr_indent();
printf("Header dump: ");
fprintf(outfile, "Header dump: ");
for(i = 0; i < headlen; ++i) {
printf("%02X ", head[i]);
fprintf(outfile, "%02X ", head[i]);
}
printf("\n");
fprintf(outfile, "\n");
}
} // namespace
@ -403,9 +469,9 @@ int verbose_on_unknown_frame_recv_callback(nghttp2_session *session,
void *user_data)
{
print_timer();
printf(" recv unknown frame\n");
fprintf(outfile, " recv unknown frame\n");
dump_header(head, headlen);
fflush(stdout);
fflush(outfile);
return 0;
}
@ -413,9 +479,9 @@ int verbose_on_frame_send_callback
(nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
{
print_timer();
printf(" send ");
fprintf(outfile, " send ");
print_frame(PRINT_SEND, frame);
fflush(stdout);
fflush(outfile);
return 0;
}

View File

@ -86,6 +86,10 @@ void print_timer();
// variable.
void set_color_output(bool f);
// Set output file when printing HTTP2 frames. By default, stdout is
// used.
void set_output(FILE *file);
} // namespace nghttp2
#endif // APP_HELPER_H

View File

@ -27,12 +27,8 @@
static void dump_val(json_t *jent, const char *key, uint8_t *val, size_t len)
{
if(val == NULL && len > 0) {
json_object_set_new(jent, key, json_string("**DEALLOCATED**"));
} else {
json_object_set_new(jent, key, json_pack("s#", val, len));
}
}
json_t* dump_header_table(nghttp2_hd_context *context)
{
@ -58,12 +54,6 @@ json_t* dump_header_table(nghttp2_hd_context *context)
json_object_set_new(obj, "size", json_integer(context->hd_table_bufsize));
json_object_set_new(obj, "max_size",
json_integer(context->hd_table_bufsize_max));
if(context->role == NGHTTP2_HD_ROLE_DEFLATE) {
json_object_set_new(obj, "deflate_size",
json_integer(context->deflate_hd_table_bufsize));
json_object_set_new(obj, "max_deflate_size",
json_integer(context->deflate_hd_table_bufsize_max));
}
return obj;
}
@ -93,13 +83,11 @@ json_t* dump_headers(const nghttp2_nv *nva, size_t nvlen)
return headers;
}
void output_json_header(int side)
void output_json_header(void)
{
printf("{\n"
" \"context\": \"%s\",\n"
" \"cases\":\n"
" [\n",
(side == NGHTTP2_HD_SIDE_REQUEST ? "request" : "response"));
" [\n");
}
void output_json_footer(void)

View File

@ -40,7 +40,7 @@ json_t* dump_header(const uint8_t *name, size_t namelen,
json_t* dump_headers(const nghttp2_nv *nva, size_t nvlen);
void output_json_header(int side);
void output_json_header(void);
void output_json_footer(void);

View File

@ -44,7 +44,6 @@
typedef struct {
size_t table_size;
size_t deflate_table_size;
nghttp2_hd_side side;
int http1text;
int dump_header_table;
int no_refset;
@ -174,11 +173,11 @@ static int deflate_hd_json(json_t *obj, nghttp2_hd_deflater *deflater, int seq)
return 0;
}
static void init_deflater(nghttp2_hd_deflater *deflater, nghttp2_hd_side side)
static void init_deflater(nghttp2_hd_deflater *deflater)
{
nghttp2_hd_deflate_init2(deflater, side, config.deflate_table_size);
nghttp2_hd_deflate_init2(deflater, config.deflate_table_size);
nghttp2_hd_deflate_set_no_refset(deflater, config.no_refset);
nghttp2_hd_change_table_size(&deflater->ctx, config.table_size);
nghttp2_hd_deflate_change_table_size(deflater, config.table_size);
}
static void deinit_deflater(nghttp2_hd_deflater *deflater)
@ -193,19 +192,12 @@ static int perform(void)
json_error_t error;
size_t len;
nghttp2_hd_deflater deflater;
nghttp2_hd_side side;
json = json_loadf(stdin, 0, &error);
if(json == NULL) {
fprintf(stderr, "JSON loading failed\n");
exit(EXIT_FAILURE);
}
if(strcmp("request", json_string_value(json_object_get(json, "context")))
== 0) {
side = NGHTTP2_HD_SIDE_REQUEST;
} else {
side = NGHTTP2_HD_SIDE_RESPONSE;
}
cases = json_object_get(json, "cases");
if(cases == NULL) {
fprintf(stderr, "Missing 'cases' key in root object\n");
@ -215,8 +207,8 @@ static int perform(void)
fprintf(stderr, "'cases' must be JSON array\n");
exit(EXIT_FAILURE);
}
init_deflater(&deflater, side);
output_json_header(side);
init_deflater(&deflater);
output_json_header();
len = json_array_size(cases);
for(i = 0; i < len; ++i) {
json_t *obj = json_array_get(cases, i);
@ -244,8 +236,8 @@ static int perform_from_http1text(void)
nghttp2_nv nva[256];
int seq = 0;
nghttp2_hd_deflater deflater;
init_deflater(&deflater, config.side);
output_json_header(config.side);
init_deflater(&deflater);
output_json_header();
for(;;) {
size_t nvlen = 0;
int end = 0;
@ -355,10 +347,6 @@ static void print_help(void)
"The output of this program can be used as input for inflatehd.\n"
"\n"
"OPTIONS:\n"
" -r, --response Use response compression context instead of\n"
" request if -t is used. For JSON input, it is\n"
" determined by inspecting \"context\" key in\n"
" root JSON object.\n"
" -t, --http1text Use HTTP/1 style header field text as input.\n"
" Each header set is delimited by single empty\n"
" line.\n"
@ -377,7 +365,6 @@ static void print_help(void)
}
static struct option long_options[] = {
{"response", no_argument, NULL, 'r'},
{"http1text", no_argument, NULL, 't'},
{"table-size", required_argument, NULL, 's'},
{"deflate-table-size", required_argument, NULL, 'S'},
@ -390,7 +377,6 @@ int main(int argc, char **argv)
{
char *end;
config.side = NGHTTP2_HD_SIDE_REQUEST;
config.table_size = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE;
config.deflate_table_size = NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE;
config.http1text = 0;
@ -398,15 +384,11 @@ int main(int argc, char **argv)
config.no_refset = 0;
while(1) {
int option_index = 0;
int c = getopt_long(argc, argv, "S:cdhrs:t", long_options, &option_index);
int c = getopt_long(argc, argv, "S:cdhs:t", long_options, &option_index);
if(c == -1) {
break;
}
switch(c) {
case 'r':
/* --response */
config.side = NGHTTP2_HD_SIDE_RESPONSE;
break;
case 'h':
print_help();
exit(EXIT_SUCCESS);

View File

@ -42,7 +42,6 @@
#include "comp_helper.h"
typedef struct {
size_t table_size;
int dump_header_table;
} inflate_config;
@ -110,7 +109,7 @@ static int inflate_hd(json_t *obj, nghttp2_hd_inflater *inflater, int seq)
seq);
return -1;
}
rv = nghttp2_hd_change_table_size(&inflater->ctx,
rv = nghttp2_hd_inflate_change_table_size(inflater,
json_integer_value(table_size));
if(rv != 0) {
fprintf(stderr,
@ -163,19 +162,12 @@ static int perform(void)
json_t *json, *cases;
json_error_t error;
size_t len;
nghttp2_hd_side side;
json = json_loadf(stdin, 0, &error);
if(json == NULL) {
fprintf(stderr, "JSON loading failed\n");
exit(EXIT_FAILURE);
}
if(strcmp("request", json_string_value(json_object_get(json, "context")))
== 0) {
side = NGHTTP2_HD_SIDE_REQUEST;
} else {
side = NGHTTP2_HD_SIDE_RESPONSE;
}
cases = json_object_get(json, "cases");
if(cases == NULL) {
fprintf(stderr, "Missing 'cases' key in root object\n");
@ -185,10 +177,8 @@ static int perform(void)
fprintf(stderr, "'cases' must be JSON array\n");
exit(EXIT_FAILURE);
}
nghttp2_hd_inflate_init(&inflater, side);
nghttp2_hd_change_table_size(&inflater.ctx, config.table_size);
output_json_header(side);
nghttp2_hd_inflate_init(&inflater);
output_json_header();
len = json_array_size(cases);
for(i = 0; i < len; ++i) {
json_t *obj = json_array_get(cases, i);
@ -241,29 +231,21 @@ static void print_help(void)
"The output of this program can be used as input for deflatehd.\n"
"\n"
"OPTIONS:\n"
" -s, --table-size=<N>\n"
" Set dynamic table size. In the HPACK\n"
" specification, this value is denoted by\n"
" SETTINGS_HEADER_TABLE_SIZE.\n"
" Default: 4096\n"
" -d, --dump-header-table\n"
" Output dynamic header table.\n");
}
static struct option long_options[] = {
{"table-size", required_argument, NULL, 's'},
{"dump-header-table", no_argument, NULL, 'd'},
{NULL, 0, NULL, 0 }
};
int main(int argc, char **argv)
{
char *end;
config.table_size = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE;
config.dump_header_table = 0;
while(1) {
int option_index = 0;
int c = getopt_long(argc, argv, "dhs:", long_options, &option_index);
int c = getopt_long(argc, argv, "dh", long_options, &option_index);
if(c == -1) {
break;
}
@ -271,15 +253,6 @@ int main(int argc, char **argv)
case 'h':
print_help();
exit(EXIT_SUCCESS);
case 's':
/* --table-size */
errno = 0;
config.table_size = strtoul(optarg, &end, 10);
if(errno == ERANGE || *end != '\0') {
fprintf(stderr, "-s: Bad option value\n");
exit(EXIT_FAILURE);
}
break;
case 'd':
/* --dump-header-table */
config.dump_header_table = 1;

View File

@ -82,6 +82,7 @@ struct Config {
std::string keyfile;
std::string datafile;
size_t output_upper_thres;
size_t padding;
ssize_t peer_max_concurrent_streams;
ssize_t header_table_size;
int32_t pri;
@ -95,10 +96,11 @@ struct Config {
bool verbose;
bool get_assets;
bool stat;
bool no_flow_control;
bool upgrade;
bool continuation;
Config()
: output_upper_thres(1024*1024),
padding(0),
peer_max_concurrent_streams(NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS),
header_table_size(-1),
pri(NGHTTP2_PRI_DEFAULT),
@ -111,8 +113,8 @@ struct Config {
verbose(false),
get_assets(false),
stat(false),
no_flow_control(false),
upgrade(false)
upgrade(false),
continuation(false)
{}
};
} // namespace
@ -366,11 +368,6 @@ size_t populate_settings(nghttp2_settings_entry *iv)
} else {
iv[1].value = NGHTTP2_INITIAL_WINDOW_SIZE;
}
if(config.no_flow_control) {
iv[niv].settings_id = NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS;
iv[niv].value = 1;
++niv;
}
if(config.header_table_size >= 0) {
iv[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
iv[niv].value = config.header_table_size;
@ -964,6 +961,12 @@ int submit_request
{"accept", "*/*"},
{"accept-encoding", "gzip, deflate"},
{"user-agent", "nghttp2/" NGHTTP2_VERSION}};
if(config.continuation) {
for(size_t i = 0; i < 8; ++i) {
build_headers.emplace_back("continuation-test-" + util::utos(i+1),
std::string(4096, '-'));
}
}
auto num_initial_headers = build_headers.size();
if(req->data_prd) {
build_headers.emplace_back("content-length", util::utos(req->data_length));
@ -1123,6 +1126,15 @@ int before_frame_send_callback
}
} // namespace
namespace {
ssize_t select_padding_callback
(nghttp2_session *session, const nghttp2_frame *frame, size_t max_payload,
void *user_data)
{
return std::min(max_payload, frame->hd.length + config.padding);
}
} // namespace
namespace {
void check_response_header(nghttp2_session *session, Request* req)
{
@ -1579,6 +1591,9 @@ int run(char **uris, int n)
}
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
callbacks.on_header_callback = on_header_callback;
if(config.padding) {
callbacks.select_padding_callback = select_padding_callback;
}
std::string prev_scheme;
std::string prev_host;
@ -1642,9 +1657,9 @@ int run(char **uris, int n)
namespace {
void print_usage(std::ostream& out)
{
out << "Usage: nghttp [-Oafnsuv] [-t <SECONDS>] [-w <WINDOW_BITS>] [-W <WINDOW_BITS>]\n"
out << "Usage: nghttp [-Oansuv] [-t <SECONDS>] [-w <WINDOW_BITS>] [-W <WINDOW_BITS>]\n"
<< " [--cert=<CERT>] [--key=<KEY>] [-d <FILE>] [-m <N>]\n"
<< " [-p <PRIORITY>] [-M <N>]\n"
<< " [-p <PRIORITY>] [-M <N>] [-b <ALIGNMENT>]\n"
<< " <URI>..."
<< std::endl;
}
@ -1686,9 +1701,6 @@ void print_help(std::ostream& out)
<< " -m, --multiply=<N> Request each URI <N> times. By default, same\n"
<< " URI is not requested twice. This option\n"
<< " disables it too.\n"
<< " -f, --no-flow-control\n"
<< " Disables connection and stream level flow\n"
<< " controls.\n"
<< " -u, --upgrade Perform HTTP Upgrade for HTTP/2.0. This\n"
<< " option is ignored if the request URI has\n"
<< " https scheme.\n"
@ -1704,7 +1716,10 @@ void print_help(std::ostream& out)
<< " is large enough as it is seen as unlimited.\n"
<< " -c, --header-table-size=<N>\n"
<< " Specify decoder header table size.\n"
<< " -b, --padding=<N> Add at most <N> bytes to a frame payload as\n"
<< " padding. Specify 0 to disable padding.\n"
<< " --color Force colored log output.\n"
<< " --continuation Send large header to test CONTINUATION.\n"
<< std::endl;
}
} // namespace
@ -1727,18 +1742,19 @@ int main(int argc, char **argv)
{"header", required_argument, nullptr, 'H'},
{"data", required_argument, nullptr, 'd'},
{"multiply", required_argument, nullptr, 'm'},
{"no-flow-control", no_argument, nullptr, 'f'},
{"upgrade", no_argument, nullptr, 'u'},
{"pri", required_argument, nullptr, 'p'},
{"peer-max-concurrent-streams", required_argument, nullptr, 'M'},
{"header-table-size", required_argument, nullptr, 'c'},
{"padding", required_argument, nullptr, 'b'},
{"cert", required_argument, &flag, 1},
{"key", required_argument, &flag, 2},
{"color", no_argument, &flag, 3},
{"continuation", no_argument, &flag, 4},
{nullptr, 0, nullptr, 0 }
};
int option_index = 0;
int c = getopt_long(argc, argv, "M:Oac:d:fm:np:hH:vst:uw:W:", long_options,
int c = getopt_long(argc, argv, "M:Oab:c:d:m:np:hH:vst:uw:W:", long_options,
&option_index);
char *end;
if(c == -1) {
@ -1752,12 +1768,12 @@ int main(int argc, char **argv)
case 'O':
config.remote_name = true;
break;
case 'f':
config.no_flow_control = true;
break;
case 'h':
print_help(std::cout);
exit(EXIT_SUCCESS);
case 'b':
config.padding = strtol(optarg, nullptr, 10);
break;
case 'n':
config.null_out = true;
break;
@ -1869,6 +1885,10 @@ int main(int argc, char **argv)
// color option
color = true;
break;
case 4:
// continuation option
config.continuation = true;
break;
}
break;
default:

View File

@ -75,7 +75,8 @@ int parse_push_config(Config& config, const char *optarg)
namespace {
void print_usage(std::ostream& out)
{
out << "Usage: nghttpd [-DVfhv] [-d <PATH>] [--no-tls] <PORT> [<PRIVATE_KEY> <CERT>]"
out << "Usage: nghttpd [-DVhpv] [-d <PATH>] [--no-tls] [-b <ALIGNMENT>]\n"
<< " <PORT> [<PRIVATE_KEY> <CERT>]"
<< std::endl;
}
} // namespace
@ -103,9 +104,6 @@ void print_help(std::ostream& out)
<< " -v, --verbose Print debug information such as reception/\n"
<< " transmission of frames and name/value pairs.\n"
<< " --no-tls Disable SSL/TLS.\n"
<< " -f, --no-flow-control\n"
<< " Disables connection and stream level flow\n"
<< " controls.\n"
<< " -c, --header-table-size=<N>\n"
<< " Specify decoder header table size.\n"
<< " --color Force colored log output.\n"
@ -117,6 +115,8 @@ void print_help(std::ostream& out)
<< " -p/=/foo.png -p/doc=/bar.css\n"
<< " PATH and PUSH_PATHs are relative to document\n"
<< " root. See --htdocs option.\n"
<< " -b, --padding=<N> Add at most <N> bytes to a frame payload as\n"
<< " padding. Specify 0 to disable padding.\n"
<< " -h, --help Print this help.\n"
<< std::endl;
}
@ -134,15 +134,15 @@ int main(int argc, char **argv)
{"help", no_argument, nullptr, 'h'},
{"verbose", no_argument, nullptr, 'v'},
{"verify-client", no_argument, nullptr, 'V'},
{"no-flow-control", no_argument, nullptr, 'f'},
{"header-table-size", required_argument, nullptr, 'c'},
{"push", required_argument, nullptr, 'p'},
{"padding", required_argument, nullptr, 'b'},
{"no-tls", no_argument, &flag, 1},
{"color", no_argument, &flag, 2},
{nullptr, 0, nullptr, 0}
};
int option_index = 0;
int c = getopt_long(argc, argv, "DVc:d:fhp:v", long_options, &option_index);
int c = getopt_long(argc, argv, "DVb:c:d:hp:v", long_options, &option_index);
char *end;
if(c == -1) {
break;
@ -154,12 +154,12 @@ int main(int argc, char **argv)
case 'V':
config.verify_client = true;
break;
case 'b':
config.padding = strtol(optarg, nullptr, 10);
break;
case 'd':
config.htdocs = optarg;
break;
case 'f':
config.no_flow_control = true;
break;
case 'h':
print_help(std::cout);
exit(EXIT_SUCCESS);

View File

@ -53,6 +53,7 @@
#include "shrpx_listen_handler.h"
#include "shrpx_ssl.h"
#include "util.h"
#include "app_helper.h"
using namespace nghttp2;
@ -435,6 +436,8 @@ void fill_default_config()
mod_config()->http2_upstream_dump_request_header = nullptr;
mod_config()->http2_upstream_dump_response_header = nullptr;
mod_config()->http2_no_cookie_crumbling = false;
mod_config()->upstream_frame_debug = false;
mod_config()->padding = 0;
}
} // namespace
@ -673,6 +676,11 @@ void print_help(std::ostream& out)
<< " --backend-no-tls Disable SSL/TLS on backend connections.\n"
<< " --http2-no-cookie-crumbling\n"
<< " Don't crumble cookie header field.\n"
<< " --padding=<N> Add at most <N> bytes to a HTTP/2 frame payload\n"
<< " as padding.\n"
<< " Specify 0 to disable padding. This option is\n"
<< " meant for debugging purpose and not intended\n"
<< " to enhance protocol security.\n"
<< "\n"
<< " Mode:\n"
<< " (default mode) Accept HTTP/2.0, SPDY and HTTP/1.1 over\n"
@ -736,6 +744,10 @@ void print_help(std::ostream& out)
<< " an empty line.\n"
<< " This option is not thread safe and MUST NOT\n"
<< " be used with option -n=N, where N >= 2.\n"
<< " -o, --frontend-frame-debug\n"
<< " Print HTTP/2 frames in frontend to stderr.\n"
<< " This option is not thread safe and MUST NOT\n"
<< " be used with option -n=N, where N >= 2.\n"
<< " -D, --daemon Run in a background. If -D is used, the\n"
<< " current working directory is changed to '/'.\n"
<< " --pid-file=<PATH> Set path to save PID of this program.\n"
@ -771,6 +783,7 @@ int main(int argc, char **argv)
{"client-proxy", no_argument, nullptr, 'p'},
{"http2-proxy", no_argument, nullptr, 's'},
{"version", no_argument, nullptr, 'v'},
{"frontend-frame-debug", no_argument, nullptr, 'o'},
{"add-x-forwarded-for", no_argument, &flag, 1},
{"frontend-http2-read-timeout", required_argument, &flag, 2},
{"frontend-read-timeout", required_argument, &flag, 3},
@ -817,11 +830,12 @@ int main(int argc, char **argv)
{"frontend-http2-connection-window-bits", required_argument, &flag, 46},
{"backend-http2-connection-window-bits", required_argument, &flag, 47},
{"tls-proto-list", required_argument, &flag, 48},
{"padding", required_argument, &flag, 49},
{nullptr, 0, nullptr, 0 }
};
int option_index = 0;
int c = getopt_long(argc, argv, "DL:b:c:f:hkn:psv", long_options,
int c = getopt_long(argc, argv, "DL:b:c:f:hkn:opsv", long_options,
&option_index);
if(c == -1) {
break;
@ -851,6 +865,9 @@ int main(int argc, char **argv)
case 'n':
cmdcfgs.emplace_back(SHRPX_OPT_WORKERS, optarg);
break;
case 'o':
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_FRAME_DEBUG, "yes");
break;
case 'p':
cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PROXY, "yes");
break;
@ -1050,6 +1067,10 @@ int main(int argc, char **argv)
// --tls-proto-list
cmdcfgs.emplace_back(SHRPX_OPT_TLS_PROTO_LIST, optarg);
break;
case 49:
// --padding
cmdcfgs.emplace_back(SHRPX_OPT_PADDING, optarg);
break;
default:
break;
}
@ -1204,6 +1225,15 @@ int main(int argc, char **argv)
get_rate_limit(get_config()->write_burst),
nullptr);
if(get_config()->upstream_frame_debug) {
// To make it sync to logging
set_output(stderr);
if(isatty(fileno(stdout))) {
set_color_output(true);
}
reset_timer();
}
struct sigaction act;
memset(&act, 0, sizeof(struct sigaction));
act.sa_handler = SIG_IGN;

View File

@ -115,6 +115,8 @@ const char SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER[] =
const char SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER[] =
"frontend-http2-dump-response-header";
const char SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING[] = "http2-no-cookie-crumbling";
const char SHRPX_OPT_FRONTEND_FRAME_DEBUG[] = "frontend-frame-debug";
const char SHRPX_OPT_PADDING[] = "padding";
namespace {
Config *config = nullptr;
@ -480,6 +482,10 @@ int parse_config(const char *opt, const char *optarg)
mod_config()->http2_upstream_dump_response_header = f;
} else if(util::strieq(opt, SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING)) {
mod_config()->http2_no_cookie_crumbling = util::strieq(optarg, "yes");
} else if(util::strieq(opt, SHRPX_OPT_FRONTEND_FRAME_DEBUG)) {
mod_config()->upstream_frame_debug = util::strieq(optarg, "yes");
} else if(util::strieq(opt, SHRPX_OPT_PADDING)) {
mod_config()->padding = strtoul(optarg, nullptr, 10);
} else if(util::strieq(opt, "conf")) {
LOG(WARNING) << "conf is ignored";
} else {

View File

@ -102,6 +102,8 @@ extern const char SHRPX_OPT_CLIENT_CERT_FILE[];
extern const char SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER[];
extern const char SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER[];
extern const char SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING[];
extern const char SHRPX_OPT_FRONTEND_FRAME_DEBUG[];
extern const char SHRPX_OPT_PADDING[];
union sockaddr_union {
sockaddr sa;
@ -179,6 +181,7 @@ struct Config {
size_t npn_list_len;
// The number of elements in tls_proto_list
size_t tls_proto_list_len;
size_t padding;
// downstream protocol; this will be determined by given options.
shrpx_proto downstream_proto;
int syslog_facility;
@ -213,6 +216,7 @@ struct Config {
// true if stderr refers to a terminal.
bool tty;
bool http2_no_cookie_crumbling;
bool upstream_frame_debug;
};
const Config* get_config();

View File

@ -94,6 +94,13 @@ std::string colorizeHeaders(const char *hdrs)
return nhdrs;
}
ssize_t select_padding_callback
(nghttp2_session *session, const nghttp2_frame *frame, size_t max_payload,
void *user_data)
{
return std::min(max_payload, frame->hd.length + get_config()->padding);
}
} // namespace http
} // namespace shrpx

View File

@ -29,6 +29,8 @@
#include <string>
#include <nghttp2/nghttp2.h>
namespace shrpx {
namespace http {
@ -40,6 +42,10 @@ std::string create_via_header_value(int major, int minor);
// Adds ANSI color codes to HTTP headers |hdrs|.
std::string colorizeHeaders(const char *hdrs);
ssize_t select_padding_callback
(nghttp2_session *session, const nghttp2_frame *frame, size_t max_payload,
void *user_data);
} // namespace http
} // namespace shrpx

View File

@ -1193,6 +1193,9 @@ int Http2Session::on_connect()
callbacks.on_unknown_frame_recv_callback = on_unknown_frame_recv_callback;
callbacks.on_header_callback = on_header_callback;
callbacks.on_begin_headers_callback = on_begin_headers_callback;
if(get_config()->padding) {
callbacks.select_padding_callback = http::select_padding_callback;
}
nghttp2_opt_set opt_set;
opt_set.no_auto_stream_window_update = 1;

View File

@ -39,6 +39,7 @@
#include "http2.h"
#include "util.h"
#include "base64.h"
#include "app_helper.h"
using namespace nghttp2;
@ -205,6 +206,10 @@ int on_header_callback(nghttp2_session *session,
const uint8_t *value, size_t valuelen,
void *user_data)
{
if(get_config()->upstream_frame_debug) {
verbose_on_header_callback(session, frame, name, namelen, value, valuelen,
user_data);
}
if(frame->hd.type != NGHTTP2_HEADERS ||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
return 0;
@ -359,6 +364,9 @@ int on_frame_recv_callback
(nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
{
int rv;
if(get_config()->upstream_frame_debug) {
verbose_on_frame_recv_callback(session, frame, user_data);
}
auto upstream = static_cast<Http2Upstream*>(user_data);
switch(frame->hd.type) {
case NGHTTP2_DATA: {
@ -428,6 +436,9 @@ namespace {
int on_frame_send_callback(nghttp2_session* session,
const nghttp2_frame *frame, void *user_data)
{
if(get_config()->upstream_frame_debug) {
verbose_on_frame_send_callback(session, frame, user_data);
}
auto upstream = static_cast<Http2Upstream*>(user_data);
if(frame->hd.type == NGHTTP2_SETTINGS &&
(frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) {
@ -508,6 +519,9 @@ Http2Upstream::Http2Upstream(ClientHandler *handler)
callbacks.on_unknown_frame_recv_callback = on_unknown_frame_recv_callback;
callbacks.on_header_callback = on_header_callback;
callbacks.on_begin_headers_callback = on_begin_headers_callback;
if(get_config()->padding) {
callbacks.select_padding_callback = http::select_padding_callback;
}
nghttp2_opt_set opt_set;
opt_set.no_auto_stream_window_update = 1;

View File

@ -179,12 +179,10 @@ int main(int argc, char* argv[])
test_nghttp2_session_defer_data) ||
!CU_add_test(pSuite, "session_flow_control",
test_nghttp2_session_flow_control) ||
!CU_add_test(pSuite, "session_flow_control_disable_remote",
test_nghttp2_session_flow_control_disable_remote) ||
!CU_add_test(pSuite, "session_flow_control_disable_local",
test_nghttp2_session_flow_control_disable_local) ||
!CU_add_test(pSuite, "session_flow_control_data_recv",
test_nghttp2_session_flow_control_data_recv) ||
!CU_add_test(pSuite, "session_flow_control_data_with_padding_recv",
test_nghttp2_session_flow_control_data_with_padding_recv) ||
!CU_add_test(pSuite, "session_data_read_temporal_failure",
test_nghttp2_session_data_read_temporal_failure) ||
!CU_add_test(pSuite, "session_on_stream_close",
@ -199,6 +197,10 @@ int main(int argc, char* argv[])
test_nghttp2_session_set_option) ||
!CU_add_test(pSuite, "session_data_backoff_by_high_pri_frame",
test_nghttp2_session_data_backoff_by_high_pri_frame) ||
!CU_add_test(pSuite, "session_pack_data_with_padding",
test_nghttp2_session_pack_data_with_padding) ||
!CU_add_test(pSuite, "session_pack_headers_with_padding",
test_nghttp2_session_pack_headers_with_padding) ||
!CU_add_test(pSuite, "pack_settings_payload",
test_nghttp2_pack_settings_payload) ||
!CU_add_test(pSuite, "frame_pack_headers",
@ -225,8 +227,6 @@ int main(int argc, char* argv[])
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_deflate_buffer",
test_nghttp2_hd_deflate_deflate_buffer) ||
!CU_add_test(pSuite, "hd_deflate_clear_refset",
test_nghttp2_hd_deflate_clear_refset) ||
!CU_add_test(pSuite, "hd_inflate_indname_noinc",

View File

@ -74,14 +74,16 @@ void test_nghttp2_frame_pack_headers()
nghttp2_headers frame, oframe;
uint8_t *buf = NULL;
size_t buflen = 0;
size_t bufoff;
ssize_t framelen;
nghttp2_nv *nva;
ssize_t nvlen;
nva_out out;
ssize_t nv_offset;
nva_out_init(&out);
nghttp2_hd_deflate_init(&deflater, NGHTTP2_HD_SIDE_REQUEST);
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_REQUEST);
nghttp2_hd_deflate_init(&deflater);
nghttp2_hd_inflate_init(&inflater);
nva = headers();
nvlen = HEADERS_LENGTH;
@ -89,17 +91,22 @@ void test_nghttp2_frame_pack_headers()
NGHTTP2_FLAG_END_STREAM|NGHTTP2_FLAG_END_HEADERS,
1000000007,
1 << 20, nva, nvlen);
framelen = nghttp2_frame_pack_headers(&buf, &buflen, &frame, &deflater);
framelen = nghttp2_frame_pack_headers(&buf, &buflen, &bufoff, &frame,
&deflater);
CU_ASSERT(0 == unpack_frame((nghttp2_frame*)&oframe, buf, framelen));
check_frame_header(framelen - NGHTTP2_FRAME_HEAD_LENGTH, NGHTTP2_HEADERS,
CU_ASSERT(0 == unpack_frame((nghttp2_frame*)&oframe, buf + bufoff,
framelen - bufoff));
check_frame_header(framelen - bufoff - NGHTTP2_FRAME_HEAD_LENGTH,
NGHTTP2_HEADERS,
NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS,
1000000007, &oframe.hd);
/* We didn't include PRIORITY flag so priority is not packed */
CU_ASSERT(1 << 30 == oframe.pri);
CU_ASSERT(framelen - 8 ==
inflate_hd(&inflater, &out, buf + 8, framelen - 8));
nv_offset = bufoff + NGHTTP2_FRAME_HEAD_LENGTH;
CU_ASSERT(framelen - nv_offset ==
inflate_hd(&inflater, &out,
buf + nv_offset, framelen - nv_offset));
CU_ASSERT(7 == out.nvlen);
CU_ASSERT(nvnameeq("method", &out.nva[0]));
@ -111,17 +118,22 @@ void test_nghttp2_frame_pack_headers()
memset(&oframe, 0, sizeof(oframe));
/* Next, include PRIORITY flag */
frame.hd.flags |= NGHTTP2_FLAG_PRIORITY;
framelen = nghttp2_frame_pack_headers(&buf, &buflen, &frame, &deflater);
framelen = nghttp2_frame_pack_headers(&buf, &buflen, &bufoff, &frame,
&deflater);
CU_ASSERT(0 == unpack_frame((nghttp2_frame*)&oframe, buf, framelen));
check_frame_header(framelen - NGHTTP2_FRAME_HEAD_LENGTH, NGHTTP2_HEADERS,
CU_ASSERT(0 == unpack_frame((nghttp2_frame*)&oframe, buf + bufoff,
framelen - bufoff));
check_frame_header(framelen - bufoff - NGHTTP2_FRAME_HEAD_LENGTH,
NGHTTP2_HEADERS,
NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS |
NGHTTP2_FLAG_PRIORITY,
1000000007, &oframe.hd);
CU_ASSERT(1 << 20 == oframe.pri);
CU_ASSERT(framelen - 12 ==
inflate_hd(&inflater, &out, buf + 12, framelen - 12));
nv_offset = bufoff + NGHTTP2_FRAME_HEAD_LENGTH + 4;
CU_ASSERT(framelen - nv_offset ==
inflate_hd(&inflater, &out,
buf + nv_offset, framelen - nv_offset));
nghttp2_nv_array_sort(out.nva, out.nvlen);
CU_ASSERT(nvnameeq("method", &out.nva[0]));
@ -140,6 +152,7 @@ void test_nghttp2_frame_pack_headers_frame_too_large(void)
nghttp2_headers frame;
uint8_t *buf = NULL;
size_t buflen = 0;
size_t bufoff;
ssize_t framelen;
nghttp2_nv *nva;
ssize_t nvlen;
@ -158,12 +171,13 @@ void test_nghttp2_frame_pack_headers_frame_too_large(void)
}
nvlen = nghttp2_nv_array_copy(&nva, big_hds, big_hdslen);
nghttp2_hd_deflate_init(&deflater, NGHTTP2_HD_SIDE_REQUEST);
nghttp2_hd_deflate_init(&deflater);
nghttp2_frame_headers_init(&frame,
NGHTTP2_FLAG_END_STREAM|NGHTTP2_FLAG_END_HEADERS,
1000000007,
0, nva, nvlen);
framelen = nghttp2_frame_pack_headers(&buf, &buflen, &frame, &deflater);
framelen = nghttp2_frame_pack_headers(&buf, &buflen, &bufoff, &frame,
&deflater);
CU_ASSERT_EQUAL(NGHTTP2_ERR_HEADER_COMP, framelen);
nghttp2_frame_headers_free(&frame);
@ -220,15 +234,17 @@ void test_nghttp2_frame_pack_settings()
iv[0].value = 256;
iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
iv[1].value = 16384;
iv[2].settings_id = NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS;
iv[2].value = 1;
iv[2].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
iv[2].value = 4096;
nghttp2_frame_settings_init(&frame, NGHTTP2_FLAG_NONE,
nghttp2_frame_iv_copy(iv, 3), 3);
framelen = nghttp2_frame_pack_settings(&buf, &buflen, &frame);
CU_ASSERT(NGHTTP2_FRAME_HEAD_LENGTH+3*8 == framelen);
CU_ASSERT(NGHTTP2_FRAME_HEAD_LENGTH +
3 * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH == framelen);
CU_ASSERT(0 == unpack_frame((nghttp2_frame*)&oframe, buf, framelen));
check_frame_header(3*8, NGHTTP2_SETTINGS, NGHTTP2_FLAG_NONE, 0, &oframe.hd);
check_frame_header(3 * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH,
NGHTTP2_SETTINGS, NGHTTP2_FLAG_NONE, 0, &oframe.hd);
CU_ASSERT(3 == oframe.niv);
for(i = 0; i < 3; ++i) {
CU_ASSERT(iv[i].settings_id == oframe.iv[i].settings_id);
@ -247,29 +263,34 @@ void test_nghttp2_frame_pack_push_promise()
nghttp2_push_promise frame, oframe;
uint8_t *buf = NULL;
size_t buflen = 0;
size_t bufoff;
ssize_t framelen;
nghttp2_nv *nva;
ssize_t nvlen;
nva_out out;
ssize_t nv_offset;
nva_out_init(&out);
nghttp2_hd_deflate_init(&deflater, NGHTTP2_HD_SIDE_RESPONSE);
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_RESPONSE);
nghttp2_hd_deflate_init(&deflater);
nghttp2_hd_inflate_init(&inflater);
nva = headers();
nvlen = HEADERS_LENGTH;
nghttp2_frame_push_promise_init(&frame, NGHTTP2_FLAG_END_PUSH_PROMISE,
1000000007, (1U << 31) - 1, nva, nvlen);
framelen = nghttp2_frame_pack_push_promise(&buf, &buflen, &frame, &deflater);
framelen = nghttp2_frame_pack_push_promise(&buf, &buflen, &bufoff, &frame,
&deflater);
CU_ASSERT(0 == unpack_frame((nghttp2_frame*)&oframe, buf, framelen));
check_frame_header(framelen - NGHTTP2_FRAME_HEAD_LENGTH,
CU_ASSERT(0 == unpack_frame((nghttp2_frame*)&oframe,
buf + bufoff, framelen - bufoff));
check_frame_header(framelen - bufoff - NGHTTP2_FRAME_HEAD_LENGTH,
NGHTTP2_PUSH_PROMISE,
NGHTTP2_FLAG_END_PUSH_PROMISE, 1000000007, &oframe.hd);
CU_ASSERT((1U << 31) - 1 == oframe.promised_stream_id);
CU_ASSERT(framelen - 12 ==
inflate_hd(&inflater, &out, buf + 12, framelen - 12));
nv_offset = bufoff + NGHTTP2_FRAME_HEAD_LENGTH + 4;
CU_ASSERT(framelen - nv_offset ==
inflate_hd(&inflater, &out, buf + nv_offset, framelen - nv_offset));
CU_ASSERT(7 == out.nvlen);
CU_ASSERT(nvnameeq("method", &out.nva[0]));
@ -397,22 +418,30 @@ void test_nghttp2_iv_check(void)
iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
iv[0].value = 100;
iv[1].settings_id = NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS;
iv[1].value = 0;
iv[2].settings_id = NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS;
iv[2].value = 1;
iv[1].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
iv[1].value = 1024;
CU_ASSERT(nghttp2_iv_check(iv, 2, 0));
CU_ASSERT(nghttp2_iv_check(iv, 3, 0));
/* Re-enabling flow-control*/
CU_ASSERT(0 == nghttp2_iv_check(iv, 2, 1));
CU_ASSERT(0 == nghttp2_iv_check(iv, 3, 1));
CU_ASSERT(nghttp2_iv_check(iv, 2));
iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
iv[1].value = NGHTTP2_MAX_WINDOW_SIZE;
CU_ASSERT(nghttp2_iv_check(iv, 2, 0));
CU_ASSERT(nghttp2_iv_check(iv, 2));
/* Too large window size */
iv[1].value = (uint32_t)NGHTTP2_MAX_WINDOW_SIZE + 1;
CU_ASSERT(0 == nghttp2_iv_check(iv, 2, 0));
CU_ASSERT(0 == nghttp2_iv_check(iv, 2));
/* ENABLE_PUSH only allows 0 or 1 */
iv[1].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
iv[1].value = 0;
CU_ASSERT(nghttp2_iv_check(iv, 2));
iv[1].value = 1;
CU_ASSERT(nghttp2_iv_check(iv, 2));
iv[1].value = 3;
CU_ASSERT(!nghttp2_iv_check(iv, 2));
/* Undefined SETTINGS ID */
iv[1].settings_id = 1000000009;
iv[1].value = 0;
CU_ASSERT(!nghttp2_iv_check(iv, 2));
}

View File

@ -35,15 +35,6 @@
#define GET_TABLE_ENT(context, index) nghttp2_hd_table_get(context, index)
static void assert_nv_equal(nghttp2_nv *a, nghttp2_nv *b, size_t len)
{
size_t i;
nghttp2_nv_array_sort(b, len);
for(i = 0; i < len; ++i, ++a, ++b) {
CU_ASSERT(nghttp2_nv_equal(a, b));
}
}
void test_nghttp2_hd_deflate(void)
{
nghttp2_hd_deflater deflater;
@ -68,8 +59,8 @@ void test_nghttp2_hd_deflate(void)
nva_out out;
nva_out_init(&out);
CU_ASSERT(0 == nghttp2_hd_deflate_init(&deflater, NGHTTP2_HD_SIDE_REQUEST));
CU_ASSERT(0 == nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_REQUEST));
CU_ASSERT(0 == nghttp2_hd_deflate_init(&deflater));
CU_ASSERT(0 == nghttp2_hd_inflate_init(&inflater));
blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, nv_offset, nva1,
sizeof(nva1)/sizeof(nghttp2_nv));
CU_ASSERT(blocklen > 0);
@ -151,8 +142,8 @@ void test_nghttp2_hd_deflate_same_indexed_repr(void)
nva_out out;
nva_out_init(&out);
CU_ASSERT(0 == nghttp2_hd_deflate_init(&deflater, NGHTTP2_HD_SIDE_REQUEST));
CU_ASSERT(0 == nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_REQUEST));
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. */
@ -207,8 +198,8 @@ void test_nghttp2_hd_deflate_common_header_eviction(void)
nva[i].valuelen = sizeof(value);
}
nghttp2_hd_deflate_init(&deflater, NGHTTP2_HD_SIDE_REQUEST);
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_REQUEST);
nghttp2_hd_deflate_init(&deflater);
nghttp2_hd_inflate_init(&inflater);
/* First emit "h1: ..." to put it in the reference set (index
= 0). */
@ -245,174 +236,6 @@ void test_nghttp2_hd_deflate_common_header_eviction(void)
nghttp2_hd_deflate_free(&deflater);
}
void test_nghttp2_hd_deflate_deflate_buffer(void)
{
nghttp2_hd_deflater deflater;
nghttp2_hd_inflater inflater;
size_t i;
ssize_t blocklen;
uint8_t *buf = NULL;
size_t buflen = 0;
nghttp2_nv nva1[] = { MAKE_NV("k1", "v1"), /* 36 */
MAKE_NV("k10", "v10"), /* 38 */
MAKE_NV("k100", "v100"), /* 40 */
MAKE_NV("k1000", "v1000") /* 42 */
}; /* Total: 156 */
nghttp2_nv nva2[] = { MAKE_NV("k10", "v10"), /* 38 */
MAKE_NV("k1", "v1") /* 36 */
};
nghttp2_nv nv3;
uint8_t val[256];
nghttp2_nv nva4[] = { MAKE_NV(":method", "GET"),
MAKE_NV(":scheme", "http")
};
nghttp2_hd_entry *ent;
nva_out out;
nva_out_init(&out);
memset(val, 'a', sizeof(val));
nv3.name = nv3.value = val;
nv3.namelen = nv3.valuelen = sizeof(val);
/* Check the case where entry from static table is inserted to
dynamic header table. And it is out of deflate header table
size. */
nghttp2_hd_deflate_init2(&deflater, NGHTTP2_HD_SIDE_REQUEST, 32);
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_REQUEST);
blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0,
nva4, ARRLEN(nva4));
CU_ASSERT(blocklen > 0);
/* Now header table should look like this:
*
* 0: :scheme, http (-)
* 1: :method, GET (-)
*
* name/value of all entries must be NULL.
*/
CU_ASSERT(2 == deflater.ctx.hd_table.len);
CU_ASSERT(0 == deflater.ctx.deflate_hd_tablelen);
CU_ASSERT(0 == deflater.ctx.deflate_hd_table_bufsize);
for(i = 0; i < 2; ++i) {
ent = nghttp2_hd_table_get(&deflater.ctx, i);
CU_ASSERT(ent->nv.name == NULL);
CU_ASSERT(ent->nv.value == NULL);
CU_ASSERT(0 == (ent->flags & NGHTTP2_HD_FLAG_REFSET));
}
CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf, blocklen));
CU_ASSERT(2 == out.nvlen);
assert_nv_equal(nva4, out.nva, 2);
nva_out_reset(&out);
nghttp2_hd_deflate_free(&deflater);
nghttp2_hd_inflate_free(&inflater);
/* 156 buffer size can hold all headers in deflate region */
nghttp2_hd_deflate_init2(&deflater, NGHTTP2_HD_SIDE_REQUEST, 156);
blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0,
nva1, ARRLEN(nva1));
CU_ASSERT(blocklen > 0);
/* Now header table should look like this:
*
* 0: k1000, v100
* 1: k100, v100
* 2: k10, v10
* 3: k1, v1
*/
CU_ASSERT(4 == deflater.ctx.hd_table.len);
CU_ASSERT(4 == deflater.ctx.deflate_hd_tablelen);
CU_ASSERT(156 == deflater.ctx.deflate_hd_table_bufsize);
for(i = 0; i < 4; ++i) {
CU_ASSERT(nghttp2_hd_table_get(&deflater.ctx, i)->nv.name != NULL);
CU_ASSERT(nghttp2_hd_table_get(&deflater.ctx, i)->nv.value != NULL);
}
CU_ASSERT(0 == nghttp2_hd_change_table_size(&deflater.ctx, 156));
CU_ASSERT(4 == deflater.ctx.hd_table.len);
CU_ASSERT(4 == deflater.ctx.deflate_hd_tablelen);
CU_ASSERT(156 == deflater.ctx.deflate_hd_table_bufsize);
blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0, &nv3, 1);
CU_ASSERT(blocklen > 0);
/* Now header table should be unchanged, because we don't index
large header */
CU_ASSERT(4 == deflater.ctx.hd_table.len);
CU_ASSERT(4 == deflater.ctx.deflate_hd_tablelen);
CU_ASSERT(156 == deflater.ctx.deflate_hd_table_bufsize);
nghttp2_hd_deflate_free(&deflater);
/* Check more complex use case */
nghttp2_hd_deflate_init2(&deflater, NGHTTP2_HD_SIDE_REQUEST, 155);
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_REQUEST);
blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0,
nva1, ARRLEN(nva1));
CU_ASSERT(blocklen > 0);
/* Now header table should look like this:
*
* 0: k1000, v100 (R)
* 1: k100, v100 (R)
* 2: k10, v10 (R)
* 3: k1, v1 (-) <- name, value must be NULL and not in reference set
*
* But due to the deflate table size limit, name/value of index=3 must
* be NULL.
*/
CU_ASSERT(4 == deflater.ctx.hd_table.len);
CU_ASSERT(3 == deflater.ctx.deflate_hd_tablelen);
CU_ASSERT(120 == deflater.ctx.deflate_hd_table_bufsize);
for(i = 0; i < 3; ++i) {
CU_ASSERT(nghttp2_hd_table_get(&deflater.ctx, i)->nv.name != NULL);
CU_ASSERT(nghttp2_hd_table_get(&deflater.ctx, i)->nv.value != NULL);
}
ent = nghttp2_hd_table_get(&deflater.ctx, 3);
CU_ASSERT(ent->nv.name == NULL);
CU_ASSERT(ent->nv.value == NULL);
CU_ASSERT(0 == (ent->flags & NGHTTP2_HD_FLAG_REFSET));
CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf, blocklen));
CU_ASSERT(4 == out.nvlen);
assert_nv_equal(nva1, out.nva, 4);
nva_out_reset(&out);
blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0,
nva2, ARRLEN(nva2));
CU_ASSERT(blocklen > 0);
/* Now header table should look like this:
*
* 0: k1, v1 (R)
* 1: k1000, v100 (R)
* 2: k100, v100 (R)
* 3: k10, v10 (-) <- name, value must be NULL
* 4: k1, v1 (-) <- name, value must be NULL
*/
CU_ASSERT(5 == deflater.ctx.hd_table.len);
CU_ASSERT(3 == deflater.ctx.deflate_hd_tablelen);
CU_ASSERT(118 == deflater.ctx.deflate_hd_table_bufsize);
ent = nghttp2_hd_table_get(&deflater.ctx, 3);
CU_ASSERT(0 == (ent->flags & NGHTTP2_HD_FLAG_REFSET));
ent = nghttp2_hd_table_get(&deflater.ctx, 3);
CU_ASSERT(0 == (ent->flags & NGHTTP2_HD_FLAG_REFSET));
CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf, blocklen));
CU_ASSERT(2 == out.nvlen);
/* Sort before comparison */
nghttp2_nv_array_sort(nva2, 2);
assert_nv_equal(nva2, out.nva, 2);
nva_out_reset(&out);
free(buf);
nghttp2_hd_inflate_free(&inflater);
nghttp2_hd_deflate_free(&deflater);
}
void test_nghttp2_hd_deflate_clear_refset(void)
{
nghttp2_hd_deflater deflater;
@ -428,10 +251,10 @@ void test_nghttp2_hd_deflate_clear_refset(void)
nva_out out;
nva_out_init(&out);
nghttp2_hd_deflate_init2(&deflater, NGHTTP2_HD_SIDE_REQUEST,
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, NGHTTP2_HD_SIDE_REQUEST);
nghttp2_hd_inflate_init(&inflater);
for(i = 0; i < 2; ++i) {
blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0,
@ -466,14 +289,13 @@ void test_nghttp2_hd_inflate_indname_noinc(void)
nva_out out;
nva_out_init(&out);
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_REQUEST);
nghttp2_hd_inflate_init(&inflater);
for(i = 0; i < ARRLEN(nv); ++i) {
offset = 0;
CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, &buflen, &offset, 56,
nv[i].value, nv[i].valuelen,
0,
NGHTTP2_HD_SIDE_REQUEST));
0));
CU_ASSERT((ssize_t)offset == inflate_hd(&inflater, &out, buf, offset));
CU_ASSERT(1 == out.nvlen);
@ -497,11 +319,10 @@ void test_nghttp2_hd_inflate_indname_inc(void)
nva_out out;
nva_out_init(&out);
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_REQUEST);
nghttp2_hd_inflate_init(&inflater);
CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, &buflen, &offset, 56,
nv.value, nv.valuelen, 1,
NGHTTP2_HD_SIDE_REQUEST));
nv.value, nv.valuelen, 1));
CU_ASSERT((ssize_t)offset == inflate_hd(&inflater, &out, buf, offset));
CU_ASSERT(1 == out.nvlen);
@ -526,21 +347,17 @@ void test_nghttp2_hd_inflate_indname_inc_eviction(void)
nva_out out;
nva_out_init(&out);
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_REQUEST);
nghttp2_hd_inflate_init(&inflater);
memset(value, '0', sizeof(value));
CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, &buflen, &offset, 13,
value, sizeof(value), 1,
NGHTTP2_HD_SIDE_REQUEST));
value, sizeof(value), 1));
CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, &buflen, &offset, 14,
value, sizeof(value), 1,
NGHTTP2_HD_SIDE_REQUEST));
value, sizeof(value), 1));
CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, &buflen, &offset, 15,
value, sizeof(value), 1,
NGHTTP2_HD_SIDE_REQUEST));
value, sizeof(value), 1));
CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, &buflen, &offset, 16,
value, sizeof(value), 1,
NGHTTP2_HD_SIDE_REQUEST));
value, sizeof(value), 1));
CU_ASSERT((ssize_t)offset == inflate_hd(&inflater, &out, buf, offset));
@ -578,12 +395,11 @@ void test_nghttp2_hd_inflate_newname_noinc(void)
nva_out out;
nva_out_init(&out);
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_REQUEST);
nghttp2_hd_inflate_init(&inflater);
for(i = 0; i < ARRLEN(nv); ++i) {
offset = 0;
CU_ASSERT(0 == nghttp2_hd_emit_newname_block(&buf, &buflen, &offset,
&nv[i], 0,
NGHTTP2_HD_SIDE_REQUEST));
&nv[i], 0));
CU_ASSERT((ssize_t)offset == inflate_hd(&inflater, &out, buf, offset));
CU_ASSERT(1 == out.nvlen);
@ -607,11 +423,10 @@ void test_nghttp2_hd_inflate_newname_inc(void)
nva_out out;
nva_out_init(&out);
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_REQUEST);
nghttp2_hd_inflate_init(&inflater);
CU_ASSERT(0 == nghttp2_hd_emit_newname_block(&buf, &buflen, &offset,
&nv, 1,
NGHTTP2_HD_SIDE_REQUEST));
&nv, 1));
CU_ASSERT((ssize_t)offset == inflate_hd(&inflater, &out, buf, offset));
CU_ASSERT(1 == out.nvlen);
@ -644,11 +459,10 @@ void test_nghttp2_hd_inflate_clearall_inc(void)
nv.value = value;
nv.valuelen = sizeof(value);
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_REQUEST);
nghttp2_hd_inflate_init(&inflater);
CU_ASSERT(0 == nghttp2_hd_emit_newname_block(&buf, &buflen, &offset,
&nv, 1,
NGHTTP2_HD_SIDE_REQUEST));
&nv, 1));
CU_ASSERT((ssize_t)offset == inflate_hd(&inflater, &out, buf, offset));
CU_ASSERT(1 == out.nvlen);
@ -672,8 +486,7 @@ void test_nghttp2_hd_inflate_clearall_inc(void)
offset = 0;
CU_ASSERT(0 == nghttp2_hd_emit_newname_block(&buf, &buflen, &offset,
&nv, 1,
NGHTTP2_HD_SIDE_REQUEST));
&nv, 1));
CU_ASSERT((ssize_t)offset == inflate_hd(&inflater, &out, buf, offset));
CU_ASSERT(1 == out.nvlen);
@ -699,7 +512,7 @@ void test_nghttp2_hd_inflate_zero_length_huffman(void)
buf[2] = 'x';
buf[3] = 0x80;
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_REQUEST);
nghttp2_hd_inflate_init(&inflater);
CU_ASSERT(4 == inflate_hd(&inflater, &out, buf, 4));
CU_ASSERT(1 == out.nvlen);
@ -715,35 +528,158 @@ void test_nghttp2_hd_inflate_zero_length_huffman(void)
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", "/") };
uint8_t *buf = NULL;
size_t buflen = 0;
ssize_t rv;
nva_out out;
size_t offset;
nva_out_init(&out);
nghttp2_hd_deflate_init(&deflater);
nghttp2_hd_inflate_init(&inflater);
/* inflater changes notifies 8000 max header table size */
CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 8000));
CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 8000));
CU_ASSERT(127 == deflater.ctx.hd_table.mask);
CU_ASSERT(8000 == deflater.ctx.hd_table_bufsize_max);
CU_ASSERT(255 == inflater.ctx.hd_table.mask);
CU_ASSERT(8000 == inflater.ctx.hd_table_bufsize_max);
CU_ASSERT(8000 == inflater.settings_hd_table_bufsize_max);
/* This will emit encoding context update with header table size 4096 */
rv = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0, nva, 2);
CU_ASSERT(rv > 0);
CU_ASSERT(2 == deflater.ctx.hd_table.len);
CU_ASSERT(4096 == deflater.ctx.hd_table_bufsize_max);
CU_ASSERT(rv == inflate_hd(&inflater, &out, buf, rv));
CU_ASSERT(2 == inflater.ctx.hd_table.len);
CU_ASSERT(4096 == inflater.ctx.hd_table_bufsize_max);
CU_ASSERT(8000 == inflater.settings_hd_table_bufsize_max);
nva_out_reset(&out);
/* inflater changes header table size to 1024 */
CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 1024));
CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 1024));
CU_ASSERT(127 == deflater.ctx.hd_table.mask);
CU_ASSERT(1024 == deflater.ctx.hd_table_bufsize_max);
CU_ASSERT(255 == inflater.ctx.hd_table.mask);
CU_ASSERT(1024 == inflater.ctx.hd_table_bufsize_max);
CU_ASSERT(1024 == inflater.settings_hd_table_bufsize_max);
rv = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0, nva, 2);
CU_ASSERT(rv >= 0);
CU_ASSERT(2 == deflater.ctx.hd_table.len);
CU_ASSERT(1024 == deflater.ctx.hd_table_bufsize_max);
CU_ASSERT(rv == inflate_hd(&inflater, &out, buf, rv));
CU_ASSERT(2 == inflater.ctx.hd_table.len);
CU_ASSERT(1024 == inflater.ctx.hd_table_bufsize_max);
CU_ASSERT(1024 == inflater.settings_hd_table_bufsize_max);
nva_out_reset(&out);
/* inflater changes header table size to 0 */
CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 0));
CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 0));
CU_ASSERT(127 == deflater.ctx.hd_table.mask);
CU_ASSERT(0 == deflater.ctx.hd_table.len);
CU_ASSERT(0 == deflater.ctx.hd_table_bufsize_max);
CU_ASSERT(255 == inflater.ctx.hd_table.mask);
CU_ASSERT(0 == inflater.ctx.hd_table.len);
CU_ASSERT(0 == inflater.ctx.hd_table_bufsize_max);
CU_ASSERT(0 == inflater.settings_hd_table_bufsize_max);
rv = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0, nva, 2);
CU_ASSERT(rv >= 0);
CU_ASSERT(0 == deflater.ctx.hd_table.len);
CU_ASSERT(0 == deflater.ctx.hd_table_bufsize_max);
CU_ASSERT(rv == inflate_hd(&inflater, &out, buf, rv));
CU_ASSERT(0 == inflater.ctx.hd_table.len);
CU_ASSERT(0 == inflater.ctx.hd_table_bufsize_max);
CU_ASSERT(0 == inflater.settings_hd_table_bufsize_max);
nva_out_reset(&out);
free(buf);
nghttp2_hd_inflate_free(&inflater);
nghttp2_hd_deflate_free(&deflater);
/* Check table buffer is expanded */
buf = NULL;
buflen = 0;
nghttp2_hd_deflate_init2(&deflater, 8192);
nghttp2_hd_inflate_init(&inflater);
/* First inflater changes header table size to 8000 */
CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 8000));
CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 8000));
nghttp2_hd_deflate_init(&deflater, NGHTTP2_HD_SIDE_REQUEST);
CU_ASSERT(0 == nghttp2_hd_change_table_size(&deflater.ctx, 8000));
CU_ASSERT(255 == deflater.ctx.hd_table.mask);
CU_ASSERT(8000 == deflater.ctx.hd_table_bufsize_max);
CU_ASSERT(255 == inflater.ctx.hd_table.mask);
CU_ASSERT(8000 == inflater.ctx.hd_table_bufsize_max);
CU_ASSERT(8000 == inflater.settings_hd_table_bufsize_max);
rv = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0, nva, 2);
CU_ASSERT(rv > 0);
CU_ASSERT(2 == deflater.ctx.hd_table.len);
CU_ASSERT(8000 == deflater.ctx.hd_table_bufsize_max);
CU_ASSERT(0 == nghttp2_hd_change_table_size(&deflater.ctx, 16384));
CU_ASSERT(511 == deflater.ctx.hd_table.mask);
CU_ASSERT(rv == inflate_hd(&inflater, &out, buf, rv));
CU_ASSERT(2 == inflater.ctx.hd_table.len);
CU_ASSERT(8000 == inflater.ctx.hd_table_bufsize_max);
CU_ASSERT(8000 == inflater.settings_hd_table_bufsize_max);
nva_out_reset(&out);
CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 16383));
CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 16383));
CU_ASSERT(255 == deflater.ctx.hd_table.mask);
CU_ASSERT(16383 == deflater.ctx.hd_table_bufsize_max);
CU_ASSERT(511 == inflater.ctx.hd_table.mask);
CU_ASSERT(16383 == inflater.ctx.hd_table_bufsize_max);
CU_ASSERT(16383 == inflater.settings_hd_table_bufsize_max);
rv = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0, nva, 2);
CU_ASSERT(rv >= 0);
CU_ASSERT(2 == deflater.ctx.hd_table.len);
CU_ASSERT(2 == deflater.ctx.deflate_hd_tablelen);
CU_ASSERT(5 ==
deflater.ctx.hd_table.buffer[deflater.ctx.hd_table.first]
->nv.namelen);
CU_ASSERT(8192 == deflater.ctx.hd_table_bufsize_max);
CU_ASSERT(0 == nghttp2_hd_change_table_size(&deflater.ctx, 0));
CU_ASSERT(511 == deflater.ctx.hd_table.mask);
CU_ASSERT(0 == deflater.ctx.hd_table.len);
CU_ASSERT(0 == deflater.ctx.deflate_hd_tablelen);
CU_ASSERT(rv == inflate_hd(&inflater, &out, buf, rv));
CU_ASSERT(2 == inflater.ctx.hd_table.len);
CU_ASSERT(8192 == inflater.ctx.hd_table_bufsize_max);
CU_ASSERT(16383 == inflater.settings_hd_table_bufsize_max);
nva_out_reset(&out);
/* Lastly, check the error condition */
offset = 0;
rv = nghttp2_hd_emit_table_size(&buf, &buflen, &offset, 25600);
CU_ASSERT(rv == 0);
CU_ASSERT(NGHTTP2_ERR_HEADER_COMP ==
inflate_hd(&inflater, &out, buf, offset));
nva_out_reset(&out);
free(buf);
nghttp2_hd_inflate_free(&inflater);
nghttp2_hd_deflate_free(&deflater);
}
@ -908,8 +844,8 @@ void test_nghttp2_hd_deflate_inflate(void)
MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"),
};
nghttp2_hd_deflate_init(&deflater, NGHTTP2_HD_SIDE_REQUEST);
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_REQUEST);
nghttp2_hd_deflate_init(&deflater);
nghttp2_hd_inflate_init(&inflater);
check_deflate_inflate(&deflater, &inflater, nv1, ARRLEN(nv1));
check_deflate_inflate(&deflater, &inflater, nv2, ARRLEN(nv2));

View File

@ -28,7 +28,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_deflate_buffer(void);
void test_nghttp2_hd_deflate_clear_refset(void);
void test_nghttp2_hd_inflate_indname_noinc(void);
void test_nghttp2_hd_inflate_indname_inc(void);

View File

@ -33,14 +33,13 @@ static void http2(void)
{
const unsigned char p[] = {
8, 'h', 't', 't', 'p', '/', '1', '.', '1',
17, 'H', 'T', 'T', 'P', '-', 'd', 'r', 'a', 'f', 't', '-', '0', '9', '/',
'2', '.', '0',
5, 'h', '2', '-', '1', '0',
6, 's', 'p', 'd', 'y', '/', '3'
};
unsigned char outlen;
unsigned char* out;
CU_ASSERT(1 == nghttp2_select_next_protocol(&out, &outlen, p, sizeof(p)));
CU_ASSERT(17 == outlen);
CU_ASSERT(NGHTTP2_PROTO_VERSION_ID_LEN == outlen);
CU_ASSERT(memcmp(NGHTTP2_PROTO_VERSION_ID, out, outlen) == 0);
}

View File

@ -40,7 +40,7 @@
#define OB_DATA(ITEM) nghttp2_outbound_item_get_data_frame(ITEM)
typedef struct {
uint8_t buf[4096];
uint8_t buf[65535];
size_t length;
} accumulator;
@ -71,6 +71,8 @@ typedef struct {
int header_cb_called;
int begin_headers_cb_called;
nghttp2_nv nv;
size_t data_chunk_len;
size_t padding_boundary;
} my_user_data;
static void scripted_data_feed_init(scripted_data_feed *df,
@ -187,6 +189,7 @@ static int on_data_chunk_recv_callback(nghttp2_session *session,
{
my_user_data *ud = (my_user_data*)user_data;
++ud->data_chunk_recv_cb_called;
ud->data_chunk_len = len;
return 0;
}
@ -200,6 +203,17 @@ static int pause_on_data_chunk_recv_callback(nghttp2_session *session,
return NGHTTP2_ERR_PAUSE;
}
static ssize_t select_padding_callback(nghttp2_session *session,
const nghttp2_frame *frame,
size_t max_payloadlen,
void *user_data)
{
my_user_data *ud = (my_user_data*)user_data;
return nghttp2_min(max_payloadlen,
(frame->hd.length + ud->padding_boundary - 1)
/ ud->padding_boundary * ud->padding_boundary);
}
static ssize_t fixed_length_data_source_read_callback
(nghttp2_session *session, int32_t stream_id,
uint8_t *buf, size_t len, int *eof,
@ -334,9 +348,10 @@ void test_nghttp2_session_recv(void)
};
uint8_t *framedata = NULL;
size_t framedatalen = 0;
size_t bufoff;
ssize_t framelen;
nghttp2_frame frame;
int i;
size_t i;
nghttp2_outbound_item *item;
nghttp2_nv *nva;
ssize_t nvlen;
@ -348,18 +363,18 @@ void test_nghttp2_session_recv(void)
callbacks.on_frame_recv_callback = on_frame_recv_callback;
user_data.df = &df;
nghttp2_session_server_new(&session, &callbacks, &user_data);
nghttp2_hd_deflate_init(&deflater, NGHTTP2_HD_SIDE_REQUEST);
nghttp2_hd_deflate_init(&deflater);
nvlen = nghttp2_nv_array_copy(&nva, nv, ARRLEN(nv));
nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS,
1, NGHTTP2_PRI_DEFAULT, nva, nvlen);
framelen = nghttp2_frame_pack_headers(&framedata, &framedatalen,
framelen = nghttp2_frame_pack_headers(&framedata, &framedatalen, &bufoff,
&frame.headers,
&deflater);
scripted_data_feed_init(&df, framedata, framelen);
scripted_data_feed_init(&df, framedata + bufoff, framelen - bufoff);
/* Send 1 byte per each read */
for(i = 0; i < framelen; ++i) {
for(i = 0; i < framelen - bufoff; ++i) {
df.feedseq[i] = 1;
}
nghttp2_frame_headers_free(&frame.headers);
@ -373,13 +388,13 @@ void test_nghttp2_session_recv(void)
/* Received HEADERS without header block, which is valid */
nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS,
5, NGHTTP2_PRI_DEFAULT, NULL, 0);
framelen = nghttp2_frame_pack_headers(&framedata, &framedatalen,
framelen = nghttp2_frame_pack_headers(&framedata, &framedatalen, &bufoff,
&frame.headers,
&deflater);
nghttp2_frame_headers_free(&frame.headers);
scripted_data_feed_init(&df, framedata, framelen);
scripted_data_feed_init(&df, framedata + bufoff, framelen - bufoff);
user_data.frame_recv_cb_called = 0;
CU_ASSERT(0 == nghttp2_session_recv(session));
CU_ASSERT(1 == user_data.frame_recv_cb_called);
@ -419,6 +434,7 @@ void test_nghttp2_session_recv_invalid_stream_id(void)
my_user_data user_data;
uint8_t *framedata = NULL;
size_t framedatalen = 0;
size_t bufoff;
ssize_t framelen;
nghttp2_frame frame;
nghttp2_hd_deflater deflater;
@ -430,15 +446,15 @@ void test_nghttp2_session_recv_invalid_stream_id(void)
user_data.df = &df;
user_data.invalid_frame_recv_cb_called = 0;
nghttp2_session_server_new(&session, &callbacks, &user_data);
nghttp2_hd_deflate_init(&deflater, NGHTTP2_HD_SIDE_REQUEST);
nghttp2_hd_deflate_init(&deflater);
nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2,
NGHTTP2_PRI_DEFAULT, NULL, 0);
framelen = nghttp2_frame_pack_headers(&framedata, &framedatalen,
framelen = nghttp2_frame_pack_headers(&framedata, &framedatalen, &bufoff,
&frame.headers,
&deflater);
scripted_data_feed_init(&df, framedata, framelen);
scripted_data_feed_init(&df, framedata + bufoff, framelen - bufoff);
nghttp2_frame_headers_free(&frame.headers);
CU_ASSERT(0 == nghttp2_session_recv(session));
@ -460,6 +476,7 @@ void test_nghttp2_session_recv_invalid_frame(void)
};
uint8_t *framedata = NULL;
size_t framedatalen = 0;
size_t bufoff;
ssize_t framelen;
nghttp2_frame frame;
nghttp2_nv *nva;
@ -474,15 +491,15 @@ void test_nghttp2_session_recv_invalid_frame(void)
user_data.df = &df;
user_data.frame_send_cb_called = 0;
nghttp2_session_server_new(&session, &callbacks, &user_data);
nghttp2_hd_deflate_init(&deflater, NGHTTP2_HD_SIDE_REQUEST);
nghttp2_hd_deflate_init(&deflater);
nvlen = nghttp2_nv_array_copy(&nva, nv, ARRLEN(nv));
nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1,
NGHTTP2_PRI_DEFAULT, nva, nvlen);
framelen = nghttp2_frame_pack_headers(&framedata, &framedatalen,
framelen = nghttp2_frame_pack_headers(&framedata, &framedatalen, &bufoff,
&frame.headers,
&deflater);
scripted_data_feed_init(&df, framedata, framelen);
scripted_data_feed_init(&df, framedata + bufoff, framelen - bufoff);
CU_ASSERT(0 == nghttp2_session_recv(session));
CU_ASSERT(0 == nghttp2_session_send(session));
@ -490,7 +507,7 @@ void test_nghttp2_session_recv_invalid_frame(void)
/* Receive exactly same bytes of HEADERS is treated as subsequent
HEADERS (e.g., trailers */
scripted_data_feed_init(&df, framedata, framelen);
scripted_data_feed_init(&df, framedata + bufoff, framelen - bufoff);
CU_ASSERT(0 == nghttp2_session_recv(session));
CU_ASSERT(0 == nghttp2_session_send(session));
@ -658,6 +675,7 @@ void test_nghttp2_session_recv_continuation(void)
uint8_t *framedata = NULL;
size_t framedatacap = 0;
size_t framedatalen;
size_t bufoff;
size_t framedataoff;
ssize_t rv;
my_user_data ud;
@ -672,20 +690,21 @@ void test_nghttp2_session_recv_continuation(void)
nghttp2_session_server_new(&session, &callbacks, &ud);
nghttp2_hd_deflate_init(&deflater, NGHTTP2_HD_SIDE_REQUEST);
nghttp2_hd_deflate_init(&deflater);
/* Make 1 HEADERS and insert CONTINUATION header */
nvlen = nghttp2_nv_array_copy(&nva, nv1, ARRLEN(nv1));
nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_NONE,
1, NGHTTP2_PRI_DEFAULT, nva, nvlen);
framedatalen = nghttp2_frame_pack_headers(&framedata, &framedatacap,
&bufoff,
&frame.headers,
&deflater);
nghttp2_frame_headers_free(&frame.headers);
memcpy(data, framedata, 9);
memcpy(data, framedata + bufoff, 9);
datalen = 9;
framedataoff = NGHTTP2_FRAME_HEAD_LENGTH + 1;
framedataoff = bufoff + NGHTTP2_FRAME_HEAD_LENGTH + 1;
nghttp2_put_uint16be(data, 1);
@ -727,18 +746,18 @@ void test_nghttp2_session_recv_continuation(void)
/* Expecting CONTINUATION, but get the other frame */
nghttp2_session_server_new(&session, &callbacks, &ud);
nghttp2_hd_deflate_init(&deflater, NGHTTP2_HD_SIDE_REQUEST);
nghttp2_hd_deflate_init(&deflater);
/* HEADERS without END_HEADERS flag */
nvlen = nghttp2_nv_array_copy(&nva, nv1, ARRLEN(nv1));
nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_NONE,
1, NGHTTP2_PRI_DEFAULT, nva, nvlen);
framedatalen = nghttp2_frame_pack_headers(&framedata, &framedatacap,
framedatalen = nghttp2_frame_pack_headers(&framedata, &framedatacap, &bufoff,
&frame.headers,
&deflater);
nghttp2_frame_headers_free(&frame.headers);
memcpy(data, framedata, framedatalen);
datalen = framedatalen;
memcpy(data, framedata + bufoff, framedatalen - bufoff);
datalen = framedatalen - bufoff;
/* Followed by PRIORITY */
nghttp2_frame_priority_init(&frame.priority, 1, 0);
@ -778,25 +797,28 @@ void test_nghttp2_session_recv_premature_headers(void)
my_user_data ud;
nghttp2_hd_deflater deflater;
nghttp2_outbound_item *item;
size_t bufoff = 0;
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
nghttp2_session_server_new(&session, &callbacks, &ud);
nghttp2_hd_deflate_init(&deflater, NGHTTP2_HD_SIDE_REQUEST);
nghttp2_hd_deflate_init(&deflater);
nvlen = nghttp2_nv_array_copy(&nva, nv1, ARRLEN(nv1));
nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS,
1, NGHTTP2_PRI_DEFAULT, nva, nvlen);
framedatalen = nghttp2_frame_pack_headers(&framedata, &framedatacap,
&bufoff,
&frame.headers,
&deflater);
nghttp2_frame_headers_free(&frame.headers);
/* Intentionally feed payload cutting last 1 byte off */
nghttp2_put_uint16be(framedata, frame.hd.length - 1);
rv = nghttp2_session_mem_recv(session, framedata, framedatalen - 1);
CU_ASSERT((ssize_t)framedatalen - 1 == rv);
nghttp2_put_uint16be(framedata + bufoff, frame.hd.length - 1);
rv = nghttp2_session_mem_recv(session, framedata + bufoff,
framedatalen - bufoff - 1);
CU_ASSERT((ssize_t)(framedatalen - bufoff - 1) == rv);
item = nghttp2_session_get_next_ob_item(session);
CU_ASSERT(NULL != item);
@ -828,6 +850,7 @@ void test_nghttp2_session_continue(void)
uint8_t buffer[4096];
uint8_t *bufp = buffer;
size_t buflen;
size_t bufoff;
nghttp2_frame frame;
nghttp2_nv *nva;
ssize_t nvlen;
@ -844,28 +867,30 @@ void test_nghttp2_session_continue(void)
nghttp2_session_server_new(&session, &callbacks, &user_data);
nghttp2_hd_deflate_init(&deflater, NGHTTP2_HD_SIDE_REQUEST);
nghttp2_hd_deflate_init(&deflater);
/* Make 2 HEADERS frames */
nvlen = nghttp2_nv_array_copy(&nva, nv1, ARRLEN(nv1));
nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS,
1, NGHTTP2_PRI_DEFAULT, nva, nvlen);
framelen1 = nghttp2_frame_pack_headers(&framedata, &framedatalen,
framelen1 = nghttp2_frame_pack_headers(&framedata, &framedatalen, &bufoff,
&frame.headers,
&deflater);
nghttp2_frame_headers_free(&frame.headers);
memcpy(buffer, framedata, framelen1);
memcpy(buffer, framedata + bufoff, framelen1 - bufoff);
framelen1 -= bufoff;
nvlen = nghttp2_nv_array_copy(&nva, nv2, ARRLEN(nv2));
nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS,
3, NGHTTP2_PRI_DEFAULT, nva, nvlen);
framelen2 = nghttp2_frame_pack_headers(&framedata, &framedatalen,
framelen2 = nghttp2_frame_pack_headers(&framedata, &framedatalen, &bufoff,
&frame.headers,
&deflater);
nghttp2_frame_headers_free(&frame.headers);
memcpy(buffer + framelen1, framedata, framelen2);
memcpy(buffer + framelen1, framedata + bufoff, framelen2 - bufoff);
framelen2 -= bufoff;
buflen = framelen1 + framelen2;
/* Receive 1st HEADERS and pause */
@ -1359,11 +1384,10 @@ void test_nghttp2_session_on_settings_received(void)
iv[2].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
iv[2].value = 64*1024;
iv[3].settings_id = NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS;
iv[3].value = 1;
iv[3].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
iv[3].value = 1024;
/* Unknown settings ID */
iv[4].settings_id = 999;
iv[4].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
iv[4].value = 0;
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
@ -1389,8 +1413,10 @@ void test_nghttp2_session_on_settings_received(void)
session->remote_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS]);
CU_ASSERT(64*1024 ==
session->remote_settings[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]);
CU_ASSERT(1 ==
session->remote_settings[NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS]);
CU_ASSERT(1024 ==
session->remote_settings[NGHTTP2_SETTINGS_HEADER_TABLE_SIZE]);
CU_ASSERT(0 ==
session->remote_settings[NGHTTP2_SETTINGS_ENABLE_PUSH]);
CU_ASSERT(64*1024 == stream1->remote_window_size);
CU_ASSERT(0 == stream2->remote_window_size);
@ -1402,10 +1428,6 @@ void test_nghttp2_session_on_settings_received(void)
CU_ASSERT(16*1024 == stream1->remote_window_size);
CU_ASSERT(-48*1024 == stream2->remote_window_size);
CU_ASSERT(0 == stream1->remote_flow_control);
CU_ASSERT(0 == stream2->remote_flow_control);
CU_ASSERT(0 == session->remote_flow_control);
nghttp2_frame_settings_free(&frame.settings);
nghttp2_session_del(session);
@ -2232,7 +2254,8 @@ void test_nghttp2_submit_request_without_data(void)
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
callbacks.send_callback = accumulator_send_callback;
CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud));
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_REQUEST);
nghttp2_hd_inflate_init(&inflater);
CU_ASSERT(0 == nghttp2_submit_request(session, NGHTTP2_PRI_DEFAULT,
nva, ARRLEN(nva), &data_prd, NULL));
item = nghttp2_session_get_next_ob_item(session);
@ -2303,7 +2326,8 @@ void test_nghttp2_submit_response_without_data(void)
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
callbacks.send_callback = accumulator_send_callback;
CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud));
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_RESPONSE);
nghttp2_hd_inflate_init(&inflater);
nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_END_STREAM,
NGHTTP2_PRI_DEFAULT,
NGHTTP2_STREAM_OPENING, NULL);
@ -2474,7 +2498,8 @@ void test_nghttp2_submit_headers(void)
callbacks.on_frame_send_callback = on_frame_send_callback;
CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud));
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_REQUEST);
nghttp2_hd_inflate_init(&inflater);
CU_ASSERT(0 == nghttp2_submit_headers(session,
NGHTTP2_FLAG_END_STREAM,
1, NGHTTP2_PRI_DEFAULT,
@ -2649,18 +2674,14 @@ void test_nghttp2_submit_settings(void)
iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
iv[1].value = 16*1024;
iv[2].settings_id = NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS;
iv[2].value = 1;
iv[2].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
iv[2].value = 50;
iv[3].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
iv[3].value = 50;
iv[3].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
iv[3].value = 0;
iv[4].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
iv[4].value = 0;
/* Attempt to re-enable flow-control */
iv[5].settings_id = NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS;
iv[5].value = 0;
iv[4].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
iv[4].value = (uint32_t)NGHTTP2_MAX_WINDOW_SIZE + 1;
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
callbacks.send_callback = null_send_callback;
@ -2668,25 +2689,23 @@ 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, 6));
nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 5));
/* Make sure that local settings are not changed */
CU_ASSERT(NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS ==
session->local_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS]);
CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE ==
session->local_settings[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]);
CU_ASSERT(0 ==
session->local_settings[NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS]);
/* Now sends without 6th one */
CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 5));
/* Now sends without 5th one */
CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 4));
item = nghttp2_session_get_next_ob_item(session);
CU_ASSERT(NGHTTP2_SETTINGS == OB_CTRL_TYPE(item));
frame = item->frame;
CU_ASSERT(5 == frame->settings.niv);
CU_ASSERT(4 == frame->settings.niv);
CU_ASSERT(5 == frame->settings.iv[0].value);
CU_ASSERT(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS ==
frame->settings.iv[0].settings_id);
@ -2709,9 +2728,6 @@ void test_nghttp2_submit_settings(void)
CU_ASSERT(16*1024 ==
session->local_settings[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]);
CU_ASSERT(1 ==
session->local_settings[NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS]);
CU_ASSERT(0 == session->local_flow_control);
CU_ASSERT(0 == session->hd_inflater.ctx.hd_table_bufsize_max);
nghttp2_session_del(session);
@ -2731,9 +2747,6 @@ void test_nghttp2_submit_settings_update_local_window_size(void)
iv[0].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
iv[0].value = 16*1024;
iv[1].settings_id = NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS;
iv[1].value = 1;
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
callbacks.send_callback = null_send_callback;
@ -2748,7 +2761,6 @@ void test_nghttp2_submit_settings_update_local_window_size(void)
stream = nghttp2_session_open_stream(session, 3, NGHTTP2_STREAM_FLAG_NONE,
NGHTTP2_PRI_DEFAULT,
NGHTTP2_STREAM_OPENED, NULL);
stream->local_flow_control = 0;
CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 1));
CU_ASSERT(0 == nghttp2_session_send(session));
@ -2759,7 +2771,7 @@ void test_nghttp2_submit_settings_update_local_window_size(void)
CU_ASSERT(16*1024 + 100 == stream->local_window_size);
stream = nghttp2_session_get_stream(session, 3);
CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE == stream->local_window_size);
CU_ASSERT(16*1024 == stream->local_window_size);
item = nghttp2_session_get_next_ob_item(session);
CU_ASSERT(NGHTTP2_WINDOW_UPDATE == OB_CTRL_TYPE(item));
@ -2767,20 +2779,6 @@ void test_nghttp2_submit_settings_update_local_window_size(void)
nghttp2_session_del(session);
/* Check flow control disabled case */
nghttp2_session_server_new(&session, &callbacks, NULL);
stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE,
NGHTTP2_PRI_DEFAULT,
NGHTTP2_STREAM_OPENED, NULL);
CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 2));
CU_ASSERT(0 == nghttp2_session_send(session));
CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &ack_frame, 0));
CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE == stream->local_window_size);
nghttp2_session_del(session);
/* Check overflow case */
iv[0].value = 128*1024;
nghttp2_session_server_new(&session, &callbacks, NULL);
@ -2793,7 +2791,10 @@ void test_nghttp2_submit_settings_update_local_window_size(void)
CU_ASSERT(0 == nghttp2_session_send(session));
CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &ack_frame, 0));
CU_ASSERT(NGHTTP2_STREAM_CLOSING == stream->state);
item = nghttp2_session_get_next_ob_item(session);
CU_ASSERT(NGHTTP2_GOAWAY == OB_CTRL_TYPE(item));
CU_ASSERT(NGHTTP2_FLOW_CONTROL_ERROR == OB_CTRL(item)->goaway.error_code);
nghttp2_session_del(session);
nghttp2_frame_settings_free(&ack_frame.settings);
@ -2875,10 +2876,6 @@ void test_nghttp2_submit_window_update(void)
CU_ASSERT(0 ==
nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 0));
/* Disable local flow control */
stream->local_flow_control = 0;
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, -1));
/* It is ok if stream is closed or does not exist at the call
time */
CU_ASSERT(0 ==
@ -3489,84 +3486,6 @@ void test_nghttp2_session_flow_control(void)
nghttp2_session_del(session);
}
void test_nghttp2_session_flow_control_disable_remote(void)
{
nghttp2_session *session;
nghttp2_session_callbacks callbacks;
my_user_data ud;
nghttp2_data_provider data_prd;
nghttp2_frame frame;
size_t data_size = 128*1024;
nghttp2_settings_entry iv = { NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS, 0x1 };
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
callbacks.send_callback = null_send_callback;
callbacks.on_frame_send_callback = on_frame_send_callback;
data_prd.read_callback = fixed_length_data_source_read_callback;
ud.frame_send_cb_called = 0;
ud.data_source_length = data_size;
/* Initial window size is 64KiB */
nghttp2_session_client_new(&session, &callbacks, &ud);
nghttp2_submit_request(session, NGHTTP2_PRI_DEFAULT, NULL, 0,
&data_prd, NULL);
/* Sends 64KiB data */
CU_ASSERT(0 == nghttp2_session_send(session));
CU_ASSERT(data_size - NGHTTP2_INITIAL_WINDOW_SIZE == ud.data_source_length);
/* Disable flow control entirely */
nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE,
dup_iv(&iv, 1), 1);
nghttp2_session_on_settings_received(session, &frame, 1);
/* Check both connection and stream-level remote_flow_control is
disabled */
CU_ASSERT(0 == nghttp2_session_get_stream(session, 1)->remote_flow_control);
CU_ASSERT(0 == session->remote_flow_control);
/* Sends remaining data */
CU_ASSERT(0 == nghttp2_session_send(session));
CU_ASSERT(0 == ud.data_source_length);
nghttp2_frame_settings_free(&frame.settings);
nghttp2_session_del(session);
}
void test_nghttp2_session_flow_control_disable_local(void)
{
nghttp2_session *session;
nghttp2_session_callbacks callbacks;
nghttp2_stream *stream;
nghttp2_settings_entry iv[1];
nghttp2_frame ack_frame;
nghttp2_frame_settings_init(&ack_frame.settings, NGHTTP2_FLAG_ACK, NULL, 0);
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
callbacks.send_callback = null_send_callback;
nghttp2_session_client_new(&session, &callbacks, NULL);
stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE,
NGHTTP2_PRI_DEFAULT,
NGHTTP2_STREAM_OPENING, NULL);
iv[0].settings_id = NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS;
iv[0].value = 1;
CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv,
ARRLEN(iv)));
CU_ASSERT(0 == nghttp2_session_send(session));
CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &ack_frame, 0));
CU_ASSERT(0 == stream->local_flow_control);
CU_ASSERT(0 == session->local_flow_control);
nghttp2_session_del(session);
nghttp2_frame_settings_free(&ack_frame.settings);
}
void test_nghttp2_session_flow_control_data_recv(void)
{
nghttp2_session *session;
@ -3630,6 +3549,46 @@ void test_nghttp2_session_flow_control_data_recv(void)
nghttp2_session_del(session);
}
void test_nghttp2_session_flow_control_data_with_padding_recv(void)
{
nghttp2_session *session;
nghttp2_session_callbacks callbacks;
uint8_t data[1024];
nghttp2_frame_hd hd;
nghttp2_stream *stream;
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
callbacks.send_callback = null_send_callback;
/* Initial window size to 64KiB - 1*/
nghttp2_session_client_new(&session, &callbacks, NULL);
stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE,
NGHTTP2_PRI_DEFAULT,
NGHTTP2_STREAM_OPENED, NULL);
/* Create DATA frame */
memset(data, 0, sizeof(data));
hd.length = 357;
hd.type = NGHTTP2_DATA;
hd.flags = NGHTTP2_FLAG_END_STREAM |
NGHTTP2_FLAG_PAD_HIGH | NGHTTP2_FLAG_PAD_LOW;;
hd.stream_id = 1;
nghttp2_frame_pack_frame_hd(data, &hd);
/* Add 2 byte padding (PAD_LOW itself is padding) */
data[NGHTTP2_FRAME_HEAD_LENGTH] = 1;
data[NGHTTP2_FRAME_HEAD_LENGTH + 1] = 1;
CU_ASSERT((ssize_t)(NGHTTP2_FRAME_HEAD_LENGTH + hd.length) ==
nghttp2_session_mem_recv(session, data,
NGHTTP2_FRAME_HEAD_LENGTH + hd.length));
CU_ASSERT((int32_t)hd.length == session->recv_window_size);
CU_ASSERT((int32_t)hd.length == stream->recv_window_size);
nghttp2_session_del(session);
}
void test_nghttp2_session_data_read_temporal_failure(void)
{
nghttp2_session *session;
@ -3984,6 +3943,162 @@ void test_nghttp2_session_data_backoff_by_high_pri_frame(void)
nghttp2_session_del(session);
}
static void check_session_recv_data_with_padding(const uint8_t *in,
size_t inlen,
size_t datalen)
{
nghttp2_session *session;
my_user_data ud;
nghttp2_session_callbacks callbacks;
memset(&callbacks, 0, sizeof(callbacks));
callbacks.on_frame_recv_callback = on_frame_recv_callback;
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
nghttp2_session_server_new(&session, &callbacks, &ud);
nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE,
NGHTTP2_PRI_DEFAULT, NGHTTP2_STREAM_OPENING,
NULL);
ud.frame_recv_cb_called = 0;
ud.data_chunk_len = 0;
CU_ASSERT((ssize_t)inlen == nghttp2_session_mem_recv(session, in, inlen));
CU_ASSERT(1 == ud.frame_recv_cb_called);
CU_ASSERT(datalen == ud.data_chunk_len);
nghttp2_session_del(session);
}
void test_nghttp2_session_pack_data_with_padding(void)
{
nghttp2_session *session;
my_user_data ud;
nghttp2_session_callbacks callbacks;
nghttp2_data_provider data_prd;
nghttp2_private_data *frame;
size_t datalen = 55;
memset(&callbacks, 0, sizeof(callbacks));
callbacks.send_callback = block_count_send_callback;
callbacks.on_frame_send_callback = on_frame_send_callback;
callbacks.select_padding_callback = select_padding_callback;
data_prd.read_callback = fixed_length_data_source_read_callback;
nghttp2_session_client_new(&session, &callbacks, &ud);
ud.padding_boundary = 512;
nghttp2_submit_request(session, NGHTTP2_PRI_DEFAULT, NULL, 0, &data_prd,
NULL);
ud.block_count = 1;
ud.data_source_length = datalen;
/* Sends HEADERS */
CU_ASSERT(0 == nghttp2_session_send(session));
CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type);
frame = OB_DATA(session->aob.item);
CU_ASSERT(ud.padding_boundary - datalen == frame->padlen);
CU_ASSERT(frame->hd.flags & NGHTTP2_FLAG_PAD_LOW);
CU_ASSERT(frame->hd.flags & NGHTTP2_FLAG_PAD_HIGH);
/* Check reception of this DATA frame */
check_session_recv_data_with_padding
(session->aob.framebuf + session->aob.framebufoff,
session->aob.framebufmark - session->aob.framebufoff,
datalen);
nghttp2_session_del(session);
/* Check without PAD_HIGH */
nghttp2_session_client_new(&session, &callbacks, &ud);
ud.padding_boundary = 64;
nghttp2_submit_request(session, NGHTTP2_PRI_DEFAULT, NULL, 0, &data_prd,
NULL);
ud.block_count = 1;
ud.data_source_length = datalen;
/* Sends HEADERS */
CU_ASSERT(0 == nghttp2_session_send(session));
CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type);
frame = OB_DATA(session->aob.item);
CU_ASSERT((frame->padlen + datalen) % ud.padding_boundary == 0);
CU_ASSERT(frame->hd.flags & NGHTTP2_FLAG_PAD_LOW);
CU_ASSERT(0 == (frame->hd.flags & NGHTTP2_FLAG_PAD_HIGH));
/* Check reception of this DATA frame */
check_session_recv_data_with_padding
(session->aob.framebuf + session->aob.framebufoff,
session->aob.framebufmark - session->aob.framebufoff,
datalen);
nghttp2_session_del(session);
}
void test_nghttp2_session_pack_headers_with_padding(void)
{
nghttp2_session *session, *sv_session;
accumulator acc;
my_user_data ud;
nghttp2_session_callbacks callbacks;
nghttp2_nv nva[8190];
size_t i;
for(i = 0; i < ARRLEN(nva); ++i) {
nva[i].name = (uint8_t*)":path";
nva[i].namelen = 5;
nva[i].value = (uint8_t*)"/";
nva[i].valuelen = 1;
}
memset(&callbacks, 0, sizeof(callbacks));
callbacks.send_callback = accumulator_send_callback;
callbacks.on_frame_send_callback = on_frame_send_callback;
callbacks.select_padding_callback = select_padding_callback;
callbacks.on_frame_recv_callback = on_frame_recv_callback;
acc.length = 0;
ud.acc = &acc;
nghttp2_session_client_new(&session, &callbacks, &ud);
nghttp2_session_server_new(&sv_session, &callbacks, &ud);
ud.padding_boundary = 16385;
CU_ASSERT(0 ==
nghttp2_submit_request(session, NGHTTP2_PRI_DEFAULT,
nva, ARRLEN(nva), NULL, NULL));
CU_ASSERT(0 == nghttp2_session_send(session));
CU_ASSERT(acc.length > NGHTTP2_MAX_FRAME_LENGTH);
ud.frame_recv_cb_called = 0;
CU_ASSERT((ssize_t)acc.length ==
nghttp2_session_mem_recv(sv_session, acc.buf, acc.length));
CU_ASSERT(1 == ud.frame_recv_cb_called);
CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(sv_session));
/* Check PUSH_PROMISE */
CU_ASSERT(0 ==
nghttp2_submit_push_promise(sv_session, NGHTTP2_FLAG_NONE, 1,
nva, ARRLEN(nva)));
acc.length = 0;
CU_ASSERT(0 == nghttp2_session_send(sv_session));
CU_ASSERT(acc.length > NGHTTP2_MAX_FRAME_LENGTH);
ud.frame_recv_cb_called = 0;
CU_ASSERT((ssize_t)acc.length ==
nghttp2_session_mem_recv(session, acc.buf, acc.length));
CU_ASSERT(1 == ud.frame_recv_cb_called);
CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
nghttp2_session_del(sv_session);
nghttp2_session_del(session);
}
void test_nghttp2_pack_settings_payload(void)
{
nghttp2_settings_entry iv[2];
@ -3992,23 +4107,23 @@ void test_nghttp2_pack_settings_payload(void)
nghttp2_settings_entry *resiv;
size_t resniv;
iv[0].settings_id = NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS;
iv[0].value = 1;
iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
iv[0].value = 1023;
iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
iv[1].value = 4095;
len = nghttp2_pack_settings_payload(buf, sizeof(buf), iv, 2);
CU_ASSERT(16 == len);
CU_ASSERT(2 * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH == len);
CU_ASSERT(0 == nghttp2_frame_unpack_settings_payload2(&resiv, &resniv,
buf, len));
CU_ASSERT(2 == resniv);
CU_ASSERT(NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS == resiv[0].settings_id);
CU_ASSERT(1 == resiv[0].value);
CU_ASSERT(NGHTTP2_SETTINGS_HEADER_TABLE_SIZE == resiv[0].settings_id);
CU_ASSERT(1023 == resiv[0].value);
CU_ASSERT(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE == resiv[1].settings_id);
CU_ASSERT(4095 == resiv[1].value);
free(resiv);
len = nghttp2_pack_settings_payload(buf, 15 /* too small */, iv, 2);
len = nghttp2_pack_settings_payload(buf, 9 /* too small */, iv, 2);
CU_ASSERT(NGHTTP2_ERR_INSUFF_BUFSIZE == len);
}

View File

@ -80,9 +80,8 @@ void test_nghttp2_session_stream_close_on_headers_push(void);
void test_nghttp2_session_stop_data_with_rst_stream(void);
void test_nghttp2_session_defer_data(void);
void test_nghttp2_session_flow_control(void);
void test_nghttp2_session_flow_control_disable_remote(void);
void test_nghttp2_session_flow_control_disable_local(void);
void test_nghttp2_session_flow_control_data_recv(void);
void test_nghttp2_session_flow_control_data_with_padding_recv(void);
void test_nghttp2_session_data_read_temporal_failure(void);
void test_nghttp2_session_on_stream_close(void);
void test_nghttp2_session_on_ctrl_not_send(void);
@ -90,6 +89,8 @@ void test_nghttp2_session_get_outbound_queue_size(void);
void test_nghttp2_session_get_effective_local_window_size(void);
void test_nghttp2_session_set_option(void);
void test_nghttp2_session_data_backoff_by_high_pri_frame(void);
void test_nghttp2_session_pack_data_with_padding(void);
void test_nghttp2_session_pack_headers_with_padding(void);
void test_nghttp2_pack_settings_payload(void);
#endif /* NGHTTP2_SESSION_TEST_H */

View File

@ -33,11 +33,15 @@ int unpack_frame(nghttp2_frame *frame, const uint8_t *in, size_t len)
ssize_t rv = 0;
const uint8_t *payload = in + NGHTTP2_FRAME_HEAD_LENGTH;
size_t payloadlen = len - NGHTTP2_FRAME_HEAD_LENGTH;
size_t payloadoff;
nghttp2_frame_unpack_frame_hd(&frame->hd, in);
switch(frame->hd.type) {
case NGHTTP2_HEADERS:
payloadoff = ((frame->hd.flags & NGHTTP2_FLAG_PAD_HIGH) > 0) +
((frame->hd.flags & NGHTTP2_FLAG_PAD_LOW) > 0);
rv = nghttp2_frame_unpack_headers_payload
(&frame->headers, payload, payloadlen);
(&frame->headers, payload + payloadoff, payloadlen - payloadoff);
break;
case NGHTTP2_PRIORITY:
nghttp2_frame_unpack_priority_payload

View File

@ -36,6 +36,24 @@
{ (uint8_t*)NAME, (uint8_t*)VALUE, strlen(NAME), strlen(VALUE) }
#define ARRLEN(ARR) (sizeof(ARR)/sizeof(ARR[0]))
#define assert_nv_equal(A, B, len) \
do { \
size_t alloclen = sizeof(nghttp2_nv) * len; \
nghttp2_nv *sa = A, *sb = B; \
nghttp2_nv *a = malloc(alloclen); \
nghttp2_nv *b = malloc(alloclen); \
ssize_t i_; \
memcpy(a, sa, alloclen); \
memcpy(b, sb, alloclen); \
nghttp2_nv_array_sort(a, len); \
nghttp2_nv_array_sort(b, len); \
for(i_ = 0; i_ < (ssize_t)len; ++i_) { \
CU_ASSERT(nghttp2_nv_equal(&a[i_], &b[i_])); \
} \
free(b); \
free(a); \
} while(0);
int unpack_frame(nghttp2_frame *frame, const uint8_t *in, size_t len);
int strmemeq(const char *a, const uint8_t *b, size_t bn);