176 lines
3.1 KiB
Go
176 lines
3.1 KiB
Go
|
package eventstream
|
||
|
|
||
|
import (
|
||
|
"encoding/binary"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
)
|
||
|
|
||
|
// Headers are a collection of EventStream header values.
|
||
|
type Headers []Header
|
||
|
|
||
|
// Header is a single EventStream Key Value header pair.
|
||
|
type Header struct {
|
||
|
Name string
|
||
|
Value Value
|
||
|
}
|
||
|
|
||
|
// Set associates the name with a value. If the header name already exists in
|
||
|
// the Headers the value will be replaced with the new one.
|
||
|
func (hs *Headers) Set(name string, value Value) {
|
||
|
var i int
|
||
|
for ; i < len(*hs); i++ {
|
||
|
if (*hs)[i].Name == name {
|
||
|
(*hs)[i].Value = value
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*hs = append(*hs, Header{
|
||
|
Name: name, Value: value,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// Get returns the Value associated with the header. Nil is returned if the
|
||
|
// value does not exist.
|
||
|
func (hs Headers) Get(name string) Value {
|
||
|
for i := 0; i < len(hs); i++ {
|
||
|
if h := hs[i]; h.Name == name {
|
||
|
return h.Value
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Del deletes the value in the Headers if it exists.
|
||
|
func (hs *Headers) Del(name string) {
|
||
|
for i := 0; i < len(*hs); i++ {
|
||
|
if (*hs)[i].Name == name {
|
||
|
copy((*hs)[i:], (*hs)[i+1:])
|
||
|
(*hs) = (*hs)[:len(*hs)-1]
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Clone returns a deep copy of the headers
|
||
|
func (hs Headers) Clone() Headers {
|
||
|
o := make(Headers, 0, len(hs))
|
||
|
for _, h := range hs {
|
||
|
o.Set(h.Name, h.Value)
|
||
|
}
|
||
|
return o
|
||
|
}
|
||
|
|
||
|
func decodeHeaders(r io.Reader) (Headers, error) {
|
||
|
hs := Headers{}
|
||
|
|
||
|
for {
|
||
|
name, err := decodeHeaderName(r)
|
||
|
if err != nil {
|
||
|
if err == io.EOF {
|
||
|
// EOF while getting header name means no more headers
|
||
|
break
|
||
|
}
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
value, err := decodeHeaderValue(r)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
hs.Set(name, value)
|
||
|
}
|
||
|
|
||
|
return hs, nil
|
||
|
}
|
||
|
|
||
|
func decodeHeaderName(r io.Reader) (string, error) {
|
||
|
var n headerName
|
||
|
|
||
|
var err error
|
||
|
n.Len, err = decodeUint8(r)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
name := n.Name[:n.Len]
|
||
|
if _, err := io.ReadFull(r, name); err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
return string(name), nil
|
||
|
}
|
||
|
|
||
|
func decodeHeaderValue(r io.Reader) (Value, error) {
|
||
|
var raw rawValue
|
||
|
|
||
|
typ, err := decodeUint8(r)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
raw.Type = valueType(typ)
|
||
|
|
||
|
var v Value
|
||
|
|
||
|
switch raw.Type {
|
||
|
case trueValueType:
|
||
|
v = BoolValue(true)
|
||
|
case falseValueType:
|
||
|
v = BoolValue(false)
|
||
|
case int8ValueType:
|
||
|
var tv Int8Value
|
||
|
err = tv.decode(r)
|
||
|
v = tv
|
||
|
case int16ValueType:
|
||
|
var tv Int16Value
|
||
|
err = tv.decode(r)
|
||
|
v = tv
|
||
|
case int32ValueType:
|
||
|
var tv Int32Value
|
||
|
err = tv.decode(r)
|
||
|
v = tv
|
||
|
case int64ValueType:
|
||
|
var tv Int64Value
|
||
|
err = tv.decode(r)
|
||
|
v = tv
|
||
|
case bytesValueType:
|
||
|
var tv BytesValue
|
||
|
err = tv.decode(r)
|
||
|
v = tv
|
||
|
case stringValueType:
|
||
|
var tv StringValue
|
||
|
err = tv.decode(r)
|
||
|
v = tv
|
||
|
case timestampValueType:
|
||
|
var tv TimestampValue
|
||
|
err = tv.decode(r)
|
||
|
v = tv
|
||
|
case uuidValueType:
|
||
|
var tv UUIDValue
|
||
|
err = tv.decode(r)
|
||
|
v = tv
|
||
|
default:
|
||
|
panic(fmt.Sprintf("unknown value type %d", raw.Type))
|
||
|
}
|
||
|
|
||
|
// Error could be EOF, let caller deal with it
|
||
|
return v, err
|
||
|
}
|
||
|
|
||
|
const maxHeaderNameLen = 255
|
||
|
|
||
|
type headerName struct {
|
||
|
Len uint8
|
||
|
Name [maxHeaderNameLen]byte
|
||
|
}
|
||
|
|
||
|
func (v headerName) encode(w io.Writer) error {
|
||
|
if err := binary.Write(w, binary.BigEndian, v.Len); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
_, err := w.Write(v.Name[:v.Len])
|
||
|
return err
|
||
|
}
|