add vendor data
All checks were successful
Create and publish a Docker image 🚀 / build-and-push-image (push) Successful in 1m49s

This commit is contained in:
Smile Rex
2026-03-10 01:11:41 +03:00
parent d56b51065f
commit 6ace91a21a
962 changed files with 384706 additions and 2 deletions

3
vendor/github.com/dunglas/httpsfv/.gitmodules generated vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "structured-field-tests"]
path = structured-field-tests
url = https://github.com/httpwg/structured-field-tests

27
vendor/github.com/dunglas/httpsfv/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,27 @@
Copyright (c) 2020 Kévin Dunglas. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

25
vendor/github.com/dunglas/httpsfv/README.md generated vendored Normal file
View File

@@ -0,0 +1,25 @@
# 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 (RFC 9651 and 8941)](https://httpwg.org/specs/rfc9651.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)
[![Coverage Status](https://coveralls.io/repos/github/dunglas/httpsfv/badge.svg?branch=master)](https://coveralls.io/github/dunglas/httpsfv?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/dunglas/httpsfv)](https://goreportcard.com/report/github.com/dunglas/httpsfv)
## Features
* Fully implementing the RFC
* Compliant with [the official test suite](https://github.com/httpwg/structured-field-tests)
* Unit and fuzz tested
* Strongly-typed
* Fast (see [the benchmark](httpwg_test.go))
* No dependencies
## Docs
[Browse the documentation on Go.dev](https://pkg.go.dev/github.com/dunglas/httpsfv).
## Credits
Created by [Kévin Dunglas](https://dunglas.fr). Sponsored by [Les-Tilleuls.coop](https://les-tilleuls.coop).

115
vendor/github.com/dunglas/httpsfv/bareitem.go generated vendored Normal file
View File

@@ -0,0 +1,115 @@
package httpsfv
import (
"errors"
"fmt"
"reflect"
"strings"
"time"
)
// ErrInvalidBareItem is returned when a bare item is invalid.
var ErrInvalidBareItem = errors.New(
"invalid bare item type (allowed types are bool, string, int64, float64, []byte, time.Time and Token)",
)
// assertBareItem asserts that v is a valid bare item
// according to https://httpwg.org/specs/rfc9651.html#item.
//
// v can be either:
//
// * an integer (Section 3.3.1.)
// * a decimal (Section 3.3.2.)
// * a string (Section 3.3.3.)
// * a token (Section 3.3.4.)
// * a byte sequence (Section 3.3.5.)
// * a boolean (Section 3.3.6.)
// * a date (Section 3.3.7.)
// * a display string (Section 3.3.8.)
func assertBareItem(v interface{}) {
switch v.(type) {
case bool,
string,
int,
int8,
int16,
int32,
int64,
uint,
uint8,
uint16,
uint32,
uint64,
float32,
float64,
[]byte,
time.Time,
Token,
DisplayString:
return
default:
panic(fmt.Errorf("%w: got %s", ErrInvalidBareItem, reflect.TypeOf(v)))
}
}
// marshalBareItem serializes as defined in
// https://httpwg.org/specs/rfc9651.html#ser-bare-item.
func marshalBareItem(b *strings.Builder, v interface{}) error {
switch v := v.(type) {
case bool:
return marshalBoolean(b, v)
case string:
return marshalString(b, v)
case int64:
return marshalInteger(b, v)
case int, int8, int16, int32:
return marshalInteger(b, reflect.ValueOf(v).Int())
case uint, uint8, uint16, uint32, uint64:
// Casting an uint64 to an int64 is possible because the maximum allowed value is 999,999,999,999,999
return marshalInteger(b, int64(reflect.ValueOf(v).Uint()))
case float32, float64:
return marshalDecimal(b, v.(float64))
case []byte:
return marshalBinary(b, v)
case time.Time:
return marshalDate(b, v)
case Token:
return v.marshalSFV(b)
case DisplayString:
return v.marshalSFV(b)
default:
panic(ErrInvalidBareItem)
}
}
// parseBareItem parses as defined in
// https://httpwg.org/specs/rfc9651.html#parse-bare-item.
func parseBareItem(s *scanner) (interface{}, error) {
if s.eof() {
return nil, &UnmarshalError{s.off, ErrUnexpectedEndOfString}
}
c := s.data[s.off]
switch c {
case '"':
return parseString(s)
case '?':
return parseBoolean(s)
case '*':
return parseToken(s)
case ':':
return parseBinary(s)
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
return parseNumber(s)
case '@':
return parseDate(s)
case '%':
return parseDisplayString(s)
default:
if isAlpha(c) {
return parseToken(s)
}
return nil, &UnmarshalError{s.off, ErrUnrecognizedCharacter}
}
}

59
vendor/github.com/dunglas/httpsfv/binary.go generated vendored Normal file
View File

@@ -0,0 +1,59 @@
package httpsfv
import (
"encoding/base64"
"errors"
"strings"
)
// ErrInvalidBinaryFormat is returned when the binary format is invalid.
var ErrInvalidBinaryFormat = errors.New("invalid binary format")
// marshalBinary serializes as defined in
// https://httpwg.org/specs/rfc9651.html#ser-binary.
func marshalBinary(b *strings.Builder, bs []byte) error {
if err := b.WriteByte(':'); err != nil {
return err
}
buf := make([]byte, base64.StdEncoding.EncodedLen(len(bs)))
base64.StdEncoding.Encode(buf, bs)
if _, err := b.Write(buf); err != nil {
return err
}
return b.WriteByte(':')
}
// parseBinary parses as defined in
// https://httpwg.org/specs/rfc9651.html#parse-binary.
func parseBinary(s *scanner) ([]byte, error) {
if s.eof() || s.data[s.off] != ':' {
return nil, &UnmarshalError{s.off, ErrInvalidBinaryFormat}
}
s.off++
start := s.off
for !s.eof() {
c := s.data[s.off]
if c == ':' {
// base64decode
decoded, err := base64.StdEncoding.DecodeString(s.data[start:s.off])
if err != nil {
return nil, &UnmarshalError{s.off, err}
}
s.off++
return decoded, nil
}
if !isAlpha(c) && !isDigit(c) && c != '+' && c != '/' && c != '=' {
return nil, &UnmarshalError{s.off, ErrInvalidBinaryFormat}
}
s.off++
}
return nil, &UnmarshalError{s.off, ErrInvalidBinaryFormat}
}

49
vendor/github.com/dunglas/httpsfv/boolean.go generated vendored Normal file
View File

@@ -0,0 +1,49 @@
package httpsfv
import (
"errors"
"io"
)
// ErrInvalidBooleanFormat is returned when a boolean format is invalid.
var ErrInvalidBooleanFormat = errors.New("invalid boolean format")
// marshalBoolean serializes as defined in
// https://httpwg.org/specs/rfc9651.html#ser-boolean.
func marshalBoolean(bd io.StringWriter, b bool) error {
if b {
_, err := bd.WriteString("?1")
return err
}
_, err := bd.WriteString("?0")
return err
}
// parseBoolean parses as defined in
// https://httpwg.org/specs/rfc9651.html#parse-boolean.
func parseBoolean(s *scanner) (bool, error) {
if s.eof() || s.data[s.off] != '?' {
return false, &UnmarshalError{s.off, ErrInvalidBooleanFormat}
}
s.off++
if s.eof() {
return false, &UnmarshalError{s.off, ErrInvalidBooleanFormat}
}
switch s.data[s.off] {
case '0':
s.off++
return false, nil
case '1':
s.off++
return true, nil
}
return false, &UnmarshalError{s.off, ErrInvalidBooleanFormat}
}

41
vendor/github.com/dunglas/httpsfv/date.go generated vendored Normal file
View File

@@ -0,0 +1,41 @@
package httpsfv
import (
"errors"
"io"
"time"
)
var ErrInvalidDateFormat = errors.New("invalid date format")
// marshalDate serializes as defined in
// https://httpwg.org/specs/rfc9651.html#ser-date.
func marshalDate(b io.StringWriter, i time.Time) error {
_, err := b.WriteString("@")
if err != nil {
return err
}
return marshalInteger(b, i.Unix())
}
// parseDate parses as defined in
// https://httpwg.org/specs/rfc9651.html#parse-date.
func parseDate(s *scanner) (time.Time, error) {
if s.eof() || s.data[s.off] != '@' {
return time.Time{}, &UnmarshalError{s.off, ErrInvalidDateFormat}
}
s.off++
n, err := parseNumber(s)
if err != nil {
return time.Time{}, &UnmarshalError{s.off, ErrInvalidDateFormat}
}
i, ok := n.(int64)
if !ok {
return time.Time{}, &UnmarshalError{s.off, ErrInvalidDateFormat}
}
return time.Unix(i, 0), nil
}

64
vendor/github.com/dunglas/httpsfv/decimal.go generated vendored Normal file
View File

@@ -0,0 +1,64 @@
package httpsfv
import (
"errors"
"io"
"math"
"strconv"
"strings"
)
const maxDecDigit = 3
// ErrInvalidDecimal is returned when a decimal is invalid.
var ErrInvalidDecimal = errors.New("the integer portion is larger than 12 digits: invalid decimal")
// marshalDecimal serializes as defined in
// https://httpwg.org/specs/rfc9651.html#ser-decimal.
//
// TODO(dunglas): add support for decimal float type when one will be available
// (https://github.com/golang/go/issues/19787)
func marshalDecimal(b io.StringWriter, d float64) error {
const TH = 0.001
rounded := math.RoundToEven(d/TH) * TH
i, frac := math.Modf(rounded)
if i < -999999999999 || i > 999999999999 {
return ErrInvalidDecimal
}
if _, err := b.WriteString(strings.TrimRight(strconv.FormatFloat(rounded, 'f', 3, 64), "0")); err != nil {
return err
}
if frac == 0 {
_, err := b.WriteString("0")
return err
}
return nil
}
func parseDecimal(s *scanner, decSepOff int, str string, neg bool) (float64, error) {
if decSepOff == s.off-1 {
return 0, &UnmarshalError{s.off, ErrInvalidDecimalFormat}
}
if len(s.data[decSepOff+1:s.off]) > maxDecDigit {
return 0, &UnmarshalError{s.off, ErrNumberOutOfRange}
}
i, err := strconv.ParseFloat(str, 64)
if err != nil {
// Should never happen
return 0, &UnmarshalError{s.off, err}
}
if neg {
i = -i
}
return i, nil
}

63
vendor/github.com/dunglas/httpsfv/decode.go generated vendored Normal file
View File

@@ -0,0 +1,63 @@
package httpsfv
import (
"errors"
"fmt"
)
// ErrUnexpectedEndOfString is returned when the end of string is unexpected.
var ErrUnexpectedEndOfString = errors.New("unexpected end of string")
// ErrUnrecognizedCharacter is returned when an unrecognized character in encountered.
var ErrUnrecognizedCharacter = errors.New("unrecognized character")
// UnmarshalError contains the underlying parsing error and the position at which it occurred.
type UnmarshalError struct {
off int
err error
}
func (e *UnmarshalError) Error() string {
if e.err != nil {
return fmt.Sprintf("%s: character %d", e.err, e.off)
}
return fmt.Sprintf("unmarshal error: character %d", e.off)
}
func (e *UnmarshalError) Unwrap() error {
return e.err
}
type scanner struct {
data string
off int
}
// scanWhileSp consumes spaces.
func (s *scanner) scanWhileSp() {
for !s.eof() {
if s.data[s.off] != ' ' {
return
}
s.off++
}
}
// scanWhileOWS consumes optional white space (OWS) characters.
func (s *scanner) scanWhileOWS() {
for !s.eof() {
c := s.data[s.off]
if c != ' ' && c != '\t' {
return
}
s.off++
}
}
// eof returns true if the parser consumed all available characters.
func (s *scanner) eof() bool {
return s.off == len(s.data)
}

167
vendor/github.com/dunglas/httpsfv/dictionary.go generated vendored Normal file
View File

@@ -0,0 +1,167 @@
package httpsfv
import (
"errors"
"strings"
)
// Dictionary is an ordered map of name-value pairs.
// See https://httpwg.org/specs/rfc9651.html#dictionary
// Values can be:
// * Item (Section 3.3.)
// * Inner List (Section 3.1.1.)
type Dictionary struct {
names []string
values map[string]Member
}
// ErrInvalidDictionaryFormat is returned when a dictionary value is invalid.
var ErrInvalidDictionaryFormat = errors.New("invalid dictionary format")
// NewDictionary creates a new ordered map.
func NewDictionary() *Dictionary {
d := Dictionary{}
d.names = []string{}
d.values = map[string]Member{}
return &d
}
// Get retrieves a member.
func (d *Dictionary) Get(k string) (Member, bool) {
v, ok := d.values[k]
return v, ok
}
// Add appends a new member to the ordered list.
func (d *Dictionary) Add(k string, v Member) {
if _, exists := d.values[k]; !exists {
d.names = append(d.names, k)
}
d.values[k] = v
}
// Del removes a member from the ordered list.
func (d *Dictionary) Del(key string) bool {
if _, ok := d.values[key]; !ok {
return false
}
for i, k := range d.names {
if k == key {
d.names = append(d.names[:i], d.names[i+1:]...)
break
}
}
delete(d.values, key)
return true
}
// Names retrieves the list of member names in the appropriate order.
func (d *Dictionary) Names() []string {
return d.names
}
func (d *Dictionary) marshalSFV(b *strings.Builder) error {
last := len(d.names) - 1
for m, k := range d.names {
if err := marshalKey(b, k); err != nil {
return err
}
v := d.values[k]
if item, ok := v.(Item); ok && item.Value == true {
if err := item.Params.marshalSFV(b); err != nil {
return err
}
} else {
if err := b.WriteByte('='); err != nil {
return err
}
if err := v.marshalSFV(b); err != nil {
return err
}
}
if m != last {
if _, err := b.WriteString(", "); err != nil {
return err
}
}
}
return nil
}
// UnmarshalDictionary parses a dictionary as defined in
// https://httpwg.org/specs/rfc9651.html#parse-dictionary.
func UnmarshalDictionary(v []string) (*Dictionary, error) {
s := &scanner{
data: strings.Join(v, ","),
}
s.scanWhileSp()
sfv, err := parseDictionary(s)
if err != nil {
return sfv, err
}
return sfv, nil
}
func parseDictionary(s *scanner) (*Dictionary, error) {
d := NewDictionary()
for !s.eof() {
k, err := parseKey(s)
if err != nil {
return nil, err
}
var m Member
if !s.eof() && s.data[s.off] == '=' {
s.off++
m, err = parseItemOrInnerList(s)
if err != nil {
return nil, err
}
} else {
p, err := parseParams(s)
if err != nil {
return nil, err
}
m = Item{true, p}
}
d.Add(k, m)
s.scanWhileOWS()
if s.eof() {
return d, nil
}
if s.data[s.off] != ',' {
return nil, &UnmarshalError{s.off, ErrInvalidDictionaryFormat}
}
s.off++
s.scanWhileOWS()
if s.eof() {
// there is a trailing comma
return nil, &UnmarshalError{s.off, ErrInvalidDictionaryFormat}
}
}
return d, nil
}

105
vendor/github.com/dunglas/httpsfv/displaystring.go generated vendored Normal file
View File

@@ -0,0 +1,105 @@
package httpsfv
import (
"encoding/hex"
"errors"
"strings"
"unicode"
"unicode/utf8"
)
type DisplayString string
var ErrInvalidDisplayString = errors.New("invalid display string type")
var notVcharOrSp = &unicode.RangeTable{
R16: []unicode.Range16{
{0x0000, 0x001f, 1},
{0x007f, 0x00ff, 1},
},
LatinOffset: 2,
}
// marshalSFV serializes as defined in
// https://httpwg.org/specs/rfc9651.html#ser-string.
func (s DisplayString) marshalSFV(b *strings.Builder) error {
if _, err := b.WriteString(`%"`); err != nil {
return err
}
for i := 0; i < len(s); i++ {
if s[i] == '%' || s[i] == '"' || unicode.Is(notVcharOrSp, rune(s[i])) {
b.WriteRune('%')
b.WriteString(hex.EncodeToString([]byte{s[i]}))
continue
}
b.WriteByte(s[i])
}
b.WriteByte('"')
return nil
}
// parseDisplayString parses as defined in
// https://httpwg.org/specs/rfc9651.html#parse-display.
func parseDisplayString(s *scanner) (DisplayString, error) {
if s.eof() || len(s.data[s.off:]) < 2 || s.data[s.off:2] != `%"` {
return "", &UnmarshalError{s.off, ErrInvalidDisplayString}
}
s.off += 2
var b strings.Builder
for !s.eof() {
c := s.data[s.off]
s.off++
switch c {
case '%':
if len(s.data[s.off:]) < 2 {
return "", &UnmarshalError{s.off, ErrInvalidDisplayString}
}
c0 := unhex(s.data[s.off])
if c0 == 0 {
return "", &UnmarshalError{s.off, ErrInvalidDisplayString}
}
c1 := unhex(s.data[s.off+1])
if c1 == 0 {
return "", &UnmarshalError{s.off, ErrInvalidDisplayString}
}
b.WriteByte(c0<<4 | c1)
s.off += 2
case '"':
r := b.String()
if !utf8.ValidString(r) {
return "", ErrInvalidDisplayString
}
return DisplayString(r), nil
default:
if unicode.Is(notVcharOrSp, rune(c)) {
return "", &UnmarshalError{s.off, ErrInvalidDisplayString}
}
b.WriteByte(c)
}
}
return "", &UnmarshalError{s.off, ErrInvalidDisplayString}
}
func unhex(c byte) byte {
switch {
case '0' <= c && c <= '9':
return c - '0'
case 'a' <= c && c <= 'f':
return c - 'a' + 10
default:
return 0
}
}

42
vendor/github.com/dunglas/httpsfv/encode.go generated vendored Normal file
View File

@@ -0,0 +1,42 @@
// Package httpsfv implements serializing and parsing
// of Structured Field Values for HTTP as defined in RFC 9651.
//
// 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.
// Other values are stored in native types:
//
// int64, for integers
// float64, for decimals
// string, for strings
// byte[], for byte sequences
// bool, for booleans
//
// The specification is available at https://httpwg.org/specs/rfc9651.html.
package httpsfv
import (
"strings"
)
// marshaler is the interface implemented by types that can marshal themselves into valid SFV.
type marshaler interface {
marshalSFV(b *strings.Builder) error
}
// StructuredFieldValue represents a List, a Dictionary or an Item.
type StructuredFieldValue interface {
marshaler
}
// Marshal returns the HTTP Structured Value serialization of v
// as defined in https://httpwg.org/specs/rfc9651.html#text-serialize.
//
// v must be a List, a Dictionary, an Item or an InnerList.
func Marshal(v StructuredFieldValue) (string, error) {
var b strings.Builder
if err := v.marshalSFV(&b); err != nil {
return "", err
}
return b.String(), nil
}

91
vendor/github.com/dunglas/httpsfv/innerlist.go generated vendored Normal file
View File

@@ -0,0 +1,91 @@
package httpsfv
import (
"errors"
"strings"
)
// ErrInvalidInnerListFormat is returned when an inner list format is invalid.
var ErrInvalidInnerListFormat = errors.New("invalid inner list format")
// InnerList represents an inner list as defined in
// https://httpwg.org/specs/rfc9651.html#inner-list.
type InnerList struct {
Items []Item
Params *Params
}
func (il InnerList) member() {
}
// marshalSFV serializes as defined in
// https://httpwg.org/specs/rfc9651.html#ser-innerlist.
func (il InnerList) marshalSFV(b *strings.Builder) error {
if err := b.WriteByte('('); err != nil {
return err
}
l := len(il.Items)
for i := 0; i < l; i++ {
if err := il.Items[i].marshalSFV(b); err != nil {
return err
}
if i != l-1 {
if err := b.WriteByte(' '); err != nil {
return err
}
}
}
if err := b.WriteByte(')'); err != nil {
return err
}
return il.Params.marshalSFV(b)
}
// parseInnerList parses as defined in
// https://httpwg.org/specs/rfc9651.html#parse-item-or-list.
func parseInnerList(s *scanner) (InnerList, error) {
if s.eof() || s.data[s.off] != '(' {
return InnerList{}, &UnmarshalError{s.off, ErrInvalidInnerListFormat}
}
s.off++
il := InnerList{nil, nil}
for !s.eof() {
s.scanWhileSp()
if s.eof() {
return InnerList{}, &UnmarshalError{s.off, ErrInvalidInnerListFormat}
}
if s.data[s.off] == ')' {
s.off++
p, err := parseParams(s)
if err != nil {
return InnerList{}, err
}
il.Params = p
return il, nil
}
i, err := parseItem(s)
if err != nil {
return InnerList{}, err
}
if s.eof() || (s.data[s.off] != ')' && s.data[s.off] != ' ') {
return InnerList{}, &UnmarshalError{s.off, ErrInvalidInnerListFormat}
}
il.Items = append(il.Items, i)
}
return InnerList{}, &UnmarshalError{s.off, ErrInvalidInnerListFormat}
}

120
vendor/github.com/dunglas/httpsfv/integer.go generated vendored Normal file
View File

@@ -0,0 +1,120 @@
package httpsfv
import (
"errors"
"io"
"strconv"
)
const maxDigit = 12
// ErrNotDigit is returned when a character should be a digit but isn't.
var ErrNotDigit = errors.New("character is not a digit")
// ErrNumberOutOfRange is returned when the number is too large according to the specification.
var ErrNumberOutOfRange = errors.New("integer or decimal out of range")
// ErrInvalidDecimalFormat is returned when the decimal format is invalid.
var ErrInvalidDecimalFormat = errors.New("invalid decimal format")
const (
typeInteger = iota
typeDecimal
)
// marshalInteger serializes as defined in
// https://httpwg.org/specs/rfc9651.html#integer.
func marshalInteger(b io.StringWriter, i int64) error {
if i < -999999999999999 || i > 999999999999999 {
return ErrNumberOutOfRange
}
_, err := b.WriteString(strconv.FormatInt(i, 10))
return err
}
// parseNumber parses as defined in
// https://httpwg.org/specs/rfc9651.html#parse-number.
func parseNumber(s *scanner) (interface{}, error) {
neg := isNeg(s)
if neg && s.eof() {
return 0, &UnmarshalError{s.off, ErrUnexpectedEndOfString}
}
if !isDigit(s.data[s.off]) {
return 0, &UnmarshalError{s.off, ErrNotDigit}
}
start := s.off
s.off++
var (
decSepOff int
t = typeInteger
)
for s.off < len(s.data) {
size := s.off - start
if (t == typeInteger && (size >= 15)) || size >= 16 {
return 0, &UnmarshalError{s.off, ErrNumberOutOfRange}
}
c := s.data[s.off]
if isDigit(c) {
s.off++
continue
}
if t == typeInteger && c == '.' {
if size > maxDigit {
return 0, &UnmarshalError{s.off, ErrNumberOutOfRange}
}
t = typeDecimal
decSepOff = s.off
s.off++
continue
}
break
}
str := s.data[start:s.off]
if t == typeInteger {
return parseInteger(str, neg, s.off)
}
return parseDecimal(s, decSepOff, str, neg)
}
func isNeg(s *scanner) bool {
if s.data[s.off] == '-' {
s.off++
return true
}
return false
}
func parseInteger(str string, neg bool, off int) (int64, error) {
i, err := strconv.ParseInt(str, 10, 64)
if err != nil {
// Should never happen
return 0, &UnmarshalError{off, err}
}
if neg {
i = -i
}
if i < -999999999999999 || i > 999999999999999 {
return 0, &UnmarshalError{off, ErrNumberOutOfRange}
}
return i, err
}

73
vendor/github.com/dunglas/httpsfv/item.go generated vendored Normal file
View File

@@ -0,0 +1,73 @@
package httpsfv
import (
"strings"
)
// Item is a bare value and associated parameters.
// See https://httpwg.org/specs/rfc9651.html#item.
type Item struct {
Value interface{}
Params *Params
}
// NewItem returns a new Item.
func NewItem(v interface{}) Item {
assertBareItem(v)
return Item{v, NewParams()}
}
func (i Item) member() {
}
// marshalSFV serializes as defined in
// https://httpwg.org/specs/rfc9651.html#ser-item.
func (i Item) marshalSFV(b *strings.Builder) error {
if i.Value == nil {
return ErrInvalidBareItem
}
if err := marshalBareItem(b, i.Value); err != nil {
return err
}
return i.Params.marshalSFV(b)
}
// UnmarshalItem parses an item as defined in
// https://httpwg.org/specs/rfc9651.html#parse-item.
func UnmarshalItem(v []string) (Item, error) {
s := &scanner{
data: strings.Join(v, ","),
}
s.scanWhileSp()
sfv, err := parseItem(s)
if err != nil {
return Item{}, err
}
s.scanWhileSp()
if !s.eof() {
return Item{}, &UnmarshalError{off: s.off}
}
return sfv, nil
}
func parseItem(s *scanner) (Item, error) {
bi, err := parseBareItem(s)
if err != nil {
return Item{}, err
}
p, err := parseParams(s)
if err != nil {
return Item{}, err
}
return Item{bi, p}, nil
}

81
vendor/github.com/dunglas/httpsfv/key.go generated vendored Normal file
View File

@@ -0,0 +1,81 @@
package httpsfv
import (
"errors"
"fmt"
"io"
)
// ErrInvalidKeyFormat is returned when the format of a parameter or dictionary key is invalid.
var ErrInvalidKeyFormat = errors.New("invalid key format")
// isKeyChar checks if c is a valid key characters.
func isKeyChar(c byte) bool {
if isLowerCaseAlpha(c) || isDigit(c) {
return true
}
switch c {
case '_', '-', '.', '*':
return true
}
return false
}
// checkKey checks if the given value is a valid parameter key according to
// https://httpwg.org/specs/rfc9651.html#param.
func checkKey(k string) error {
if len(k) == 0 {
return fmt.Errorf("a key cannot be empty: %w", ErrInvalidKeyFormat)
}
if !isLowerCaseAlpha(k[0]) && k[0] != '*' {
return fmt.Errorf("a key must start with a lower case alpha character or *: %w", ErrInvalidKeyFormat)
}
for i := 1; i < len(k); i++ {
if !isKeyChar(k[i]) {
return fmt.Errorf("the character %c isn't allowed in a key: %w", k[i], ErrInvalidKeyFormat)
}
}
return nil
}
// marshalKey serializes as defined in
// https://httpwg.org/specs/rfc9651.html#ser-key.
func marshalKey(b io.StringWriter, k string) error {
if err := checkKey(k); err != nil {
return err
}
_, err := b.WriteString(k)
return err
}
// parseKey parses as defined in
// https://httpwg.org/specs/rfc9651.html#parse-key.
func parseKey(s *scanner) (string, error) {
if s.eof() {
return "", &UnmarshalError{s.off, ErrInvalidKeyFormat}
}
c := s.data[s.off]
if !isLowerCaseAlpha(c) && c != '*' {
return "", &UnmarshalError{s.off, ErrInvalidKeyFormat}
}
start := s.off
s.off++
for !s.eof() {
if !isKeyChar(s.data[s.off]) {
break
}
s.off++
}
return s.data[start:s.off], nil
}

99
vendor/github.com/dunglas/httpsfv/list.go generated vendored Normal file
View File

@@ -0,0 +1,99 @@
package httpsfv
import (
"errors"
"strings"
)
// ErrInvalidListFormat is returned when the format of a list is invalid.
var ErrInvalidListFormat = errors.New("invalid list format")
// List contains items an inner lists.
//
// See https://httpwg.org/specs/rfc9651.html#list
type List []Member
// marshalSFV serializes as defined in
// https://httpwg.org/specs/rfc9651.html#ser-list.
func (l List) marshalSFV(b *strings.Builder) error {
s := len(l)
for i := 0; i < s; i++ {
if err := l[i].marshalSFV(b); err != nil {
return err
}
if i != s-1 {
if _, err := b.WriteString(", "); err != nil {
return err
}
}
}
return nil
}
// UnmarshalList parses a list as defined in
// https://httpwg.org/specs/rfc9651.html#parse-list.
func UnmarshalList(v []string) (List, error) {
s := &scanner{
data: strings.Join(v, ","),
}
s.scanWhileSp()
sfv, err := parseList(s)
if err != nil {
return List{}, err
}
return sfv, nil
}
// parseList parses as defined in
// https://httpwg.org/specs/rfc9651.html#parse-list.
func parseList(s *scanner) (List, error) {
var l List
for !s.eof() {
m, err := parseItemOrInnerList(s)
if err != nil {
return nil, err
}
l = append(l, m)
s.scanWhileOWS()
if s.eof() {
return l, nil
}
if s.data[s.off] != ',' {
return nil, &UnmarshalError{s.off, ErrInvalidListFormat}
}
s.off++
s.scanWhileOWS()
if s.eof() {
// there is a trailing comma
return nil, &UnmarshalError{s.off, ErrInvalidListFormat}
}
}
return l, nil
}
// parseItemOrInnerList parses as defined in
// https://httpwg.org/specs/rfc9651.html#parse-item-or-list.
func parseItemOrInnerList(s *scanner) (Member, error) {
if s.eof() {
return nil, &UnmarshalError{s.off, ErrInvalidInnerListFormat}
}
if s.data[s.off] == '(' {
return parseInnerList(s)
}
return parseItem(s)
}

9
vendor/github.com/dunglas/httpsfv/member.go generated vendored Normal file
View File

@@ -0,0 +1,9 @@
package httpsfv
// Member is a marker interface for members of dictionaries and lists.
//
// See https://httpwg.org/specs/rfc9651.html#list.
type Member interface {
member()
marshaler
}

143
vendor/github.com/dunglas/httpsfv/params.go generated vendored Normal file
View File

@@ -0,0 +1,143 @@
package httpsfv
import (
"errors"
"strings"
)
// Params are an ordered map of key-value pairs that are associated with an item or an inner list.
//
// See https://httpwg.org/specs/rfc9651.html#param.
type Params struct {
names []string
values map[string]interface{}
}
// ErrInvalidParameterFormat is returned when the format of a parameter is invalid.
var ErrInvalidParameterFormat = errors.New("invalid parameter format")
// ErrInvalidParameterValue is returned when a parameter key is invalid.
var ErrInvalidParameterValue = errors.New("invalid parameter value")
// ErrMissingParameters is returned when the Params structure is missing from the element.
var ErrMissingParameters = errors.New("missing parameters")
// NewParams creates a new ordered map.
func NewParams() *Params {
p := Params{}
p.names = []string{}
p.values = map[string]interface{}{}
return &p
}
// Get retrieves a parameter.
func (p *Params) Get(k string) (interface{}, bool) {
v, ok := p.values[k]
return v, ok
}
// Add appends a new parameter to the ordered list.
// If the key already exists, overwrite its value.
func (p *Params) Add(k string, v interface{}) {
assertBareItem(v)
if _, exists := p.values[k]; !exists {
p.names = append(p.names, k)
}
p.values[k] = v
}
// Del removes a parameter from the ordered list.
func (p *Params) Del(key string) bool {
if _, ok := p.values[key]; !ok {
return false
}
for i, k := range p.names {
if k == key {
p.names = append(p.names[:i], p.names[i+1:]...)
break
}
}
delete(p.values, key)
return true
}
// Names retrieves the list of parameter names in the appropriate order.
func (p *Params) Names() []string {
return p.names
}
// marshalSFV serializes as defined in
// https://httpwg.org/specs/rfc9651.html#ser-params.
func (p *Params) marshalSFV(b *strings.Builder) error {
if p == nil {
return ErrMissingParameters
}
for _, k := range p.names {
if err := b.WriteByte(';'); err != nil {
return err
}
if err := marshalKey(b, k); err != nil {
return err
}
v := p.values[k]
if v == true {
continue
}
if err := b.WriteByte('='); err != nil {
return err
}
if err := marshalBareItem(b, v); err != nil {
return err
}
}
return nil
}
// parseParams parses as defined in
// https://httpwg.org/specs/rfc9651.html#parse-param.
func parseParams(s *scanner) (*Params, error) {
p := NewParams()
for !s.eof() {
if s.data[s.off] != ';' {
break
}
s.off++
s.scanWhileSp()
k, err := parseKey(s)
if err != nil {
return nil, err
}
var i interface{}
if !s.eof() && s.data[s.off] == '=' {
s.off++
i, err = parseBareItem(s)
if err != nil {
return nil, err
}
} else {
i = true
}
p.Add(k, i)
}
return p, nil
}

88
vendor/github.com/dunglas/httpsfv/string.go generated vendored Normal file
View File

@@ -0,0 +1,88 @@
package httpsfv
import (
"errors"
"strings"
"unicode"
)
// ErrInvalidStringFormat is returned when a string format is invalid.
var ErrInvalidStringFormat = errors.New("invalid string format")
// marshalString serializes as defined in
// https://httpwg.org/specs/rfc9651.html#ser-string.
func marshalString(b *strings.Builder, s string) error {
if err := b.WriteByte('"'); err != nil {
return err
}
for i := 0; i < len(s); i++ {
if s[i] <= '\u001F' || s[i] >= unicode.MaxASCII {
return ErrInvalidStringFormat
}
switch s[i] {
case '"', '\\':
if err := b.WriteByte('\\'); err != nil {
return err
}
}
if err := b.WriteByte(s[i]); err != nil {
return err
}
}
if err := b.WriteByte('"'); err != nil {
return err
}
return nil
}
// parseString parses as defined in
// https://httpwg.org/specs/rfc9651.html#parse-string.
func parseString(s *scanner) (string, error) {
if s.eof() || s.data[s.off] != '"' {
return "", &UnmarshalError{s.off, ErrInvalidStringFormat}
}
s.off++
var b strings.Builder
for !s.eof() {
c := s.data[s.off]
s.off++
switch c {
case '\\':
if s.eof() {
return "", &UnmarshalError{s.off, ErrInvalidStringFormat}
}
n := s.data[s.off]
if n != '"' && n != '\\' {
return "", &UnmarshalError{s.off, ErrInvalidStringFormat}
}
s.off++
if err := b.WriteByte(n); err != nil {
return "", err
}
continue
case '"':
return b.String(), nil
default:
if c <= '\u001F' || c >= unicode.MaxASCII {
return "", &UnmarshalError{s.off, ErrInvalidStringFormat}
}
if err := b.WriteByte(c); err != nil {
return "", err
}
}
}
return "", &UnmarshalError{s.off, ErrInvalidStringFormat}
}

71
vendor/github.com/dunglas/httpsfv/token.go generated vendored Normal file
View File

@@ -0,0 +1,71 @@
package httpsfv
import (
"errors"
"fmt"
"io"
)
// isExtendedTchar checks if c is a valid token character as defined in the spec.
func isExtendedTchar(c byte) bool {
if isAlpha(c) || isDigit(c) {
return true
}
switch c {
case '!', '#', '$', '%', '&', '\'', '*', '+', '-', '.', '^', '_', '`', '|', '~', ':', '/':
return true
}
return false
}
// ErrInvalidTokenFormat is returned when a token format is invalid.
var ErrInvalidTokenFormat = errors.New("invalid token format")
// Token represents a token as defined in
// https://httpwg.org/specs/rfc9651.html#token.
// A specific type is used to distinguish tokens from strings.
type Token string
// marshalSFV serializes as defined in
// https://httpwg.org/specs/rfc9651.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)
}
if !isAlpha(t[0]) && t[0] != '*' {
return fmt.Errorf("a token must start with an alpha character or *: %w", ErrInvalidTokenFormat)
}
for i := 1; i < len(t); i++ {
if !isExtendedTchar(t[i]) {
return fmt.Errorf("the character %c isn't allowed in a token: %w", t[i], ErrInvalidTokenFormat)
}
}
_, err := b.WriteString(string(t))
return err
}
// parseToken parses as defined in
// https://httpwg.org/specs/rfc9651.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}
}
start := s.off
s.off++
for !s.eof() {
if !isExtendedTchar(s.data[s.off]) {
break
}
s.off++
}
return Token(s.data[start:s.off]), nil
}

16
vendor/github.com/dunglas/httpsfv/utils.go generated vendored Normal file
View File

@@ -0,0 +1,16 @@
package httpsfv
// isLowerCaseAlpha checks if c is a lower cased alpha character.
func isLowerCaseAlpha(c byte) bool {
return 'a' <= c && c <= 'z'
}
// isAlpha checks if c is an alpha character.
func isAlpha(c byte) bool {
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
}
// isDigit checks if c is a digit.
func isDigit(c byte) bool {
return '0' <= c && c <= '9'
}