Skip to content

Commit

Permalink
allow rpcdaemon to bind to tcp (erigontech#6184)
Browse files Browse the repository at this point in the history
this pr adds CLI flag to allow the rpcdaemon to bind to a TCP port.

this is very useful if one wants to maintain a remote connection with
the rpcdaemon without using websocket. This is useful because a lot of
issues come with the websocket protocol (compression, max size, etc).
TCP socket gets around these things (it is just raw json over tcp
stream)

the rpc package already supports this, it was just a matter of adding
the bind.

try `echo
'{"jsonrpc":"2.0","method":"eth_blockNumber","id":"1","params":[""]}' |
nc localhost 8548` as a basic test

to test. Subscriptions are also working (idk how to send keepalives with
netcat)

the default rpc.(*Client).Dial method does not support TCP. I have not
included that in this PR. The code for such is as follow

```
// DialTCP create a new TCP client that connects to the given endpoint.
//
// The context is used for the initial connection establishment. It does not
// affect subsequent interactions with the client.
func DialTCP(ctx context.Context, endpoint string) (*Client, error) {
	parsed, err := url.Parse(endpoint)
	if err != nil {
		return nil, err
	}
	ans := make(chan *Client)
	errc := make(chan error)
	go func() {
		client, err := newClient(ctx, func(ctx context.Context) (ServerCodec, error) {
			conn, err := net.Dial("tcp", parsed.Host)
			if err != nil {
				return nil, err
			}
			return NewCodec(conn), nil
		})
		if err != nil {
			errc <- err
			return
		}
		ans <- client
	}()
	select {
	case err := <-errc:
		return nil, err
	case a := <-ans:
		return a, nil
	case <-ctx.Done():
		return nil, ctx.Err()
	}
}

// DialContext creates a new RPC client, just like Dial.
//
// The context is used to cancel or time out the initial connection establishment. It does
// not affect subsequent interactions with the client.
func DialContext(ctx context.Context, rawurl string) (*Client, error) {
	u, err := url.Parse(rawurl)
	if err != nil {
		return nil, err
	}
	switch u.Scheme {
	case "http", "https":
		return DialHTTP(rawurl)
	case "ws", "wss":
		return DialWebsocket(ctx, rawurl, "")
	case "tcp":
		return DialTCP(ctx, rawurl)
	case "stdio":
		return DialStdIO(ctx)
	case "":
		return DialIPC(ctx, rawurl)
	default:
		return nil, fmt.Errorf("no known transport for URL scheme %q", u.Scheme)
	}
}


```

let me know if you would like me to add this to the PR as well. the TCP
connection can then be established with `rpc.Dial("tcp://host:port")`
  • Loading branch information
elee1766 authored Dec 3, 2022
1 parent 8108085 commit 007234b
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 13 deletions.
22 changes: 22 additions & 0 deletions cmd/rpcdaemon/cli/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ func RootCommand() (*cobra.Command, *httpcfg.HttpCfg) {
rootCmd.PersistentFlags().StringVar(&cfg.GRPCListenAddress, "grpc.addr", nodecfg.DefaultGRPCHost, "GRPC server listening interface")
rootCmd.PersistentFlags().IntVar(&cfg.GRPCPort, "grpc.port", nodecfg.DefaultGRPCPort, "GRPC server listening port")
rootCmd.PersistentFlags().BoolVar(&cfg.GRPCHealthCheckEnabled, "grpc.healthcheck", false, "Enable GRPC health check")

rootCmd.PersistentFlags().BoolVar(&cfg.TCPServerEnabled, "tcp", false, "Enable TCP server")
rootCmd.PersistentFlags().StringVar(&cfg.TCPListenAddress, "tcp.addr", nodecfg.DefaultTCPHost, "TCP server listening interface")
rootCmd.PersistentFlags().IntVar(&cfg.TCPPort, "tcp.port", nodecfg.DefaultTCPPort, "TCP server listening port")

rootCmd.PersistentFlags().BoolVar(&cfg.TraceRequests, utils.HTTPTraceFlag.Name, false, "Trace HTTP requests with INFO level")
rootCmd.PersistentFlags().DurationVar(&cfg.HTTPTimeouts.ReadTimeout, "http.timeouts.read", rpccfg.DefaultHTTPTimeouts.ReadTimeout, "Maximum duration for reading the entire request, including the body.")
rootCmd.PersistentFlags().DurationVar(&cfg.HTTPTimeouts.WriteTimeout, "http.timeouts.write", rpccfg.DefaultHTTPTimeouts.WriteTimeout, "Maximum duration before timing out writes of the response. It is reset whenever a new request's header is read")
Expand Down Expand Up @@ -516,6 +521,23 @@ func startRegularRpcServer(ctx context.Context, cfg httpcfg.HttpCfg, rpcAPI []rp
if err != nil {
return fmt.Errorf("could not start RPC api: %w", err)
}

if cfg.TCPServerEnabled {
tcpEndpoint := fmt.Sprintf("%s:%d", cfg.TCPListenAddress, cfg.TCPPort)
tcpListener, err := net.Listen("tcp", tcpEndpoint)
if err != nil {
return fmt.Errorf("could not start TCP Listener: %w", err)
}
go func() {
defer tcpListener.Close()
err := srv.ServeListener(tcpListener)
if err != nil {
log.Error("TCP Listener Fatal Error", "err", err)
}
}()
log.Info("TCP Endpoint opened", "url", tcpEndpoint)
}

info := []interface{}{"url", httpEndpoint, "ws", cfg.WebsocketEnabled,
"ws.compression", cfg.WebsocketCompression, "grpc", cfg.GRPCServerEnabled}

Expand Down
34 changes: 21 additions & 13 deletions cmd/rpcdaemon/cli/httpcfg/http_cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,25 @@ type HttpCfg struct {
StateCache kvcache.CoherentConfig
Snap ethconfig.Snapshot
Sync ethconfig.Sync
GRPCServerEnabled bool
GRPCListenAddress string
GRPCPort int
GRPCHealthCheckEnabled bool
StarknetGRPCAddress string
JWTSecretPath string // Engine API Authentication
TraceRequests bool // Always trace requests in INFO level
HTTPTimeouts rpccfg.HTTPTimeouts
AuthRpcTimeouts rpccfg.HTTPTimeouts
EvmCallTimeout time.Duration
InternalCL bool
LogDirVerbosity string
LogDirPath string

// GRPC server
GRPCServerEnabled bool
GRPCListenAddress string
GRPCPort int
GRPCHealthCheckEnabled bool

// Raw TCP Server
TCPServerEnabled bool
TCPListenAddress string
TCPPort int

StarknetGRPCAddress string
JWTSecretPath string // Engine API Authentication
TraceRequests bool // Always trace requests in INFO level
HTTPTimeouts rpccfg.HTTPTimeouts
AuthRpcTimeouts rpccfg.HTTPTimeouts
EvmCallTimeout time.Duration
InternalCL bool
LogDirVerbosity string
LogDirPath string
}
2 changes: 2 additions & 0 deletions node/nodecfg/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ const (
DefaultWSPort = 8546 // Default TCP port for the websocket RPC server
DefaultGRPCHost = "localhost" // Default host interface for the GRPC server
DefaultGRPCPort = 8547 // Default TCP port for the GRPC server
DefaultTCPHost = "localhost" // default host interrface for TCP RPC server
DefaultTCPPort = 8548 // default TCP port for TCP RPC server
)

// DefaultConfig contains reasonable default settings.
Expand Down

0 comments on commit 007234b

Please sign in to comment.