srop

package module
v0.0.0-...-c571f1a Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 27, 2020 License: MIT Imports: 8 Imported by: 0

README

SROP

Simple remote object protocol. Think smalltalk, erlang and 9p, not CORBA or SOAP.

Explicit goals of the protocol are:

  • Simplicity
  • Security
  • Flexibility

The protocol is based on message passing where clients send arbitrary messages to objects on a server and the objects are able to reply with arbitrary messages. Specific applications may define how the objects it serves will respond to different application specific messages. There is a small set of messages the reference implementation defines that other implementations are encouraged to reuse.

Applications may define their own message types by simply selecting a unique 64 bit identifier for that message and defining the message format. There is no need for complex protocol negotiation, objects simply respond to messages they understand. If an object recieves a message it does not understand, it can reply with an 'UnknownMessage' response.

The allocation of new objects and their corresponding id on the server is an application detail. For example a message sent to the root object may trigger the allocation of a new object. Communicating new ids to the client is an application detail, but would generally be communicated via an application specific response message.

Object ids could be considered analagous to file descriptors in an operating system, where having an object id associated with a session implies permission to send that object messages. This means SROP makes capability based security quite simple to implement.

Example

Running the example
$ go run example/server/main.go 
  listening on 127.0.0.1:4444
  RootObject got a message: &example.MakeGreeter{Name:"bob"}
  I just greated a greeter with id: 1
  greeter 1 got a message: &example.Hello{From:"client"}
  client just said hello to me, saying hello back in one second.
  greeter 1 got a message: &srop.Clunk{}
  destroying myself.
  Greeter with id 1 clunked.
  RootObject clunked.
$ go run ./example/client/main.go
  Creating a new greeter named bob by contacting the bootstrap object...
  Saying hello to our new greeter...
  Got a reply from: bob
  destroying the greeter...
  closing the connection...
Talking to the example Greeter server from go

  client := srop.NewClient(c, srop.ClientOptions{})

  // The server root object responds to MakeGreeter messages by returning us a new object handle.
  reply, _ := client.Send(srop.BOOTSTRAP_OBJECT_ID, &example.MakeGreeter{Name: "bob"})

  greeterId := reply.(*srop.ObjectRef).Id

  // Sending hello to our newly created remote greeter results in a reply hello message.
  reply, _ = client.Send(greeterId, &example.Hello{From: "client"})

  fmt.Printf("greetings from: %s" reply.(*example.Hello).From)

  client.Close()

Specification

here

Implementations

  • Go - The reference implementation in this repository.

Documentation

Index

Constants

View Source
const (
	// From spec
	TYPE_ERR = 0x81aba3f7522edc6b
	// From spec
	TYPE_OK = 0xd4924862b91c639d
	// From spec
	TYPE_CLUNK = 0xcf3a50d623ee637d
	// From spec
	TYPE_OBJECT_REF = 0xd782cf4b395eca05
	// From spec
	TYPE_OBJECT_NOT_EXIST = 0xab0547366de885bc
	// From spec
	TYPE_UNEXPECTED_MESSAGE = 0xd47d4e94917934b2
	// From spec
	BOOTSTRAP_OBJECT_ID = 0
)

Variables

View Source
var (
	ErrBadResponse      = errors.New("bad response.")
	ErrPayloadTooBig    = errors.New("payload too big.")
	ErrRequestCancelled = errors.New("request cancelled.")
	ErrClientShutdown   = errors.New("client has been shutdown.")
)

Functions

func RegisterMessage

func RegisterMessage(id uint64, mk func() Message)

func RegisterStandardMessagesAndErrors

func RegisterStandardMessagesAndErrors(reg *Registry)

func WriteRequest

func WriteRequest(w io.Writer, req Request) error

func WriteResponse

func WriteResponse(w io.Writer, resp Response) error

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

func NewClient

func NewClient(conn io.ReadWriteCloser, options ClientOptions) *Client

func (*Client) Close

func (c *Client) Close()

func (*Client) RawSend

func (c *Client) RawSend(oid uint64, paramType uint64, paramData []byte) (Response, error)

func (*Client) RawSendCtx

func (c *Client) RawSendCtx(ctx context.Context, oid uint64, paramType uint64, paramData []byte) (Response, error)

func (*Client) RawSendParsedReply

func (c *Client) RawSendParsedReply(reg *Registry, oid uint64, paramType uint64, paramData []byte) (interface{}, error)

func (*Client) RawSendParsedReplyCtx

func (c *Client) RawSendParsedReplyCtx(ctx context.Context, reg *Registry, oid uint64, paramType uint64, paramData []byte) (interface{}, error)

func (*Client) Send

func (c *Client) Send(oid uint64, arg Message) (interface{}, error)

func (*Client) SendCtx

func (c *Client) SendCtx(ctx context.Context, oid uint64, arg Message) (interface{}, error)

func (*Client) SendWithReg

func (c *Client) SendWithReg(reg *Registry, oid uint64, arg Message) (interface{}, error)

func (*Client) SendWithRegCtx

func (c *Client) SendWithRegCtx(ctx context.Context, reg *Registry, oid uint64, arg Message) (interface{}, error)

type ClientOptions

type ClientOptions struct {
	MaxResponseSize uint64
	// If nil, defaults to DefaultRegistry
	Registry *Registry
}

type Clunk

type Clunk struct{}

func (*Clunk) SropMarshal

func (m *Clunk) SropMarshal() []byte

func (*Clunk) SropType

func (m *Clunk) SropType() uint64

func (*Clunk) SropUnmarshal

func (m *Clunk) SropUnmarshal(buf []byte) bool

type ConnServer

type ConnServer struct {
	// contains filtered or unexported fields
}

func NewConnServer

func NewConnServer(c io.ReadWriteCloser, options ConnServerOptions) *ConnServer

func (*ConnServer) Clunk

func (s *ConnServer) Clunk(oid uint64)

func (*ConnServer) Go

func (s *ConnServer) Go(f func())

func (*ConnServer) HandleRequest

func (s *ConnServer) HandleRequest(ctx context.Context, r Request, respond RespondFunc)

func (*ConnServer) Register

func (s *ConnServer) Register(o Object) uint64

func (*ConnServer) RegisterBootstrap

func (s *ConnServer) RegisterBootstrap(o Object)

func (*ConnServer) Serve

func (s *ConnServer) Serve(ctx context.Context, c io.ReadWriteCloser)

func (*ConnServer) Wait

func (s *ConnServer) Wait()

type ConnServerOptions

type ConnServerOptions struct {
	MaxRequestSize uint64
	// Each connection will stop reading new
	// requests if this is exceeded, zero
	// means unlimited.
	MaxOutstandingRequests uint64
	// This function is used to create the root object
	// a client can send messages to.
	//
	// It takes a connection object as a way for out of band connection
	// information to be used while creating the root object.
	// One example is using a unix socket to get the remote user id,
	// and then using that for authentication.
	BootstrapFunc func(io.ReadWriteCloser) Object
	// If nil, defaults to DefaultRegistry
	Registry *Registry
}

type Message

type Message interface {
	SropType() uint64
	SropMarshal() []byte
	// The buffer is guaranteed to be read only.
	// This means zero copy references into the
	// buffer are okay (and encouraged).
	SropUnmarshal([]byte) bool
}

type Object

type Object interface {
	Message(context.Context, *ConnServer, Message, RespondFunc)
	UnknownMessage(context.Context, *ConnServer, uint64, []byte, RespondFunc)
	Clunk(*ConnServer)
}

type ObjectNotExist

type ObjectNotExist struct{}

func (*ObjectNotExist) SropMarshal

func (m *ObjectNotExist) SropMarshal() []byte

func (*ObjectNotExist) SropType

func (m *ObjectNotExist) SropType() uint64

func (*ObjectNotExist) SropUnmarshal

func (m *ObjectNotExist) SropUnmarshal(buf []byte) bool

type ObjectRef

type ObjectRef struct {
	Id uint64
}

func (*ObjectRef) SropMarshal

func (m *ObjectRef) SropMarshal() []byte

func (*ObjectRef) SropType

func (m *ObjectRef) SropType() uint64

func (*ObjectRef) SropUnmarshal

func (m *ObjectRef) SropUnmarshal(buf []byte) bool

type Ok

type Ok struct{}

func (*Ok) SropMarshal

func (m *Ok) SropMarshal() []byte

func (*Ok) SropType

func (m *Ok) SropType() uint64

func (*Ok) SropUnmarshal

func (m *Ok) SropUnmarshal(buf []byte) bool

type Registry

type Registry struct {
	// contains filtered or unexported fields
}
var DefaultRegistry *Registry

func NewRegistry

func NewRegistry() *Registry

func (*Registry) RegisterMessage

func (reg *Registry) RegisterMessage(id uint64, mk func() Message)

func (*Registry) Unmarshal

func (reg *Registry) Unmarshal(id uint64, data []byte) (Message, bool)

type Request

type Request struct {
	RequestId   uint64
	ObjectId    uint64
	MessageType uint64
	// Modifying this buffer is an error.
	MessageData []byte
}

func ReadRequest

func ReadRequest(r io.Reader, maxRequestSize uint64) (Request, error)

type RespondFunc

type RespondFunc func(Message)

type Response

type Response struct {
	RequestId    uint64
	ResponseType uint64
	// Modifying this buffer is an error.
	ResponseData []byte
}

func ReadResponse

func ReadResponse(r io.Reader, maxResponseSize uint64) (Response, error)

type Server

type Server struct {
	// contains filtered or unexported fields
}

func NewServer

func NewServer(options ServerOptions) *Server

func (*Server) Close

func (s *Server) Close()

func (*Server) GoHandle

func (s *Server) GoHandle(c io.ReadWriteCloser)

func (*Server) Serve

func (s *Server) Serve(l net.Listener) error

func (*Server) Wait

func (s *Server) Wait()

type ServerOptions

type ServerOptions struct {
	ConnOptions ConnServerOptions
}

type UnexpectedMessage

type UnexpectedMessage struct{}

func (*UnexpectedMessage) SropMarshal

func (m *UnexpectedMessage) SropMarshal() []byte

func (*UnexpectedMessage) SropType

func (m *UnexpectedMessage) SropType() uint64

func (*UnexpectedMessage) SropUnmarshal

func (m *UnexpectedMessage) SropUnmarshal(buf []byte) bool

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL