github.com/klaytn/klaytn@v1.12.1/networks/p2p/simulations/http.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2017 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from p2p/simulations/http.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package simulations
    22  
    23  import (
    24  	"bufio"
    25  	"bytes"
    26  	"context"
    27  	"encoding/json"
    28  	"fmt"
    29  	"io"
    30  	"net/http"
    31  	"strconv"
    32  	"strings"
    33  	"sync"
    34  
    35  	"github.com/gorilla/websocket"
    36  	"github.com/julienschmidt/httprouter"
    37  	"github.com/klaytn/klaytn/event"
    38  	"github.com/klaytn/klaytn/networks/p2p"
    39  	"github.com/klaytn/klaytn/networks/p2p/discover"
    40  	"github.com/klaytn/klaytn/networks/p2p/simulations/adapters"
    41  	"github.com/klaytn/klaytn/networks/rpc"
    42  )
    43  
    44  // DefaultClient is the default simulation API client which expects the API
    45  // to be running at http://localhost:8888
    46  var DefaultClient = NewClient("http://localhost:8888")
    47  
    48  // Client is a client for the simulation HTTP API which supports creating
    49  // and managing simulation networks
    50  type Client struct {
    51  	URL string
    52  
    53  	client *http.Client
    54  }
    55  
    56  // NewClient returns a new simulation API client
    57  func NewClient(url string) *Client {
    58  	return &Client{
    59  		URL:    url,
    60  		client: http.DefaultClient,
    61  	}
    62  }
    63  
    64  // GetNetwork returns details of the network
    65  func (c *Client) GetNetwork() (*Network, error) {
    66  	network := &Network{}
    67  	return network, c.Get("/", network)
    68  }
    69  
    70  // StartNetwork starts all existing nodes in the simulation network
    71  func (c *Client) StartNetwork() error {
    72  	return c.Post("/start", nil, nil)
    73  }
    74  
    75  // StopNetwork stops all existing nodes in a simulation network
    76  func (c *Client) StopNetwork() error {
    77  	return c.Post("/stop", nil, nil)
    78  }
    79  
    80  // CreateSnapshot creates a network snapshot
    81  func (c *Client) CreateSnapshot() (*Snapshot, error) {
    82  	snap := &Snapshot{}
    83  	return snap, c.Get("/snapshot", snap)
    84  }
    85  
    86  // LoadSnapshot loads a snapshot into the network
    87  func (c *Client) LoadSnapshot(snap *Snapshot) error {
    88  	return c.Post("/snapshot", snap, nil)
    89  }
    90  
    91  // SubscribeOpts is a collection of options to use when subscribing to network
    92  // events
    93  type SubscribeOpts struct {
    94  	// Current instructs the server to send events for existing nodes and
    95  	// connections first
    96  	Current bool
    97  
    98  	// Filter instructs the server to only send a subset of message events
    99  	Filter string
   100  }
   101  
   102  // SubscribeNetwork subscribes to network events which are sent from the server
   103  // as a server-sent-events stream, optionally receiving events for existing
   104  // nodes and connections and filtering message events
   105  func (c *Client) SubscribeNetwork(events chan *Event, opts SubscribeOpts) (event.Subscription, error) {
   106  	url := fmt.Sprintf("%s/events?current=%t&filter=%s", c.URL, opts.Current, opts.Filter)
   107  	req, err := http.NewRequest("GET", url, nil)
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  	req.Header.Set("Accept", "text/event-stream")
   112  	res, err := c.client.Do(req)
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  	if res.StatusCode != http.StatusOK {
   117  		response, _ := io.ReadAll(res.Body)
   118  		res.Body.Close()
   119  		return nil, fmt.Errorf("unexpected HTTP status: %s: %s", res.Status, response)
   120  	}
   121  
   122  	// define a producer function to pass to event.Subscription
   123  	// which reads server-sent events from res.Body and sends
   124  	// them to the events channel
   125  	producer := func(stop <-chan struct{}) error {
   126  		defer res.Body.Close()
   127  
   128  		// read lines from res.Body in a goroutine so that we are
   129  		// always reading from the stop channel
   130  		lines := make(chan string)
   131  		errC := make(chan error, 1)
   132  		go func() {
   133  			s := bufio.NewScanner(res.Body)
   134  			for s.Scan() {
   135  				select {
   136  				case lines <- s.Text():
   137  				case <-stop:
   138  					return
   139  				}
   140  			}
   141  			errC <- s.Err()
   142  		}()
   143  
   144  		// detect any lines which start with "data:", decode the data
   145  		// into an event and send it to the events channel
   146  		for {
   147  			select {
   148  			case line := <-lines:
   149  				if !strings.HasPrefix(line, "data:") {
   150  					continue
   151  				}
   152  				data := strings.TrimSpace(strings.TrimPrefix(line, "data:"))
   153  				event := &Event{}
   154  				if err := json.Unmarshal([]byte(data), event); err != nil {
   155  					return fmt.Errorf("error decoding SSE event: %s", err)
   156  				}
   157  				select {
   158  				case events <- event:
   159  				case <-stop:
   160  					return nil
   161  				}
   162  			case err := <-errC:
   163  				return err
   164  			case <-stop:
   165  				return nil
   166  			}
   167  		}
   168  	}
   169  
   170  	return event.NewSubscription(producer), nil
   171  }
   172  
   173  // GetNodes returns all nodes which exist in the network
   174  func (c *Client) GetNodes() ([]*p2p.NodeInfo, error) {
   175  	var nodes []*p2p.NodeInfo
   176  	return nodes, c.Get("/nodes", &nodes)
   177  }
   178  
   179  // CreateNode creates a node in the network using the given configuration
   180  func (c *Client) CreateNode(config *adapters.NodeConfig) (*p2p.NodeInfo, error) {
   181  	node := &p2p.NodeInfo{}
   182  	return node, c.Post("/nodes", config, node)
   183  }
   184  
   185  // GetNode returns details of a node
   186  func (c *Client) GetNode(nodeID string) (*p2p.NodeInfo, error) {
   187  	node := &p2p.NodeInfo{}
   188  	return node, c.Get(fmt.Sprintf("/nodes/%s", nodeID), node)
   189  }
   190  
   191  func (c *Client) ConnectAll() error {
   192  	return c.Post(fmt.Sprintf("/connectall"), nil, nil)
   193  }
   194  
   195  func (c *Client) DisconnectAll() error {
   196  	return c.Post(fmt.Sprintf("/disconnectall"), nil, nil)
   197  }
   198  
   199  func (c *Client) DisconnectOnly(nodeID, peerID string) error {
   200  	return c.Post(fmt.Sprintf("/nodes/%s/disconnectonly/%s", nodeID, peerID), nil, nil)
   201  }
   202  
   203  // StartNode starts a node
   204  func (c *Client) StartNode(nodeID string) error {
   205  	return c.Post(fmt.Sprintf("/nodes/%s/start", nodeID), nil, nil)
   206  }
   207  
   208  // StopNode stops a node
   209  func (c *Client) StopNode(nodeID string) error {
   210  	return c.Post(fmt.Sprintf("/nodes/%s/stop", nodeID), nil, nil)
   211  }
   212  
   213  // ConnectNode connects a node to a peer node
   214  func (c *Client) ConnectNode(nodeID, peerID string) error {
   215  	return c.Post(fmt.Sprintf("/nodes/%s/conn/%s", nodeID, peerID), nil, nil)
   216  }
   217  
   218  // DisconnectNode disconnects a node from a peer node
   219  func (c *Client) DisconnectNode(nodeID, peerID string) error {
   220  	return c.Delete(fmt.Sprintf("/nodes/%s/conn/%s", nodeID, peerID))
   221  }
   222  
   223  // RPCClient returns an RPC client connected to a node
   224  func (c *Client) RPCClient(ctx context.Context, nodeID string) (*rpc.Client, error) {
   225  	baseURL := strings.Replace(c.URL, "http", "ws", 1)
   226  	return rpc.DialWebsocket(ctx, fmt.Sprintf("%s/nodes/%s/rpc", baseURL, nodeID), "")
   227  }
   228  
   229  // Get performs a HTTP GET request decoding the resulting JSON response
   230  // into "out"
   231  func (c *Client) Get(path string, out interface{}) error {
   232  	return c.Send("GET", path, nil, out)
   233  }
   234  
   235  // Post performs a HTTP POST request sending "in" as the JSON body and
   236  // decoding the resulting JSON response into "out"
   237  func (c *Client) Post(path string, in, out interface{}) error {
   238  	return c.Send("POST", path, in, out)
   239  }
   240  
   241  // Delete performs a HTTP DELETE request
   242  func (c *Client) Delete(path string) error {
   243  	return c.Send("DELETE", path, nil, nil)
   244  }
   245  
   246  // Send performs a HTTP request, sending "in" as the JSON request body and
   247  // decoding the JSON response into "out"
   248  func (c *Client) Send(method, path string, in, out interface{}) error {
   249  	var body []byte
   250  	if in != nil {
   251  		var err error
   252  		body, err = json.Marshal(in)
   253  		if err != nil {
   254  			return err
   255  		}
   256  	}
   257  	req, err := http.NewRequest(method, c.URL+path, bytes.NewReader(body))
   258  	if err != nil {
   259  		return err
   260  	}
   261  	req.Header.Set("Content-Type", "application/json")
   262  	req.Header.Set("Accept", "application/json")
   263  	res, err := c.client.Do(req)
   264  	if err != nil {
   265  		return err
   266  	}
   267  	defer res.Body.Close()
   268  	if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusCreated {
   269  		response, _ := io.ReadAll(res.Body)
   270  		return fmt.Errorf("unexpected HTTP status: %s: %s", res.Status, response)
   271  	}
   272  	if out != nil {
   273  		if err := json.NewDecoder(res.Body).Decode(out); err != nil {
   274  			return err
   275  		}
   276  	}
   277  	return nil
   278  }
   279  
   280  // Server is an HTTP server providing an API to manage a simulation network
   281  type Server struct {
   282  	router     *httprouter.Router
   283  	network    *Network
   284  	mockerStop chan struct{} // when set, stops the current mocker
   285  	mockerMtx  sync.Mutex    // synchronises access to the mockerStop field
   286  }
   287  
   288  // NewServer returns a new simulation API server
   289  func NewServer(network *Network) *Server {
   290  	s := &Server{
   291  		router:  httprouter.New(),
   292  		network: network,
   293  	}
   294  
   295  	s.OPTIONS("/", s.Options)
   296  	s.GET("/", s.GetNetwork)
   297  	s.POST("/start", s.StartNetwork)
   298  	s.POST("/stop", s.StopNetwork)
   299  	s.POST("/mocker/start", s.StartMocker)
   300  	s.POST("/mocker/stop", s.StopMocker)
   301  	s.GET("/mocker", s.GetMockers)
   302  	s.POST("/reset", s.ResetNetwork)
   303  	s.GET("/events", s.StreamNetworkEvents)
   304  	s.GET("/snapshot", s.CreateSnapshot)
   305  	s.POST("/snapshot", s.LoadSnapshot)
   306  	s.POST("/nodes", s.CreateNode)
   307  	s.GET("/nodes", s.GetNodes)
   308  	s.GET("/nodes/:nodeid", s.GetNode)
   309  	s.POST("/nodes/:nodeid/start", s.StartNode)
   310  	s.POST("/nodes/:nodeid/stop", s.StopNode)
   311  	s.POST("/nodes/:nodeid/conn/:peerid", s.ConnectNode)
   312  	s.DELETE("/nodes/:nodeid/conn/:peerid", s.DisconnectNode)
   313  	s.POST("/nodes/:nodeid/disconnectonly/:peerid", s.DisconnectOnly)
   314  	s.GET("/nodes/:nodeid/rpc", s.NodeRPC)
   315  	s.POST("/connectall", s.ConnectAll)
   316  	s.POST("/disconnectall", s.DisconnectAll)
   317  
   318  	return s
   319  }
   320  
   321  // GetNetwork returns details of the network
   322  func (s *Server) GetNetwork(w http.ResponseWriter, req *http.Request) {
   323  	s.JSON(w, http.StatusOK, s.network)
   324  }
   325  
   326  // StartNetwork starts all nodes in the network
   327  func (s *Server) StartNetwork(w http.ResponseWriter, req *http.Request) {
   328  	if err := s.network.StartAll(); err != nil {
   329  		http.Error(w, err.Error(), http.StatusInternalServerError)
   330  		return
   331  	}
   332  
   333  	w.WriteHeader(http.StatusOK)
   334  }
   335  
   336  // StopNetwork stops all nodes in the network
   337  func (s *Server) StopNetwork(w http.ResponseWriter, req *http.Request) {
   338  	if err := s.network.StopAll(); err != nil {
   339  		http.Error(w, err.Error(), http.StatusInternalServerError)
   340  		return
   341  	}
   342  
   343  	w.WriteHeader(http.StatusOK)
   344  }
   345  
   346  // StartMocker starts the mocker node simulation
   347  func (s *Server) StartMocker(w http.ResponseWriter, req *http.Request) {
   348  	s.mockerMtx.Lock()
   349  	defer s.mockerMtx.Unlock()
   350  	if s.mockerStop != nil {
   351  		http.Error(w, "mocker already running", http.StatusInternalServerError)
   352  		return
   353  	}
   354  	mockerType := req.FormValue("mocker-type")
   355  	mockerFn := LookupMocker(mockerType)
   356  	if mockerFn == nil {
   357  		http.Error(w, fmt.Sprintf("unknown mocker type %q", mockerType), http.StatusBadRequest)
   358  		return
   359  	}
   360  	nodeCount, err := strconv.Atoi(req.FormValue("node-count"))
   361  	if err != nil {
   362  		http.Error(w, "invalid node-count provided", http.StatusBadRequest)
   363  		return
   364  	}
   365  	s.mockerStop = make(chan struct{})
   366  	go mockerFn(s.network, s.mockerStop, nodeCount)
   367  
   368  	w.WriteHeader(http.StatusOK)
   369  }
   370  
   371  // StopMocker stops the mocker node simulation
   372  func (s *Server) StopMocker(w http.ResponseWriter, req *http.Request) {
   373  	s.mockerMtx.Lock()
   374  	defer s.mockerMtx.Unlock()
   375  	if s.mockerStop == nil {
   376  		http.Error(w, "stop channel not initialized", http.StatusInternalServerError)
   377  		return
   378  	}
   379  	close(s.mockerStop)
   380  	s.mockerStop = nil
   381  
   382  	w.WriteHeader(http.StatusOK)
   383  }
   384  
   385  // GetMockerList returns a list of available mockers
   386  func (s *Server) GetMockers(w http.ResponseWriter, req *http.Request) {
   387  	list := GetMockerList()
   388  	s.JSON(w, http.StatusOK, list)
   389  }
   390  
   391  // ResetNetwork resets all properties of a network to its initial (empty) state
   392  func (s *Server) ResetNetwork(w http.ResponseWriter, req *http.Request) {
   393  	s.network.Reset()
   394  
   395  	w.WriteHeader(http.StatusOK)
   396  }
   397  
   398  // StreamNetworkEvents streams network events as a server-sent-events stream
   399  func (s *Server) StreamNetworkEvents(w http.ResponseWriter, req *http.Request) {
   400  	events := make(chan *Event)
   401  	sub := s.network.events.Subscribe(events)
   402  	defer sub.Unsubscribe()
   403  
   404  	// stop the stream if the client goes away
   405  	var clientGone <-chan bool
   406  	if cn, ok := w.(http.CloseNotifier); ok {
   407  		clientGone = cn.CloseNotify()
   408  	}
   409  
   410  	// write writes the given event and data to the stream like:
   411  	//
   412  	// event: <event>
   413  	// data: <data>
   414  	//
   415  	write := func(event, data string) {
   416  		fmt.Fprintf(w, "event: %s\n", event)
   417  		fmt.Fprintf(w, "data: %s\n\n", data)
   418  		if fw, ok := w.(http.Flusher); ok {
   419  			fw.Flush()
   420  		}
   421  	}
   422  	writeEvent := func(event *Event) error {
   423  		data, err := json.Marshal(event)
   424  		if err != nil {
   425  			return err
   426  		}
   427  		write("network", string(data))
   428  		return nil
   429  	}
   430  	writeErr := func(err error) {
   431  		write("error", err.Error())
   432  	}
   433  
   434  	// check if filtering has been requested
   435  	var filters MsgFilters
   436  	if filterParam := req.URL.Query().Get("filter"); filterParam != "" {
   437  		var err error
   438  		filters, err = NewMsgFilters(filterParam)
   439  		if err != nil {
   440  			http.Error(w, err.Error(), http.StatusBadRequest)
   441  			return
   442  		}
   443  	}
   444  
   445  	w.Header().Set("Content-Type", "text/event-stream; charset=utf-8")
   446  	w.WriteHeader(http.StatusOK)
   447  	fmt.Fprintf(w, "\n\n")
   448  	if fw, ok := w.(http.Flusher); ok {
   449  		fw.Flush()
   450  	}
   451  
   452  	// optionally send the existing nodes and connections
   453  	if req.URL.Query().Get("current") == "true" {
   454  		snap, err := s.network.Snapshot()
   455  		if err != nil {
   456  			writeErr(err)
   457  			return
   458  		}
   459  		for _, node := range snap.Nodes {
   460  			event := NewEvent(&node.Node)
   461  			if err := writeEvent(event); err != nil {
   462  				writeErr(err)
   463  				return
   464  			}
   465  		}
   466  		for _, conn := range snap.Conns {
   467  			event := NewEvent(&conn)
   468  			if err := writeEvent(event); err != nil {
   469  				writeErr(err)
   470  				return
   471  			}
   472  		}
   473  	}
   474  
   475  	for {
   476  		select {
   477  		case event := <-events:
   478  			// only send message events which match the filters
   479  			if event.Msg != nil && !filters.Match(event.Msg) {
   480  				continue
   481  			}
   482  			if err := writeEvent(event); err != nil {
   483  				writeErr(err)
   484  				return
   485  			}
   486  		case <-clientGone:
   487  			return
   488  		}
   489  	}
   490  }
   491  
   492  // NewMsgFilters constructs a collection of message filters from a URL query
   493  // parameter.
   494  //
   495  // The parameter is expected to be a dash-separated list of individual filters,
   496  // each having the format '<proto>:<codes>', where <proto> is the name of a
   497  // protocol and <codes> is a comma-separated list of message codes.
   498  //
   499  // A message code of '*' or '-1' is considered a wildcard and matches any code.
   500  func NewMsgFilters(filterParam string) (MsgFilters, error) {
   501  	filters := make(MsgFilters)
   502  	for _, filter := range strings.Split(filterParam, "-") {
   503  		protoCodes := strings.SplitN(filter, ":", 2)
   504  		if len(protoCodes) != 2 || protoCodes[0] == "" || protoCodes[1] == "" {
   505  			return nil, fmt.Errorf("invalid message filter: %s", filter)
   506  		}
   507  		proto := protoCodes[0]
   508  		for _, code := range strings.Split(protoCodes[1], ",") {
   509  			if code == "*" || code == "-1" {
   510  				filters[MsgFilter{Proto: proto, Code: -1}] = struct{}{}
   511  				continue
   512  			}
   513  			n, err := strconv.ParseUint(code, 10, 64)
   514  			if err != nil {
   515  				return nil, fmt.Errorf("invalid message code: %s", code)
   516  			}
   517  			filters[MsgFilter{Proto: proto, Code: int64(n)}] = struct{}{}
   518  		}
   519  	}
   520  	return filters, nil
   521  }
   522  
   523  // MsgFilters is a collection of filters which are used to filter message
   524  // events
   525  type MsgFilters map[MsgFilter]struct{}
   526  
   527  // Match checks if the given message matches any of the filters
   528  func (m MsgFilters) Match(msg *Msg) bool {
   529  	// check if there is a wildcard filter for the message's protocol
   530  	if _, ok := m[MsgFilter{Proto: msg.Protocol, Code: -1}]; ok {
   531  		return true
   532  	}
   533  
   534  	// check if there is a filter for the message's protocol and code
   535  	if _, ok := m[MsgFilter{Proto: msg.Protocol, Code: int64(msg.Code)}]; ok {
   536  		return true
   537  	}
   538  
   539  	return false
   540  }
   541  
   542  // MsgFilter is used to filter message events based on protocol and message
   543  // code
   544  type MsgFilter struct {
   545  	// Proto is matched against a message's protocol
   546  	Proto string
   547  
   548  	// Code is matched against a message's code, with -1 matching all codes
   549  	Code int64
   550  }
   551  
   552  // CreateSnapshot creates a network snapshot
   553  func (s *Server) CreateSnapshot(w http.ResponseWriter, req *http.Request) {
   554  	snap, err := s.network.Snapshot()
   555  	if err != nil {
   556  		http.Error(w, err.Error(), http.StatusInternalServerError)
   557  		return
   558  	}
   559  
   560  	s.JSON(w, http.StatusOK, snap)
   561  }
   562  
   563  // LoadSnapshot loads a snapshot into the network
   564  func (s *Server) LoadSnapshot(w http.ResponseWriter, req *http.Request) {
   565  	snap := &Snapshot{}
   566  	if err := json.NewDecoder(req.Body).Decode(snap); err != nil {
   567  		http.Error(w, err.Error(), http.StatusBadRequest)
   568  		return
   569  	}
   570  
   571  	if err := s.network.Load(snap); err != nil {
   572  		http.Error(w, err.Error(), http.StatusInternalServerError)
   573  		return
   574  	}
   575  
   576  	s.JSON(w, http.StatusOK, s.network)
   577  }
   578  
   579  // CreateNode creates a node in the network using the given configuration
   580  func (s *Server) CreateNode(w http.ResponseWriter, req *http.Request) {
   581  	config := &adapters.NodeConfig{}
   582  
   583  	err := json.NewDecoder(req.Body).Decode(config)
   584  	if err != nil && err != io.EOF {
   585  		http.Error(w, err.Error(), http.StatusBadRequest)
   586  		return
   587  	}
   588  
   589  	node, err := s.network.NewNodeWithConfig(config)
   590  	if err != nil {
   591  		http.Error(w, err.Error(), http.StatusInternalServerError)
   592  		return
   593  	}
   594  
   595  	s.JSON(w, http.StatusCreated, node.NodeInfo())
   596  }
   597  
   598  // GetNodes returns all nodes which exist in the network
   599  func (s *Server) GetNodes(w http.ResponseWriter, req *http.Request) {
   600  	nodes := s.network.GetNodes()
   601  
   602  	infos := make([]*p2p.NodeInfo, len(nodes))
   603  	for i, node := range nodes {
   604  		infos[i] = node.NodeInfo()
   605  	}
   606  
   607  	s.JSON(w, http.StatusOK, infos)
   608  }
   609  
   610  // GetNode returns details of a node
   611  func (s *Server) GetNode(w http.ResponseWriter, req *http.Request) {
   612  	node := req.Context().Value("node").(*Node)
   613  
   614  	s.JSON(w, http.StatusOK, node.NodeInfo())
   615  }
   616  
   617  // StartNode starts a node
   618  func (s *Server) StartNode(w http.ResponseWriter, req *http.Request) {
   619  	node := req.Context().Value("node").(*Node)
   620  
   621  	if err := s.network.Start(node.ID()); err != nil {
   622  		http.Error(w, err.Error(), http.StatusInternalServerError)
   623  		return
   624  	}
   625  
   626  	s.JSON(w, http.StatusOK, node.NodeInfo())
   627  }
   628  
   629  // StopNode stops a node
   630  func (s *Server) StopNode(w http.ResponseWriter, req *http.Request) {
   631  	node := req.Context().Value("node").(*Node)
   632  
   633  	if err := s.network.Stop(node.ID()); err != nil {
   634  		http.Error(w, err.Error(), http.StatusInternalServerError)
   635  		return
   636  	}
   637  
   638  	s.JSON(w, http.StatusOK, node.NodeInfo())
   639  }
   640  
   641  func (s *Server) ConnectAll(w http.ResponseWriter, req *http.Request) {
   642  	if err := s.network.ConnectAll(); err != nil {
   643  		http.Error(w, err.Error(), http.StatusInternalServerError)
   644  		return
   645  	}
   646  
   647  	// TODO: check whether all connections are successful or not
   648  	s.JSON(w, http.StatusOK, "connected all nodes")
   649  }
   650  
   651  func (s *Server) DisconnectAll(w http.ResponseWriter, req *http.Request) {
   652  	if err := s.network.DisconnectAll(); err != nil {
   653  		http.Error(w, err.Error(), http.StatusInternalServerError)
   654  		return
   655  	}
   656  
   657  	// TODO: check whether all connections are successful or not
   658  	s.JSON(w, http.StatusOK, "disconnected all nodes")
   659  }
   660  
   661  func (s *Server) DisconnectOnly(w http.ResponseWriter, req *http.Request) {
   662  	node := req.Context().Value("node").(*Node)
   663  	peer := req.Context().Value("peer").(*Node)
   664  
   665  	if err := s.network.DisconnectOnly(node.ID(), peer.ID()); err != nil {
   666  		http.Error(w, err.Error(), http.StatusInternalServerError)
   667  		return
   668  	}
   669  
   670  	s.JSON(w, http.StatusOK, node.NodeInfo())
   671  }
   672  
   673  // ConnectNode connects a node to a peer node
   674  func (s *Server) ConnectNode(w http.ResponseWriter, req *http.Request) {
   675  	node := req.Context().Value("node").(*Node)
   676  	peer := req.Context().Value("peer").(*Node)
   677  
   678  	if err := s.network.Connect(node.ID(), peer.ID()); err != nil {
   679  		http.Error(w, err.Error(), http.StatusInternalServerError)
   680  		return
   681  	}
   682  
   683  	s.JSON(w, http.StatusOK, node.NodeInfo())
   684  }
   685  
   686  // DisconnectNode disconnects a node from a peer node
   687  func (s *Server) DisconnectNode(w http.ResponseWriter, req *http.Request) {
   688  	node := req.Context().Value("node").(*Node)
   689  	peer := req.Context().Value("peer").(*Node)
   690  
   691  	if err := s.network.Disconnect(node.ID(), peer.ID()); err != nil {
   692  		http.Error(w, err.Error(), http.StatusInternalServerError)
   693  		return
   694  	}
   695  
   696  	s.JSON(w, http.StatusOK, node.NodeInfo())
   697  }
   698  
   699  // Options responds to the OPTIONS HTTP method by returning a 200 OK response
   700  // with the "Access-Control-Allow-Headers" header set to "Content-Type"
   701  func (s *Server) Options(w http.ResponseWriter, req *http.Request) {
   702  	w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
   703  	w.WriteHeader(http.StatusOK)
   704  }
   705  
   706  var wsUpgrade = websocket.Upgrader{
   707  	CheckOrigin: func(*http.Request) bool { return true },
   708  }
   709  
   710  // NodeRPC forwards RPC requests to a node in the network via a WebSocket
   711  // connection
   712  func (s *Server) NodeRPC(w http.ResponseWriter, req *http.Request) {
   713  	conn, err := wsUpgrade.Upgrade(w, req, nil)
   714  	if err != nil {
   715  		return
   716  	}
   717  	defer conn.Close()
   718  	node := req.Context().Value("node").(*Node)
   719  	node.ServeRPC(conn)
   720  }
   721  
   722  // ServeHTTP implements the http.Handler interface by delegating to the
   723  // underlying httprouter.Router
   724  func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
   725  	s.router.ServeHTTP(w, req)
   726  }
   727  
   728  // GET registers a handler for GET requests to a particular path
   729  func (s *Server) GET(path string, handle http.HandlerFunc) {
   730  	s.router.GET(path, s.wrapHandler(handle))
   731  }
   732  
   733  // POST registers a handler for POST requests to a particular path
   734  func (s *Server) POST(path string, handle http.HandlerFunc) {
   735  	s.router.POST(path, s.wrapHandler(handle))
   736  }
   737  
   738  // DELETE registers a handler for DELETE requests to a particular path
   739  func (s *Server) DELETE(path string, handle http.HandlerFunc) {
   740  	s.router.DELETE(path, s.wrapHandler(handle))
   741  }
   742  
   743  // OPTIONS registers a handler for OPTIONS requests to a particular path
   744  func (s *Server) OPTIONS(path string, handle http.HandlerFunc) {
   745  	s.router.OPTIONS("/*path", s.wrapHandler(handle))
   746  }
   747  
   748  // JSON sends "data" as a JSON HTTP response
   749  func (s *Server) JSON(w http.ResponseWriter, status int, data interface{}) {
   750  	w.Header().Set("Content-Type", "application/json")
   751  	w.WriteHeader(status)
   752  	json.NewEncoder(w).Encode(data)
   753  }
   754  
   755  // wrapHandler returns a httprouter.Handle which wraps a http.HandlerFunc by
   756  // populating request.Context with any objects from the URL params
   757  func (s *Server) wrapHandler(handler http.HandlerFunc) httprouter.Handle {
   758  	return func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
   759  		w.Header().Set("Access-Control-Allow-Origin", "*")
   760  		w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
   761  
   762  		ctx := context.Background()
   763  
   764  		if id := params.ByName("nodeid"); id != "" {
   765  			var node *Node
   766  			if nodeID, err := discover.HexID(id); err == nil {
   767  				node = s.network.GetNode(nodeID)
   768  			} else {
   769  				node = s.network.GetNodeByName(id)
   770  			}
   771  			if node == nil {
   772  				http.NotFound(w, req)
   773  				return
   774  			}
   775  			ctx = context.WithValue(ctx, "node", node)
   776  		}
   777  
   778  		if id := params.ByName("peerid"); id != "" {
   779  			var peer *Node
   780  			if peerID, err := discover.HexID(id); err == nil {
   781  				peer = s.network.GetNode(peerID)
   782  			} else {
   783  				peer = s.network.GetNodeByName(id)
   784  			}
   785  			if peer == nil {
   786  				http.NotFound(w, req)
   787  				return
   788  			}
   789  			ctx = context.WithValue(ctx, "peer", peer)
   790  		}
   791  
   792  		handler(w, req.WithContext(ctx))
   793  	}
   794  }