Documentation ¶
Overview ¶
WebExtensions native messaging support based on net/rpc and net/rpc/jsonrpc facilities from the standard library.
Most of the types and functions can be wiped from the public interface but such building blocks might be reused for similar protocols.
Example ¶
package main import ( "net/rpc" "net/rpc/jsonrpc" "os" "github.com/maxnikulin/burl/pkg/webextensions" ) type Backend struct{} func (b *Backend) Ping(query *string, reply *string) error { *reply = *query + "pong" return nil } func main() { rpc.RegisterName("Example", new(Backend)) rpc.ServeCodec(webextensions.NewServerCodecSplit(os.Stdin, os.Stdout, jsonrpc.NewServerCodec)) }
Output:
Index ¶
- Variables
- func NewClientCodecSplit(reader io.ReadCloser, writer io.WriteCloser, ...) rpc.ClientCodec
- func NewServerCodecSplit(reader io.ReadCloser, writer io.WriteCloser, parentFactory CodecFactory) rpc.ServerCodec
- type ClientCommand
- type CodecFactory
- type FrameBufferedWriter
- type FrameLimitedReader
- type FrameReadWriteCloser
- type FrameReader
- type FrameWriter
- type Manifest
- type MappedServerCodec
- type SplitFrameReadWriteCloser
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNoCommand = errors.New("Command is not specified")
var ErrOutBufferNotEmpty = errors.New("Output buffer is not empty on close")
var ErrReadBody = errors.New("Frame body has not read till its end")
var MaxInSize uint32 = 1024 * 1024 * 1024
Said to protect browser from buggy backends
var NativeEndian = binary.LittleEndian
Conditional compilation hack to bypass attempts of Go authors to make world better by prohibiting usage of native encoding. Real world spec originating from Chrome developers states:
"Each message ... is preceded with a 32-bit value containing the message length in native byte order."
Portable alternative https://github.com/koneu/natend relies on unsafe. Refused request for a similar feature in the "binary/encoding" package: https://groups.google.com/d/topic/golang-nuts/3GEzwKfRRQw
var SizeMismatchError = errors.New("Written frame size differs from actual")
Do not know if it may happen
var TooLargeError = errors.New("Frame size is too large")
Functions ¶
func NewClientCodecSplit ¶
func NewClientCodecSplit(reader io.ReadCloser, writer io.WriteCloser, parentFactory func(io.ReadWriteCloser) rpc.ClientCodec, ) rpc.ClientCodec
The purpose of client codec is usage in test applications that allows to debug backend without a browser. RPC client created with such codec is a usual RPC client.
Example ¶
package main import ( "fmt" "net/rpc" "net/rpc/jsonrpc" "os" "os/exec" "github.com/maxnikulin/burl/pkg/webextensions" ) func main() { runClient := func() error { cmd := exec.Command("we_example", "--option", "argument") cmd.Stderr = os.Stderr cmdStdin, err := cmd.StdinPipe() if err != nil { return fmt.Errorf("piping command stdin: %w", err) } cmdStdout, err := cmd.StdoutPipe() if err != nil { return fmt.Errorf("piping command stdout: %w", err) } if err := cmd.Start(); err != nil { return fmt.Errorf("run backend: %w", err) } rpcClient := rpc.NewClientWithCodec(webextensions.NewClientCodecSplit(cmdStdout, cmdStdin, jsonrpc.NewClientCodec)) query := "ping" var response string if err := rpcClient.Call("Backend.Ping", &query, &response); err != nil { return err } else { fmt.Printf("response: %s\n", response) } if err := rpcClient.Close(); err != nil { return fmt.Errorf("client close: %w", err) } if err := cmd.Wait(); err != nil { return fmt.Errorf("exec wait: %w", err) } return nil } runIt := false // compile-only example if runIt { if err := runClient(); err != nil { fmt.Printf("error: %s", err) } } }
Output:
func NewServerCodecSplit ¶
func NewServerCodecSplit(reader io.ReadCloser, writer io.WriteCloser, parentFactory CodecFactory, ) rpc.ServerCodec
The primary function in this library. Wrap server codec created by parentFactory (e.g. jsonrpc.NewServerCodec) for usage with split input and output streams and to handle preceding packet size.
Example ¶
package main import ( "net/rpc" "net/rpc/jsonrpc" "os" "github.com/maxnikulin/burl/pkg/webextensions" ) func main() { rpc.ServeCodec(webextensions.NewServerCodecSplit(os.Stdin, os.Stdout, jsonrpc.NewServerCodec)) }
Output:
Types ¶
type ClientCommand ¶
func NewClientCommand ¶
func NewClientCommand(args []string) (client *ClientCommand, err error)
func (*ClientCommand) Close ¶
func (c *ClientCommand) Close() error
type CodecFactory ¶
type CodecFactory func(io.ReadWriteCloser) rpc.ServerCodec
func MappedServerCodecFactory ¶
func MappedServerCodecFactory(methodMap map[string]string, parentFactory CodecFactory) CodecFactory
Usage:
methodMap := map[string]string{ "hello": "Addon.Hello" } f := webextensions.MappedServerCodecFactory(methodMap, jsonrpc.NewServerCodec)))
Result can be passed to NewServerCodecSplit.
type FrameBufferedWriter ¶
func (*FrameBufferedWriter) Discard ¶
func (w *FrameBufferedWriter) Discard()
func (*FrameBufferedWriter) Empty ¶
func (w *FrameBufferedWriter) Empty() bool
func (*FrameBufferedWriter) WriteFrame ¶
func (w *FrameBufferedWriter) WriteFrame() error
type FrameLimitedReader ¶
type FrameLimitedReader struct {
io.LimitedReader
}
FrameReader with binary uint32 size header as in Webextensions native messaging protocol. Internal helper that might be helpful in other application.
func (*FrameLimitedReader) ReadHeader ¶
func (r *FrameLimitedReader) ReadHeader() error
type FrameReadWriteCloser ¶
type FrameReadWriteCloser interface { FrameReader FrameWriter io.Closer }
Wrapper interface that behaves as ordinary connection for RPC codec and use "size,data" format for communication with other end. It is an internal interface, but there is no reason to hide it.
func NewSplitFrameReadWriteCloser ¶
func NewSplitFrameReadWriteCloser(reader io.ReadCloser, writer io.WriteCloser) FrameReadWriteCloser
type FrameReader ¶
Split continuous data into packets using "size,data" protocol. Call ReadHeader() then consume packet data using Read. More internal interface than public one.
func NewFrameLimitedReader ¶
func NewFrameLimitedReader(reader io.ReadCloser) FrameReader
type FrameWriter ¶
type FrameWriter interface { io.Writer WriteFrame() error // Likely have an underlying buffer. Returns if it is empty. Empty() bool // Annoying item, unsure if it is worth to check for non-empty buffer error Discard() }
func NewFrameBufferedWriter ¶
func NewFrameBufferedWriter(w io.WriteCloser) FrameWriter
type Manifest ¶
type Manifest struct { Name string `json:"name"` Description string `json:"description"` Path string `json:"path"` Type string `json:"type"` // Chrome AllowedOrigins []string `json:"allowed_origins,omitempty"` // Firefox AllowedExtensions []string `json:"allowed_extensions,omitempty"` ManifestPath string `json:"-"` }
Due to excessive number of fields, there is no point in New function. Call Init to initialize Type field and validate.
type MappedServerCodec ¶
type MappedServerCodec struct { rpc.ServerCodec // contains filtered or unexported fields }
Mapping for arbitrary names of RPC methods. To overcome limitations that method names must start with a capital letter otherwise they are not exported.
func (*MappedServerCodec) ReadRequestHeader ¶
func (c *MappedServerCodec) ReadRequestHeader(r *rpc.Request) error
type SplitFrameReadWriteCloser ¶
type SplitFrameReadWriteCloser struct { FrameReader FrameWriter R io.Closer // might be obtained from FrameReader using type cast W io.Closer }
A mediator that merges stdin and stdout into unified connection expected by a RPC codec. Handle packet size before data during data exchange over IO streams. Is not supposed to be used directly in ordinary cases.
func (*SplitFrameReadWriteCloser) Close ¶
func (s *SplitFrameReadWriteCloser) Close() error
func (*SplitFrameReadWriteCloser) Discard ¶
func (s *SplitFrameReadWriteCloser) Discard()