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.
Files changed (246) hide show
  1. {http_fields-0.1.1 → http_fields-0.1.2}/CHANGELOG.md +23 -0
  2. {http_fields-0.1.1 → http_fields-0.1.2}/PKG-INFO +9 -3
  3. {http_fields-0.1.1 → http_fields-0.1.2}/README.md +6 -0
  4. http_fields-0.1.2/SECURITY.md +68 -0
  5. {http_fields-0.1.1 → http_fields-0.1.2}/docs/explanation/design.md +29 -0
  6. {http_fields-0.1.1 → http_fields-0.1.2}/pyproject.toml +7 -1
  7. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/setcookie.py +4 -0
  8. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc7240.py +3 -2
  9. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_prefer.py +17 -0
  10. http_fields-0.1.1/SECURITY.md +0 -26
  11. {http_fields-0.1.1 → http_fields-0.1.2}/.gitignore +0 -0
  12. {http_fields-0.1.1 → http_fields-0.1.2}/LICENSE.txt +0 -0
  13. {http_fields-0.1.1 → http_fields-0.1.2}/docs/conf.py +0 -0
  14. {http_fields-0.1.1 → http_fields-0.1.2}/docs/how-to/custom-and-unknown-headers.md +0 -0
  15. {http_fields-0.1.1 → http_fields-0.1.2}/docs/how-to/parse-and-build-headers.md +0 -0
  16. {http_fields-0.1.1 → http_fields-0.1.2}/docs/how-to/structured-fields.md +0 -0
  17. {http_fields-0.1.1 → http_fields-0.1.2}/docs/index.md +0 -0
  18. {http_fields-0.1.1 → http_fields-0.1.2}/docs/reference/api.md +0 -0
  19. {http_fields-0.1.1 → http_fields-0.1.2}/docs/reference/headers.md +0 -0
  20. {http_fields-0.1.1 → http_fields-0.1.2}/docs/reference/structured-fields.md +0 -0
  21. {http_fields-0.1.1 → http_fields-0.1.2}/docs/tutorials/getting-started.md +0 -0
  22. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/__init__.py +0 -0
  23. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/accept.py +0 -0
  24. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/acceptcharset.py +0 -0
  25. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/acceptencoding.py +0 -0
  26. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/acceptlanguage.py +0 -0
  27. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/acceptranges.py +0 -0
  28. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/accesscontrol.py +0 -0
  29. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/age.py +0 -0
  30. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/allow.py +0 -0
  31. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/altsvc.py +0 -0
  32. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/authbases.py +0 -0
  33. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/authenticationinfo.py +0 -0
  34. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/authorization.py +0 -0
  35. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/cachecontrol.py +0 -0
  36. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/cachestatus.py +0 -0
  37. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/connection.py +0 -0
  38. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/contentdigest.py +0 -0
  39. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/contentdisposition.py +0 -0
  40. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/contentencoding.py +0 -0
  41. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/contentlanguage.py +0 -0
  42. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/contentlength.py +0 -0
  43. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/contentlocation.py +0 -0
  44. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/contentrange.py +0 -0
  45. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/contenttype.py +0 -0
  46. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/cookie.py +0 -0
  47. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/date.py +0 -0
  48. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/dateheader.py +0 -0
  49. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/entitytaglist.py +0 -0
  50. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/etag.py +0 -0
  51. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/expect.py +0 -0
  52. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/expires.py +0 -0
  53. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/forwarded.py +0 -0
  54. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/fromheader.py +0 -0
  55. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/header.py +0 -0
  56. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/host.py +0 -0
  57. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/ifmatch.py +0 -0
  58. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/ifmodifiedsince.py +0 -0
  59. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/ifnonematch.py +0 -0
  60. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/ifrange.py +0 -0
  61. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/ifunmodifiedsince.py +0 -0
  62. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/lastmodified.py +0 -0
  63. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/link.py +0 -0
  64. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/location.py +0 -0
  65. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/maxforwards.py +0 -0
  66. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/origin.py +0 -0
  67. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/parsedobjs.py +0 -0
  68. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/prefer.py +0 -0
  69. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/priority.py +0 -0
  70. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/productlistheader.py +0 -0
  71. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/proxyauthenticate.py +0 -0
  72. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/proxyauthenticationinfo.py +0 -0
  73. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/proxyauthorization.py +0 -0
  74. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/proxystatus.py +0 -0
  75. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/py.typed +0 -0
  76. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/range.py +0 -0
  77. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/referer.py +0 -0
  78. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/retryafter.py +0 -0
  79. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/server.py +0 -0
  80. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/stricttransportsecurity.py +0 -0
  81. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/structuredfields.py +0 -0
  82. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/structuredheaders.py +0 -0
  83. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/te.py +0 -0
  84. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/trailer.py +0 -0
  85. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/upgrade.py +0 -0
  86. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/uriheader.py +0 -0
  87. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/useragent.py +0 -0
  88. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/vary.py +0 -0
  89. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/via.py +0 -0
  90. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/__init__.py +0 -0
  91. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc3986.py +0 -0
  92. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc6265.py +0 -0
  93. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc6266.py +0 -0
  94. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc7230.py +0 -0
  95. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc7239.py +0 -0
  96. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc7838.py +0 -0
  97. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc8288.py +0 -0
  98. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/__init__.py +0 -0
  99. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/_base.py +0 -0
  100. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/accept.py +0 -0
  101. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/acceptcharset.py +0 -0
  102. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/acceptencoding.py +0 -0
  103. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/acceptlanguage.py +0 -0
  104. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/acceptranges.py +0 -0
  105. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/allow.py +0 -0
  106. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/authenticationinfo.py +0 -0
  107. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/authorization.py +0 -0
  108. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/authparam.py +0 -0
  109. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/authscheme.py +0 -0
  110. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/challenge.py +0 -0
  111. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/comment.py +0 -0
  112. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/connection.py +0 -0
  113. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/contentlanguage.py +0 -0
  114. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/contentlength.py +0 -0
  115. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/contentrange.py +0 -0
  116. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/contenttype.py +0 -0
  117. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/date.py +0 -0
  118. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/entitytag.py +0 -0
  119. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/etag.py +0 -0
  120. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/field.py +0 -0
  121. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/host.py +0 -0
  122. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/httpdate.py +0 -0
  123. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/ifmatch.py +0 -0
  124. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/ifmodifiedsince.py +0 -0
  125. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/ifnonematch.py +0 -0
  126. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/ifunmodifiedsince.py +0 -0
  127. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/lastmodified.py +0 -0
  128. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/location.py +0 -0
  129. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/parameters.py +0 -0
  130. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/product.py +0 -0
  131. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/quotedstring.py +0 -0
  132. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/range.py +0 -0
  133. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/rangeunit.py +0 -0
  134. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/retryafter.py +0 -0
  135. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/server.py +0 -0
  136. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/te.py +0 -0
  137. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/token.py +0 -0
  138. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/token68.py +0 -0
  139. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/type.py +0 -0
  140. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/upgrade.py +0 -0
  141. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/useragent.py +0 -0
  142. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/vary.py +0 -0
  143. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/via.py +0 -0
  144. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/weight.py +0 -0
  145. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9110/wwwauthenticate.py +0 -0
  146. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/visitors/rfc9111.py +0 -0
  147. {http_fields-0.1.1 → http_fields-0.1.2}/src/http_fields/wwwauthenticate.py +0 -0
  148. {http_fields-0.1.1 → http_fields-0.1.2}/tests/__init__.py +0 -0
  149. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_accept.py +0 -0
  150. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_acceptcharset.py +0 -0
  151. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_acceptencoding.py +0 -0
  152. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_acceptlanguage.py +0 -0
  153. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_acceptranges.py +0 -0
  154. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_accesscontrol.py +0 -0
  155. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_age.py +0 -0
  156. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_allow.py +0 -0
  157. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_altsvc.py +0 -0
  158. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_authenticationinfo.py +0 -0
  159. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_authorization.py +0 -0
  160. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_cachecontrol.py +0 -0
  161. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_cachestatus.py +0 -0
  162. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_connection.py +0 -0
  163. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_contentdigest.py +0 -0
  164. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_contentdisposition.py +0 -0
  165. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_contentencoding.py +0 -0
  166. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_contentlanguage.py +0 -0
  167. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_contentlength.py +0 -0
  168. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_contentlocation.py +0 -0
  169. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_contentrange.py +0 -0
  170. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_contenttype.py +0 -0
  171. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_cookie.py +0 -0
  172. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_date.py +0 -0
  173. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_etag.py +0 -0
  174. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_expect.py +0 -0
  175. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_expires.py +0 -0
  176. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_forwarded.py +0 -0
  177. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_from.py +0 -0
  178. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_header.py +0 -0
  179. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_host.py +0 -0
  180. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_ifmatch.py +0 -0
  181. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_ifmodifiedsince.py +0 -0
  182. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_ifnonematch.py +0 -0
  183. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_ifrange.py +0 -0
  184. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_ifunmodifiedsince.py +0 -0
  185. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_injection.py +0 -0
  186. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_lastmodified.py +0 -0
  187. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_limits.py +0 -0
  188. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_link.py +0 -0
  189. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_location.py +0 -0
  190. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_maxforwards.py +0 -0
  191. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_origin.py +0 -0
  192. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_plausibles.py +0 -0
  193. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_priority.py +0 -0
  194. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_proxyauth.py +0 -0
  195. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_range.py +0 -0
  196. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_referer.py +0 -0
  197. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_retryafter.py +0 -0
  198. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_roundtrip.py +0 -0
  199. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_server.py +0 -0
  200. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_setcookie.py +0 -0
  201. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_stricttransportsecurity.py +0 -0
  202. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_structuredfields.py +0 -0
  203. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_structuredfields_properties.py +0 -0
  204. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_te.py +0 -0
  205. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_trailer.py +0 -0
  206. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_upgrade.py +0 -0
  207. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_useragent.py +0 -0
  208. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_value_cache.py +0 -0
  209. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_vary.py +0 -0
  210. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_via.py +0 -0
  211. {http_fields-0.1.1 → http_fields-0.1.2}/tests/test_wwwauthenticate.py +0 -0
  212. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/__init__.py +0 -0
  213. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/__iinit__.py +0 -0
  214. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_accept.py +0 -0
  215. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_acceptcharset.py +0 -0
  216. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_acceptencoding.py +0 -0
  217. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_acceptlanguage.py +0 -0
  218. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_authenticationinfo.py +0 -0
  219. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_authorization.py +0 -0
  220. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_authparam.py +0 -0
  221. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_authscheme.py +0 -0
  222. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_base.py +0 -0
  223. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_challenge.py +0 -0
  224. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_comment.py +0 -0
  225. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_connection.py +0 -0
  226. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_contentlength.py +0 -0
  227. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_contentrange.py +0 -0
  228. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_etag.py +0 -0
  229. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_host.py +0 -0
  230. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_httpdate.py +0 -0
  231. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_ifmatch.py +0 -0
  232. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_ifnonematch.py +0 -0
  233. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_parameters.py +0 -0
  234. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_product.py +0 -0
  235. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_quotedstring.py +0 -0
  236. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_token.py +0 -0
  237. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_token68.py +0 -0
  238. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_useragent.py +0 -0
  239. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/rfc9110/test_weight.py +0 -0
  240. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/test_entitytag.py +0 -0
  241. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/test_misc.py +0 -0
  242. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/test_rfc3986.py +0 -0
  243. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/test_rfc6265.py +0 -0
  244. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/test_rfc6266.py +0 -0
  245. {http_fields-0.1.1 → http_fields-0.1.2}/tests/visitors/test_rfc7230.py +0 -0
  246. {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.4
1
+ Metadata-Version: 2.5
2
2
  Name: http-fields
3
- Version: 0.1.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.6.0
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
+ [![PyPI](https://img.shields.io/pypi/v/http-fields)](https://pypi.org/project/http-fields/)
32
+ [![Python versions](https://img.shields.io/pypi/pyversions/http-fields)](https://pypi.org/project/http-fields/)
33
+ [![CI](https://github.com/declaresub/http-fields/actions/workflows/ci.yml/badge.svg)](https://github.com/declaresub/http-fields/actions/workflows/ci.yml)
34
+ [![Docs](https://readthedocs.org/projects/http-fields/badge/?version=latest)](https://http-fields.readthedocs.io/)
35
+ [![License](https://img.shields.io/pypi/l/http-fields)](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
+ [![PyPI](https://img.shields.io/pypi/v/http-fields)](https://pypi.org/project/http-fields/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/http-fields)](https://pypi.org/project/http-fields/)
5
+ [![CI](https://github.com/declaresub/http-fields/actions/workflows/ci.yml/badge.svg)](https://github.com/declaresub/http-fields/actions/workflows/ci.yml)
6
+ [![Docs](https://readthedocs.org/projects/http-fields/badge/?version=latest)](https://http-fields.readthedocs.io/)
7
+ [![License](https://img.shields.io/pypi/l/http-fields)](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.6.0",
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
- # RFC 7240's abnf uses the same `preference` production for Preference-Applied.
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 == "preference"
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(
@@ -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