Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file

version: 2
updates:
- package-ecosystem: "pub"
directory: "/" # Location of package manifests
schedule:
interval: "daily"
42 changes: 42 additions & 0 deletions .github/workflows/dart.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

name: Dart

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

# Note: This workflow uses the latest stable version of the Dart SDK.
# You can specify other versions if desired, see documentation here:
# https://github.com/dart-lang/setup-dart/blob/main/README.md
# - uses: dart-lang/setup-dart@v1
- uses: dart-lang/setup-dart@v1

- name: Install dependencies
run: dart pub get

# Verify the use of 'dart format' on each commit.
- name: Verify formatting
run: dart format --output=none --set-exit-if-changed .

# Ensure code quality meets package standards.
- name: Analyze project source
run: dart analyze --fatal-infos

# Your project will need to have tests in test/ and a dependency on
# package:test for this step to succeed. Note that Flutter projects will
# want to change this to 'flutter test'.
- name: Run tests
run: dart test
14 changes: 14 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: Publish to pub.dev

on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+*'

jobs:
publish:
permissions:
id-token: write # Required for authentication using OIDC
uses: dart-lang/setup-dart/.github/workflows/publish.yml@v1
with:
environment: 'pub.dev'
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@
# Avoid committing pubspec.lock for library packages; see
# https://dart.dev/tools/pub/private-files#pubspec-lock.
pubspec.lock

# Ignore generated API documentation
doc/api/
13 changes: 13 additions & 0 deletions .pubignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Ignore local dart and pub tools config/caches
.dart_tool/
pubspec.lock

# Ignore generated API documentation
doc/api/

# Ignore development tools and benchmarks
tool/
benchmark/

# Ignore conformance tests submodule
structured-field-tests/
8 changes: 8 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
The MIT License (MIT)
Original code: Copyright © 2026 Dropbear Software

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
93 changes: 58 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
# Structured Field Values for HTTP (RFC 9651)
# Structured Field Values for HTTP

A fully compliant implementation of **RFC 9651 (Structured Field Values for HTTP)** in Dart. This package handles parsing and serialization of structured headers and trailers, obsoleting the older RFC 8941 specification.
[![pub package](https://img.shields.io/badge/pub-1.0.0-blue.svg)](https://pub.dev/packages/structured_field_values)
[![Dart CI](https://github.com/dropbear-software/structured_field_values/actions/workflows/dart.yml/badge.svg)](https://github.com/dropbear-software/structured_field_values/actions)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

It provides a strongly typed representation of structured fields and adheres strictly to the step-by-step parsing and serialization algorithms outlined in the RFC.
A fully compliant implementation of **RFC 9651 (Structured Field Values for
HTTP)** in native Dart. This package handles the parsing and serialization of
structured headers and trailers, replacing the older RFC 8941 specification.

It provides a strongly typed representation of structured fields and adheres
strictly to the step-by-step parsing and serialization algorithms outlined in
the RFC.

---

Expand All @@ -14,26 +22,41 @@ It provides a strongly typed representation of structured fields and adheres str
- `StructuredItem` (parameterized single values)
- **Inner Lists**: `StructuredInnerList` (parenthesized space-separated lists).
- **Parameters**: `StructuredParameters` (semicolon-delimited key-value pairs).
- **All RFC 9651 Bare Item Types**:
- **Integer**: range `-999,999,999,999,999` to `999,999,999,999,999`.
- **Decimal**: Up to 12 integer digits and 3 fractional digits.
- **String**: Printable ASCII characters (`%x20-7E`).
- **Token**: Identifiers beginning with `ALPHA` or `*`, plus `:` and `/`.
- **Byte Sequence**: Base64 encoded binary data.
- **Boolean**: `?1` (true) or `?0` (false).
- **Date**: Unix timestamps preceded by `@` (e.g. `@1659578233`).
- **Display String**: Unicode strings formatted with percent encoding (e.g. `%"This is intended for display to %c3%bcsers."`).

### RFC 9651 Bare Item Types

This library provides full support for all bare item types defined in the RFC:

| RFC Type | Dart Class | Description / Constraints | Example Syntax |
| :--- | :--- | :--- | :--- |
| **Integer** | `StructuredInteger` | Range `-999,999,999,999,999` to `999,999,999,999,999` | `42` |
| **Decimal** | `StructuredDecimal` | Up to 12 integer digits and 3 fractional component digits | `3.14` |
| **String** | `StructuredString` | Zero or more printable ASCII characters (`%x20-7E`) | `"hello"` |
| **Token** | `StructuredToken` | Identifiers starting with `ALPHA` or `*` | `text/html` |
| **Byte Sequence** | `StructuredByteSequence` | Base64 encoded binary data enclosed in colons | `:aGVsbG8=:` |
| **Boolean** | `StructuredBoolean` | `?1` (true) or `?0` (false) | `?1` |
| **Date** | `StructuredDate` | Unix timestamps preceded by `@` | `@1659578233` |
| **Display String** | `StructuredDisplayString` | Unicode strings formatted with percent-encoding | `%"hello"` |

---

## Compliance Highlights

- **Official Test Suite Compliance**: 100% compliant with the official IETF HTTP Working Group [structured-field-tests](https://github.com/httpwg/structured-field-tests) suite (tested against all 2,135 parse and serialization test cases).
- **Order Preservation**: Dictionary and Parameter maps preserve key insertion order.
- **Round-to-Even**: Decimals with $>3$ fractional digits are rounded using the round-to-nearest-even strategy (IEEE 754 round half to even) during serialization.
- **Strict Parsing**: Invalid syntaxes fail immediately throwing a `StructuredFieldParseException`.
- **Base64 Tolerances**: Adheres to the RFC advice to tolerate missing base64 padding or non-zero pad bits.
- **Token / String Distinction**: Explicit classes (`StructuredToken` vs `StructuredString`) ensure they are not conflated.
- **Official Test Suite Compliance**: 100% compliant with the official IETF
HTTP Working Group
[structured-field-tests](https://github.com/httpwg/structured-field-tests)
suite (tested against all 2,135 parse and serialization test cases).
- **Order Preservation**: Dictionary and Parameter maps preserve key insertion
order.
- **Round-to-Even**: Decimals with $>3$ fractional digits are rounded using the
round-to-nearest-even strategy (IEEE 754 round half to even) during
serialization.
- **Strict Parsing**: Invalid syntaxes fail immediately throwing a
`StructuredFieldParseException`.
- **Base64 Tolerances**: Adheres to the RFC advice to tolerate missing base64
padding or non-zero pad bits.
- **Token / String Distinction**: Explicit classes (`StructuredToken` vs
`StructuredString`) ensure they are not conflated.

---

Expand All @@ -43,9 +66,7 @@ Add the package to your `pubspec.yaml`:

```yaml
dependencies:
structured_field_values:
hosted: https://homebar.dropbear.dev
version: ^1.0.0
structured_field_values: ^1.0.0
```

Then run `dart pub get`.
Expand All @@ -56,32 +77,35 @@ Then run `dart pub get`.

### Parsing Field Values

To parse an HTTP header value, use standard codecs: `structuredList`, `structuredDictionary`, or `structuredItem`. Decoding is fully type-safe and does not require manual casting:
To parse an HTTP header value, use standard codecs: `structuredList`,
`structuredDictionary`, or `structuredItem`. Decoding is fully type-safe and
does not require manual casting:

```dart
import 'package:structured_field_values/structured_field_values.dart';

void main() {
// Parsing a List of Inner Lists
const listHeader = '("sugar" "tea"), ("rum")';
final parsedList = structuredList.decode(listHeader); // Statically typed as StructuredList
final parsedList = structuredList.decode(listHeader); // StructuredList
print(parsedList);

// Parsing a Dictionary with Boolean Parameters
const dictHeader = 'a=?0, b, c;foo=bar';
final parsedDict = structuredDictionary.decode(dictHeader); // Statically typed as StructuredDictionary
final parsedDict = structuredDictionary.decode(dictHeader); // StructuredDictionary
print(parsedDict);

// Parsing an Item
const itemHeader = '@1659578233;expires=3600';
final parsedItem = structuredItem.decode(itemHeader); // Statically typed as StructuredItem
final parsedItem = structuredItem.decode(itemHeader); // StructuredItem
print(parsedItem);
}
```

### Serializing Structured Fields

To serialize a structured field back to its HTTP header string representation, use the codec's `encode` method:
To serialize a structured field back to its HTTP header string representation,
use the codec's `encode` method:

```dart
import 'package:structured_field_values/structured_field_values.dart';
Expand All @@ -106,8 +130,10 @@ void main() {

### Exception Handling

- `StructuredFieldParseException` is thrown if the input string contains invalid syntax.
- `StructuredFieldSerializationException` is thrown if you try to serialize an out-of-range integer, an invalid key, or prohibited surrogate code points.
- `StructuredFieldParseException` is thrown if the input string contains
invalid syntax.
- `StructuredFieldSerializationException` is thrown if you try to serialize
an out-of-range integer, an invalid key, or prohibited surrogate code points.

```dart
import 'package:structured_field_values/structured_field_values.dart';
Expand All @@ -126,7 +152,9 @@ void main() {

## Conformance Testing

This package integrates the official IETF HTTP Working Group [structured-field-tests](https://github.com/httpwg/structured-field-tests) suite as a Git submodule.
This package integrates the official IETF HTTP Working Group
[structured-field-tests](https://github.com/httpwg/structured-field-tests)
suite as a Git submodule.

To run the conformance tests:

Expand All @@ -145,9 +173,4 @@ To run the conformance tests:

---

## Design and API References

Detailed classes can be found under:
* [types.dart](file:///home/mark/Code/structured_field_values/lib/src/types.dart) - Strong typing models.
* [parser.dart](file:///home/mark/Code/structured_field_values/lib/src/parser.dart) - Decoders and validators.
* [serializer.dart](file:///home/mark/Code/structured_field_values/lib/src/serializer.dart) - Encoders and formatters.
For support, feedback, or to report issues, please visit our [Issue Tracker](https://github.com/dropbear-software/structured_field_values/issues).
3 changes: 1 addition & 2 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ description: A Dart native implementation of Structured Field Values for HTTP (R
version: 1.0.0
repository: https://github.com/dropbear-software/structured_field_values
issue_tracker: https://github.com/dropbear-software/structured_field_values/issues
publish_to: https://homebar.dropbear.dev

environment:
sdk: ^3.12.0
Expand All @@ -13,6 +12,6 @@ dependencies:
meta: ^1.19.0

dev_dependencies:
benchmark_harness: any
benchmark_harness: ^2.4.0
lints: ^6.0.0
test: ^1.25.6
Loading