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