func ParseTraceparentHeader()

in module/apmhttp/traceheaders.go [81:144]


func ParseTraceparentHeader(h string) (apm.TraceContext, error) {
	var out apm.TraceContext
	if len(h) < 3 || h[2] != '-' {
		return out, errors.Errorf("invalid traceparent header %q", h)
	}
	var version byte
	if !strings.HasPrefix(h, "00") {
		decoded, err := hex.DecodeString(h[:2])
		if err != nil {
			return out, errors.Wrap(err, "error decoding traceparent header version")
		}
		version = decoded[0]
	}
	h = h[3:]

	switch version {
	case 255:
		// "Version 255 is invalid."
		return out, errors.Errorf("traceparent header version 255 is forbidden")
	default:
		// "If higher version is detected - implementation SHOULD try to parse it."
		fallthrough
	case 0:
		// Version 00:
		//
		//     version-format   = trace-id "-" span-id "-" trace-options
		//     trace-id         = 32HEXDIG
		//     span-id          = 16HEXDIG
		//     trace-options    = 2HEXDIG
		const (
			traceIDEnd        = 32
			spanIDStart       = traceIDEnd + 1
			spanIDEnd         = spanIDStart + 16
			traceOptionsStart = spanIDEnd + 1
			traceOptionsEnd   = traceOptionsStart + 2
		)
		switch {
		case len(h) < traceOptionsEnd,
			h[traceIDEnd] != '-',
			h[spanIDEnd] != '-',
			version == 0 && len(h) != traceOptionsEnd,
			version > 0 && len(h) > traceOptionsEnd && h[traceOptionsEnd] != '-':
			return out, errors.Errorf("invalid version %d traceparent header %q", version, h)
		}
		if _, err := hex.Decode(out.Trace[:], []byte(h[:traceIDEnd])); err != nil {
			return out, errors.Wrapf(err, "error decoding trace-id for version %d", version)
		}
		if err := out.Trace.Validate(); err != nil {
			return out, errors.Wrap(err, "invalid trace-id")
		}
		if _, err := hex.Decode(out.Span[:], []byte(h[spanIDStart:spanIDEnd])); err != nil {
			return out, errors.Wrapf(err, "error decoding span-id for version %d", version)
		}
		if err := out.Span.Validate(); err != nil {
			return out, errors.Wrap(err, "invalid span-id")
		}
		var traceOptions [1]byte
		if _, err := hex.Decode(traceOptions[:], []byte(h[traceOptionsStart:traceOptionsEnd])); err != nil {
			return out, errors.Wrapf(err, "error decoding trace-options for version %d", version)
		}
		out.Options = apm.TraceOptions(traceOptions[0])
		return out, nil
	}
}