Skip to content

Commit 04e175b

Browse files
fjlkaralabe
authored andcommitted
rpc: implement websockets with github.com/gorilla/websocket (#19866)
* rpc: implement websockets with github.com/gorilla/websocket This change makes package rpc use the github.com/gorilla/websocket package for WebSockets instead of golang.org/x/net/websocket. The new library is more robust and supports all WebSocket features including continuation frames. There are new tests for two issues with the previously-used library: - TestWebsocketClientPing checks handling of Ping frames. - TestWebsocketLargeCall checks whether the request size limit is applied correctly. * rpc: raise HTTP/WebSocket request size limit to 5MB * rpc: remove default origin for client connections The client used to put the local hostname into the Origin header because the server wanted an origin to accept the connection, but that's silly: Origin is for browsers/websites. The nobody would whitelist a particular hostname. Now that the server doesn't need Origin anymore, don't bother setting one for clients. Users who need an origin can use DialWebsocket to create a client with arbitrary origin if needed. * vendor: put golang.org/x/net/websocket back * rpc: don't set Origin header for empty (default) origin * rpc: add HTTP status code to handshake error This makes it easier to debug failing connections. * ethstats: use github.com/gorilla/websocket * rpc: fix lint
1 parent e8141e1 commit 04e175b

32 files changed

+3955
-177
lines changed

ethstats/ethstats.go

+18-18
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import (
2323
"errors"
2424
"fmt"
2525
"math/big"
26-
"net"
26+
"net/http"
2727
"regexp"
2828
"runtime"
2929
"strconv"
@@ -41,7 +41,7 @@ import (
4141
"github.com/ethereum/go-ethereum/log"
4242
"github.com/ethereum/go-ethereum/p2p"
4343
"github.com/ethereum/go-ethereum/rpc"
44-
"golang.org/x/net/websocket"
44+
"github.com/gorilla/websocket"
4545
)
4646

4747
const (
@@ -200,21 +200,21 @@ func (s *Service) loop() {
200200
path := fmt.Sprintf("%s/api", s.host)
201201
urls := []string{path}
202202

203-
if !strings.Contains(path, "://") { // url.Parse and url.IsAbs is unsuitable (https://github.com/golang/go/issues/19779)
203+
// url.Parse and url.IsAbs is unsuitable (https://github.com/golang/go/issues/19779)
204+
if !strings.Contains(path, "://") {
204205
urls = []string{"wss://" + path, "ws://" + path}
205206
}
206207
// Establish a websocket connection to the server on any supported URL
207208
var (
208-
conf *websocket.Config
209209
conn *websocket.Conn
210210
err error
211211
)
212+
dialer := websocket.Dialer{HandshakeTimeout: 5 * time.Second}
213+
header := make(http.Header)
214+
header.Set("origin", "http://localhost")
212215
for _, url := range urls {
213-
if conf, err = websocket.NewConfig(url, "http://localhost/"); err != nil {
214-
continue
215-
}
216-
conf.Dialer = &net.Dialer{Timeout: 5 * time.Second}
217-
if conn, err = websocket.DialConfig(conf); err == nil {
216+
conn, _, err = dialer.Dial(url, header)
217+
if err == nil {
218218
break
219219
}
220220
}
@@ -284,7 +284,7 @@ func (s *Service) readLoop(conn *websocket.Conn) {
284284
for {
285285
// Retrieve the next generic network packet and bail out on error
286286
var msg map[string][]interface{}
287-
if err := websocket.JSON.Receive(conn, &msg); err != nil {
287+
if err := conn.ReadJSON(&msg); err != nil {
288288
log.Warn("Failed to decode stats server message", "err", err)
289289
return
290290
}
@@ -399,12 +399,12 @@ func (s *Service) login(conn *websocket.Conn) error {
399399
login := map[string][]interface{}{
400400
"emit": {"hello", auth},
401401
}
402-
if err := websocket.JSON.Send(conn, login); err != nil {
402+
if err := conn.WriteJSON(login); err != nil {
403403
return err
404404
}
405405
// Retrieve the remote ack or connection termination
406406
var ack map[string][]string
407-
if err := websocket.JSON.Receive(conn, &ack); err != nil || len(ack["emit"]) != 1 || ack["emit"][0] != "ready" {
407+
if err := conn.ReadJSON(&ack); err != nil || len(ack["emit"]) != 1 || ack["emit"][0] != "ready" {
408408
return errors.New("unauthorized")
409409
}
410410
return nil
@@ -441,7 +441,7 @@ func (s *Service) reportLatency(conn *websocket.Conn) error {
441441
"clientTime": start.String(),
442442
}},
443443
}
444-
if err := websocket.JSON.Send(conn, ping); err != nil {
444+
if err := conn.WriteJSON(ping); err != nil {
445445
return err
446446
}
447447
// Wait for the pong request to arrive back
@@ -463,7 +463,7 @@ func (s *Service) reportLatency(conn *websocket.Conn) error {
463463
"latency": latency,
464464
}},
465465
}
466-
return websocket.JSON.Send(conn, stats)
466+
return conn.WriteJSON(stats)
467467
}
468468

469469
// blockStats is the information to report about individual blocks.
@@ -514,7 +514,7 @@ func (s *Service) reportBlock(conn *websocket.Conn, block *types.Block) error {
514514
report := map[string][]interface{}{
515515
"emit": {"block", stats},
516516
}
517-
return websocket.JSON.Send(conn, report)
517+
return conn.WriteJSON(report)
518518
}
519519

520520
// assembleBlockStats retrieves any required metadata to report a single block
@@ -628,7 +628,7 @@ func (s *Service) reportHistory(conn *websocket.Conn, list []uint64) error {
628628
report := map[string][]interface{}{
629629
"emit": {"history", stats},
630630
}
631-
return websocket.JSON.Send(conn, report)
631+
return conn.WriteJSON(report)
632632
}
633633

634634
// pendStats is the information to report about pending transactions.
@@ -658,7 +658,7 @@ func (s *Service) reportPending(conn *websocket.Conn) error {
658658
report := map[string][]interface{}{
659659
"emit": {"pending", stats},
660660
}
661-
return websocket.JSON.Send(conn, report)
661+
return conn.WriteJSON(report)
662662
}
663663

664664
// nodeStats is the information to report about the local node.
@@ -713,5 +713,5 @@ func (s *Service) reportStats(conn *websocket.Conn) error {
713713
report := map[string][]interface{}{
714714
"emit": {"stats", stats},
715715
}
716-
return websocket.JSON.Send(conn, report)
716+
return conn.WriteJSON(report)
717717
}

rpc/client.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,8 @@ var (
4141

4242
const (
4343
// Timeouts
44-
tcpKeepAliveInterval = 30 * time.Second
45-
defaultDialTimeout = 10 * time.Second // used if context has no deadline
46-
subscribeTimeout = 5 * time.Second // overall timeout eth_subscribe, rpc_modules calls
44+
defaultDialTimeout = 10 * time.Second // used if context has no deadline
45+
subscribeTimeout = 5 * time.Second // overall timeout eth_subscribe, rpc_modules calls
4746
)
4847

4948
const (

rpc/http.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import (
3636
)
3737

3838
const (
39-
maxRequestContentLength = 1024 * 512
39+
maxRequestContentLength = 1024 * 1024 * 5
4040
contentType = "application/json"
4141
)
4242

rpc/ipc_unix.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,5 @@ func ipcListen(endpoint string) (net.Listener, error) {
5050

5151
// newIPCConnection will connect to a Unix socket on the given endpoint.
5252
func newIPCConnection(ctx context.Context, endpoint string) (net.Conn, error) {
53-
return dialContext(ctx, "unix", endpoint)
53+
return new(net.Dialer).DialContext(ctx, "unix", endpoint)
5454
}

rpc/json.go

+11-6
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,11 @@ type Conn interface {
141141
SetWriteDeadline(time.Time) error
142142
}
143143

144+
type deadlineCloser interface {
145+
io.Closer
146+
SetWriteDeadline(time.Time) error
147+
}
148+
144149
// ConnRemoteAddr wraps the RemoteAddr operation, which returns a description
145150
// of the peer address of a connection. If a Conn also implements ConnRemoteAddr, this
146151
// description is used in log messages.
@@ -165,12 +170,10 @@ type jsonCodec struct {
165170
decode func(v interface{}) error // decoder to allow multiple transports
166171
encMu sync.Mutex // guards the encoder
167172
encode func(v interface{}) error // encoder to allow multiple transports
168-
conn Conn
173+
conn deadlineCloser
169174
}
170175

171-
// NewCodec creates a new RPC server codec with support for JSON-RPC 2.0 based
172-
// on explicitly given encoding and decoding methods.
173-
func NewCodec(conn Conn, encode, decode func(v interface{}) error) ServerCodec {
176+
func newCodec(conn deadlineCloser, encode, decode func(v interface{}) error) ServerCodec {
174177
codec := &jsonCodec{
175178
closed: make(chan interface{}),
176179
encode: encode,
@@ -183,12 +186,14 @@ func NewCodec(conn Conn, encode, decode func(v interface{}) error) ServerCodec {
183186
return codec
184187
}
185188

186-
// NewJSONCodec creates a new RPC server codec with support for JSON-RPC 2.0.
189+
// NewJSONCodec creates a codec that reads from the given connection. If conn implements
190+
// ConnRemoteAddr, log messages will use it to include the remote address of the
191+
// connection.
187192
func NewJSONCodec(conn Conn) ServerCodec {
188193
enc := json.NewEncoder(conn)
189194
dec := json.NewDecoder(conn)
190195
dec.UseNumber()
191-
return NewCodec(conn, enc.Encode, dec.Decode)
196+
return newCodec(conn, enc.Encode, dec.Decode)
192197
}
193198

194199
func (c *jsonCodec) RemoteAddr() string {

0 commit comments

Comments
 (0)