mirror of
https://github.com/beego/bee.git
synced 2025-06-24 00:00:18 +00:00
Update vendor folder (support for Delve RPC)
This commit is contained in:
409
vendor/github.com/derekparker/delve/service/rpccommon/server.go
generated
vendored
Normal file
409
vendor/github.com/derekparker/delve/service/rpccommon/server.go
generated
vendored
Normal file
@ -0,0 +1,409 @@
|
||||
package rpccommon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/rpc"
|
||||
"net/rpc/jsonrpc"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sync"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/derekparker/delve/pkg/version"
|
||||
"github.com/derekparker/delve/service"
|
||||
"github.com/derekparker/delve/service/api"
|
||||
"github.com/derekparker/delve/service/debugger"
|
||||
"github.com/derekparker/delve/service/rpc1"
|
||||
"github.com/derekparker/delve/service/rpc2"
|
||||
)
|
||||
|
||||
// ServerImpl implements a JSON-RPC server that can switch between two
|
||||
// versions of the API.
|
||||
type ServerImpl struct {
|
||||
// config is all the information necessary to start the debugger and server.
|
||||
config *service.Config
|
||||
// listener is used to serve HTTP.
|
||||
listener net.Listener
|
||||
// stopChan is used to stop the listener goroutine.
|
||||
stopChan chan struct{}
|
||||
// debugger is the debugger service.
|
||||
debugger *debugger.Debugger
|
||||
// s1 is APIv1 server.
|
||||
s1 *rpc1.RPCServer
|
||||
// s2 is APIv2 server.
|
||||
s2 *rpc2.RPCServer
|
||||
// maps of served methods, one for each supported API.
|
||||
methodMaps []map[string]*methodType
|
||||
}
|
||||
|
||||
type RPCCallback struct {
|
||||
s *ServerImpl
|
||||
sending *sync.Mutex
|
||||
codec rpc.ServerCodec
|
||||
req rpc.Request
|
||||
}
|
||||
|
||||
// RPCServer implements the RPC method calls common to all versions of the API.
|
||||
type RPCServer struct {
|
||||
s *ServerImpl
|
||||
}
|
||||
|
||||
type methodType struct {
|
||||
method reflect.Method
|
||||
Rcvr reflect.Value
|
||||
ArgType reflect.Type
|
||||
ReplyType reflect.Type
|
||||
Synchronous bool
|
||||
}
|
||||
|
||||
// NewServer creates a new RPCServer.
|
||||
func NewServer(config *service.Config, logEnabled bool) *ServerImpl {
|
||||
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
|
||||
if !logEnabled {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
}
|
||||
if config.APIVersion < 2 {
|
||||
log.Printf("Using API v1")
|
||||
}
|
||||
return &ServerImpl{
|
||||
config: config,
|
||||
listener: config.Listener,
|
||||
stopChan: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Stop stops the JSON-RPC server.
|
||||
func (s *ServerImpl) Stop(kill bool) error {
|
||||
if s.config.AcceptMulti {
|
||||
close(s.stopChan)
|
||||
s.listener.Close()
|
||||
}
|
||||
return s.debugger.Detach(kill)
|
||||
}
|
||||
|
||||
// Restart restarts the debugger.
|
||||
func (s *ServerImpl) Restart() error {
|
||||
if s.config.AttachPid != 0 {
|
||||
return errors.New("cannot restart process Delve did not create")
|
||||
}
|
||||
return s.s2.Restart(rpc2.RestartIn{}, nil)
|
||||
}
|
||||
|
||||
// Run starts a debugger and exposes it with an HTTP server. The debugger
|
||||
// itself can be stopped with the `detach` API. Run blocks until the HTTP
|
||||
// server stops.
|
||||
func (s *ServerImpl) Run() error {
|
||||
var err error
|
||||
|
||||
if s.config.APIVersion < 2 {
|
||||
s.config.APIVersion = 1
|
||||
}
|
||||
if s.config.APIVersion > 2 {
|
||||
return fmt.Errorf("unknown API version")
|
||||
}
|
||||
|
||||
// Create and start the debugger
|
||||
if s.debugger, err = debugger.New(&debugger.Config{
|
||||
ProcessArgs: s.config.ProcessArgs,
|
||||
AttachPid: s.config.AttachPid,
|
||||
WorkingDir: s.config.WorkingDir,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.s1 = rpc1.NewServer(s.config, s.debugger)
|
||||
s.s2 = rpc2.NewServer(s.config, s.debugger)
|
||||
|
||||
rpcServer := &RPCServer{s}
|
||||
|
||||
s.methodMaps = make([]map[string]*methodType, 2)
|
||||
|
||||
s.methodMaps[0] = map[string]*methodType{}
|
||||
s.methodMaps[1] = map[string]*methodType{}
|
||||
suitableMethods(s.s1, s.methodMaps[0])
|
||||
suitableMethods(rpcServer, s.methodMaps[0])
|
||||
suitableMethods(s.s2, s.methodMaps[1])
|
||||
suitableMethods(rpcServer, s.methodMaps[1])
|
||||
|
||||
go func() {
|
||||
defer s.listener.Close()
|
||||
for {
|
||||
c, err := s.listener.Accept()
|
||||
if err != nil {
|
||||
select {
|
||||
case <-s.stopChan:
|
||||
// We were supposed to exit, do nothing and return
|
||||
return
|
||||
default:
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
go s.serveJSONCodec(c)
|
||||
if !s.config.AcceptMulti {
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Precompute the reflect type for error. Can't use error directly
|
||||
// because Typeof takes an empty interface value. This is annoying.
|
||||
var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
|
||||
|
||||
// Is this an exported - upper case - name?
|
||||
func isExported(name string) bool {
|
||||
rune, _ := utf8.DecodeRuneInString(name)
|
||||
return unicode.IsUpper(rune)
|
||||
}
|
||||
|
||||
// Is this type exported or a builtin?
|
||||
func isExportedOrBuiltinType(t reflect.Type) bool {
|
||||
for t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
// PkgPath will be non-empty even for an exported type,
|
||||
// so we need to check the type name as well.
|
||||
return isExported(t.Name()) || t.PkgPath() == ""
|
||||
}
|
||||
|
||||
// Fills methods map with the methods of receiver that should be made
|
||||
// available through the RPC interface.
|
||||
// These are all the public methods of rcvr that have one of those
|
||||
// two signatures:
|
||||
// func (rcvr ReceiverType) Method(in InputType, out *ReplyType) error
|
||||
// func (rcvr ReceiverType) Method(in InputType, cb service.RPCCallback)
|
||||
func suitableMethods(rcvr interface{}, methods map[string]*methodType) {
|
||||
typ := reflect.TypeOf(rcvr)
|
||||
rcvrv := reflect.ValueOf(rcvr)
|
||||
sname := reflect.Indirect(rcvrv).Type().Name()
|
||||
if sname == "" {
|
||||
log.Printf("rpc.Register: no service name for type %s", typ)
|
||||
return
|
||||
}
|
||||
for m := 0; m < typ.NumMethod(); m++ {
|
||||
method := typ.Method(m)
|
||||
mname := method.Name
|
||||
mtype := method.Type
|
||||
// method must be exported
|
||||
if method.PkgPath != "" {
|
||||
continue
|
||||
}
|
||||
// Method needs three ins: (receive, *args, *reply) or (receiver, *args, *RPCCallback)
|
||||
if mtype.NumIn() != 3 {
|
||||
log.Println("method", mname, "has wrong number of ins:", mtype.NumIn())
|
||||
continue
|
||||
}
|
||||
// First arg need not be a pointer.
|
||||
argType := mtype.In(1)
|
||||
if !isExportedOrBuiltinType(argType) {
|
||||
log.Println(mname, "argument type not exported:", argType)
|
||||
continue
|
||||
}
|
||||
|
||||
replyType := mtype.In(2)
|
||||
synchronous := replyType.String() != "service.RPCCallback"
|
||||
|
||||
if synchronous {
|
||||
// Second arg must be a pointer.
|
||||
if replyType.Kind() != reflect.Ptr {
|
||||
log.Println("method", mname, "reply type not a pointer:", replyType)
|
||||
continue
|
||||
}
|
||||
// Reply type must be exported.
|
||||
if !isExportedOrBuiltinType(replyType) {
|
||||
log.Println("method", mname, "reply type not exported:", replyType)
|
||||
continue
|
||||
}
|
||||
|
||||
// Method needs one out.
|
||||
if mtype.NumOut() != 1 {
|
||||
log.Println("method", mname, "has wrong number of outs:", mtype.NumOut())
|
||||
continue
|
||||
}
|
||||
// The return type of the method must be error.
|
||||
if returnType := mtype.Out(0); returnType != typeOfError {
|
||||
log.Println("method", mname, "returns", returnType.String(), "not error")
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// Method needs zero outs.
|
||||
if mtype.NumOut() != 0 {
|
||||
log.Println("method", mname, "has wrong number of outs:", mtype.NumOut())
|
||||
continue
|
||||
}
|
||||
}
|
||||
methods[sname+"."+mname] = &methodType{method: method, ArgType: argType, ReplyType: replyType, Synchronous: synchronous, Rcvr: rcvrv}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *ServerImpl) serveJSONCodec(conn io.ReadWriteCloser) {
|
||||
sending := new(sync.Mutex)
|
||||
codec := jsonrpc.NewServerCodec(conn)
|
||||
var req rpc.Request
|
||||
var resp rpc.Response
|
||||
for {
|
||||
req = rpc.Request{}
|
||||
err := codec.ReadRequestHeader(&req)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
log.Println("rpc:", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
mtype, ok := s.methodMaps[s.config.APIVersion-1][req.ServiceMethod]
|
||||
if !ok {
|
||||
log.Printf("rpc: can't find method %s", req.ServiceMethod)
|
||||
continue
|
||||
}
|
||||
|
||||
var argv, replyv reflect.Value
|
||||
|
||||
// Decode the argument value.
|
||||
argIsValue := false // if true, need to indirect before calling.
|
||||
if mtype.ArgType.Kind() == reflect.Ptr {
|
||||
argv = reflect.New(mtype.ArgType.Elem())
|
||||
} else {
|
||||
argv = reflect.New(mtype.ArgType)
|
||||
argIsValue = true
|
||||
}
|
||||
// argv guaranteed to be a pointer now.
|
||||
if err = codec.ReadRequestBody(argv.Interface()); err != nil {
|
||||
return
|
||||
}
|
||||
if argIsValue {
|
||||
argv = argv.Elem()
|
||||
}
|
||||
|
||||
if mtype.Synchronous {
|
||||
replyv = reflect.New(mtype.ReplyType.Elem())
|
||||
function := mtype.method.Func
|
||||
var returnValues []reflect.Value
|
||||
var errInter interface{}
|
||||
func() {
|
||||
defer func() {
|
||||
if ierr := recover(); ierr != nil {
|
||||
errInter = newInternalError(ierr, 2)
|
||||
}
|
||||
}()
|
||||
returnValues = function.Call([]reflect.Value{mtype.Rcvr, argv, replyv})
|
||||
errInter = returnValues[0].Interface()
|
||||
}()
|
||||
|
||||
errmsg := ""
|
||||
if errInter != nil {
|
||||
errmsg = errInter.(error).Error()
|
||||
}
|
||||
resp = rpc.Response{}
|
||||
s.sendResponse(sending, &req, &resp, replyv.Interface(), codec, errmsg)
|
||||
} else {
|
||||
function := mtype.method.Func
|
||||
ctl := &RPCCallback{s, sending, codec, req}
|
||||
go func() {
|
||||
defer func() {
|
||||
if ierr := recover(); ierr != nil {
|
||||
ctl.Return(nil, newInternalError(ierr, 2))
|
||||
}
|
||||
}()
|
||||
function.Call([]reflect.Value{mtype.Rcvr, argv, reflect.ValueOf(ctl)})
|
||||
}()
|
||||
}
|
||||
}
|
||||
codec.Close()
|
||||
}
|
||||
|
||||
// A value sent as a placeholder for the server's response value when the server
|
||||
// receives an invalid request. It is never decoded by the client since the Response
|
||||
// contains an error when it is used.
|
||||
var invalidRequest = struct{}{}
|
||||
|
||||
func (s *ServerImpl) sendResponse(sending *sync.Mutex, req *rpc.Request, resp *rpc.Response, reply interface{}, codec rpc.ServerCodec, errmsg string) {
|
||||
resp.ServiceMethod = req.ServiceMethod
|
||||
if errmsg != "" {
|
||||
resp.Error = errmsg
|
||||
reply = invalidRequest
|
||||
}
|
||||
resp.Seq = req.Seq
|
||||
sending.Lock()
|
||||
defer sending.Unlock()
|
||||
err := codec.WriteResponse(resp, reply)
|
||||
if err != nil {
|
||||
log.Println("rpc: writing response:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (cb *RPCCallback) Return(out interface{}, err error) {
|
||||
errmsg := ""
|
||||
if err != nil {
|
||||
errmsg = err.Error()
|
||||
}
|
||||
var resp rpc.Response
|
||||
cb.s.sendResponse(cb.sending, &cb.req, &resp, out, cb.codec, errmsg)
|
||||
}
|
||||
|
||||
// GetVersion returns the version of delve as well as the API version
|
||||
// currently served.
|
||||
func (s *RPCServer) GetVersion(args api.GetVersionIn, out *api.GetVersionOut) error {
|
||||
out.DelveVersion = version.DelveVersion.String()
|
||||
out.APIVersion = s.s.config.APIVersion
|
||||
return nil
|
||||
}
|
||||
|
||||
// Changes version of the API being served.
|
||||
func (s *RPCServer) SetApiVersion(args api.SetAPIVersionIn, out *api.SetAPIVersionOut) error {
|
||||
if args.APIVersion < 2 {
|
||||
args.APIVersion = 1
|
||||
}
|
||||
if args.APIVersion > 2 {
|
||||
return fmt.Errorf("unknown API version")
|
||||
}
|
||||
s.s.config.APIVersion = args.APIVersion
|
||||
return nil
|
||||
}
|
||||
|
||||
type internalError struct {
|
||||
Err interface{}
|
||||
Stack []internalErrorFrame
|
||||
}
|
||||
|
||||
type internalErrorFrame struct {
|
||||
Pc uintptr
|
||||
Func string
|
||||
File string
|
||||
Line int
|
||||
}
|
||||
|
||||
func newInternalError(ierr interface{}, skip int) *internalError {
|
||||
r := &internalError{ierr, nil}
|
||||
for i := skip; ; i++ {
|
||||
pc, file, line, ok := runtime.Caller(i)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
fname := "<unknown>"
|
||||
fn := runtime.FuncForPC(pc)
|
||||
if fn != nil {
|
||||
fname = fn.Name()
|
||||
}
|
||||
r.Stack = append(r.Stack, internalErrorFrame{pc, fname, file, line})
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (err *internalError) Error() string {
|
||||
var out bytes.Buffer
|
||||
fmt.Fprintf(&out, "Internal debugger error: %v\n", err.Err)
|
||||
for _, frame := range err.Stack {
|
||||
fmt.Fprintf(&out, "%s (%#x)\n\t%s:%d\n", frame.Func, frame.Pc, frame.File, frame.Line)
|
||||
}
|
||||
return out.String()
|
||||
}
|
Reference in New Issue
Block a user