nghttp2 2022-09-21 (87fef4ab)

Code extracted from:

    https://github.com/nghttp2/nghttp2.git

at commit 87fef4ab71bebb2168f8d3d554df8d2f0f01f497 (v1.50.0).
This commit is contained in:
nghttp2 upstream
2022-09-21 16:55:07 +09:00
committed by Brad King
parent 5dc6921805
commit 7ce2a682a3
26 changed files with 3252 additions and 696 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -82,8 +82,10 @@ void nghttp2_buf_reset(nghttp2_buf *buf) {
}
void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len) {
buf->begin = buf->pos = buf->last = buf->mark = begin;
buf->end = begin + len;
buf->begin = buf->pos = buf->last = buf->mark = buf->end = begin;
if (len) {
buf->end += len;
}
}
static int buf_chain_new(nghttp2_buf_chain **chain, size_t chunk_length,

View File

@@ -99,7 +99,7 @@ void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem);
* |new_cap|. If extensions took place, buffer pointers in |buf| will
* change.
*
* This function returns 0 if it succeeds, or one of the followings
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM

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

@@ -253,6 +253,31 @@ void nghttp2_frame_origin_free(nghttp2_extension *frame, nghttp2_mem *mem) {
nghttp2_mem_free(mem, origin->ov);
}
void nghttp2_frame_priority_update_init(nghttp2_extension *frame,
int32_t stream_id, uint8_t *field_value,
size_t field_value_len) {
nghttp2_ext_priority_update *priority_update;
nghttp2_frame_hd_init(&frame->hd, 4 + field_value_len,
NGHTTP2_PRIORITY_UPDATE, NGHTTP2_FLAG_NONE, 0);
priority_update = frame->payload;
priority_update->stream_id = stream_id;
priority_update->field_value = field_value;
priority_update->field_value_len = field_value_len;
}
void nghttp2_frame_priority_update_free(nghttp2_extension *frame,
nghttp2_mem *mem) {
nghttp2_ext_priority_update *priority_update;
priority_update = frame->payload;
if (priority_update == NULL) {
return;
}
nghttp2_mem_free(mem, priority_update->field_value);
}
size_t nghttp2_frame_priority_len(uint8_t flags) {
if (flags & NGHTTP2_FLAG_PRIORITY) {
return NGHTTP2_PRIORITY_SPECLEN;
@@ -654,8 +679,6 @@ int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame,
var_gift_payloadlen = 0;
}
payloadlen -= var_gift_payloadlen;
if (!var_gift_payloadlen) {
var_gift_payload = NULL;
} else {
@@ -818,8 +841,10 @@ int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame,
size_t len = 0;
origin = frame->payload;
p = payload;
end = p + payloadlen;
p = end = payload;
if (payloadlen) {
end += payloadlen;
}
for (; p != end;) {
if (end - p < 2) {
@@ -876,6 +901,57 @@ int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame,
return 0;
}
int nghttp2_frame_pack_priority_update(nghttp2_bufs *bufs,
nghttp2_extension *frame) {
int rv;
nghttp2_buf *buf;
nghttp2_ext_priority_update *priority_update;
/* This is required with --disable-assert. */
(void)rv;
priority_update = frame->payload;
buf = &bufs->head->buf;
assert(nghttp2_buf_avail(buf) >= 4 + priority_update->field_value_len);
buf->pos -= NGHTTP2_FRAME_HDLEN;
nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
nghttp2_put_uint32be(buf->last, (uint32_t)priority_update->stream_id);
buf->last += 4;
rv = nghttp2_bufs_add(bufs, priority_update->field_value,
priority_update->field_value_len);
assert(rv == 0);
return 0;
}
void nghttp2_frame_unpack_priority_update_payload(nghttp2_extension *frame,
uint8_t *payload,
size_t payloadlen) {
nghttp2_ext_priority_update *priority_update;
assert(payloadlen >= 4);
priority_update = frame->payload;
priority_update->stream_id =
nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK;
if (payloadlen > 4) {
priority_update->field_value = payload + 4;
priority_update->field_value_len = payloadlen - 4;
} else {
priority_update->field_value = NULL;
priority_update->field_value_len = 0;
}
}
nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv,
size_t niv, nghttp2_mem *mem) {
nghttp2_settings_entry *iv_copy;
@@ -897,9 +973,25 @@ nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv,
}
int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b) {
return a->namelen == b->namelen && a->valuelen == b->valuelen &&
memcmp(a->name, b->name, a->namelen) == 0 &&
memcmp(a->value, b->value, a->valuelen) == 0;
if (a->namelen != b->namelen || a->valuelen != b->valuelen) {
return 0;
}
if (a->name == NULL || b->name == NULL) {
assert(a->namelen == 0);
assert(b->namelen == 0);
} else if (memcmp(a->name, b->name, a->namelen) != 0) {
return 0;
}
if (a->value == NULL || b->value == NULL) {
assert(a->valuelen == 0);
assert(b->valuelen == 0);
} else if (memcmp(a->value, b->value, a->valuelen) != 0) {
return 0;
}
return 1;
}
void nghttp2_nv_array_del(nghttp2_nv *nva, nghttp2_mem *mem) {
@@ -1055,6 +1147,11 @@ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv) {
return 0;
}
break;
case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES:
if (iv[i].value != 0 && iv[i].value != 1) {
return 0;
}
break;
}
}
return 1;

View File

@@ -46,7 +46,7 @@
#define NGHTTP2_MAX_FRAME_SIZE_MIN (1 << 14)
#define NGHTTP2_MAX_PAYLOADLEN 16384
/* The one frame buffer length for tranmission. We may use several of
/* The one frame buffer length for transmission. We may use several of
them to support CONTINUATION. To account for Pad Length field, we
allocate extra 1 byte, which saves extra large memcopying. */
#define NGHTTP2_FRAMEBUF_CHUNKLEN \
@@ -57,7 +57,7 @@
/* Maximum headers block size to send, calculated using
nghttp2_hd_deflate_bound(). This is the default value, and can be
overridden by nghttp2_option_set_max_send_header_block_size(). */
overridden by nghttp2_option_set_max_send_header_block_length(). */
#define NGHTTP2_MAX_HEADERSLEN 65536
/* The number of bytes for each SETTINGS entry */
@@ -73,6 +73,7 @@
typedef union {
nghttp2_ext_altsvc altsvc;
nghttp2_ext_origin origin;
nghttp2_ext_priority_update priority_update;
} nghttp2_ext_frame_payload;
void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd);
@@ -423,6 +424,31 @@ int nghttp2_frame_pack_origin(nghttp2_bufs *bufs, nghttp2_extension *ext);
int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame,
const uint8_t *payload,
size_t payloadlen, nghttp2_mem *mem);
/*
* Packs PRIORITY_UPDATE frame |frame| in wire frame format and store
* it in |bufs|.
*
* The caller must make sure that nghttp2_bufs_reset(bufs) is called
* before calling this function.
*
* This function always succeeds and returns 0.
*/
int nghttp2_frame_pack_priority_update(nghttp2_bufs *bufs,
nghttp2_extension *ext);
/*
* Unpacks PRIORITY_UPDATE wire format into |frame|. The |payload| of
* |payloadlen| bytes contains frame payload. This function assumes
* that frame->payload points to the nghttp2_ext_priority_update
* object.
*
* This function always succeeds and returns 0.
*/
void nghttp2_frame_unpack_priority_update_payload(nghttp2_extension *frame,
uint8_t *payload,
size_t payloadlen);
/*
* Initializes HEADERS frame |frame| with given values. |frame| takes
* ownership of |nva|, so caller must not free it. If |stream_id| is
@@ -538,6 +564,25 @@ void nghttp2_frame_origin_init(nghttp2_extension *frame,
*/
void nghttp2_frame_origin_free(nghttp2_extension *frame, nghttp2_mem *mem);
/*
* Initializes PRIORITY_UPDATE frame |frame| with given values. This
* function assumes that frame->payload points to
* nghttp2_ext_priority_update object. On success, this function
* takes ownership of |field_value|, so caller must not free it.
*/
void nghttp2_frame_priority_update_init(nghttp2_extension *frame,
int32_t stream_id, uint8_t *field_value,
size_t field_value_len);
/*
* Frees up resources under |frame|. This function does not free
* nghttp2_ext_priority_update object pointed by frame->payload. This
* function only frees field_value pointed by
* nghttp2_ext_priority_update.field_value.
*/
void nghttp2_frame_priority_update_free(nghttp2_extension *frame,
nghttp2_mem *mem);
/*
* Returns the number of padding bytes after payload. The total
* padding length is given in the |padlen|. The returned value does

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:
@@ -1263,6 +1268,8 @@ int nghttp2_hd_inflate_change_table_size(
return NGHTTP2_ERR_INVALID_STATE;
}
inflater->settings_hd_table_bufsize_max = settings_max_dynamic_table_size;
/* It seems that encoder is not required to send dynamic table size
update if the table size is not changed after applying
SETTINGS_HEADER_TABLE_SIZE. RFC 7541 is ambiguous here, but this
@@ -1275,13 +1282,12 @@ int nghttp2_hd_inflate_change_table_size(
/* Remember minimum value, and validate that encoder sends the
value less than or equal to this. */
inflater->min_hd_table_bufsize_max = settings_max_dynamic_table_size;
inflater->ctx.hd_table_bufsize_max = settings_max_dynamic_table_size;
hd_context_shrink_table_size(&inflater->ctx, NULL);
}
inflater->settings_hd_table_bufsize_max = settings_max_dynamic_table_size;
inflater->ctx.hd_table_bufsize_max = settings_max_dynamic_table_size;
hd_context_shrink_table_size(&inflater->ctx, NULL);
return 0;
}

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

@@ -334,6 +334,8 @@ const char *nghttp2_strerror(int error_code) {
case NGHTTP2_ERR_FLOODED:
return "Flooding was detected in this HTTP/2 session, and it must be "
"closed";
case NGHTTP2_ERR_TOO_MANY_SETTINGS:
return "SETTINGS frame contained more than the maximum allowed entries";
default:
return "Unknown error code";
}
@@ -505,7 +507,179 @@ int nghttp2_check_header_value(const uint8_t *value, size_t len) {
return 1;
}
/* Generated by genauthroitychartbl.py */
int nghttp2_check_header_value_rfc9113(const uint8_t *value, size_t len) {
if (len == 0) {
return 1;
}
if (*value == ' ' || *value == '\t' || *(value + len - 1) == ' ' ||
*(value + len - 1) == '\t') {
return 0;
}
return nghttp2_check_header_value(value, len);
}
/* Generated by genmethodchartbl.py */
static char VALID_METHOD_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 /* . */, 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 /* @ */, 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 */
};
int nghttp2_check_method(const uint8_t *value, size_t len) {
const uint8_t *last;
if (len == 0) {
return 0;
}
for (last = value + len; value != last; ++value) {
if (!VALID_METHOD_CHARS[*value]) {
return 0;
}
}
return 1;
}
/* Generated by genpathchartbl.py */
static char VALID_PATH_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 /* ! */, 1 /* " */, 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 /* [ */,
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 /* { */,
1 /* | */, 1 /* } */, 1 /* ~ */, 0 /* DEL */,
1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */,
1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */,
1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */,
1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */,
1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */,
1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */,
1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */,
1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */,
1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */,
1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */,
1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */,
1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */,
1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */,
1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */,
1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */,
1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */,
1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */,
1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */,
1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */,
1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */,
1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */,
1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */,
1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */,
1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */,
1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */,
1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */,
1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */,
1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */,
1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */,
1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */,
1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */,
1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */
};
int nghttp2_check_path(const uint8_t *value, size_t len) {
const uint8_t *last;
for (last = value + len; value != last; ++value) {
if (!VALID_PATH_CHARS[*value]) {
return 0;
}
}
return 1;
}
/* Generated by genauthoritychartbl.py */
static char VALID_AUTHORITY_CHARS[] = {
0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,

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;
@@ -72,25 +73,12 @@ static int64_t parse_uint(const uint8_t *s, size_t len) {
return n;
}
static int lws(const uint8_t *s, size_t n) {
size_t i;
for (i = 0; i < n; ++i) {
if (s[i] != ' ' && s[i] != '\t') {
return 0;
}
}
return 1;
}
static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
int flag) {
if (stream->http_flags & flag) {
uint32_t flag) {
if ((stream->http_flags & flag) || nv->value->len == 0) {
return 0;
}
if (lws(nv->value->base, nv->value->len)) {
return 0;
}
stream->http_flags = (uint16_t)(stream->http_flags | flag);
stream->http_flags = stream->http_flags | flag;
return 1;
}
@@ -114,6 +102,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 +202,23 @@ 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 &&
/* Do not parse the header field in PUSH_PROMISE. */
(stream->stream_id & 1) &&
(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) &&
!(stream->http_flags & NGHTTP2_HTTP_FLAG_BAD_PRIORITY)) {
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);
stream->http_flags |= NGHTTP2_HTTP_FLAG_PRIORITY;
} else {
stream->http_flags &= (uint32_t)~NGHTTP2_HTTP_FLAG_PRIORITY;
stream->http_flags |= NGHTTP2_HTTP_FLAG_BAD_PRIORITY;
}
}
break;
default:
if (nv->name->base[0] == ':') {
return NGHTTP2_ERR_HTTP_HEADER;
@@ -329,6 +336,16 @@ static int check_scheme(const uint8_t *value, size_t len) {
return 1;
}
static int lws(const uint8_t *s, size_t n) {
size_t i;
for (i = 0; i < n; ++i) {
if (s[i] != ' ' && s[i] != '\t') {
return 0;
}
}
return 1;
}
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
nghttp2_frame *frame, nghttp2_hd_nv *nv,
int trailer) {
@@ -360,13 +377,46 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
return NGHTTP2_ERR_IGN_HTTP_HEADER;
}
if (nv->token == NGHTTP2_TOKEN__AUTHORITY ||
nv->token == NGHTTP2_TOKEN_HOST) {
rv = nghttp2_check_authority(nv->value->base, nv->value->len);
} else if (nv->token == NGHTTP2_TOKEN__SCHEME) {
switch (nv->token) {
case NGHTTP2_TOKEN__METHOD:
rv = nghttp2_check_method(nv->value->base, nv->value->len);
break;
case NGHTTP2_TOKEN__PATH:
rv = nghttp2_check_path(nv->value->base, nv->value->len);
break;
case NGHTTP2_TOKEN__AUTHORITY:
case NGHTTP2_TOKEN_HOST:
if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
rv = nghttp2_check_authority(nv->value->base, nv->value->len);
} else if (
stream->flags &
NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) {
rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
} else {
rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len);
}
break;
case NGHTTP2_TOKEN__SCHEME:
rv = check_scheme(nv->value->base, nv->value->len);
} else {
rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
break;
case NGHTTP2_TOKEN__PROTOCOL:
/* Check the value consists of just white spaces, which was done
in check_pseudo_header before
nghttp2_check_header_value_rfc9113 has been introduced. */
if ((stream->flags &
NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) &&
lws(nv->value->base, nv->value->len)) {
rv = 0;
break;
}
/* fall through */
default:
if (stream->flags &
NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) {
rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
} else {
rv = nghttp2_check_header_value_rfc9113(nv->value->base, nv->value->len);
}
}
if (rv == 0) {
@@ -434,16 +484,15 @@ int nghttp2_http_on_response_headers(nghttp2_stream *stream) {
if (stream->status_code / 100 == 1) {
/* non-final response */
stream->http_flags =
(uint16_t)((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) |
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE);
stream->http_flags = (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) |
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
stream->content_length = -1;
stream->status_code = -1;
return 0;
}
stream->http_flags =
(uint16_t)(stream->http_flags & ~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE);
stream->http_flags & (uint32_t)~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
if (!expect_response_body(stream)) {
stream->content_length = 0;
@@ -528,3 +577,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

@@ -1,7 +1,8 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2012 Tatsuhiro Tsujikawa
* Copyright (c) 2017 ngtcp2 contributors
* Copyright (c) 2012 nghttp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -25,14 +26,19 @@
#include "nghttp2_map.h"
#include <string.h>
#include <assert.h>
#include <stdio.h>
#define INITIAL_TABLE_LENGTH 256
#include "nghttp2_helper.h"
#define NGHTTP2_INITIAL_TABLE_LENBITS 8
int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) {
map->mem = mem;
map->tablelen = INITIAL_TABLE_LENGTH;
map->tablelen = 1 << NGHTTP2_INITIAL_TABLE_LENBITS;
map->tablelenbits = NGHTTP2_INITIAL_TABLE_LENBITS;
map->table =
nghttp2_mem_calloc(mem, map->tablelen, sizeof(nghttp2_map_entry *));
nghttp2_mem_calloc(mem, map->tablelen, sizeof(nghttp2_map_bucket));
if (map->table == NULL) {
return NGHTTP2_ERR_NOMEM;
}
@@ -43,112 +49,188 @@ int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) {
}
void nghttp2_map_free(nghttp2_map *map) {
if (!map) {
return;
}
nghttp2_mem_free(map->mem, map->table);
}
void nghttp2_map_each_free(nghttp2_map *map,
int (*func)(nghttp2_map_entry *entry, void *ptr),
void nghttp2_map_each_free(nghttp2_map *map, int (*func)(void *data, void *ptr),
void *ptr) {
uint32_t i;
nghttp2_map_bucket *bkt;
for (i = 0; i < map->tablelen; ++i) {
nghttp2_map_entry *entry;
for (entry = map->table[i]; entry;) {
nghttp2_map_entry *next = entry->next;
func(entry, ptr);
entry = next;
bkt = &map->table[i];
if (bkt->data == NULL) {
continue;
}
map->table[i] = NULL;
func(bkt->data, ptr);
}
}
int nghttp2_map_each(nghttp2_map *map,
int (*func)(nghttp2_map_entry *entry, void *ptr),
int nghttp2_map_each(nghttp2_map *map, int (*func)(void *data, void *ptr),
void *ptr) {
int rv;
uint32_t i;
nghttp2_map_bucket *bkt;
for (i = 0; i < map->tablelen; ++i) {
nghttp2_map_entry *entry;
for (entry = map->table[i]; entry; entry = entry->next) {
rv = func(entry, ptr);
if (rv != 0) {
return rv;
}
bkt = &map->table[i];
if (bkt->data == NULL) {
continue;
}
rv = func(bkt->data, ptr);
if (rv != 0) {
return rv;
}
}
return 0;
}
void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key) {
entry->key = key;
entry->next = NULL;
static uint32_t hash(nghttp2_map_key_type key) {
return (uint32_t)key * 2654435769u;
}
/* Same hash function in android HashMap source code. */
/* The |mod| must be power of 2 */
static uint32_t hash(int32_t key, uint32_t mod) {
uint32_t h = (uint32_t)key;
h ^= (h >> 20) ^ (h >> 12);
h ^= (h >> 7) ^ (h >> 4);
return h & (mod - 1);
static size_t h2idx(uint32_t hash, uint32_t bits) {
return hash >> (32 - bits);
}
static int insert(nghttp2_map_entry **table, uint32_t tablelen,
nghttp2_map_entry *entry) {
uint32_t h = hash(entry->key, tablelen);
if (table[h] == NULL) {
table[h] = entry;
} else {
nghttp2_map_entry *p;
/* We won't allow duplicated key, so check it out. */
for (p = table[h]; p; p = p->next) {
if (p->key == entry->key) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
}
entry->next = table[h];
table[h] = entry;
}
return 0;
static size_t distance(uint32_t tablelen, uint32_t tablelenbits,
nghttp2_map_bucket *bkt, size_t idx) {
return (idx - h2idx(bkt->hash, tablelenbits)) & (tablelen - 1);
}
/* new_tablelen must be power of 2 */
static int resize(nghttp2_map *map, uint32_t new_tablelen) {
static void map_bucket_swap(nghttp2_map_bucket *bkt, uint32_t *phash,
nghttp2_map_key_type *pkey, void **pdata) {
uint32_t h = bkt->hash;
nghttp2_map_key_type key = bkt->key;
void *data = bkt->data;
bkt->hash = *phash;
bkt->key = *pkey;
bkt->data = *pdata;
*phash = h;
*pkey = key;
*pdata = data;
}
static void map_bucket_set_data(nghttp2_map_bucket *bkt, uint32_t hash,
nghttp2_map_key_type key, void *data) {
bkt->hash = hash;
bkt->key = key;
bkt->data = data;
}
void nghttp2_map_print_distance(nghttp2_map *map) {
uint32_t i;
nghttp2_map_entry **new_table;
size_t idx;
nghttp2_map_bucket *bkt;
for (i = 0; i < map->tablelen; ++i) {
bkt = &map->table[i];
if (bkt->data == NULL) {
fprintf(stderr, "@%u <EMPTY>\n", i);
continue;
}
idx = h2idx(bkt->hash, map->tablelenbits);
fprintf(stderr, "@%u hash=%08x key=%d base=%zu distance=%zu\n", i,
bkt->hash, bkt->key, idx,
distance(map->tablelen, map->tablelenbits, bkt, idx));
}
}
static int insert(nghttp2_map_bucket *table, uint32_t tablelen,
uint32_t tablelenbits, uint32_t hash,
nghttp2_map_key_type key, void *data) {
size_t idx = h2idx(hash, tablelenbits);
size_t d = 0, dd;
nghttp2_map_bucket *bkt;
for (;;) {
bkt = &table[idx];
if (bkt->data == NULL) {
map_bucket_set_data(bkt, hash, key, data);
return 0;
}
dd = distance(tablelen, tablelenbits, bkt, idx);
if (d > dd) {
map_bucket_swap(bkt, &hash, &key, &data);
d = dd;
} else if (bkt->key == key) {
/* TODO This check is just a waste after first swap or if this
function is called from map_resize. That said, there is no
difference with or without this conditional in performance
wise. */
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
++d;
idx = (idx + 1) & (tablelen - 1);
}
}
/* new_tablelen must be power of 2 and new_tablelen == (1 <<
new_tablelenbits) must hold. */
static int map_resize(nghttp2_map *map, uint32_t new_tablelen,
uint32_t new_tablelenbits) {
uint32_t i;
nghttp2_map_bucket *new_table;
nghttp2_map_bucket *bkt;
int rv;
(void)rv;
new_table =
nghttp2_mem_calloc(map->mem, new_tablelen, sizeof(nghttp2_map_entry *));
nghttp2_mem_calloc(map->mem, new_tablelen, sizeof(nghttp2_map_bucket));
if (new_table == NULL) {
return NGHTTP2_ERR_NOMEM;
}
for (i = 0; i < map->tablelen; ++i) {
nghttp2_map_entry *entry;
for (entry = map->table[i]; entry;) {
nghttp2_map_entry *next = entry->next;
entry->next = NULL;
/* This function must succeed */
insert(new_table, new_tablelen, entry);
entry = next;
bkt = &map->table[i];
if (bkt->data == NULL) {
continue;
}
rv = insert(new_table, new_tablelen, new_tablelenbits, bkt->hash, bkt->key,
bkt->data);
assert(0 == rv);
}
nghttp2_mem_free(map->mem, map->table);
map->tablelen = new_tablelen;
map->tablelenbits = new_tablelenbits;
map->table = new_table;
return 0;
}
int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *new_entry) {
int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data) {
int rv;
assert(data);
/* Load factor is 0.75 */
if ((map->size + 1) * 4 > map->tablelen * 3) {
rv = resize(map, map->tablelen * 2);
rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1);
if (rv != 0) {
return rv;
}
}
rv = insert(map->table, map->tablelen, new_entry);
rv = insert(map->table, map->tablelen, map->tablelenbits, hash(key), key,
data);
if (rv != 0) {
return rv;
}
@@ -156,34 +238,76 @@ int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *new_entry) {
return 0;
}
nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key) {
uint32_t h;
nghttp2_map_entry *entry;
h = hash(key, map->tablelen);
for (entry = map->table[h]; entry; entry = entry->next) {
if (entry->key == key) {
return entry;
void *nghttp2_map_find(nghttp2_map *map, nghttp2_map_key_type key) {
uint32_t h = hash(key);
size_t idx = h2idx(h, map->tablelenbits);
nghttp2_map_bucket *bkt;
size_t d = 0;
for (;;) {
bkt = &map->table[idx];
if (bkt->data == NULL ||
d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
return NULL;
}
if (bkt->key == key) {
return bkt->data;
}
++d;
idx = (idx + 1) & (map->tablelen - 1);
}
return NULL;
}
int nghttp2_map_remove(nghttp2_map *map, key_type key) {
uint32_t h;
nghttp2_map_entry **dst;
int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key) {
uint32_t h = hash(key);
size_t idx = h2idx(h, map->tablelenbits), didx;
nghttp2_map_bucket *bkt;
size_t d = 0;
h = hash(key, map->tablelen);
for (;;) {
bkt = &map->table[idx];
for (dst = &map->table[h]; *dst; dst = &(*dst)->next) {
if ((*dst)->key != key) {
continue;
if (bkt->data == NULL ||
d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
*dst = (*dst)->next;
--map->size;
return 0;
if (bkt->key == key) {
map_bucket_set_data(bkt, 0, 0, NULL);
didx = idx;
idx = (idx + 1) & (map->tablelen - 1);
for (;;) {
bkt = &map->table[idx];
if (bkt->data == NULL ||
distance(map->tablelen, map->tablelenbits, bkt, idx) == 0) {
break;
}
map->table[didx] = *bkt;
map_bucket_set_data(bkt, 0, 0, NULL);
didx = idx;
idx = (idx + 1) & (map->tablelen - 1);
}
--map->size;
return 0;
}
++d;
idx = (idx + 1) & (map->tablelen - 1);
}
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
void nghttp2_map_clear(nghttp2_map *map) {
memset(map->table, 0, sizeof(*map->table) * map->tablelen);
map->size = 0;
}
size_t nghttp2_map_size(nghttp2_map *map) { return map->size; }

View File

@@ -1,7 +1,8 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2012 Tatsuhiro Tsujikawa
* Copyright (c) 2017 ngtcp2 contributors
* Copyright (c) 2012 nghttp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -30,27 +31,25 @@
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
#include "nghttp2_int.h"
#include "nghttp2_mem.h"
/* Implementation of unordered map */
typedef int32_t key_type;
typedef int32_t nghttp2_map_key_type;
typedef struct nghttp2_map_entry {
struct nghttp2_map_entry *next;
key_type key;
#if SIZEOF_INT_P == 4
/* we requires 8 bytes aligment */
int64_t pad;
#endif
} nghttp2_map_entry;
typedef struct nghttp2_map_bucket {
uint32_t hash;
nghttp2_map_key_type key;
void *data;
} nghttp2_map_bucket;
typedef struct {
nghttp2_map_entry **table;
typedef struct nghttp2_map {
nghttp2_map_bucket *table;
nghttp2_mem *mem;
size_t size;
uint32_t tablelen;
uint32_t tablelenbits;
} nghttp2_map;
/*
@@ -74,21 +73,14 @@ void nghttp2_map_free(nghttp2_map *map);
/*
* Deallocates each entries using |func| function and any resources
* allocated for |map|. The |func| function is responsible for freeing
* given the |entry| object. The |ptr| will be passed to the |func| as
* given the |data| object. The |ptr| will be passed to the |func| as
* send argument. The return value of the |func| will be ignored.
*/
void nghttp2_map_each_free(nghttp2_map *map,
int (*func)(nghttp2_map_entry *entry, void *ptr),
void nghttp2_map_each_free(nghttp2_map *map, int (*func)(void *data, void *ptr),
void *ptr);
/*
* Initializes the |entry| with the |key|. All entries to be inserted
* to the map must be initialized with this function.
*/
void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key);
/*
* Inserts the new |entry| with the key |entry->key| to the map |map|.
* Inserts the new |data| with the |key| to the map |map|.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
@@ -98,25 +90,30 @@ void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key);
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *entry);
int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data);
/*
* Returns the entry associated by the key |key|. If there is no such
* entry, this function returns NULL.
* Returns the data associated by the key |key|. If there is no such
* data, this function returns NULL.
*/
nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key);
void *nghttp2_map_find(nghttp2_map *map, nghttp2_map_key_type key);
/*
* Removes the entry associated by the key |key| from the |map|. The
* removed entry is not freed by this function.
* Removes the data associated by the key |key| from the |map|. The
* removed data is not freed by this function.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_INVALID_ARGUMENT
* The entry associated by |key| does not exist.
* The data associated by |key| does not exist.
*/
int nghttp2_map_remove(nghttp2_map *map, key_type key);
int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key);
/*
* Removes all entries from |map|.
*/
void nghttp2_map_clear(nghttp2_map *map);
/*
* Returns the number of items stored in the map |map|.
@@ -124,21 +121,22 @@ int nghttp2_map_remove(nghttp2_map *map, key_type key);
size_t nghttp2_map_size(nghttp2_map *map);
/*
* Applies the function |func| to each entry in the |map| with the
* Applies the function |func| to each data in the |map| with the
* optional user supplied pointer |ptr|.
*
* If the |func| returns 0, this function calls the |func| with the
* next entry. If the |func| returns nonzero, it will not call the
* next data. If the |func| returns nonzero, it will not call the
* |func| for further entries and return the return value of the
* |func| immediately. Thus, this function returns 0 if all the
* invocations of the |func| return 0, or nonzero value which the last
* invocation of |func| returns.
*
* Don't use this function to free each entry. Use
* Don't use this function to free each data. Use
* nghttp2_map_each_free() instead.
*/
int nghttp2_map_each(nghttp2_map *map,
int (*func)(nghttp2_map_entry *entry, void *ptr),
int nghttp2_map_each(nghttp2_map *map, int (*func)(void *data, void *ptr),
void *ptr);
void nghttp2_map_print_distance(nghttp2_map *map);
#endif /* NGHTTP2_MAP_H */

View File

@@ -42,7 +42,7 @@
#if defined(WIN32)
/* Windows requires ws2_32 library for ntonl family functions. We
define inline functions for those function so that we don't have
dependeny on that lib. */
dependency on that lib. */
# ifdef _MSC_VER
# define STIN static __inline
@@ -53,7 +53,7 @@
STIN uint32_t htonl(uint32_t hostlong) {
uint32_t res;
unsigned char *p = (unsigned char *)&res;
*p++ = hostlong >> 24;
*p++ = (unsigned char)(hostlong >> 24);
*p++ = (hostlong >> 16) & 0xffu;
*p++ = (hostlong >> 8) & 0xffu;
*p = hostlong & 0xffu;
@@ -63,7 +63,7 @@ STIN uint32_t htonl(uint32_t hostlong) {
STIN uint16_t htons(uint16_t hostshort) {
uint16_t res;
unsigned char *p = (unsigned char *)&res;
*p++ = hostshort >> 8;
*p++ = (unsigned char)(hostshort >> 8);
*p = hostshort & 0xffu;
return res;
}

View File

@@ -90,6 +90,10 @@ void nghttp2_option_set_builtin_recv_extension_type(nghttp2_option *option,
option->opt_set_mask |= NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES;
option->builtin_recv_ext_types |= NGHTTP2_TYPEMASK_ORIGIN;
return;
case NGHTTP2_PRIORITY_UPDATE:
option->opt_set_mask |= NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES;
option->builtin_recv_ext_types |= NGHTTP2_TYPEMASK_PRIORITY_UPDATE;
return;
default:
return;
}
@@ -121,3 +125,21 @@ void nghttp2_option_set_max_outbound_ack(nghttp2_option *option, size_t val) {
option->opt_set_mask |= NGHTTP2_OPT_MAX_OUTBOUND_ACK;
option->max_outbound_ack = val;
}
void nghttp2_option_set_max_settings(nghttp2_option *option, size_t val) {
option->opt_set_mask |= NGHTTP2_OPT_MAX_SETTINGS;
option->max_settings = val;
}
void nghttp2_option_set_server_fallback_rfc7540_priorities(
nghttp2_option *option, int val) {
option->opt_set_mask |= NGHTTP2_OPT_SERVER_FALLBACK_RFC7540_PRIORITIES;
option->server_fallback_rfc7540_priorities = val;
}
void nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(
nghttp2_option *option, int val) {
option->opt_set_mask |=
NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION;
option->no_rfc9113_leading_and_trailing_ws_validation = val;
}

View File

@@ -67,6 +67,9 @@ typedef enum {
NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE = 1 << 9,
NGHTTP2_OPT_NO_CLOSED_STREAMS = 1 << 10,
NGHTTP2_OPT_MAX_OUTBOUND_ACK = 1 << 11,
NGHTTP2_OPT_MAX_SETTINGS = 1 << 12,
NGHTTP2_OPT_SERVER_FALLBACK_RFC7540_PRIORITIES = 1 << 13,
NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION = 1 << 14,
} nghttp2_option_flag;
/**
@@ -85,6 +88,10 @@ struct nghttp2_option {
* NGHTTP2_OPT_MAX_OUTBOUND_ACK
*/
size_t max_outbound_ack;
/**
* NGHTTP2_OPT_MAX_SETTINGS
*/
size_t max_settings;
/**
* Bitwise OR of nghttp2_option_flag to determine that which fields
* are specified.
@@ -122,6 +129,14 @@ struct nghttp2_option {
* NGHTTP2_OPT_NO_CLOSED_STREAMS
*/
int no_closed_streams;
/**
* NGHTTP2_OPT_SERVER_FALLBACK_RFC7540_PRIORITIES
*/
int server_fallback_rfc7540_priorities;
/**
* NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION
*/
int no_rfc9113_leading_and_trailing_ws_validation;
/**
* NGHTTP2_OPT_USER_RECV_EXT_TYPES
*/

View File

@@ -89,6 +89,9 @@ void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) {
case NGHTTP2_ORIGIN:
nghttp2_frame_origin_free(&frame->ext, mem);
break;
case NGHTTP2_PRIORITY_UPDATE:
nghttp2_frame_priority_update_free(&frame->ext, mem);
break;
default:
assert(0);
break;

View File

@@ -111,7 +111,7 @@ struct nghttp2_outbound_item {
to this structure to avoid frequent memory allocation. */
nghttp2_ext_frame_payload ext_frame_payload;
nghttp2_aux_data aux_data;
/* The priority used in priority comparion. Smaller is served
/* The priority used in priority comparison. Smaller is served
earlier. For PING, SETTINGS and non-DATA frames (excluding
response HEADERS frame) have dedicated cycle value defined above.
For DATA frame, cycle is computed by taking into account of

View File

@@ -29,13 +29,12 @@
#include "nghttp2_helper.h"
int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem) {
void nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem) {
pq->mem = mem;
pq->capacity = 0;
pq->q = NULL;
pq->length = 0;
pq->less = less;
return 0;
}
void nghttp2_pq_free(nghttp2_pq *pq) {

View File

@@ -55,14 +55,8 @@ typedef struct {
/*
* Initializes priority queue |pq| with compare function |cmp|.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory.
*/
int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem);
void nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem);
/*
* Deallocates any resources allocated for |pq|. The stored items are
@@ -114,7 +108,7 @@ typedef int (*nghttp2_pq_item_cb)(nghttp2_pq_entry *item, void *arg);
void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg);
/*
* Applys |fun| to each item in |pq|. The |arg| is passed as arg
* Applies |fun| to each item in |pq|. The |arg| is passed as arg
* parameter to callback function. This function must not change the
* ordering key. If the return value from callback is nonzero, this
* function returns 1 immediately without iterating remaining items.

File diff suppressed because it is too large Load Diff

View File

@@ -52,7 +52,9 @@ typedef enum {
NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC = 1 << 1,
NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2,
NGHTTP2_OPTMASK_NO_AUTO_PING_ACK = 1 << 3,
NGHTTP2_OPTMASK_NO_CLOSED_STREAMS = 1 << 4
NGHTTP2_OPTMASK_NO_CLOSED_STREAMS = 1 << 4,
NGHTTP2_OPTMASK_SERVER_FALLBACK_RFC7540_PRIORITIES = 1 << 5,
NGHTTP2_OPTMASK_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION = 1 << 6,
} nghttp2_optmask;
/*
@@ -62,7 +64,8 @@ typedef enum {
typedef enum {
NGHTTP2_TYPEMASK_NONE = 0,
NGHTTP2_TYPEMASK_ALTSVC = 1 << 0,
NGHTTP2_TYPEMASK_ORIGIN = 1 << 1
NGHTTP2_TYPEMASK_ORIGIN = 1 << 1,
NGHTTP2_TYPEMASK_PRIORITY_UPDATE = 1 << 2
} nghttp2_typemask;
typedef enum {
@@ -151,10 +154,8 @@ typedef struct {
/* padding length for the current frame */
size_t padlen;
nghttp2_inbound_state state;
/* Small buffer. Currently the largest contiguous chunk to buffer
is frame header. We buffer part of payload, but they are smaller
than frame header. */
uint8_t raw_sbuf[NGHTTP2_FRAME_HDLEN];
/* Small fixed sized buffer. */
uint8_t raw_sbuf[32];
} nghttp2_inbound_frame;
typedef struct {
@@ -165,6 +166,7 @@ typedef struct {
uint32_t max_frame_size;
uint32_t max_header_list_size;
uint32_t enable_connect_protocol;
uint32_t no_rfc7540_priorities;
} nghttp2_settings_storage;
typedef enum {
@@ -202,6 +204,12 @@ struct nghttp2_session {
response) frame, which are subject to
SETTINGS_MAX_CONCURRENT_STREAMS limit. */
nghttp2_outbound_queue ob_syn;
/* 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;
@@ -227,6 +235,9 @@ struct nghttp2_session {
/* Queue of In-flight SETTINGS values. SETTINGS bearing ACK is not
considered as in-flight. */
nghttp2_inflight_settings *inflight_settings_head;
/* Sequential number across all streams to process streams in
FIFO. */
uint64_t stream_seq;
/* The number of outgoing streams. This will be capped by
remote_settings.max_concurrent_streams. */
size_t num_outgoing_streams;
@@ -267,6 +278,8 @@ struct nghttp2_session {
/* The maximum length of header block to send. Calculated by the
same way as nghttp2_hd_deflate_bound() does. */
size_t max_send_header_block_length;
/* The maximum number of settings accepted per SETTINGS frame. */
size_t max_settings;
/* Next Stream ID. Made unsigned int to detect >= (1 << 31). */
uint32_t next_stream_id;
/* The last stream ID this session initiated. For client session,
@@ -326,6 +339,11 @@ struct nghttp2_session {
/* Unacked local ENABLE_CONNECT_PROTOCOL value. We use this to
accept :protocol header field before SETTINGS_ACK is received. */
uint8_t pending_enable_connect_protocol;
/* Unacked local SETTINGS_NO_RFC7540_PRIORITIES value, which is
effective before it is acknowledged. */
uint8_t pending_no_rfc7540_priorities;
/* Turn on fallback to RFC 7540 priorities; for server use only. */
uint8_t fallback_rfc7540_priorities;
/* Nonzero if the session is server side. */
uint8_t server;
/* Flags indicating GOAWAY is sent and/or received. The flags are
@@ -406,7 +424,7 @@ int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
uint32_t error_code);
/*
* Adds PING frame. This is a convenient functin built on top of
* Adds PING frame. This is a convenient function built on top of
* nghttp2_session_add_frame() to add PING easily.
*
* If the |opaque_data| is not NULL, it must point to 8 bytes memory
@@ -771,6 +789,19 @@ int nghttp2_session_on_altsvc_received(nghttp2_session *session,
int nghttp2_session_on_origin_received(nghttp2_session *session,
nghttp2_frame *frame);
/*
* Called when PRIORITY_UPDATE is received, assuming |frame| is
* properly initialized.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_CALLBACK_FAILURE
* The callback function failed.
*/
int nghttp2_session_on_priority_update_received(nghttp2_session *session,
nghttp2_frame *frame);
/*
* Called when DATA is received, assuming |frame| is properly
* initialized.
@@ -898,4 +929,36 @@ int nghttp2_session_terminate_session_with_reason(nghttp2_session *session,
uint32_t error_code,
const char *reason);
/*
* Accumulates received bytes |delta_size| for connection-level flow
* control and decides whether to send WINDOW_UPDATE to the
* connection. If NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE is set,
* WINDOW_UPDATE will not be sent.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory.
*/
int nghttp2_session_update_recv_connection_window_size(nghttp2_session *session,
size_t delta_size);
/*
* Accumulates received bytes |delta_size| for stream-level flow
* control and decides whether to send WINDOW_UPDATE to that stream.
* If NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE is set, WINDOW_UPDATE will not
* be sent.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory.
*/
int nghttp2_session_update_recv_stream_window_size(nghttp2_session *session,
nghttp2_stream *stream,
size_t delta_size,
int send_window_update);
#endif /* NGHTTP2_SESSION_H */

View File

@@ -33,7 +33,7 @@
#include "nghttp2_frame.h"
/* Maximum distance between any two stream's cycle in the same
prirority queue. Imagine stream A's cycle is A, and stream B's
priority queue. Imagine stream A's cycle is A, and stream B's
cycle is B, and A < B. The cycle is unsigned 32 bit integer, it
may get overflow. Because of how we calculate the next cycle
value, if B - A is less than or equals to
@@ -62,7 +62,6 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
int32_t weight, int32_t remote_initial_window_size,
int32_t local_initial_window_size,
void *stream_user_data, nghttp2_mem *mem) {
nghttp2_map_entry_init(&stream->map_entry, (key_type)stream_id);
nghttp2_pq_init(&stream->obq, stream_less, mem);
stream->stream_id = stream_id;
@@ -101,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) {
@@ -485,6 +486,10 @@ int nghttp2_stream_attach_item(nghttp2_stream *stream,
stream->item = item;
if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
return 0;
}
rv = stream_update_dep_on_attach_item(stream);
if (rv != 0) {
/* This may relave stream->queued == 1, but stream->item == NULL.
@@ -504,6 +509,10 @@ int nghttp2_stream_detach_item(nghttp2_stream *stream) {
stream->item = NULL;
stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_DEFERRED_ALL);
if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
return 0;
}
return stream_update_dep_on_detach_item(stream);
}
@@ -515,6 +524,10 @@ int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags) {
stream->flags |= flags;
if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
return 0;
}
return stream_update_dep_on_detach_item(stream);
}
@@ -530,6 +543,10 @@ int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags) {
return 0;
}
if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
return 0;
}
return stream_update_dep_on_attach_item(stream);
}

View File

@@ -90,8 +90,15 @@ typedef enum {
NGHTTP2_STREAM_FLAG_DEFERRED_USER = 0x08,
/* bitwise OR of NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and
NGHTTP2_STREAM_FLAG_DEFERRED_USER. */
NGHTTP2_STREAM_FLAG_DEFERRED_ALL = 0x0c
NGHTTP2_STREAM_FLAG_DEFERRED_ALL = 0x0c,
/* Indicates that this stream is not subject to RFC7540
priorities scheme. */
NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES = 0x10,
/* Ignore client RFC 9218 priority signal. */
NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES = 0x20,
/* Indicates that RFC 9113 leading and trailing white spaces
validation against a field value is not performed. */
NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION = 0x40,
} nghttp2_stream_flag;
/* HTTP related flags to enforce HTTP semantics */
@@ -132,11 +139,14 @@ typedef enum {
/* set if final response is expected */
NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 14,
NGHTTP2_HTTP_FLAG__PROTOCOL = 1 << 15,
/* set if priority header field is received */
NGHTTP2_HTTP_FLAG_PRIORITY = 1 << 16,
/* set if an error is encountered while parsing priority header
field */
NGHTTP2_HTTP_FLAG_BAD_PRIORITY = 1 << 17,
} nghttp2_http_flag;
struct nghttp2_stream {
/* Intrusive Map */
nghttp2_map_entry map_entry;
/* Entry for dep_prev->obq */
nghttp2_pq_entry pq_entry;
/* Priority Queue storing direct descendant (nghttp2_stream). Only
@@ -206,7 +216,7 @@ struct nghttp2_stream {
/* status code from remote server */
int16_t status_code;
/* Bitwise OR of zero or more nghttp2_http_flag values */
uint16_t http_flags;
uint32_t http_flags;
/* This is bitwise-OR of 0 or more of nghttp2_stream_flag. */
uint8_t flags;
/* Bitwise OR of zero or more nghttp2_shut_flag values */
@@ -220,6 +230,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

@@ -196,7 +196,8 @@ int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
flags &= NGHTTP2_FLAG_END_STREAM;
if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) {
if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec) &&
session->remote_settings.no_rfc7540_priorities != 1) {
rv = detect_self_dependency(session, stream_id, pri_spec);
if (rv != 0) {
return rv;
@@ -229,6 +230,10 @@ int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags,
mem = &session->mem;
if (session->remote_settings.no_rfc7540_priorities == 1) {
return 0;
}
if (stream_id == 0 || pri_spec == NULL) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
@@ -450,6 +455,13 @@ int nghttp2_session_set_local_window_size(nghttp2_session *session,
if (rv != 0) {
return rv;
}
if (window_size_increment > 0) {
return nghttp2_session_add_window_update(session, 0, stream_id,
window_size_increment);
}
return nghttp2_session_update_recv_connection_window_size(session, 0);
} else {
stream = nghttp2_session_get_stream(session, stream_id);
@@ -476,14 +488,15 @@ int nghttp2_session_set_local_window_size(nghttp2_session *session,
if (rv != 0) {
return rv;
}
}
if (window_size_increment > 0) {
return nghttp2_session_add_window_update(session, 0, stream_id,
window_size_increment);
}
if (window_size_increment > 0) {
return nghttp2_session_add_window_update(session, 0, stream_id,
window_size_increment);
}
return 0;
return nghttp2_session_update_recv_stream_window_size(session, stream, 0,
1);
}
}
int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags,
@@ -654,6 +667,78 @@ fail_item_malloc:
return rv;
}
int nghttp2_submit_priority_update(nghttp2_session *session, uint8_t flags,
int32_t stream_id,
const uint8_t *field_value,
size_t field_value_len) {
nghttp2_mem *mem;
uint8_t *buf, *p;
nghttp2_outbound_item *item;
nghttp2_frame *frame;
nghttp2_ext_priority_update *priority_update;
int rv;
(void)flags;
mem = &session->mem;
if (session->server) {
return NGHTTP2_ERR_INVALID_STATE;
}
if (session->remote_settings.no_rfc7540_priorities == 0) {
return 0;
}
if (stream_id == 0 || 4 + field_value_len > NGHTTP2_MAX_PAYLOADLEN) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
if (field_value_len) {
buf = nghttp2_mem_malloc(mem, field_value_len + 1);
if (buf == NULL) {
return NGHTTP2_ERR_NOMEM;
}
p = nghttp2_cpymem(buf, field_value, field_value_len);
*p = '\0';
} else {
buf = NULL;
}
item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
if (item == NULL) {
rv = NGHTTP2_ERR_NOMEM;
goto fail_item_malloc;
}
nghttp2_outbound_item_init(item);
item->aux_data.ext.builtin = 1;
priority_update = &item->ext_frame_payload.priority_update;
frame = &item->frame;
frame->ext.payload = priority_update;
nghttp2_frame_priority_update_init(&frame->ext, stream_id, buf,
field_value_len);
rv = nghttp2_session_add_item(session, item);
if (rv != 0) {
nghttp2_frame_priority_update_free(&frame->ext, mem);
nghttp2_mem_free(mem, item);
return rv;
}
return 0;
fail_item_malloc:
free(buf);
return rv;
}
static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec,
const nghttp2_data_provider *data_prd) {
uint8_t flags = NGHTTP2_FLAG_NONE;
@@ -680,7 +765,8 @@ int32_t nghttp2_submit_request(nghttp2_session *session,
return NGHTTP2_ERR_PROTO;
}
if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) {
if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec) &&
session->remote_settings.no_rfc7540_priorities != 1) {
rv = detect_self_dependency(session, -1, pri_spec);
if (rv != 0) {
return rv;