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 }