github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/network/p2p/simulations/http.go (about)

     1  package simulations
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"context"
     7  	"encoding/json"
     8  	"fmt"
     9  	"io"
    10  	"io/ioutil"
    11  	"net/http"
    12  	"strconv"
    13  	"strings"
    14  	"sync"
    15  
    16  	"github.com/julienschmidt/httprouter"
    17  	"github.com/neatlab/neatio/network/p2p"
    18  	"github.com/neatlab/neatio/network/p2p/discover"
    19  	"github.com/neatlab/neatio/network/p2p/simulations/adapters"
    20  	"github.com/neatlab/neatio/network/rpc"
    21  	"github.com/neatlab/neatio/utilities/event"
    22  	"golang.org/x/net/websocket"
    23  )
    24  
    25  var DefaultClient = NewClient("http://localhost:8888")
    26  
    27  type Client struct {
    28  	URL string
    29  
    30  	client *http.Client
    31  }
    32  
    33  func NewClient(url string) *Client {
    34  	return &Client{
    35  		URL:    url,
    36  		client: http.DefaultClient,
    37  	}
    38  }
    39  
    40  func (c *Client) GetNetwork() (*Network, error) {
    41  	network := &Network{}
    42  	return network, c.Get("/", network)
    43  }
    44  
    45  func (c *Client) StartNetwork() error {
    46  	return c.Post("/start", nil, nil)
    47  }
    48  
    49  func (c *Client) StopNetwork() error {
    50  	return c.Post("/stop", nil, nil)
    51  }
    52  
    53  func (c *Client) CreateSnapshot() (*Snapshot, error) {
    54  	snap := &Snapshot{}
    55  	return snap, c.Get("/snapshot", snap)
    56  }
    57  
    58  func (c *Client) LoadSnapshot(snap *Snapshot) error {
    59  	return c.Post("/snapshot", snap, nil)
    60  }
    61  
    62  type SubscribeOpts struct {
    63  	Current bool
    64  
    65  	Filter string
    66  }
    67  
    68  func (c *Client) SubscribeNetwork(events chan *Event, opts SubscribeOpts) (event.Subscription, error) {
    69  	url := fmt.Sprintf("%s/events?current=%t&filter=%s", c.URL, opts.Current, opts.Filter)
    70  	req, err := http.NewRequest("GET", url, nil)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	req.Header.Set("Accept", "text/event-stream")
    75  	res, err := c.client.Do(req)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	if res.StatusCode != http.StatusOK {
    80  		response, _ := ioutil.ReadAll(res.Body)
    81  		res.Body.Close()
    82  		return nil, fmt.Errorf("unexpected HTTP status: %s: %s", res.Status, response)
    83  	}
    84  
    85  	producer := func(stop <-chan struct{}) error {
    86  		defer res.Body.Close()
    87  
    88  		lines := make(chan string)
    89  		errC := make(chan error, 1)
    90  		go func() {
    91  			s := bufio.NewScanner(res.Body)
    92  			for s.Scan() {
    93  				select {
    94  				case lines <- s.Text():
    95  				case <-stop:
    96  					return
    97  				}
    98  			}
    99  			errC <- s.Err()
   100  		}()
   101  
   102  		for {
   103  			select {
   104  			case line := <-lines:
   105  				if !strings.HasPrefix(line, "data:") {
   106  					continue
   107  				}
   108  				data := strings.TrimSpace(strings.TrimPrefix(line, "data:"))
   109  				event := &Event{}
   110  				if err := json.Unmarshal([]byte(data), event); err != nil {
   111  					return fmt.Errorf("error decoding SSE event: %s", err)
   112  				}
   113  				select {
   114  				case events <- event:
   115  				case <-stop:
   116  					return nil
   117  				}
   118  			case err := <-errC:
   119  				return err
   120  			case <-stop:
   121  				return nil
   122  			}
   123  		}
   124  	}
   125  
   126  	return event.NewSubscription(producer), nil
   127  }
   128  
   129  func (c *Client) GetNodes() ([]*p2p.NodeInfo, error) {
   130  	var nodes []*p2p.NodeInfo
   131  	return nodes, c.Get("/nodes", &nodes)
   132  }
   133  
   134  func (c *Client) CreateNode(config *adapters.NodeConfig) (*p2p.NodeInfo, error) {
   135  	node := &p2p.NodeInfo{}
   136  	return node, c.Post("/nodes", config, node)
   137  }
   138  
   139  func (c *Client) GetNode(nodeID string) (*p2p.NodeInfo, error) {
   140  	node := &p2p.NodeInfo{}
   141  	return node, c.Get(fmt.Sprintf("/nodes/%s", nodeID), node)
   142  }
   143  
   144  func (c *Client) StartNode(nodeID string) error {
   145  	return c.Post(fmt.Sprintf("/nodes/%s/start", nodeID), nil, nil)
   146  }
   147  
   148  func (c *Client) StopNode(nodeID string) error {
   149  	return c.Post(fmt.Sprintf("/nodes/%s/stop", nodeID), nil, nil)
   150  }
   151  
   152  func (c *Client) ConnectNode(nodeID, peerID string) error {
   153  	return c.Post(fmt.Sprintf("/nodes/%s/conn/%s", nodeID, peerID), nil, nil)
   154  }
   155  
   156  func (c *Client) DisconnectNode(nodeID, peerID string) error {
   157  	return c.Delete(fmt.Sprintf("/nodes/%s/conn/%s", nodeID, peerID))
   158  }
   159  
   160  func (c *Client) RPCClient(ctx context.Context, nodeID string) (*rpc.Client, error) {
   161  	baseURL := strings.Replace(c.URL, "http", "ws", 1)
   162  	return rpc.DialWebsocket(ctx, fmt.Sprintf("%s/nodes/%s/rpc", baseURL, nodeID), "")
   163  }
   164  
   165  func (c *Client) Get(path string, out interface{}) error {
   166  	return c.Send("GET", path, nil, out)
   167  }
   168  
   169  func (c *Client) Post(path string, in, out interface{}) error {
   170  	return c.Send("POST", path, in, out)
   171  }
   172  
   173  func (c *Client) Delete(path string) error {
   174  	return c.Send("DELETE", path, nil, nil)
   175  }
   176  
   177  func (c *Client) Send(method, path string, in, out interface{}) error {
   178  	var body []byte
   179  	if in != nil {
   180  		var err error
   181  		body, err = json.Marshal(in)
   182  		if err != nil {
   183  			return err
   184  		}
   185  	}
   186  	req, err := http.NewRequest(method, c.URL+path, bytes.NewReader(body))
   187  	if err != nil {
   188  		return err
   189  	}
   190  	req.Header.Set("Content-Type", "application/json")
   191  	req.Header.Set("Accept", "application/json")
   192  	res, err := c.client.Do(req)
   193  	if err != nil {
   194  		return err
   195  	}
   196  	defer res.Body.Close()
   197  	if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusCreated {
   198  		response, _ := ioutil.ReadAll(res.Body)
   199  		return fmt.Errorf("unexpected HTTP status: %s: %s", res.Status, response)
   200  	}
   201  	if out != nil {
   202  		if err := json.NewDecoder(res.Body).Decode(out); err != nil {
   203  			return err
   204  		}
   205  	}
   206  	return nil
   207  }
   208  
   209  type Server struct {
   210  	router     *httprouter.Router
   211  	network    *Network
   212  	mockerStop chan struct{}
   213  	mockerMtx  sync.Mutex
   214  }
   215  
   216  func NewServer(network *Network) *Server {
   217  	s := &Server{
   218  		router:  httprouter.New(),
   219  		network: network,
   220  	}
   221  
   222  	s.OPTIONS("/", s.Options)
   223  	s.GET("/", s.GetNetwork)
   224  	s.POST("/start", s.StartNetwork)
   225  	s.POST("/stop", s.StopNetwork)
   226  	s.POST("/mocker/start", s.StartMocker)
   227  	s.POST("/mocker/stop", s.StopMocker)
   228  	s.GET("/mocker", s.GetMockers)
   229  	s.POST("/reset", s.ResetNetwork)
   230  	s.GET("/events", s.StreamNetworkEvents)
   231  	s.GET("/snapshot", s.CreateSnapshot)
   232  	s.POST("/snapshot", s.LoadSnapshot)
   233  	s.POST("/nodes", s.CreateNode)
   234  	s.GET("/nodes", s.GetNodes)
   235  	s.GET("/nodes/:nodeid", s.GetNode)
   236  	s.POST("/nodes/:nodeid/start", s.StartNode)
   237  	s.POST("/nodes/:nodeid/stop", s.StopNode)
   238  	s.POST("/nodes/:nodeid/conn/:peerid", s.ConnectNode)
   239  	s.DELETE("/nodes/:nodeid/conn/:peerid", s.DisconnectNode)
   240  	s.GET("/nodes/:nodeid/rpc", s.NodeRPC)
   241  
   242  	return s
   243  }
   244  
   245  func (s *Server) GetNetwork(w http.ResponseWriter, req *http.Request) {
   246  	s.JSON(w, http.StatusOK, s.network)
   247  }
   248  
   249  func (s *Server) StartNetwork(w http.ResponseWriter, req *http.Request) {
   250  	if err := s.network.StartAll(); err != nil {
   251  		http.Error(w, err.Error(), http.StatusInternalServerError)
   252  		return
   253  	}
   254  
   255  	w.WriteHeader(http.StatusOK)
   256  }
   257  
   258  func (s *Server) StopNetwork(w http.ResponseWriter, req *http.Request) {
   259  	if err := s.network.StopAll(); err != nil {
   260  		http.Error(w, err.Error(), http.StatusInternalServerError)
   261  		return
   262  	}
   263  
   264  	w.WriteHeader(http.StatusOK)
   265  }
   266  
   267  func (s *Server) StartMocker(w http.ResponseWriter, req *http.Request) {
   268  	s.mockerMtx.Lock()
   269  	defer s.mockerMtx.Unlock()
   270  	if s.mockerStop != nil {
   271  		http.Error(w, "mocker already running", http.StatusInternalServerError)
   272  		return
   273  	}
   274  	mockerType := req.FormValue("mocker-type")
   275  	mockerFn := LookupMocker(mockerType)
   276  	if mockerFn == nil {
   277  		http.Error(w, fmt.Sprintf("unknown mocker type %q", mockerType), http.StatusBadRequest)
   278  		return
   279  	}
   280  	nodeCount, err := strconv.Atoi(req.FormValue("node-count"))
   281  	if err != nil {
   282  		http.Error(w, "invalid node-count provided", http.StatusBadRequest)
   283  		return
   284  	}
   285  	s.mockerStop = make(chan struct{})
   286  	go mockerFn(s.network, s.mockerStop, nodeCount)
   287  
   288  	w.WriteHeader(http.StatusOK)
   289  }
   290  
   291  func (s *Server) StopMocker(w http.ResponseWriter, req *http.Request) {
   292  	s.mockerMtx.Lock()
   293  	defer s.mockerMtx.Unlock()
   294  	if s.mockerStop == nil {
   295  		http.Error(w, "stop channel not initialized", http.StatusInternalServerError)
   296  		return
   297  	}
   298  	close(s.mockerStop)
   299  	s.mockerStop = nil
   300  
   301  	w.WriteHeader(http.StatusOK)
   302  }
   303  
   304  func (s *Server) GetMockers(w http.ResponseWriter, req *http.Request) {
   305  
   306  	list := GetMockerList()
   307  	s.JSON(w, http.StatusOK, list)
   308  }
   309  
   310  func (s *Server) ResetNetwork(w http.ResponseWriter, req *http.Request) {
   311  	s.network.Reset()
   312  
   313  	w.WriteHeader(http.StatusOK)
   314  }
   315  
   316  func (s *Server) StreamNetworkEvents(w http.ResponseWriter, req *http.Request) {
   317  	events := make(chan *Event)
   318  	sub := s.network.events.Subscribe(events)
   319  	defer sub.Unsubscribe()
   320  
   321  	var clientGone <-chan bool
   322  	if cn, ok := w.(http.CloseNotifier); ok {
   323  		clientGone = cn.CloseNotify()
   324  	}
   325  
   326  	write := func(event, data string) {
   327  		fmt.Fprintf(w, "event: %s\n", event)
   328  		fmt.Fprintf(w, "data: %s\n\n", data)
   329  		if fw, ok := w.(http.Flusher); ok {
   330  			fw.Flush()
   331  		}
   332  	}
   333  	writeEvent := func(event *Event) error {
   334  		data, err := json.Marshal(event)
   335  		if err != nil {
   336  			return err
   337  		}
   338  		write("network", string(data))
   339  		return nil
   340  	}
   341  	writeErr := func(err error) {
   342  		write("error", err.Error())
   343  	}
   344  
   345  	var filters MsgFilters
   346  	if filterParam := req.URL.Query().Get("filter"); filterParam != "" {
   347  		var err error
   348  		filters, err = NewMsgFilters(filterParam)
   349  		if err != nil {
   350  			http.Error(w, err.Error(), http.StatusBadRequest)
   351  			return
   352  		}
   353  	}
   354  
   355  	w.Header().Set("Content-Type", "text/event-stream; charset=utf-8")
   356  	w.WriteHeader(http.StatusOK)
   357  	fmt.Fprintf(w, "\n\n")
   358  	if fw, ok := w.(http.Flusher); ok {
   359  		fw.Flush()
   360  	}
   361  
   362  	if req.URL.Query().Get("current") == "true" {
   363  		snap, err := s.network.Snapshot()
   364  		if err != nil {
   365  			writeErr(err)
   366  			return
   367  		}
   368  		for _, node := range snap.Nodes {
   369  			event := NewEvent(&node.Node)
   370  			if err := writeEvent(event); err != nil {
   371  				writeErr(err)
   372  				return
   373  			}
   374  		}
   375  		for _, conn := range snap.Conns {
   376  			event := NewEvent(&conn)
   377  			if err := writeEvent(event); err != nil {
   378  				writeErr(err)
   379  				return
   380  			}
   381  		}
   382  	}
   383  
   384  	for {
   385  		select {
   386  		case event := <-events:
   387  			if event.Msg != nil && !filters.Match(event.Msg) {
   388  				continue
   389  			}
   390  			if err := writeEvent(event); err != nil {
   391  				writeErr(err)
   392  				return
   393  			}
   394  		case <-clientGone:
   395  			return
   396  		}
   397  	}
   398  }
   399  
   400  func NewMsgFilters(filterParam string) (MsgFilters, error) {
   401  	filters := make(MsgFilters)
   402  	for _, filter := range strings.Split(filterParam, "-") {
   403  		protoCodes := strings.SplitN(filter, ":", 2)
   404  		if len(protoCodes) != 2 || protoCodes[0] == "" || protoCodes[1] == "" {
   405  			return nil, fmt.Errorf("invalid message filter: %s", filter)
   406  		}
   407  		proto := protoCodes[0]
   408  		for _, code := range strings.Split(protoCodes[1], ",") {
   409  			if code == "*" || code == "-1" {
   410  				filters[MsgFilter{Proto: proto, Code: -1}] = struct{}{}
   411  				continue
   412  			}
   413  			n, err := strconv.ParseUint(code, 10, 64)
   414  			if err != nil {
   415  				return nil, fmt.Errorf("invalid message code: %s", code)
   416  			}
   417  			filters[MsgFilter{Proto: proto, Code: int64(n)}] = struct{}{}
   418  		}
   419  	}
   420  	return filters, nil
   421  }
   422  
   423  type MsgFilters map[MsgFilter]struct{}
   424  
   425  func (m MsgFilters) Match(msg *Msg) bool {
   426  	if _, ok := m[MsgFilter{Proto: msg.Protocol, Code: -1}]; ok {
   427  		return true
   428  	}
   429  
   430  	if _, ok := m[MsgFilter{Proto: msg.Protocol, Code: int64(msg.Code)}]; ok {
   431  		return true
   432  	}
   433  
   434  	return false
   435  }
   436  
   437  type MsgFilter struct {
   438  	Proto string
   439  
   440  	Code int64
   441  }
   442  
   443  func (s *Server) CreateSnapshot(w http.ResponseWriter, req *http.Request) {
   444  	snap, err := s.network.Snapshot()
   445  	if err != nil {
   446  		http.Error(w, err.Error(), http.StatusInternalServerError)
   447  		return
   448  	}
   449  
   450  	s.JSON(w, http.StatusOK, snap)
   451  }
   452  
   453  func (s *Server) LoadSnapshot(w http.ResponseWriter, req *http.Request) {
   454  	snap := &Snapshot{}
   455  	if err := json.NewDecoder(req.Body).Decode(snap); err != nil {
   456  		http.Error(w, err.Error(), http.StatusBadRequest)
   457  		return
   458  	}
   459  
   460  	if err := s.network.Load(snap); err != nil {
   461  		http.Error(w, err.Error(), http.StatusInternalServerError)
   462  		return
   463  	}
   464  
   465  	s.JSON(w, http.StatusOK, s.network)
   466  }
   467  
   468  func (s *Server) CreateNode(w http.ResponseWriter, req *http.Request) {
   469  	config := adapters.RandomNodeConfig()
   470  	err := json.NewDecoder(req.Body).Decode(config)
   471  	if err != nil && err != io.EOF {
   472  		http.Error(w, err.Error(), http.StatusBadRequest)
   473  		return
   474  	}
   475  
   476  	node, err := s.network.NewNodeWithConfig(config)
   477  	if err != nil {
   478  		http.Error(w, err.Error(), http.StatusInternalServerError)
   479  		return
   480  	}
   481  
   482  	s.JSON(w, http.StatusCreated, node.NodeInfo())
   483  }
   484  
   485  func (s *Server) GetNodes(w http.ResponseWriter, req *http.Request) {
   486  	nodes := s.network.GetNodes()
   487  
   488  	infos := make([]*p2p.NodeInfo, len(nodes))
   489  	for i, node := range nodes {
   490  		infos[i] = node.NodeInfo()
   491  	}
   492  
   493  	s.JSON(w, http.StatusOK, infos)
   494  }
   495  
   496  func (s *Server) GetNode(w http.ResponseWriter, req *http.Request) {
   497  	node := req.Context().Value("node").(*Node)
   498  
   499  	s.JSON(w, http.StatusOK, node.NodeInfo())
   500  }
   501  
   502  func (s *Server) StartNode(w http.ResponseWriter, req *http.Request) {
   503  	node := req.Context().Value("node").(*Node)
   504  
   505  	if err := s.network.Start(node.ID()); err != nil {
   506  		http.Error(w, err.Error(), http.StatusInternalServerError)
   507  		return
   508  	}
   509  
   510  	s.JSON(w, http.StatusOK, node.NodeInfo())
   511  }
   512  
   513  func (s *Server) StopNode(w http.ResponseWriter, req *http.Request) {
   514  	node := req.Context().Value("node").(*Node)
   515  
   516  	if err := s.network.Stop(node.ID()); err != nil {
   517  		http.Error(w, err.Error(), http.StatusInternalServerError)
   518  		return
   519  	}
   520  
   521  	s.JSON(w, http.StatusOK, node.NodeInfo())
   522  }
   523  
   524  func (s *Server) ConnectNode(w http.ResponseWriter, req *http.Request) {
   525  	node := req.Context().Value("node").(*Node)
   526  	peer := req.Context().Value("peer").(*Node)
   527  
   528  	if err := s.network.Connect(node.ID(), peer.ID()); err != nil {
   529  		http.Error(w, err.Error(), http.StatusInternalServerError)
   530  		return
   531  	}
   532  
   533  	s.JSON(w, http.StatusOK, node.NodeInfo())
   534  }
   535  
   536  func (s *Server) DisconnectNode(w http.ResponseWriter, req *http.Request) {
   537  	node := req.Context().Value("node").(*Node)
   538  	peer := req.Context().Value("peer").(*Node)
   539  
   540  	if err := s.network.Disconnect(node.ID(), peer.ID()); err != nil {
   541  		http.Error(w, err.Error(), http.StatusInternalServerError)
   542  		return
   543  	}
   544  
   545  	s.JSON(w, http.StatusOK, node.NodeInfo())
   546  }
   547  
   548  func (s *Server) Options(w http.ResponseWriter, req *http.Request) {
   549  	w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
   550  	w.WriteHeader(http.StatusOK)
   551  }
   552  
   553  func (s *Server) NodeRPC(w http.ResponseWriter, req *http.Request) {
   554  	node := req.Context().Value("node").(*Node)
   555  
   556  	handler := func(conn *websocket.Conn) {
   557  		node.ServeRPC(conn)
   558  	}
   559  
   560  	websocket.Server{Handler: handler}.ServeHTTP(w, req)
   561  }
   562  
   563  func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
   564  	s.router.ServeHTTP(w, req)
   565  }
   566  
   567  func (s *Server) GET(path string, handle http.HandlerFunc) {
   568  	s.router.GET(path, s.wrapHandler(handle))
   569  }
   570  
   571  func (s *Server) POST(path string, handle http.HandlerFunc) {
   572  	s.router.POST(path, s.wrapHandler(handle))
   573  }
   574  
   575  func (s *Server) DELETE(path string, handle http.HandlerFunc) {
   576  	s.router.DELETE(path, s.wrapHandler(handle))
   577  }
   578  
   579  func (s *Server) OPTIONS(path string, handle http.HandlerFunc) {
   580  	s.router.OPTIONS("/*path", s.wrapHandler(handle))
   581  }
   582  
   583  func (s *Server) JSON(w http.ResponseWriter, status int, data interface{}) {
   584  	w.Header().Set("Content-Type", "application/json")
   585  	w.WriteHeader(status)
   586  	json.NewEncoder(w).Encode(data)
   587  }
   588  
   589  func (s *Server) wrapHandler(handler http.HandlerFunc) httprouter.Handle {
   590  	return func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
   591  		w.Header().Set("Access-Control-Allow-Origin", "*")
   592  		w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
   593  
   594  		ctx := context.Background()
   595  
   596  		if id := params.ByName("nodeid"); id != "" {
   597  			var node *Node
   598  			if nodeID, err := discover.HexID(id); err == nil {
   599  				node = s.network.GetNode(nodeID)
   600  			} else {
   601  				node = s.network.GetNodeByName(id)
   602  			}
   603  			if node == nil {
   604  				http.NotFound(w, req)
   605  				return
   606  			}
   607  			ctx = context.WithValue(ctx, "node", node)
   608  		}
   609  
   610  		if id := params.ByName("peerid"); id != "" {
   611  			var peer *Node
   612  			if peerID, err := discover.HexID(id); err == nil {
   613  				peer = s.network.GetNode(peerID)
   614  			} else {
   615  				peer = s.network.GetNodeByName(id)
   616  			}
   617  			if peer == nil {
   618  				http.NotFound(w, req)
   619  				return
   620  			}
   621  			ctx = context.WithValue(ctx, "peer", peer)
   622  		}
   623  
   624  		handler(w, req.WithContext(ctx))
   625  	}
   626  }