go-ethereum/p2p/simulations/session_controller.go

252 lines
6.2 KiB
Go

package simulations
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"reflect"
"sync"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/p2p/adapters"
)
type returnHandler func(body io.Reader) (resp io.ReadSeeker, err error)
type ResourceHandler struct {
Handle func(interface{}, *ResourceController) (interface{}, error)
Type reflect.Type
}
type ResourceHandlers struct {
Create, Retrieve, Update, Destroy *ResourceHandler
}
type ResourceController struct {
lock sync.Mutex
controllers map[string]Controller
id int
methods []string
*ResourceHandlers
}
type NodeResult struct {
Nodes []*Node
}
type NodeIF struct {
One uint
Other uint
MessageType uint8
}
var methodsAvailable = []string{"POST", "GET", "PUT", "DELETE"}
func (self *ResourceHandlers) handler(method string) *ResourceHandler {
var h *ResourceHandler
switch method {
case "POST":
h = self.Create
case "GET":
h = self.Retrieve
case "PUT":
h = self.Update
case "DELETE":
h = self.Destroy
case "OPTIONS":
h = &ResourceHandler{
Handle: func(msg interface{}, c *ResourceController) (interface{}, error) {
return struct{}{}, nil
},
}
}
return h
}
func NewResourceContoller(c *ResourceHandlers) *ResourceController {
var methods []string
for _, method := range methodsAvailable {
if c.handler(method) != nil {
methods = append(methods, method)
}
}
return &ResourceController{
ResourceHandlers: c,
controllers: make(map[string]Controller),
methods: methods,
}
}
var empty = struct{}{}
func NewSessionController() (*ResourceController, chan bool) {
quitc := make(chan bool)
return NewResourceContoller(
&ResourceHandlers{
Create: &ResourceHandler{
Handle: func(msg interface{}, parent *ResourceController) (interface{}, error) {
conf := msg.(*NetworkConfig)
journal := NewJournal()
net := NewNetwork(nil, &event.TypeMux{})
net.SetNaf(net.NewGenericSimNode)
glog.V(logger.Info).Infof("new network controller on %v", conf.Id)
m := NewNetworkController(conf, net.Events(), journal)
if len(conf.Id) == 0 {
conf.Id = fmt.Sprintf("%d", parent.id)
}
if parent != nil {
parent.SetResource(conf.Id, m)
}
parent.id++
m.SetResource("debug", NewResourceContoller(
&ResourceHandlers{
Create: &ResourceHandler{
Handle: func(msg interface{}, parent *ResourceController) (interface{}, error) {
journaldump := []string{}
eventfmt := func(e *event.TypeMuxEvent) bool {
journaldump = append(journaldump, fmt.Sprintf("%v", e))
return true
}
journal.Read(eventfmt)
return struct{ Results []string }{Results: journaldump}, nil
},
},
},
))
m.SetResource("node", NewResourceContoller(
&ResourceHandlers{
Create: &ResourceHandler{
Handle: func(msg interface{}, parent *ResourceController) (interface{}, error) {
nodeid := adapters.RandomNodeId()
net.NewNode(&NodeConfig{Id: nodeid})
return &NodeConfig{Id: nodeid}, nil
},
},
Retrieve: &ResourceHandler{
Handle: func(msg interface{}, parent *ResourceController) (interface{}, error) {
return &NodeResult{Nodes: net.Nodes}, nil
},
},
Update: &ResourceHandler{
Handle: func(msg interface{}, parent *ResourceController) (interface{}, error) {
var othernode *Node
args := msg.(*NodeIF)
onenode := net.Nodes[args.One-1]
if args.Other == 0 {
if net.Start(onenode.Id) != nil {
net.Stop(onenode.Id)
}
return &NodeResult{Nodes: []*Node{onenode}}, nil
} else {
othernode = net.Nodes[args.Other-1]
net.Connect(onenode.Id, othernode.Id)
return &NodeResult{Nodes: []*Node{onenode, othernode}}, nil
}
},
Type: reflect.TypeOf(&NodeIF{}), // this is input not output param structure
},
},
))
return empty, nil
},
Type: reflect.TypeOf(&NetworkConfig{}),
},
Destroy: &ResourceHandler{
Handle: func(msg interface{}, parent *ResourceController) (interface{}, error) {
glog.V(logger.Debug).Infof("destroy handler called")
// this can quit the entire app (shut down the backend server)
quitc <- true
return empty, nil
},
},
},
), quitc
}
func (self *ResourceController) Handle(method string) (returnHandler, error) {
h := self.handler(method)
if h == nil {
return nil, fmt.Errorf("allowed methods: %v", self.methods)
}
rh := func(r io.Reader) (io.ReadSeeker, error) {
input, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
var arg interface{}
if len(input) == 0 {
input = []byte("{}")
}
if h.Type != nil {
val := reflect.New(h.Type)
req := val.Elem()
req.Set(reflect.Zero(h.Type))
err = json.Unmarshal(input, val.Interface())
if err != nil {
return nil, err
}
arg = req.Interface()
}
res, err := h.Handle(arg, self)
if err != nil {
return nil, err
}
resp, err := json.MarshalIndent(res, "", " ")
return bytes.NewReader(resp), nil
}
return rh, nil
}
func (self *ResourceController) Resource(id string) (Controller, error) {
self.lock.Lock()
defer self.lock.Unlock()
c, ok := self.controllers[id]
if !ok {
return nil, fmt.Errorf("not found")
}
return c, nil
}
func (self *ResourceController) SetResource(id string, c Controller) {
self.lock.Lock()
defer self.lock.Unlock()
if c == nil {
delete(self.controllers, id)
} else {
self.controllers[id] = c
}
}
func (self *ResourceController) DeleteResource(id string) {
delete(self.controllers, id)
}
func RandomNodeId() *adapters.NodeId {
key, err := crypto.GenerateKey()
if err != nil {
panic("unable to generate key")
}
pubkey := crypto.FromECDSAPub(&key.PublicKey)
return adapters.NewNodeId(pubkey[1:])
}
func RandomNodeIds(n int) []*adapters.NodeId {
var ids []*adapters.NodeId
for i := 0; i < n; i++ {
ids = append(ids, RandomNodeId())
}
return ids
}