websocket.go 5.1 KB

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