mirror of
https://github.com/Kong/go-pluginserver.git
synced 2025-10-05 16:17:01 +08:00
feat(*) initial commit
msgpackrpc and plugin loader (copied from existing runtime)
This commit is contained in:
236
pluginserver.go
Normal file
236
pluginserver.go
Normal file
@@ -0,0 +1,236 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/kong/go-pdk"
|
||||
"github.com/ugorji/go/codec"
|
||||
"log"
|
||||
"net"
|
||||
"net/rpc"
|
||||
"path"
|
||||
"plugin"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var socket = flag.String("socket", "", "Socket to listen into")
|
||||
|
||||
func runServer(listener net.Listener) {
|
||||
var handle codec.MsgpackHandle
|
||||
|
||||
go func() {
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
log.Printf("accept(): %s", err)
|
||||
return
|
||||
}
|
||||
// rpcCodec := codec.GoRpc.ServerCodec(conn, &handle)
|
||||
rpcCodec := codec.MsgpackSpecRpc.ServerCodec(conn, &handle)
|
||||
rpc.ServeCodec(rpcCodec)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if *socket != "" {
|
||||
listener, err := net.Listen("unix", *socket)
|
||||
if err != nil {
|
||||
log.Printf(`listen("%s"): %s`, socket, err)
|
||||
return
|
||||
}
|
||||
|
||||
rpc.RegisterName("plugin", &PluginServer{})
|
||||
|
||||
runServer(listener)
|
||||
}
|
||||
}
|
||||
|
||||
// --- pluginData --- //
|
||||
type pluginData struct {
|
||||
code *plugin.Plugin
|
||||
initialized bool
|
||||
config interface{}
|
||||
handlers map[string]func(kong *pdk.PDK)
|
||||
}
|
||||
|
||||
type (
|
||||
certificater interface{ Certificate(*pdk.PDK) }
|
||||
rewriter interface{ Rewrite(*pdk.PDK) }
|
||||
accesser interface{ Access(*pdk.PDK) }
|
||||
headerFilter interface{ HeaderFilter(*pdk.PDK) }
|
||||
bodyFilter interface{ BodyFilter(*pdk.PDK) }
|
||||
prereader interface{ Preread(*pdk.PDK) }
|
||||
logger interface{ Log(*pdk.PDK) }
|
||||
)
|
||||
|
||||
func (plug *pluginData) setHandlers() {
|
||||
handlers := map[string]func(kong *pdk.PDK){}
|
||||
config := plug.config
|
||||
|
||||
if h, ok := config.(certificater); ok { handlers["certificate"] = h.Certificate }
|
||||
if h, ok := config.(rewriter) ; ok { handlers["rewrite"] = h.Rewrite }
|
||||
if h, ok := config.(accesser) ; ok { handlers["access"] = h.Access }
|
||||
if h, ok := config.(headerFilter); ok { handlers["header_filter"] = h.HeaderFilter }
|
||||
if h, ok := config.(bodyFilter) ; ok { handlers["body_filter"] = h.BodyFilter }
|
||||
if h, ok := config.(prereader) ; ok { handlers["preread"] = h.Preread }
|
||||
if h, ok := config.(logger) ; ok { handlers["log"] = h.Log }
|
||||
|
||||
plug.handlers = handlers
|
||||
}
|
||||
|
||||
// --- PluginServer --- //
|
||||
type PluginServer struct {
|
||||
pluginsDir string
|
||||
plugins map[string]pluginData
|
||||
}
|
||||
|
||||
/// exported method
|
||||
func (s *PluginServer) SetPluginDir(dir string, reply *string) error {
|
||||
s.pluginsDir = dir
|
||||
*reply = "ok"
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s PluginServer) loadPlugin(name string) (plug pluginData, err error) {
|
||||
plug, ok := s.plugins[name]
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
|
||||
code, err := plugin.Open(path.Join(s.pluginsDir, name+".so"))
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to open plugin %s: %w", name, err)
|
||||
return
|
||||
}
|
||||
|
||||
constructorSymbol, err := code.Lookup("New")
|
||||
if err != nil {
|
||||
err = fmt.Errorf("No constructor function on plugin %s: %w", name, err)
|
||||
return
|
||||
}
|
||||
constructor, ok := constructorSymbol.(func() interface{})
|
||||
if !ok {
|
||||
err = fmt.Errorf("Wrong constructor signature on plugin %s: %w", name, err)
|
||||
return
|
||||
}
|
||||
|
||||
plug = pluginData{
|
||||
code: code,
|
||||
config: constructor(),
|
||||
}
|
||||
plug.setHandlers()
|
||||
|
||||
s.plugins[name] = plug
|
||||
return
|
||||
}
|
||||
|
||||
func getSchemaType(t reflect.Type) (string, bool) {
|
||||
//log.Printf("SCHEMA TYPE FOR T IS %s\n", t.String())
|
||||
switch t.Kind() {
|
||||
case reflect.String:
|
||||
return `"string"`, true
|
||||
case reflect.Bool:
|
||||
return `"boolean"`, true
|
||||
case reflect.Int, reflect.Int32:
|
||||
return `"integer"`, true
|
||||
case reflect.Uint, reflect.Uint32:
|
||||
return `"integer","between":[0,2147483648]`, true
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return `"number"`, true
|
||||
case reflect.Array:
|
||||
elemType, ok := getSchemaType(t.Elem())
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
return `"array","elements":{"type":` + elemType + `}`, true
|
||||
case reflect.Map:
|
||||
kType, ok := getSchemaType(t.Key())
|
||||
vType, ok := getSchemaType(t.Elem())
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
return `"map","keys":{"type":` + kType + `},"values":{"type":` + vType + `}`, true
|
||||
case reflect.Struct:
|
||||
var out strings.Builder
|
||||
out.WriteString(`"record","fields":[`)
|
||||
n := t.NumField()
|
||||
for i := 0; i < n; i++ {
|
||||
field := t.Field(i)
|
||||
typeDecl, ok := getSchemaType(field.Type)
|
||||
if !ok {
|
||||
// ignore unrepresentable types
|
||||
continue
|
||||
}
|
||||
if i > 0 {
|
||||
out.WriteString(`,`)
|
||||
}
|
||||
name := field.Tag.Get("json")
|
||||
if name == "" {
|
||||
name = strings.ToLower(field.Name)
|
||||
}
|
||||
out.WriteString(`{"`)
|
||||
out.WriteString(name)
|
||||
out.WriteString(`":{"type":`)
|
||||
out.WriteString(typeDecl)
|
||||
out.WriteString(`}}`)
|
||||
}
|
||||
out.WriteString(`]`)
|
||||
return out.String(), true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
type PluginInfo struct {
|
||||
name string
|
||||
phases []string
|
||||
version string
|
||||
priority int
|
||||
schema string
|
||||
}
|
||||
|
||||
/// exported method
|
||||
func (s *PluginServer) GetPluginInfo(name string, info *PluginInfo) error {
|
||||
info.name = name
|
||||
|
||||
plug, err := s.loadPlugin(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*info = PluginInfo{}
|
||||
|
||||
info.phases = make([]string, len(plug.handlers))
|
||||
var i = 0
|
||||
for name := range plug.handlers {
|
||||
info.phases[i] = name
|
||||
i++
|
||||
}
|
||||
|
||||
v, _ := plug.code.Lookup("Version")
|
||||
if v != nil {
|
||||
info.version = v.(string)
|
||||
}
|
||||
|
||||
prio, _ := plug.code.Lookup("Priority")
|
||||
if prio != nil {
|
||||
info.priority = prio.(int)
|
||||
}
|
||||
|
||||
var out strings.Builder
|
||||
out.WriteString(`{"name":"`)
|
||||
out.WriteString(name)
|
||||
out.WriteString(`","fields":[{"config":{"type":`)
|
||||
|
||||
st, _ := getSchemaType(reflect.TypeOf(plug.config).Elem())
|
||||
out.WriteString(st)
|
||||
|
||||
out.WriteString(`}}]}`)
|
||||
|
||||
info.schema = out.String()
|
||||
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user