Merge pull request #1728 from nghttp2/rfc9218-ext-priority

Implement RFC 9218 extensible prioritization scheme
This commit is contained in:
Tatsuhiro Tsujikawa 2022-06-12 17:32:23 +09:00 committed by GitHub
commit c44caa0580
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 1974 additions and 38 deletions

View File

@ -68,6 +68,7 @@ HEADERS = [
('proxy-connection', None),
('upgrade', None),
(':protocol', None),
('priority', None),
]
def to_enum_hd(k):

View File

@ -23,6 +23,7 @@ set(NGHTTP2_SOURCES
nghttp2_mem.c
nghttp2_http.c
nghttp2_rcbuf.c
nghttp2_extpri.c
nghttp2_debug.c
)

View File

@ -50,6 +50,7 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \
nghttp2_mem.c \
nghttp2_http.c \
nghttp2_rcbuf.c \
nghttp2_extpri.c \
nghttp2_debug.c
HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
@ -66,6 +67,7 @@ HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
nghttp2_mem.h \
nghttp2_http.h \
nghttp2_rcbuf.h \
nghttp2_extpri.h \
nghttp2_debug.h
libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS)

View File

@ -705,8 +705,7 @@ typedef enum {
*/
NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x08,
/**
* SETTINGS_NO_RFC7540_PRIORITIES (`RFC 9218
* <https://datatracker.ietf.org/doc/html/rfc9218>`_)
* SETTINGS_NO_RFC7540_PRIORITIES (:rfc:`9218`)
*/
NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES = 0x09
} nghttp2_settings_id;
@ -4233,6 +4232,61 @@ nghttp2_submit_priority(nghttp2_session *session, uint8_t flags,
int32_t stream_id,
const nghttp2_priority_spec *pri_spec);
/**
* @macro
*
* :macro:`NGHTTP2_EXTPRI_DEFAULT_URGENCY` is the default urgency
* level for :rfc:`9218` extensible priorities.
*/
#define NGHTTP2_EXTPRI_DEFAULT_URGENCY 3
/**
* @macro
*
* :macro:`NGHTTP2_EXTPRI_URGENCY_HIGH` is the highest urgency level
* for :rfc:`9218` extensible priorities.
*/
#define NGHTTP2_EXTPRI_URGENCY_HIGH 0
/**
* @macro
*
* :macro:`NGHTTP2_EXTPRI_URGENCY_LOW` is the lowest urgency level for
* :rfc:`9218` extensible priorities.
*/
#define NGHTTP2_EXTPRI_URGENCY_LOW 7
/**
* @macro
*
* :macro:`NGHTTP2_EXTPRI_URGENCY_LEVELS` is the number of urgency
* levels for :rfc:`9218` extensible priorities.
*/
#define NGHTTP2_EXTPRI_URGENCY_LEVELS (NGHTTP2_EXTPRI_URGENCY_LOW + 1)
/**
* @struct
*
* :type:`nghttp2_extpri` is :rfc:`9218` extensible priorities
* specification for a stream.
*/
typedef struct nghttp2_extpri {
/**
* :member:`urgency` is the urgency of a stream, it must be in
* [:macro:`NGHTTP2_EXTPRI_URGENCY_HIGH`,
* :macro:`NGHTTP2_EXTPRI_URGENCY_LOW`], inclusive, and 0 is the
* highest urgency.
*/
uint32_t urgency;
/**
* :member:`inc` indicates that a content can be processed
* incrementally or not. If inc is 0, it cannot be processed
* incrementally. If inc is 1, it can be processed incrementally.
* Other value is not permitted.
*/
int inc;
} nghttp2_extpri;
/**
* @function
*

35
lib/nghttp2_extpri.c Normal file
View File

@ -0,0 +1,35 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2022 nghttp3 contributors
* Copyright (c) 2022 nghttp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "nghttp2_extpri.h"
uint8_t nghttp2_extpri_to_uint8(const nghttp2_extpri *extpri) {
return (uint8_t)((uint32_t)extpri->inc << 7 | extpri->urgency);
}
void nghttp2_extpri_from_uint8(nghttp2_extpri *extpri, uint8_t u8extpri) {
extpri->urgency = nghttp2_extpri_uint8_urgency(u8extpri);
extpri->inc = nghttp2_extpri_uint8_inc(u8extpri);
}

65
lib/nghttp2_extpri.h Normal file
View File

@ -0,0 +1,65 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2022 nghttp3 contributors
* Copyright (c) 2022 nghttp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef NGHTTP2_EXTPRI_H
#define NGHTTP2_EXTPRI_H
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
/*
* NGHTTP2_EXTPRI_INC_MASK is a bit mask to retrieve incremental bit
* from a value produced by nghttp2_extpri_to_uint8.
*/
#define NGHTTP2_EXTPRI_INC_MASK (1 << 7)
/*
* nghttp2_extpri_to_uint8 encodes |pri| into uint8_t variable.
*/
uint8_t nghttp2_extpri_to_uint8(const nghttp2_extpri *extpri);
/*
* nghttp2_extpri_from_uint8 decodes |u8extpri|, which is produced by
* nghttp2_extpri_to_uint8, intto |extpri|.
*/
void nghttp2_extpri_from_uint8(nghttp2_extpri *extpri, uint8_t u8extpri);
/*
* nghttp2_extpri_uint8_urgency extracts urgency from |PRI| which is
* supposed to be constructed by nghttp2_extpri_to_uint8.
*/
#define nghttp2_extpri_uint8_urgency(PRI) \
((uint32_t)((PRI) & ~NGHTTP2_EXTPRI_INC_MASK))
/*
* nghttp2_extpri_uint8_inc extracts inc from |PRI| which is supposed to
* be constructed by nghttp2_extpri_to_uint8.
*/
#define nghttp2_extpri_uint8_inc(PRI) (((PRI)&NGHTTP2_EXTPRI_INC_MASK) != 0)
#endif /* NGHTTP2_EXTPRI_H */

View File

@ -269,6 +269,11 @@ static int32_t lookup_token(const uint8_t *name, size_t namelen) {
return NGHTTP2_TOKEN_LOCATION;
}
break;
case 'y':
if (memeq("priorit", name, 7)) {
return NGHTTP2_TOKEN_PRIORITY;
}
break;
}
break;
case 9:

View File

@ -112,6 +112,7 @@ typedef enum {
NGHTTP2_TOKEN_PROXY_CONNECTION,
NGHTTP2_TOKEN_UPGRADE,
NGHTTP2_TOKEN__PROTOCOL,
NGHTTP2_TOKEN_PRIORITY,
} nghttp2_token;
struct nghttp2_hd_entry;

View File

@ -30,6 +30,7 @@
#include "nghttp2_hd.h"
#include "nghttp2_helper.h"
#include "nghttp2_extpri.h"
static uint8_t downcase(uint8_t c) {
return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
@ -114,6 +115,8 @@ static int check_path(nghttp2_stream *stream) {
static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
int trailer, int connect_protocol) {
nghttp2_extpri extpri;
if (nv->name->base[0] == ':') {
if (trailer ||
(stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
@ -212,6 +215,16 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
return NGHTTP2_ERR_HTTP_HEADER;
}
break;
case NGHTTP2_TOKEN_PRIORITY:
if (!trailer &&
(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES)) {
nghttp2_extpri_from_uint8(&extpri, stream->http_extpri);
if (nghttp2_http_parse_priority(&extpri, nv->value->base,
nv->value->len) == 0) {
stream->http_extpri = nghttp2_extpri_to_uint8(&extpri);
}
}
break;
default:
if (nv->name->base[0] == ':') {
return NGHTTP2_ERR_HTTP_HEADER;
@ -541,3 +554,715 @@ void nghttp2_http_record_request_method(nghttp2_stream *stream,
return;
}
}
/* Generated by genchartbl.py */
static const int SF_KEY_CHARS[] = {
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */,
0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */,
0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */,
0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */,
0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */,
0 /* RS */, 0 /* US */, 0 /* SPC */, 0 /* ! */, 0 /* " */,
0 /* # */, 0 /* $ */, 0 /* % */, 0 /* & */, 0 /* ' */,
0 /* ( */, 0 /* ) */, 1 /* * */, 0 /* + */, 0 /* , */,
1 /* - */, 1 /* . */, 0 /* / */, 1 /* 0 */, 1 /* 1 */,
1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */,
1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */,
0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */,
0 /* A */, 0 /* B */, 0 /* C */, 0 /* D */, 0 /* E */,
0 /* F */, 0 /* G */, 0 /* H */, 0 /* I */, 0 /* J */,
0 /* K */, 0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */,
0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */, 0 /* T */,
0 /* U */, 0 /* V */, 0 /* W */, 0 /* X */, 0 /* Y */,
0 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 0 /* ^ */,
1 /* _ */, 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */,
1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */,
1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */,
1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 0 /* | */,
0 /* } */, 0 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */,
0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */,
0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */,
0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */,
0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */,
0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */,
0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */,
0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */,
0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */,
0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */,
0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */,
0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */,
0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */,
0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */,
0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */,
0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */,
0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */,
0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */,
0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */,
0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */,
0 /* 0xff */,
};
static ssize_t sf_parse_key(const uint8_t *begin, const uint8_t *end) {
const uint8_t *p = begin;
if ((*p < 'a' || 'z' < *p) && *p != '*') {
return -1;
}
for (; p != end && SF_KEY_CHARS[*p]; ++p)
;
return p - begin;
}
static ssize_t sf_parse_integer_or_decimal(nghttp2_sf_value *dest,
const uint8_t *begin,
const uint8_t *end) {
const uint8_t *p = begin;
int sign = 1;
int64_t value = 0;
int type = NGHTTP2_SF_VALUE_TYPE_INTEGER;
size_t len = 0;
size_t fpos = 0;
size_t i;
if (*p == '-') {
if (++p == end) {
return -1;
}
sign = -1;
}
if (*p < '0' || '9' < *p) {
return -1;
}
for (; p != end; ++p) {
switch (*p) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
value *= 10;
value += *p - '0';
if (++len > 15) {
return -1;
}
break;
case '.':
if (type != NGHTTP2_SF_VALUE_TYPE_INTEGER) {
goto fin;
}
if (len > 12) {
return -1;
}
fpos = len;
type = NGHTTP2_SF_VALUE_TYPE_DECIMAL;
break;
default:
goto fin;
};
}
fin:
switch (type) {
case NGHTTP2_SF_VALUE_TYPE_INTEGER:
if (dest) {
dest->type = (uint8_t)type;
dest->i = value * sign;
}
return p - begin;
case NGHTTP2_SF_VALUE_TYPE_DECIMAL:
if (fpos == len || len - fpos > 3) {
return -1;
}
if (dest) {
dest->type = (uint8_t)type;
dest->d = (double)value;
for (i = len - fpos; i > 0; --i) {
dest->d /= (double)10;
}
dest->d *= sign;
}
return p - begin;
default:
assert(0);
abort();
}
}
/* Generated by genchartbl.py */
static const int SF_DQUOTE_CHARS[] = {
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */,
0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */,
0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */,
0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */,
0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */,
0 /* RS */, 0 /* US */, 1 /* SPC */, 1 /* ! */, 0 /* " */,
1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, 1 /* , */,
1 /* - */, 1 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */,
1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */,
1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */,
1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */, 1 /* @ */,
1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */,
1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */,
1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */,
1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */,
1 /* Z */, 1 /* [ */, 0 /* \ */, 1 /* ] */, 1 /* ^ */,
1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */,
1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */,
1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */,
1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */, 1 /* | */,
1 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */,
0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */,
0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */,
0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */,
0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */,
0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */,
0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */,
0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */,
0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */,
0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */,
0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */,
0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */,
0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */,
0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */,
0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */,
0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */,
0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */,
0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */,
0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */,
0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */,
0 /* 0xff */,
};
static ssize_t sf_parse_string(nghttp2_sf_value *dest, const uint8_t *begin,
const uint8_t *end) {
const uint8_t *p = begin;
if (*p++ != '"') {
return -1;
}
for (; p != end; ++p) {
switch (*p) {
case '\\':
if (++p == end) {
return -1;
}
switch (*p) {
case '"':
case '\\':
break;
default:
return -1;
}
break;
case '"':
if (dest) {
dest->type = NGHTTP2_SF_VALUE_TYPE_STRING;
dest->s.base = begin + 1;
dest->s.len = (size_t)(p - dest->s.base);
}
++p;
return p - begin;
default:
if (!SF_DQUOTE_CHARS[*p]) {
return -1;
}
}
}
return -1;
}
/* Generated by genchartbl.py */
static const int SF_TOKEN_CHARS[] = {
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */,
0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */,
0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */,
0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */,
0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */,
0 /* RS */, 0 /* US */, 0 /* SPC */, 1 /* ! */, 0 /* " */,
1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */, 0 /* , */,
1 /* - */, 1 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */,
1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */,
1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 0 /* ; */,
0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */,
1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */,
1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */,
1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */,
1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */,
1 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 1 /* ^ */,
1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */,
1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */,
1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */,
1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 1 /* | */,
0 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */,
0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */,
0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */,
0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */,
0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */,
0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */,
0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */,
0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */,
0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */,
0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */,
0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */,
0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */,
0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */,
0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */,
0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */,
0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */,
0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */,
0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */,
0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */,
0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */,
0 /* 0xff */,
};
static ssize_t sf_parse_token(nghttp2_sf_value *dest, const uint8_t *begin,
const uint8_t *end) {
const uint8_t *p = begin;
if ((*p < 'A' || 'Z' < *p) && (*p < 'a' || 'z' < *p) && *p != '*') {
return -1;
}
for (; p != end && SF_TOKEN_CHARS[*p]; ++p)
;
if (dest) {
dest->type = NGHTTP2_SF_VALUE_TYPE_TOKEN;
dest->s.base = begin;
dest->s.len = (size_t)(p - begin);
}
return p - begin;
}
/* Generated by genchartbl.py */
static const int SF_BYTESEQ_CHARS[] = {
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */,
0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */,
0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */,
0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */,
0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */,
0 /* RS */, 0 /* US */, 0 /* SPC */, 0 /* ! */, 0 /* " */,
0 /* # */, 0 /* $ */, 0 /* % */, 0 /* & */, 0 /* ' */,
0 /* ( */, 0 /* ) */, 0 /* * */, 1 /* + */, 0 /* , */,
0 /* - */, 0 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */,
1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */,
1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */,
0 /* < */, 1 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */,
1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */,
1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */,
1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */,
1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */,
1 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 0 /* ^ */,
0 /* _ */, 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */,
1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */,
1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */,
1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 0 /* | */,
0 /* } */, 0 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */,
0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */,
0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */,
0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */,
0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */,
0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */,
0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */,
0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */,
0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */,
0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */,
0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */,
0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */,
0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */,
0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */,
0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */,
0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */,
0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */,
0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */,
0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */,
0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */,
0 /* 0xff */,
};
static ssize_t sf_parse_byteseq(nghttp2_sf_value *dest, const uint8_t *begin,
const uint8_t *end) {
const uint8_t *p = begin;
if (*p++ != ':') {
return -1;
}
for (; p != end; ++p) {
switch (*p) {
case ':':
if (dest) {
dest->type = NGHTTP2_SF_VALUE_TYPE_BYTESEQ;
dest->s.base = begin + 1;
dest->s.len = (size_t)(p - dest->s.base);
}
++p;
return p - begin;
default:
if (!SF_BYTESEQ_CHARS[*p]) {
return -1;
}
}
}
return -1;
}
static ssize_t sf_parse_boolean(nghttp2_sf_value *dest, const uint8_t *begin,
const uint8_t *end) {
const uint8_t *p = begin;
int b;
if (*p++ != '?') {
return -1;
}
if (p == end) {
return -1;
}
switch (*p++) {
case '0':
b = 0;
break;
case '1':
b = 1;
break;
default:
return -1;
}
if (dest) {
dest->type = NGHTTP2_SF_VALUE_TYPE_BOOLEAN;
dest->b = b;
}
return p - begin;
}
static ssize_t sf_parse_bare_item(nghttp2_sf_value *dest, const uint8_t *begin,
const uint8_t *end) {
switch (*begin) {
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return sf_parse_integer_or_decimal(dest, begin, end);
case '"':
return sf_parse_string(dest, begin, end);
case '*':
return sf_parse_token(dest, begin, end);
case ':':
return sf_parse_byteseq(dest, begin, end);
case '?':
return sf_parse_boolean(dest, begin, end);
default:
if (('A' <= *begin && *begin <= 'Z') || ('a' <= *begin && *begin <= 'z')) {
return sf_parse_token(dest, begin, end);
}
return -1;
}
}
#define sf_discard_sp_end_err(BEGIN, END, ERR) \
for (;; ++(BEGIN)) { \
if ((BEGIN) == (END)) { \
return (ERR); \
} \
if (*(BEGIN) != ' ') { \
break; \
} \
}
static ssize_t sf_parse_params(const uint8_t *begin, const uint8_t *end) {
const uint8_t *p = begin;
ssize_t slen;
for (; p != end && *p == ';';) {
++p;
sf_discard_sp_end_err(p, end, -1);
slen = sf_parse_key(p, end);
if (slen < 0) {
return -1;
}
p += slen;
if (p == end || *p != '=') {
/* Boolean true */
} else if (++p == end) {
return -1;
} else {
slen = sf_parse_bare_item(NULL, p, end);
if (slen < 0) {
return -1;
}
p += slen;
}
}
return p - begin;
}
static ssize_t sf_parse_item(nghttp2_sf_value *dest, const uint8_t *begin,
const uint8_t *end) {
const uint8_t *p = begin;
ssize_t slen;
slen = sf_parse_bare_item(dest, p, end);
if (slen < 0) {
return -1;
}
p += slen;
slen = sf_parse_params(p, end);
if (slen < 0) {
return -1;
}
p += slen;
return p - begin;
}
ssize_t nghttp2_sf_parse_item(nghttp2_sf_value *dest, const uint8_t *begin,
const uint8_t *end) {
return sf_parse_item(dest, begin, end);
}
static ssize_t sf_parse_inner_list(nghttp2_sf_value *dest, const uint8_t *begin,
const uint8_t *end) {
const uint8_t *p = begin;
ssize_t slen;
if (*p++ != '(') {
return -1;
}
for (;;) {
sf_discard_sp_end_err(p, end, -1);
if (*p == ')') {
++p;
slen = sf_parse_params(p, end);
if (slen < 0) {
return -1;
}
p += slen;
if (dest) {
dest->type = NGHTTP2_SF_VALUE_TYPE_INNER_LIST;
}
return p - begin;
}
slen = sf_parse_item(NULL, p, end);
if (slen < 0) {
return -1;
}
p += slen;
if (p == end || (*p != ' ' && *p != ')')) {
return -1;
}
}
}
ssize_t nghttp2_sf_parse_inner_list(nghttp2_sf_value *dest,
const uint8_t *begin, const uint8_t *end) {
return sf_parse_inner_list(dest, begin, end);
}
static ssize_t sf_parse_item_or_inner_list(nghttp2_sf_value *dest,
const uint8_t *begin,
const uint8_t *end) {
if (*begin == '(') {
return sf_parse_inner_list(dest, begin, end);
}
return sf_parse_item(dest, begin, end);
}
#define sf_discard_ows(BEGIN, END) \
for (;; ++(BEGIN)) { \
if ((BEGIN) == (END)) { \
goto fin; \
} \
if (*(BEGIN) != ' ' && *(BEGIN) != '\t') { \
break; \
} \
}
#define sf_discard_ows_end_err(BEGIN, END, ERR) \
for (;; ++(BEGIN)) { \
if ((BEGIN) == (END)) { \
return (ERR); \
} \
if (*(BEGIN) != ' ' && *(BEGIN) != '\t') { \
break; \
} \
}
int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value,
size_t valuelen) {
const uint8_t *p = value, *end = value + valuelen;
ssize_t slen;
nghttp2_sf_value val;
nghttp2_extpri pri = *dest;
const uint8_t *key;
size_t keylen;
for (; p != end && *p == ' '; ++p)
;
for (; p != end;) {
slen = sf_parse_key(p, end);
if (slen < 0) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
key = p;
keylen = (size_t)slen;
p += slen;
if (p == end || *p != '=') {
/* Boolean true */
val.type = NGHTTP2_SF_VALUE_TYPE_BOOLEAN;
val.b = 1;
slen = sf_parse_params(p, end);
if (slen < 0) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
} else if (++p == end) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
} else {
slen = sf_parse_item_or_inner_list(&val, p, end);
if (slen < 0) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
}
p += slen;
if (keylen == 1) {
switch (key[0]) {
case 'i':
if (val.type != NGHTTP2_SF_VALUE_TYPE_BOOLEAN) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
pri.inc = val.b;
break;
case 'u':
if (val.type != NGHTTP2_SF_VALUE_TYPE_INTEGER ||
val.i < NGHTTP2_EXTPRI_URGENCY_HIGH ||
NGHTTP2_EXTPRI_URGENCY_LOW < val.i) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
pri.urgency = (uint32_t)val.i;
break;
}
}
sf_discard_ows(p, end);
if (*p++ != ',') {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
sf_discard_ows_end_err(p, end, NGHTTP2_ERR_INVALID_ARGUMENT);
}
fin:
*dest = pri;
return 0;
}

View File

@ -94,4 +94,55 @@ int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n);
void nghttp2_http_record_request_method(nghttp2_stream *stream,
nghttp2_frame *frame);
/*
* RFC 8941 Structured Field Values.
*/
typedef enum nghttp2_sf_value_type {
NGHTTP2_SF_VALUE_TYPE_BOOLEAN,
NGHTTP2_SF_VALUE_TYPE_INTEGER,
NGHTTP2_SF_VALUE_TYPE_DECIMAL,
NGHTTP2_SF_VALUE_TYPE_STRING,
NGHTTP2_SF_VALUE_TYPE_TOKEN,
NGHTTP2_SF_VALUE_TYPE_BYTESEQ,
NGHTTP2_SF_VALUE_TYPE_INNER_LIST,
} nghttp2_sf_value_type;
/*
* nghttp2_sf_value stores Structured Field Values item. For Inner
* List, only type is set to NGHTTP2_SF_VALUE_TYPE_INNER_LIST.
*/
typedef struct nghttp2_sf_value {
uint8_t type;
union {
int b;
int64_t i;
double d;
struct {
const uint8_t *base;
size_t len;
} s;
};
} nghttp2_sf_value;
/*
* nghttp2_sf_parse_item parses the input sequence [|begin|, |end|)
* and stores the parsed an Item in |dest|. It returns the number of
* bytes consumed if it succeeds, or -1. This function is declared
* here for unit tests.
*/
ssize_t nghttp2_sf_parse_item(nghttp2_sf_value *dest, const uint8_t *begin,
const uint8_t *end);
/*
* nghttp2_sf_parse_inner_list parses the input sequence [|begin|, |end|)
* and stores the parsed an Inner List in |dest|. It returns the number of
* bytes consumed if it succeeds, or -1. This function is declared
* here for unit tests.
*/
ssize_t nghttp2_sf_parse_inner_list(nghttp2_sf_value *dest,
const uint8_t *begin, const uint8_t *end);
int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value,
size_t valuelen);
#endif /* NGHTTP2_HTTP_H */

View File

@ -36,6 +36,7 @@
#include "nghttp2_option.h"
#include "nghttp2_http.h"
#include "nghttp2_pq.h"
#include "nghttp2_extpri.h"
#include "nghttp2_debug.h"
/*
@ -399,13 +400,19 @@ static void active_outbound_item_reset(nghttp2_active_outbound_item *aob,
aob->state = NGHTTP2_OB_POP_ITEM;
}
#define NGHTTP2_STREAM_MAX_CYCLE_GAP ((uint64_t)NGHTTP2_MAX_FRAME_SIZE_MAX)
static int stream_less(const void *lhsx, const void *rhsx) {
const nghttp2_stream *lhs, *rhs;
lhs = nghttp2_struct_of(lhsx, nghttp2_stream, pq_entry);
rhs = nghttp2_struct_of(rhsx, nghttp2_stream, pq_entry);
if (lhs->cycle == rhs->cycle) {
return lhs->seq < rhs->seq;
}
return rhs->cycle - lhs->cycle <= NGHTTP2_STREAM_MAX_CYCLE_GAP;
}
int nghttp2_enable_strict_preface = 1;
@ -418,6 +425,7 @@ static int session_new(nghttp2_session **session_ptr,
size_t nbuffer;
size_t max_deflate_dynamic_table_size =
NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE;
size_t i;
if (mem == NULL) {
mem = nghttp2_mem_default();
@ -595,7 +603,9 @@ static int session_new(nghttp2_session **session_ptr,
}
}
nghttp2_pq_init(&(*session_ptr)->ob_data, stream_less, mem);
for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) {
nghttp2_pq_init(&(*session_ptr)->sched[i].ob_data, stream_less, mem);
}
return 0;
@ -748,6 +758,7 @@ static void inflight_settings_del(nghttp2_inflight_settings *settings,
void nghttp2_session_del(nghttp2_session *session) {
nghttp2_mem *mem;
nghttp2_inflight_settings *settings;
size_t i;
if (session == NULL) {
return;
@ -761,7 +772,9 @@ void nghttp2_session_del(nghttp2_session *session) {
settings = next;
}
nghttp2_pq_free(&session->ob_data);
for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) {
nghttp2_pq_free(&session->sched[i].ob_data);
}
nghttp2_stream_free(&session->root);
/* Have to free streams first, so that we can check
@ -857,14 +870,40 @@ int nghttp2_session_reprioritize_stream(
return 0;
}
static uint64_t pq_get_first_cycle(nghttp2_pq *pq) {
nghttp2_stream *stream;
if (nghttp2_pq_empty(pq)) {
return 0;
}
stream = nghttp2_struct_of(nghttp2_pq_top(pq), nghttp2_stream, pq_entry);
return stream->cycle;
}
static int session_ob_data_push(nghttp2_session *session,
nghttp2_stream *stream) {
int rv;
uint32_t urgency;
int inc;
nghttp2_pq *pq;
assert(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES);
assert(stream->queued == 0);
rv = nghttp2_pq_push(&session->ob_data, &stream->pq_entry);
urgency = nghttp2_extpri_uint8_urgency(stream->extpri);
inc = nghttp2_extpri_uint8_inc(stream->extpri);
assert(urgency < NGHTTP2_EXTPRI_URGENCY_LEVELS);
pq = &session->sched[urgency].ob_data;
stream->cycle = pq_get_first_cycle(pq);
if (inc) {
stream->cycle += stream->last_writelen;
}
rv = nghttp2_pq_push(pq, &stream->pq_entry);
if (rv != 0) {
return rv;
}
@ -876,10 +915,16 @@ static int session_ob_data_push(nghttp2_session *session,
static int session_ob_data_remove(nghttp2_session *session,
nghttp2_stream *stream) {
uint32_t urgency;
assert(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES);
assert(stream->queued == 1);
nghttp2_pq_remove(&session->ob_data, &stream->pq_entry);
urgency = nghttp2_extpri_uint8_urgency(stream->extpri);
assert(urgency < NGHTTP2_EXTPRI_URGENCY_LEVELS);
nghttp2_pq_remove(&session->sched[urgency].ob_data, &stream->pq_entry);
stream->queued = 0;
@ -955,6 +1000,84 @@ static int session_resume_deferred_stream_item(nghttp2_session *session,
return session_ob_data_push(session, stream);
}
static nghttp2_outbound_item *
session_sched_get_next_outbound_item(nghttp2_session *session) {
size_t i;
nghttp2_pq_entry *ent;
nghttp2_stream *stream;
for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) {
ent = nghttp2_pq_top(&session->sched[i].ob_data);
if (!ent) {
continue;
}
stream = nghttp2_struct_of(ent, nghttp2_stream, pq_entry);
return stream->item;
}
return NULL;
}
static int session_sched_empty(nghttp2_session *session) {
size_t i;
for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) {
if (!nghttp2_pq_empty(&session->sched[i].ob_data)) {
return 0;
}
}
return 1;
}
static void session_sched_reschedule_stream(nghttp2_session *session,
nghttp2_stream *stream) {
nghttp2_pq *pq;
uint32_t urgency = nghttp2_extpri_uint8_urgency(stream->extpri);
int inc = nghttp2_extpri_uint8_inc(stream->extpri);
uint64_t penalty = (uint64_t)stream->last_writelen;
int rv;
(void)rv;
assert(urgency < NGHTTP2_EXTPRI_URGENCY_LEVELS);
pq = &session->sched[urgency].ob_data;
if (!inc || nghttp2_pq_size(pq) == 1) {
return;
}
nghttp2_pq_remove(pq, &stream->pq_entry);
stream->cycle += penalty;
rv = nghttp2_pq_push(pq, &stream->pq_entry);
assert(0 == rv);
}
static int session_update_stream_priority(nghttp2_session *session,
nghttp2_stream *stream,
uint8_t u8extpri) {
if (stream->extpri == u8extpri) {
return 0;
}
if (stream->queued) {
session_ob_data_remove(session, stream);
stream->extpri = u8extpri;
return session_ob_data_push(session, stream);
}
stream->extpri = u8extpri;
return 0;
}
int nghttp2_session_add_item(nghttp2_session *session,
nghttp2_outbound_item *item) {
/* TODO Return error if stream is not found for the frame requiring
@ -2489,8 +2612,6 @@ static int session_prep_frame(nghttp2_session *session,
nghttp2_outbound_item *
nghttp2_session_get_next_ob_item(nghttp2_session *session) {
nghttp2_outbound_item *item;
nghttp2_pq_entry *ent;
nghttp2_stream *stream;
if (nghttp2_outbound_queue_top(&session->ob_urgent)) {
return nghttp2_outbound_queue_top(&session->ob_urgent);
@ -2512,13 +2633,7 @@ nghttp2_session_get_next_ob_item(nghttp2_session *session) {
return item;
}
ent = nghttp2_pq_top(&session->ob_data);
if (!ent) {
return NULL;
}
stream = nghttp2_struct_of(ent, nghttp2_stream, pq_entry);
return stream->item;
return session_sched_get_next_outbound_item(session);
}
return NULL;
@ -2527,8 +2642,6 @@ nghttp2_session_get_next_ob_item(nghttp2_session *session) {
nghttp2_outbound_item *
nghttp2_session_pop_next_ob_item(nghttp2_session *session) {
nghttp2_outbound_item *item;
nghttp2_pq_entry *ent;
nghttp2_stream *stream;
item = nghttp2_outbound_queue_top(&session->ob_urgent);
if (item) {
@ -2559,13 +2672,7 @@ nghttp2_session_pop_next_ob_item(nghttp2_session *session) {
return item;
}
ent = nghttp2_pq_top(&session->ob_data);
if (!ent) {
return NULL;
}
stream = nghttp2_struct_of(ent, nghttp2_stream, pq_entry);
return stream->item;
return session_sched_get_next_outbound_item(session);
}
return NULL;
@ -2675,10 +2782,20 @@ static int session_close_stream_on_goaway(nghttp2_session *session,
return 0;
}
static void reschedule_stream(nghttp2_stream *stream) {
static void session_reschedule_stream(nghttp2_session *session,
nghttp2_stream *stream) {
stream->last_writelen = stream->item->frame.hd.length;
if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES)) {
nghttp2_stream_reschedule(stream);
return;
}
if (!session->server) {
return;
}
session_sched_reschedule_stream(session, stream);
}
static int session_update_stream_consumed_size(nghttp2_session *session,
@ -3906,6 +4023,19 @@ static int session_end_stream_headers_received(nghttp2_session *session,
nghttp2_frame *frame,
nghttp2_stream *stream) {
int rv;
assert(frame->hd.type == NGHTTP2_HEADERS);
if (session->server && session_enforce_http_messaging(session) &&
frame->headers.cat == NGHTTP2_HCAT_REQUEST &&
(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES)) {
rv = session_update_stream_priority(session, stream, stream->http_extpri);
if (rv != 0) {
assert(nghttp2_is_fatal(rv));
return rv;
}
}
if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
return 0;
}
@ -7080,7 +7210,7 @@ int nghttp2_session_want_write(nghttp2_session *session) {
return session->aob.item || nghttp2_outbound_queue_top(&session->ob_urgent) ||
nghttp2_outbound_queue_top(&session->ob_reg) ||
((!nghttp2_pq_empty(&session->root.obq) ||
!nghttp2_pq_empty(&session->ob_data)) &&
!session_sched_empty(session)) &&
session->remote_window_size > 0) ||
(nghttp2_outbound_queue_top(&session->ob_syn) &&
!session_is_outgoing_concurrent_streams_max(session));
@ -7474,9 +7604,7 @@ int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
return rv;
}
if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES)) {
reschedule_stream(stream);
}
session_reschedule_stream(session, stream);
if (frame->hd.length == 0 && (data_flags & NGHTTP2_DATA_FLAG_EOF) &&
(data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM)) {

View File

@ -203,9 +203,12 @@ struct nghttp2_session {
response) frame, which are subject to
SETTINGS_MAX_CONCURRENT_STREAMS limit. */
nghttp2_outbound_queue ob_syn;
/* Queue for DATA frames which is used when
SETTINGS_NO_RFC7540_PRIORITIES is enabled. */
/* Queues for DATA frames which is used when
SETTINGS_NO_RFC7540_PRIORITIES is enabled. This implements RFC
9218 extensible prioritization scheme. */
struct {
nghttp2_pq ob_data;
} sched[NGHTTP2_EXTPRI_URGENCY_LEVELS];
nghttp2_active_outbound_item aob;
nghttp2_inbound_frame iframe;
nghttp2_hd_deflater hd_deflater;

View File

@ -100,6 +100,8 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
stream->descendant_next_seq = 0;
stream->seq = 0;
stream->last_writelen = 0;
stream->extpri = stream->http_extpri = NGHTTP2_EXTPRI_DEFAULT_URGENCY;
}
void nghttp2_stream_free(nghttp2_stream *stream) {

View File

@ -220,6 +220,12 @@ struct nghttp2_stream {
this stream. The nonzero does not necessarily mean WINDOW_UPDATE
is not queued. */
uint8_t window_update_queued;
/* extpri is a stream priority produced by nghttp2_extpri_to_uint8
used by RFC 9218 extensible priorities. */
uint8_t extpri;
/* http_extpri is a stream priority received in HTTP request header
fields and produced by nghttp2_extpri_to_uint8. */
uint8_t http_extpri;
};
void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,

View File

@ -21,6 +21,8 @@ if(HAVE_CUNIT)
nghttp2_npn_test.c
nghttp2_helper_test.c
nghttp2_buf_test.c
nghttp2_http_test.c
nghttp2_extpri_test.c
)
add_executable(main EXCLUDE_FROM_ALL

View File

@ -40,14 +40,18 @@ OBJECTS = main.c nghttp2_pq_test.c nghttp2_map_test.c nghttp2_queue_test.c \
nghttp2_hd_test.c \
nghttp2_npn_test.c \
nghttp2_helper_test.c \
nghttp2_buf_test.c
nghttp2_buf_test.c \
nghttp2_http_test.c \
nghttp2_extpri_test.c
HFILES = nghttp2_pq_test.h nghttp2_map_test.h nghttp2_queue_test.h \
nghttp2_session_test.h \
nghttp2_frame_test.h nghttp2_stream_test.h nghttp2_hd_test.h \
nghttp2_npn_test.h nghttp2_helper_test.h \
nghttp2_test_helper.h \
nghttp2_buf_test.h
nghttp2_buf_test.h \
nghttp2_http_test.h \
nghttp2_extpri_test.h
main_SOURCES = $(HFILES) $(OBJECTS)

View File

@ -40,6 +40,8 @@
#include "nghttp2_npn_test.h"
#include "nghttp2_helper_test.h"
#include "nghttp2_buf_test.h"
#include "nghttp2_http_test.h"
#include "nghttp2_extpri_test.h"
extern int nghttp2_enable_strict_preface;
@ -93,6 +95,8 @@ int main(void) {
test_nghttp2_session_recv_headers_early_response) ||
!CU_add_test(pSuite, "session_recv_headers_for_closed_stream",
test_nghttp2_session_recv_headers_for_closed_stream) ||
!CU_add_test(pSuite, "session_recv_headers_with_extpri",
test_nghttp2_session_recv_headers_with_extpri) ||
!CU_add_test(pSuite, "session_server_recv_push_response",
test_nghttp2_session_server_recv_push_response) ||
!CU_add_test(pSuite, "session_recv_premature_headers",
@ -427,7 +431,13 @@ int main(void) {
!CU_add_test(pSuite, "bufs_advance", test_nghttp2_bufs_advance) ||
!CU_add_test(pSuite, "bufs_next_present",
test_nghttp2_bufs_next_present) ||
!CU_add_test(pSuite, "bufs_realloc", test_nghttp2_bufs_realloc)) {
!CU_add_test(pSuite, "bufs_realloc", test_nghttp2_bufs_realloc) ||
!CU_add_test(pSuite, "http_parse_priority",
test_nghttp2_http_parse_priority) ||
!CU_add_test(pSuite, "sf_parse_item", test_nghttp2_sf_parse_item) ||
!CU_add_test(pSuite, "sf_parse_inner_list",
test_nghttp2_sf_parse_inner_list) ||
!CU_add_test(pSuite, "extpri_to_uint8", test_nghttp2_extpri_to_uint8)) {
CU_cleanup_registry();
return (int)CU_get_error();
}

View File

@ -0,0 +1,50 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2022 nghttp3 contributors
* Copyright (c) 2022 nghttp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "nghttp2_extpri_test.h"
#include <CUnit/CUnit.h>
#include "nghttp2_extpri.h"
#include "nghttp2_test_helper.h"
void test_nghttp2_extpri_to_uint8(void) {
{
nghttp2_extpri pri = {1, 0};
CU_ASSERT(1 == nghttp2_extpri_to_uint8(&pri));
}
{
nghttp2_extpri pri = {1, 1};
CU_ASSERT((0x80 | 1) == nghttp2_extpri_to_uint8(&pri));
}
{
nghttp2_extpri pri = {7, 1};
CU_ASSERT((0x80 | 7) == nghttp2_extpri_to_uint8(&pri));
}
{
nghttp2_extpri pri = {7, 0};
CU_ASSERT(7 == nghttp2_extpri_to_uint8(&pri));
}
}

View File

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

655
tests/nghttp2_http_test.c Normal file
View File

@ -0,0 +1,655 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2022 nghttp3 contributors
* Copyright (c) 2022 nghttp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "nghttp2_http_test.h"
#include <assert.h>
#include <CUnit/CUnit.h>
#include "nghttp2_http.h"
#include "nghttp2_test_helper.h"
void test_nghttp2_http_parse_priority(void) {
int rv;
{
nghttp2_extpri pri = {(uint32_t)-1, -1};
const uint8_t v[] = "";
rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1);
CU_ASSERT(0 == rv);
CU_ASSERT((uint32_t)-1 == pri.urgency);
CU_ASSERT(-1 == pri.inc);
}
{
nghttp2_extpri pri = {(uint32_t)-1, -1};
const uint8_t v[] = "u=7,i";
rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1);
CU_ASSERT(0 == rv);
CU_ASSERT((uint32_t)7 == pri.urgency);
CU_ASSERT(1 == pri.inc);
}
{
nghttp2_extpri pri = {(uint32_t)-1, -1};
const uint8_t v[] = "u=0,i=?0";
rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1);
CU_ASSERT(0 == rv);
CU_ASSERT((uint32_t)0 == pri.urgency);
CU_ASSERT(0 == pri.inc);
}
{
nghttp2_extpri pri = {(uint32_t)-1, -1};
const uint8_t v[] = "u=3, i";
rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1);
CU_ASSERT(0 == rv);
CU_ASSERT((uint32_t)3 == pri.urgency);
CU_ASSERT(1 == pri.inc);
}
{
nghttp2_extpri pri = {(uint32_t)-1, -1};
const uint8_t v[] = "u=0, i, i=?0, u=6";
rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1);
CU_ASSERT(0 == rv);
CU_ASSERT((uint32_t)6 == pri.urgency);
CU_ASSERT(0 == pri.inc);
}
{
nghttp2_extpri pri = {(uint32_t)-1, -1};
const uint8_t v[] = "u=0,";
rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1);
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
}
{
nghttp2_extpri pri = {(uint32_t)-1, -1};
const uint8_t v[] = "u=0, ";
rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1);
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
}
{
nghttp2_extpri pri = {(uint32_t)-1, -1};
const uint8_t v[] = "u=";
rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1);
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
}
{
nghttp2_extpri pri = {(uint32_t)-1, -1};
const uint8_t v[] = "u";
rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1);
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
}
{
nghttp2_extpri pri = {(uint32_t)-1, -1};
const uint8_t v[] = "i=?1";
rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1);
CU_ASSERT(0 == rv);
CU_ASSERT((uint32_t)-1 == pri.urgency);
CU_ASSERT(1 == pri.inc);
}
{
nghttp2_extpri pri = {(uint32_t)-1, -1};
const uint8_t v[] = "i=?2";
rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1);
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
}
{
nghttp2_extpri pri = {(uint32_t)-1, -1};
const uint8_t v[] = "i=?";
rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1);
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
}
{
nghttp2_extpri pri = {(uint32_t)-1, -1};
const uint8_t v[] = "i=";
rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1);
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
}
{
nghttp2_extpri pri = {(uint32_t)-1, -1};
const uint8_t v[] = "u=-1";
rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1);
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
}
{
nghttp2_extpri pri = {(uint32_t)-1, -1};
const uint8_t v[] = "u=8";
rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1);
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
}
{
nghttp2_extpri pri = {(uint32_t)-1, -1};
const uint8_t v[] =
"i=?0, u=1, a=(x y z), u=2; i=?0;foo=\",,,\", i=?1;i=?0; u=6";
rv = nghttp2_http_parse_priority(&pri, v, sizeof(v) - 1);
CU_ASSERT(0 == rv);
CU_ASSERT((uint32_t)2 == pri.urgency);
CU_ASSERT(1 == pri.inc);
}
{
nghttp2_extpri pri = {(uint32_t)-1, -1};
const uint8_t v[] = {'u', '='};
rv = nghttp2_http_parse_priority(&pri, v, sizeof(v));
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
}
}
void test_nghttp2_sf_parse_item(void) {
{
nghttp2_sf_value val;
const uint8_t s[] = "?1";
val.type = 0xff;
CU_ASSERT(2 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BOOLEAN == val.type);
CU_ASSERT(1 == val.b);
}
{
nghttp2_sf_value val;
const uint8_t s[] = "?1 ";
val.type = 0xff;
CU_ASSERT(2 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BOOLEAN == val.type);
CU_ASSERT(1 == val.b);
}
{
nghttp2_sf_value val;
const uint8_t s[] = "?1;foo=bar";
val.type = 0xff;
CU_ASSERT(10 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BOOLEAN == val.type);
CU_ASSERT(1 == val.b);
}
{
const uint8_t s[] = {'?', '1', ';', 'f', 'o', 'o', '='};
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s)));
}
{
nghttp2_sf_value val;
const uint8_t s[] = "?0";
val.type = 0xff;
CU_ASSERT(2 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BOOLEAN == val.type);
CU_ASSERT(0 == val.b);
}
{
nghttp2_sf_value val;
const uint8_t s[] = "?0 ";
val.type = 0xff;
CU_ASSERT(2 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BOOLEAN == val.type);
CU_ASSERT(0 == val.b);
}
{
const uint8_t s[] = "?2";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "?";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "?1";
CU_ASSERT(2 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
nghttp2_sf_value val;
const uint8_t s[] = ":cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==:";
val.type = 0xff;
CU_ASSERT(46 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BYTESEQ == val.type);
CU_ASSERT(44 == val.s.len);
CU_ASSERT(0 == memcmp("cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==",
val.s.base, val.s.len));
}
{
nghttp2_sf_value val;
const uint8_t s[] = ":cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==: ";
val.type = 0xff;
CU_ASSERT(46 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BYTESEQ == val.type);
CU_ASSERT(44 == val.s.len);
CU_ASSERT(0 == memcmp("cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==",
val.s.base, val.s.len));
}
{
nghttp2_sf_value val;
const uint8_t s[] = "::";
val.type = 0xff;
CU_ASSERT(2 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BYTESEQ == val.type);
CU_ASSERT(0 == val.s.len);
}
{
const uint8_t s[] = ":cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = ":";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = ":@:";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = ":foo:";
CU_ASSERT(5 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
nghttp2_sf_value val;
const uint8_t s[] =
":abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=:";
val.type = 0xff;
CU_ASSERT(67 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_BYTESEQ == val.type);
CU_ASSERT(65 == val.s.len);
CU_ASSERT(
0 ==
memcmp(
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=",
val.s.base, val.s.len));
}
{
nghttp2_sf_value val;
const uint8_t s[] = "foo123/456";
val.type = 0xff;
CU_ASSERT(10 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_TOKEN == val.type);
CU_ASSERT(10 == val.s.len);
CU_ASSERT(0 == memcmp(s, val.s.base, val.s.len));
}
{
nghttp2_sf_value val;
const uint8_t s[] = "foo123/456 ";
val.type = 0xff;
CU_ASSERT(10 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_TOKEN == val.type);
CU_ASSERT(10 == val.s.len);
CU_ASSERT(0 == memcmp(s, val.s.base, val.s.len));
}
{
nghttp2_sf_value val;
const uint8_t s[] = "*";
val.type = 0xff;
CU_ASSERT(1 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_TOKEN == val.type);
CU_ASSERT(1 == val.s.len);
CU_ASSERT(0 == memcmp(s, val.s.base, val.s.len));
}
{
const uint8_t s[] = "*";
CU_ASSERT(1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
nghttp2_sf_value val;
const uint8_t s[] = "\"hello world\"";
val.type = 0xff;
CU_ASSERT(13 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_STRING == val.type);
CU_ASSERT(11 == val.s.len);
CU_ASSERT(0 == memcmp("hello world", val.s.base, val.s.len));
}
{
nghttp2_sf_value val;
const uint8_t s[] = "\"hello world\" ";
val.type = 0xff;
CU_ASSERT(13 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_STRING == val.type);
CU_ASSERT(11 == val.s.len);
CU_ASSERT(0 == memcmp("hello world", val.s.base, val.s.len));
}
{
nghttp2_sf_value val;
const uint8_t s[] = "\"foo\\\"\\\\\"";
val.type = 0xff;
CU_ASSERT(9 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_STRING == val.type);
CU_ASSERT(7 == val.s.len);
CU_ASSERT(0 == memcmp("foo\\\"\\\\", val.s.base, val.s.len));
}
{
const uint8_t s[] = "\"foo\\x\"";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "\"foo";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "\"\x7f\"";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "\"\x1f\"";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "\"foo\"";
CU_ASSERT(5 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
nghttp2_sf_value val;
const uint8_t s[] = "4.5";
val.type = NGHTTP2_SF_VALUE_TYPE_DECIMAL;
CU_ASSERT(3 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_DECIMAL == val.type);
CU_ASSERT(fabs(4.5 - val.d) < 1e-9);
}
{
nghttp2_sf_value val;
const uint8_t s[] = "4.5 ";
val.type = NGHTTP2_SF_VALUE_TYPE_DECIMAL;
CU_ASSERT(3 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_DECIMAL == val.type);
CU_ASSERT(fabs(4.5 - val.d) < 1e-9);
}
{
nghttp2_sf_value val;
const uint8_t s[] = "-4.5";
val.type = NGHTTP2_SF_VALUE_TYPE_DECIMAL;
CU_ASSERT(4 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_DECIMAL == val.type);
CU_ASSERT(fabs(-4.5 - val.d) < 1e-9);
}
{
const uint8_t s[] = "4.5";
CU_ASSERT(3 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
nghttp2_sf_value val;
const uint8_t s[] = "123456789012.123";
val.type = NGHTTP2_SF_VALUE_TYPE_DECIMAL;
CU_ASSERT(16 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_DECIMAL == val.type);
CU_ASSERT(fabs(123456789012.123 - val.d) < 1e-9);
}
{
const uint8_t s[] = "1123456789012.123";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "123456789012.1234";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "1.";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
nghttp2_sf_value val;
const uint8_t s[] = "123456789012345";
val.type = NGHTTP2_SF_VALUE_TYPE_DECIMAL;
CU_ASSERT(15 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_INTEGER == val.type);
CU_ASSERT(123456789012345 == val.i);
}
{
nghttp2_sf_value val;
const uint8_t s[] = "1 ";
val.type = NGHTTP2_SF_VALUE_TYPE_DECIMAL;
CU_ASSERT(1 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_INTEGER == val.type);
CU_ASSERT(1 == val.i);
}
{
const uint8_t s[] = "1";
CU_ASSERT(1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "1234567890123456";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
nghttp2_sf_value val;
const uint8_t s[] = "\"foo\";a; b=\"bar\";c=1.3;d=9;e=baz;f=:aaa:";
val.type = 0xff;
CU_ASSERT(41 == nghttp2_sf_parse_item(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_STRING == val.type);
CU_ASSERT(0 == memcmp("foo", val.s.base, val.s.len));
}
{
const uint8_t s[] = "\"foo\";a; b=\"bar";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "foo;";
CU_ASSERT(-1 == nghttp2_sf_parse_item(NULL, s, s + sizeof(s) - 1));
}
}
void test_nghttp2_sf_parse_inner_list(void) {
{
nghttp2_sf_value val;
const uint8_t s[] = "()";
val.type = 0xff;
CU_ASSERT(2 == nghttp2_sf_parse_inner_list(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_INNER_LIST == val.type);
}
{
nghttp2_sf_value val;
const uint8_t s[] = "( )";
val.type = 0xff;
CU_ASSERT(7 == nghttp2_sf_parse_inner_list(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_INNER_LIST == val.type);
}
{
nghttp2_sf_value val;
const uint8_t s[] = "(a)";
val.type = 0xff;
CU_ASSERT(3 == nghttp2_sf_parse_inner_list(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_INNER_LIST == val.type);
}
{
nghttp2_sf_value val;
const uint8_t s[] = "(a b)";
val.type = 0xff;
CU_ASSERT(5 == nghttp2_sf_parse_inner_list(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_INNER_LIST == val.type);
}
{
nghttp2_sf_value val;
const uint8_t s[] = "( a b )";
val.type = 0xff;
CU_ASSERT(10 == nghttp2_sf_parse_inner_list(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_INNER_LIST == val.type);
}
{
nghttp2_sf_value val;
const uint8_t s[] = "( a;foo=bar)";
val.type = 0xff;
CU_ASSERT(12 == nghttp2_sf_parse_inner_list(&val, s, s + sizeof(s) - 1));
CU_ASSERT(NGHTTP2_SF_VALUE_TYPE_INNER_LIST == val.type);
}
{
const uint8_t s[] = "(";
CU_ASSERT(-1 == nghttp2_sf_parse_inner_list(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "(a";
CU_ASSERT(-1 == nghttp2_sf_parse_inner_list(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "(a ";
CU_ASSERT(-1 == nghttp2_sf_parse_inner_list(NULL, s, s + sizeof(s) - 1));
}
{
const uint8_t s[] = "(a;b";
CU_ASSERT(-1 == nghttp2_sf_parse_inner_list(NULL, s, s + sizeof(s) - 1));
}
}

37
tests/nghttp2_http_test.h Normal file
View File

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

View File

@ -35,6 +35,7 @@
#include "nghttp2_helper.h"
#include "nghttp2_test_helper.h"
#include "nghttp2_priority_spec.h"
#include "nghttp2_extpri.h"
typedef struct {
uint8_t buf[65535];
@ -1828,6 +1829,67 @@ void test_nghttp2_session_recv_headers_for_closed_stream(void) {
nghttp2_session_del(session);
}
void test_nghttp2_session_recv_headers_with_extpri(void) {
nghttp2_session *session;
nghttp2_session_callbacks callbacks;
nghttp2_nv *nva;
size_t nvlen;
nghttp2_frame frame;
nghttp2_bufs bufs;
nghttp2_buf *buf;
ssize_t rv;
nghttp2_hd_deflater deflater;
nghttp2_stream *stream;
nghttp2_mem *mem;
const nghttp2_nv extpri_reqnv[] = {
MAKE_NV(":method", "GET"), MAKE_NV(":path", "/"),
MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "localhost"),
MAKE_NV("priority", "i,u=2"),
};
nghttp2_settings_entry iv;
mem = nghttp2_mem_default();
frame_pack_bufs_init(&bufs);
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
nghttp2_session_server_new(&session, &callbacks, NULL);
iv.settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES;
iv.value = 1;
nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1);
nghttp2_hd_deflate_init(&deflater, mem);
nvlen = ARRLEN(extpri_reqnv);
nghttp2_nv_array_copy(&nva, extpri_reqnv, nvlen, mem);
nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1,
NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen);
rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
CU_ASSERT(0 == rv);
CU_ASSERT(nghttp2_bufs_len(&bufs) > 0);
nghttp2_frame_headers_free(&frame.headers, mem);
buf = &bufs.head->buf;
assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf));
rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
stream = nghttp2_session_get_stream(session, 1);
CU_ASSERT(2 == nghttp2_extpri_uint8_urgency(stream->extpri));
CU_ASSERT(1 == nghttp2_extpri_uint8_inc(stream->extpri));
nghttp2_bufs_free(&bufs);
nghttp2_hd_deflate_free(&deflater);
nghttp2_session_del(session);
}
void test_nghttp2_session_server_recv_push_response(void) {
nghttp2_session *session;
nghttp2_session_callbacks callbacks;
@ -11209,7 +11271,8 @@ void test_nghttp2_session_no_rfc7540_priorities(void) {
mem);
CU_ASSERT(0 == nghttp2_session_send(session));
CU_ASSERT(1 == nghttp2_pq_size(&session->ob_data));
CU_ASSERT(1 == nghttp2_pq_size(
&session->sched[NGHTTP2_EXTPRI_DEFAULT_URGENCY].ob_data));
CU_ASSERT(nghttp2_pq_empty(&session->root.obq));
nghttp2_session_del(session);

View File

@ -40,6 +40,7 @@ void test_nghttp2_session_recv_headers_with_priority(void);
void test_nghttp2_session_recv_headers_with_padding(void);
void test_nghttp2_session_recv_headers_early_response(void);
void test_nghttp2_session_recv_headers_for_closed_stream(void);
void test_nghttp2_session_recv_headers_with_extpri(void);
void test_nghttp2_session_server_recv_push_response(void);
void test_nghttp2_session_recv_premature_headers(void);
void test_nghttp2_session_recv_unknown_frame(void);