http-fields 0.1.1__py3-none-any.whl

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 (132) hide show
  1. http_fields/__init__.py +193 -0
  2. http_fields/accept.py +37 -0
  3. http_fields/acceptcharset.py +48 -0
  4. http_fields/acceptencoding.py +42 -0
  5. http_fields/acceptlanguage.py +47 -0
  6. http_fields/acceptranges.py +35 -0
  7. http_fields/accesscontrol.py +197 -0
  8. http_fields/age.py +34 -0
  9. http_fields/allow.py +46 -0
  10. http_fields/altsvc.py +70 -0
  11. http_fields/authbases.py +79 -0
  12. http_fields/authenticationinfo.py +19 -0
  13. http_fields/authorization.py +23 -0
  14. http_fields/cachecontrol.py +119 -0
  15. http_fields/cachestatus.py +12 -0
  16. http_fields/connection.py +35 -0
  17. http_fields/contentdigest.py +19 -0
  18. http_fields/contentdisposition.py +66 -0
  19. http_fields/contentencoding.py +45 -0
  20. http_fields/contentlanguage.py +38 -0
  21. http_fields/contentlength.py +34 -0
  22. http_fields/contentlocation.py +24 -0
  23. http_fields/contentrange.py +74 -0
  24. http_fields/contenttype.py +104 -0
  25. http_fields/cookie.py +38 -0
  26. http_fields/date.py +17 -0
  27. http_fields/dateheader.py +32 -0
  28. http_fields/entitytaglist.py +39 -0
  29. http_fields/etag.py +46 -0
  30. http_fields/expect.py +52 -0
  31. http_fields/expires.py +17 -0
  32. http_fields/forwarded.py +36 -0
  33. http_fields/fromheader.py +42 -0
  34. http_fields/header.py +250 -0
  35. http_fields/host.py +44 -0
  36. http_fields/ifmatch.py +24 -0
  37. http_fields/ifmodifiedsince.py +17 -0
  38. http_fields/ifnonematch.py +24 -0
  39. http_fields/ifrange.py +49 -0
  40. http_fields/ifunmodifiedsince.py +17 -0
  41. http_fields/lastmodified.py +17 -0
  42. http_fields/link.py +35 -0
  43. http_fields/location.py +24 -0
  44. http_fields/maxforwards.py +32 -0
  45. http_fields/origin.py +42 -0
  46. http_fields/parsedobjs.py +62 -0
  47. http_fields/prefer.py +64 -0
  48. http_fields/priority.py +55 -0
  49. http_fields/productlistheader.py +33 -0
  50. http_fields/proxyauthenticate.py +17 -0
  51. http_fields/proxyauthenticationinfo.py +17 -0
  52. http_fields/proxyauthorization.py +17 -0
  53. http_fields/proxystatus.py +12 -0
  54. http_fields/py.typed +0 -0
  55. http_fields/range.py +37 -0
  56. http_fields/referer.py +24 -0
  57. http_fields/retryafter.py +45 -0
  58. http_fields/server.py +18 -0
  59. http_fields/setcookie.py +419 -0
  60. http_fields/stricttransportsecurity.py +96 -0
  61. http_fields/structuredfields.py +356 -0
  62. http_fields/structuredheaders.py +90 -0
  63. http_fields/te.py +36 -0
  64. http_fields/trailer.py +44 -0
  65. http_fields/upgrade.py +35 -0
  66. http_fields/uriheader.py +36 -0
  67. http_fields/useragent.py +18 -0
  68. http_fields/vary.py +40 -0
  69. http_fields/via.py +35 -0
  70. http_fields/visitors/__init__.py +1 -0
  71. http_fields/visitors/rfc3986.py +195 -0
  72. http_fields/visitors/rfc6265.py +62 -0
  73. http_fields/visitors/rfc6266.py +234 -0
  74. http_fields/visitors/rfc7230.py +80 -0
  75. http_fields/visitors/rfc7239.py +43 -0
  76. http_fields/visitors/rfc7240.py +77 -0
  77. http_fields/visitors/rfc7838.py +77 -0
  78. http_fields/visitors/rfc8288.py +56 -0
  79. http_fields/visitors/rfc9110/__init__.py +117 -0
  80. http_fields/visitors/rfc9110/_base.py +71 -0
  81. http_fields/visitors/rfc9110/accept.py +135 -0
  82. http_fields/visitors/rfc9110/acceptcharset.py +26 -0
  83. http_fields/visitors/rfc9110/acceptencoding.py +76 -0
  84. http_fields/visitors/rfc9110/acceptlanguage.py +55 -0
  85. http_fields/visitors/rfc9110/acceptranges.py +13 -0
  86. http_fields/visitors/rfc9110/allow.py +13 -0
  87. http_fields/visitors/rfc9110/authenticationinfo.py +19 -0
  88. http_fields/visitors/rfc9110/authorization.py +77 -0
  89. http_fields/visitors/rfc9110/authparam.py +40 -0
  90. http_fields/visitors/rfc9110/authscheme.py +17 -0
  91. http_fields/visitors/rfc9110/challenge.py +57 -0
  92. http_fields/visitors/rfc9110/comment.py +99 -0
  93. http_fields/visitors/rfc9110/connection.py +13 -0
  94. http_fields/visitors/rfc9110/contentlanguage.py +21 -0
  95. http_fields/visitors/rfc9110/contentlength.py +9 -0
  96. http_fields/visitors/rfc9110/contentrange.py +70 -0
  97. http_fields/visitors/rfc9110/contenttype.py +52 -0
  98. http_fields/visitors/rfc9110/date.py +12 -0
  99. http_fields/visitors/rfc9110/entitytag.py +66 -0
  100. http_fields/visitors/rfc9110/etag.py +12 -0
  101. http_fields/visitors/rfc9110/field.py +15 -0
  102. http_fields/visitors/rfc9110/host.py +32 -0
  103. http_fields/visitors/rfc9110/httpdate.py +167 -0
  104. http_fields/visitors/rfc9110/ifmatch.py +24 -0
  105. http_fields/visitors/rfc9110/ifmodifiedsince.py +12 -0
  106. http_fields/visitors/rfc9110/ifnonematch.py +26 -0
  107. http_fields/visitors/rfc9110/ifunmodifiedsince.py +12 -0
  108. http_fields/visitors/rfc9110/lastmodified.py +14 -0
  109. http_fields/visitors/rfc9110/location.py +9 -0
  110. http_fields/visitors/rfc9110/parameters.py +124 -0
  111. http_fields/visitors/rfc9110/product.py +39 -0
  112. http_fields/visitors/rfc9110/quotedstring.py +66 -0
  113. http_fields/visitors/rfc9110/range.py +100 -0
  114. http_fields/visitors/rfc9110/rangeunit.py +17 -0
  115. http_fields/visitors/rfc9110/retryafter.py +20 -0
  116. http_fields/visitors/rfc9110/server.py +16 -0
  117. http_fields/visitors/rfc9110/te.py +83 -0
  118. http_fields/visitors/rfc9110/token.py +18 -0
  119. http_fields/visitors/rfc9110/token68.py +18 -0
  120. http_fields/visitors/rfc9110/type.py +17 -0
  121. http_fields/visitors/rfc9110/upgrade.py +57 -0
  122. http_fields/visitors/rfc9110/useragent.py +16 -0
  123. http_fields/visitors/rfc9110/vary.py +10 -0
  124. http_fields/visitors/rfc9110/via.py +86 -0
  125. http_fields/visitors/rfc9110/weight.py +40 -0
  126. http_fields/visitors/rfc9110/wwwauthenticate.py +21 -0
  127. http_fields/visitors/rfc9111.py +92 -0
  128. http_fields/wwwauthenticate.py +24 -0
  129. http_fields-0.1.1.dist-info/METADATA +88 -0
  130. http_fields-0.1.1.dist-info/RECORD +132 -0
  131. http_fields-0.1.1.dist-info/WHEEL +4 -0
  132. http_fields-0.1.1.dist-info/licenses/LICENSE.txt +16 -0
@@ -0,0 +1,193 @@
1
+ """Header classes. All classes intended for public use are imported here."""
2
+
3
+ __all__ = [
4
+ "Accept",
5
+ "AcceptCharset",
6
+ "AcceptLanguage",
7
+ "AcceptEncoding",
8
+ "AcceptRanges",
9
+ "AccessControlAllowCredentials",
10
+ "AccessControlAllowHeaders",
11
+ "AccessControlAllowMethods",
12
+ "AccessControlAllowOrigin",
13
+ "AccessControlExposeHeaders",
14
+ "AccessControlMaxAge",
15
+ "AccessControlRequestHeaders",
16
+ "AccessControlRequestMethod",
17
+ "Age",
18
+ "Allow",
19
+ "AltSvc",
20
+ "AltUsed",
21
+ "AuthenticationInfo",
22
+ "Authorization",
23
+ "AuthParam",
24
+ "CacheControl",
25
+ "CacheDirective",
26
+ "CacheStatus",
27
+ "Connection",
28
+ "ContentDigest",
29
+ "ContentDisposition",
30
+ "ContentEncoding",
31
+ "ContentLanguage",
32
+ "ContentLength",
33
+ "ContentLocation",
34
+ "ContentRange",
35
+ "ContentType",
36
+ "Cookie",
37
+ "CustomHeader",
38
+ "Date",
39
+ "EntityTag",
40
+ "ETag",
41
+ "Expect",
42
+ "Expires",
43
+ "Forwarded",
44
+ "From",
45
+ "Header",
46
+ "Host",
47
+ "IfMatch",
48
+ "IfModifiedSince",
49
+ "IfNoneMatch",
50
+ "IfRange",
51
+ "IfUnmodifiedSince",
52
+ "LanguageTag",
53
+ "LastModified",
54
+ "Link",
55
+ "Location",
56
+ "MaxForwards",
57
+ "MediaType",
58
+ "Origin",
59
+ "Prefer",
60
+ "PreferenceApplied",
61
+ "Priority",
62
+ "RangeUnit",
63
+ "NonNegativeInt",
64
+ "Protocol",
65
+ "ProxyAuthenticate",
66
+ "ProxyAuthenticationInfo",
67
+ "ProxyAuthorization",
68
+ "ProxyStatus",
69
+ "Range",
70
+ "Referer",
71
+ "ReprDigest",
72
+ "RetryAfter",
73
+ "Server",
74
+ "SetCookie",
75
+ "StrictTransportSecurity",
76
+ "TCoding",
77
+ "TE",
78
+ "Trailer",
79
+ "Upgrade",
80
+ "UserAgent",
81
+ "Vary",
82
+ "Via",
83
+ "WWWAuthenticate",
84
+ "ExtValue",
85
+ "CookiePair",
86
+ "AltValue",
87
+ "ForwardedElement",
88
+ "IntRange",
89
+ "LinkValue",
90
+ "Preference",
91
+ "SuffixRange",
92
+ "ViaElement",
93
+ "WeightedCoding",
94
+ "WeightedLanguageRange",
95
+ # Leaf types for strict list-header construction (e.g. Connection(Token("keep-alive"))).
96
+ "CorsFieldName",
97
+ "CorsMethod",
98
+ "Expectation",
99
+ "FieldName",
100
+ "Method",
101
+ "Token",
102
+ ]
103
+
104
+ from http_fields.accept import Accept
105
+ from http_fields.acceptcharset import AcceptCharset
106
+ from http_fields.acceptencoding import AcceptEncoding, WeightedCoding
107
+ from http_fields.acceptlanguage import AcceptLanguage
108
+ from http_fields.acceptranges import AcceptRanges
109
+ from http_fields.accesscontrol import (
110
+ AccessControlAllowCredentials,
111
+ AccessControlAllowHeaders,
112
+ AccessControlAllowMethods,
113
+ AccessControlAllowOrigin,
114
+ AccessControlExposeHeaders,
115
+ AccessControlMaxAge,
116
+ AccessControlRequestHeaders,
117
+ AccessControlRequestMethod,
118
+ CorsFieldName,
119
+ CorsMethod,
120
+ )
121
+ from http_fields.age import Age
122
+ from http_fields.allow import Allow, Method
123
+ from http_fields.altsvc import AltSvc, AltUsed
124
+ from http_fields.authenticationinfo import AuthenticationInfo
125
+ from http_fields.authorization import Authorization
126
+ from http_fields.cachecontrol import CacheControl, CacheDirective
127
+ from http_fields.cachestatus import CacheStatus
128
+ from http_fields.connection import Connection
129
+ from http_fields.contentdigest import ContentDigest, ReprDigest
130
+ from http_fields.contentdisposition import ContentDisposition, ExtValue
131
+ from http_fields.contentencoding import ContentEncoding
132
+ from http_fields.contentlanguage import ContentLanguage
133
+ from http_fields.contentlength import ContentLength
134
+ from http_fields.contentlocation import ContentLocation
135
+ from http_fields.contentrange import ContentRange
136
+ from http_fields.contenttype import ContentType
137
+ from http_fields.cookie import Cookie, CookiePair
138
+ from http_fields.date import Date
139
+ from http_fields.etag import ETag
140
+ from http_fields.expect import Expect, Expectation
141
+ from http_fields.expires import Expires
142
+ from http_fields.forwarded import Forwarded
143
+ from http_fields.fromheader import From
144
+ from http_fields.header import CustomHeader, Header
145
+ from http_fields.host import Host
146
+ from http_fields.ifmatch import IfMatch
147
+ from http_fields.ifmodifiedsince import IfModifiedSince
148
+ from http_fields.ifnonematch import IfNoneMatch
149
+ from http_fields.ifrange import IfRange
150
+ from http_fields.ifunmodifiedsince import IfUnmodifiedSince
151
+ from http_fields.lastmodified import LastModified
152
+ from http_fields.link import Link
153
+ from http_fields.location import Location
154
+ from http_fields.maxforwards import MaxForwards
155
+ from http_fields.origin import Origin
156
+ from http_fields.parsedobjs import NonNegativeInt
157
+ from http_fields.prefer import Prefer, PreferenceApplied
158
+ from http_fields.priority import Priority
159
+ from http_fields.proxyauthenticate import ProxyAuthenticate
160
+ from http_fields.proxyauthenticationinfo import ProxyAuthenticationInfo
161
+ from http_fields.proxyauthorization import ProxyAuthorization
162
+ from http_fields.proxystatus import ProxyStatus
163
+ from http_fields.range import Range
164
+ from http_fields.referer import Referer
165
+ from http_fields.retryafter import RetryAfter
166
+ from http_fields.server import Server
167
+ from http_fields.setcookie import SetCookie
168
+ from http_fields.stricttransportsecurity import StrictTransportSecurity
169
+ from http_fields.te import TE
170
+ from http_fields.trailer import Trailer
171
+ from http_fields.upgrade import Upgrade
172
+ from http_fields.useragent import UserAgent
173
+ from http_fields.vary import Vary
174
+ from http_fields.via import Via
175
+ from http_fields.visitors.rfc7239 import ForwardedElement
176
+ from http_fields.visitors.rfc7240 import Preference
177
+ from http_fields.visitors.rfc7838 import AltValue
178
+ from http_fields.visitors.rfc8288 import LinkValue
179
+ from http_fields.visitors.rfc9110 import (
180
+ AuthParam,
181
+ EntityTag,
182
+ FieldName,
183
+ MediaType,
184
+ RangeUnit,
185
+ Token,
186
+ WeightedLanguageRange,
187
+ )
188
+ from http_fields.visitors.rfc9110.contentlanguage import LanguageTag
189
+ from http_fields.visitors.rfc9110.range import IntRange, SuffixRange
190
+ from http_fields.visitors.rfc9110.te import TCoding
191
+ from http_fields.visitors.rfc9110.upgrade import Protocol
192
+ from http_fields.visitors.rfc9110.via import ViaElement
193
+ from http_fields.wwwauthenticate import WWWAuthenticate
http_fields/accept.py ADDED
@@ -0,0 +1,37 @@
1
+ """Accept header class."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import ClassVar
5
+
6
+ from abnf import Rule
7
+ from abnf.grammars import rfc9110
8
+ from typing_extensions import Self
9
+
10
+ from http_fields.header import Header
11
+ from http_fields.visitors.rfc9110 import AcceptType, AcceptVisitor
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class Accept(Header):
16
+ """Accept header, as defined by RFC 9110.
17
+
18
+ An Accept header may have an empty value (``Accept:``), which is treated as accepting
19
+ any media type. RFC 9110 no longer includes ext-params (accept-ext) in the grammar.
20
+ """
21
+
22
+ name: ClassVar[str] = "Accept"
23
+ rule: ClassVar[Rule] = rfc9110.Rule("Accept")
24
+ visitor: ClassVar[AcceptVisitor] = AcceptVisitor()
25
+
26
+ accept_types: tuple[AcceptType, ...]
27
+
28
+ def __init__(self, *accept_types: AcceptType) -> None:
29
+ object.__setattr__(self, "accept_types", tuple(accept_types))
30
+
31
+ @classmethod
32
+ def parse(cls, value: str) -> Self:
33
+ return cls(*cls.visitor.visit(cls._node(value)))
34
+
35
+ @property
36
+ def value(self) -> str:
37
+ return ", ".join(str(accept_type) for accept_type in self.accept_types)
@@ -0,0 +1,48 @@
1
+ """Accept-Charset header class."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import ClassVar
5
+
6
+ from abnf import Rule
7
+ from abnf.grammars import rfc9110
8
+ from typing_extensions import Self
9
+
10
+ from http_fields.header import Header
11
+ from http_fields.visitors.rfc9110 import AcceptCharsetVisitor, Token, Weight
12
+
13
+
14
+ def _as_weight(value: float | Weight | None) -> Weight | None:
15
+ """Normalize a weight argument to a Weight or None. A numeric 0 is a valid weight
16
+ ("not acceptable"), so only None means "no weight specified"."""
17
+ if isinstance(value, Weight):
18
+ return value
19
+ if isinstance(value, (int, float)) and not isinstance(value, bool):
20
+ return Weight(float(value))
21
+ return None
22
+
23
+
24
+ @dataclass(frozen=True)
25
+ class AcceptCharset(Header):
26
+ """Accept-Charset header, as defined by RFC 9110."""
27
+
28
+ name: ClassVar[str] = "accept-charset"
29
+ rule: ClassVar[Rule] = rfc9110.Rule("Accept-Charset")
30
+ visitor: ClassVar[AcceptCharsetVisitor] = AcceptCharsetVisitor()
31
+
32
+ charsets: tuple[tuple[Token, Weight | None], ...]
33
+
34
+ def __init__(self, *charsets: tuple[str, float | Weight | None]) -> None:
35
+ object.__setattr__(
36
+ self, "charsets", tuple((Token(c), _as_weight(w)) for c, w in charsets)
37
+ )
38
+
39
+ @classmethod
40
+ def parse(cls, value: str) -> Self:
41
+ return cls(*cls.visitor.visit(cls._node(value)))
42
+
43
+ @property
44
+ def value(self) -> str:
45
+ return ", ".join(
46
+ f"{charset}" + (f";{weight}" if weight is not None else "")
47
+ for charset, weight in self.charsets
48
+ )
@@ -0,0 +1,42 @@
1
+ """Accept-Encoding header class."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import ClassVar
5
+
6
+ from abnf import Rule
7
+ from abnf.grammars import rfc9110
8
+ from typing_extensions import Self
9
+
10
+ from http_fields.header import Header
11
+ from http_fields.visitors.rfc9110 import AcceptEncodingVisitor, Weight, WeightedCoding
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class AcceptEncoding(Header):
16
+ """Accept-Encoding header, as defined by RFC 9110."""
17
+
18
+ name: ClassVar[str] = "accept-encoding"
19
+ rule: ClassVar[Rule] = rfc9110.Rule("Accept-Encoding")
20
+ visitor: ClassVar[AcceptEncodingVisitor] = AcceptEncodingVisitor()
21
+
22
+ codings: tuple[WeightedCoding, ...]
23
+
24
+ def __init__(
25
+ self, *codings: WeightedCoding | tuple[str, float | Weight | None]
26
+ ) -> None:
27
+ object.__setattr__(
28
+ self,
29
+ "codings",
30
+ tuple(
31
+ c if isinstance(c, WeightedCoding) else WeightedCoding(*c)
32
+ for c in codings
33
+ ),
34
+ )
35
+
36
+ @classmethod
37
+ def parse(cls, value: str) -> Self:
38
+ return cls(*cls.visitor.visit(cls._node(value)))
39
+
40
+ @property
41
+ def value(self) -> str:
42
+ return ", ".join(str(coding) for coding in self.codings)
@@ -0,0 +1,47 @@
1
+ """Accept-Language header class."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import ClassVar
5
+
6
+ from abnf import Rule
7
+ from abnf.grammars import rfc9110
8
+ from typing_extensions import Self
9
+
10
+ from http_fields.header import Header
11
+ from http_fields.visitors.rfc9110 import (
12
+ AcceptLanguageVisitor,
13
+ Weight,
14
+ WeightedLanguageRange,
15
+ )
16
+
17
+
18
+ @dataclass(frozen=True)
19
+ class AcceptLanguage(Header):
20
+ """Accept-Language header, as defined by RFC 9110."""
21
+
22
+ name: ClassVar[str] = "accept-language"
23
+ rule: ClassVar[Rule] = rfc9110.Rule("Accept-Language")
24
+ visitor: ClassVar[AcceptLanguageVisitor] = AcceptLanguageVisitor()
25
+
26
+ language_ranges: tuple[WeightedLanguageRange, ...]
27
+
28
+ def __init__(
29
+ self,
30
+ *language_ranges: WeightedLanguageRange | tuple[str, float | Weight | None],
31
+ ) -> None:
32
+ object.__setattr__(
33
+ self,
34
+ "language_ranges",
35
+ tuple(
36
+ r if isinstance(r, WeightedLanguageRange) else WeightedLanguageRange(*r)
37
+ for r in language_ranges
38
+ ),
39
+ )
40
+
41
+ @classmethod
42
+ def parse(cls, value: str) -> Self:
43
+ return cls(*cls.visitor.visit(cls._node(value)))
44
+
45
+ @property
46
+ def value(self) -> str:
47
+ return ", ".join(str(r) for r in self.language_ranges)
@@ -0,0 +1,35 @@
1
+ """AcceptRanges header class."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import ClassVar
5
+
6
+ from abnf import Rule
7
+ from abnf.grammars import rfc9110
8
+ from typing_extensions import Self
9
+
10
+ from http_fields.header import Header
11
+ from http_fields.visitors.rfc9110 import AcceptRangesVisitor, RangeUnit
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class AcceptRanges(Header):
16
+ """Accept-Ranges header, as defined by RFC 9110."""
17
+
18
+ name: ClassVar[str] = "accept-ranges"
19
+ rule: ClassVar[Rule] = rfc9110.Rule("accept-ranges")
20
+ visitor: ClassVar[AcceptRangesVisitor] = AcceptRangesVisitor()
21
+
22
+ range_units: tuple[RangeUnit, ...]
23
+
24
+ def __init__(self, *range_units: RangeUnit) -> None:
25
+ object.__setattr__(self, "range_units", tuple(range_units))
26
+
27
+ @classmethod
28
+ def parse(cls, value: str) -> Self:
29
+ return cls(
30
+ *(RangeUnit(r, parse=False) for r in cls.visitor.visit(cls._node(value)))
31
+ )
32
+
33
+ @property
34
+ def value(self) -> str:
35
+ return ",".join(self.range_units)
@@ -0,0 +1,197 @@
1
+ """Access-Control-* (CORS) header classes, as defined by the Fetch standard.
2
+
3
+ The grammar comes from abnf's ``cors`` module.
4
+ """
5
+
6
+ from dataclasses import dataclass
7
+ from typing import ClassVar
8
+
9
+ from abnf import Rule
10
+ from abnf.grammars import cors
11
+ from typing_extensions import Self
12
+
13
+ from http_fields.header import Header
14
+ from http_fields.parsedobjs import NonNegativeInt, ParsedStr
15
+
16
+
17
+ class CorsMethod(ParsedStr):
18
+ """A CORS method token. Self-validating."""
19
+
20
+ parser = cors.Rule("method")
21
+
22
+
23
+ class CorsFieldName(ParsedStr):
24
+ """A CORS field-name. Self-validating."""
25
+
26
+ parser = cors.Rule("field-name")
27
+
28
+
29
+ class AllowedOrigin(ParsedStr):
30
+ """An Access-Control-Allow-Origin value (an origin, ``null``, or ``*``). Self-validating."""
31
+
32
+ parser = cors.Rule("Access-Control-Allow-Origin")
33
+
34
+
35
+ class RequestedMethod(ParsedStr):
36
+ """An Access-Control-Request-Method value (a single method). Self-validating."""
37
+
38
+ parser = cors.Rule("Access-Control-Request-Method")
39
+
40
+
41
+ __all__ = [
42
+ "AccessControlAllowCredentials",
43
+ "AccessControlAllowHeaders",
44
+ "AccessControlAllowMethods",
45
+ "AccessControlAllowOrigin",
46
+ "AccessControlExposeHeaders",
47
+ "AccessControlMaxAge",
48
+ "AccessControlRequestHeaders",
49
+ "AccessControlRequestMethod",
50
+ ]
51
+
52
+
53
+ @dataclass(frozen=True)
54
+ class _CorsList(Header):
55
+ """Base for CORS headers that are a comma-separated list of tokens (methods or
56
+ field-names). Subclasses set ``name``/``rule`` and ``item_type`` (the self-validating
57
+ ParsedStr type of each list item)."""
58
+
59
+ item_type: ClassVar[type[ParsedStr]]
60
+
61
+ # Subclasses narrow this to their item_type so the field-type check is strict.
62
+ items: tuple[ParsedStr, ...]
63
+
64
+ def __init__(self, *items: ParsedStr) -> None:
65
+ object.__setattr__(self, "items", tuple(items))
66
+
67
+ @classmethod
68
+ def parse(cls, value: str) -> Self:
69
+ node = cls._node(value)
70
+ it = cls.item_type
71
+ return cls(
72
+ *(
73
+ it(c.value, parse=False)
74
+ for c in node.children
75
+ if c.name == it.parser.name
76
+ )
77
+ )
78
+
79
+ @property
80
+ def value(self) -> str:
81
+ return ", ".join(self.items)
82
+
83
+
84
+ class AccessControlAllowMethods(_CorsList):
85
+ """Access-Control-Allow-Methods header."""
86
+
87
+ name: ClassVar[str] = "access-control-allow-methods"
88
+ rule: ClassVar[Rule] = cors.Rule("Access-Control-Allow-Methods")
89
+ item_type = CorsMethod
90
+ items: tuple[CorsMethod, ...]
91
+
92
+
93
+ class AccessControlAllowHeaders(_CorsList):
94
+ """Access-Control-Allow-Headers header."""
95
+
96
+ name: ClassVar[str] = "access-control-allow-headers"
97
+ rule: ClassVar[Rule] = cors.Rule("Access-Control-Allow-Headers")
98
+ item_type = CorsFieldName
99
+ items: tuple[CorsFieldName, ...]
100
+
101
+
102
+ class AccessControlExposeHeaders(_CorsList):
103
+ """Access-Control-Expose-Headers header."""
104
+
105
+ name: ClassVar[str] = "access-control-expose-headers"
106
+ rule: ClassVar[Rule] = cors.Rule("Access-Control-Expose-Headers")
107
+ item_type = CorsFieldName
108
+ items: tuple[CorsFieldName, ...]
109
+
110
+
111
+ class AccessControlRequestHeaders(_CorsList):
112
+ """Access-Control-Request-Headers header."""
113
+
114
+ name: ClassVar[str] = "access-control-request-headers"
115
+ rule: ClassVar[Rule] = cors.Rule("Access-Control-Request-Headers")
116
+ item_type = CorsFieldName
117
+ items: tuple[CorsFieldName, ...]
118
+
119
+
120
+ @dataclass(frozen=True)
121
+ class AccessControlAllowOrigin(Header):
122
+ """Access-Control-Allow-Origin header (an origin, ``null``, or ``*``)."""
123
+
124
+ name: ClassVar[str] = "access-control-allow-origin"
125
+ rule: ClassVar[Rule] = cors.Rule("Access-Control-Allow-Origin")
126
+
127
+ origin: AllowedOrigin
128
+
129
+ def __init__(self, origin: str) -> None:
130
+ object.__setattr__(self, "origin", AllowedOrigin(origin))
131
+
132
+ @classmethod
133
+ def parse(cls, value: str) -> Self:
134
+ return cls(value)
135
+
136
+ @property
137
+ def value(self) -> str:
138
+ return self.origin
139
+
140
+
141
+ @dataclass(frozen=True)
142
+ class AccessControlRequestMethod(Header):
143
+ """Access-Control-Request-Method header (a single method)."""
144
+
145
+ name: ClassVar[str] = "access-control-request-method"
146
+ rule: ClassVar[Rule] = cors.Rule("Access-Control-Request-Method")
147
+
148
+ method: RequestedMethod
149
+
150
+ def __init__(self, method: str) -> None:
151
+ object.__setattr__(self, "method", RequestedMethod(method))
152
+
153
+ @classmethod
154
+ def parse(cls, value: str) -> Self:
155
+ return cls(value)
156
+
157
+ @property
158
+ def value(self) -> str:
159
+ return self.method
160
+
161
+
162
+ @dataclass(frozen=True)
163
+ class AccessControlMaxAge(Header):
164
+ """Access-Control-Max-Age header (delta-seconds)."""
165
+
166
+ name: ClassVar[str] = "access-control-max-age"
167
+ rule: ClassVar[Rule] = cors.Rule("Access-Control-Max-Age")
168
+
169
+ max_age: NonNegativeInt
170
+
171
+ def __init__(self, max_age: int) -> None:
172
+ object.__setattr__(self, "max_age", NonNegativeInt(max_age))
173
+
174
+ @classmethod
175
+ def parse(cls, value: str) -> Self:
176
+ return cls(NonNegativeInt(cls._node(value).value))
177
+
178
+ @property
179
+ def value(self) -> str:
180
+ return str(self.max_age)
181
+
182
+
183
+ @dataclass(frozen=True)
184
+ class AccessControlAllowCredentials(Header):
185
+ """Access-Control-Allow-Credentials header (always the literal ``true``)."""
186
+
187
+ name: ClassVar[str] = "access-control-allow-credentials"
188
+ rule: ClassVar[Rule] = cors.Rule("Access-Control-Allow-Credentials")
189
+
190
+ @classmethod
191
+ def parse(cls, value: str) -> Self:
192
+ cls._node(value) # validates the value is "true"
193
+ return cls()
194
+
195
+ @property
196
+ def value(self) -> str:
197
+ return "true"
http_fields/age.py ADDED
@@ -0,0 +1,34 @@
1
+ """Age header class."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import ClassVar
5
+
6
+ from abnf import Rule
7
+ from abnf.grammars import rfc9111
8
+ from typing_extensions import Self
9
+
10
+ from http_fields.header import Header
11
+ from http_fields.parsedobjs import NonNegativeInt
12
+ from http_fields.visitors.rfc9111 import AgeVisitor
13
+
14
+
15
+ @dataclass(frozen=True)
16
+ class Age(Header):
17
+ """Age header, as defined by RFC 9111."""
18
+
19
+ name: ClassVar[str] = "age"
20
+ rule: ClassVar[Rule] = rfc9111.Rule("Age")
21
+ visitor: ClassVar[AgeVisitor] = AgeVisitor()
22
+
23
+ seconds: NonNegativeInt
24
+
25
+ def __init__(self, seconds: int) -> None:
26
+ object.__setattr__(self, "seconds", NonNegativeInt(seconds))
27
+
28
+ @classmethod
29
+ def parse(cls, value: str) -> Self:
30
+ return cls(cls.visitor.visit(cls._node(value)))
31
+
32
+ @property
33
+ def value(self) -> str:
34
+ return str(self.seconds)
http_fields/allow.py ADDED
@@ -0,0 +1,46 @@
1
+ """Allow header class."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import ClassVar
5
+
6
+ from abnf import Rule
7
+ from abnf.grammars import rfc9110
8
+ from typing_extensions import Self
9
+
10
+ from http_fields.header import Header
11
+ from http_fields.parsedobjs import ParsedStr
12
+ from http_fields.visitors.rfc9110 import AllowVisitor
13
+
14
+
15
+ class Method(ParsedStr):
16
+ """An HTTP method (a token). Methods are case-sensitive (RFC 9110 section 9.1),
17
+ unlike the caseless :class:`Token`."""
18
+
19
+ parser = rfc9110.Rule("token")
20
+
21
+
22
+ @dataclass(frozen=True)
23
+ class Allow(Header):
24
+ """Allow header, as defined by RFC 9110.
25
+
26
+ Allow: GET, POST, OPTIONS
27
+ """
28
+
29
+ name: ClassVar[str] = "Allow"
30
+ rule: ClassVar[Rule] = rfc9110.Rule("Allow")
31
+ visitor: ClassVar[AllowVisitor] = AllowVisitor()
32
+
33
+ methods: tuple[Method, ...]
34
+
35
+ def __init__(self, *methods: Method) -> None:
36
+ object.__setattr__(self, "methods", tuple(methods))
37
+
38
+ @classmethod
39
+ def parse(cls, value: str) -> Self:
40
+ return cls(
41
+ *(Method(m, parse=False) for m in cls.visitor.visit(cls._node(value)))
42
+ )
43
+
44
+ @property
45
+ def value(self) -> str:
46
+ return ",".join(self.methods)