Browse Source

Merge branch 'upstream-nghttp2' into update-nghttp2

# By nghttp2 upstream
* upstream-nghttp2:
  nghttp2 2022-09-21 (87fef4ab)
Brad King 3 years ago
parent
commit
8b73605408

File diff suppressed because it is too large
+ 321 - 216
Utilities/cmnghttp2/lib/includes/nghttp2/nghttp2.h


+ 4 - 2
Utilities/cmnghttp2/lib/nghttp2_buf.c

@@ -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,

+ 1 - 1
Utilities/cmnghttp2/lib/nghttp2_buf.h

@@ -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 - 0
Utilities/cmnghttp2/lib/nghttp2_extpri.c

@@ -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 - 0
Utilities/cmnghttp2/lib/nghttp2_extpri.h

@@ -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 */

+ 104 - 7
Utilities/cmnghttp2/lib/nghttp2_frame.c

@@ -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;

+ 47 - 2
Utilities/cmnghttp2/lib/nghttp2_frame.h

@@ -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

+ 10 - 4
Utilities/cmnghttp2/lib/nghttp2_hd.c

@@ -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->settings_hd_table_bufsize_max = settings_max_dynamic_table_size;
+    inflater->ctx.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);
+  }
 
-  hd_context_shrink_table_size(&inflater->ctx, NULL);
   return 0;
 }
 

+ 1 - 0
Utilities/cmnghttp2/lib/nghttp2_hd.h

@@ -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;

+ 175 - 1
Utilities/cmnghttp2/lib/nghttp2_helper.c

@@ -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  */,

+ 787 - 26
Utilities/cmnghttp2/lib/nghttp2_http.c

@@ -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) {
-    return 0;
-  }
-  if (lws(nv->value->base, nv->value->len)) {
+                               uint32_t flag) {
+  if ((stream->http_flags & flag) || nv->value->len == 0) {
     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;
+}

+ 51 - 0
Utilities/cmnghttp2/lib/nghttp2_http.h

@@ -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 */

+ 202 - 78
Utilities/cmnghttp2/lib/nghttp2_map.c

@@ -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;
-      }
+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);
+}
+
+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;
+  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;
     }
-    entry->next = table[h];
-    table[h] = entry;
+
+    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);
   }
-  return 0;
 }
 
-/* new_tablelen must be power of 2 */
-static int resize(nghttp2_map *map, uint32_t new_tablelen) {
+/* 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_entry **new_table;
+  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;
+    }
+
+    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;
     }
 
-    *dst = (*dst)->next;
-    --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; }

+ 34 - 36
Utilities/cmnghttp2/lib/nghttp2_map.h

@@ -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 */

+ 3 - 3
Utilities/cmnghttp2/lib/nghttp2_net.h

@@ -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;
 }

+ 22 - 0
Utilities/cmnghttp2/lib/nghttp2_option.c

@@ -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;
+}

+ 15 - 0
Utilities/cmnghttp2/lib/nghttp2_option.h

@@ -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
    */

+ 3 - 0
Utilities/cmnghttp2/lib/nghttp2_outbound_item.c

@@ -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;

+ 1 - 1
Utilities/cmnghttp2/lib/nghttp2_outbound_item.h

@@ -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

+ 1 - 2
Utilities/cmnghttp2/lib/nghttp2_pq.c

@@ -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) {

+ 2 - 8
Utilities/cmnghttp2/lib/nghttp2_pq.h

@@ -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
+ 561 - 64
Utilities/cmnghttp2/lib/nghttp2_session.c


+ 70 - 7
Utilities/cmnghttp2/lib/nghttp2_session.h

@@ -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 */

+ 19 - 2
Utilities/cmnghttp2/lib/nghttp2_stream.c

@@ -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);
 }
 

+ 21 - 5
Utilities/cmnghttp2/lib/nghttp2_stream.h

@@ -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,

+ 94 - 8
Utilities/cmnghttp2/lib/nghttp2_submit.c

@@ -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;

Some files were not shown because too many files changed in this diff