mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 06:06:44 +00:00
console: implemented context timeout
This commit is contained in:
parent
f62fd849d5
commit
47eb6b817e
4 changed files with 105 additions and 32 deletions
|
|
@ -22,6 +22,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/console"
|
"github.com/ethereum/go-ethereum/console"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -67,10 +68,13 @@ func localConsole(ctx *cli.Context) error {
|
||||||
defer node.Stop()
|
defer node.Stop()
|
||||||
|
|
||||||
// Attach to the newly started node and start the JavaScript console
|
// Attach to the newly started node and start the JavaScript console
|
||||||
|
client := rpc.NewClientRestartWrapper(func() rpc.Client {
|
||||||
client, err := node.Attach()
|
client, err := node.Attach()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to attach to the inproc geth: %v", err)
|
utils.Fatalf("Failed to attach to the inproc geth: %v", err)
|
||||||
}
|
}
|
||||||
|
return client
|
||||||
|
})
|
||||||
config := console.Config{
|
config := console.Config{
|
||||||
DataDir: node.DataDir(),
|
DataDir: node.DataDir(),
|
||||||
DocRoot: ctx.GlobalString(utils.JSpathFlag.Name),
|
DocRoot: ctx.GlobalString(utils.JSpathFlag.Name),
|
||||||
|
|
@ -99,10 +103,14 @@ func localConsole(ctx *cli.Context) error {
|
||||||
// console to it.
|
// console to it.
|
||||||
func remoteConsole(ctx *cli.Context) error {
|
func remoteConsole(ctx *cli.Context) error {
|
||||||
// Attach to a remotely running geth instance and start the JavaScript console
|
// Attach to a remotely running geth instance and start the JavaScript console
|
||||||
|
client := rpc.NewClientRestartWrapper(func() rpc.Client {
|
||||||
client, err := utils.NewRemoteRPCClient(ctx)
|
client, err := utils.NewRemoteRPCClient(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Unable to attach to remote geth: %v", err)
|
utils.Fatalf("Unable to attach to remote geth: %v", err)
|
||||||
}
|
}
|
||||||
|
return client
|
||||||
|
})
|
||||||
|
|
||||||
config := console.Config{
|
config := console.Config{
|
||||||
DataDir: utils.MustMakeDataDir(ctx),
|
DataDir: utils.MustMakeDataDir(ctx),
|
||||||
DocRoot: ctx.GlobalString(utils.JSpathFlag.Name),
|
DocRoot: ctx.GlobalString(utils.JSpathFlag.Name),
|
||||||
|
|
@ -137,10 +145,14 @@ func ephemeralConsole(ctx *cli.Context) error {
|
||||||
defer node.Stop()
|
defer node.Stop()
|
||||||
|
|
||||||
// Attach to the newly started node and start the JavaScript console
|
// Attach to the newly started node and start the JavaScript console
|
||||||
|
client := rpc.NewClientRestartWrapper(func() rpc.Client {
|
||||||
client, err := node.Attach()
|
client, err := node.Attach()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to attach to the inproc geth: %v", err)
|
utils.Fatalf("Failed to attach to the inproc geth: %v", err)
|
||||||
}
|
}
|
||||||
|
return client
|
||||||
|
})
|
||||||
|
|
||||||
config := console.Config{
|
config := console.Config{
|
||||||
DataDir: node.DataDir(),
|
DataDir: node.DataDir(),
|
||||||
DocRoot: ctx.GlobalString(utils.JSpathFlag.Name),
|
DocRoot: ctx.GlobalString(utils.JSpathFlag.Name),
|
||||||
|
|
|
||||||
|
|
@ -26,18 +26,20 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/robertkrimen/otto"
|
"github.com/robertkrimen/otto"
|
||||||
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// bridge is a collection of JavaScript utility methods to bride the .js runtime
|
// bridge is a collection of JavaScript utility methods to bride the .js runtime
|
||||||
// environment and the Go RPC connection backing the remote method calls.
|
// environment and the Go RPC connection backing the remote method calls.
|
||||||
type bridge struct {
|
type bridge struct {
|
||||||
client rpc.Client // RPC client to execute Ethereum requests through
|
client *rpc.ClientRestartWrapper // RPC client to execute Ethereum requests through
|
||||||
prompter UserPrompter // Input prompter to allow interactive user feedback
|
prompter UserPrompter // Input prompter to allow interactive user feedback
|
||||||
printer io.Writer // Output writer to serialize any display strings to
|
printer io.Writer // Output writer to serialize any display strings to
|
||||||
|
ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
// newBridge creates a new JavaScript wrapper around an RPC client.
|
// newBridge creates a new JavaScript wrapper around an RPC client.
|
||||||
func newBridge(client rpc.Client, prompter UserPrompter, printer io.Writer) *bridge {
|
func newBridge(client *rpc.ClientRestartWrapper, prompter UserPrompter, printer io.Writer) *bridge {
|
||||||
return &bridge{
|
return &bridge{
|
||||||
client: client,
|
client: client,
|
||||||
prompter: prompter,
|
prompter: prompter,
|
||||||
|
|
@ -45,6 +47,10 @@ func newBridge(client rpc.Client, prompter UserPrompter, printer io.Writer) *bri
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *bridge) setContext(ctx context.Context) {
|
||||||
|
b.ctx = ctx
|
||||||
|
}
|
||||||
|
|
||||||
// NewAccount is a wrapper around the personal.newAccount RPC method that uses a
|
// NewAccount is a wrapper around the personal.newAccount RPC method that uses a
|
||||||
// non-echoing password prompt to aquire the passphrase and executes the original
|
// non-echoing password prompt to aquire the passphrase and executes the original
|
||||||
// RPC method (saved in jeth.newAccount) with it to actually execute the RPC call.
|
// RPC method (saved in jeth.newAccount) with it to actually execute the RPC call.
|
||||||
|
|
@ -223,11 +229,29 @@ func (b *bridge) Send(call otto.FunctionCall) (response otto.Value) {
|
||||||
|
|
||||||
for i, req := range reqs {
|
for i, req := range reqs {
|
||||||
// Execute the RPC request and parse the reply
|
// Execute the RPC request and parse the reply
|
||||||
if err = b.client.Send(&req); err != nil {
|
client := b.client.Client()
|
||||||
|
if err = client.Send(&req); err != nil {
|
||||||
return newErrorResponse(call, -32603, err.Error(), req.Id)
|
return newErrorResponse(call, -32603, err.Error(), req.Id)
|
||||||
}
|
}
|
||||||
|
errc := make(chan error, 1)
|
||||||
|
errc2 := make(chan error)
|
||||||
|
go func(){
|
||||||
|
if b.ctx != nil {
|
||||||
|
select {
|
||||||
|
case <-b.ctx.Done():
|
||||||
|
b.client.Restart()
|
||||||
|
errc2 <- b.ctx.Err()
|
||||||
|
case err := <-errc:
|
||||||
|
errc2 <- err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errc2 <- <-errc
|
||||||
|
}
|
||||||
|
}()
|
||||||
result := make(map[string]interface{})
|
result := make(map[string]interface{})
|
||||||
if err = b.client.Recv(&result); err != nil {
|
errc <- client.Recv(&result)
|
||||||
|
err := <-errc2
|
||||||
|
if err != nil {
|
||||||
return newErrorResponse(call, -32603, err.Error(), req.Id)
|
return newErrorResponse(call, -32603, err.Error(), req.Id)
|
||||||
}
|
}
|
||||||
// Feed the reply back into the JavaScript runtime environment
|
// Feed the reply back into the JavaScript runtime environment
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/internal/jsre"
|
"github.com/ethereum/go-ethereum/internal/jsre"
|
||||||
"github.com/ethereum/go-ethereum/internal/web3ext"
|
"github.com/ethereum/go-ethereum/internal/web3ext"
|
||||||
|
|
@ -33,6 +34,7 @@ import (
|
||||||
"github.com/mattn/go-colorable"
|
"github.com/mattn/go-colorable"
|
||||||
"github.com/peterh/liner"
|
"github.com/peterh/liner"
|
||||||
"github.com/robertkrimen/otto"
|
"github.com/robertkrimen/otto"
|
||||||
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -52,7 +54,7 @@ const DefaultPrompt = "> "
|
||||||
type Config struct {
|
type Config struct {
|
||||||
DataDir string // Data directory to store the console history at
|
DataDir string // Data directory to store the console history at
|
||||||
DocRoot string // Filesystem path from where to load JavaScript files from
|
DocRoot string // Filesystem path from where to load JavaScript files from
|
||||||
Client rpc.Client // RPC client to execute Ethereum requests through
|
Client *rpc.ClientRestartWrapper // RPC client to execute Ethereum requests through
|
||||||
Prompt string // Input prompt prefix string (defaults to DefaultPrompt)
|
Prompt string // Input prompt prefix string (defaults to DefaultPrompt)
|
||||||
Prompter UserPrompter // Input prompter to allow interactive user feedback (defaults to TerminalPrompter)
|
Prompter UserPrompter // Input prompter to allow interactive user feedback (defaults to TerminalPrompter)
|
||||||
Printer io.Writer // Output writer to serialize any display strings to (defaults to os.Stdout)
|
Printer io.Writer // Output writer to serialize any display strings to (defaults to os.Stdout)
|
||||||
|
|
@ -63,13 +65,14 @@ type Config struct {
|
||||||
// JavaScript console attached to a running node via an external or in-process RPC
|
// JavaScript console attached to a running node via an external or in-process RPC
|
||||||
// client.
|
// client.
|
||||||
type Console struct {
|
type Console struct {
|
||||||
client rpc.Client // RPC client to execute Ethereum requests through
|
client *rpc.ClientRestartWrapper // RPC client to execute Ethereum requests through
|
||||||
jsre *jsre.JSRE // JavaScript runtime environment running the interpreter
|
jsre *jsre.JSRE // JavaScript runtime environment running the interpreter
|
||||||
prompt string // Input prompt prefix string
|
prompt string // Input prompt prefix string
|
||||||
prompter UserPrompter // Input prompter to allow interactive user feedback
|
prompter UserPrompter // Input prompter to allow interactive user feedback
|
||||||
histPath string // Absolute path to the console scrollback history
|
histPath string // Absolute path to the console scrollback history
|
||||||
history []string // Scroll history maintained by the console
|
history []string // Scroll history maintained by the console
|
||||||
printer io.Writer // Output writer to serialize any display strings to
|
printer io.Writer // Output writer to serialize any display strings to
|
||||||
|
setContext func(context.Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(config Config) (*Console, error) {
|
func New(config Config) (*Console, error) {
|
||||||
|
|
@ -103,6 +106,7 @@ func New(config Config) (*Console, error) {
|
||||||
func (c *Console) init(preload []string) error {
|
func (c *Console) init(preload []string) error {
|
||||||
// Initialize the JavaScript <-> Go RPC bridge
|
// Initialize the JavaScript <-> Go RPC bridge
|
||||||
bridge := newBridge(c.client, c.prompter, c.printer)
|
bridge := newBridge(c.client, c.prompter, c.printer)
|
||||||
|
c.setContext = bridge.setContext
|
||||||
c.jsre.Set("jeth", struct{}{})
|
c.jsre.Set("jeth", struct{}{})
|
||||||
|
|
||||||
jethObj, _ := c.jsre.Get("jeth")
|
jethObj, _ := c.jsre.Get("jeth")
|
||||||
|
|
@ -127,7 +131,7 @@ func (c *Console) init(preload []string) error {
|
||||||
return fmt.Errorf("web3 provider: %v", err)
|
return fmt.Errorf("web3 provider: %v", err)
|
||||||
}
|
}
|
||||||
// Load the supported APIs into the JavaScript runtime environment
|
// Load the supported APIs into the JavaScript runtime environment
|
||||||
apis, err := c.client.SupportedModules()
|
apis, err := c.client.Client().SupportedModules()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("api modules: %v", err)
|
return fmt.Errorf("api modules: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -253,7 +257,7 @@ func (c *Console) Welcome() {
|
||||||
console.log(" datadir: " + admin.datadir);
|
console.log(" datadir: " + admin.datadir);
|
||||||
`)
|
`)
|
||||||
// List all the supported modules for the user to call
|
// List all the supported modules for the user to call
|
||||||
if apis, err := c.client.SupportedModules(); err == nil {
|
if apis, err := c.client.Client().SupportedModules(); err == nil {
|
||||||
modules := make([]string, 0, len(apis))
|
modules := make([]string, 0, len(apis))
|
||||||
for api, version := range apis {
|
for api, version := range apis {
|
||||||
modules = append(modules, fmt.Sprintf("%s:%s", api, version))
|
modules = append(modules, fmt.Sprintf("%s:%s", api, version))
|
||||||
|
|
@ -347,7 +351,12 @@ func (c *Console) Interactive() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
done := make(chan struct{})
|
||||||
|
ctx, _ := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
|
c.setContext(ctx)
|
||||||
c.Evaluate(input)
|
c.Evaluate(input)
|
||||||
|
c.setContext(nil)
|
||||||
|
close(done)
|
||||||
input = ""
|
input = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
28
rpc/types.go
28
rpc/types.go
|
|
@ -284,3 +284,31 @@ type Client interface {
|
||||||
|
|
||||||
Close()
|
Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ClientRestartWrapper struct {
|
||||||
|
client Client
|
||||||
|
newClientFn func() Client
|
||||||
|
mu sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClientRestartWrapper(newClientFn func() Client) *ClientRestartWrapper {
|
||||||
|
return &ClientRestartWrapper {
|
||||||
|
client: newClientFn(),
|
||||||
|
newClientFn: newClientFn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *ClientRestartWrapper) Client() Client {
|
||||||
|
rw.mu.RLock()
|
||||||
|
defer rw.mu.RUnlock()
|
||||||
|
|
||||||
|
return rw.client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *ClientRestartWrapper) Restart() {
|
||||||
|
rw.mu.Lock()
|
||||||
|
defer rw.mu.Unlock()
|
||||||
|
|
||||||
|
rw.client.Close()
|
||||||
|
rw.client = rw.newClientFn()
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue