feat: RFC compliant typed headers#360
Conversation
… quote-aware splitting Closes serverpod#102
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThe PR raises the Dart SDK baseline to 3.10.3, updates CI gating to match, and refactors multiple typed HTTP header parsers and primitives to use stricter canonicalization, new parsing helpers, and revised validation semantics. Tests and IO behavior were updated to match the new header contracts. ChangesCI and SDK baseline
Typed headers and primitives
Sequence Diagram(s)sequenceDiagram
participant PackageExports as relic_core.dart
participant Parsers as typed header parsers
participant IO as relic_io handlers
participant Tests as typed tests
PackageExports->>Parsers: export new primitives
Parsers->>IO: provide parsed headers and validators
Parsers->>Tests: expose new parse/encode behavior
IO->>Tests: verified by range and transfer-encoding tests
Estimated code review effort🎯 5 (Critical) | ⏱️ ~90+ minutes Possibly related issues
Possibly related PRs
✨ Finishing Touches🧪 Generate unit tests (beta)
|
da8791d to
9608c86
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #360 +/- ##
==========================================
+ Coverage 91.21% 91.72% +0.51%
==========================================
Files 98 108 +10
Lines 3849 4510 +661
Branches 1964 2291 +327
==========================================
+ Hits 3511 4137 +626
- Misses 338 373 +35
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
34062ed to
1fe6945
Compare
|
@CodeRabbit review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 18
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
packages/relic_core/lib/src/headers/typed/headers/accept_header.dart (1)
79-114:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftPreserve media-range parameters instead of discarding them.
MediaRange.parse()now walks the;parameters only to extractq, and_encode()serializes just$type/$subtypeplus the weight. That means inputs liketext/html;level=1orapplication/json;charset=utf-8;q=0.8round-trip without their media parameters, which changes content-negotiation semantics rather than just normalizing syntax. This needs a parameter model onMediaRangeand matching encode logic before the Accept parser can be considered RFC-compliant.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/relic_core/lib/src/headers/typed/headers/accept_header.dart` around lines 79 - 114, MediaRange.parse currently only extracts the q-value and drops all other media parameters; update the MediaRange model to include a parameters field (e.g., Map<String,String> or List<KeyValuePair>) and modify MediaRange.parse to parse every semicolon parameter into that field (case-preserving keys but compare keys case-insensitively for 'q' to set quality), and update the MediaRange constructor to accept parameters; then change _encode to serialize type/subtype followed by all preserved parameters in their original order and finally append the ;q=... token if a quality is present (ensuring formatting via formatQValue) so inputs like "text/html;level=1" or "application/json;charset=utf-8;q=0.8" round-trip correctly.packages/relic_core/lib/src/headers/typed/headers/content_encoding_header.dart (1)
36-40:⚠️ Potential issue | 🟠 Major | ⚡ Quick winNormalize first, then de-duplicate, in
content_encoding_header.dart,te_header.dart, andvary_header.dart. Each of these paths removes duplicates on the raw token first and only lowercases afterward, so case-only variants likegzip, GZIPorAccept-Encoding, accept-encodingsurvive as duplicate canonical entries. The shared fix is to apply case normalization before uniqueness filtering, or to perform a second dedupe pass on the canonicalized value before storing it.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/relic_core/lib/src/headers/typed/headers/content_encoding_header.dart` around lines 36 - 40, The current flow maps splitValues to ContentEncoding.parse and then deduplicates raw tokens, which preserves case-only duplicates; update the logic in content_encoding_header.dart (and mirror in te_header.dart and vary_header.dart) to normalize/canonicalize each token (e.g., lowercase or use ContentEncoding.parse()/canonical form) before performing uniqueness filtering, or perform a second dedupe pass after mapping to the canonical value; specifically, ensure the code that builds parsedEncodings (the splitValues.map -> ContentEncoding.parse pipeline) lowercases/canonicalizes values first or removes duplicates from the parsedEncodings list before calling ContentEncodingHeader.encodings so case-only variants like "gzip" and "GZIP" are collapsed into one entry.packages/relic_core/lib/src/headers/typed/headers/set_cookie_header.dart (1)
245-263:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFix the unresolved dartdoc member references.
The pipeline is already warning that
[Cookie.sameSite]and[Cookie.secure]do not resolve here. Those links should point atSetCookieHeader, notCookie.Suggested fix
/// Cookie cross-site availability configuration. /// -/// The value of [Cookie.sameSite], which defines whether an +/// The value of [SetCookieHeader.sameSite], which defines whether a /// HTTP cookie is available from other sites or not. /// /// Has three possible values: [lax], [strict] and [none]. final class SameSite { @@ /// Cookie with this value will be sent in all requests. /// - /// [Cookie.secure] must also be set to true, otherwise the `none` value + /// [SetCookieHeader.secure] must also be set to true, otherwise the `none` value /// will have no effect. static const none = SameSite._('None');🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/relic_core/lib/src/headers/typed/headers/set_cookie_header.dart` around lines 245 - 263, The documentation on the SameSite class contains unresolved references to Cookie.sameSite and Cookie.secure; update those dartdoc links to point at the SetCookieHeader API instead (e.g., replace `[Cookie.sameSite]` with `[SetCookieHeader.sameSite]` and `[Cookie.secure]` with `[SetCookieHeader.secure]`) so the links resolve correctly; modify the doc comments in the SameSite class accordingly.Source: Pipeline failures
packages/relic_core/lib/src/headers/typed/headers/authorization_header.dart (1)
309-328:⚠️ Potential issue | 🟠 Major | ⚡ Quick winValidate and strip the
Digestscheme before scanning auth-params.
DigestAuthorizationHeader.parse()currently regex-scans the entire input, so strings likeBasic username="u", realm="r", nonce="n", uri="/", response="x"or even bareusername="u", ...are accepted as Digest headers. That makes the direct parser inconsistent withBasicAuthorizationHeader.parse()/BearerAuthorizationHeader.parse()and weakens scheme validation.Suggested fix
factory DigestAuthorizationHeader.parse(final String value) { if (value.isEmpty) { throw const FormatException('Digest token cannot be empty.'); } + final paramsValue = _stripScheme(value, prefix); // Each auth-param is `token = ( token / quoted-string )` (RFC 7616 3.4): // quoted-string values are DQUOTE-wrapped (group 2, with quoted-pair // escapes), token values are bare (group 3). Accepting both is required // because conformant peers send algorithm/qop/nc/stale unquoted. final Map<String, String> params = {}; final regex = RegExp(r'(\w+)\s*=\s*(?:"((?:[^"\\]|\\.)*)"|([^",\s]+))'); - for (final match in regex.allMatches(value)) { + for (final match in regex.allMatches(paramsValue)) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/relic_core/lib/src/headers/typed/headers/authorization_header.dart` around lines 309 - 328, DigestAuthorizationHeader.parse currently scans the whole input and accepts non-Digest schemes; update parse to validate and strip the "Digest" scheme token (case-insensitive) before running the auth-param regex: check the input begins with "Digest" followed by whitespace, remove that scheme prefix and trim the remainder, throw a FormatException if the scheme is missing/incorrect or the remainder is empty, then proceed to extract params with the existing regex and keep using _unescapeQuoted and Token.validate for values.packages/relic_core/lib/src/headers/typed/headers/from_header.dart (1)
24-34:⚠️ Potential issue | 🟠 MajorFix FromHeader.parse “kept as-is” claim: splitTrimAndFilterUnique is not quote-aware
FromHeader.parseroutes values throughsplitTrimAndFilterUnique(), which splits usingString.split(',')(not quote-aware), so"Doe, John" <john@example.com>will be split/corrupted before reachingFromHeader.emails(...).- Update the comment/behavior (use a quote-aware mailbox-list splitter or avoid comma-splitting) so the stated “kept as-is” meaning matches actual parsing.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/relic_core/lib/src/headers/typed/headers/from_header.dart` around lines 24 - 34, FromHeader.parse currently calls splitTrimAndFilterUnique (which uses naive String.split(',') and is not quote-aware), causing quoted display-names like "Doe, John" <john@example.com> to be incorrectly split; update FromHeader.parse to stop using splitTrimAndFilterUnique and either pass the original Iterable<String> values through unchanged to FromHeader.emails or replace the splitter with a quote-aware mailbox-list splitter that follows RFC 5322 semantics; locate the call in FromHeader.parse and change it to use a proper quote-aware splitter (or no splitting) so the earlier comment “kept as-is rather than format-validated” is accurate.
🧹 Nitpick comments (1)
packages/relic/test/headers/header_test.dart (1)
856-859: ⚡ Quick winAdd a versioned
UpgradeProtocolcase to this round-trip matrix.The breaking change here is that
versionis now an opaqueString, but this shared coverage still only exercises a protocol without any version. Using something likeUpgradeProtocol(protocol: 'HTTP', version: '2.0')would actually verify the new parse/encode/equality contract and catch the formatting-preservation regressions this PR is trying to prevent. This follows the updatedUpgradeProtocolsemantics described in the PR context.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/relic/test/headers/header_test.dart` around lines 856 - 859, The round-trip matrix for Headers.upgrade currently only tests UpgradeProtocol without a version; update the test to include a versioned case so encoding/decoding and equality are exercised with the new opaque String version semantics. Modify the matrix entry that sets h.upgrade = UpgradeHeader.protocols([...]) to include an additional UpgradeProtocol instance with both protocol and version set (e.g., UpgradeProtocol(protocol: 'HTTP', version: '2.0')) alongside the existing protocol-only case so Headers.upgrade, UpgradeHeader, and UpgradeProtocol parsing/encoding are validated for the versioned format.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/relic_core/lib/src/headers/typed/headers/accept_ranges_header.dart`:
- Around line 28-49: In AcceptRangesHeader.parse, detect if the parsed,
lower-cased units list contains 'none' together with any other unit and reject
that case by throwing a FormatException (e.g., "Invalid value: 'none' must be
alone"); update the factory AcceptRangesHeader.parse to perform this exclusivity
check after mapping Token.validate and before returning
AcceptRangesHeader._(List.unmodifiable(units)), so the typed model never
contains contradictory states for isNone/isBytes.
In `@packages/relic_core/lib/src/headers/typed/headers/authorization_header.dart`:
- Around line 298-302: The constructor currently validates algorithm/qop/nc
using Token.validate, but nc must be RFC-compliant (exactly 8 hex digits) per
Digest specs; replace the Token.validate(nc!) check with an RFC nc validator:
verify nc matches /^[0-9A-Fa-f]{8}$/ (or call a new helper e.g.
NCAuthorization.validateNc) and throw the same validation error if it fails,
keeping Token.validate for algorithm and qop and referencing the nc variable and
Token.validate call sites to locate the change.
- Around line 465-469: The _quoteString function currently only escapes quotes
and backslashes but allows raw control characters (e.g., CR/LF) which can enable
header injection; modify _quoteString to validate the input string for ASCII
control characters (U+0000–U+001F and U+007F) and reject them before quoting by
throwing a descriptive exception (e.g., FormatException or ArgumentError) when
any control character is present; keep the existing escapes for '"' and '\' and
only proceed to return the quoted string if validation passes so Digest/header
values cannot contain raw control characters.
In `@packages/relic_core/lib/src/headers/typed/headers/cache_control_header.dart`:
- Around line 125-134: The _delta function collapses a bare directive (e.g.,
"max-stale") into null making it indistinguishable from an absent directive;
change the parsing to preserve the bare form by returning a sentinel value
(e.g., -1) or converting the return type to a tri-state (e.g., enum or int? with
distinct values) so callers can detect "present-without-value" vs "absent".
Update _delta (and its callers that handle the Cache-Control max-stale handling
and the fields referenced around lines 209-210) to treat the sentinel/tri-state
as "bare directive present" when encoding and keep null only for truly absent
directives. Ensure re-encoding emits "max-stale" when the sentinel/tri-state
indicates presence-without-value.
- Around line 176-178: The code currently throws when a directive name is not in
_validDirectiveSet; update the parser in cache_control_header.dart (the block
that checks if (!_validDirectiveSet.contains(name))) to ignore unknown/extension
directives instead of throwing — simply skip unrecognized names (and their
optional values) and continue parsing so forward-compatible Cache-Control
extension directives are accepted; do not remove handling for known directives,
just replace the throw const FormatException('Invalid directive') with logic to
consume/skip the unknown directive and proceed.
In
`@packages/relic_core/lib/src/headers/typed/headers/content_language_header.dart`:
- Around line 32-41: The code currently validates tags with
LanguageTag.parse(language) but returns the original raw string, which preserves
case and makes comparisons/hashCode case-sensitive; change the logic in the
block that builds languages so it stores a canonical/normalized form from the
parsed tag (use the parsed LanguageTag instance’s canonical representation,
e.g., parsedTag.toString() or its canonicalization API) instead of the raw
input, and ensure the same normalized values are used by the header's equality
(==) and hashCode calculations so `en-US` and `EN-us` compare equal.
In `@packages/relic_core/lib/src/headers/typed/headers/content_range_header.dart`:
- Around line 31-44: The ContentRangeHeader constructor currently allows
negative start, end, or size values; update the ContentRangeHeader constructor
to validate that any non-null numeric parameters (start, end, size) are >= 0 and
throw a FormatException with an appropriate message if any are negative. Locate
the public ContentRangeHeader constructor and add these checks (in addition to
the existing null/coherence and start<=end checks) so direct construction cannot
produce negative-range values that would serialize invalid Content-Range
headers.
In
`@packages/relic_core/lib/src/headers/typed/headers/permission_policy_header.dart`:
- Around line 29-40: The constructor PermissionsPolicyHeader.directives
currently only rejects empty names; update it to validate each
PermissionsPolicyDirective.name as a structured-field token (no spaces or
control chars, only allowed token characters) and throw a FormatException for
any invalid token. Specifically, in PermissionsPolicyHeader.directives iterate
the directives and check directive.name against a structured-field token pattern
(e.g., token characters per RFC — no whitespace or embedded quotes/special
separators); if any name fails, throw a clear FormatException (e.g.,
"Permissions-Policy feature name is invalid") so malformed names like " " or
"geolocation allow" are rejected at construction. Ensure you reference
PermissionsPolicyHeader.directives and PermissionsPolicyDirective.name when
adding the validation.
In `@packages/relic_core/lib/src/headers/typed/headers/set_cookie_header.dart`:
- Around line 124-160: The switch in set_cookie_header.dart inside the attribute
parsing block (switch on attrName.toLowerCase()) is missing terminators causing
fall-through; update each case (cases 'samesite', 'path', 'domain', 'max-age',
'expires', 'secure', 'httponly') to end with an explicit break (or return) after
setting the corresponding variable or throwing, so execution does not continue
into subsequent cases; ensure the default remains as-is.
In `@packages/relic_core/lib/src/headers/typed/headers/util/cookie_util.dart`:
- Around line 95-103: The validateCookiePath function currently allows non-ASCII
characters; update its validation loop (in validateCookiePath) to also reject
any code units >= 0x80 by throwing the same FormatException so path bytes
outside the 0x00-0x7F ASCII range are treated as invalid; keep the existing
checks for CTLs, DEL and ';' and return the path only if all bytes are within
allowed ASCII range.
In `@packages/relic_core/lib/src/headers/typed/headers/util/report_to.dart`:
- Around line 35-37: The function encodeReportToParam currently only escapes
backslash and quote but must first reject control characters (e.g. CR, LF and
other C0 controls) to avoid header-splitting; update encodeReportToParam to scan
value for any code unit <= 0x1F or == 0x7F and throw an
ArgumentError/FormatException if any are found, then proceed to escape '\' and
'"' and return 'report-to="...'" as before (refer to encodeReportToParam for the
exact location to add the validation).
In `@packages/relic_core/lib/src/headers/typed/primitives/host.dart`:
- Around line 183-184: The single-line if containing the ALPHA check (the if
with condition "(c >= 0x41 && c <= 0x5A) || (c >= 0x61 && c <= 0x7A)" that
returns true) must be expanded to use braces: replace the single-line form with
a block using { return true; } so the statement is not a single-line if; update
the same function containing that if (the method with the ALPHA check) to use
the braced form to silence the analyzer warning.
- Around line 42-56: The host validation currently only rejects a small
forbidden set; update the host validation (around the loop using
_isForbiddenHostChar and the subsequent IP-literal check) to also enforce RFC
3986 reg-name rules: if the host is not an IP-literal (_validateIpLiteral) and
not an IPv4 address, ensure the host bytes are ASCII and every character is
either an unreserved character, a sub-delims character, or a valid
percent-encoding sequence ("%HEXHEX"); reject and throw FormatException for any
non-ASCII byte or invalid percent-encoding (refer to the host variable,
_isForbiddenHostChar, _validateIpLiteral and encode logic when implementing
this).
In `@packages/relic_core/lib/src/headers/typed/primitives/language_tag.dart`:
- Around line 104-129: The parser currently allows duplicate variant subtags and
duplicate extension singletons; update the parsing in the variant loop and the
extension loop (the code using _isVariant, _isExtensionSingleton, canonical,
raw, and source) to track seen items and reject repeats: create a local
Set<String> seenVariants and check before adding each _lower(raw[i]) in the
variant loop (throw FormatException with source on duplicate), and create a
Set<String> seenExtensionSingletons for the extension loop to check the
singleton (and/or its lowercased form) before accepting it (throw
FormatException if the singleton already seen); keep canonical additions and
existing subtag validation but ensure duplicates cause failure.
In
`@packages/relic_core/test/headers/typed/access_control_allow_origin_header_test.dart`:
- Around line 14-15: Remove the brittle hashCode inequality assertion comparing
wildcard.hashCode and opaque.hashCode and keep the semantic equality check only;
update the test in access_control_allow_origin_header_test.dart to delete the
line referencing wildcard.hashCode != opaque.hashCode and rely on
expect(wildcard == opaque, isFalse) (or expect(wildcard != opaque, isTrue)) to
assert inequality between the wildcard and opaque instances.
In `@packages/relic_core/test/headers/typed/transfer_encoding_header_test.dart`:
- Around line 78-84: The test currently asserts that
TransferEncodingHeader.parse(['custom-encoding']) throws, which incorrectly
rejects RFC‑allowed extension codings; update the test to accept extension
tokens instead of expecting a FormatException — modify the assertion in the test
that calls TransferEncodingHeader.parse to expect successful parsing (or a
specific representation for extension codings) rather than
throwsFormatException, and ensure any canonicalization code paths in
TransferEncodingHeader.parse/TransferCoding handling extension tokens (e.g.,
parsing logic used by TransferEncodingHeader.parse and the TransferCoding
representation) are exercised by this test.
In
`@packages/relic/test/headers/typed/access_control_allow_origin_header_test.dart`:
- Around line 73-83: The test currently only checks for any BadRequestException
from getServerRequestHeaders; update it to assert the specific rejection reason
for path-containing origins by matching the exception message or a specific
error property on BadRequestException (e.g., check e.message contains "origins
must not include paths" or "trailing slash" or the server's exact error text).
Replace the generic throwsA(isA<BadRequestException>()) with
throwsA(predicate((e) => e is BadRequestException && e.message.contains('<server
path-rejection message>'))) so the test validates the Fetch-style "origins must
not include paths" behavior for accessControlAllowOrigin.
---
Outside diff comments:
In `@packages/relic_core/lib/src/headers/typed/headers/accept_header.dart`:
- Around line 79-114: MediaRange.parse currently only extracts the q-value and
drops all other media parameters; update the MediaRange model to include a
parameters field (e.g., Map<String,String> or List<KeyValuePair>) and modify
MediaRange.parse to parse every semicolon parameter into that field
(case-preserving keys but compare keys case-insensitively for 'q' to set
quality), and update the MediaRange constructor to accept parameters; then
change _encode to serialize type/subtype followed by all preserved parameters in
their original order and finally append the ;q=... token if a quality is present
(ensuring formatting via formatQValue) so inputs like "text/html;level=1" or
"application/json;charset=utf-8;q=0.8" round-trip correctly.
In `@packages/relic_core/lib/src/headers/typed/headers/authorization_header.dart`:
- Around line 309-328: DigestAuthorizationHeader.parse currently scans the whole
input and accepts non-Digest schemes; update parse to validate and strip the
"Digest" scheme token (case-insensitive) before running the auth-param regex:
check the input begins with "Digest" followed by whitespace, remove that scheme
prefix and trim the remainder, throw a FormatException if the scheme is
missing/incorrect or the remainder is empty, then proceed to extract params with
the existing regex and keep using _unescapeQuoted and Token.validate for values.
In
`@packages/relic_core/lib/src/headers/typed/headers/content_encoding_header.dart`:
- Around line 36-40: The current flow maps splitValues to ContentEncoding.parse
and then deduplicates raw tokens, which preserves case-only duplicates; update
the logic in content_encoding_header.dart (and mirror in te_header.dart and
vary_header.dart) to normalize/canonicalize each token (e.g., lowercase or use
ContentEncoding.parse()/canonical form) before performing uniqueness filtering,
or perform a second dedupe pass after mapping to the canonical value;
specifically, ensure the code that builds parsedEncodings (the splitValues.map
-> ContentEncoding.parse pipeline) lowercases/canonicalizes values first or
removes duplicates from the parsedEncodings list before calling
ContentEncodingHeader.encodings so case-only variants like "gzip" and "GZIP" are
collapsed into one entry.
In `@packages/relic_core/lib/src/headers/typed/headers/from_header.dart`:
- Around line 24-34: FromHeader.parse currently calls splitTrimAndFilterUnique
(which uses naive String.split(',') and is not quote-aware), causing quoted
display-names like "Doe, John" <john@example.com> to be incorrectly split;
update FromHeader.parse to stop using splitTrimAndFilterUnique and either pass
the original Iterable<String> values through unchanged to FromHeader.emails or
replace the splitter with a quote-aware mailbox-list splitter that follows RFC
5322 semantics; locate the call in FromHeader.parse and change it to use a
proper quote-aware splitter (or no splitting) so the earlier comment “kept as-is
rather than format-validated” is accurate.
In `@packages/relic_core/lib/src/headers/typed/headers/set_cookie_header.dart`:
- Around line 245-263: The documentation on the SameSite class contains
unresolved references to Cookie.sameSite and Cookie.secure; update those dartdoc
links to point at the SetCookieHeader API instead (e.g., replace
`[Cookie.sameSite]` with `[SetCookieHeader.sameSite]` and `[Cookie.secure]` with
`[SetCookieHeader.secure]`) so the links resolve correctly; modify the doc
comments in the SameSite class accordingly.
---
Nitpick comments:
In `@packages/relic/test/headers/header_test.dart`:
- Around line 856-859: The round-trip matrix for Headers.upgrade currently only
tests UpgradeProtocol without a version; update the test to include a versioned
case so encoding/decoding and equality are exercised with the new opaque String
version semantics. Modify the matrix entry that sets h.upgrade =
UpgradeHeader.protocols([...]) to include an additional UpgradeProtocol instance
with both protocol and version set (e.g., UpgradeProtocol(protocol: 'HTTP',
version: '2.0')) alongside the existing protocol-only case so Headers.upgrade,
UpgradeHeader, and UpgradeProtocol parsing/encoding are validated for the
versioned format.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 4e5e23f4-1764-45d0-96bc-88e9c51ae11e
📒 Files selected for processing (104)
.github/workflows/ci.yamlpackages/relic/test/headers/header_test.dartpackages/relic/test/headers/typed/accept_encoding_header_test.dartpackages/relic/test/headers/typed/accept_header_test.dartpackages/relic/test/headers/typed/accept_language_test.dartpackages/relic/test/headers/typed/accept_ranges_header_test.dartpackages/relic/test/headers/typed/access_control_allow_headers_header_test.dartpackages/relic/test/headers/typed/access_control_allow_origin_header_test.dartpackages/relic/test/headers/typed/access_control_expose_headers_header_test.dartpackages/relic/test/headers/typed/authorization_header_test.dartpackages/relic/test/headers/typed/cache_control_header_test.dartpackages/relic/test/headers/typed/connection_header_test.dartpackages/relic/test/headers/typed/content_encoding_header_test.dartpackages/relic/test/headers/typed/content_language_header_test.dartpackages/relic/test/headers/typed/content_security_policy_header_test.dartpackages/relic/test/headers/typed/cookie_header_test.dartpackages/relic/test/headers/typed/expect_header_test.dartpackages/relic/test/headers/typed/from_header_test.dartpackages/relic/test/headers/typed/if_range_header_test.dartpackages/relic/test/headers/typed/permissions_policy_header_test.dartpackages/relic/test/headers/typed/proxy_authorization_header_test.dartpackages/relic/test/headers/typed/referrer_policy_header_test.dartpackages/relic/test/headers/typed/retry_after_header_test.dartpackages/relic/test/headers/typed/sec_fetch_dest_header_test.dartpackages/relic/test/headers/typed/sec_fetch_mode_header_test.dartpackages/relic/test/headers/typed/sec_fetch_site_header_test.dartpackages/relic/test/headers/typed/set_cookie_header_test.dartpackages/relic/test/headers/typed/te_header_test.dartpackages/relic/test/headers/typed/transfer_encoding_header_test.dartpackages/relic/test/headers/typed/upgrade_header_test.dartpackages/relic/test/headers/typed/vary_header_test.dartpackages/relic/test/message/apply_headers_test.dartpackages/relic_core/lib/relic_core.dartpackages/relic_core/lib/src/headers/typed/headers/accept_encoding_header.dartpackages/relic_core/lib/src/headers/typed/headers/accept_header.dartpackages/relic_core/lib/src/headers/typed/headers/accept_language_header.dartpackages/relic_core/lib/src/headers/typed/headers/accept_ranges_header.dartpackages/relic_core/lib/src/headers/typed/headers/access_control_allow_headers_header.dartpackages/relic_core/lib/src/headers/typed/headers/access_control_allow_origin_header.dartpackages/relic_core/lib/src/headers/typed/headers/access_control_expose_headers_header.dartpackages/relic_core/lib/src/headers/typed/headers/authorization_header.dartpackages/relic_core/lib/src/headers/typed/headers/cache_control_header.dartpackages/relic_core/lib/src/headers/typed/headers/clear_site_data_header.dartpackages/relic_core/lib/src/headers/typed/headers/connection_header.dartpackages/relic_core/lib/src/headers/typed/headers/content_encoding_header.dartpackages/relic_core/lib/src/headers/typed/headers/content_language_header.dartpackages/relic_core/lib/src/headers/typed/headers/content_range_header.dartpackages/relic_core/lib/src/headers/typed/headers/content_security_policy_header.dartpackages/relic_core/lib/src/headers/typed/headers/cookie_header.dartpackages/relic_core/lib/src/headers/typed/headers/cross_origin_embedder_policy_header.dartpackages/relic_core/lib/src/headers/typed/headers/cross_origin_opener_policy_header.dartpackages/relic_core/lib/src/headers/typed/headers/expect_header.dartpackages/relic_core/lib/src/headers/typed/headers/from_header.dartpackages/relic_core/lib/src/headers/typed/headers/host_header.dartpackages/relic_core/lib/src/headers/typed/headers/if_range_header.dartpackages/relic_core/lib/src/headers/typed/headers/permission_policy_header.dartpackages/relic_core/lib/src/headers/typed/headers/range_header.dartpackages/relic_core/lib/src/headers/typed/headers/referrer_policy_header.dartpackages/relic_core/lib/src/headers/typed/headers/retry_after_header.dartpackages/relic_core/lib/src/headers/typed/headers/sec_fetch_dest_header.dartpackages/relic_core/lib/src/headers/typed/headers/sec_fetch_mode_header.dartpackages/relic_core/lib/src/headers/typed/headers/sec_fetch_site_header.dartpackages/relic_core/lib/src/headers/typed/headers/set_cookie_header.dartpackages/relic_core/lib/src/headers/typed/headers/strict_transport_security_header.dartpackages/relic_core/lib/src/headers/typed/headers/te_header.dartpackages/relic_core/lib/src/headers/typed/headers/transfer_encoding_header.dartpackages/relic_core/lib/src/headers/typed/headers/upgrade_header.dartpackages/relic_core/lib/src/headers/typed/headers/util/cookie_util.dartpackages/relic_core/lib/src/headers/typed/headers/util/qvalue.dartpackages/relic_core/lib/src/headers/typed/headers/util/report_to.dartpackages/relic_core/lib/src/headers/typed/headers/vary_header.dartpackages/relic_core/lib/src/headers/typed/primitives/delta_seconds.dartpackages/relic_core/lib/src/headers/typed/primitives/etag_value.dartpackages/relic_core/lib/src/headers/typed/primitives/header_scanner.dartpackages/relic_core/lib/src/headers/typed/primitives/host.dartpackages/relic_core/lib/src/headers/typed/primitives/language_tag.dartpackages/relic_core/lib/src/headers/typed/primitives/origin.dartpackages/relic_core/lib/src/headers/typed/primitives/parameter_value.dartpackages/relic_core/lib/src/headers/typed/primitives/token.dartpackages/relic_core/test/headers/typed/access_control_allow_origin_header_test.dartpackages/relic_core/test/headers/typed/authorization_header_test.dartpackages/relic_core/test/headers/typed/connection_header_test.dartpackages/relic_core/test/headers/typed/content_range_header_test.dartpackages/relic_core/test/headers/typed/cross_origin_policy_header_test.dartpackages/relic_core/test/headers/typed/expect_header_test.dartpackages/relic_core/test/headers/typed/host_header_test.dartpackages/relic_core/test/headers/typed/permission_policy_header_test.dartpackages/relic_core/test/headers/typed/primitives/delta_seconds_test.dartpackages/relic_core/test/headers/typed/primitives/etag_value_test.dartpackages/relic_core/test/headers/typed/primitives/header_scanner_test.dartpackages/relic_core/test/headers/typed/primitives/host_test.dartpackages/relic_core/test/headers/typed/primitives/language_tag_test.dartpackages/relic_core/test/headers/typed/primitives/origin_test.dartpackages/relic_core/test/headers/typed/primitives/parameter_value_test.dartpackages/relic_core/test/headers/typed/primitives/token_test.dartpackages/relic_core/test/headers/typed/range_header_test.dartpackages/relic_core/test/headers/typed/set_cookie_header_test.dartpackages/relic_core/test/headers/typed/strict_transport_security_header_test.dartpackages/relic_core/test/headers/typed/transfer_encoding_header_test.dartpackages/relic_core/test/headers/typed/util/qvalue_test.dartpackages/relic_io/lib/src/adapter/http_response_extension.dartpackages/relic_io/lib/src/io/static/static_handler.dartpackages/relic_io/test/static/if_range_test.dartpubspec.yaml
a008fa0 to
2137809
Compare
…intercepts over a socket
… Sec-Fetch kept strict with corrected docs
…eport-to quoted values
2137809 to
55aee1d
Compare
…ngletons (BCP 47)
Description
Overhaul typed headers to be fully (/more?) RFC compliant. Parsing is still lenient (except for security related headers), but construction (and serialization) is always strict.
Related Issues
Pre-Launch Checklist
Please ensure that your PR meets the following requirements before submitting:
///), ensuring consistency with existing project documentation.Breaking Changes
Many typed headers changed, both their shape (Dart interface) and semantics (behavior or parse, etc.).
Additional Notes
These changes are landing now in concert with serverpod 4.0
Summary by CodeRabbit
New Features
Bug Fixes & Improvements