Implement RFC 9218 extensible prioritization scheme

This commit implements RFC 9218 extensible prioritization scheme.  It
is enabled when a local endpoint submits
SETTINGS_NO_RFC7540_PRIORITIES = 1.  This commit only handles priority
signal in HTTP request header field.  Priority header field in
PUSH_PROMISE is not supported.

HTTP messaging must be enabled to take advantage of this
prioritization scheme because HTTP fields are not parsed if HTTP
messaging is disabled.
This commit is contained in:
Tatsuhiro Tsujikawa 2022-06-12 15:10:51 +09:00
parent 4ffb63a737
commit c10a55588b
23 changed files with 1974 additions and 38 deletions

View File

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

View File

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

View File

@ -50,6 +50,7 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \
nghttp2_mem.c \ nghttp2_mem.c \
nghttp2_http.c \ nghttp2_http.c \
nghttp2_rcbuf.c \ nghttp2_rcbuf.c \
nghttp2_extpri.c \
nghttp2_debug.c nghttp2_debug.c
HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ 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_mem.h \
nghttp2_http.h \ nghttp2_http.h \
nghttp2_rcbuf.h \ nghttp2_rcbuf.h \
nghttp2_extpri.h \
nghttp2_debug.h nghttp2_debug.h
libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS) libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS)

View File

@ -705,8 +705,7 @@ typedef enum {
*/ */
NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x08, NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x08,
/** /**
* SETTINGS_NO_RFC7540_PRIORITIES (`RFC 9218 * SETTINGS_NO_RFC7540_PRIORITIES (:rfc:`9218`)
* <https://datatracker.ietf.org/doc/html/rfc9218>`_)
*/ */
NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES = 0x09 NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES = 0x09
} nghttp2_settings_id; } nghttp2_settings_id;
@ -4233,6 +4232,61 @@ nghttp2_submit_priority(nghttp2_session *session, uint8_t flags,
int32_t stream_id, int32_t stream_id,
const nghttp2_priority_spec *pri_spec); 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 * @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; return NGHTTP2_TOKEN_LOCATION;
} }
break; break;
case 'y':
if (memeq("priorit", name, 7)) {
return NGHTTP2_TOKEN_PRIORITY;
}
break;
} }
break; break;
case 9: case 9:

View File

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

View File

@ -30,6 +30,7 @@
#include "nghttp2_hd.h" #include "nghttp2_hd.h"
#include "nghttp2_helper.h" #include "nghttp2_helper.h"
#include "nghttp2_extpri.h"
static uint8_t downcase(uint8_t c) { static uint8_t downcase(uint8_t c) {
return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : 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, static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
int trailer, int connect_protocol) { int trailer, int connect_protocol) {
nghttp2_extpri extpri;
if (nv->name->base[0] == ':') { if (nv->name->base[0] == ':') {
if (trailer || if (trailer ||
(stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { (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; return NGHTTP2_ERR_HTTP_HEADER;
} }
break; 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: default:
if (nv->name->base[0] == ':') { if (nv->name->base[0] == ':') {
return NGHTTP2_ERR_HTTP_HEADER; return NGHTTP2_ERR_HTTP_HEADER;
@ -541,3 +554,715 @@ void nghttp2_http_record_request_method(nghttp2_stream *stream,
return; 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, void nghttp2_http_record_request_method(nghttp2_stream *stream,
nghttp2_frame *frame); 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 */ #endif /* NGHTTP2_HTTP_H */

View File

@ -36,6 +36,7 @@
#include "nghttp2_option.h" #include "nghttp2_option.h"
#include "nghttp2_http.h" #include "nghttp2_http.h"
#include "nghttp2_pq.h" #include "nghttp2_pq.h"
#include "nghttp2_extpri.h"
#include "nghttp2_debug.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; 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) { static int stream_less(const void *lhsx, const void *rhsx) {
const nghttp2_stream *lhs, *rhs; const nghttp2_stream *lhs, *rhs;
lhs = nghttp2_struct_of(lhsx, nghttp2_stream, pq_entry); lhs = nghttp2_struct_of(lhsx, nghttp2_stream, pq_entry);
rhs = nghttp2_struct_of(rhsx, nghttp2_stream, pq_entry); rhs = nghttp2_struct_of(rhsx, nghttp2_stream, pq_entry);
if (lhs->cycle == rhs->cycle) {
return lhs->seq < rhs->seq; return lhs->seq < rhs->seq;
}
return rhs->cycle - lhs->cycle <= NGHTTP2_STREAM_MAX_CYCLE_GAP;
} }
int nghttp2_enable_strict_preface = 1; int nghttp2_enable_strict_preface = 1;
@ -418,6 +425,7 @@ static int session_new(nghttp2_session **session_ptr,
size_t nbuffer; size_t nbuffer;
size_t max_deflate_dynamic_table_size = size_t max_deflate_dynamic_table_size =
NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE; NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE;
size_t i;
if (mem == NULL) { if (mem == NULL) {
mem = nghttp2_mem_default(); 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; return 0;
@ -748,6 +758,7 @@ static void inflight_settings_del(nghttp2_inflight_settings *settings,
void nghttp2_session_del(nghttp2_session *session) { void nghttp2_session_del(nghttp2_session *session) {
nghttp2_mem *mem; nghttp2_mem *mem;
nghttp2_inflight_settings *settings; nghttp2_inflight_settings *settings;
size_t i;
if (session == NULL) { if (session == NULL) {
return; return;
@ -761,7 +772,9 @@ void nghttp2_session_del(nghttp2_session *session) {
settings = next; 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); nghttp2_stream_free(&session->root);
/* Have to free streams first, so that we can check /* Have to free streams first, so that we can check
@ -857,14 +870,40 @@ int nghttp2_session_reprioritize_stream(
return 0; 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, static int session_ob_data_push(nghttp2_session *session,
nghttp2_stream *stream) { nghttp2_stream *stream) {
int rv; int rv;
uint32_t urgency;
int inc;
nghttp2_pq *pq;
assert(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES); assert(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES);
assert(stream->queued == 0); 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) { if (rv != 0) {
return rv; return rv;
} }
@ -876,10 +915,16 @@ static int session_ob_data_push(nghttp2_session *session,
static int session_ob_data_remove(nghttp2_session *session, static int session_ob_data_remove(nghttp2_session *session,
nghttp2_stream *stream) { nghttp2_stream *stream) {
uint32_t urgency;
assert(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES); assert(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES);
assert(stream->queued == 1); 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; stream->queued = 0;
@ -955,6 +1000,84 @@ static int session_resume_deferred_stream_item(nghttp2_session *session,
return session_ob_data_push(session, stream); 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, int nghttp2_session_add_item(nghttp2_session *session,
nghttp2_outbound_item *item) { nghttp2_outbound_item *item) {
/* TODO Return error if stream is not found for the frame requiring /* 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_outbound_item *
nghttp2_session_get_next_ob_item(nghttp2_session *session) { nghttp2_session_get_next_ob_item(nghttp2_session *session) {
nghttp2_outbound_item *item; nghttp2_outbound_item *item;
nghttp2_pq_entry *ent;
nghttp2_stream *stream;
if (nghttp2_outbound_queue_top(&session->ob_urgent)) { if (nghttp2_outbound_queue_top(&session->ob_urgent)) {
return 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; return item;
} }
ent = nghttp2_pq_top(&session->ob_data); return session_sched_get_next_outbound_item(session);
if (!ent) {
return NULL;
}
stream = nghttp2_struct_of(ent, nghttp2_stream, pq_entry);
return stream->item;
} }
return NULL; return NULL;
@ -2527,8 +2642,6 @@ nghttp2_session_get_next_ob_item(nghttp2_session *session) {
nghttp2_outbound_item * nghttp2_outbound_item *
nghttp2_session_pop_next_ob_item(nghttp2_session *session) { nghttp2_session_pop_next_ob_item(nghttp2_session *session) {
nghttp2_outbound_item *item; nghttp2_outbound_item *item;
nghttp2_pq_entry *ent;
nghttp2_stream *stream;
item = nghttp2_outbound_queue_top(&session->ob_urgent); item = nghttp2_outbound_queue_top(&session->ob_urgent);
if (item) { if (item) {
@ -2559,13 +2672,7 @@ nghttp2_session_pop_next_ob_item(nghttp2_session *session) {
return item; return item;
} }
ent = nghttp2_pq_top(&session->ob_data); return session_sched_get_next_outbound_item(session);
if (!ent) {
return NULL;
}
stream = nghttp2_struct_of(ent, nghttp2_stream, pq_entry);
return stream->item;
} }
return NULL; return NULL;
@ -2675,10 +2782,20 @@ static int session_close_stream_on_goaway(nghttp2_session *session,
return 0; 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; stream->last_writelen = stream->item->frame.hd.length;
if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES)) {
nghttp2_stream_reschedule(stream); 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, 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_frame *frame,
nghttp2_stream *stream) { nghttp2_stream *stream) {
int rv; 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) { if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
return 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) || return session->aob.item || nghttp2_outbound_queue_top(&session->ob_urgent) ||
nghttp2_outbound_queue_top(&session->ob_reg) || nghttp2_outbound_queue_top(&session->ob_reg) ||
((!nghttp2_pq_empty(&session->root.obq) || ((!nghttp2_pq_empty(&session->root.obq) ||
!nghttp2_pq_empty(&session->ob_data)) && !session_sched_empty(session)) &&
session->remote_window_size > 0) || session->remote_window_size > 0) ||
(nghttp2_outbound_queue_top(&session->ob_syn) && (nghttp2_outbound_queue_top(&session->ob_syn) &&
!session_is_outgoing_concurrent_streams_max(session)); !session_is_outgoing_concurrent_streams_max(session));
@ -7474,9 +7604,7 @@ int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
return rv; return rv;
} }
if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES)) { session_reschedule_stream(session, stream);
reschedule_stream(stream);
}
if (frame->hd.length == 0 && (data_flags & NGHTTP2_DATA_FLAG_EOF) && if (frame->hd.length == 0 && (data_flags & NGHTTP2_DATA_FLAG_EOF) &&
(data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM)) { (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM)) {

View File

@ -203,9 +203,12 @@ struct nghttp2_session {
response) frame, which are subject to response) frame, which are subject to
SETTINGS_MAX_CONCURRENT_STREAMS limit. */ SETTINGS_MAX_CONCURRENT_STREAMS limit. */
nghttp2_outbound_queue ob_syn; nghttp2_outbound_queue ob_syn;
/* Queue for DATA frames which is used when /* Queues for DATA frames which is used when
SETTINGS_NO_RFC7540_PRIORITIES is enabled. */ SETTINGS_NO_RFC7540_PRIORITIES is enabled. This implements RFC
9218 extensible prioritization scheme. */
struct {
nghttp2_pq ob_data; nghttp2_pq ob_data;
} sched[NGHTTP2_EXTPRI_URGENCY_LEVELS];
nghttp2_active_outbound_item aob; nghttp2_active_outbound_item aob;
nghttp2_inbound_frame iframe; nghttp2_inbound_frame iframe;
nghttp2_hd_deflater hd_deflater; 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->descendant_next_seq = 0;
stream->seq = 0; stream->seq = 0;
stream->last_writelen = 0; stream->last_writelen = 0;
stream->extpri = stream->http_extpri = NGHTTP2_EXTPRI_DEFAULT_URGENCY;
} }
void nghttp2_stream_free(nghttp2_stream *stream) { 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 this stream. The nonzero does not necessarily mean WINDOW_UPDATE
is not queued. */ is not queued. */
uint8_t window_update_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, 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_npn_test.c
nghttp2_helper_test.c nghttp2_helper_test.c
nghttp2_buf_test.c nghttp2_buf_test.c
nghttp2_http_test.c
nghttp2_extpri_test.c
) )
add_executable(main EXCLUDE_FROM_ALL 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_hd_test.c \
nghttp2_npn_test.c \ nghttp2_npn_test.c \
nghttp2_helper_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 \ HFILES = nghttp2_pq_test.h nghttp2_map_test.h nghttp2_queue_test.h \
nghttp2_session_test.h \ nghttp2_session_test.h \
nghttp2_frame_test.h nghttp2_stream_test.h nghttp2_hd_test.h \ nghttp2_frame_test.h nghttp2_stream_test.h nghttp2_hd_test.h \
nghttp2_npn_test.h nghttp2_helper_test.h \ nghttp2_npn_test.h nghttp2_helper_test.h \
nghttp2_test_helper.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) main_SOURCES = $(HFILES) $(OBJECTS)

View File

@ -40,6 +40,8 @@
#include "nghttp2_npn_test.h" #include "nghttp2_npn_test.h"
#include "nghttp2_helper_test.h" #include "nghttp2_helper_test.h"
#include "nghttp2_buf_test.h" #include "nghttp2_buf_test.h"
#include "nghttp2_http_test.h"
#include "nghttp2_extpri_test.h"
extern int nghttp2_enable_strict_preface; extern int nghttp2_enable_strict_preface;
@ -93,6 +95,8 @@ int main(void) {
test_nghttp2_session_recv_headers_early_response) || test_nghttp2_session_recv_headers_early_response) ||
!CU_add_test(pSuite, "session_recv_headers_for_closed_stream", !CU_add_test(pSuite, "session_recv_headers_for_closed_stream",
test_nghttp2_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", !CU_add_test(pSuite, "session_server_recv_push_response",
test_nghttp2_session_server_recv_push_response) || test_nghttp2_session_server_recv_push_response) ||
!CU_add_test(pSuite, "session_recv_premature_headers", !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_advance", test_nghttp2_bufs_advance) ||
!CU_add_test(pSuite, "bufs_next_present", !CU_add_test(pSuite, "bufs_next_present",
test_nghttp2_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(); CU_cleanup_registry();
return (int)CU_get_error(); 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_helper.h"
#include "nghttp2_test_helper.h" #include "nghttp2_test_helper.h"
#include "nghttp2_priority_spec.h" #include "nghttp2_priority_spec.h"
#include "nghttp2_extpri.h"
typedef struct { typedef struct {
uint8_t buf[65535]; uint8_t buf[65535];
@ -1828,6 +1829,67 @@ void test_nghttp2_session_recv_headers_for_closed_stream(void) {
nghttp2_session_del(session); 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) { void test_nghttp2_session_server_recv_push_response(void) {
nghttp2_session *session; nghttp2_session *session;
nghttp2_session_callbacks callbacks; nghttp2_session_callbacks callbacks;
@ -11209,7 +11271,8 @@ void test_nghttp2_session_no_rfc7540_priorities(void) {
mem); mem);
CU_ASSERT(0 == nghttp2_session_send(session)); 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)); CU_ASSERT(nghttp2_pq_empty(&session->root.obq));
nghttp2_session_del(session); 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_with_padding(void);
void test_nghttp2_session_recv_headers_early_response(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_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_server_recv_push_response(void);
void test_nghttp2_session_recv_premature_headers(void); void test_nghttp2_session_recv_premature_headers(void);
void test_nghttp2_session_recv_unknown_frame(void); void test_nghttp2_session_recv_unknown_frame(void);