| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- // Copyright 2015 The go-ethereum Authors
- // This file is part of the go-ethereum library.
- //
- // The go-ethereum library is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Lesser General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // The go-ethereum library is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Lesser General Public License for more details.
- //
- // You should have received a copy of the GNU Lesser General Public License
- // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
- package rpc
- import (
- "fmt"
- "net/http"
- "os"
- "strings"
- "sync"
- "github.com/ethereum/go-ethereum/logger"
- "github.com/ethereum/go-ethereum/logger/glog"
- "golang.org/x/net/websocket"
- "gopkg.in/fatih/set.v0"
- )
- // wsReaderWriterCloser reads and write payloads from and to a websocket connection.
- type wsReaderWriterCloser struct {
- c *websocket.Conn
- }
- // Read will read incoming payload data into p.
- func (rw *wsReaderWriterCloser) Read(p []byte) (int, error) {
- return rw.c.Read(p)
- }
- // Write writes p to the websocket.
- func (rw *wsReaderWriterCloser) Write(p []byte) (int, error) {
- return rw.c.Write(p)
- }
- // Close closes the websocket connection.
- func (rw *wsReaderWriterCloser) Close() error {
- return rw.c.Close()
- }
- // wsHandshakeValidator returns a handler that verifies the origin during the
- // websocket upgrade process. When a '*' is specified as an allowed origins all
- // connections are accepted.
- func wsHandshakeValidator(allowedOrigins []string) func(*websocket.Config, *http.Request) error {
- origins := set.New()
- allowAllOrigins := false
- for _, origin := range allowedOrigins {
- if origin == "*" {
- allowAllOrigins = true
- }
- if origin != "" {
- origins.Add(strings.ToLower(origin))
- }
- }
- // allow localhost if no allowedOrigins are specified.
- if len(origins.List()) == 0 {
- origins.Add("http://localhost")
- if hostname, err := os.Hostname(); err == nil {
- origins.Add("http://" + strings.ToLower(hostname))
- }
- }
- glog.V(logger.Debug).Infof("Allowed origin(s) for WS RPC interface %v\n", origins.List())
- f := func(cfg *websocket.Config, req *http.Request) error {
- origin := strings.ToLower(req.Header.Get("Origin"))
- if allowAllOrigins || origins.Has(origin) {
- return nil
- }
- glog.V(logger.Debug).Infof("origin '%s' not allowed on WS-RPC interface\n", origin)
- return fmt.Errorf("origin %s not allowed", origin)
- }
- return f
- }
- // NewWSServer creates a new websocket RPC server around an API provider.
- func NewWSServer(allowedOrigins string, handler *Server) *http.Server {
- return &http.Server{
- Handler: websocket.Server{
- Handshake: wsHandshakeValidator(strings.Split(allowedOrigins, ",")),
- Handler: func(conn *websocket.Conn) {
- handler.ServeCodec(NewJSONCodec(&wsReaderWriterCloser{conn}),
- OptionMethodInvocation|OptionSubscriptions)
- },
- },
- }
- }
- // wsClient represents a RPC client that communicates over websockets with a
- // RPC server.
- type wsClient struct {
- endpoint string
- connMu sync.Mutex
- conn *websocket.Conn
- }
- // NewWSClientj creates a new RPC client that communicates with a RPC server
- // that is listening on the given endpoint using JSON encoding.
- func NewWSClient(endpoint string) (Client, error) {
- return &wsClient{endpoint: endpoint}, nil
- }
- // connection will return a websocket connection to the RPC server. It will
- // (re)connect when necessary.
- func (client *wsClient) connection() (*websocket.Conn, error) {
- if client.conn != nil {
- return client.conn, nil
- }
- origin, err := os.Hostname()
- if err != nil {
- return nil, err
- }
- origin = "http://" + origin
- client.conn, err = websocket.Dial(client.endpoint, "", origin)
- return client.conn, err
- }
- // SupportedModules is the collection of modules the RPC server offers.
- func (client *wsClient) SupportedModules() (map[string]string, error) {
- return SupportedModules(client)
- }
- // Send writes the JSON serialized msg to the websocket. It will create a new
- // websocket connection to the server if the client is currently not connected.
- func (client *wsClient) Send(msg interface{}) (err error) {
- client.connMu.Lock()
- defer client.connMu.Unlock()
- var conn *websocket.Conn
- if conn, err = client.connection(); err == nil {
- if err = websocket.JSON.Send(conn, msg); err != nil {
- client.conn.Close()
- client.conn = nil
- }
- }
- return err
- }
- // Recv reads a JSON message from the websocket and unmarshals it into msg.
- func (client *wsClient) Recv(msg interface{}) (err error) {
- client.connMu.Lock()
- defer client.connMu.Unlock()
- var conn *websocket.Conn
- if conn, err = client.connection(); err == nil {
- if err = websocket.JSON.Receive(conn, msg); err != nil {
- client.conn.Close()
- client.conn = nil
- }
- }
- return
- }
- // Close closes the underlaying websocket connection.
- func (client *wsClient) Close() {
- client.connMu.Lock()
- defer client.connMu.Unlock()
- if client.conn != nil {
- client.conn.Close()
- client.conn = nil
- }
- }
|