Skip to content

Commit a04730d

Browse files
Feat/pbserver (#59)
* Protobuf-based plugin RPC
1 parent 35746ae commit a04730d

37 files changed

+5457
-1005
lines changed

bridge/bridge.go

Lines changed: 124 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -4,146 +4,174 @@ Used internally for the RPC protocol.
44
package bridge
55

66
import (
7+
"encoding/binary"
78
"errors"
9+
"io"
10+
"log"
11+
"net"
12+
13+
"github.com/Kong/go-pdk/server/kong_plugin_protocol"
14+
"github.com/golang/protobuf/proto"
15+
"google.golang.org/protobuf/types/known/structpb"
816
)
917

1018
type PdkBridge struct {
11-
ch chan interface{}
19+
conn net.Conn
1220
}
1321

1422
type StepData struct {
1523
Method string
1624
Args []interface{}
1725
}
1826

19-
func New(ch chan interface{}) PdkBridge {
20-
return PdkBridge{ch: ch}
27+
func New(conn net.Conn) PdkBridge {
28+
return PdkBridge{
29+
conn: conn,
30+
}
2131
}
2232

23-
func (b PdkBridge) Ask(method string, args ...interface{}) (interface{}, error) {
24-
b.ch <- StepData{method, args}
33+
func readPbFrame(conn net.Conn) (data []byte, err error) {
34+
var len uint32
35+
err = binary.Read(conn, binary.LittleEndian, &len)
36+
if err != nil {
37+
return
38+
}
2539

26-
reply := <-b.ch
40+
data = make([]byte, len)
41+
if data == nil {
42+
return nil, errors.New("no memory")
43+
}
2744

28-
err, ok := reply.(error)
29-
if ok {
45+
_, err = io.ReadFull(conn, data)
46+
if err != nil {
3047
return nil, err
3148
}
3249

33-
return reply, nil
34-
}
35-
36-
func (b PdkBridge) AskClose(method string, args ...interface{}) {
37-
b.ch <- StepData{ method, args }
38-
close(b.ch)
50+
return
3951
}
4052

41-
func (b PdkBridge) AskInt(method string, args ...interface{}) (i int, err error) {
42-
val, err := b.Ask(method, args...)
53+
func writePbFrame(conn net.Conn, data []byte) (err error) {
54+
var len uint32 = uint32(len(data))
55+
err = binary.Write(conn, binary.LittleEndian, len)
4356
if err != nil {
4457
return
4558
}
46-
if val == nil {
47-
err = errors.New("null response")
48-
return
49-
}
5059

51-
switch val := val.(type) {
52-
case int:
53-
i = int(val)
54-
case int8:
55-
i = int(val)
56-
case int16:
57-
i = int(val)
58-
case int32:
59-
i = int(val)
60-
case int64:
61-
i = int(val)
62-
case uint:
63-
i = int(val)
64-
case uint8:
65-
i = int(val)
66-
case uint16:
67-
i = int(val)
68-
case uint32:
69-
i = int(val)
70-
case uint64:
71-
i = int(val)
72-
default:
73-
err = ReturnTypeError("integer")
60+
if len > 0 {
61+
_, err = conn.Write(data)
7462
}
63+
7564
return
7665
}
7766

78-
func (b PdkBridge) AskFloat(method string, args ...interface{}) (f float64, err error) {
79-
val, err := b.Ask(method, args...)
80-
if err != nil {
81-
return
67+
func WrapString(s string) *kong_plugin_protocol.String {
68+
return &kong_plugin_protocol.String{V: s}
69+
}
70+
71+
func WrapHeaders(h map[string][]string) (*structpb.Struct, error) {
72+
h2 := make(map[string]interface{}, len(h))
73+
for k, v := range h {
74+
l := make([]interface{}, len(v))
75+
for i, v2 := range v {
76+
l[i] = v2
77+
}
78+
h2[k] = l
8279
}
83-
if val == nil {
84-
err = errors.New("null response")
85-
return
80+
81+
st, err := structpb.NewStruct(h2)
82+
if err != nil {
83+
return nil, err
8684
}
8785

88-
switch val := val.(type) {
89-
case int:
90-
f = float64(val)
91-
case int8:
92-
f = float64(val)
93-
case int16:
94-
f = float64(val)
95-
case int32:
96-
f = float64(val)
97-
case int64:
98-
f = float64(val)
99-
case uint:
100-
f = float64(val)
101-
case uint8:
102-
f = float64(val)
103-
case uint16:
104-
f = float64(val)
105-
case uint32:
106-
f = float64(val)
107-
case uint64:
108-
f = float64(val)
109-
case float32:
110-
f = float64(val)
111-
case float64:
112-
f = float64(val)
113-
default:
114-
err = ReturnTypeError("float")
86+
return st, nil
87+
}
88+
89+
func UnwrapHeaders(st *structpb.Struct) map[string][]string {
90+
m := st.AsMap()
91+
m2 := make(map[string][]string)
92+
for k, v := range m {
93+
switch v2 := v.(type) {
94+
case string:
95+
m2[k] = []string{v2}
96+
case []string:
97+
m2[k] = v2
98+
case []interface{}:
99+
m2[k] = make([]string, len(v2))
100+
for i, v3 := range v2 {
101+
if s, ok := v3.(string); ok {
102+
m2[k][i] = s
103+
}
104+
}
105+
default:
106+
log.Printf("unexpected type %T on header %s:%v", v2, k, v2)
107+
}
115108
}
116-
return
109+
110+
return m2
117111
}
118112

119-
func (b PdkBridge) AskString(method string, args ...interface{}) (s string, err error) {
120-
val, err := b.Ask(method, args...)
113+
func (b PdkBridge) Ask(method string, args proto.Message, out proto.Message) error {
114+
err := writePbFrame(b.conn, []byte(method))
121115
if err != nil {
122-
return
116+
return err
123117
}
124-
if val == nil {
125-
err = errors.New("null response")
126-
return
118+
119+
var args_d []byte
120+
121+
if args != nil {
122+
args_d, err = proto.Marshal(args)
123+
if err != nil {
124+
return err
125+
}
127126
}
128127

129-
var ok bool
130-
if s, ok = val.(string); !ok {
131-
err = ReturnTypeError("string")
128+
err = writePbFrame(b.conn, args_d)
129+
if err != nil {
130+
return err
132131
}
133-
return
134-
}
135132

136-
func (b PdkBridge) AskMap(method string, args ...interface{}) (m map[string][]string, err error) {
137-
val, err := b.Ask(method, args...)
133+
out_d, err := readPbFrame(b.conn)
138134
if err != nil {
139-
return
135+
return err
140136
}
141137

142-
var ok bool
143-
if m, ok = val.(map[string][]string); !ok {
144-
err = ReturnTypeError("map[string][]string")
138+
if out != nil {
139+
err = proto.Unmarshal(out_d, out)
145140
}
146-
return
141+
142+
return err
143+
}
144+
145+
func (b PdkBridge) AskString(method string, args proto.Message) (string, error) {
146+
out := new(kong_plugin_protocol.String)
147+
err := b.Ask(method, args, out)
148+
return out.V, err
149+
}
150+
151+
func (b PdkBridge) AskInt(method string, args proto.Message) (int, error) {
152+
out := new(kong_plugin_protocol.Int)
153+
err := b.Ask(method, args, out)
154+
return int(out.V), err
155+
}
156+
157+
func (b PdkBridge) AskNumber(method string, args proto.Message) (float64, error) {
158+
out := new(kong_plugin_protocol.Number)
159+
err := b.Ask(method, args, out)
160+
return out.V, err
161+
}
162+
163+
func (b PdkBridge) AskValue(method string, args proto.Message) (interface{}, error) {
164+
out := new(structpb.Value)
165+
err := b.Ask(method, args, out)
166+
if err != nil {
167+
return nil, err
168+
}
169+
170+
return out.AsInterface(), nil
171+
}
172+
173+
func (b PdkBridge) Close() error {
174+
return b.conn.Close()
147175
}
148176

149177
func ReturnTypeError(expected string) error {

bridge/bridge_test.go

Lines changed: 29 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,43 +3,38 @@ package bridge
33
import (
44
"testing"
55

6-
"github.com/Kong/go-pdk/entities"
7-
"github.com/stretchr/testify/assert"
6+
"github.com/Kong/go-pdk/bridge/bridgetest"
7+
"github.com/Kong/go-pdk/server/kong_plugin_protocol"
88
)
99

10-
var ch chan interface{}
11-
var bridge PdkBridge
1210

13-
func init() {
14-
ch = make(chan interface{})
15-
bridge = New(ch)
11+
func TestAsk(t *testing.T) {
12+
b := New(bridgetest.Mock(t, []bridgetest.MockStep{
13+
{"foo.bar", WrapString("first"), WrapString("resp")},
14+
}))
15+
16+
17+
out := new(kong_plugin_protocol.String)
18+
err := b.Ask("foo.bar", WrapString("first"), out)
19+
if err != nil {
20+
t.Fatalf("got this: %s", err)
21+
}
22+
if out.V != "resp" {
23+
t.Fatalf("no 'resp': %v", out.V)
24+
}
25+
b.Close()
1626
}
1727

18-
func TestAsk(t *testing.T) {
19-
go func() {
20-
bridge.Ask("foo.bar", 1, 2, 3, 1.23, false)
21-
}()
22-
23-
call := <-ch
24-
ch <- ""
25-
26-
assert.Equal(t, call, StepData{
27-
Method: "foo.bar",
28-
Args: []interface{}{1, 2, 3, 1.23, false},
29-
})
30-
31-
go func() {
32-
n := "gs"
33-
bridge.Ask("foo.bar", entities.Consumer{Username: n})
34-
}()
35-
36-
call = <-ch
37-
ch <- ""
38-
39-
n := "gs"
40-
consumer := []interface{}{entities.Consumer{Username: n}}
41-
assert.Equal(t, StepData{
42-
Method: "foo.bar",
43-
Args: consumer,
44-
}, call)
28+
func TestAskString(t *testing.T) {
29+
b := New(bridgetest.Mock(t, []bridgetest.MockStep{
30+
{"foo.bar", WrapString("first"), WrapString("resp")},
31+
}))
32+
33+
ret, err := b.AskString("foo.bar", WrapString("first"))
34+
if err != nil {
35+
t.Fatalf("got this: %s", err)
36+
}
37+
if ret != "resp" {
38+
t.Fatalf("no 'resp': %v", ret)
39+
}
4540
}

0 commit comments

Comments
 (0)