starpc 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +19 -0
- package/README.md +122 -27
- package/dist/echo/echo.d.ts +59 -0
- package/dist/echo/echo.js +85 -0
- package/dist/srpc/broadcast-channel.d.ts +16 -0
- package/dist/srpc/broadcast-channel.js +60 -0
- package/dist/srpc/client-rpc.d.ts +31 -0
- package/dist/srpc/client-rpc.js +176 -0
- package/dist/srpc/client.d.ts +12 -0
- package/dist/srpc/client.js +129 -0
- package/dist/srpc/conn.d.ts +14 -0
- package/dist/srpc/conn.js +38 -0
- package/dist/srpc/index.d.ts +3 -0
- package/dist/srpc/index.js +2 -0
- package/dist/srpc/packet.d.ts +9 -0
- package/dist/srpc/packet.js +89 -0
- package/dist/srpc/rpcproto.d.ts +194 -0
- package/dist/srpc/rpcproto.js +322 -0
- package/dist/srpc/stream.d.ts +5 -0
- package/dist/srpc/stream.js +1 -0
- package/dist/srpc/ts-proto-rpc.d.ts +7 -0
- package/dist/srpc/ts-proto-rpc.js +1 -0
- package/dist/srpc/websocket.d.ts +7 -0
- package/dist/srpc/websocket.js +18 -0
- package/echo/echo.go +1 -0
- package/echo/echo.pb.go +165 -0
- package/echo/echo.proto +19 -0
- package/echo/echo.ts +191 -0
- package/echo/echo_srpc.pb.go +333 -0
- package/echo/echo_vtproto.pb.go +271 -0
- package/echo/server.go +73 -0
- package/package.json +71 -9
- package/srpc/broadcast-channel.ts +72 -0
- package/srpc/client-rpc.go +163 -0
- package/srpc/client-rpc.ts +197 -0
- package/srpc/client.go +96 -0
- package/srpc/client.ts +182 -0
- package/srpc/conn.go +7 -0
- package/srpc/conn.ts +49 -0
- package/srpc/errors.go +20 -0
- package/srpc/handler.go +13 -0
- package/srpc/index.ts +3 -0
- package/srpc/message.go +7 -0
- package/srpc/mux.go +76 -0
- package/srpc/packet-rw.go +102 -0
- package/srpc/packet.go +71 -0
- package/srpc/packet.ts +105 -0
- package/srpc/rpc-stream.go +76 -0
- package/srpc/rpcproto.pb.go +455 -0
- package/srpc/rpcproto.proto +46 -0
- package/srpc/rpcproto.ts +467 -0
- package/srpc/rpcproto_vtproto.pb.go +1094 -0
- package/srpc/server-http.go +66 -0
- package/srpc/server-pipe.go +26 -0
- package/srpc/server-rpc.go +160 -0
- package/srpc/server.go +29 -0
- package/srpc/stream-pipe.go +86 -0
- package/srpc/stream.go +24 -0
- package/srpc/stream.ts +11 -0
- package/srpc/ts-proto-rpc.ts +29 -0
- package/srpc/websocket.go +68 -0
- package/srpc/websocket.ts +22 -0
- package/srpc/writer.go +9 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
package srpc
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"encoding/binary"
|
|
6
|
+
"io"
|
|
7
|
+
|
|
8
|
+
"github.com/pkg/errors"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// maxMessageSize is the max message size in bytes
|
|
12
|
+
var maxMessageSize = 1e7
|
|
13
|
+
|
|
14
|
+
// PacketReaderWriter reads and writes packets from a io.ReadWriter.
|
|
15
|
+
// Uses a LittleEndian uint32 length prefix.
|
|
16
|
+
type PacketReaderWriter struct {
|
|
17
|
+
// rw is the io.ReadWriterCloser
|
|
18
|
+
rw io.ReadWriteCloser
|
|
19
|
+
// cb is the callback
|
|
20
|
+
cb PacketHandler
|
|
21
|
+
// buf is the buffered data
|
|
22
|
+
buf bytes.Buffer
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// NewPacketReadWriter constructs a new read/writer.
|
|
26
|
+
func NewPacketReadWriter(rw io.ReadWriteCloser, cb PacketHandler) *PacketReaderWriter {
|
|
27
|
+
return &PacketReaderWriter{rw: rw, cb: cb}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// WritePacket writes a packet to the writer.
|
|
31
|
+
func (r *PacketReaderWriter) WritePacket(p *Packet) error {
|
|
32
|
+
msgSize := p.SizeVT()
|
|
33
|
+
data := make([]byte, 4+msgSize)
|
|
34
|
+
binary.LittleEndian.PutUint32(data, uint32(msgSize))
|
|
35
|
+
_, err := p.MarshalToVT(data[4:])
|
|
36
|
+
if err != nil {
|
|
37
|
+
return err
|
|
38
|
+
}
|
|
39
|
+
_, err = r.rw.Write(data)
|
|
40
|
+
return err
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ReadPump executes the read pump in a goroutine.
|
|
44
|
+
func (r *PacketReaderWriter) ReadPump() error {
|
|
45
|
+
var currLen uint32
|
|
46
|
+
buf := make([]byte, 2048)
|
|
47
|
+
for {
|
|
48
|
+
n, err := r.rw.Read(buf)
|
|
49
|
+
if err != nil {
|
|
50
|
+
if err == io.EOF {
|
|
51
|
+
err = nil
|
|
52
|
+
}
|
|
53
|
+
return err
|
|
54
|
+
}
|
|
55
|
+
_, err = r.buf.Write(buf[:n])
|
|
56
|
+
if err != nil {
|
|
57
|
+
return err
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// check if we have enough for a length prefix
|
|
61
|
+
bufLen := r.buf.Len()
|
|
62
|
+
if currLen == 0 {
|
|
63
|
+
if bufLen < 4 {
|
|
64
|
+
continue
|
|
65
|
+
}
|
|
66
|
+
currLen = r.readLengthPrefix(r.buf.Bytes())
|
|
67
|
+
if currLen == 0 {
|
|
68
|
+
return errors.New("unexpected zero len prefix")
|
|
69
|
+
}
|
|
70
|
+
if currLen > uint32(maxMessageSize) {
|
|
71
|
+
return errors.Errorf("message size %v greater than maximum %v", currLen, maxMessageSize)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if currLen != 0 && bufLen >= int(currLen)+4 {
|
|
75
|
+
pkt := r.buf.Next(int(currLen + 4))[4:]
|
|
76
|
+
currLen = 0
|
|
77
|
+
npkt := &Packet{}
|
|
78
|
+
if err := npkt.UnmarshalVT(pkt); err != nil {
|
|
79
|
+
return err
|
|
80
|
+
}
|
|
81
|
+
if err := r.cb(npkt); err != nil {
|
|
82
|
+
return err
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Close closes the packet rw.
|
|
89
|
+
func (r *PacketReaderWriter) Close() error {
|
|
90
|
+
return r.rw.Close()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// readLengthPrefix reads the length prefix.
|
|
94
|
+
func (r *PacketReaderWriter) readLengthPrefix(b []byte) uint32 {
|
|
95
|
+
if len(b) < 4 {
|
|
96
|
+
return 0
|
|
97
|
+
}
|
|
98
|
+
return binary.LittleEndian.Uint32(b)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// _ is a type assertion
|
|
102
|
+
var _ Writer = (*PacketReaderWriter)(nil)
|
package/srpc/packet.go
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
package srpc
|
|
2
|
+
|
|
3
|
+
// PacketHandler handles a packet.
|
|
4
|
+
type PacketHandler = func(pkt *Packet) error
|
|
5
|
+
|
|
6
|
+
// Validate performs cursory validation of the packet.
|
|
7
|
+
func (p *Packet) Validate() error {
|
|
8
|
+
switch b := p.GetBody().(type) {
|
|
9
|
+
case *Packet_CallStart:
|
|
10
|
+
return b.CallStart.Validate()
|
|
11
|
+
case *Packet_CallData:
|
|
12
|
+
return b.CallData.Validate()
|
|
13
|
+
case *Packet_CallStartResp:
|
|
14
|
+
return b.CallStartResp.Validate()
|
|
15
|
+
default:
|
|
16
|
+
return ErrUnrecognizedPacket
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// NewCallStartPacket constructs a new CallStart packet.
|
|
21
|
+
func NewCallStartPacket(service, method string, data []byte) *Packet {
|
|
22
|
+
return &Packet{Body: &Packet_CallStart{
|
|
23
|
+
CallStart: &CallStart{
|
|
24
|
+
RpcService: service,
|
|
25
|
+
RpcMethod: method,
|
|
26
|
+
Data: data,
|
|
27
|
+
},
|
|
28
|
+
}}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Validate performs cursory validation of the packet.
|
|
32
|
+
func (p *CallStart) Validate() error {
|
|
33
|
+
method := p.GetRpcMethod()
|
|
34
|
+
if len(method) == 0 {
|
|
35
|
+
return ErrEmptyMethodID
|
|
36
|
+
}
|
|
37
|
+
service := p.GetRpcService()
|
|
38
|
+
if len(service) == 0 {
|
|
39
|
+
return ErrEmptyServiceID
|
|
40
|
+
}
|
|
41
|
+
return nil
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// NewCallDataPacket constructs a new CallData packet.
|
|
45
|
+
func NewCallDataPacket(data []byte, complete bool, err error) *Packet {
|
|
46
|
+
var errStr string
|
|
47
|
+
if err != nil {
|
|
48
|
+
errStr = err.Error()
|
|
49
|
+
}
|
|
50
|
+
return &Packet{Body: &Packet_CallData{
|
|
51
|
+
CallData: &CallData{
|
|
52
|
+
Data: data,
|
|
53
|
+
Complete: err != nil || complete,
|
|
54
|
+
Error: errStr,
|
|
55
|
+
},
|
|
56
|
+
}}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Validate performs cursory validation of the packet.
|
|
60
|
+
func (p *CallData) Validate() error {
|
|
61
|
+
if len(p.GetData()) == 0 && !p.GetComplete() && len(p.GetError()) == 0 {
|
|
62
|
+
return ErrEmptyPacket
|
|
63
|
+
}
|
|
64
|
+
return nil
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Validate performs cursory validation of the packet.
|
|
68
|
+
func (p *CallStartResp) Validate() error {
|
|
69
|
+
// nothing to check, empty packet is valid.
|
|
70
|
+
return nil
|
|
71
|
+
}
|
package/srpc/packet.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { Source, Transform } from 'it-stream-types'
|
|
2
|
+
import { Packet } from './rpcproto'
|
|
3
|
+
import {
|
|
4
|
+
encode as lengthPrefixEncode,
|
|
5
|
+
decode as lengthPrefixDecode,
|
|
6
|
+
} from 'it-length-prefixed'
|
|
7
|
+
|
|
8
|
+
// decodePacketSource unmarshals and async yields encoded Packets.
|
|
9
|
+
export async function* decodePacketSource(
|
|
10
|
+
source: Source<Uint8Array | Uint8Array[]>
|
|
11
|
+
): AsyncIterable<Packet> {
|
|
12
|
+
for await (const pkt of source) {
|
|
13
|
+
if (Array.isArray(pkt)) {
|
|
14
|
+
for (const p of pkt) {
|
|
15
|
+
yield* [Packet.decode(p)]
|
|
16
|
+
}
|
|
17
|
+
} else {
|
|
18
|
+
yield* [Packet.decode(pkt)]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// encodePacketSource marshals and async yields packets.
|
|
24
|
+
export async function* encodePacketSource(
|
|
25
|
+
source: Source<Packet | Packet[]>
|
|
26
|
+
): AsyncIterable<Uint8Array> {
|
|
27
|
+
for await (const pkt of source) {
|
|
28
|
+
if (Array.isArray(pkt)) {
|
|
29
|
+
for (const p of pkt) {
|
|
30
|
+
yield* [Packet.encode(p).finish()]
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
yield* [Packet.encode(pkt).finish()]
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// uint32LEDecode removes the length prefix.
|
|
39
|
+
const uint32LEDecode = (data: Uint8Array) => {
|
|
40
|
+
if (data.length < 4) {
|
|
41
|
+
throw RangeError('Could not decode int32BE')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength)
|
|
45
|
+
return view.getUint32(0, true)
|
|
46
|
+
}
|
|
47
|
+
uint32LEDecode.bytes = 4
|
|
48
|
+
|
|
49
|
+
// uint32LEEncode adds the length prefix.
|
|
50
|
+
const uint32LEEncode = (value: number, target?: Uint8Array, offset?: number) => {
|
|
51
|
+
target = target ?? new Uint8Array(4)
|
|
52
|
+
const view = new DataView(target.buffer, target.byteOffset, target.byteLength)
|
|
53
|
+
view.setUint32(offset ?? 0, value, true)
|
|
54
|
+
return target
|
|
55
|
+
}
|
|
56
|
+
uint32LEEncode.bytes = 4
|
|
57
|
+
|
|
58
|
+
// prependLengthPrefixTransform adds a length prefix to a message source.
|
|
59
|
+
// little-endian uint32
|
|
60
|
+
export function prependLengthPrefixTransform(): Transform<Uint8Array> {
|
|
61
|
+
return lengthPrefixEncode({lengthEncoder: uint32LEEncode})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// parseLengthPrefixTransform parses the length prefix from a message source.
|
|
65
|
+
// little-endian uint32
|
|
66
|
+
export function parseLengthPrefixTransform(): Transform<Uint8Array> {
|
|
67
|
+
return lengthPrefixDecode({ lengthDecoder: uint32LEDecode })
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// encodeUint32Le encodes the number as a uint32 with little endian.
|
|
71
|
+
export function encodeUint32Le(value: number): Uint8Array {
|
|
72
|
+
// output is a 4 byte array
|
|
73
|
+
const output = new Uint8Array(4)
|
|
74
|
+
for (let index = 0; index < output.length; index++) {
|
|
75
|
+
const b = value & 0xff
|
|
76
|
+
output[index] = b
|
|
77
|
+
value = (value - b) / 256
|
|
78
|
+
}
|
|
79
|
+
return output
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// decodeUint32Le decodes a uint32 from a 4 byte Uint8Array.
|
|
83
|
+
// returns 0 if decoding failed.
|
|
84
|
+
// callers should check that len(data) == 4
|
|
85
|
+
export function decodeUint32Le(data: Uint8Array): number {
|
|
86
|
+
let value = 0
|
|
87
|
+
let nbytes = 4
|
|
88
|
+
if (data.length < nbytes) {
|
|
89
|
+
nbytes = data.length
|
|
90
|
+
}
|
|
91
|
+
for (let i = nbytes - 1; i >= 0; i--) {
|
|
92
|
+
value = value * 256 + data[i]
|
|
93
|
+
}
|
|
94
|
+
return value
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// prependPacketLen adds the message length prefix to a packet.
|
|
98
|
+
export function prependPacketLen(msgData: Uint8Array): Uint8Array {
|
|
99
|
+
const msgLen = msgData.length
|
|
100
|
+
const msgLenData = encodeUint32Le(msgLen)
|
|
101
|
+
const merged = new Uint8Array(msgLen + msgLenData.length)
|
|
102
|
+
merged.set(msgLenData)
|
|
103
|
+
merged.set(msgData, msgLenData.length)
|
|
104
|
+
return merged
|
|
105
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
package srpc
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"io"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// RPCStream implements the stream interface passed to implementations.
|
|
9
|
+
type RPCStream struct {
|
|
10
|
+
// ctx is the stream context
|
|
11
|
+
ctx context.Context
|
|
12
|
+
// writer is the stream writer
|
|
13
|
+
writer Writer
|
|
14
|
+
// dataCh is the incoming data channel.
|
|
15
|
+
dataCh chan []byte
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// NewRPCStream constructs a new Stream with a ClientRPC.
|
|
19
|
+
// dataCh should be closed when no more messages will arrive.
|
|
20
|
+
func NewRPCStream(ctx context.Context, writer Writer, dataCh chan []byte) *RPCStream {
|
|
21
|
+
return &RPCStream{
|
|
22
|
+
ctx: ctx,
|
|
23
|
+
writer: writer,
|
|
24
|
+
dataCh: dataCh,
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Context is canceled when the Stream is no longer valid.
|
|
29
|
+
func (r *RPCStream) Context() context.Context {
|
|
30
|
+
return r.ctx
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// MsgSend sends the message to the remote.
|
|
34
|
+
func (r *RPCStream) MsgSend(msg Message) error {
|
|
35
|
+
select {
|
|
36
|
+
case <-r.ctx.Done():
|
|
37
|
+
return context.Canceled
|
|
38
|
+
default:
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
msgData, err := msg.MarshalVT()
|
|
42
|
+
if err != nil {
|
|
43
|
+
return err
|
|
44
|
+
}
|
|
45
|
+
outPkt := NewCallDataPacket(msgData, false, nil)
|
|
46
|
+
return r.writer.WritePacket(outPkt)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// MsgRecv receives an incoming message from the remote.
|
|
50
|
+
// Parses the message into the object at msg.
|
|
51
|
+
func (r *RPCStream) MsgRecv(msg Message) error {
|
|
52
|
+
select {
|
|
53
|
+
case <-r.Context().Done():
|
|
54
|
+
return context.Canceled
|
|
55
|
+
case data, ok := <-r.dataCh:
|
|
56
|
+
if !ok {
|
|
57
|
+
return io.EOF
|
|
58
|
+
}
|
|
59
|
+
return msg.UnmarshalVT(data)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// CloseSend signals to the remote that we will no longer send any messages.
|
|
64
|
+
func (r *RPCStream) CloseSend() error {
|
|
65
|
+
outPkt := NewCallDataPacket(nil, true, nil)
|
|
66
|
+
return r.writer.WritePacket(outPkt)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Close closes the stream.
|
|
70
|
+
func (r *RPCStream) Close() error {
|
|
71
|
+
_ = r.writer.Close()
|
|
72
|
+
return nil
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// _ is a type assertion
|
|
76
|
+
var _ Stream = ((*RPCStream)(nil))
|