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