Skip to content

fix(const): Method.get never checks values, so BASELINE-CONTROL and VERSION-CONTROL mis-resolve #908

Description

@JarryShaw

Describe the bug

Method.get does no value lookup, so the two IANA methods whose member name differs from their value fail to resolve and come back with wrong metadata. A real HTTP/1 capture can hit this.

BASELINE_CONTROL -> value 'BASELINE-CONTROL'  (registry: safe=False idempotent=True)
VERSION_CONTROL  -> value 'VERSION-CONTROL'   (registry: safe=False idempotent=True)

Method.get('BASELINE-CONTROL')  is Method.BASELINE_CONTROL ?  False    idempotent=False   <- wrong
Method('BASELINE-CONTROL')      is Method.BASELINE_CONTROL ?  True
Method.get('VERSION-CONTROL')   is Method.VERSION_CONTROL  ?  False    idempotent=False   <- wrong
Method('VERSION-CONTROL')       is Method.VERSION_CONTROL  ?  True

Both are RFC 3253 methods. They are the only two of the 40 members whose name and value differ, because the hyphen cannot appear in a Python identifier.

Why a capture reaches it. pcapkit/protocols/application/httpv1.py:60:

_RE_METHOD = re.compile(rb"(?P<method>[A-Z][A-Z-]*)\Z")

That pattern admits hyphens, so a request line carrying BASELINE-CONTROL matches, reaches the lookup at httpv1.py:434, and is reported with idempotent=False where the registry says True. The parse succeeds; only the metadata is wrong, which is the harder kind to notice.

The mechanism. The override at pcapkit/const/http/method.py checks _member_map_ (names) and then falls through to building an unregistered member. It never consults _value2member_map_. The base EnumRegistry.get does exactly that fall-through — pcapkit/corekit/enum.py:368-376 — which is why the constructor resolves these two correctly and get does not.

Expected behavior

Method.get('BASELINE-CONTROL') should return Method.BASELINE_CONTROL, with the registry's own safe/idempotent values.

The fix that comes for free. Rather than adding a hand-rolled value check, delegate and keep the override only for the fallback:

try:
    return super().get(key)
except KeyError:
    return Method._unregistered_member(key, key.upper())

That preserves case-sensitivity (per RFC 9110 §9.1, settled in #896), preserves the caller's-casing unregistered member, and picks up the base's _value2member_map_ match — closing this defect without a special case. It must go in the crawler template (pcapkit/vendor/http/method.py, which carries its own LINE lambda rather than using pcapkit/vendor/default.py's), not in the generated file, or the next regeneration discards it.

System information

Measured on origin/main at 0981d771d, repo venv, PYTHONSAFEPATH=1.

Additional context

Found by the cross-review of #907, which fixed Method.get's case folding for #896. Not a regression from #907 — .upper() is a no-op on an already-uppercase token, so the behaviour is identical before and after — but #907 touches the exact line that would fix it, so it is recorded here rather than silently carried.

A second, related inconsistency in the same file, deliberately out of #896's scope and noted so it is not lost: _missing_ still folds case, and its docstring still reads "Matched case-insensitively against the canonical upper-case member names" with no RFC caveat — fifteen lines from a get whose docstring now says RFC 9110 requires the opposite. Method('get') still resolves to GET while Method.get('get') no longer does. That split was ruled deliberate, but a reader of the one file gets two contradictory rationales and no pointer between them. Whoever fixes the value lookup should add that pointer, in the template.

Related: #903 (the registry-wide case-sensitivity audit), #877 (the EnumLookup/EnumRegistry split, which is what makes delegating to super().get clean).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugIssues reporting a defect (set by the bug report template; a default, not an assessment)constRegenerated IANA or vendor constant tables; members keep their numeric valuesfixPull requests that fix a defect (fix: subject prefix)

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions