You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
subgraph-oz/ipc/message.go

121 lines
2.3 KiB

package ipc
import (
"encoding/json"
"errors"
"fmt"
"reflect"
"syscall"
)
func NewMsgFactory(msgTypes ...interface{}) MsgFactory {
mf := (MsgFactory)(make(map[string]func() interface{}))
for _, mt := range msgTypes {
if err := mf.register(mt); err != nil {
defaultLog.Fatalf("failed adding (%T) in NewMsgFactory: %v", mt, err)
return nil
}
}
return mf
}
type MsgFactory map[string](func() interface{})
func (mf MsgFactory) create(msgType string) (interface{}, error) {
f, ok := mf[msgType]
if !ok {
return nil, fmt.Errorf("cannot create msg type: %s", msgType)
}
return f(), nil
}
func (mf MsgFactory) register(mt interface{}) error {
t := reflect.TypeOf(mt)
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
if t.Kind() != reflect.Struct {
return errors.New("not a structure")
}
if t.NumField() == 0 || len(t.Field(0).Tag) == 0 {
return errors.New("no tag on first field of structure")
}
tag := string(t.Field(0).Tag)
mf[tag] = func() interface{} {
v := reflect.New(t)
return v.Interface()
}
return nil
}
type Message struct {
Type string
MsgID int
Body interface{}
Ucred *syscall.Ucred
Fds []int
mconn *MsgConn
}
type BaseMsg struct {
Type string
MsgID int
IsResponse bool
Body json.RawMessage
}
func (mc *MsgConn) parseMessage(data []byte) (*Message, error) {
var base BaseMsg
if err := json.Unmarshal(data, &base); err != nil {
return nil, err
}
body, err := mc.factory.create(base.Type)
if err != nil {
return nil, err
}
if err := json.Unmarshal(base.Body, body); err != nil {
return nil, err
}
m := new(Message)
m.Type = base.Type
m.MsgID = base.MsgID
m.Body = body
return m, nil
}
func (m *Message) Free() {
for _, fd := range m.Fds {
syscall.Close(fd)
}
m.Fds = nil
}
func (m *Message) parseControlData(data []byte) error {
cmsgs, err := syscall.ParseSocketControlMessage(data)
if err != nil {
return err
}
for _, cmsg := range cmsgs {
switch cmsg.Header.Type {
case syscall.SCM_CREDENTIALS:
cred, err := syscall.ParseUnixCredentials(&cmsg)
if err != nil {
return err
}
m.Ucred = cred
case syscall.SCM_RIGHTS:
fds, err := syscall.ParseUnixRights(&cmsg)
if err != nil {
return err
}
m.Fds = fds
}
}
return nil
}
func (m *Message) Respond(msg interface{}, fds ...int) error {
return m.mconn.sendMessage(msg, m.MsgID, fds...)
}