mirror of https://github.com/knative/docs.git
124 lines
3.5 KiB
Go
124 lines
3.5 KiB
Go
package tracestate
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
// ErrInvalidListMember occurs if at least one list member is invalid, e.g., contains an unexpected character.
|
|
ErrInvalidListMember = errors.New("tracecontext: Invalid tracestate list member")
|
|
// ErrDuplicateListMemberKey occurs if at least two list members contain the same vendor-tenant pair.
|
|
ErrDuplicateListMemberKey = errors.New("tracecontext: Duplicate list member key in tracestate")
|
|
// ErrTooManyListMembers occurs if the list contains more than the maximum number of members per the spec, i.e., 32.
|
|
ErrTooManyListMembers = errors.New("tracecontext: Too many list members in tracestate")
|
|
)
|
|
|
|
const (
|
|
maxMembers = 32
|
|
|
|
delimiter = ","
|
|
)
|
|
|
|
var (
|
|
re = regexp.MustCompile(`^\s*(?:([a-z0-9_\-*/]{1,241})@([a-z0-9_\-*/]{1,14})|([a-z0-9_\-*/]{1,256}))=([\x20-\x2b\x2d-\x3c\x3e-\x7e]*[\x21-\x2b\x2d-\x3c\x3e-\x7e])\s*$`)
|
|
)
|
|
|
|
// Member contains vendor-specific data that should be propagated across all new spans started within a given trace.
|
|
type Member struct {
|
|
// Vendor is a key representing a particular trace vendor.
|
|
Vendor string
|
|
// Tenant is a key used to distinguish between tenants of a multi-tenant trace vendor.
|
|
Tenant string
|
|
// Value is the particular data that the vendor intents to pass to child spans.
|
|
Value string
|
|
}
|
|
|
|
// String encodes a `Member` into a string formatted according to the W3C spec.
|
|
// The string may be invalid if any fields are invalid, e.g, the vendor contains a non-compliant character.
|
|
func (m Member) String() string {
|
|
if m.Tenant == "" {
|
|
return fmt.Sprintf("%s=%s", m.Vendor, m.Value)
|
|
}
|
|
return fmt.Sprintf("%s@%s=%s", m.Vendor, m.Tenant, m.Value)
|
|
}
|
|
|
|
// TraceState represents a list of `Member`s that should be propagated to new spans started in a trace.
|
|
type TraceState []Member
|
|
|
|
// String encodes all `Member`s of the `TraceState` into a single string, formatted according to the W3C spec.
|
|
// The string may be invalid if any `Member`s are invalid, e.g., containing a non-compliant character.
|
|
func (ts TraceState) String() string {
|
|
var members []string
|
|
for _, member := range ts {
|
|
members = append(members, member.String())
|
|
}
|
|
return strings.Join(members, ",")
|
|
}
|
|
|
|
// Parse attempts to decode a `TraceState` from a byte array.
|
|
// It returns an error if the byte array is invalid, e.g., it contains an incorrectly formatted list member.
|
|
func Parse(traceState []byte) (TraceState, error) {
|
|
return parse(string(traceState))
|
|
}
|
|
|
|
// ParseString attempts to decode a `TraceState` from a string.
|
|
// It returns an error if the string is invalid, e.g., it contains an incorrectly formatted list member.
|
|
func ParseString(traceState string) (TraceState, error) {
|
|
return parse(traceState)
|
|
}
|
|
|
|
func parse(traceState string) (ts TraceState, err error) {
|
|
found := make(map[string]interface{})
|
|
|
|
members := strings.Split(traceState, delimiter)
|
|
|
|
for _, member := range members {
|
|
if len(member) == 0 {
|
|
continue
|
|
}
|
|
|
|
var m Member
|
|
m, err = parseMember(member)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
key := fmt.Sprintf("%s%s", m.Vendor, m.Tenant)
|
|
if _, ok := found[key]; ok {
|
|
err = ErrDuplicateListMemberKey
|
|
return
|
|
}
|
|
found[key] = nil
|
|
|
|
ts = append(ts, m)
|
|
|
|
if len(ts) > maxMembers {
|
|
err = ErrTooManyListMembers
|
|
return
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func parseMember(s string) (Member, error) {
|
|
matches := re.FindStringSubmatch(s)
|
|
if len(matches) != 5 {
|
|
return Member{}, ErrInvalidListMember
|
|
}
|
|
|
|
vendor := matches[1]
|
|
if vendor == "" {
|
|
vendor = matches[3]
|
|
}
|
|
|
|
return Member{
|
|
Vendor: vendor,
|
|
Tenant: matches[2],
|
|
Value: matches[4],
|
|
}, nil
|
|
}
|