starpc 0.0.1 → 0.1.2

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 (73) hide show
  1. package/LICENSE +19 -0
  2. package/Makefile +140 -0
  3. package/README.md +128 -26
  4. package/dist/echo/echo.d.ts +59 -0
  5. package/dist/echo/echo.js +85 -0
  6. package/dist/srpc/broadcast-channel.d.ts +16 -0
  7. package/dist/srpc/broadcast-channel.js +60 -0
  8. package/dist/srpc/client-rpc.d.ts +31 -0
  9. package/dist/srpc/client-rpc.js +176 -0
  10. package/dist/srpc/client.d.ts +12 -0
  11. package/dist/srpc/client.js +129 -0
  12. package/dist/srpc/conn.d.ts +14 -0
  13. package/dist/srpc/conn.js +38 -0
  14. package/dist/srpc/index.d.ts +3 -0
  15. package/dist/srpc/index.js +2 -0
  16. package/dist/srpc/packet.d.ts +9 -0
  17. package/dist/srpc/packet.js +89 -0
  18. package/dist/srpc/rpcproto.d.ts +194 -0
  19. package/dist/srpc/rpcproto.js +322 -0
  20. package/dist/srpc/stream.d.ts +5 -0
  21. package/dist/srpc/stream.js +1 -0
  22. package/dist/srpc/ts-proto-rpc.d.ts +7 -0
  23. package/dist/srpc/ts-proto-rpc.js +1 -0
  24. package/dist/srpc/websocket.d.ts +7 -0
  25. package/dist/srpc/websocket.js +18 -0
  26. package/e2e/e2e.go +1 -0
  27. package/e2e/e2e_test.go +158 -0
  28. package/echo/echo.go +1 -0
  29. package/echo/echo.pb.go +165 -0
  30. package/echo/echo.proto +19 -0
  31. package/echo/echo.ts +191 -0
  32. package/echo/echo_srpc.pb.go +333 -0
  33. package/echo/echo_vtproto.pb.go +271 -0
  34. package/echo/server.go +73 -0
  35. package/go.mod +50 -0
  36. package/go.sum +210 -0
  37. package/integration/integration.bash +25 -0
  38. package/integration/integration.go +30 -0
  39. package/integration/integration.ts +54 -0
  40. package/integration/tsconfig.json +11 -0
  41. package/package.json +77 -9
  42. package/patches/@libp2p+mplex+1.2.1.patch +22 -0
  43. package/srpc/broadcast-channel.ts +72 -0
  44. package/srpc/client-rpc.go +163 -0
  45. package/srpc/client-rpc.ts +197 -0
  46. package/srpc/client.go +96 -0
  47. package/srpc/client.ts +182 -0
  48. package/srpc/conn.go +7 -0
  49. package/srpc/conn.ts +49 -0
  50. package/srpc/errors.go +20 -0
  51. package/srpc/handler.go +13 -0
  52. package/srpc/index.ts +3 -0
  53. package/srpc/message.go +7 -0
  54. package/srpc/mux.go +76 -0
  55. package/srpc/packet-rw.go +102 -0
  56. package/srpc/packet.go +71 -0
  57. package/srpc/packet.ts +105 -0
  58. package/srpc/rpc-stream.go +76 -0
  59. package/srpc/rpcproto.pb.go +455 -0
  60. package/srpc/rpcproto.proto +46 -0
  61. package/srpc/rpcproto.ts +467 -0
  62. package/srpc/rpcproto_vtproto.pb.go +1094 -0
  63. package/srpc/server-http.go +66 -0
  64. package/srpc/server-pipe.go +26 -0
  65. package/srpc/server-rpc.go +160 -0
  66. package/srpc/server.go +29 -0
  67. package/srpc/stream-pipe.go +86 -0
  68. package/srpc/stream.go +24 -0
  69. package/srpc/stream.ts +11 -0
  70. package/srpc/ts-proto-rpc.ts +29 -0
  71. package/srpc/websocket.go +68 -0
  72. package/srpc/websocket.ts +22 -0
  73. package/srpc/writer.go +9 -0
package/srpc/client.go ADDED
@@ -0,0 +1,96 @@
1
+ package srpc
2
+
3
+ import (
4
+ "context"
5
+ "io"
6
+
7
+ "github.com/pkg/errors"
8
+ )
9
+
10
+ // Client implements a SRPC client which can initiate RPC streams.
11
+ type Client interface {
12
+ // Invoke executes a unary RPC with the remote.
13
+ Invoke(ctx context.Context, service, method string, in, out Message) error
14
+
15
+ // NewStream starts a streaming RPC with the remote & returns the stream.
16
+ // firstMsg is optional.
17
+ NewStream(ctx context.Context, service, method string, firstMsg Message) (Stream, error)
18
+ }
19
+
20
+ // OpenStreamFunc opens a stream with a remote.
21
+ // msgHandler must not be called concurrently.
22
+ type OpenStreamFunc = func(ctx context.Context, msgHandler func(pkt *Packet) error) (Writer, error)
23
+
24
+ // client implements Client with a transport.
25
+ type client struct {
26
+ // openStream opens a new stream.
27
+ openStream OpenStreamFunc
28
+ }
29
+
30
+ // NewClient constructs a client with a OpenStreamFunc.
31
+ func NewClient(openStream OpenStreamFunc) Client {
32
+ return &client{
33
+ openStream: openStream,
34
+ }
35
+ }
36
+
37
+ // Invoke executes a unary RPC with the remote.
38
+ func (c *client) Invoke(rctx context.Context, service, method string, in, out Message) error {
39
+ ctx, ctxCancel := context.WithCancel(rctx)
40
+ defer ctxCancel()
41
+
42
+ firstMsg, err := in.MarshalVT()
43
+ if err != nil {
44
+ return err
45
+ }
46
+ clientRPC := NewClientRPC(ctx, service, method)
47
+ writer, err := c.openStream(ctx, clientRPC.HandlePacket)
48
+ if err != nil {
49
+ return err
50
+ }
51
+ if err := clientRPC.Start(writer, firstMsg); err != nil {
52
+ return err
53
+ }
54
+ msgs, err := clientRPC.ReadAll()
55
+ if err != nil {
56
+ // this includes any server returned error.
57
+ return err
58
+ }
59
+ if len(msgs) == 0 {
60
+ // no reply? return eof.
61
+ return io.EOF
62
+ }
63
+ // parse first message to out
64
+ if err := out.UnmarshalVT(msgs[0]); err != nil {
65
+ return errors.Wrap(ErrInvalidMessage, err.Error())
66
+ }
67
+ // done
68
+ return nil
69
+ }
70
+
71
+ // NewStream starts a streaming RPC with the remote & returns the stream.
72
+ // firstMsg is optional.
73
+ func (c *client) NewStream(ctx context.Context, service, method string, firstMsg Message) (Stream, error) {
74
+ var firstMsgData []byte
75
+ if firstMsg != nil {
76
+ var err error
77
+ firstMsgData, err = firstMsg.MarshalVT()
78
+ if err != nil {
79
+ return nil, err
80
+ }
81
+ }
82
+
83
+ clientRPC := NewClientRPC(ctx, service, method)
84
+ writer, err := c.openStream(ctx, clientRPC.HandlePacket)
85
+ if err != nil {
86
+ return nil, err
87
+ }
88
+ if err := clientRPC.Start(writer, firstMsgData); err != nil {
89
+ return nil, err
90
+ }
91
+
92
+ return NewRPCStream(ctx, clientRPC.writer, clientRPC.dataCh), nil
93
+ }
94
+
95
+ // _ is a type assertion
96
+ var _ Client = ((*client)(nil))
package/srpc/client.ts ADDED
@@ -0,0 +1,182 @@
1
+ import { Observable, from as observableFrom } from 'rxjs'
2
+ import type { TsProtoRpc } from './ts-proto-rpc'
3
+ import type { OpenStreamFunc } from './stream'
4
+ import { DataCb, ClientRPC } from './client-rpc'
5
+ import { pipe } from 'it-pipe'
6
+ import { pushable, Pushable } from 'it-pushable'
7
+ import {
8
+ decodePacketSource,
9
+ encodePacketSource,
10
+ parseLengthPrefixTransform,
11
+ prependLengthPrefixTransform,
12
+ } from './packet'
13
+
14
+ // unaryDataCb builds a new unary request data callback.
15
+ function unaryDataCb(resolve: (data: Uint8Array) => void): DataCb {
16
+ return async (
17
+ data: Uint8Array
18
+ ): Promise<boolean | void> => {
19
+ // resolve the promise
20
+ resolve(data)
21
+ // this is the last data we expect.
22
+ return false
23
+ }
24
+ }
25
+
26
+ // streamingDataCb builds a new streaming request data callback.
27
+ /*
28
+ function streamingDataCb(resolve: (data: Uint8Array) => void): DataCb {
29
+ return async (
30
+ data: Uint8Array
31
+ ): Promise<boolean | void> => {
32
+ // TODO
33
+ }
34
+ }
35
+ */
36
+
37
+ // writeClientStream registers the subscriber to write the client data stream.
38
+ function writeClientStream(call: ClientRPC, data: Observable<Uint8Array>) {
39
+ data.subscribe({
40
+ next(value) {
41
+ call.writeCallData(value)
42
+ },
43
+ error(err) {
44
+ call.close(err)
45
+ },
46
+ complete() {
47
+ call.writeCallData(new Uint8Array(0), true)
48
+ },
49
+ })
50
+ }
51
+
52
+ // waitCallComplete handles the call complete promise.
53
+ function waitCallComplete(
54
+ call: ClientRPC,
55
+ resolve: (data: Uint8Array) => void,
56
+ reject: (err: Error) => void,
57
+ ) {
58
+ call.waitComplete().catch(reject).finally(() => {
59
+ // ensure we resolve it if no data was ever returned.
60
+ resolve(new Uint8Array())
61
+ })
62
+ }
63
+
64
+ // Client implements the ts-proto Rpc interface with the drpcproto protocol.
65
+ export class Client implements TsProtoRpc {
66
+ // openConnFn is the open connection function.
67
+ // called when starting RPC.
68
+ private openConnFn: OpenStreamFunc
69
+
70
+ constructor(openConnFn: OpenStreamFunc) {
71
+ this.openConnFn = openConnFn
72
+ }
73
+
74
+ // request starts a non-streaming request.
75
+ public async request(
76
+ service: string,
77
+ method: string,
78
+ data: Uint8Array
79
+ ): Promise<Uint8Array> {
80
+ return new Promise<Uint8Array>((resolve, reject) => {
81
+ const dataCb = unaryDataCb(resolve)
82
+ this.startRpc(service, method, data, dataCb)
83
+ .then((call) => {
84
+ waitCallComplete(call, resolve, reject)
85
+ })
86
+ .catch(reject)
87
+ })
88
+ }
89
+
90
+ // clientStreamingRequest starts a client side streaming request.
91
+ public clientStreamingRequest(
92
+ service: string,
93
+ method: string,
94
+ data: Observable<Uint8Array>
95
+ ): Promise<Uint8Array> {
96
+ return new Promise<Uint8Array>((resolve, reject) => {
97
+ const dataCb = unaryDataCb(resolve)
98
+ this.startRpc(service, method, null, dataCb)
99
+ .then((call) => {
100
+ writeClientStream(call, data)
101
+ waitCallComplete(call, resolve, reject)
102
+ })
103
+ .catch(reject)
104
+ })
105
+ }
106
+
107
+ // serverStreamingRequest starts a server-side streaming request.
108
+ public serverStreamingRequest(
109
+ service: string,
110
+ method: string,
111
+ data: Uint8Array
112
+ ): Observable<Uint8Array> {
113
+ const pushServerData: Pushable<Uint8Array> = pushable()
114
+ const serverData = observableFrom(pushServerData)
115
+ const dataCb: DataCb = async (data: Uint8Array): Promise<boolean | void> => {
116
+ // push the message to the observable
117
+ pushServerData.push(data)
118
+ // expect more messages
119
+ return true
120
+ }
121
+ this.startRpc(service, method, data, dataCb)
122
+ .then((call) => {
123
+ call.waitComplete().catch((err: Error) => {
124
+ pushServerData.throw(err)
125
+ }).finally(() => {
126
+ pushServerData.end()
127
+ })
128
+ })
129
+ .catch(pushServerData.throw.bind(pushServerData))
130
+ return serverData
131
+ }
132
+
133
+ // bidirectionalStreamingRequest starts a two-way streaming request.
134
+ public bidirectionalStreamingRequest(
135
+ service: string,
136
+ method: string,
137
+ data: Observable<Uint8Array>
138
+ ): Observable<Uint8Array> {
139
+ const pushServerData: Pushable<Uint8Array> = pushable()
140
+ const serverData = observableFrom(pushServerData)
141
+ const dataCb: DataCb = async (data: Uint8Array): Promise<boolean | void> => {
142
+ // push the message to the observable
143
+ pushServerData.push(data)
144
+ // expect more messages
145
+ return true
146
+ }
147
+ this.startRpc(service, method, null, dataCb)
148
+ .then((call) => {
149
+ writeClientStream(call, data)
150
+ call.waitComplete().catch((err: Error) => {
151
+ pushServerData.throw(err)
152
+ }).finally(() => {
153
+ pushServerData.end()
154
+ })
155
+ })
156
+ .catch(pushServerData.throw.bind(pushServerData))
157
+ return serverData
158
+ }
159
+
160
+ // startRpc is a common utility function to begin a rpc call.
161
+ // throws any error starting the rpc call
162
+ private async startRpc(
163
+ rpcService: string,
164
+ rpcMethod: string,
165
+ data: Uint8Array | null,
166
+ dataCb: DataCb
167
+ ): Promise<ClientRPC> {
168
+ const conn = await this.openConnFn()
169
+ const call = new ClientRPC(rpcService, rpcMethod, dataCb)
170
+ pipe(
171
+ conn,
172
+ parseLengthPrefixTransform(),
173
+ decodePacketSource,
174
+ call,
175
+ encodePacketSource,
176
+ prependLengthPrefixTransform(),
177
+ conn,
178
+ )
179
+ await call.writeCallStart(data || undefined)
180
+ return call
181
+ }
182
+ }
package/srpc/conn.go ADDED
@@ -0,0 +1,7 @@
1
+ package srpc
2
+
3
+ // Conn represents a connection to a remote.
4
+ type Conn interface {
5
+ // GetOpenStreamFunc returns the OpenStream func.
6
+ GetOpenStreamFunc() OpenStreamFunc
7
+ }
package/srpc/conn.ts ADDED
@@ -0,0 +1,49 @@
1
+ import type { Stream } from '@libp2p/interfaces/connection'
2
+ import type { Duplex } from 'it-stream-types'
3
+ import { Components } from '@libp2p/interfaces/components'
4
+ import { MplexStreamMuxer } from '@libp2p/mplex'
5
+ import type { Stream as SRPCStream } from './stream'
6
+ import { Client } from './client'
7
+
8
+ // Conn implements a generic connection with a two-way stream.
9
+ export class Conn implements Duplex<Uint8Array> {
10
+ // muxer is the mplex stream muxer.
11
+ private muxer: MplexStreamMuxer
12
+
13
+ constructor() {
14
+ // see https://github.com/libp2p/js-libp2p-mplex/pull/179
15
+ this.muxer = new MplexStreamMuxer(new Components(), {
16
+ onIncomingStream: this.handleIncomingStream.bind(this),
17
+ })
18
+ }
19
+
20
+ // sink returns the message sink.
21
+ get sink() {
22
+ return this.muxer.sink
23
+ }
24
+
25
+ // source returns the outgoing message source.
26
+ get source() {
27
+ return this.muxer.source
28
+ }
29
+
30
+ // streams returns the set of all ongoing streams.
31
+ get streams() {
32
+ return this.muxer.streams
33
+ }
34
+
35
+ // buildClient builds a new client from the connection.
36
+ public buildClient(): Client {
37
+ return new Client(this.openStream.bind(this))
38
+ }
39
+
40
+ // openStream implements the client open stream function.
41
+ public async openStream(): Promise<SRPCStream> {
42
+ return this.muxer.newStream()
43
+ }
44
+
45
+ // handleIncomingStream handles an incoming stream.
46
+ private handleIncomingStream(strm: Stream) {
47
+ strm.abort(new Error('server -> client streams not implemented'))
48
+ }
49
+ }
package/srpc/errors.go ADDED
@@ -0,0 +1,20 @@
1
+ package srpc
2
+
3
+ import "errors"
4
+
5
+ var (
6
+ // ErrUnimplemented is returned if the RPC method was not implemented.
7
+ ErrUnimplemented = errors.New("unimplemented")
8
+ // ErrCompleted is returned if a message is received after the rpc was completed.
9
+ ErrCompleted = errors.New("unexpected packet after rpc was completed")
10
+ // ErrUnrecognizedPacket is returned if the packet type was not recognized.
11
+ ErrUnrecognizedPacket = errors.New("unrecognized packet type")
12
+ // ErrEmptyPacket is returned if nothing is specified in a packet.
13
+ ErrEmptyPacket = errors.New("invalid empty packet")
14
+ // ErrInvalidMessage indicates the message failed to parse.
15
+ ErrInvalidMessage = errors.New("invalid message")
16
+ // ErrEmptyMethodID is returned if the method id was empty.
17
+ ErrEmptyMethodID = errors.New("method id empty")
18
+ // ErrEmptyServiceID is returned if the service id was empty.
19
+ ErrEmptyServiceID = errors.New("service id empty")
20
+ )
@@ -0,0 +1,13 @@
1
+ package srpc
2
+
3
+ // Handler describes a SRPC call handler implementation.
4
+ type Handler interface {
5
+ // GetServiceID returns the ID of the service.
6
+ GetServiceID() string
7
+ // GetMethodIDs returns the list of methods for the service.
8
+ GetMethodIDs() []string
9
+ // InvokeMethod invokes the method matching the service & method ID.
10
+ // Returns false, nil if not found.
11
+ // If service string is empty, ignore it.
12
+ InvokeMethod(serviceID, methodID string, strm Stream) (bool, error)
13
+ }
package/srpc/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { Client } from './client'
2
+ export type { OpenStreamFunc } from './stream'
3
+ export { WebSocketConn } from './websocket'
@@ -0,0 +1,7 @@
1
+ package srpc
2
+
3
+ // Message is the vtprotobuf message interface.
4
+ type Message interface {
5
+ MarshalVT() ([]byte, error)
6
+ UnmarshalVT([]byte) error
7
+ }
package/srpc/mux.go ADDED
@@ -0,0 +1,76 @@
1
+ package srpc
2
+
3
+ import "sync"
4
+
5
+ // Mux contains a set of <service, method> handlers.
6
+ type Mux interface {
7
+ // Register registers a new RPC method handler (service).
8
+ Register(handler Handler) error
9
+ // InvokeMethod invokes the method matching the service & method ID.
10
+ // Returns false, nil if not found.
11
+ // If service string is empty, ignore it.
12
+ InvokeMethod(serviceID, methodID string, strm Stream) (bool, error)
13
+ }
14
+
15
+ // muxMethods is a mapping from method id to handler.
16
+ type muxMethods map[string]Handler
17
+
18
+ // mux is the default implementation of Mux.
19
+ type mux struct {
20
+ // rmtx guards below fields
21
+ rmtx sync.RWMutex
22
+ // services contains a mapping from services to handlers.
23
+ services map[string]muxMethods
24
+ }
25
+
26
+ // NewMux constructs a new Mux.
27
+ func NewMux() Mux {
28
+ return &mux{services: make(map[string]muxMethods)}
29
+ }
30
+
31
+ // Register registers a new RPC method handler (service).
32
+ func (m *mux) Register(handler Handler) error {
33
+ serviceID := handler.GetServiceID()
34
+ methodIDs := handler.GetMethodIDs()
35
+ if serviceID == "" {
36
+ return ErrEmptyServiceID
37
+ }
38
+
39
+ m.rmtx.Lock()
40
+ defer m.rmtx.Unlock()
41
+
42
+ serviceMethods := m.services[serviceID]
43
+ if serviceMethods == nil {
44
+ serviceMethods = make(muxMethods)
45
+ m.services[serviceID] = serviceMethods
46
+ }
47
+ for _, methodID := range methodIDs {
48
+ if methodID != "" {
49
+ serviceMethods[methodID] = handler
50
+ }
51
+ }
52
+
53
+ return nil
54
+ }
55
+
56
+ // InvokeMethod invokes the method matching the service & method ID.
57
+ // Returns false, nil if not found.
58
+ // If service string is empty, ignore it.
59
+ func (m *mux) InvokeMethod(serviceID, methodID string, strm Stream) (bool, error) {
60
+ var handler Handler
61
+ m.rmtx.RLock()
62
+ svcMethods := m.services[serviceID]
63
+ if svcMethods != nil {
64
+ handler = svcMethods[methodID]
65
+ }
66
+ m.rmtx.RUnlock()
67
+
68
+ if handler == nil {
69
+ return false, nil
70
+ }
71
+
72
+ return handler.InvokeMethod(serviceID, methodID, strm)
73
+ }
74
+
75
+ // _ is a type assertion
76
+ var _ Mux = ((*mux)(nil))
@@ -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
+ }