github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/network/p2p/simulations/http.go (about) 1 package simulations 2 3 import ( 4 "bufio" 5 "bytes" 6 "context" 7 "encoding/json" 8 "fmt" 9 "io" 10 "io/ioutil" 11 "net/http" 12 "strconv" 13 "strings" 14 "sync" 15 16 "github.com/julienschmidt/httprouter" 17 "github.com/neatlab/neatio/network/p2p" 18 "github.com/neatlab/neatio/network/p2p/discover" 19 "github.com/neatlab/neatio/network/p2p/simulations/adapters" 20 "github.com/neatlab/neatio/network/rpc" 21 "github.com/neatlab/neatio/utilities/event" 22 "golang.org/x/net/websocket" 23 ) 24 25 var DefaultClient = NewClient("http://localhost:8888") 26 27 type Client struct { 28 URL string 29 30 client *http.Client 31 } 32 33 func NewClient(url string) *Client { 34 return &Client{ 35 URL: url, 36 client: http.DefaultClient, 37 } 38 } 39 40 func (c *Client) GetNetwork() (*Network, error) { 41 network := &Network{} 42 return network, c.Get("/", network) 43 } 44 45 func (c *Client) StartNetwork() error { 46 return c.Post("/start", nil, nil) 47 } 48 49 func (c *Client) StopNetwork() error { 50 return c.Post("/stop", nil, nil) 51 } 52 53 func (c *Client) CreateSnapshot() (*Snapshot, error) { 54 snap := &Snapshot{} 55 return snap, c.Get("/snapshot", snap) 56 } 57 58 func (c *Client) LoadSnapshot(snap *Snapshot) error { 59 return c.Post("/snapshot", snap, nil) 60 } 61 62 type SubscribeOpts struct { 63 Current bool 64 65 Filter string 66 } 67 68 func (c *Client) SubscribeNetwork(events chan *Event, opts SubscribeOpts) (event.Subscription, error) { 69 url := fmt.Sprintf("%s/events?current=%t&filter=%s", c.URL, opts.Current, opts.Filter) 70 req, err := http.NewRequest("GET", url, nil) 71 if err != nil { 72 return nil, err 73 } 74 req.Header.Set("Accept", "text/event-stream") 75 res, err := c.client.Do(req) 76 if err != nil { 77 return nil, err 78 } 79 if res.StatusCode != http.StatusOK { 80 response, _ := ioutil.ReadAll(res.Body) 81 res.Body.Close() 82 return nil, fmt.Errorf("unexpected HTTP status: %s: %s", res.Status, response) 83 } 84 85 producer := func(stop <-chan struct{}) error { 86 defer res.Body.Close() 87 88 lines := make(chan string) 89 errC := make(chan error, 1) 90 go func() { 91 s := bufio.NewScanner(res.Body) 92 for s.Scan() { 93 select { 94 case lines <- s.Text(): 95 case <-stop: 96 return 97 } 98 } 99 errC <- s.Err() 100 }() 101 102 for { 103 select { 104 case line := <-lines: 105 if !strings.HasPrefix(line, "data:") { 106 continue 107 } 108 data := strings.TrimSpace(strings.TrimPrefix(line, "data:")) 109 event := &Event{} 110 if err := json.Unmarshal([]byte(data), event); err != nil { 111 return fmt.Errorf("error decoding SSE event: %s", err) 112 } 113 select { 114 case events <- event: 115 case <-stop: 116 return nil 117 } 118 case err := <-errC: 119 return err 120 case <-stop: 121 return nil 122 } 123 } 124 } 125 126 return event.NewSubscription(producer), nil 127 } 128 129 func (c *Client) GetNodes() ([]*p2p.NodeInfo, error) { 130 var nodes []*p2p.NodeInfo 131 return nodes, c.Get("/nodes", &nodes) 132 } 133 134 func (c *Client) CreateNode(config *adapters.NodeConfig) (*p2p.NodeInfo, error) { 135 node := &p2p.NodeInfo{} 136 return node, c.Post("/nodes", config, node) 137 } 138 139 func (c *Client) GetNode(nodeID string) (*p2p.NodeInfo, error) { 140 node := &p2p.NodeInfo{} 141 return node, c.Get(fmt.Sprintf("/nodes/%s", nodeID), node) 142 } 143 144 func (c *Client) StartNode(nodeID string) error { 145 return c.Post(fmt.Sprintf("/nodes/%s/start", nodeID), nil, nil) 146 } 147 148 func (c *Client) StopNode(nodeID string) error { 149 return c.Post(fmt.Sprintf("/nodes/%s/stop", nodeID), nil, nil) 150 } 151 152 func (c *Client) ConnectNode(nodeID, peerID string) error { 153 return c.Post(fmt.Sprintf("/nodes/%s/conn/%s", nodeID, peerID), nil, nil) 154 } 155 156 func (c *Client) DisconnectNode(nodeID, peerID string) error { 157 return c.Delete(fmt.Sprintf("/nodes/%s/conn/%s", nodeID, peerID)) 158 } 159 160 func (c *Client) RPCClient(ctx context.Context, nodeID string) (*rpc.Client, error) { 161 baseURL := strings.Replace(c.URL, "http", "ws", 1) 162 return rpc.DialWebsocket(ctx, fmt.Sprintf("%s/nodes/%s/rpc", baseURL, nodeID), "") 163 } 164 165 func (c *Client) Get(path string, out interface{}) error { 166 return c.Send("GET", path, nil, out) 167 } 168 169 func (c *Client) Post(path string, in, out interface{}) error { 170 return c.Send("POST", path, in, out) 171 } 172 173 func (c *Client) Delete(path string) error { 174 return c.Send("DELETE", path, nil, nil) 175 } 176 177 func (c *Client) Send(method, path string, in, out interface{}) error { 178 var body []byte 179 if in != nil { 180 var err error 181 body, err = json.Marshal(in) 182 if err != nil { 183 return err 184 } 185 } 186 req, err := http.NewRequest(method, c.URL+path, bytes.NewReader(body)) 187 if err != nil { 188 return err 189 } 190 req.Header.Set("Content-Type", "application/json") 191 req.Header.Set("Accept", "application/json") 192 res, err := c.client.Do(req) 193 if err != nil { 194 return err 195 } 196 defer res.Body.Close() 197 if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusCreated { 198 response, _ := ioutil.ReadAll(res.Body) 199 return fmt.Errorf("unexpected HTTP status: %s: %s", res.Status, response) 200 } 201 if out != nil { 202 if err := json.NewDecoder(res.Body).Decode(out); err != nil { 203 return err 204 } 205 } 206 return nil 207 } 208 209 type Server struct { 210 router *httprouter.Router 211 network *Network 212 mockerStop chan struct{} 213 mockerMtx sync.Mutex 214 } 215 216 func NewServer(network *Network) *Server { 217 s := &Server{ 218 router: httprouter.New(), 219 network: network, 220 } 221 222 s.OPTIONS("/", s.Options) 223 s.GET("/", s.GetNetwork) 224 s.POST("/start", s.StartNetwork) 225 s.POST("/stop", s.StopNetwork) 226 s.POST("/mocker/start", s.StartMocker) 227 s.POST("/mocker/stop", s.StopMocker) 228 s.GET("/mocker", s.GetMockers) 229 s.POST("/reset", s.ResetNetwork) 230 s.GET("/events", s.StreamNetworkEvents) 231 s.GET("/snapshot", s.CreateSnapshot) 232 s.POST("/snapshot", s.LoadSnapshot) 233 s.POST("/nodes", s.CreateNode) 234 s.GET("/nodes", s.GetNodes) 235 s.GET("/nodes/:nodeid", s.GetNode) 236 s.POST("/nodes/:nodeid/start", s.StartNode) 237 s.POST("/nodes/:nodeid/stop", s.StopNode) 238 s.POST("/nodes/:nodeid/conn/:peerid", s.ConnectNode) 239 s.DELETE("/nodes/:nodeid/conn/:peerid", s.DisconnectNode) 240 s.GET("/nodes/:nodeid/rpc", s.NodeRPC) 241 242 return s 243 } 244 245 func (s *Server) GetNetwork(w http.ResponseWriter, req *http.Request) { 246 s.JSON(w, http.StatusOK, s.network) 247 } 248 249 func (s *Server) StartNetwork(w http.ResponseWriter, req *http.Request) { 250 if err := s.network.StartAll(); err != nil { 251 http.Error(w, err.Error(), http.StatusInternalServerError) 252 return 253 } 254 255 w.WriteHeader(http.StatusOK) 256 } 257 258 func (s *Server) StopNetwork(w http.ResponseWriter, req *http.Request) { 259 if err := s.network.StopAll(); err != nil { 260 http.Error(w, err.Error(), http.StatusInternalServerError) 261 return 262 } 263 264 w.WriteHeader(http.StatusOK) 265 } 266 267 func (s *Server) StartMocker(w http.ResponseWriter, req *http.Request) { 268 s.mockerMtx.Lock() 269 defer s.mockerMtx.Unlock() 270 if s.mockerStop != nil { 271 http.Error(w, "mocker already running", http.StatusInternalServerError) 272 return 273 } 274 mockerType := req.FormValue("mocker-type") 275 mockerFn := LookupMocker(mockerType) 276 if mockerFn == nil { 277 http.Error(w, fmt.Sprintf("unknown mocker type %q", mockerType), http.StatusBadRequest) 278 return 279 } 280 nodeCount, err := strconv.Atoi(req.FormValue("node-count")) 281 if err != nil { 282 http.Error(w, "invalid node-count provided", http.StatusBadRequest) 283 return 284 } 285 s.mockerStop = make(chan struct{}) 286 go mockerFn(s.network, s.mockerStop, nodeCount) 287 288 w.WriteHeader(http.StatusOK) 289 } 290 291 func (s *Server) StopMocker(w http.ResponseWriter, req *http.Request) { 292 s.mockerMtx.Lock() 293 defer s.mockerMtx.Unlock() 294 if s.mockerStop == nil { 295 http.Error(w, "stop channel not initialized", http.StatusInternalServerError) 296 return 297 } 298 close(s.mockerStop) 299 s.mockerStop = nil 300 301 w.WriteHeader(http.StatusOK) 302 } 303 304 func (s *Server) GetMockers(w http.ResponseWriter, req *http.Request) { 305 306 list := GetMockerList() 307 s.JSON(w, http.StatusOK, list) 308 } 309 310 func (s *Server) ResetNetwork(w http.ResponseWriter, req *http.Request) { 311 s.network.Reset() 312 313 w.WriteHeader(http.StatusOK) 314 } 315 316 func (s *Server) StreamNetworkEvents(w http.ResponseWriter, req *http.Request) { 317 events := make(chan *Event) 318 sub := s.network.events.Subscribe(events) 319 defer sub.Unsubscribe() 320 321 var clientGone <-chan bool 322 if cn, ok := w.(http.CloseNotifier); ok { 323 clientGone = cn.CloseNotify() 324 } 325 326 write := func(event, data string) { 327 fmt.Fprintf(w, "event: %s\n", event) 328 fmt.Fprintf(w, "data: %s\n\n", data) 329 if fw, ok := w.(http.Flusher); ok { 330 fw.Flush() 331 } 332 } 333 writeEvent := func(event *Event) error { 334 data, err := json.Marshal(event) 335 if err != nil { 336 return err 337 } 338 write("network", string(data)) 339 return nil 340 } 341 writeErr := func(err error) { 342 write("error", err.Error()) 343 } 344 345 var filters MsgFilters 346 if filterParam := req.URL.Query().Get("filter"); filterParam != "" { 347 var err error 348 filters, err = NewMsgFilters(filterParam) 349 if err != nil { 350 http.Error(w, err.Error(), http.StatusBadRequest) 351 return 352 } 353 } 354 355 w.Header().Set("Content-Type", "text/event-stream; charset=utf-8") 356 w.WriteHeader(http.StatusOK) 357 fmt.Fprintf(w, "\n\n") 358 if fw, ok := w.(http.Flusher); ok { 359 fw.Flush() 360 } 361 362 if req.URL.Query().Get("current") == "true" { 363 snap, err := s.network.Snapshot() 364 if err != nil { 365 writeErr(err) 366 return 367 } 368 for _, node := range snap.Nodes { 369 event := NewEvent(&node.Node) 370 if err := writeEvent(event); err != nil { 371 writeErr(err) 372 return 373 } 374 } 375 for _, conn := range snap.Conns { 376 event := NewEvent(&conn) 377 if err := writeEvent(event); err != nil { 378 writeErr(err) 379 return 380 } 381 } 382 } 383 384 for { 385 select { 386 case event := <-events: 387 if event.Msg != nil && !filters.Match(event.Msg) { 388 continue 389 } 390 if err := writeEvent(event); err != nil { 391 writeErr(err) 392 return 393 } 394 case <-clientGone: 395 return 396 } 397 } 398 } 399 400 func NewMsgFilters(filterParam string) (MsgFilters, error) { 401 filters := make(MsgFilters) 402 for _, filter := range strings.Split(filterParam, "-") { 403 protoCodes := strings.SplitN(filter, ":", 2) 404 if len(protoCodes) != 2 || protoCodes[0] == "" || protoCodes[1] == "" { 405 return nil, fmt.Errorf("invalid message filter: %s", filter) 406 } 407 proto := protoCodes[0] 408 for _, code := range strings.Split(protoCodes[1], ",") { 409 if code == "*" || code == "-1" { 410 filters[MsgFilter{Proto: proto, Code: -1}] = struct{}{} 411 continue 412 } 413 n, err := strconv.ParseUint(code, 10, 64) 414 if err != nil { 415 return nil, fmt.Errorf("invalid message code: %s", code) 416 } 417 filters[MsgFilter{Proto: proto, Code: int64(n)}] = struct{}{} 418 } 419 } 420 return filters, nil 421 } 422 423 type MsgFilters map[MsgFilter]struct{} 424 425 func (m MsgFilters) Match(msg *Msg) bool { 426 if _, ok := m[MsgFilter{Proto: msg.Protocol, Code: -1}]; ok { 427 return true 428 } 429 430 if _, ok := m[MsgFilter{Proto: msg.Protocol, Code: int64(msg.Code)}]; ok { 431 return true 432 } 433 434 return false 435 } 436 437 type MsgFilter struct { 438 Proto string 439 440 Code int64 441 } 442 443 func (s *Server) CreateSnapshot(w http.ResponseWriter, req *http.Request) { 444 snap, err := s.network.Snapshot() 445 if err != nil { 446 http.Error(w, err.Error(), http.StatusInternalServerError) 447 return 448 } 449 450 s.JSON(w, http.StatusOK, snap) 451 } 452 453 func (s *Server) LoadSnapshot(w http.ResponseWriter, req *http.Request) { 454 snap := &Snapshot{} 455 if err := json.NewDecoder(req.Body).Decode(snap); err != nil { 456 http.Error(w, err.Error(), http.StatusBadRequest) 457 return 458 } 459 460 if err := s.network.Load(snap); err != nil { 461 http.Error(w, err.Error(), http.StatusInternalServerError) 462 return 463 } 464 465 s.JSON(w, http.StatusOK, s.network) 466 } 467 468 func (s *Server) CreateNode(w http.ResponseWriter, req *http.Request) { 469 config := adapters.RandomNodeConfig() 470 err := json.NewDecoder(req.Body).Decode(config) 471 if err != nil && err != io.EOF { 472 http.Error(w, err.Error(), http.StatusBadRequest) 473 return 474 } 475 476 node, err := s.network.NewNodeWithConfig(config) 477 if err != nil { 478 http.Error(w, err.Error(), http.StatusInternalServerError) 479 return 480 } 481 482 s.JSON(w, http.StatusCreated, node.NodeInfo()) 483 } 484 485 func (s *Server) GetNodes(w http.ResponseWriter, req *http.Request) { 486 nodes := s.network.GetNodes() 487 488 infos := make([]*p2p.NodeInfo, len(nodes)) 489 for i, node := range nodes { 490 infos[i] = node.NodeInfo() 491 } 492 493 s.JSON(w, http.StatusOK, infos) 494 } 495 496 func (s *Server) GetNode(w http.ResponseWriter, req *http.Request) { 497 node := req.Context().Value("node").(*Node) 498 499 s.JSON(w, http.StatusOK, node.NodeInfo()) 500 } 501 502 func (s *Server) StartNode(w http.ResponseWriter, req *http.Request) { 503 node := req.Context().Value("node").(*Node) 504 505 if err := s.network.Start(node.ID()); err != nil { 506 http.Error(w, err.Error(), http.StatusInternalServerError) 507 return 508 } 509 510 s.JSON(w, http.StatusOK, node.NodeInfo()) 511 } 512 513 func (s *Server) StopNode(w http.ResponseWriter, req *http.Request) { 514 node := req.Context().Value("node").(*Node) 515 516 if err := s.network.Stop(node.ID()); err != nil { 517 http.Error(w, err.Error(), http.StatusInternalServerError) 518 return 519 } 520 521 s.JSON(w, http.StatusOK, node.NodeInfo()) 522 } 523 524 func (s *Server) ConnectNode(w http.ResponseWriter, req *http.Request) { 525 node := req.Context().Value("node").(*Node) 526 peer := req.Context().Value("peer").(*Node) 527 528 if err := s.network.Connect(node.ID(), peer.ID()); err != nil { 529 http.Error(w, err.Error(), http.StatusInternalServerError) 530 return 531 } 532 533 s.JSON(w, http.StatusOK, node.NodeInfo()) 534 } 535 536 func (s *Server) DisconnectNode(w http.ResponseWriter, req *http.Request) { 537 node := req.Context().Value("node").(*Node) 538 peer := req.Context().Value("peer").(*Node) 539 540 if err := s.network.Disconnect(node.ID(), peer.ID()); err != nil { 541 http.Error(w, err.Error(), http.StatusInternalServerError) 542 return 543 } 544 545 s.JSON(w, http.StatusOK, node.NodeInfo()) 546 } 547 548 func (s *Server) Options(w http.ResponseWriter, req *http.Request) { 549 w.Header().Set("Access-Control-Allow-Headers", "Content-Type") 550 w.WriteHeader(http.StatusOK) 551 } 552 553 func (s *Server) NodeRPC(w http.ResponseWriter, req *http.Request) { 554 node := req.Context().Value("node").(*Node) 555 556 handler := func(conn *websocket.Conn) { 557 node.ServeRPC(conn) 558 } 559 560 websocket.Server{Handler: handler}.ServeHTTP(w, req) 561 } 562 563 func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) { 564 s.router.ServeHTTP(w, req) 565 } 566 567 func (s *Server) GET(path string, handle http.HandlerFunc) { 568 s.router.GET(path, s.wrapHandler(handle)) 569 } 570 571 func (s *Server) POST(path string, handle http.HandlerFunc) { 572 s.router.POST(path, s.wrapHandler(handle)) 573 } 574 575 func (s *Server) DELETE(path string, handle http.HandlerFunc) { 576 s.router.DELETE(path, s.wrapHandler(handle)) 577 } 578 579 func (s *Server) OPTIONS(path string, handle http.HandlerFunc) { 580 s.router.OPTIONS("/*path", s.wrapHandler(handle)) 581 } 582 583 func (s *Server) JSON(w http.ResponseWriter, status int, data interface{}) { 584 w.Header().Set("Content-Type", "application/json") 585 w.WriteHeader(status) 586 json.NewEncoder(w).Encode(data) 587 } 588 589 func (s *Server) wrapHandler(handler http.HandlerFunc) httprouter.Handle { 590 return func(w http.ResponseWriter, req *http.Request, params httprouter.Params) { 591 w.Header().Set("Access-Control-Allow-Origin", "*") 592 w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") 593 594 ctx := context.Background() 595 596 if id := params.ByName("nodeid"); id != "" { 597 var node *Node 598 if nodeID, err := discover.HexID(id); err == nil { 599 node = s.network.GetNode(nodeID) 600 } else { 601 node = s.network.GetNodeByName(id) 602 } 603 if node == nil { 604 http.NotFound(w, req) 605 return 606 } 607 ctx = context.WithValue(ctx, "node", node) 608 } 609 610 if id := params.ByName("peerid"); id != "" { 611 var peer *Node 612 if peerID, err := discover.HexID(id); err == nil { 613 peer = s.network.GetNode(peerID) 614 } else { 615 peer = s.network.GetNodeByName(id) 616 } 617 if peer == nil { 618 http.NotFound(w, req) 619 return 620 } 621 ctx = context.WithValue(ctx, "peer", peer) 622 } 623 624 handler(w, req.WithContext(ctx)) 625 } 626 }