http-fields 0.1.1__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {http_fields-0.1.1 → http_fields-0.1.2}/CHANGELOG.md +23 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/PKG-INFO +9 -3
- {http_fields-0.1.1 → http_fields-0.1.2}/README.md +6 -0
- http_fields-0.1.2/SECURITY.md +68 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/docs/explanation/design.md +29 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/pyproject.toml +7 -1
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/setcookie.py +4 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc7240.py +3 -2
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_prefer.py +17 -0
- http_fields-0.1.1/SECURITY.md +0 -26
- {http_fields-0.1.1 → http_fields-0.1.2}/.gitignore +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/LICENSE.txt +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/docs/conf.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/docs/how-to/custom-and-unknown-headers.md +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/docs/how-to/parse-and-build-headers.md +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/docs/how-to/structured-fields.md +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/docs/index.md +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/docs/reference/api.md +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/docs/reference/headers.md +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/docs/reference/structured-fields.md +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/docs/tutorials/getting-started.md +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/__init__.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/accept.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/acceptcharset.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/acceptencoding.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/acceptlanguage.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/acceptranges.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/accesscontrol.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/age.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/allow.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/altsvc.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/authbases.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/authenticationinfo.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/authorization.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/cachecontrol.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/cachestatus.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/connection.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/contentdigest.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/contentdisposition.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/contentencoding.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/contentlanguage.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/contentlength.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/contentlocation.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/contentrange.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/contenttype.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/cookie.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/date.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/dateheader.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/entitytaglist.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/etag.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/expect.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/expires.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/forwarded.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/fromheader.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/header.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/host.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/ifmatch.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/ifmodifiedsince.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/ifnonematch.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/ifrange.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/ifunmodifiedsince.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/lastmodified.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/link.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/location.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/maxforwards.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/origin.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/parsedobjs.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/prefer.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/priority.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/productlistheader.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/proxyauthenticate.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/proxyauthenticationinfo.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/proxyauthorization.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/proxystatus.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/py.typed +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/range.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/referer.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/retryafter.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/server.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/stricttransportsecurity.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/structuredfields.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/structuredheaders.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/te.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/trailer.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/upgrade.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/uriheader.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/useragent.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/vary.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/via.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/__init__.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc3986.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc6265.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc6266.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc7230.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc7239.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc7838.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc8288.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/__init__.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/_base.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/accept.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/acceptcharset.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/acceptencoding.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/acceptlanguage.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/acceptranges.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/allow.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/authenticationinfo.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/authorization.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/authparam.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/authscheme.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/challenge.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/comment.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/connection.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/contentlanguage.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/contentlength.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/contentrange.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/contenttype.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/date.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/entitytag.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/etag.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/field.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/host.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/httpdate.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/ifmatch.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/ifmodifiedsince.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/ifnonematch.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/ifunmodifiedsince.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/lastmodified.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/location.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/parameters.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/product.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/quotedstring.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/range.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/rangeunit.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/retryafter.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/server.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/te.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/token.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/token68.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/type.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/upgrade.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/useragent.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/vary.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/via.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/weight.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/wwwauthenticate.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9111.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/wwwauthenticate.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/__init__.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_accept.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_acceptcharset.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_acceptencoding.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_acceptlanguage.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_acceptranges.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_accesscontrol.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_age.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_allow.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_altsvc.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_authenticationinfo.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_authorization.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_cachecontrol.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_cachestatus.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_connection.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_contentdigest.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_contentdisposition.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_contentencoding.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_contentlanguage.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_contentlength.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_contentlocation.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_contentrange.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_contenttype.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_cookie.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_date.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_etag.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_expect.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_expires.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_forwarded.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_from.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_header.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_host.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_ifmatch.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_ifmodifiedsince.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_ifnonematch.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_ifrange.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_ifunmodifiedsince.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_injection.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_lastmodified.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_limits.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_link.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_location.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_maxforwards.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_origin.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_plausibles.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_priority.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_proxyauth.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_range.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_referer.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_retryafter.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_roundtrip.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_server.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_setcookie.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_stricttransportsecurity.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_structuredfields.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_structuredfields_properties.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_te.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_trailer.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_upgrade.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_useragent.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_value_cache.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_vary.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_via.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_wwwauthenticate.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/__init__.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/__iinit__.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_accept.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_acceptcharset.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_acceptencoding.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_acceptlanguage.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_authenticationinfo.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_authorization.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_authparam.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_authscheme.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_base.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_challenge.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_comment.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_connection.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_contentlength.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_contentrange.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_etag.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_host.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_httpdate.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_ifmatch.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_ifnonematch.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_parameters.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_product.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_quotedstring.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_token.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_token68.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_useragent.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_weight.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/test_entitytag.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/test_misc.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/test_rfc3986.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/test_rfc6265.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/test_rfc6266.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/test_rfc7230.py +0 -0
- {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/test_rfc9111.py +0 -0
|
@@ -4,6 +4,28 @@ All notable changes to this project are documented here. The format is based on
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This project is in its 0.x series;
|
|
5
5
|
the API may still change between minor versions.
|
|
6
6
|
|
|
7
|
+
## [0.1.2] - 2026-10-21
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- `PreferenceApplied.parse()` returned no preferences, without raising, when abnf 2.9.0 or later
|
|
12
|
+
was installed, which is what a fresh install of 0.1.1 resolves to. abnf 2.9.0 corrected its
|
|
13
|
+
RFC 7240 grammar so that `Preference-Applied` items are `applied-pref` rather than Prefer's
|
|
14
|
+
`preference`, and the visitor was still looking for the old name.
|
|
15
|
+
([#53](https://github.com/declaresub/http-fields/issues/53))
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- Requires `abnf>=2.9.0` (was `>=2.6.0`).
|
|
20
|
+
- `PreferenceApplied.parse()` now rejects parameters (e.g. `respond-async; wait=10`), which
|
|
21
|
+
RFC 7240 §3 does not allow in `Preference-Applied`.
|
|
22
|
+
- `SetCookie.build()` raises `ValueError` for an empty string in `extension`, which would
|
|
23
|
+
otherwise serialize as a stray `"; "`.
|
|
24
|
+
|
|
25
|
+
### Documentation
|
|
26
|
+
|
|
27
|
+
- The design page gains an "On performance" section.
|
|
28
|
+
|
|
7
29
|
## [0.1.1] - 2026-07-22
|
|
8
30
|
|
|
9
31
|
Initial release published to PyPI as **http-fields** (`import http_fields`).
|
|
@@ -47,4 +69,5 @@ stability of a 0.x release. Caught on TestPyPI before the PyPI upload.)
|
|
|
47
69
|
- Each header's serialized `value` (and `str()`/`bytes()`/`asgi_value`) is computed once and cached
|
|
48
70
|
per instance.
|
|
49
71
|
|
|
72
|
+
[0.1.2]: https://github.com/declaresub/http-fields/releases/tag/v0.1.2
|
|
50
73
|
[0.1.1]: https://github.com/declaresub/http-fields/releases/tag/v0.1.1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: http-fields
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Typed, immutable HTTP header fields for Python, parsed and validated against RFC ABNF grammars
|
|
5
5
|
Project-URL: Homepage, https://github.com/declaresub/http-fields
|
|
6
6
|
Project-URL: Documentation, https://http-fields.readthedocs.io/
|
|
@@ -22,12 +22,18 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.14
|
|
23
23
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
24
24
|
Requires-Python: >=3.10
|
|
25
|
-
Requires-Dist: abnf>=2.
|
|
25
|
+
Requires-Dist: abnf>=2.9.0
|
|
26
26
|
Requires-Dist: typing-extensions>=4.0
|
|
27
27
|
Description-Content-Type: text/markdown
|
|
28
28
|
|
|
29
29
|
# http-fields
|
|
30
30
|
|
|
31
|
+
[](https://pypi.org/project/http-fields/)
|
|
32
|
+
[](https://pypi.org/project/http-fields/)
|
|
33
|
+
[](https://github.com/declaresub/http-fields/actions/workflows/ci.yml)
|
|
34
|
+
[](https://http-fields.readthedocs.io/)
|
|
35
|
+
[](LICENSE.txt)
|
|
36
|
+
|
|
31
37
|
Typed, validated HTTP headers for Python. Each header is an immutable
|
|
32
38
|
[dataclass](https://docs.python.org/3/library/dataclasses.html) whose fields are the structured
|
|
33
39
|
components of the header. [abnf](https://pypi.org/project/abnf/) grammars are used both to
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# http-fields
|
|
2
2
|
|
|
3
|
+
[](https://pypi.org/project/http-fields/)
|
|
4
|
+
[](https://pypi.org/project/http-fields/)
|
|
5
|
+
[](https://github.com/declaresub/http-fields/actions/workflows/ci.yml)
|
|
6
|
+
[](https://http-fields.readthedocs.io/)
|
|
7
|
+
[](LICENSE.txt)
|
|
8
|
+
|
|
3
9
|
Typed, validated HTTP headers for Python. Each header is an immutable
|
|
4
10
|
[dataclass](https://docs.python.org/3/library/dataclasses.html) whose fields are the structured
|
|
5
11
|
components of the header. [abnf](https://pypi.org/project/abnf/) grammars are used both to
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
`http-fields` parses and validates HTTP header values, often from untrusted input, so
|
|
4
|
+
correctness of that validation is a security property (e.g. rejecting CR/LF/NUL to prevent
|
|
5
|
+
response splitting / header injection).
|
|
6
|
+
|
|
7
|
+
## Reporting a vulnerability
|
|
8
|
+
|
|
9
|
+
Please report suspected vulnerabilities **privately**, not via a public issue:
|
|
10
|
+
|
|
11
|
+
- Preferred: GitHub's **"Report a vulnerability"** button on the repository's *Security* tab
|
|
12
|
+
(private vulnerability reporting).
|
|
13
|
+
- Alternatively, email the maintainer at **charles@declaresub.com**.
|
|
14
|
+
|
|
15
|
+
Please include a description, affected version/commit, and a minimal reproduction. You'll get
|
|
16
|
+
an acknowledgement, and we'll coordinate a fix and disclosure with you.
|
|
17
|
+
|
|
18
|
+
## Supported versions
|
|
19
|
+
|
|
20
|
+
This project is in its 0.x series. Security fixes land on the latest released version, so please
|
|
21
|
+
confirm the issue against the newest `0.x` release before reporting.
|
|
22
|
+
|
|
23
|
+
| Version | Supported |
|
|
24
|
+
| ------- | ------------------------------------------------------------- |
|
|
25
|
+
| 0.1.x | ✅ |
|
|
26
|
+
| 0.1.0 | ❌ — tagged but never published to PyPI; use 0.1.1 or later |
|
|
27
|
+
|
|
28
|
+
## Verifying a release
|
|
29
|
+
|
|
30
|
+
Releases are built and published by a GitHub Actions workflow
|
|
31
|
+
([`publish.yml`](.github/workflows/publish.yml)) from a signed tag, using **trusted publishing**
|
|
32
|
+
(OIDC — there are no long-lived PyPI API tokens that could be stolen). Both publish steps require
|
|
33
|
+
a manual approval in a protected environment. Every release is independently verifiable —
|
|
34
|
+
substitute the version you are checking for `0.1.1` below.
|
|
35
|
+
|
|
36
|
+
### PyPI attestations (PEP 740)
|
|
37
|
+
|
|
38
|
+
Artifacts on PyPI carry attestations binding them to this repository and workflow:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
pip download --no-deps http-fields==0.1.1
|
|
42
|
+
uvx pypi-attestations verify pypi \
|
|
43
|
+
--repository https://github.com/declaresub/http-fields \
|
|
44
|
+
http_fields-0.1.1-py3-none-any.whl
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The provenance can also be inspected directly:
|
|
48
|
+
<https://pypi.org/integrity/http-fields/0.1.1/http_fields-0.1.1-py3-none-any.whl/provenance>
|
|
49
|
+
|
|
50
|
+
It should name the publisher as repository `declaresub/http-fields`, workflow `publish.yml`,
|
|
51
|
+
environment `pypi`.
|
|
52
|
+
|
|
53
|
+
### Sigstore bundles
|
|
54
|
+
|
|
55
|
+
Each GitHub release attaches the wheel and sdist together with a `.sigstore.json` bundle:
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
gh release download v0.1.1 --repo declaresub/http-fields
|
|
59
|
+
uvx sigstore verify identity \
|
|
60
|
+
--cert-identity "https://github.com/declaresub/http-fields/.github/workflows/publish.yml@refs/tags/v0.1.1" \
|
|
61
|
+
--cert-oidc-issuer "https://token.actions.githubusercontent.com" \
|
|
62
|
+
http_fields-0.1.1-py3-none-any.whl http_fields-0.1.1.tar.gz
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### SBOM
|
|
66
|
+
|
|
67
|
+
Each release also attaches a CycloneDX SBOM (`http-fields.cdx.json`) describing the package's
|
|
68
|
+
runtime dependency closure (build and development tooling are excluded).
|
|
@@ -56,6 +56,35 @@ There is a single, consistent way to make headers:
|
|
|
56
56
|
This is a deliberate break from the older overloaded "string *or* pieces" constructors: one
|
|
57
57
|
object, one obvious way to build it, and precise types throughout.
|
|
58
58
|
|
|
59
|
+
## On performance
|
|
60
|
+
|
|
61
|
+
Correctness comes first, deliberately. Header handling has a long history of vulnerabilities —
|
|
62
|
+
request smuggling, response splitting, header injection — and they are almost never caused by
|
|
63
|
+
slow code. They come from *disagreement*: two hand-written parsers in a chain resolve the same
|
|
64
|
+
bytes differently (`Content-Length` versus `Transfer-Encoding` precedence, obs-fold continuation
|
|
65
|
+
lines, whitespace before the colon, repeated fields), and the gap between them is the
|
|
66
|
+
vulnerability. Driving every header from its RFC ABNF removes the room in which such a divergence
|
|
67
|
+
can appear — the grammar *is* the specification, so there is no shortcut available to quietly
|
|
68
|
+
disagree with it.
|
|
69
|
+
|
|
70
|
+
The same reasoning applies on the way out, and that direction is easy to overlook. Response
|
|
71
|
+
splitting usually originates not in a parser but in application code interpolating
|
|
72
|
+
attacker-influenced data *into* a header it is building. That is why validity here is a property
|
|
73
|
+
of the field types rather than a check performed by `parse()`: an invalid value cannot be held in
|
|
74
|
+
the first place, so *every* construction path — not just `parse()` — rejects CR/LF/NUL.
|
|
75
|
+
|
|
76
|
+
None of this is an argument for being gratuitously slow. A header is parsed at most once (see
|
|
77
|
+
above), and each header's serialized `value` is computed once and cached per instance, so the
|
|
78
|
+
common paths avoid repeated work.
|
|
79
|
+
|
|
80
|
+
What is deliberately *not* claimed is a throughput figure. There are no benchmarks, and raw
|
|
81
|
+
parsing speed has neither been measured nor tuned. If it ever matters, the measurement worth
|
|
82
|
+
making is not headers per second but **worst-case behaviour on hostile input**: grammar
|
|
83
|
+
backtracking can be super-linear, which turns a pathological value into an algorithmic
|
|
84
|
+
denial of service. That is a correctness property wearing a performance costume — and it is why
|
|
85
|
+
`parse()` bounds the length of its input (`Header.max_length`, 8192 by default and overridable
|
|
86
|
+
per subclass) before running the grammar at all.
|
|
87
|
+
|
|
59
88
|
## Shared bases where shapes repeat
|
|
60
89
|
|
|
61
90
|
Families that share an identical shape are factored onto a common base — dates (`DateHeader`),
|
|
@@ -26,7 +26,7 @@ classifiers = [
|
|
|
26
26
|
"Programming Language :: Python :: 3.14",
|
|
27
27
|
]
|
|
28
28
|
dependencies = [
|
|
29
|
-
"abnf>=2.
|
|
29
|
+
"abnf>=2.9.0",
|
|
30
30
|
"typing_extensions>=4.0",
|
|
31
31
|
]
|
|
32
32
|
dynamic = ["version"]
|
|
@@ -77,6 +77,12 @@ testpaths = ["tests"]
|
|
|
77
77
|
[tool.ruff]
|
|
78
78
|
src = ["src", "tests"]
|
|
79
79
|
|
|
80
|
+
[tool.ruff.format]
|
|
81
|
+
# Ruff >= 0.16 formats Python code blocks in Markdown by default. Prose snippets are
|
|
82
|
+
# written for reading (aligned trailing comments, elided bodies), not for the formatter,
|
|
83
|
+
# so keep Markdown out of the formatter's scope.
|
|
84
|
+
exclude = ["*.md"]
|
|
85
|
+
|
|
80
86
|
[tool.ruff.lint]
|
|
81
87
|
select = ["E", "F", "I", "UP", "B"]
|
|
82
88
|
# The formatter owns code line length (88 cols); E501 only fires on strings,
|
|
@@ -256,6 +256,10 @@ class SetCookie(Header):
|
|
|
256
256
|
# the RFC 6265 grammar requires max-age to be positive; this is almost universally
|
|
257
257
|
# ignored, so we go with the crowd and clamp negatives to 0.
|
|
258
258
|
normalized_max_age = 0 if max_age and max_age < 0 else max_age
|
|
259
|
+
# extension-av's prose rule admits the empty string, but an empty attribute
|
|
260
|
+
# would serialize as a stray "; ".
|
|
261
|
+
if extension and not all(extension):
|
|
262
|
+
raise ValueError("Invalid extension value.")
|
|
259
263
|
try:
|
|
260
264
|
ext = (
|
|
261
265
|
tuple(
|
|
@@ -70,8 +70,9 @@ class PreferVisitor(NodeVisitor):
|
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
class PreferenceAppliedVisitor(PreferVisitor):
|
|
73
|
-
#
|
|
73
|
+
# Preference-Applied items are `applied-pref`: token [ BWS "=" BWS word ], no
|
|
74
|
+
# parameters -- a subset of `preference`, so visit_preference handles them.
|
|
74
75
|
def visit_preference_applied(self, node: Node) -> list[Preference]:
|
|
75
76
|
return [
|
|
76
|
-
self.visit_preference(c) for c in node.children if c.name == "
|
|
77
|
+
self.visit_preference(c) for c in node.children if c.name == "applied-pref"
|
|
77
78
|
]
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
1
3
|
from http_fields import Header, Prefer, PreferenceApplied
|
|
2
4
|
from http_fields.visitors.rfc7240 import Preference
|
|
3
5
|
|
|
@@ -20,6 +22,21 @@ def test_preference_applied_parse():
|
|
|
20
22
|
assert header.preferences == (Preference("return", "representation"),)
|
|
21
23
|
|
|
22
24
|
|
|
25
|
+
def test_preference_applied_parse_multiple():
|
|
26
|
+
header = PreferenceApplied.parse("return=minimal, respond-async")
|
|
27
|
+
assert header.preferences == (
|
|
28
|
+
Preference("return", "minimal"),
|
|
29
|
+
Preference("respond-async"),
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@pytest.mark.parametrize("value", ["respond-async; wait=10", "return=minimal; x"])
|
|
34
|
+
def test_preference_applied_rejects_parameters(value: str):
|
|
35
|
+
# RFC 7240 section 3: applied-pref carries no parameters, unlike Prefer's preference.
|
|
36
|
+
with pytest.raises(ValueError):
|
|
37
|
+
PreferenceApplied.parse(value)
|
|
38
|
+
|
|
39
|
+
|
|
23
40
|
def test_prefer_create():
|
|
24
41
|
assert isinstance(Header.create("prefer", "respond-async"), Prefer)
|
|
25
42
|
assert isinstance(
|
http_fields-0.1.1/SECURITY.md
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# Security Policy
|
|
2
|
-
|
|
3
|
-
`http-fields` parses and validates HTTP header values, often from untrusted input, so
|
|
4
|
-
correctness of that validation is a security property (e.g. rejecting CR/LF/NUL to prevent
|
|
5
|
-
response splitting / header injection).
|
|
6
|
-
|
|
7
|
-
## Reporting a vulnerability
|
|
8
|
-
|
|
9
|
-
Please report suspected vulnerabilities **privately**, not via a public issue:
|
|
10
|
-
|
|
11
|
-
- Preferred: GitHub's **"Report a vulnerability"** button on the repository's *Security* tab
|
|
12
|
-
(private vulnerability reporting).
|
|
13
|
-
- Alternatively, email the maintainer at **charles@declaresub.com**.
|
|
14
|
-
|
|
15
|
-
Please include a description, affected version/commit, and a minimal reproduction. You'll get
|
|
16
|
-
an acknowledgement, and we'll coordinate a fix and disclosure with you.
|
|
17
|
-
|
|
18
|
-
## Supported versions
|
|
19
|
-
|
|
20
|
-
This project is **pre-release** (no tagged release yet); the latest `master` is the supported
|
|
21
|
-
version. Once releases are published, this section will list the supported release lines.
|
|
22
|
-
|
|
23
|
-
## Verifying a release
|
|
24
|
-
|
|
25
|
-
Once releases are published to PyPI, this section will describe how to verify a release's
|
|
26
|
-
provenance (build attestations / Sigstore bundle and the attached SBOM).
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/authenticationinfo.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/ifunmodifiedsince.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|