github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/p2p/simulations/http.go (about)

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