/*
 * nghttp3
 *
 * Copyright (c) 2019 nghttp3 contributors
 * Copyright (c) 2015 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 "nghttp3_http.h"

#include <string.h>
#include <assert.h>

#include "nghttp3_stream.h"
#include "nghttp3_macro.h"
#include "nghttp3_conv.h"

static uint8_t downcase(uint8_t c) {
  return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
}

static int memieq(const void *a, const void *b, size_t n) {
  size_t i;
  const uint8_t *aa = a, *bb = b;

  for (i = 0; i < n; ++i) {
    if (downcase(aa[i]) != downcase(bb[i])) {
      return 0;
    }
  }
  return 1;
}

#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))

static int64_t parse_uint(const uint8_t *s, size_t len) {
  int64_t n = 0;
  size_t i;
  if (len == 0) {
    return -1;
  }
  for (i = 0; i < len; ++i) {
    if ('0' <= s[i] && s[i] <= '9') {
      if (n > INT64_MAX / 10) {
        return -1;
      }
      n *= 10;
      if (n > INT64_MAX - (s[i] - '0')) {
        return -1;
      }
      n += s[i] - '0';
      continue;
    }
    return -1;
  }
  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(nghttp3_http_state *http,
                               const nghttp3_qpack_nv *nv, int flag) {
  if (http->flags & flag) {
    return 0;
  }
  if (lws(nv->value->base, nv->value->len)) {
    return 0;
  }
  http->flags = (uint16_t)(http->flags | flag);
  return 1;
}

static int expect_response_body(nghttp3_http_state *http) {
  return (http->flags & NGHTTP3_HTTP_FLAG_METH_HEAD) == 0 &&
         http->status_code / 100 != 1 && http->status_code != 304 &&
         http->status_code != 204;
}

/* For "http" or "https" URIs, OPTIONS request may have "*" in :path
   header field to represent system-wide OPTIONS request.  Otherwise,
   :path header field value must start with "/".  This function must
   be called after ":method" header field was received.  This function
   returns nonzero if path is valid.*/
static int check_path(nghttp3_http_state *http) {
  return (http->flags & NGHTTP3_HTTP_FLAG_SCHEME_HTTP) == 0 ||
         ((http->flags & NGHTTP3_HTTP_FLAG_PATH_REGULAR) ||
          ((http->flags & NGHTTP3_HTTP_FLAG_METH_OPTIONS) &&
           (http->flags & NGHTTP3_HTTP_FLAG_PATH_ASTERISK)));
}

int nghttp3_http_parse_priority(nghttp3_pri *dest, const uint8_t *value,
                                size_t len) {
  nghttp3_pri pri = *dest;
  const uint8_t *p = value, *end = value + len;

  for (;;) {
    for (; p != end && (*p == ' ' || *p == '\t'); ++p)
      ;

    if (p == end) {
      break;
    }

    switch (*p) {
    case 'u':
      ++p;

      if (p + 2 > end || *p++ != '=') {
        return NGHTTP3_ERR_INVALID_ARGUMENT;
      }

      if (!('0' <= *p && *p <= '7')) {
        return NGHTTP3_ERR_INVALID_ARGUMENT;
      }

      pri.urgency = (uint32_t)(*p++ - '0');

      if (p == end) {
        goto fin;
      }

      if (*p++ != ',') {
        return NGHTTP3_ERR_INVALID_ARGUMENT;
      }

      break;
    case 'i':
      ++p;

      if (p == end) {
        pri.inc = 1;
        goto fin;
      }

      if (*p == ',') {
        pri.inc = 1;
        ++p;
        break;
      }

      if (p + 3 > end || *p != '=' || *(p + 1) != '?' ||
          (*(p + 2) != '0' && *(p + 2) != '1')) {
        return NGHTTP3_ERR_INVALID_ARGUMENT;
      }

      pri.inc = *(p + 2) == '1';

      p += 3;

      if (p == end) {
        goto fin;
      }

      if (*p++ != ',') {
        return NGHTTP3_ERR_INVALID_ARGUMENT;
      }

      break;
    default:
      return NGHTTP3_ERR_INVALID_ARGUMENT;
    }

    if (p == end) {
      return NGHTTP3_ERR_INVALID_ARGUMENT;
    }
  }

fin:

  *dest = pri;

  return 0;
}

static int http_request_on_header(nghttp3_http_state *http, int64_t frame_type,
                                  nghttp3_qpack_nv *nv, int trailers,
                                  int connect_protocol) {
  nghttp3_pri pri;

  if (nv->name->base[0] == ':') {
    if (trailers ||
        (http->flags & NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
  }

  switch (nv->token) {
  case NGHTTP3_QPACK_TOKEN__AUTHORITY:
    if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__AUTHORITY)) {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
    break;
  case NGHTTP3_QPACK_TOKEN__METHOD:
    if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__METHOD)) {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
    switch (nv->value->len) {
    case 4:
      if (lstreq("HEAD", nv->value->base, nv->value->len)) {
        http->flags |= NGHTTP3_HTTP_FLAG_METH_HEAD;
      }
      break;
    case 7:
      switch (nv->value->base[6]) {
      case 'T':
        if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
          if (frame_type == NGHTTP3_FRAME_PUSH_PROMISE) {
            /* we won't allow CONNECT for push */
            return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
          }
          http->flags |= NGHTTP3_HTTP_FLAG_METH_CONNECT;
        }
        break;
      case 'S':
        if (lstreq("OPTIONS", nv->value->base, nv->value->len)) {
          http->flags |= NGHTTP3_HTTP_FLAG_METH_OPTIONS;
        }
        break;
      }
      break;
    }
    break;
  case NGHTTP3_QPACK_TOKEN__PATH:
    if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__PATH)) {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
    if (nv->value->base[0] == '/') {
      http->flags |= NGHTTP3_HTTP_FLAG_PATH_REGULAR;
    } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
      http->flags |= NGHTTP3_HTTP_FLAG_PATH_ASTERISK;
    }
    break;
  case NGHTTP3_QPACK_TOKEN__SCHEME:
    if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__SCHEME)) {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
    if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) ||
        (nv->value->len == 5 && memieq("https", nv->value->base, 5))) {
      http->flags |= NGHTTP3_HTTP_FLAG_SCHEME_HTTP;
    }
    break;
  case NGHTTP3_QPACK_TOKEN__PROTOCOL:
    if (!connect_protocol) {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }

    if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__PROTOCOL)) {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
    break;
  case NGHTTP3_QPACK_TOKEN_HOST:
    if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG_HOST)) {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
    break;
  case NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH: {
    /* https://tools.ietf.org/html/rfc7230#section-4.1.2: A sender
       MUST NOT generate a trailer that contains a field necessary for
       message framing (e.g., Transfer-Encoding and Content-Length),
       ... */
    if (trailers) {
      return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
    }
    if (http->content_length != -1) {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
    http->content_length = parse_uint(nv->value->base, nv->value->len);
    if (http->content_length == -1) {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
    break;
  }
  /* disallowed header fields */
  case NGHTTP3_QPACK_TOKEN_CONNECTION:
  case NGHTTP3_QPACK_TOKEN_KEEP_ALIVE:
  case NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION:
  case NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING:
  case NGHTTP3_QPACK_TOKEN_UPGRADE:
    return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
  case NGHTTP3_QPACK_TOKEN_TE:
    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
    break;
  case NGHTTP3_QPACK_TOKEN_PRIORITY:
    pri.urgency = nghttp3_pri_uint8_urgency(http->pri);
    pri.inc = nghttp3_pri_uint8_inc(http->pri);
    if (nghttp3_http_parse_priority(&pri, nv->value->base, nv->value->len) ==
        0) {
      http->pri = nghttp3_pri_to_uint8(&pri);
    }
    break;
  default:
    if (nv->name->base[0] == ':') {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
  }

  if (nv->name->base[0] != ':') {
    http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
  }

  return 0;
}

static int http_response_on_header(nghttp3_http_state *http,
                                   nghttp3_qpack_nv *nv, int trailers) {
  if (nv->name->base[0] == ':') {
    if (trailers ||
        (http->flags & NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
  }

  switch (nv->token) {
  case NGHTTP3_QPACK_TOKEN__STATUS: {
    if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__STATUS)) {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
    if (nv->value->len != 3) {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
    http->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len);
    if (http->status_code < 100 || http->status_code == 101) {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
    break;
  }
  case NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH: {
    /* https://tools.ietf.org/html/rfc7230#section-4.1.2: A sender
       MUST NOT generate a trailer that contains a field necessary for
       message framing (e.g., Transfer-Encoding and Content-Length),
       ... */
    if (trailers) {
      return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
    }
    if (http->status_code == 204) {
      /* content-length header field in 204 response is prohibited by
         RFC 7230.  But some widely used servers send content-length:
         0.  Until they get fixed, we ignore it. */
      if (http->content_length != -1) {
        /* Found multiple content-length field */
        return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
      }
      if (!lstrieq("0", nv->value->base, nv->value->len)) {
        return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
      }
      http->content_length = 0;
      return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
    }
    if (http->status_code / 100 == 1) {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
    /* https://tools.ietf.org/html/rfc7230#section-3.3.3 */
    if (http->status_code / 100 == 2 &&
        (http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT)) {
      return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
    }
    if (http->content_length != -1) {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
    http->content_length = parse_uint(nv->value->base, nv->value->len);
    if (http->content_length == -1) {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
    break;
  }
  /* disallowed header fields */
  case NGHTTP3_QPACK_TOKEN_CONNECTION:
  case NGHTTP3_QPACK_TOKEN_KEEP_ALIVE:
  case NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION:
  case NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING:
  case NGHTTP3_QPACK_TOKEN_UPGRADE:
    return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
  case NGHTTP3_QPACK_TOKEN_TE:
    if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
    break;
  default:
    if (nv->name->base[0] == ':') {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
  }

  if (nv->name->base[0] != ':') {
    http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
  }

  return 0;
}

/* Generated by genauthroitychartbl.py */
static char VALID_AUTHORITY_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 /* "    */, 0 /* #    */,
    1 /* $    */, 1 /* %    */, 1 /* &    */, 1 /* '    */,
    1 /* (    */, 1 /* )    */, 1 /* *    */, 1 /* +    */,
    1 /* ,    */, 1 /* -    */, 1 /* .    */, 0 /* /    */,
    1 /* 0    */, 1 /* 1    */, 1 /* 2    */, 1 /* 3    */,
    1 /* 4    */, 1 /* 5    */, 1 /* 6    */, 1 /* 7    */,
    1 /* 8    */, 1 /* 9    */, 1 /* :    */, 1 /* ;    */,
    0 /* <    */, 1 /* =    */, 0 /* >    */, 0 /* ?    */,
    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 /* ]    */, 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 /* }    */, 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 int check_authority(const uint8_t *value, size_t len) {
  const uint8_t *last;
  for (last = value + len; value != last; ++value) {
    if (!VALID_AUTHORITY_CHARS[*value]) {
      return 0;
    }
  }
  return 1;
}

static int check_scheme(const uint8_t *value, size_t len) {
  const uint8_t *last;
  if (len == 0) {
    return 0;
  }

  if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
    return 0;
  }

  last = value + len;
  ++value;

  for (; value != last; ++value) {
    if (!(('A' <= *value && *value <= 'Z') ||
          ('a' <= *value && *value <= 'z') ||
          ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
          *value == '.')) {
      return 0;
    }
  }
  return 1;
}

int nghttp3_http_on_header(nghttp3_http_state *http, int64_t frame_type,
                           nghttp3_qpack_nv *nv, int request, int trailers) {
  int rv;
  size_t i;
  uint8_t c;

  if (!nghttp3_check_header_name(nv->name->base, nv->name->len)) {
    if (nv->name->len > 0 && nv->name->base[0] == ':') {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
    /* header field name must be lower-cased without exception */
    for (i = 0; i < nv->name->len; ++i) {
      c = nv->name->base[i];
      if ('A' <= c && c <= 'Z') {
        return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
      }
    }
    /* When ignoring regular header fields, we set this flag so that
       we still enforce header field ordering rule for pseudo header
       fields. */
    http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
    return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
  }

  if (nv->token == NGHTTP3_QPACK_TOKEN__AUTHORITY ||
      nv->token == NGHTTP3_QPACK_TOKEN_HOST) {
    rv = check_authority(nv->value->base, nv->value->len);
  } else if (nv->token == NGHTTP3_QPACK_TOKEN__SCHEME) {
    rv = check_scheme(nv->value->base, nv->value->len);
  } else {
    rv = nghttp3_check_header_value(nv->value->base, nv->value->len);
  }

  if (rv == 0) {
    assert(nv->name->len > 0);
    if (nv->name->base[0] == ':') {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
    /* When ignoring regular header fields, we set this flag so that
       we still enforce header field ordering rule for pseudo header
       fields. */
    http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
    return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
  }

  if (request) {
    return http_request_on_header(http, frame_type, nv, trailers,
                                  /* connect_protocol = */ 0);
  }

  return http_response_on_header(http, nv, trailers);
}

int nghttp3_http_on_request_headers(nghttp3_http_state *http) {
  if (!(http->flags & NGHTTP3_HTTP_FLAG__PROTOCOL) &&
      (http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT)) {
    if ((http->flags & (NGHTTP3_HTTP_FLAG__SCHEME | NGHTTP3_HTTP_FLAG__PATH)) ||
        (http->flags & NGHTTP3_HTTP_FLAG__AUTHORITY) == 0) {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
    http->content_length = -1;
  } else {
    if ((http->flags & NGHTTP3_HTTP_FLAG_REQ_HEADERS) !=
            NGHTTP3_HTTP_FLAG_REQ_HEADERS ||
        (http->flags &
         (NGHTTP3_HTTP_FLAG__AUTHORITY | NGHTTP3_HTTP_FLAG_HOST)) == 0) {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
    if ((http->flags & NGHTTP3_HTTP_FLAG__PROTOCOL) &&
        ((http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) == 0 ||
         (http->flags & NGHTTP3_HTTP_FLAG__AUTHORITY) == 0)) {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
    if (!check_path(http)) {
      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
    }
  }

  return 0;
}

int nghttp3_http_on_response_headers(nghttp3_http_state *http) {
  if ((http->flags & NGHTTP3_HTTP_FLAG__STATUS) == 0) {
    return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
  }

  if (http->status_code / 100 == 1) {
    /* non-final response */
    http->flags = (uint16_t)((http->flags & NGHTTP3_HTTP_FLAG_METH_ALL) |
                             NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE);
    http->content_length = -1;
    http->status_code = -1;
    return 0;
  }

  http->flags =
      (uint16_t)(http->flags & ~NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE);

  if (!expect_response_body(http)) {
    http->content_length = 0;
  } else if (http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) {
    http->content_length = -1;
  }

  return 0;
}

int nghttp3_http_on_remote_end_stream(nghttp3_stream *stream) {
  if (stream->flags & NGHTTP3_STREAM_FLAG_RESET) {
    return 0;
  }
  if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
      (stream->rx.http.content_length != -1 &&
       stream->rx.http.content_length != stream->rx.http.recv_content_length)) {
    return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
  }

  return 0;
}

int nghttp3_http_on_data_chunk(nghttp3_stream *stream, size_t n) {
  stream->rx.http.recv_content_length += (int64_t)n;

  if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
      (stream->rx.http.content_length != -1 &&
       stream->rx.http.recv_content_length > stream->rx.http.content_length)) {
    return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
  }

  return 0;
}

void nghttp3_http_record_request_method(nghttp3_stream *stream,
                                        const nghttp3_nv *nva, size_t nvlen) {
  size_t i;
  const nghttp3_nv *nv;

  /* TODO we should do this strictly. */
  for (i = 0; i < nvlen; ++i) {
    nv = &nva[i];
    if (!(nv->namelen == 7 && nv->name[6] == 'd' &&
          memcmp(":metho", nv->name, nv->namelen - 1) == 0)) {
      continue;
    }
    if (lstreq("CONNECT", nv->value, nv->valuelen)) {
      stream->rx.http.flags |= NGHTTP3_HTTP_FLAG_METH_CONNECT;
      return;
    }
    if (lstreq("HEAD", nv->value, nv->valuelen)) {
      stream->rx.http.flags |= NGHTTP3_HTTP_FLAG_METH_HEAD;
      return;
    }
    return;
  }
}

/* Generated by gennmchartbl.py */
static const int VALID_HD_NAME_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 /* @    */, 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 /* ]    */, 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 nghttp3_check_header_name(const uint8_t *name, size_t len) {
  const uint8_t *last;
  if (len == 0) {
    return 0;
  }
  if (*name == ':') {
    if (len == 1) {
      return 0;
    }
    ++name;
    --len;
  }
  for (last = name + len; name != last; ++name) {
    if (!VALID_HD_NAME_CHARS[*name]) {
      return 0;
    }
  }
  return 1;
}

/* Generated by genvchartbl.py */
static const int VALID_HD_VALUE_CHARS[] = {
    0 /* NUL  */, 0 /* SOH  */, 0 /* STX  */, 0 /* ETX  */,
    0 /* EOT  */, 0 /* ENQ  */, 0 /* ACK  */, 0 /* BEL  */,
    0 /* BS   */, 1 /* 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 /* !    */, 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 nghttp3_check_header_value(const uint8_t *value, size_t len) {
  const uint8_t *last;
  for (last = value + len; value != last; ++value) {
    if (!VALID_HD_VALUE_CHARS[*value]) {
      return 0;
    }
  }
  return 1;
}
