github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/rpc/websocket.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 12:09:46</date> 10 //</624342665999159296> 11 12 13 package rpc 14 15 import ( 16 "bytes" 17 "context" 18 "crypto/tls" 19 "encoding/json" 20 "fmt" 21 "net" 22 "net/http" 23 "net/url" 24 "os" 25 "strings" 26 "time" 27 28 mapset "github.com/deckarep/golang-set" 29 "github.com/ethereum/go-ethereum/log" 30 "golang.org/x/net/websocket" 31 ) 32 33 //WebSocketJSoncodec是一个自定义的JSON编解码器,具有有效负载大小强制和 34 //特殊数字分析。 35 var websocketJSONCodec = websocket.Codec{ 36 //Marshal也是WebSocket库使用的常用JSON Marshaller。 37 Marshal: func(v interface{}) ([]byte, byte, error) { 38 msg, err := json.Marshal(v) 39 return msg, websocket.TextFrame, err 40 }, 41 //解组是一种特殊的解组器,用于正确转换数字。 42 Unmarshal: func(msg []byte, payloadType byte, v interface{}) error { 43 dec := json.NewDecoder(bytes.NewReader(msg)) 44 dec.UseNumber() 45 46 return dec.Decode(v) 47 }, 48 } 49 50 //WebSocketHandler返回一个为JSON-RPC到WebSocket连接提供服务的处理程序。 51 // 52 //allowedorigins应该是允许的原始URL的逗号分隔列表。 53 //要允许与任何来源的连接,请通过“*”。 54 func (srv *Server) WebsocketHandler(allowedOrigins []string) http.Handler { 55 return websocket.Server{ 56 Handshake: wsHandshakeValidator(allowedOrigins), 57 Handler: func(conn *websocket.Conn) { 58 //创建自定义编码/解码对以强制有效负载大小和数字编码 59 conn.MaxPayloadBytes = maxRequestContentLength 60 61 encoder := func(v interface{}) error { 62 return websocketJSONCodec.Send(conn, v) 63 } 64 decoder := func(v interface{}) error { 65 return websocketJSONCodec.Receive(conn, v) 66 } 67 srv.ServeCodec(NewCodec(conn, encoder, decoder), OptionMethodInvocation|OptionSubscriptions) 68 }, 69 } 70 } 71 72 //newwsserver围绕API提供程序创建新的WebSocket RPC服务器。 73 // 74 //已弃用:使用server.websockethandler 75 func NewWSServer(allowedOrigins []string, srv *Server) *http.Server { 76 return &http.Server{Handler: srv.WebsocketHandler(allowedOrigins)} 77 } 78 79 //wshandshakevalidator返回一个处理程序,该处理程序在 80 //WebSocket升级过程。当将“*”指定为允许的源时,所有 81 //接受连接。 82 func wsHandshakeValidator(allowedOrigins []string) func(*websocket.Config, *http.Request) error { 83 origins := mapset.NewSet() 84 allowAllOrigins := false 85 86 for _, origin := range allowedOrigins { 87 if origin == "*" { 88 allowAllOrigins = true 89 } 90 if origin != "" { 91 origins.Add(strings.ToLower(origin)) 92 } 93 } 94 95 //如果未指定allowedorigins,则允许localhost。 96 if len(origins.ToSlice()) == 0 { 97 origins.Add("http://“本地主机” 98 if hostname, err := os.Hostname(); err == nil { 99 origins.Add("http://“+strings.tolower(主机名)) 100 } 101 } 102 103 log.Debug(fmt.Sprintf("Allowed origin(s) for WS RPC interface %v\n", origins.ToSlice())) 104 105 f := func(cfg *websocket.Config, req *http.Request) error { 106 origin := strings.ToLower(req.Header.Get("Origin")) 107 if allowAllOrigins || origins.Contains(origin) { 108 return nil 109 } 110 log.Warn(fmt.Sprintf("origin '%s' not allowed on WS-RPC interface\n", origin)) 111 return fmt.Errorf("origin %s not allowed", origin) 112 } 113 114 return f 115 } 116 117 //DialWebSocket创建一个新的与JSON-RPC服务器通信的RPC客户端 118 //正在侦听给定的端点。 119 // 120 //上下文用于建立初始连接。它不 121 //影响与客户的后续交互。 122 func DialWebsocket(ctx context.Context, endpoint, origin string) (*Client, error) { 123 if origin == "" { 124 var err error 125 if origin, err = os.Hostname(); err != nil { 126 return nil, err 127 } 128 if strings.HasPrefix(endpoint, "wss") { 129 origin = "https://“+strings.tolower(原点) 130 } else { 131 origin = "http://“+strings.tolower(原点) 132 } 133 } 134 config, err := websocket.NewConfig(endpoint, origin) 135 if err != nil { 136 return nil, err 137 } 138 139 return newClient(ctx, func(ctx context.Context) (net.Conn, error) { 140 return wsDialContext(ctx, config) 141 }) 142 } 143 144 func wsDialContext(ctx context.Context, config *websocket.Config) (*websocket.Conn, error) { 145 var conn net.Conn 146 var err error 147 switch config.Location.Scheme { 148 case "ws": 149 conn, err = dialContext(ctx, "tcp", wsDialAddress(config.Location)) 150 case "wss": 151 dialer := contextDialer(ctx) 152 conn, err = tls.DialWithDialer(dialer, "tcp", wsDialAddress(config.Location), config.TlsConfig) 153 default: 154 err = websocket.ErrBadScheme 155 } 156 if err != nil { 157 return nil, err 158 } 159 ws, err := websocket.NewClient(config, conn) 160 if err != nil { 161 conn.Close() 162 return nil, err 163 } 164 return ws, err 165 } 166 167 var wsPortMap = map[string]string{"ws": "80", "wss": "443"} 168 169 func wsDialAddress(location *url.URL) string { 170 if _, ok := wsPortMap[location.Scheme]; ok { 171 if _, _, err := net.SplitHostPort(location.Host); err != nil { 172 return net.JoinHostPort(location.Host, wsPortMap[location.Scheme]) 173 } 174 } 175 return location.Host 176 } 177 178 func dialContext(ctx context.Context, network, addr string) (net.Conn, error) { 179 d := &net.Dialer{KeepAlive: tcpKeepAliveInterval} 180 return d.DialContext(ctx, network, addr) 181 } 182 183 func contextDialer(ctx context.Context) *net.Dialer { 184 dialer := &net.Dialer{Cancel: ctx.Done(), KeepAlive: tcpKeepAliveInterval} 185 if deadline, ok := ctx.Deadline(); ok { 186 dialer.Deadline = deadline 187 } else { 188 dialer.Deadline = time.Now().Add(defaultDialTimeout) 189 } 190 return dialer 191 } 192