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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# httpsfv: Structured Field Values for HTTP in Go

This [Go (golang)](https://golang.org) library implements parsing and serialization for [Structured Field Values for HTTP](https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html).
This [Go (golang)](https://golang.org) library implements parsing and serialization for [Structured Field Values for HTTP (RFC 8941)](https://httpwg.org/specs/rfc8941.html).

[![PkgGoDev](https://pkg.go.dev/badge/github.com/dunglas/httpsfv)](https://pkg.go.dev/github.com/dunglas/httpsfv)
![CI](https://github.com/dunglas/httpsfv/workflows/CI/badge.svg)
Expand All @@ -9,7 +9,7 @@ This [Go (golang)](https://golang.org) library implements parsing and serializat

## Features

* Implements the latest version (19) of the Internet-Draft
* Fully implemented the RFC
* Compliant with [the official test suite](https://github.com/httpwg/structured-field-tests)
* Unit tested
* Strongly-typed
Expand Down
6 changes: 3 additions & 3 deletions bareitem.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ var ErrInvalidBareItem = errors.New(
)

// assertBareItem asserts that v is a valid bare item
// according to https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#item.
// according to https://httpwg.org/specs/rfc8941.html#item.
//
// v can be either:
//
Expand Down Expand Up @@ -48,7 +48,7 @@ func assertBareItem(v interface{}) {
}

// marshalBareItem serializes as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#ser-bare-item.
// https://httpwg.org/specs/rfc8941.html#ser-bare-item.
func marshalBareItem(b *strings.Builder, v interface{}) error {
switch v := v.(type) {
case bool:
Expand All @@ -74,7 +74,7 @@ func marshalBareItem(b *strings.Builder, v interface{}) error {
}

// parseBareItem parses as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#parse-bare-item.
// https://httpwg.org/specs/rfc8941.html#parse-bare-item.
func parseBareItem(s *scanner) (interface{}, error) {
if s.eof() {
return nil, &UnmarshalError{s.off, ErrUnexpectedEndOfString}
Expand Down
4 changes: 2 additions & 2 deletions binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
var ErrInvalidBinaryFormat = errors.New("invalid binary format")

// marshalBinary serializes as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#ser-binary.
// https://httpwg.org/specs/rfc8941.html#ser-binary.
func marshalBinary(b *strings.Builder, bs []byte) error {
if err := b.WriteByte(':'); err != nil {
return err
Expand All @@ -27,7 +27,7 @@ func marshalBinary(b *strings.Builder, bs []byte) error {
}

// parseBinary parses as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#parse-binary.
// https://httpwg.org/specs/rfc8941.html#parse-binary.
func parseBinary(s *scanner) ([]byte, error) {
if s.eof() || s.data[s.off] != ':' {
return nil, &UnmarshalError{s.off, ErrInvalidBinaryFormat}
Expand Down
4 changes: 2 additions & 2 deletions boolean.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
var ErrInvalidBooleanFormat = errors.New("invalid boolean format")

// marshalBoolean serializes as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#ser-boolean.
// https://httpwg.org/specs/rfc8941.html#ser-boolean.
func marshalBoolean(bd io.StringWriter, b bool) error {
if b {
_, err := bd.WriteString("?1")
Expand All @@ -23,7 +23,7 @@ func marshalBoolean(bd io.StringWriter, b bool) error {
}

// parseBoolean parses as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#parse-boolean.
// https://httpwg.org/specs/rfc8941.html#parse-boolean.
func parseBoolean(s *scanner) (bool, error) {
if s.eof() || s.data[s.off] != '?' {
return false, &UnmarshalError{s.off, ErrInvalidBooleanFormat}
Expand Down
2 changes: 1 addition & 1 deletion decimal.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const maxDecDigit = 3
var ErrInvalidDecimal = errors.New("the integer portion is larger than 12 digits: invalid decimal")

// marshalDecimal serializes as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#ser-decimal.
// https://httpwg.org/specs/rfc8941.html#ser-decimal.
//
// TODO(dunglas): add support for decimal float type when one will be available
// (https://github.com/golang/go/issues/19787)
Expand Down
4 changes: 2 additions & 2 deletions dictionary.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
)

// Dictionary is an ordered map of name-value pairs.
// See https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#dictionary
// See https://httpwg.org/specs/rfc8941.html#dictionary
// Values can be:
// * Item (Section 3.3.)
// * Inner List (Section 3.1.1.)
Expand Down Expand Up @@ -101,7 +101,7 @@ func (d *Dictionary) marshalSFV(b *strings.Builder) error {
}

// UnmarshalDictionary parses a dictionary as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#parse-dictionary.
// https://httpwg.org/specs/rfc8941.html#parse-dictionary.
func UnmarshalDictionary(v []string) (*Dictionary, error) {
s := &scanner{
data: strings.Join(v, ","),
Expand Down
6 changes: 3 additions & 3 deletions encode.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Package httpsfv implements serializing and parsing
// of Structured Field Values for HTTP as defined in the draft-ietf-httpbis-header-structure Internet-Draft.
// of Structured Field Values for HTTP as defined in RFC 8941.
//
// Structured Field Values are either lists, dictionaries or items. Dedicated types are provided for all of them.
// Dedicated types are also used for tokens, parameters and inner lists.
Expand All @@ -11,7 +11,7 @@
// byte[], for byte sequences
// bool, for booleans
//
// The specification is available at https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html.
// The specification is available at https://httpwg.org/specs/rfc8941.html.
package httpsfv

import (
Expand All @@ -29,7 +29,7 @@ type StructuredFieldValue interface {
}

// Marshal returns the HTTP Structured Value serialization of v
// as defined in https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#text-serialize.
// as defined in https://httpwg.org/specs/rfc8941.html#text-serialize.
//
// v must be a List, a Dictionary or an Item.
func Marshal(v StructuredFieldValue) (string, error) {
Expand Down
6 changes: 3 additions & 3 deletions innerlist.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
var ErrInvalidInnerListFormat = errors.New("invalid inner list format")

// InnerList represents an inner list as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#inner-list.
// https://httpwg.org/specs/rfc8941.html#inner-list.
type InnerList struct {
Items []Item
Params *Params
Expand All @@ -19,7 +19,7 @@ func (il InnerList) member() {
}

// marshalSFV serializes as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#ser-innerlist.
// https://httpwg.org/specs/rfc8941.html#ser-innerlist.
func (il InnerList) marshalSFV(b *strings.Builder) error {
if err := b.WriteByte('('); err != nil {
return err
Expand All @@ -46,7 +46,7 @@ func (il InnerList) marshalSFV(b *strings.Builder) error {
}

// parseInnerList parses as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#parse-item-or-list.
// https://httpwg.org/specs/rfc8941.html#parse-item-or-list.
func parseInnerList(s *scanner) (InnerList, error) {
if s.eof() || s.data[s.off] != '(' {
return InnerList{}, &UnmarshalError{s.off, ErrInvalidInnerListFormat}
Expand Down
4 changes: 2 additions & 2 deletions integer.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const (
)

// marshalInteger serialized as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#integer.
// https://httpwg.org/specs/rfc8941.html#integer.
func marshalInteger(b io.StringWriter, i int64) error {
if i < -999999999999999 || i > 999999999999999 {
return ErrNumberOutOfRange
Expand All @@ -35,7 +35,7 @@ func marshalInteger(b io.StringWriter, i int64) error {
}

// parseNumber parses as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#parse-number.
// https://httpwg.org/specs/rfc8941.html#parse-number.
func parseNumber(s *scanner) (interface{}, error) {
neg := isNeg(s)
if neg && s.eof() {
Expand Down
6 changes: 3 additions & 3 deletions item.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
)

// Item is a bare value and associated parameters.
// See https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#item.
// See https://httpwg.org/specs/rfc8941.html#item.
type Item struct {
Value interface{}
Params *Params
Expand All @@ -22,7 +22,7 @@ func (i Item) member() {
}

// marshalSFV serializes as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#ser-item.
// https://httpwg.org/specs/rfc8941.html#ser-item.
func (i Item) marshalSFV(b *strings.Builder) error {
if i.Value == nil {
return ErrInvalidBareItem
Expand All @@ -36,7 +36,7 @@ func (i Item) marshalSFV(b *strings.Builder) error {
}

// UnmarshalItem parses an item as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#parse-item.
// https://httpwg.org/specs/rfc8941.html#parse-item.
func UnmarshalItem(v []string) (Item, error) {
s := &scanner{
data: strings.Join(v, ","),
Expand Down
6 changes: 3 additions & 3 deletions key.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func isKeyChar(c byte) bool {
}

// checkKey checks if the given value is a valid parameter key according to
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#param.
// https://httpwg.org/specs/rfc8941.html#param.
func checkKey(k string) error {
if len(k) == 0 {
return fmt.Errorf("a key cannot be empty: %w", ErrInvalidKeyFormat)
Expand All @@ -44,7 +44,7 @@ func checkKey(k string) error {
}

// marshalKey serializes as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#ser-key.
// https://httpwg.org/specs/rfc8941.html#ser-key.
func marshalKey(b io.StringWriter, k string) error {
if err := checkKey(k); err != nil {
return err
Expand All @@ -56,7 +56,7 @@ func marshalKey(b io.StringWriter, k string) error {
}

// parseKey parses as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#parse-key.
// https://httpwg.org/specs/rfc8941.html#parse-key.
func parseKey(s *scanner) (string, error) {
if s.eof() {
return "", &UnmarshalError{s.off, ErrInvalidKeyFormat}
Expand Down
10 changes: 5 additions & 5 deletions list.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ var ErrInvalidListFormat = errors.New("invalid list format")

// List contains items an inner lists.
//
// See https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#list
// See https://httpwg.org/specs/rfc8941.html#list
type List []Member

// marshalSFV serializes as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#ser-list.
// https://httpwg.org/specs/rfc8941.html#ser-list.
func (l List) marshalSFV(b *strings.Builder) error {
s := len(l)
for i := 0; i < s; i++ {
Expand All @@ -33,7 +33,7 @@ func (l List) marshalSFV(b *strings.Builder) error {
}

// UnmarshalList parses a list as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#parse-list.
// https://httpwg.org/specs/rfc8941.html#parse-list.
func UnmarshalList(v []string) (List, error) {
s := &scanner{
data: strings.Join(v, ","),
Expand All @@ -50,7 +50,7 @@ func UnmarshalList(v []string) (List, error) {
}

// parseList parses as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#parse-list.
// https://httpwg.org/specs/rfc8941.html#parse-list.
func parseList(s *scanner) (List, error) {
var l List

Expand Down Expand Up @@ -85,7 +85,7 @@ func parseList(s *scanner) (List, error) {
}

// parseItemOrInnerList parses as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#parse-item-or-list.
// https://httpwg.org/specs/rfc8941.html#parse-item-or-list.
func parseItemOrInnerList(s *scanner) (Member, error) {
if s.eof() {
return nil, &UnmarshalError{s.off, ErrInvalidInnerListFormat}
Expand Down
2 changes: 1 addition & 1 deletion member.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package httpsfv

// Member is a marker interface for members of dictionaries and lists.
//
// See https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#list.
// See https://httpwg.org/specs/rfc8941.html#list.
type Member interface {
member()
marshaler
Expand Down
6 changes: 3 additions & 3 deletions params.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (

// Params are an ordered map of key-value pairs that are associated with an item or an inner list.
//
// See https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#param.
// See https://httpwg.org/specs/rfc8941.html#param.
type Params struct {
names []string
values map[string]interface{}
Expand Down Expand Up @@ -72,7 +72,7 @@ func (p *Params) Names() []string {
}

// marshalSFV serializes as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#ser-params.
// https://httpwg.org/specs/rfc8941.html#ser-params.
func (p *Params) marshalSFV(b *strings.Builder) error {
for _, k := range p.names {
if err := b.WriteByte(';'); err != nil {
Expand Down Expand Up @@ -101,7 +101,7 @@ func (p *Params) marshalSFV(b *strings.Builder) error {
}

// parseParams parses as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#parse-param.
// https://httpwg.org/specs/rfc8941.html#parse-param.
func parseParams(s *scanner) (*Params, error) {
p := NewParams()

Expand Down
4 changes: 2 additions & 2 deletions string.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
var ErrInvalidStringFormat = errors.New("invalid string format")

// marshalSFV serializes as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#ser-string.
// https://httpwg.org/specs/rfc8941.html#ser-string.
func marshalString(b io.ByteWriter, s string) error {
if err := b.WriteByte('"'); err != nil {
return err
Expand Down Expand Up @@ -42,7 +42,7 @@ func marshalString(b io.ByteWriter, s string) error {
}

// parseString parses as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#parse-string.
// https://httpwg.org/specs/rfc8941.html#parse-string.
func parseString(s *scanner) (string, error) {
if s.eof() || s.data[s.off] != '"' {
return "", &UnmarshalError{s.off, ErrInvalidStringFormat}
Expand Down
2 changes: 1 addition & 1 deletion structured-field-tests
6 changes: 3 additions & 3 deletions token.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ func isExtendedTchar(c byte) bool {
var ErrInvalidTokenFormat = errors.New("invalid token format")

// Token represents a token as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#token.
// https://httpwg.org/specs/rfc8941.html#token.
// A specific type is used to distinguish tokens from strings.
type Token string

// marshalSFV serializes as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#ser-token.
// https://httpwg.org/specs/rfc8941.html#ser-token.
func (t Token) marshalSFV(b io.StringWriter) error {
if len(t) == 0 {
return fmt.Errorf("a token cannot be empty: %w", ErrInvalidTokenFormat)
Expand All @@ -51,7 +51,7 @@ func (t Token) marshalSFV(b io.StringWriter) error {
}

// parseToken parses as defined in
// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html#parse-token.
// https://httpwg.org/specs/rfc8941.html#parse-token.
func parseToken(s *scanner) (Token, error) {
if s.eof() || (!isAlpha(s.data[s.off]) && s.data[s.off] != '*') {
return "", &UnmarshalError{s.off, ErrInvalidTokenFormat}
Expand Down