github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/p2p/simulations/http.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:41</date> 10 //</624450106917130240> 11 12 13 package simulations 14 15 import ( 16 "bufio" 17 "bytes" 18 "context" 19 "encoding/json" 20 "fmt" 21 "io" 22 "io/ioutil" 23 "net/http" 24 "strconv" 25 "strings" 26 "sync" 27 28 "github.com/ethereum/go-ethereum/event" 29 "github.com/ethereum/go-ethereum/p2p" 30 "github.com/ethereum/go-ethereum/p2p/enode" 31 "github.com/ethereum/go-ethereum/p2p/simulations/adapters" 32 "github.com/ethereum/go-ethereum/rpc" 33 "github.com/julienschmidt/httprouter" 34 "golang.org/x/net/websocket" 35 ) 36 37 //DefaultClient是默认的模拟API客户端,它需要API 38 //在http://localhost:8888上运行 39 var DefaultClient = NewClient("http://本地主机:8888“) 40 41 //客户端是支持创建的模拟HTTP API的客户端 42 //以及管理模拟网络 43 type Client struct { 44 URL string 45 46 client *http.Client 47 } 48 49 //new client返回新的模拟API客户端 50 func NewClient(url string) *Client { 51 return &Client{ 52 URL: url, 53 client: http.DefaultClient, 54 } 55 } 56 57 //GetNetwork返回网络的详细信息 58 func (c *Client) GetNetwork() (*Network, error) { 59 network := &Network{} 60 return network, c.Get("/", network) 61 } 62 63 //StartNetwork启动模拟网络中的所有现有节点 64 func (c *Client) StartNetwork() error { 65 return c.Post("/start", nil, nil) 66 } 67 68 //stopNetwork停止模拟网络中的所有现有节点 69 func (c *Client) StopNetwork() error { 70 return c.Post("/stop", nil, nil) 71 } 72 73 //创建快照创建网络快照 74 func (c *Client) CreateSnapshot() (*Snapshot, error) { 75 snap := &Snapshot{} 76 return snap, c.Get("/snapshot", snap) 77 } 78 79 //LoadSnapshot将快照加载到网络中 80 func (c *Client) LoadSnapshot(snap *Snapshot) error { 81 return c.Post("/snapshot", snap, nil) 82 } 83 84 //subscribeopts是订阅网络时要使用的选项集合 85 //事件 86 type SubscribeOpts struct { 87 //current指示服务器发送现有节点的事件和 88 //先连接 89 Current bool 90 91 //筛选器指示服务器仅发送消息事件的子集 92 Filter string 93 } 94 95 //订阅网络订阅从服务器发送的网络事件 96 //作为服务器发送的事件流,可以选择接收现有事件 97 //节点和连接以及筛选消息事件 98 func (c *Client) SubscribeNetwork(events chan *Event, opts SubscribeOpts) (event.Subscription, error) { 99 url := fmt.Sprintf("%s/events?current=%t&filter=%s", c.URL, opts.Current, opts.Filter) 100 req, err := http.NewRequest("GET", url, nil) 101 if err != nil { 102 return nil, err 103 } 104 req.Header.Set("Accept", "text/event-stream") 105 res, err := c.client.Do(req) 106 if err != nil { 107 return nil, err 108 } 109 if res.StatusCode != http.StatusOK { 110 response, _ := ioutil.ReadAll(res.Body) 111 res.Body.Close() 112 return nil, fmt.Errorf("unexpected HTTP status: %s: %s", res.Status, response) 113 } 114 115 //定义要传递到事件的生产者函数。订阅 116 //从Res.Body读取服务器发送的事件并发送 117 //他们到活动频道 118 producer := func(stop <-chan struct{}) error { 119 defer res.Body.Close() 120 121 //在Goroutine中阅读Res.Body的台词,这样我们 122 //总是从停止通道读取 123 lines := make(chan string) 124 errC := make(chan error, 1) 125 go func() { 126 s := bufio.NewScanner(res.Body) 127 for s.Scan() { 128 select { 129 case lines <- s.Text(): 130 case <-stop: 131 return 132 } 133 } 134 errC <- s.Err() 135 }() 136 137 //检测以“数据:”开头的任何行,解码数据 138 //将其发送到事件频道 139 for { 140 select { 141 case line := <-lines: 142 if !strings.HasPrefix(line, "data:") { 143 continue 144 } 145 data := strings.TrimSpace(strings.TrimPrefix(line, "data:")) 146 event := &Event{} 147 if err := json.Unmarshal([]byte(data), event); err != nil { 148 return fmt.Errorf("error decoding SSE event: %s", err) 149 } 150 select { 151 case events <- event: 152 case <-stop: 153 return nil 154 } 155 case err := <-errC: 156 return err 157 case <-stop: 158 return nil 159 } 160 } 161 } 162 163 return event.NewSubscription(producer), nil 164 } 165 166 //getnodes返回网络中存在的所有节点 167 func (c *Client) GetNodes() ([]*p2p.NodeInfo, error) { 168 var nodes []*p2p.NodeInfo 169 return nodes, c.Get("/nodes", &nodes) 170 } 171 172 //createNode使用给定的配置在网络中创建节点 173 func (c *Client) CreateNode(config *adapters.NodeConfig) (*p2p.NodeInfo, error) { 174 node := &p2p.NodeInfo{} 175 return node, c.Post("/nodes", config, node) 176 } 177 178 //getnode返回节点的详细信息 179 func (c *Client) GetNode(nodeID string) (*p2p.NodeInfo, error) { 180 node := &p2p.NodeInfo{} 181 return node, c.Get(fmt.Sprintf("/nodes/%s", nodeID), node) 182 } 183 184 //startnode启动节点 185 func (c *Client) StartNode(nodeID string) error { 186 return c.Post(fmt.Sprintf("/nodes/%s/start", nodeID), nil, nil) 187 } 188 189 //停止节点停止节点 190 func (c *Client) StopNode(nodeID string) error { 191 return c.Post(fmt.Sprintf("/nodes/%s/stop", nodeID), nil, nil) 192 } 193 194 //ConnectNode将节点连接到对等节点 195 func (c *Client) ConnectNode(nodeID, peerID string) error { 196 return c.Post(fmt.Sprintf("/nodes/%s/conn/%s", nodeID, peerID), nil, nil) 197 } 198 199 //断开节点断开节点与对等节点的连接 200 func (c *Client) DisconnectNode(nodeID, peerID string) error { 201 return c.Delete(fmt.Sprintf("/nodes/%s/conn/%s", nodeID, peerID)) 202 } 203 204 //rpc client返回连接到节点的rpc客户端 205 func (c *Client) RPCClient(ctx context.Context, nodeID string) (*rpc.Client, error) { 206 baseURL := strings.Replace(c.URL, "http", "ws", 1) 207 return rpc.DialWebsocket(ctx, fmt.Sprintf("%s/nodes/%s/rpc", baseURL, nodeID), "") 208 } 209 210 //GET执行HTTP GET请求,对结果JSON响应进行解码。 211 //入“出” 212 func (c *Client) Get(path string, out interface{}) error { 213 return c.Send("GET", path, nil, out) 214 } 215 216 //post执行一个HTTP post请求,将“in”作为JSON主体发送,并且 217 //将得到的JSON响应解码为“out” 218 func (c *Client) Post(path string, in, out interface{}) error { 219 return c.Send("POST", path, in, out) 220 } 221 222 //删除执行HTTP删除请求 223 func (c *Client) Delete(path string) error { 224 return c.Send("DELETE", path, nil, nil) 225 } 226 227 //send执行一个HTTP请求,将“in”作为JSON请求体发送,并且 228 //将JSON响应解码为“out” 229 func (c *Client) Send(method, path string, in, out interface{}) error { 230 var body []byte 231 if in != nil { 232 var err error 233 body, err = json.Marshal(in) 234 if err != nil { 235 return err 236 } 237 } 238 req, err := http.NewRequest(method, c.URL+path, bytes.NewReader(body)) 239 if err != nil { 240 return err 241 } 242 req.Header.Set("Content-Type", "application/json") 243 req.Header.Set("Accept", "application/json") 244 res, err := c.client.Do(req) 245 if err != nil { 246 return err 247 } 248 defer res.Body.Close() 249 if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusCreated { 250 response, _ := ioutil.ReadAll(res.Body) 251 return fmt.Errorf("unexpected HTTP status: %s: %s", res.Status, response) 252 } 253 if out != nil { 254 if err := json.NewDecoder(res.Body).Decode(out); err != nil { 255 return err 256 } 257 } 258 return nil 259 } 260 261 //服务器是一个HTTP服务器,提供用于管理模拟网络的API 262 type Server struct { 263 router *httprouter.Router 264 network *Network 265 mockerStop chan struct{} //设置后,停止当前mocker 266 mockerMtx sync.Mutex //同步访问Mockerstop字段 267 } 268 269 //NewServer返回新的模拟API服务器 270 func NewServer(network *Network) *Server { 271 s := &Server{ 272 router: httprouter.New(), 273 network: network, 274 } 275 276 s.OPTIONS("/", s.Options) 277 s.GET("/", s.GetNetwork) 278 s.POST("/start", s.StartNetwork) 279 s.POST("/stop", s.StopNetwork) 280 s.POST("/mocker/start", s.StartMocker) 281 s.POST("/mocker/stop", s.StopMocker) 282 s.GET("/mocker", s.GetMockers) 283 s.POST("/reset", s.ResetNetwork) 284 s.GET("/events", s.StreamNetworkEvents) 285 s.GET("/snapshot", s.CreateSnapshot) 286 s.POST("/snapshot", s.LoadSnapshot) 287 s.POST("/nodes", s.CreateNode) 288 s.GET("/nodes", s.GetNodes) 289 s.GET("/nodes/:nodeid", s.GetNode) 290 s.POST("/nodes/:nodeid/start", s.StartNode) 291 s.POST("/nodes/:nodeid/stop", s.StopNode) 292 s.POST("/nodes/:nodeid/conn/:peerid", s.ConnectNode) 293 s.DELETE("/nodes/:nodeid/conn/:peerid", s.DisconnectNode) 294 s.GET("/nodes/:nodeid/rpc", s.NodeRPC) 295 296 return s 297 } 298 299 //GetNetwork返回网络的详细信息 300 func (s *Server) GetNetwork(w http.ResponseWriter, req *http.Request) { 301 s.JSON(w, http.StatusOK, s.network) 302 } 303 304 //StartNetwork启动网络中的所有节点 305 func (s *Server) StartNetwork(w http.ResponseWriter, req *http.Request) { 306 if err := s.network.StartAll(); err != nil { 307 http.Error(w, err.Error(), http.StatusInternalServerError) 308 return 309 } 310 311 w.WriteHeader(http.StatusOK) 312 } 313 314 //stopNetwork停止网络中的所有节点 315 func (s *Server) StopNetwork(w http.ResponseWriter, req *http.Request) { 316 if err := s.network.StopAll(); err != nil { 317 http.Error(w, err.Error(), http.StatusInternalServerError) 318 return 319 } 320 321 w.WriteHeader(http.StatusOK) 322 } 323 324 //StartMocker启动Mocker节点模拟 325 func (s *Server) StartMocker(w http.ResponseWriter, req *http.Request) { 326 s.mockerMtx.Lock() 327 defer s.mockerMtx.Unlock() 328 if s.mockerStop != nil { 329 http.Error(w, "mocker already running", http.StatusInternalServerError) 330 return 331 } 332 mockerType := req.FormValue("mocker-type") 333 mockerFn := LookupMocker(mockerType) 334 if mockerFn == nil { 335 http.Error(w, fmt.Sprintf("unknown mocker type %q", mockerType), http.StatusBadRequest) 336 return 337 } 338 nodeCount, err := strconv.Atoi(req.FormValue("node-count")) 339 if err != nil { 340 http.Error(w, "invalid node-count provided", http.StatusBadRequest) 341 return 342 } 343 s.mockerStop = make(chan struct{}) 344 go mockerFn(s.network, s.mockerStop, nodeCount) 345 346 w.WriteHeader(http.StatusOK) 347 } 348 349 //StopMocker停止Mocker节点模拟 350 func (s *Server) StopMocker(w http.ResponseWriter, req *http.Request) { 351 s.mockerMtx.Lock() 352 defer s.mockerMtx.Unlock() 353 if s.mockerStop == nil { 354 http.Error(w, "stop channel not initialized", http.StatusInternalServerError) 355 return 356 } 357 close(s.mockerStop) 358 s.mockerStop = nil 359 360 w.WriteHeader(http.StatusOK) 361 } 362 363 //getmokerlist返回可用mocker的列表 364 func (s *Server) GetMockers(w http.ResponseWriter, req *http.Request) { 365 366 list := GetMockerList() 367 s.JSON(w, http.StatusOK, list) 368 } 369 370 //ResetNetwork将网络的所有属性重置为其初始(空)状态 371 func (s *Server) ResetNetwork(w http.ResponseWriter, req *http.Request) { 372 s.network.Reset() 373 374 w.WriteHeader(http.StatusOK) 375 } 376 377 //streamNetworkEvents将网络事件作为服务器发送的事件流进行流式处理 378 func (s *Server) StreamNetworkEvents(w http.ResponseWriter, req *http.Request) { 379 events := make(chan *Event) 380 sub := s.network.events.Subscribe(events) 381 defer sub.Unsubscribe() 382 383 //如果客户端不在,则停止流 384 var clientGone <-chan bool 385 if cn, ok := w.(http.CloseNotifier); ok { 386 clientGone = cn.CloseNotify() 387 } 388 389 //写入将给定事件和数据写入流,如下所示: 390 // 391 //事件:<事件> 392 //数据:<数据> 393 // 394 write := func(event, data string) { 395 fmt.Fprintf(w, "event: %s\n", event) 396 fmt.Fprintf(w, "data: %s\n\n", data) 397 if fw, ok := w.(http.Flusher); ok { 398 fw.Flush() 399 } 400 } 401 writeEvent := func(event *Event) error { 402 data, err := json.Marshal(event) 403 if err != nil { 404 return err 405 } 406 write("network", string(data)) 407 return nil 408 } 409 writeErr := func(err error) { 410 write("error", err.Error()) 411 } 412 413 //检查是否已请求筛选 414 var filters MsgFilters 415 if filterParam := req.URL.Query().Get("filter"); filterParam != "" { 416 var err error 417 filters, err = NewMsgFilters(filterParam) 418 if err != nil { 419 http.Error(w, err.Error(), http.StatusBadRequest) 420 return 421 } 422 } 423 424 w.Header().Set("Content-Type", "text/event-stream; charset=utf-8") 425 w.WriteHeader(http.StatusOK) 426 fmt.Fprintf(w, "\n\n") 427 if fw, ok := w.(http.Flusher); ok { 428 fw.Flush() 429 } 430 431 //可选发送现有节点和连接 432 if req.URL.Query().Get("current") == "true" { 433 snap, err := s.network.Snapshot() 434 if err != nil { 435 writeErr(err) 436 return 437 } 438 for _, node := range snap.Nodes { 439 event := NewEvent(&node.Node) 440 if err := writeEvent(event); err != nil { 441 writeErr(err) 442 return 443 } 444 } 445 for _, conn := range snap.Conns { 446 event := NewEvent(&conn) 447 if err := writeEvent(event); err != nil { 448 writeErr(err) 449 return 450 } 451 } 452 } 453 454 for { 455 select { 456 case event := <-events: 457 //仅发送与筛选器匹配的消息事件 458 if event.Msg != nil && !filters.Match(event.Msg) { 459 continue 460 } 461 if err := writeEvent(event); err != nil { 462 writeErr(err) 463 return 464 } 465 case <-clientGone: 466 return 467 } 468 } 469 } 470 471 //newmsgfilters从URL查询构造消息筛选器集合 472 //参数。 473 // 474 //该参数应为单独过滤器的虚线分隔列表, 475 //每个都具有格式“<proto>:<codes>”,其中<proto>是 476 //Protocol和<codes>是以逗号分隔的消息代码列表。 477 // 478 //“*”或“-1”的消息代码被视为通配符并与任何代码匹配。 479 func NewMsgFilters(filterParam string) (MsgFilters, error) { 480 filters := make(MsgFilters) 481 for _, filter := range strings.Split(filterParam, "-") { 482 protoCodes := strings.SplitN(filter, ":", 2) 483 if len(protoCodes) != 2 || protoCodes[0] == "" || protoCodes[1] == "" { 484 return nil, fmt.Errorf("invalid message filter: %s", filter) 485 } 486 proto := protoCodes[0] 487 for _, code := range strings.Split(protoCodes[1], ",") { 488 if code == "*" || code == "-1" { 489 filters[MsgFilter{Proto: proto, Code: -1}] = struct{}{} 490 continue 491 } 492 n, err := strconv.ParseUint(code, 10, 64) 493 if err != nil { 494 return nil, fmt.Errorf("invalid message code: %s", code) 495 } 496 filters[MsgFilter{Proto: proto, Code: int64(n)}] = struct{}{} 497 } 498 } 499 return filters, nil 500 } 501 502 //msgfilters是用于筛选消息的筛选器的集合 503 //事件 504 type MsgFilters map[MsgFilter]struct{} 505 506 //匹配检查给定消息是否与任何筛选器匹配 507 func (m MsgFilters) Match(msg *Msg) bool { 508 //检查是否存在消息协议的通配符筛选器 509 if _, ok := m[MsgFilter{Proto: msg.Protocol, Code: -1}]; ok { 510 return true 511 } 512 513 //检查是否有消息协议和代码的筛选器 514 if _, ok := m[MsgFilter{Proto: msg.Protocol, Code: int64(msg.Code)}]; ok { 515 return true 516 } 517 518 return false 519 } 520 521 //msgfilter用于根据协议和消息筛选消息事件 522 //代码 523 type MsgFilter struct { 524 //协议与消息的协议匹配 525 Proto string 526 527 //代码与消息的代码匹配,其中-1与所有代码匹配 528 Code int64 529 } 530 531 //创建快照创建网络快照 532 func (s *Server) CreateSnapshot(w http.ResponseWriter, req *http.Request) { 533 snap, err := s.network.Snapshot() 534 if err != nil { 535 http.Error(w, err.Error(), http.StatusInternalServerError) 536 return 537 } 538 539 s.JSON(w, http.StatusOK, snap) 540 } 541 542 //LoadSnapshot将快照加载到网络中 543 func (s *Server) LoadSnapshot(w http.ResponseWriter, req *http.Request) { 544 snap := &Snapshot{} 545 if err := json.NewDecoder(req.Body).Decode(snap); err != nil { 546 http.Error(w, err.Error(), http.StatusBadRequest) 547 return 548 } 549 550 if err := s.network.Load(snap); err != nil { 551 http.Error(w, err.Error(), http.StatusInternalServerError) 552 return 553 } 554 555 s.JSON(w, http.StatusOK, s.network) 556 } 557 558 //createNode使用给定的配置在网络中创建节点 559 func (s *Server) CreateNode(w http.ResponseWriter, req *http.Request) { 560 config := &adapters.NodeConfig{} 561 562 err := json.NewDecoder(req.Body).Decode(config) 563 if err != nil && err != io.EOF { 564 http.Error(w, err.Error(), http.StatusBadRequest) 565 return 566 } 567 568 node, err := s.network.NewNodeWithConfig(config) 569 if err != nil { 570 http.Error(w, err.Error(), http.StatusInternalServerError) 571 return 572 } 573 574 s.JSON(w, http.StatusCreated, node.NodeInfo()) 575 } 576 577 //getnodes返回网络中存在的所有节点 578 func (s *Server) GetNodes(w http.ResponseWriter, req *http.Request) { 579 nodes := s.network.GetNodes() 580 581 infos := make([]*p2p.NodeInfo, len(nodes)) 582 for i, node := range nodes { 583 infos[i] = node.NodeInfo() 584 } 585 586 s.JSON(w, http.StatusOK, infos) 587 } 588 589 //getnode返回节点的详细信息 590 func (s *Server) GetNode(w http.ResponseWriter, req *http.Request) { 591 node := req.Context().Value("node").(*Node) 592 593 s.JSON(w, http.StatusOK, node.NodeInfo()) 594 } 595 596 //startnode启动节点 597 func (s *Server) StartNode(w http.ResponseWriter, req *http.Request) { 598 node := req.Context().Value("node").(*Node) 599 600 if err := s.network.Start(node.ID()); err != nil { 601 http.Error(w, err.Error(), http.StatusInternalServerError) 602 return 603 } 604 605 s.JSON(w, http.StatusOK, node.NodeInfo()) 606 } 607 608 //停止节点停止节点 609 func (s *Server) StopNode(w http.ResponseWriter, req *http.Request) { 610 node := req.Context().Value("node").(*Node) 611 612 if err := s.network.Stop(node.ID()); err != nil { 613 http.Error(w, err.Error(), http.StatusInternalServerError) 614 return 615 } 616 617 s.JSON(w, http.StatusOK, node.NodeInfo()) 618 } 619 620 //ConnectNode将节点连接到对等节点 621 func (s *Server) ConnectNode(w http.ResponseWriter, req *http.Request) { 622 node := req.Context().Value("node").(*Node) 623 peer := req.Context().Value("peer").(*Node) 624 625 if err := s.network.Connect(node.ID(), peer.ID()); err != nil { 626 http.Error(w, err.Error(), http.StatusInternalServerError) 627 return 628 } 629 630 s.JSON(w, http.StatusOK, node.NodeInfo()) 631 } 632 633 //断开节点断开节点与对等节点的连接 634 func (s *Server) DisconnectNode(w http.ResponseWriter, req *http.Request) { 635 node := req.Context().Value("node").(*Node) 636 peer := req.Context().Value("peer").(*Node) 637 638 if err := s.network.Disconnect(node.ID(), peer.ID()); err != nil { 639 http.Error(w, err.Error(), http.StatusInternalServerError) 640 return 641 } 642 643 s.JSON(w, http.StatusOK, node.NodeInfo()) 644 } 645 646 //选项通过返回200 OK响应来响应选项HTTP方法 647 //将“访问控制允许邮件头”邮件头设置为“内容类型” 648 func (s *Server) Options(w http.ResponseWriter, req *http.Request) { 649 w.Header().Set("Access-Control-Allow-Headers", "Content-Type") 650 w.WriteHeader(http.StatusOK) 651 } 652 653 //node rpc通过websocket将rpc请求转发到网络中的节点 654 //连接 655 func (s *Server) NodeRPC(w http.ResponseWriter, req *http.Request) { 656 node := req.Context().Value("node").(*Node) 657 658 handler := func(conn *websocket.Conn) { 659 node.ServeRPC(conn) 660 } 661 662 websocket.Server{Handler: handler}.ServeHTTP(w, req) 663 } 664 665 //servehtp通过委托给 666 //底层httprouter.router 667 func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) { 668 s.router.ServeHTTP(w, req) 669 } 670 671 //get为特定路径的get请求注册一个处理程序 672 func (s *Server) GET(path string, handle http.HandlerFunc) { 673 s.router.GET(path, s.wrapHandler(handle)) 674 } 675 676 //post为特定路径的post请求注册一个处理程序 677 func (s *Server) POST(path string, handle http.HandlerFunc) { 678 s.router.POST(path, s.wrapHandler(handle)) 679 } 680 681 //delete为特定路径的删除请求注册处理程序 682 func (s *Server) DELETE(path string, handle http.HandlerFunc) { 683 s.router.DELETE(path, s.wrapHandler(handle)) 684 } 685 686 //选项为特定路径的选项请求注册处理程序 687 func (s *Server) OPTIONS(path string, handle http.HandlerFunc) { 688 /*outer.options(“/*路径”,s.wraphandler(handle)) 689 } 690 691 //JSON以JSON HTTP响应的形式发送“data” 692 func(s*server)json(w http.responsewriter,status int,数据接口) 693 w.header().set(“内容类型”,“应用程序/json”)。 694 w.writeheader(状态) 695 json.newencoder(w).encode(数据) 696 } 697 698 //wraphandler返回一个httprouter.handle,它将http.handlerFunc包装为 699 //用URL参数中的任何对象填充request.context 700 func(s*server)wraphandler(handlerHTTP.handlerFunc)httprouter.handle_ 701 返回func(w http.responsewriter,req*http.request,params httprouter.params) 702 w.header().set(“访问控制允许来源”,“*”) 703 w.header().set(“访问控制允许方法”、“获取、发布、放置、删除、选项”)。 704 705 ctx:=context.background()。 706 707 如果id:=params.byname(“nodeid”);id!=“{” 708 变量nodeid enode.id 709 VAR节点*节点 710 if nodeid.unmashaltext([]byte(id))==nil 711 node=s.network.getnode(nodeid) 712 }否则{ 713 node=s.network.getnodebyname(id) 714 } 715 如果节点==nil 716 未找到(w,req) 717 返回 718 } 719 ctx=context.withValue(ctx,“节点”,节点) 720 } 721 722 如果id:=params.byname(“peerid”);id!=“{” 723 变量peerid enode.id 724 VaR对等节点 725 if peerID.unmashaltext([]byte(id))==nil 726 peer=s.network.getnode(peerid) 727 }否则{ 728 peer=s.network.getnodebyname(id) 729 } 730 如果peer==nil 731 未找到(w,req) 732 返回 733 } 734 ctx=context.withValue(ctx,“对等”,对等) 735 } 736 737 处理程序(w,req.withContext(ctx))。 738 } 739 } 740