proto

package
v0.0.0-...-64ea551 Latest Latest
Warning

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

Go to latest
Published: Feb 19, 2020 License: BSD-3-Clause, MIT Imports: 11 Imported by: 0

Documentation

Overview

Package proto defines the building blocks for API defined protocols with HBI

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ClosedChan

func ClosedChan() chan struct{}

ClosedChan returns a non-nil channnel guaranteed to have been closed.

func NewConnection

func NewConnection(wire HBIWire, env *HostingEnv) (*PostingEnd, *HostingEnd, error)

NewConnection creates the posting & hosting endpoints from a transport wire with a hosting environment

func Repr

func Repr(val interface{}) string

Repr converts a value object to its textual representation, that can be used to reconstruct the object by Anko (https://github.com/mattn/anko), or an HBI HostingEnv implemented in other programming languages / runtimes.

The syntax is very much JSON like, with `[]interface{}` maps to JSON array, and `map[interface{}]interface{}` maps to JSON object. But note it's not JSON compatible when a non-string key is present.

Despite those few special types, the representation of a value is majorly obtained via `Sprintf("%#v", v)`, which can be customized by overriding `Format(fmt.State, rune)` method of its type, like the example shows. For desired result:

fmt.Printf("(%6s) %s\n", "Repr", hbi.Repr(msg))
fmt.Printf("(%6s) %#v\n", "Repr", msg)
fmt.Printf("(%6s) %+v\n", "Long", msg)
fmt.Printf("(%6s) %v\n", "Short", msg)
fmt.Printf("(%6s) %s\n", "String", msg)

// Output:
// (  Repr) Msg("Compl","Hello, HBI world!",1557998919)
// (  Repr) Msg("Compl","Hello, HBI world!",1557998919)
// (  Long) [May 16 17:28:39+08] @Compl: Hello, HBI world!
// ( Short) @Compl: Hello, HBI world!
// (String) Msg<@Compl

Implement the `Format(fmt.State, rune)` method like this:

func (msg *Msg) Format(s fmt.State, verb rune) {
	switch verb {
	case 's': // string form
		io.WriteString(s, "Msg<@")
		io.WriteString(s, msg.From)
	case 'v':
		if s.Flag('#') { // repr form
			io.WriteString(s, "Msg(")
			io.WriteString(s, fmt.Sprintf("%#v", msg.From))
			io.WriteString(s, ",")
			io.WriteString(s, fmt.Sprintf("%#v", msg.Content))
			io.WriteString(s, ",")
			io.WriteString(s, fmt.Sprintf("%d", msg.Time.Unix()))
			io.WriteString(s, ")")
		} else { // value form
			if s.Flag('+') {
				io.WriteString(s, "[")
				io.WriteString(s, msg.Time.Format("Jan 02 15:04:05Z07"))
				io.WriteString(s, "] ")
			}
			io.WriteString(s, "@")
			io.WriteString(s, msg.From)
			io.WriteString(s, ": ")
			io.WriteString(s, msg.Content)
		}
	}
}

See: https://docs.python.org/3/library/functions.html#repr and https://docs.python.org/3/reference/datamodel.html#object.__repr__ for a similar construct in Python.

Expand the `Example` section below to see full source.

Types

type CancellableContext

type CancellableContext interface {
	// be a context.Context
	context.Context

	// allow explicit cancel and query of cancelled state
	Cancel(err error)
	Cancelled() bool
}

CancellableContext defines an interface compatible with `context.Context`, and its cancelled status can be queried without blocking wait.

This comes at a cost of an extra `sync.RWMutex` to synchronize the querying and setting of the state.

func NewCancellableContext

func NewCancellableContext() CancellableContext

NewCancellableContext creates a new context that is cancellable.

type CleanupMagicFunction

type CleanupMagicFunction = func(po *PostingEnd, ho *HostingEnd, discReason string)

CleanupMagicFunction is the prototype for magic functions to be called on HBI wire disconnected.

Such a function should be exposed with name `__hbi_cleanup__` from the `HostingEnv`.

note: this interface should really be defined in `he` package, but that'll create cyclic imports between `proto` and `he`.

type HBIC

type HBIC struct {
	// embed a cancellable context
	CancellableContext
	// contains filtered or unexported fields
}

HBIC is designed to interface with HBI wire protocol implementations, HBI applications should not use HBIC directly.

func (*HBIC) Cancel

func (hbic *HBIC) Cancel(err error)

func (*HBIC) Close

func (hbic *HBIC) Close()

func (*HBIC) Disconnect

func (hbic *HBIC) Disconnect(errReason string, trySendPeerError bool)

func (*HBIC) Ho

func (hbic *HBIC) Ho() *HostingEnd

func (*HBIC) NetIdent

func (hbic *HBIC) NetIdent() string

func (*HBIC) Po

func (hbic *HBIC) Po() *PostingEnd

func (*HBIC) Wire

func (hbic *HBIC) Wire() HBIWire

type HBIWire

type HBIWire interface {
	NetIdent() string
	LocalAddr() string
	RemoteAddr() string

	SendPacket(payload, wireDir string) (n int64, err error)
	SendData(d []byte) (n int64, err error)
	SendStream(ds func() ([]byte, error)) (n int64, err error)

	RecvPacket() (packet *Packet, err error)
	RecvData(d []byte) (n int64, err error)
	RecvStream(ds func() ([]byte, error)) (n int64, err error)

	Disconnect()
}

HBIWire is the abstract interface an HBI wire should implement

type HoCo

type HoCo struct {
	// hosting env to use for this conversation, if not nil
	HE *HostingEnv
	// contains filtered or unexported fields
}

HoCo is the passive, hosting conversation.

A HoCo is triggered by a PoCo from peer's posting endpoint, it is automatically available to application, obtained by calling HostingEnd.Co()

func (*HoCo) Close

func (co *HoCo) Close() error

Close closes this hosting conversation, neither send nor recv operation can be performed with a closed hosting conversation.

Note this can only be called from the dedicated hosting goroutine, i.e. from functions exposed to the hosting environment and called by the peer-scripting-code from the remote posting conversation which triggered this ho co.

func (*HoCo) CoSeq

func (co *HoCo) CoSeq() string

CoSeq returns the sequence number of this conversation.

The sequence of a hosting conversation is always the same as the peer's posting conversation that triggered it.

func (*HoCo) FinishRecv

func (co *HoCo) FinishRecv() error

FinishRecv transits this hosting conversation from `recv` to `work` stage.

As soon as all recv operations done, if some time-consuming work should be carried out to prepare the response to be sent back, a hosting conversation should transit to `work` stage by calling `FinishRecv()`; a hosting conversion should be closed directly, if nothing is supposed to be sent back.

Note this can only be called from the dedicated hosting goroutine, i.e. from functions exposed to the hosting environment and called by the peer-scripting-code from the remote posting conversation which triggered this ho co.

func (*HoCo) RecvData

func (co *HoCo) RecvData(d []byte) error

RecvData receives the binary data/stream sent by calling PoCo.SendData() or PoCo.SendStream() with the remote posting conversation which triggered this ho co.

Note this can only be called in `recv` stage, and from the dedicated hosting goroutine, i.e. from functions exposed to the hosting environment and called by the peer-scripting-code from the remote posting conversation which triggered this ho co.

func (*HoCo) RecvObj

func (co *HoCo) RecvObj() (interface{}, error)

RecvObj returns the landed result of a piece of peer-scripting-code sent by calling PoCo.SendObj() with the remote posting conversation which triggered this ho co.

Note this can only be called in `recv` stage, and from the dedicated hosting goroutine, i.e. from functions exposed to the hosting environment and called by the peer-scripting-code from the remote posting conversation which triggered this ho co.

func (*HoCo) RecvStream

func (co *HoCo) RecvStream(ds func() ([]byte, error)) error

RecvStream receives the binary data/stream sent by calling PoCo.SendData() or PoCo.SendStream() with the remote posting conversation which triggered this ho co.

Note this can only be called in `recv` stage, and from the dedicated hosting goroutine, i.e. from functions exposed to the hosting environment and called by the peer-scripting-code from the remote posting conversation which triggered this ho co.

func (*HoCo) SendCode

func (co *HoCo) SendCode(code string) error

SendCode sends `code` as back-script to peer's hosting endpoint for landing by its hosting environment. Only side effects are expected from landing of `code` at peer site.

Note this can only be called in `send` stage, and from the dedicated hosting goroutine, i.e. from functions exposed to the hosting environment and called by the peer-scripting-code from the remote posting conversation which triggered this ho co.

func (*HoCo) SendData

func (co *HoCo) SendData(d []byte) error

SendData sends a single chunk of binary data to peer site, to be received with the remote posting conversation which triggered this ho co, by calling PoCo.RecvData() or PoCo.RecvStream()

Note this can only be called in `send` stage, and from the dedicated hosting goroutine, i.e. from functions exposed to the hosting environment and called by the peer-scripting-code from the remote posting conversation which triggered this ho co.

func (*HoCo) SendObj

func (co *HoCo) SendObj(code string) error

SendObj sends `code` to peer's hosting endpoint for landing by its hosting environment, and the landed value to be received by calling PoCo.RecvObj() with the remote posting conversation which triggered this ho co.

Note this can only be called in `send` stage, and from the dedicated hosting goroutine, i.e. from functions exposed to the hosting environment and called by the peer-scripting-code from the remote posting conversation which triggered this ho co.

func (*HoCo) SendStream

func (co *HoCo) SendStream(ds func() ([]byte, error)) error

SendStream polls callback function `ds()` until it returns a nil []byte or non-nil error, and send each chunk to peer site in order to be received with the remote posting conversation which triggered this ho co, by calling PoCo.RecvData() or PoCo.RecvStream()

`ds()` will be called each time after the chunk returned from the previous call has been sent out.

Note this can only be called in `send` stage, and from the dedicated hosting goroutine, i.e. from functions exposed to the hosting environment and called by the peer-scripting-code from the remote posting conversation which triggered this ho co.

func (*HoCo) StartSend

func (co *HoCo) StartSend() error

StartSend transits this hosting conversation from `recv` or `work` stage to `send` stage.

As soon as all recv operations done, if no time-consuming work needs to be carried out to prepare the response to be sent back, a hosting conversation should transit to `send` stage by calling `StartSend()`; a hosting conversion should be closed directly, if nothing is supposed to be sent back.

As soon as no further back-script and/or data/stream is to be sent with a hosting conversation, it should close to release the underlying HBI transport wire for the next posting conversation to start off or next send-ready hosting conversation to start sending.

Note this can only be called from the dedicated hosting goroutine, i.e. from functions exposed to the hosting environment and called by the peer-scripting-code from the remote posting conversation which triggered this ho co.

type HostingEnd

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

HostingEnd is the application programming interface of a hosting endpoint.

func (*HostingEnd) Close

func (ho *HostingEnd) Close()

Close disconnects the underlying wire of the HBI connection.

func (*HostingEnd) Co

func (ho *HostingEnd) Co() *HoCo

Co returns the current hosting conversation in `recv` stage.

func (*HostingEnd) Context

func (ho *HostingEnd) Context() context.Context

Context returns the conttext associated with the underlying HBI connection.

func (*HostingEnd) Disconnect

func (ho *HostingEnd) Disconnect(errReason string, trySendPeerError bool)

Disconnect disconnects the underlying wire of the HBI connection, optionally with a error message sent to peer site for information purpose.

func (*HostingEnd) Disconnected

func (ho *HostingEnd) Disconnected() bool

Disconnected tells whether the underlying HBI connection has been disconnected.

func (*HostingEnd) Env

func (ho *HostingEnd) Env() *HostingEnv

Env returns the hosting environment that this hosting endpoint is attached to.

func (*HostingEnd) LocalAddr

func (ho *HostingEnd) LocalAddr() string

LocalAddr returns the local network address of the underlying wire.

func (*HostingEnd) NetIdent

func (ho *HostingEnd) NetIdent() string

NetIdent returns the network identification string of the underlying wire.

type HostingEnv

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

HostingEnv is the container of HBI artifacts, including:

  • functions
  • object constructors (special functions taking n args, returning 1 object)
  • reactor methods
  • value objects

These artifacts need to be explicitly exposed to a hosting environment, to accomodate landing of `peer-scripting-code` from the other end.

func NewHostingEnv

func NewHostingEnv() *HostingEnv

NewHostingEnv creates a new, empty hosting environment.

func (*HostingEnv) AnkoEnv

func (he *HostingEnv) AnkoEnv() *vm.Env

AnkoEnv returns the underlying Anko (https://github.com/mattn/anko) env.

func (*HostingEnv) Augment

func (he *HostingEnv) Augment(overridesAndExtras map[string]interface{}) *HostingEnv

Augment creates a new hosting environment with the same set of exposed artifacts, optionally having some of the artifacts overridden, and some extra artifacts available.

todo consider spliting overrides and extras as 2 separate args, validate them against exposed named list.

func (*HostingEnv) ExposeCtor

func (he *HostingEnv) ExposeCtor(ctorFunc interface{}, typeAlias string)

ExposeCtor exposes the specified constructor function, with expose name as the function's return type name, or `typeAlias` if it's not empty.

note a constructor function should return one and only one value.

func (*HostingEnv) ExposeFunction

func (he *HostingEnv) ExposeFunction(name string, fun interface{})

ExposeFunction exposes an arbitrary function to this env.

func (*HostingEnv) ExposeReactor

func (he *HostingEnv) ExposeReactor(reactor interface{})

ExposeReactor exposes all methods declared (by the list returned from `NamesToExpose`) from `reactor`. If `reactor` does not implements `Reactor`, all its exported methods are exposed.

func (*HostingEnv) ExposeValue

func (he *HostingEnv) ExposeValue(name string, val interface{})

ExposeValue exposes the specified `val` with `name`.

func (*HostingEnv) ExposedNames

func (he *HostingEnv) ExposedNames() []string

ExposedNames returns a copy of the slice of names, ever exposed through the `Expose*()` calles, up to the point it is called.

func (*HostingEnv) Get

func (he *HostingEnv) Get(name string) interface{}

Get returns what ever value associated with `name` in this env, which can be an:

  • exposed artifact
  • intrinsic object
  • implanted artifact by peer script

or nil if the name does not bind to anything in this env.

func (*HostingEnv) Ho

func (he *HostingEnv) Ho() *HostingEnd

Ho returns the hosting endpoint attached to this env.

func (*HostingEnv) NameExposed

func (he *HostingEnv) NameExposed(name string) bool

NameExposed tells whether the specified name is exported from this env, up to the point it is called.

func (*HostingEnv) Po

func (he *HostingEnv) Po() *PostingEnd

Po reutrns the posting endpoint attached to this env.

func (*HostingEnv) RunInEnv

func (he *HostingEnv) RunInEnv(ctx context.Context, code string) (result interface{}, err error)

RunInEnv executes `code` within this environment.

type InitMagicFunction

type InitMagicFunction = func(po *PostingEnd, ho *HostingEnd)

InitMagicFunction is the prototype for magic functions to be called on HBI wire connection made.

Such a function should be exposed with name `__hbi_init__` from the `HostingEnv`.

note: this interface should really be defined in `he` package, but that'll create cyclic imports between `proto` and `he`.

type Packet

type Packet struct {
	// wire directive
	WireDir string

	// textual payload
	Payload string
}

Packet is the atomic textual unit to be transfered over an HBI wire

func (Packet) Format

func (pkt Packet) Format(s fmt.State, verb rune)

Format make Packet implement fmt.Formatter

func (Packet) String

func (pkt Packet) String() string

type PoCo

type PoCo struct {
	// hosting env to use for this conversation, if not nil
	HE *HostingEnv
	// contains filtered or unexported fields
}

PoCo is the active, posting conversation.

A PoCo is created from application by calling PostingEnd.NewCo()

func (*PoCo) Close

func (co *PoCo) Close() error

Close closes this posting conversation, neither send nor recv operation can be performed with a closed posting conversation.

Note this can only be called from the goroutine which created this conversation.

func (*PoCo) CoSeq

func (co *PoCo) CoSeq() string

CoSeq returns the sequence number of this conversation.

The sequence number of a posting conversation is assigned by the posting endpoint created it, the value does not necessarily be unique across a long time period, but won't repeat among a lot of conversations per sent over a wire in line.

func (*PoCo) Completed

func (co *PoCo) Completed() <-chan struct{}

Completed returns the channel that get closed when this posting conversation has been fully processed with the triggered hosting conversation at remote site done.

Subsequent processes depending on the success of this conversation's completion can receive from the returned channel to wait the signal of proceed, with the backing hbic's Done() channel selected together.

Closing of this channel before its backing hbic is closed can confirm the final success of this conversation, as well its `recv` stage. i.e. all peer-scripting-code and data/stream sent with this conversation has been landed by peer's hosting endpoint, with a triggered hosting conversation, and all back-scripts (plus data/stream if any) as the response from that hosting conversation has been landed by local hosting endpoint, and received with this posting conversation (if any recv ops involved).

func (*PoCo) RecvData

func (co *PoCo) RecvData(d []byte) error

RecvData receives the binary data/stream sent with the triggered hosting conversation at remote site via HoCo.SendData() or HoCo.SendStream()

Note this can only be called in `recv` stage, and from the goroutine which created this conversation.

func (*PoCo) RecvObj

func (co *PoCo) RecvObj() (obj interface{}, err error)

RecvObj returns the landed result of a piece of back-script `code` sent with the triggered hosting conversation at remote site via HoCo.SendObj(code)

Note this can only be called in `recv` stage, and from the goroutine which created this conversation.

func (*PoCo) RecvStream

func (co *PoCo) RecvStream(ds func() ([]byte, error)) error

RecvStream receives the binary data/stream sent with the triggered hosting conversation at remote site via HoCo.SendData() or HoCo.SendStream()

Note this can only be called in `recv` stage, and from the goroutine which created this conversation.

func (*PoCo) SendCode

func (co *PoCo) SendCode(code string) error

SendCode sends `code` to peer's hosting endpoint for landing by its hosting environment.

Only side effects are expected from landing of `code` at peer site.

Note this can only be called in `send` stage, and from the goroutine which created this conversation.

func (*PoCo) SendData

func (co *PoCo) SendData(d []byte) error

SendData sends a single chunk of binary data to peer site.

The respective hosting conversation at peer site is expected to receive the data by calling HoCo.RecvData() or HoCo.RecvStream()

Note this can only be called in `send` stage, and from the goroutine which created this conversation.

func (*PoCo) SendObj

func (co *PoCo) SendObj(code string) error

SendObj sends `code` to peer's hosting endpoint for landing by its hosting environment.

The respective hosting conversation at peer site is expected to receive the result value from landing of `code`, by calling HoCo.RecvObj()

Note this can only be called in `send` stage, and from the goroutine which created this conversation.

func (*PoCo) SendStream

func (co *PoCo) SendStream(ds func() ([]byte, error)) error

SendStream polls callback function `ds()` until it returns a nil []byte or non-nil error, and send each chunk to peer site in line. `ds()` will be called another time after the chunk returned from the previous call has been sent out.

The respective hosting conversation at peer site is expected to receive the data by calling HoCo.RecvData() or HoCo.RecvStream()

Note this can only be called in `send` stage, and from the goroutine which created this conversation.

func (*PoCo) StartRecv

func (co *PoCo) StartRecv() error

StartRecv transits this posting conversation from `send` stage to `recv` stage.

Once in `recv` stage, no `send` operation can be performed any more with this conversation, the underlying wire is released for other posting conversation to start off.

Note this can only be called in `send` stage, and from the goroutine which created this conversation.

type PostingEnd

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

PostingEnd is the application programming interface of a posting endpoint.

func (*PostingEnd) Close

func (po *PostingEnd) Close()

Close disconnects the underlying wire of the HBI connection.

func (*PostingEnd) Context

func (po *PostingEnd) Context() context.Context

Context returns the conttext associated with the underlying HBI connection.

func (*PostingEnd) Disconnect

func (po *PostingEnd) Disconnect(errReason string, trySendPeerError bool)

Disconnect disconnects the underlying wire of the HBI connection, optionally with a error message sent to peer site for information purpose.

func (*PostingEnd) Disconnected

func (po *PostingEnd) Disconnected() bool

Disconnected tells whether the underlying HBI connection has been disconnected.

func (*PostingEnd) NetIdent

func (po *PostingEnd) NetIdent() string

NetIdent returns the network identification string of the underlying wire.

func (*PostingEnd) NewCo

func (po *PostingEnd) NewCo(he *HostingEnv) (*PoCo, error)

NewCo starts a new posting conversation.

func (*PostingEnd) Notif

func (po *PostingEnd) Notif(code string) (completed <-chan struct{}, err error)

Notif is shorthand to (implicitly) create a posting conversation, which is closed immediately after `code` is sent with it.

the `completed` channel returned can be received from, to wait acknowledgement for end of conversation has been received from remote peer.

func (*PostingEnd) NotifData

func (po *PostingEnd) NotifData(code string, d []byte) (completed <-chan struct{}, err error)

NotifData is shorthand to (implicitly) create a posting conversation, which is closed immediately after `code` and `d` are sent with it.

the `completed` channel returned can be received from, to wait acknowledgement for end of conversation has been received from remote peer.

func (*PostingEnd) RemoteAddr

func (po *PostingEnd) RemoteAddr() string

RemoteAddr returns the remote network address of the underlying wire.

type Reactor

type Reactor interface {
	NamesToExpose() []string
}

Reactor is the interface optionally implemented by a type whose instances are to be exposed to a `HostingEnv` by calling `he.ExposeReactor()`.

If a reactor object does not implement this interface, all its exported fields and methods will be exposed.

Jump to

Keyboard shortcuts

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