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