github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/rpc/websocket.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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-ethereum 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 rpc 18 19 import ( 20 "fmt" 21 "net/http" 22 "os" 23 "strings" 24 "sync" 25 26 "github.com/ethereumproject/go-ethereum/logger" 27 "github.com/ethereumproject/go-ethereum/logger/glog" 28 "golang.org/x/net/websocket" 29 "gopkg.in/fatih/set.v0" 30 ) 31 32 // wsReaderWriterCloser reads and write payloads from and to a websocket connection. 33 type wsReaderWriterCloser struct { 34 c *websocket.Conn 35 } 36 37 // Read will read incoming payload data into p. 38 func (rw *wsReaderWriterCloser) Read(p []byte) (int, error) { 39 return rw.c.Read(p) 40 } 41 42 // Write writes p to the websocket. 43 func (rw *wsReaderWriterCloser) Write(p []byte) (int, error) { 44 return rw.c.Write(p) 45 } 46 47 // Close closes the websocket connection. 48 func (rw *wsReaderWriterCloser) Close() error { 49 return rw.c.Close() 50 } 51 52 // wsHandshakeValidator returns a handler that verifies the origin during the 53 // websocket upgrade process. When a '*' is specified as an allowed origins all 54 // connections are accepted. 55 func wsHandshakeValidator(allowedOrigins []string) func(*websocket.Config, *http.Request) error { 56 origins := set.New() 57 allowAllOrigins := false 58 59 for _, origin := range allowedOrigins { 60 if origin == "*" { 61 allowAllOrigins = true 62 } 63 if origin != "" { 64 origins.Add(strings.ToLower(origin)) 65 } 66 } 67 68 // allow localhost if no allowedOrigins are specified. 69 if len(origins.List()) == 0 { 70 origins.Add("http://localhost") 71 if hostname, err := os.Hostname(); err == nil { 72 origins.Add("http://" + strings.ToLower(hostname)) 73 } 74 } 75 76 glog.V(logger.Debug).Infof("Allowed origin(s) for WS RPC interface %v\n", origins.List()) 77 78 f := func(cfg *websocket.Config, req *http.Request) error { 79 origin := strings.ToLower(req.Header.Get("Origin")) 80 if allowAllOrigins || origins.Has(origin) { 81 return nil 82 } 83 glog.V(logger.Debug).Infof("origin '%s' not allowed on WS-RPC interface\n", origin) 84 return fmt.Errorf("origin %s not allowed", origin) 85 } 86 87 return f 88 } 89 90 // NewWSServer creates a new websocket RPC server around an API provider. 91 func NewWSServer(allowedOrigins string, handler *Server) *http.Server { 92 return &http.Server{ 93 Handler: websocket.Server{ 94 Handshake: wsHandshakeValidator(strings.Split(allowedOrigins, ",")), 95 Handler: func(conn *websocket.Conn) { 96 handler.ServeCodec(NewJSONCodec(&wsReaderWriterCloser{conn}), 97 OptionMethodInvocation|OptionSubscriptions) 98 }, 99 }, 100 } 101 } 102 103 // wsClient represents a RPC client that communicates over websockets with a 104 // RPC server. 105 type wsClient struct { 106 endpoint string 107 connMu sync.Mutex 108 conn *websocket.Conn 109 } 110 111 // connection will return a websocket connection to the RPC server. It will 112 // (re)connect when necessary. 113 func (client *wsClient) connection() (*websocket.Conn, error) { 114 if client.conn != nil { 115 return client.conn, nil 116 } 117 118 origin, err := os.Hostname() 119 if err != nil { 120 return nil, err 121 } 122 123 origin = "http://" + origin 124 client.conn, err = websocket.Dial(client.endpoint, "", origin) 125 126 return client.conn, err 127 } 128 129 // SupportedModules is the collection of modules the RPC server offers. 130 func (client *wsClient) SupportedModules() (map[string]string, error) { 131 return SupportedModules(client) 132 } 133 134 // Send writes the JSON serialized msg to the websocket. It will create a new 135 // websocket connection to the server if the client is currently not connected. 136 func (client *wsClient) Send(msg interface{}) (err error) { 137 client.connMu.Lock() 138 defer client.connMu.Unlock() 139 140 var conn *websocket.Conn 141 if conn, err = client.connection(); err == nil { 142 if err = websocket.JSON.Send(conn, msg); err != nil { 143 client.conn.Close() 144 client.conn = nil 145 } 146 } 147 148 return err 149 } 150 151 // Recv reads a JSON message from the websocket and unmarshals it into msg. 152 func (client *wsClient) Recv(msg interface{}) (err error) { 153 client.connMu.Lock() 154 defer client.connMu.Unlock() 155 156 var conn *websocket.Conn 157 if conn, err = client.connection(); err == nil { 158 if err = websocket.JSON.Receive(conn, msg); err != nil { 159 client.conn.Close() 160 client.conn = nil 161 } 162 } 163 return 164 } 165 166 // Close closes the underlaying websocket connection. 167 func (client *wsClient) Close() { 168 client.connMu.Lock() 169 defer client.connMu.Unlock() 170 171 if client.conn != nil { 172 client.conn.Close() 173 client.conn = nil 174 } 175 176 }