github.com/waltonchain/waltonchain_gwtc_src@v1.1.4-0.20201225072101-8a298c95a819/p2p/simulations/http.go (about)

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