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.
Files changed (63) hide show
  1. package/LICENSE +19 -0
  2. package/README.md +122 -27
  3. package/dist/echo/echo.d.ts +59 -0
  4. package/dist/echo/echo.js +85 -0
  5. package/dist/srpc/broadcast-channel.d.ts +16 -0
  6. package/dist/srpc/broadcast-channel.js +60 -0
  7. package/dist/srpc/client-rpc.d.ts +31 -0
  8. package/dist/srpc/client-rpc.js +176 -0
  9. package/dist/srpc/client.d.ts +12 -0
  10. package/dist/srpc/client.js +129 -0
  11. package/dist/srpc/conn.d.ts +14 -0
  12. package/dist/srpc/conn.js +38 -0
  13. package/dist/srpc/index.d.ts +3 -0
  14. package/dist/srpc/index.js +2 -0
  15. package/dist/srpc/packet.d.ts +9 -0
  16. package/dist/srpc/packet.js +89 -0
  17. package/dist/srpc/rpcproto.d.ts +194 -0
  18. package/dist/srpc/rpcproto.js +322 -0
  19. package/dist/srpc/stream.d.ts +5 -0
  20. package/dist/srpc/stream.js +1 -0
  21. package/dist/srpc/ts-proto-rpc.d.ts +7 -0
  22. package/dist/srpc/ts-proto-rpc.js +1 -0
  23. package/dist/srpc/websocket.d.ts +7 -0
  24. package/dist/srpc/websocket.js +18 -0
  25. package/echo/echo.go +1 -0
  26. package/echo/echo.pb.go +165 -0
  27. package/echo/echo.proto +19 -0
  28. package/echo/echo.ts +191 -0
  29. package/echo/echo_srpc.pb.go +333 -0
  30. package/echo/echo_vtproto.pb.go +271 -0
  31. package/echo/server.go +73 -0
  32. package/package.json +71 -9
  33. package/srpc/broadcast-channel.ts +72 -0
  34. package/srpc/client-rpc.go +163 -0
  35. package/srpc/client-rpc.ts +197 -0
  36. package/srpc/client.go +96 -0
  37. package/srpc/client.ts +182 -0
  38. package/srpc/conn.go +7 -0
  39. package/srpc/conn.ts +49 -0
  40. package/srpc/errors.go +20 -0
  41. package/srpc/handler.go +13 -0
  42. package/srpc/index.ts +3 -0
  43. package/srpc/message.go +7 -0
  44. package/srpc/mux.go +76 -0
  45. package/srpc/packet-rw.go +102 -0
  46. package/srpc/packet.go +71 -0
  47. package/srpc/packet.ts +105 -0
  48. package/srpc/rpc-stream.go +76 -0
  49. package/srpc/rpcproto.pb.go +455 -0
  50. package/srpc/rpcproto.proto +46 -0
  51. package/srpc/rpcproto.ts +467 -0
  52. package/srpc/rpcproto_vtproto.pb.go +1094 -0
  53. package/srpc/server-http.go +66 -0
  54. package/srpc/server-pipe.go +26 -0
  55. package/srpc/server-rpc.go +160 -0
  56. package/srpc/server.go +29 -0
  57. package/srpc/stream-pipe.go +86 -0
  58. package/srpc/stream.go +24 -0
  59. package/srpc/stream.ts +11 -0
  60. package/srpc/ts-proto-rpc.ts +29 -0
  61. package/srpc/websocket.go +68 -0
  62. package/srpc/websocket.ts +22 -0
  63. 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))