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 }