github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/rpc/websocket.go (about) 1 2 //此源码被清华学神尹成大魔王专业翻译分析并修改 3 //尹成QQ77025077 4 //尹成微信18510341407 5 //尹成所在QQ群721929980 6 //尹成邮箱 yinc13@mails.tsinghua.edu.cn 7 //尹成毕业于清华大学,微软区块链领域全球最有价值专家 8 //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620 9 //版权所有2015 Go Ethereum作者 10 //此文件是Go以太坊库的一部分。 11 // 12 //Go-Ethereum库是免费软件:您可以重新分发它和/或修改 13 //根据GNU发布的较低通用公共许可证的条款 14 //自由软件基金会,或者许可证的第3版,或者 15 //(由您选择)任何更高版本。 16 // 17 //Go以太坊图书馆的发行目的是希望它会有用, 18 //但没有任何保证;甚至没有 19 //适销性或特定用途的适用性。见 20 //GNU较低的通用公共许可证,了解更多详细信息。 21 // 22 //你应该收到一份GNU较低级别的公共许可证副本 23 //以及Go以太坊图书馆。如果没有,请参见<http://www.gnu.org/licenses/>。 24 25 package rpc 26 27 import ( 28 "bytes" 29 "context" 30 "crypto/tls" 31 "encoding/json" 32 "fmt" 33 "net" 34 "net/http" 35 "net/url" 36 "os" 37 "strings" 38 "time" 39 40 mapset "github.com/deckarep/golang-set" 41 "github.com/ethereum/go-ethereum/log" 42 "golang.org/x/net/websocket" 43 ) 44 45 //WebSocketJSoncodec是一个自定义的JSON编解码器,具有有效负载大小强制和 46 //特殊数字分析。 47 var websocketJSONCodec = websocket.Codec{ 48 //Marshal也是WebSocket库使用的常用JSON Marshaller。 49 Marshal: func(v interface{}) ([]byte, byte, error) { 50 msg, err := json.Marshal(v) 51 return msg, websocket.TextFrame, err 52 }, 53 //解组是一种特殊的解组器,用于正确转换数字。 54 Unmarshal: func(msg []byte, payloadType byte, v interface{}) error { 55 dec := json.NewDecoder(bytes.NewReader(msg)) 56 dec.UseNumber() 57 58 return dec.Decode(v) 59 }, 60 } 61 62 //WebSocketHandler返回一个为JSON-RPC到WebSocket连接提供服务的处理程序。 63 // 64 //allowedorigins应该是允许的原始URL的逗号分隔列表。 65 //要允许与任何来源的连接,请通过“*”。 66 func (srv *Server) WebsocketHandler(allowedOrigins []string) http.Handler { 67 return websocket.Server{ 68 Handshake: wsHandshakeValidator(allowedOrigins), 69 Handler: func(conn *websocket.Conn) { 70 //创建自定义编码/解码对以强制有效负载大小和数字编码 71 conn.MaxPayloadBytes = maxRequestContentLength 72 73 encoder := func(v interface{}) error { 74 return websocketJSONCodec.Send(conn, v) 75 } 76 decoder := func(v interface{}) error { 77 return websocketJSONCodec.Receive(conn, v) 78 } 79 srv.ServeCodec(NewCodec(conn, encoder, decoder), OptionMethodInvocation|OptionSubscriptions) 80 }, 81 } 82 } 83 84 //newwsserver围绕API提供程序创建新的WebSocket RPC服务器。 85 // 86 //已弃用:使用server.websockethandler 87 func NewWSServer(allowedOrigins []string, srv *Server) *http.Server { 88 return &http.Server{Handler: srv.WebsocketHandler(allowedOrigins)} 89 } 90 91 //wshandshakevalidator返回一个处理程序,该处理程序在 92 //WebSocket升级过程。当将“*”指定为允许的源时,所有 93 //接受连接。 94 func wsHandshakeValidator(allowedOrigins []string) func(*websocket.Config, *http.Request) error { 95 origins := mapset.NewSet() 96 allowAllOrigins := false 97 98 for _, origin := range allowedOrigins { 99 if origin == "*" { 100 allowAllOrigins = true 101 } 102 if origin != "" { 103 origins.Add(strings.ToLower(origin)) 104 } 105 } 106 107 //如果未指定allowedorigins,则允许localhost。 108 if len(origins.ToSlice()) == 0 { 109 origins.Add("http://“本地主机” 110 if hostname, err := os.Hostname(); err == nil { 111 origins.Add("http://“+strings.tolower(主机名)) 112 } 113 } 114 115 log.Debug(fmt.Sprintf("Allowed origin(s) for WS RPC interface %v\n", origins.ToSlice())) 116 117 f := func(cfg *websocket.Config, req *http.Request) error { 118 origin := strings.ToLower(req.Header.Get("Origin")) 119 if allowAllOrigins || origins.Contains(origin) { 120 return nil 121 } 122 log.Warn(fmt.Sprintf("origin '%s' not allowed on WS-RPC interface\n", origin)) 123 return fmt.Errorf("origin %s not allowed", origin) 124 } 125 126 return f 127 } 128 129 //DialWebSocket创建一个新的与JSON-RPC服务器通信的RPC客户端 130 //正在侦听给定的端点。 131 // 132 //上下文用于建立初始连接。它不 133 //影响与客户的后续交互。 134 func DialWebsocket(ctx context.Context, endpoint, origin string) (*Client, error) { 135 if origin == "" { 136 var err error 137 if origin, err = os.Hostname(); err != nil { 138 return nil, err 139 } 140 if strings.HasPrefix(endpoint, "wss") { 141 origin = "https://“+strings.tolower(原点) 142 } else { 143 origin = "http://“+strings.tolower(原点) 144 } 145 } 146 config, err := websocket.NewConfig(endpoint, origin) 147 if err != nil { 148 return nil, err 149 } 150 151 return newClient(ctx, func(ctx context.Context) (net.Conn, error) { 152 return wsDialContext(ctx, config) 153 }) 154 } 155 156 func wsDialContext(ctx context.Context, config *websocket.Config) (*websocket.Conn, error) { 157 var conn net.Conn 158 var err error 159 switch config.Location.Scheme { 160 case "ws": 161 conn, err = dialContext(ctx, "tcp", wsDialAddress(config.Location)) 162 case "wss": 163 dialer := contextDialer(ctx) 164 conn, err = tls.DialWithDialer(dialer, "tcp", wsDialAddress(config.Location), config.TlsConfig) 165 default: 166 err = websocket.ErrBadScheme 167 } 168 if err != nil { 169 return nil, err 170 } 171 ws, err := websocket.NewClient(config, conn) 172 if err != nil { 173 conn.Close() 174 return nil, err 175 } 176 return ws, err 177 } 178 179 var wsPortMap = map[string]string{"ws": "80", "wss": "443"} 180 181 func wsDialAddress(location *url.URL) string { 182 if _, ok := wsPortMap[location.Scheme]; ok { 183 if _, _, err := net.SplitHostPort(location.Host); err != nil { 184 return net.JoinHostPort(location.Host, wsPortMap[location.Scheme]) 185 } 186 } 187 return location.Host 188 } 189 190 func dialContext(ctx context.Context, network, addr string) (net.Conn, error) { 191 d := &net.Dialer{KeepAlive: tcpKeepAliveInterval} 192 return d.DialContext(ctx, network, addr) 193 } 194 195 func contextDialer(ctx context.Context) *net.Dialer { 196 dialer := &net.Dialer{Cancel: ctx.Done(), KeepAlive: tcpKeepAliveInterval} 197 if deadline, ok := ctx.Deadline(); ok { 198 dialer.Deadline = deadline 199 } else { 200 dialer.Deadline = time.Now().Add(defaultDialTimeout) 201 } 202 return dialer 203 }