2023-04-30 19:49:18 +00:00
|
|
|
package gotelem
|
|
|
|
|
2023-05-01 14:49:47 +00:00
|
|
|
import (
|
|
|
|
"net"
|
2023-04-30 19:49:18 +00:00
|
|
|
|
2023-05-01 14:49:47 +00:00
|
|
|
"github.com/tinylib/msgp/msgp"
|
|
|
|
)
|
2023-04-30 19:49:18 +00:00
|
|
|
|
|
|
|
|
2023-05-01 14:49:47 +00:00
|
|
|
// the target architecture is a subscribe function that
|
|
|
|
// takes a can FILTER. Then the server will emit notifications.
|
|
|
|
// that contain new can packets as they come in.
|
|
|
|
|
|
|
|
// this means that the client should be able to handle
|
|
|
|
// notify packets on top of response packets.
|
|
|
|
|
|
|
|
// we should register handlers. They should handle serialization
|
|
|
|
// and deserialization on their own. This way we avoid reflect.
|
|
|
|
// since reflected code can be more complex under the hood.
|
|
|
|
|
|
|
|
// ServiceFunc is a RPC service handler.
|
|
|
|
type ServiceFunc func(params msgp.Raw) (res msgp.MarshalSizer, err error)
|
|
|
|
|
|
|
|
|
|
|
|
// RPCConn is a single RPC communication pair.
|
|
|
|
type RPCConn struct {
|
|
|
|
// TODO: use io.readwritecloser?
|
|
|
|
conn net.Conn
|
|
|
|
handlers map[string]ServiceFunc
|
2023-04-30 19:49:18 +00:00
|
|
|
|
2023-05-01 14:49:47 +00:00
|
|
|
// indicates what messages we've used.
|
|
|
|
// TODO: use a channel to return a response?
|
|
|
|
// TODO: lock with mutex
|
|
|
|
ct map[uint32]struct{}
|
2023-04-30 19:49:18 +00:00
|
|
|
}
|
|
|
|
|
2023-05-01 14:49:47 +00:00
|
|
|
// Call intiates an RPC call to a remote method and returns the
|
|
|
|
// response, or the error, if any.
|
|
|
|
// TODO: determine signature
|
|
|
|
// TODO: this should block?
|
|
|
|
func (rpc *RPCConn) Call(method string, params msgp.Marshaler) {
|
|
|
|
|
2023-04-30 19:49:18 +00:00
|
|
|
}
|
|
|
|
|
2023-05-01 14:49:47 +00:00
|
|
|
// Notify initiates a notification to a remote method. It does not
|
|
|
|
// return any information. There is no response from the server.
|
|
|
|
// This method will not block. An error is returned if there is a local
|
|
|
|
// problem.
|
|
|
|
func (rpc *RPCConn) Notify(method string, params msgp.Marshaler) {
|
|
|
|
// TODO: return an error if there's a local problem?
|
|
|
|
|
2023-04-30 19:49:18 +00:00
|
|
|
}
|
|
|
|
|
2023-05-01 14:49:47 +00:00
|
|
|
// Register a new handler to be called by the remote side. An error
|
|
|
|
// is returned if the handler name is already in use.
|
|
|
|
func (rpc *RPCConn) RegisterHandler(name string, fn ServiceFunc) error {
|
|
|
|
// TODO: check if name in use.
|
|
|
|
// TODO: mutex lock for sync (or use sync.map?
|
|
|
|
rpc.handlers[name] = fn
|
|
|
|
|
|
|
|
return nil
|
2023-04-30 19:49:18 +00:00
|
|
|
}
|
|
|
|
|
2023-05-01 14:49:47 +00:00
|
|
|
// Serve runs the server. It will dispatch goroutines to handle each
|
|
|
|
// method call. This can (and should in most cases) be run in the background to allow for
|
|
|
|
// sending and receving on the same connection.
|
|
|
|
func (rpc *RPCConn) Serve() {
|
2023-04-30 19:49:18 +00:00
|
|
|
|
2023-05-01 14:49:47 +00:00
|
|
|
// construct a stream reader.
|
|
|
|
msgReader := msgp.NewReader(rpc.conn)
|
|
|
|
|
|
|
|
// read a request/notification from the connection.
|
|
|
|
|
|
|
|
var rawmsg msgp.Raw = make(msgp.Raw, 0, 4)
|
|
|
|
|
|
|
|
rawmsg.DecodeMsg(msgReader)
|
|
|
|
|
|
|
|
rpcIntf, err := parseRPC(rawmsg)
|
|
|
|
|
|
|
|
switch rpcObject := rpcIntf.(type) {
|
|
|
|
case Request:
|
|
|
|
// the object is a request - we must dispatch a goroutine
|
|
|
|
// that will call the handler and also send a return value.
|
|
|
|
go rpc.dispatch(rpcObject)
|
|
|
|
case Notification:
|
|
|
|
go rpc.dispatchNotif(rpcObject)
|
|
|
|
case Response:
|
|
|
|
// TODO: return response to caller.
|
2023-04-30 19:49:18 +00:00
|
|
|
}
|
2023-05-01 14:49:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (rpc *RPCConn) dispatch(req Request) {
|
2023-04-30 19:49:18 +00:00
|
|
|
|
2023-05-01 14:49:47 +00:00
|
|
|
result, err := rpc.handlers[req.Method](req.Params)
|
2023-04-30 19:49:18 +00:00
|
|
|
|
|
|
|
if err != nil {
|
2023-05-01 14:49:47 +00:00
|
|
|
// log the error.
|
2023-04-30 19:49:18 +00:00
|
|
|
}
|
2023-05-01 14:49:47 +00:00
|
|
|
// construct the response frame.
|
|
|
|
var rpcE *RPCError = MakeRPCError(err)
|
2023-04-30 19:49:18 +00:00
|
|
|
|
2023-05-01 14:49:47 +00:00
|
|
|
w := msgp.NewWriter(rpc.conn)
|
|
|
|
resBuf := make(msgp.Raw, result.Msgsize())
|
|
|
|
|
|
|
|
result.MarshalMsg(resBuf)
|
2023-04-30 19:49:18 +00:00
|
|
|
|
2023-05-01 14:49:47 +00:00
|
|
|
response := NewResponse(req.MsgId, *rpcE, resBuf)
|
2023-04-30 19:49:18 +00:00
|
|
|
|
2023-05-01 14:49:47 +00:00
|
|
|
response.EncodeMsg(w)
|
|
|
|
|
|
|
|
}
|
|
|
|
func (rpc *RPCConn) dispatchNotif(req Notification) {
|
2023-04-30 19:49:18 +00:00
|
|
|
|
2023-05-01 14:49:47 +00:00
|
|
|
_, err := rpc.handlers[req.Method](req.Params)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
// log the error.
|
2023-04-30 19:49:18 +00:00
|
|
|
}
|
2023-05-01 14:49:47 +00:00
|
|
|
// no need for response.
|
2023-04-30 19:49:18 +00:00
|
|
|
}
|