Export Property members so getters are no longer required and made amf.GetProp a method on Object.

This commit is contained in:
scruzin 2019-01-12 16:10:09 +10:30
parent 396c809424
commit 2105339657
3 changed files with 113 additions and 93 deletions

View File

@ -70,27 +70,28 @@ const (
// AMF represents an AMF message (object), which is simply a collection of properties. // AMF represents an AMF message (object), which is simply a collection of properties.
type Object struct { type Object struct {
Props []Property // ToDo: consider not exporting this Props []Property
} }
// Property represents an AMF property. // Property represents an AMF property.
type Property struct { type Property struct {
name string Name string
dtype uint8 Type uint8
number float64 Number float64
str string String string
obj Object Object Object
} }
// AMF errors. // AMF errors.
var ( var (
ErrShortBuffer = errors.New("amf: short buffer") ErrShortBuffer = errors.New("amf: short buffer")
ErrEndOfBuffer = errors.New("amf: end of buffer") ErrEndOfBuffer = errors.New("amf: end of buffer")
ErrInvalidType = errors.New("amf: invalid type") ErrInvalidType = errors.New("amf: invalid type")
ErrUnimplemented = errors.New("amf: unimplemented feature") ErrUnimplemented = errors.New("amf: unimplemented feature")
ErrDecodingName = errors.New("amf: name decoding error") ErrDecodingName = errors.New("amf: name decoding error")
ErrDecodingString = errors.New("amf: string decoding error") ErrDecodingString = errors.New("amf: string decoding error")
ErrUnexpectedEnd = errors.New("amf: unexpected end") ErrUnexpectedEnd = errors.New("amf: unexpected end")
ErrPropertyNotFound = errors.New("amf: property not found")
) )
// DecodeInt16 decodes a 16-bit integer. // DecodeInt16 decodes a 16-bit integer.
@ -259,56 +260,31 @@ func EncodeNamedBoolean(buf []byte, key string, val bool) ([]byte, error) {
return EncodeBoolean(buf[len(key):], val) return EncodeBoolean(buf[len(key):], val)
} }
// Property functions.
func PropSetName(prop *Property, name string) {
prop.name = name
}
func PropGetNumber(prop *Property) float64 {
return prop.number
}
func PropGetString(prop *Property) string {
if prop.dtype == typeString {
return prop.str
}
return ""
}
func PropGetObject(prop *Property, obj *Object) {
if prop.dtype == TypeObject {
*obj = prop.obj
} else {
*obj = Object{}
}
}
// PropEncode encodes a property. // PropEncode encodes a property.
func PropEncode(p *Property, buf []byte) ([]byte, error) { func PropEncode(p *Property, buf []byte) ([]byte, error) {
if p.dtype == typeInvalid { if p.Type == typeInvalid {
return nil, ErrShortBuffer return nil, ErrShortBuffer
} }
if p.dtype != TypeNull && len(p.name)+2+1 >= len(buf) { if p.Type != TypeNull && len(p.Name)+2+1 >= len(buf) {
return nil, ErrShortBuffer return nil, ErrShortBuffer
} }
if p.dtype != TypeNull && len(p.name) != 0 { if p.Type != TypeNull && len(p.Name) != 0 {
binary.BigEndian.PutUint16(buf[:2], uint16(len(p.name))) binary.BigEndian.PutUint16(buf[:2], uint16(len(p.Name)))
buf = buf[2:] buf = buf[2:]
copy(buf, p.name) copy(buf, p.Name)
buf = buf[len(p.name):] buf = buf[len(p.Name):]
} }
var err error var err error
switch p.dtype { switch p.Type {
case typeNumber: case typeNumber:
buf, err = EncodeNumber(buf, p.number) buf, err = EncodeNumber(buf, p.Number)
case typeBoolean: case typeBoolean:
buf, err = EncodeBoolean(buf, p.number != 0) buf, err = EncodeBoolean(buf, p.Number != 0)
case typeString: case typeString:
buf, err = EncodeString(buf, p.str) buf, err = EncodeString(buf, p.String)
case TypeNull: case TypeNull:
if len(buf) < 2 { if len(buf) < 2 {
return nil, ErrShortBuffer return nil, ErrShortBuffer
@ -316,11 +292,11 @@ func PropEncode(p *Property, buf []byte) ([]byte, error) {
buf[0] = TypeNull buf[0] = TypeNull
buf = buf[1:] buf = buf[1:]
case TypeObject: case TypeObject:
buf, err = Encode(&p.obj, buf) buf, err = Encode(&p.Object, buf)
case typeEcmaArray: case typeEcmaArray:
buf, err = EncodeEcmaArray(&p.obj, buf) buf, err = EncodeEcmaArray(&p.Object, buf)
case typeStrictArray: case typeStrictArray:
buf, err = EncodeArray(&p.obj, buf) buf, err = EncodeArray(&p.Object, buf)
default: default:
buf, err = nil, ErrInvalidType buf, err = nil, ErrInvalidType
} }
@ -329,8 +305,6 @@ func PropEncode(p *Property, buf []byte) ([]byte, error) {
// PropDecode decodes a property, returning the number of bytes consumed from the supplied buffer. // PropDecode decodes a property, returning the number of bytes consumed from the supplied buffer.
func PropDecode(prop *Property, buf []byte, decodeName bool) (int, error) { func PropDecode(prop *Property, buf []byte, decodeName bool) (int, error) {
prop.name = ""
sz := len(buf) sz := len(buf)
if len(buf) == 0 { if len(buf) == 0 {
return 0, ErrEndOfBuffer return 0, ErrEndOfBuffer
@ -345,23 +319,25 @@ func PropDecode(prop *Property, buf []byte, decodeName bool) (int, error) {
return 0, ErrDecodingName return 0, ErrDecodingName
} }
prop.name = DecodeString(buf) prop.Name = DecodeString(buf)
buf = buf[2+n:] buf = buf[2+n:]
} else {
prop.Name = ""
} }
if len(buf) == 0 { if len(buf) == 0 {
return 0, ErrEndOfBuffer return 0, ErrEndOfBuffer
} }
prop.dtype = uint8(buf[0]) prop.Type = uint8(buf[0])
buf = buf[1:] buf = buf[1:]
switch prop.dtype { switch prop.Type {
case typeNumber: case typeNumber:
if len(buf) < 8 { if len(buf) < 8 {
return 0, ErrShortBuffer return 0, ErrShortBuffer
} }
prop.number = DecodeNumber(buf[:8]) prop.Number = DecodeNumber(buf[:8])
buf = buf[8:] buf = buf[8:]
case typeBoolean: case typeBoolean:
@ -372,23 +348,22 @@ func PropDecode(prop *Property, buf []byte, decodeName bool) (int, error) {
if len(buf) < int(n+2) { if len(buf) < int(n+2) {
return 0, ErrShortBuffer return 0, ErrShortBuffer
} }
prop.str = DecodeString(buf) prop.String = DecodeString(buf)
buf = buf[2+n:] buf = buf[2+n:]
case TypeObject: case TypeObject:
n, err := Decode(&prop.obj, buf, true) n, err := Decode(&prop.Object, buf, true)
if err != nil { if err != nil {
return 0, err return 0, err
} }
buf = buf[n:] buf = buf[n:]
case TypeNull, typeUndefined, typeUnsupported: case TypeNull, typeUndefined, typeUnsupported:
prop.dtype = TypeNull prop.Type = TypeNull
case typeEcmaArray: case typeEcmaArray:
buf = buf[4:] buf = buf[4:]
n, err := Decode(&prop.obj, buf, true) n, err := Decode(&prop.Object, buf, true)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -482,7 +457,7 @@ func Decode(obj *Object, buf []byte, decodeName bool) (int, error) {
for len(buf) != 0 { for len(buf) != 0 {
if len(buf) >= 3 && DecodeInt24(buf[:3]) == TypeObjectEnd { if len(buf) >= 3 && DecodeInt24(buf[:3]) == TypeObjectEnd {
buf = buf[3:] buf = buf[3:]
break break
} }
var prop Property var prop Property
@ -497,19 +472,19 @@ func Decode(obj *Object, buf []byte, decodeName bool) (int, error) {
return sz - len(buf), nil return sz - len(buf), nil
} }
// GetProp returns an object's property, either by its index when idx is non-negative, or by its name when name otherwise. // GetProp returns an object's property, either by its index when idx is non-negative, or by its name otherwise.
// If the matching property is not found nil is returned. // If the requested property is not found an ErrPropertyNotFound error is returned.
func GetProp(obj *Object, name string, idx int) *Property { func (obj *Object)GetProp(name string, idx int) (*Property, error) {
if idx >= 0 { if idx >= 0 {
if idx < len(obj.Props) { if idx < len(obj.Props) {
return &obj.Props[idx] return &obj.Props[idx], nil
} }
} else { } else {
for i, p := range obj.Props { for i, p := range obj.Props {
if p.name == name { if p.Name == name {
return &obj.Props[i] return &obj.Props[i], nil
} }
} }
} }
return nil return nil, ErrPropertyNotFound
} }

View File

@ -88,7 +88,7 @@ func TestNumbers(t *testing.T) {
} }
dn := int32(DecodeInt24(buf)) dn := int32(DecodeInt24(buf))
if n != dn { if n != dn {
t.Errorf("DecodeInt24 did not produce original number, got %v", dn) t.Errorf("DecodeInt24 did not produce original Number, got %v", dn)
} }
} }
} }
@ -97,13 +97,13 @@ func TestNumbers(t *testing.T) {
func TestProperties(t *testing.T) { func TestProperties(t *testing.T) {
var buf [1024]byte var buf [1024]byte
// Encode/decode number properties. // Encode/decode Number properties.
enc := buf[:] enc := buf[:]
var err error var err error
for i, _ := range testNumbers { for i, _ := range testNumbers {
enc, err = PropEncode(&Property{dtype: typeNumber, number: float64(testNumbers[i])}, enc) enc, err = PropEncode(&Property{Type: typeNumber, Number: float64(testNumbers[i])}, enc)
if err != nil { if err != nil {
t.Errorf("PropEncode of number failed") t.Errorf("PropEncode of Number failed")
} }
} }
@ -113,10 +113,10 @@ func TestProperties(t *testing.T) {
for i, _ := range testNumbers { for i, _ := range testNumbers {
n, err := PropDecode(&prop, dec, false) n, err := PropDecode(&prop, dec, false)
if err != nil { if err != nil {
t.Errorf("PropDecode of number failed") t.Errorf("PropDecode of Number failed")
} }
if int32(prop.number) != testNumbers[i] { if int32(prop.Number) != testNumbers[i] {
t.Errorf("PropEncode/PropDecode returned wrong number; got %v, expected %v", int32(prop.number), testNumbers[i]) t.Errorf("PropEncode/PropDecode returned wrong Number; got %v, expected %v", int32(prop.Number), testNumbers[i])
} }
dec = dec[n:] dec = dec[n:]
} }
@ -124,7 +124,7 @@ func TestProperties(t *testing.T) {
// Encode/decode string properties. // Encode/decode string properties.
enc = buf[:] enc = buf[:]
for i, _ := range testStrings { for i, _ := range testStrings {
enc, err = PropEncode(&Property{dtype: typeString, str: testStrings[i]}, enc) enc, err = PropEncode(&Property{Type: typeString, String: testStrings[i]}, enc)
if err != nil { if err != nil {
t.Errorf("PropEncode of string failed") t.Errorf("PropEncode of string failed")
} }
@ -137,8 +137,8 @@ func TestProperties(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("PropDecode of string failed") t.Errorf("PropDecode of string failed")
} }
if prop.str != testStrings[i] { if prop.String != testStrings[i] {
t.Errorf("PropEncode/PropDecode returned wrong string; got %s, expected %s", prop.str, testStrings[i]) t.Errorf("PropEncode/PropDecode returned wrong string; got %s, expected %s", prop.String, testStrings[i])
} }
dec = dec[n:] dec = dec[n:]
} }
@ -149,8 +149,8 @@ func TestProperties(t *testing.T) {
func TestObject(t *testing.T) { func TestObject(t *testing.T) {
var buf [1024]byte var buf [1024]byte
// Construct a simple object that has one property, the number 42. // Construct a simple object that has one property, the Number 42.
prop1 := Property{dtype: typeNumber, number: 42} prop1 := Property{Type: typeNumber, Number: 42}
obj1 := Object{} obj1 := Object{}
obj1.Props = append(obj1.Props, prop1) obj1.Props = append(obj1.Props, prop1)
@ -171,7 +171,7 @@ func TestObject(t *testing.T) {
} }
num := DecodeNumber(buf[2:10]) num := DecodeNumber(buf[2:10])
if num != 42 { if num != 42 {
t.Errorf("Encoded wrong number") t.Errorf("Encoded wrong Number")
} }
end := int32(DecodeInt24(buf[10:13])) end := int32(DecodeInt24(buf[10:13]))
if end != TypeObjectEnd { if end != TypeObjectEnd {
@ -189,8 +189,8 @@ func TestObject(t *testing.T) {
// Construct a more complicated object. // Construct a more complicated object.
var obj2 Object var obj2 Object
for i, _ := range testStrings { for i, _ := range testStrings {
obj2.Props = append(obj2.Props, Property{dtype: typeString, str: testStrings[i]}) obj2.Props = append(obj2.Props, Property{Type: typeString, String: testStrings[i]})
obj2.Props = append(obj2.Props, Property{dtype: typeNumber, number: float64(testNumbers[i])}) obj2.Props = append(obj2.Props, Property{Type: typeNumber, Number: float64(testNumbers[i])})
} }
// Encode it. // Encode it.
@ -207,4 +207,26 @@ func TestObject(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("Decode of object failed") t.Errorf("Decode of object failed")
} }
// Find some properties that exist.
prop, err := obj2.GetProp("", 2)
if err != nil {
t.Errorf("GetProp(2) failed")
}
if prop.String != "foo" {
t.Errorf("GetProp(2) returned wrong Property")
}
prop, err = obj2.GetProp("", 3)
if err != nil {
t.Errorf("GetProp(1) failed")
}
if prop.Number != 1 {
t.Errorf("GetProp(1) returned wrong Property")
}
// Try to find one that doesn't exist.
prop, err = obj2.GetProp("", 10)
if err != ErrPropertyNotFound {
t.Errorf("GetProp(10) failed")
}
} }

View File

@ -686,10 +686,18 @@ func handleInvoke(s *Session, body []byte) error {
return err return err
} }
meth := amf.PropGetString(amf.GetProp(&obj, "", 0)) prop, err := obj.GetProp("", 0)
s.log(DebugLevel, pkg+"invoking method "+meth) if err != nil {
txn := amf.PropGetNumber(amf.GetProp(&obj, "", 1)) return err
}
meth := prop.String
prop, err = obj.GetProp("", 1)
if err != nil {
return err
}
txn := prop.Number
s.log(DebugLevel, pkg+"invoking method "+meth)
switch meth { switch meth {
case av_result: case av_result:
var methodInvoked string var methodInvoked string
@ -728,7 +736,11 @@ func handleInvoke(s *Session, body []byte) error {
} }
case avCreatestream: case avCreatestream:
s.streamID = int32(amf.PropGetNumber(amf.GetProp(&obj, "", 3))) prop, err = obj.GetProp("", 3)
if err != nil {
return err
}
s.streamID = int32(prop.Number)
if s.link.protocol&featureWrite == 0 { if s.link.protocol&featureWrite == 0 {
return errNotWritable return errNotWritable
} }
@ -765,10 +777,21 @@ func handleInvoke(s *Session, body []byte) error {
s.log(FatalLevel, pkg+"unsupported method avClose") s.log(FatalLevel, pkg+"unsupported method avClose")
case avOnStatus: case avOnStatus:
var obj2 amf.Object prop, err = obj.GetProp("", 3)
amf.PropGetObject(amf.GetProp(&obj, "", 3), &obj2) if err != nil {
code := amf.PropGetString(amf.GetProp(&obj2, avCode, -1)) return err
level := amf.PropGetString(amf.GetProp(&obj2, avLevel, -1)) }
obj2 := prop.Object
prop, err = obj2.GetProp(avCode, -1)
if err != nil {
return err
}
code := prop.String
prop, err = obj2.GetProp(avLevel, -1)
if err != nil {
return err
}
level := prop.String
s.log(DebugLevel, pkg+"onStatus", "code", code, "level", level) s.log(DebugLevel, pkg+"onStatus", "code", code, "level", level)
switch code { switch code {