RFC Errata


Errata Search

 
Source of RFC  
Summary Table Full Records

Found 2 records.

Status: Reported (1)

RFC 9204, "QPACK: Field Compression for HTTP/3", June 2022

Source of RFC: quic (wit)

Errata ID: 8410
Status: Reported
Type: Technical
Publication Format(s) : TEXT

Reported By: Kazu Yamamoto
Date Reported: 2025-05-08

Section Appendix C says:

base = dynamicTable.getInsertCount()
requiredInsertCount = 0
for line in fieldLines:
  staticIndex = staticTable.findIndex(line)
  if staticIndex is not None:
    encodeStaticIndexReference(streamBuffer, staticIndex)
    continue

  dynamicIndex = dynamicTable.findIndex(line)
  if dynamicIndex is None:
    # No matching entry.  Either insert+index or encode literal
    staticNameIndex = staticTable.findName(line.name)
    if staticNameIndex is None:
       dynamicNameIndex = dynamicTable.findName(line.name)

    if shouldIndex(line) and dynamicTable.canIndex(line):
      encodeInsert(encoderBuffer, staticNameIndex,
                   dynamicNameIndex, line)
      dynamicIndex = dynamicTable.add(line)

  if dynamicIndex is None:
    # Could not index it, literal
    if dynamicNameIndex is not None:
      # Encode literal with dynamic name, possibly above Base
      encodeDynamicLiteral(streamBuffer, dynamicNameIndex,
                           base, line)
      requiredInsertCount = max(requiredInsertCount,
                                dynamicNameIndex)
    else:
      # Encodes a literal with a static name or literal name
      encodeLiteral(streamBuffer, staticNameIndex, line)
  else:
    # Dynamic index reference
    assert(dynamicIndex is not None)
    requiredInsertCount = max(requiredInsertCount, dynamicIndex)
    # Encode dynamicIndex, possibly above Base
    encodeDynamicIndexReference(streamBuffer, dynamicIndex, base)

# encode the prefix
if requiredInsertCount == 0:
  encodeInteger(prefixBuffer, 0x00, 0, 8)
  encodeInteger(prefixBuffer, 0x00, 0, 7)
else:
  wireRIC = (
    requiredInsertCount
    % (2 * getMaxEntries(maxTableCapacity))
  ) + 1;
  encodeInteger(prefixBuffer, 0x00, wireRIC, 8)
  if base >= requiredInsertCount:
    encodeInteger(prefixBuffer, 0x00,
                  base - requiredInsertCount, 7)
  else:
    encodeInteger(prefixBuffer, 0x80,
                  requiredInsertCount - base - 1, 7)

return encoderBuffer, prefixBuffer + streamBuffer

It should say:

base = dynamicTable.getInsertCount()
requiredInsertCount = 0
for line in fieldLines:
  staticIndex = staticTable.findIndex(line)
  if staticIndex is not None:
    encodeStaticIndexReference(streamBuffer, staticIndex)
    continue

  dynamicIndex = dynamicTable.findIndex(line)
  if dynamicIndex is None:
    # No matching entry.  Either insert+index or encode literal
    staticNameIndex = staticTable.findName(line.name)
    if staticNameIndex is None:
       dynamicNameIndex = dynamicTable.findName(line.name)

    if shouldIndex(line) and dynamicTable.canIndex(line):
      encodeInsert(encoderBuffer, staticNameIndex,
                   dynamicNameIndex, line)
      dynamicIndex = dynamicTable.add(line)

  if dynamicIndex is None:
    # Could not index it, literal
    if dynamicNameIndex is not None:
      # Encode literal with dynamic name, possibly above Base
      encodeDynamicLiteral(streamBuffer, dynamicNameIndex,
                           base, line)
      requiredInsertCount = max(requiredInsertCount,
                                dynamicNameIndex + 1)
    else:
      # Encodes a literal with a static name or literal name
      encodeLiteral(streamBuffer, staticNameIndex, line)
  else:
    # Dynamic index reference
    assert(dynamicIndex is not None)
    requiredInsertCount = max(requiredInsertCount, dynamicIndex + 1)
    # Encode dynamicIndex, possibly above Base
    encodeDynamicIndexReference(streamBuffer, dynamicIndex, base)

# encode the prefix
if requiredInsertCount == 0:
  encodeInteger(prefixBuffer, 0x00, 0, 8)
  encodeInteger(prefixBuffer, 0x00, 0, 7)
else:
  wireRIC = (
    requiredInsertCount
    % (2 * getMaxEntries(maxTableCapacity))
  ) + 1;
  encodeInteger(prefixBuffer, 0x00, wireRIC, 8)
  if base >= requiredInsertCount:
    encodeInteger(prefixBuffer, 0x00,
                  base - requiredInsertCount, 7)
  else:
    encodeInteger(prefixBuffer, 0x80,
                  requiredInsertCount - base - 1, 7)

return encoderBuffer, prefixBuffer + streamBuffer

Notes:

"Sample Single-Pass Encoding Algorithm" in Appendix C has a bug that Count is equel to the index. Count must be one higher than index.

Reasoning the code:

requiredInsertCount is initialized with 0:

requiredInsertCount = 0

dynamicIndex can be 0 if it is the first entry of the dynamic table:

dynamicIndex = dynamicTable.findIndex(line)

In this case, requiredInsertCount stay with 0:

requiredInsertCount = max(requiredInsertCount, dynamicIndex)

This results in a wrong prefix:

if requiredInsertCount == 0:
encodeInteger(prefixBuffer, 0x00, 0, 8)
encodeInteger(prefixBuffer, 0x00, 0, 7)

The following code is correct:

requiredInsertCount = max(requiredInsertCount, dynamicIndex + 1)

Status: Held for Document Update (1)

RFC 9204, "QPACK: Field Compression for HTTP/3", June 2022

Source of RFC: quic (wit)

Errata ID: 7277
Status: Held for Document Update
Type: Technical
Publication Format(s) : TEXT, PDF, HTML

Reported By: Rory Hewitt
Date Reported: 2022-12-15
Held for Document Update by: Francesca Palombini
Date Held: 2024-01-30

Section Appendix A says:

In the static table, entry 73 has a value of:

access-control-allow-credentials: TRUE

and entry 74 has a value of:

access-control-allow-credentials: FALSE

It should say:

Entry 73 should have a value of:

access-control-allow-credentials: true

(note the lower-case value of "true")

and entry 74 should NOT EXIST since "FALSE" (in upper-case
or lower-case) is not a valid value for this header.

Notes:

The "access-control-allow-credentials" header is a CORS header. It only has one allowed value - "true" (without quotes, MUST be in lower-case). Values of "TRUE", "FALSE" and "false" are all invalid values, as is any mixed-case version of "true".

See the latest WHATWG spec at https://fetch.spec.whatwg.org/#cors-protocol-and-credentials which notes the required case-sensitivity of the "true" value and that it is the only valid value.

Also see the prior W3C spec at https://www.w3.org/TR/2020/SPSD-cors-20200602/#access-control-allow-credentials-response-header which says the same thing. Note that the W3C spec was superseded by the WHATWG spec.

Note that there are many instances of "access-control-allow-credentials: false" being returned from server responses (which is presumably why these values were added to the table), but they are invalid and the servers that send them are not following the CORS specification.

There may be case to be made that the static table is defined to make the QPACK algorithm as performant as possible and therefore it should include not only commonly-used valid values, but also commonly-used invalid values. However, the static table should ideally contain only valid header values.

-- Verifier notes
See https://mailarchive.ietf.org/arch/msg/quic/tgmjRvHDPev-mjPQWEM_zqRn5LE/

Report New Errata



Advanced Search