github.com/Kong/go-pdk@v0.11.0/server/pbserver.go (about)

     1  package server
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  	"io"
     7  	"log"
     8  	"net"
     9  
    10  	"github.com/Kong/go-pdk"
    11  	"github.com/Kong/go-pdk/server/kong_plugin_protocol"
    12  	"google.golang.org/protobuf/proto"
    13  )
    14  
    15  func servePb(conn net.Conn, rh *rpcHandler) {
    16  	var err error
    17  	var d, rd []byte
    18  	for {
    19  		d, err = readPbFrame(conn)
    20  		if err != nil {
    21  			break
    22  		}
    23  
    24  		rd, err = codecPb(rh, conn, d)
    25  		if err != nil {
    26  			break
    27  		}
    28  
    29  		err = writePbFrame(conn, rd)
    30  		if err != nil {
    31  			break
    32  		}
    33  	}
    34  
    35  	conn.Close()
    36  	if err != nil {
    37  		log.Print(err)
    38  	}
    39  }
    40  
    41  func readPbFrame(conn net.Conn) (data []byte, err error) {
    42  	var len uint32
    43  	err = binary.Read(conn, binary.LittleEndian, &len)
    44  	if err != nil {
    45  		return
    46  	}
    47  
    48  	data = make([]byte, len)
    49  
    50  	_, err = io.ReadFull(conn, data)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  
    55  	return
    56  }
    57  
    58  func writePbFrame(conn net.Conn, data []byte) (err error) {
    59  	var len uint32 = uint32(len(data))
    60  	err = binary.Write(conn, binary.LittleEndian, len)
    61  	if err != nil {
    62  		return
    63  	}
    64  
    65  	_, err = conn.Write(data)
    66  
    67  	return
    68  }
    69  
    70  func codecPb(rh *rpcHandler, conn net.Conn, data []byte) (retData []byte, err error) {
    71  	var m kong_plugin_protocol.RpcCall
    72  	err = proto.Unmarshal(data, &m)
    73  	if err != nil {
    74  		return
    75  	}
    76  
    77  	rm, err := handlePbCmd(rh, conn, &m)
    78  	if err != nil {
    79  		return
    80  	}
    81  
    82  	if rm != nil {
    83  		retData, err = proto.Marshal(rm)
    84  	}
    85  
    86  	return
    87  }
    88  
    89  func pbInstanceStatus(status InstanceStatus) *kong_plugin_protocol.RpcReturn_InstanceStatus {
    90  	return &kong_plugin_protocol.RpcReturn_InstanceStatus{
    91  		InstanceStatus: &kong_plugin_protocol.InstanceStatus{
    92  			Name:       status.Name,
    93  			InstanceId: int32(status.Id),
    94  			StartedAt:  status.StartTime,
    95  		},
    96  	}
    97  }
    98  
    99  func handlePbCmd(rh *rpcHandler, conn net.Conn, m *kong_plugin_protocol.RpcCall) (rm *kong_plugin_protocol.RpcReturn, err error) {
   100  	switch c := m.Call.(type) {
   101  	case *kong_plugin_protocol.RpcCall_CmdGetPluginNames:
   102  		// 		log.Printf("GetPluginNames: %v", c)
   103  
   104  	case *kong_plugin_protocol.RpcCall_CmdGetPluginInfo:
   105  		// 		log.Printf("GetPluginInfo: %v", c)
   106  
   107  	case *kong_plugin_protocol.RpcCall_CmdStartInstance:
   108  		config := PluginConfig{
   109  			Name:   c.CmdStartInstance.Name,
   110  			Config: c.CmdStartInstance.Config,
   111  		}
   112  		var status InstanceStatus
   113  		err = rh.StartInstance(config, &status)
   114  		if err != nil {
   115  			return
   116  		}
   117  
   118  		rm = &kong_plugin_protocol.RpcReturn{
   119  			Sequence: m.Sequence,
   120  			Return:   pbInstanceStatus(status),
   121  		}
   122  
   123  	case *kong_plugin_protocol.RpcCall_CmdGetInstanceStatus:
   124  		var status InstanceStatus
   125  		err = rh.InstanceStatus(int(c.CmdGetInstanceStatus.InstanceId), &status)
   126  		if err != nil {
   127  			return
   128  		}
   129  
   130  		rm = &kong_plugin_protocol.RpcReturn{
   131  			Sequence: m.Sequence,
   132  			Return:   pbInstanceStatus(status),
   133  		}
   134  
   135  	case *kong_plugin_protocol.RpcCall_CmdCloseInstance:
   136  		var status InstanceStatus
   137  		err = rh.CloseInstance(int(c.CmdCloseInstance.InstanceId), &status)
   138  		if err != nil {
   139  			return
   140  		}
   141  		rm = &kong_plugin_protocol.RpcReturn{
   142  			Sequence: m.Sequence,
   143  			Return:   pbInstanceStatus(status),
   144  		}
   145  
   146  	case *kong_plugin_protocol.RpcCall_CmdHandleEvent:
   147  		err = handlePbEvent(rh, conn, c.CmdHandleEvent)
   148  		rm = &kong_plugin_protocol.RpcReturn{
   149  			Sequence: m.Sequence,
   150  		}
   151  
   152  	default:
   153  		err = fmt.Errorf("RPC call has unexpected type %T", c)
   154  	}
   155  
   156  	return
   157  }
   158  
   159  func handlePbEvent(rh *rpcHandler, conn net.Conn, e *kong_plugin_protocol.CmdHandleEvent) error {
   160  	rh.lock.RLock()
   161  	instance, ok := rh.instances[int(e.InstanceId)]
   162  	rh.lock.RUnlock()
   163  	if !ok {
   164  		return fmt.Errorf("no plugin instance %d", e.InstanceId)
   165  	}
   166  
   167  	h, ok := instance.handlers[e.EventName]
   168  	if !ok {
   169  		return fmt.Errorf("undefined method %s", e.EventName)
   170  	}
   171  
   172  	pdk := pdk.Init(conn)
   173  
   174  	h(pdk)
   175  	return writePbFrame(conn, []byte{})
   176  }
   177  
   178  // Start the embedded plugin server, ProtoBuf version.
   179  // Handles CLI flags, and returns immediately if appropriate.
   180  // Otherwise, returns only if the server is stopped.
   181  func StartServer(constructor func() interface{}, version string, priority int) error {
   182  	parseCli()
   183  
   184  	rh := newRpcHandler(constructor, version, priority)
   185  
   186  	if *dump {
   187  		dumpInfo(rh)
   188  		return nil
   189  	}
   190  
   191  	listener, err := openSocket()
   192  	if err != nil {
   193  		return err
   194  	}
   195  	defer listener.Close()
   196  
   197  	for {
   198  		conn, err := listener.Accept()
   199  		if err != nil {
   200  			log.Fatal(err)
   201  		}
   202  
   203  		go servePb(conn, rh)
   204  	}
   205  }