| // Copyright 2025 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| //go:build go1.24 |
| |
| package http3 |
| |
| import ( |
| "errors" |
| "math/bits" |
| ) |
| |
| type qpackDecoder struct { |
| // The decoder has no state for now, |
| // but that'll change once we add dynamic table support. |
| // |
| // TODO: dynamic table support. |
| } |
| |
| func (qd *qpackDecoder) decode(st *stream, f func(itype indexType, name, value string) error) error { |
| // Encoded Field Section prefix. |
| |
| // We set SETTINGS_QPACK_MAX_TABLE_CAPACITY to 0, |
| // so the Required Insert Count must be 0. |
| _, requiredInsertCount, err := st.readPrefixedInt(8) |
| if err != nil { |
| return err |
| } |
| if requiredInsertCount != 0 { |
| return errQPACKDecompressionFailed |
| } |
| |
| // Delta Base. We don't use the dynamic table yet, so this may be ignored. |
| _, _, err = st.readPrefixedInt(7) |
| if err != nil { |
| return err |
| } |
| |
| sawNonPseudo := false |
| for st.lim > 0 { |
| firstByte, err := st.ReadByte() |
| if err != nil { |
| return err |
| } |
| var name, value string |
| var itype indexType |
| switch bits.LeadingZeros8(firstByte) { |
| case 0: |
| // Indexed Field Line |
| itype, name, value, err = st.decodeIndexedFieldLine(firstByte) |
| case 1: |
| // Literal Field Line With Name Reference |
| itype, name, value, err = st.decodeLiteralFieldLineWithNameReference(firstByte) |
| case 2: |
| // Literal Field Line with Literal Name |
| itype, name, value, err = st.decodeLiteralFieldLineWithLiteralName(firstByte) |
| case 3: |
| // Indexed Field Line With Post-Base Index |
| err = errors.New("dynamic table is not supported yet") |
| case 4: |
| // Indexed Field Line With Post-Base Name Reference |
| err = errors.New("dynamic table is not supported yet") |
| } |
| if err != nil { |
| return err |
| } |
| if len(name) == 0 { |
| return errH3MessageError |
| } |
| if name[0] == ':' { |
| if sawNonPseudo { |
| return errH3MessageError |
| } |
| } else { |
| sawNonPseudo = true |
| } |
| if err := f(itype, name, value); err != nil { |
| return err |
| } |
| } |
| return nil |
| } |