mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-24 21:56:43 +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/console"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
|
|
@ -67,10 +68,13 @@ func localConsole(ctx *cli.Context) error {
|
|||
defer node.Stop()
|
||||
|
||||
// Attach to the newly started node and start the JavaScript console
|
||||
client := rpc.NewClientRestartWrapper(func() rpc.Client {
|
||||
client, err := node.Attach()
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to attach to the inproc geth: %v", err)
|
||||
}
|
||||
return client
|
||||
})
|
||||
config := console.Config{
|
||||
DataDir: node.DataDir(),
|
||||
DocRoot: ctx.GlobalString(utils.JSpathFlag.Name),
|
||||
|
|
@ -99,10 +103,14 @@ func localConsole(ctx *cli.Context) error {
|
|||
// console to it.
|
||||
func remoteConsole(ctx *cli.Context) error {
|
||||
// Attach to a remotely running geth instance and start the JavaScript console
|
||||
client := rpc.NewClientRestartWrapper(func() rpc.Client {
|
||||
client, err := utils.NewRemoteRPCClient(ctx)
|
||||
if err != nil {
|
||||
utils.Fatalf("Unable to attach to remote geth: %v", err)
|
||||
}
|
||||
return client
|
||||
})
|
||||
|
||||
config := console.Config{
|
||||
DataDir: utils.MustMakeDataDir(ctx),
|
||||
DocRoot: ctx.GlobalString(utils.JSpathFlag.Name),
|
||||
|
|
@ -137,10 +145,14 @@ func ephemeralConsole(ctx *cli.Context) error {
|
|||
defer node.Stop()
|
||||
|
||||
// Attach to the newly started node and start the JavaScript console
|
||||
client := rpc.NewClientRestartWrapper(func() rpc.Client {
|
||||
client, err := node.Attach()
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to attach to the inproc geth: %v", err)
|
||||
}
|
||||
return client
|
||||
})
|
||||
|
||||
config := console.Config{
|
||||
DataDir: node.DataDir(),
|
||||
DocRoot: ctx.GlobalString(utils.JSpathFlag.Name),
|
||||
|
|
|
|||
|
|
@ -26,18 +26,20 @@ import (
|
|||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/robertkrimen/otto"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// bridge is a collection of JavaScript utility methods to bride the .js runtime
|
||||
// environment and the Go RPC connection backing the remote method calls.
|
||||
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
|
||||
printer io.Writer // Output writer to serialize any display strings to
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// 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{
|
||||
client: client,
|
||||
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
|
||||
// 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.
|
||||
|
|
@ -223,11 +229,29 @@ func (b *bridge) Send(call otto.FunctionCall) (response otto.Value) {
|
|||
|
||||
for i, req := range reqs {
|
||||
// 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)
|
||||
}
|
||||
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{})
|
||||
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)
|
||||
}
|
||||
// Feed the reply back into the JavaScript runtime environment
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import (
|
|||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/internal/jsre"
|
||||
"github.com/ethereum/go-ethereum/internal/web3ext"
|
||||
|
|
@ -33,6 +34,7 @@ import (
|
|||
"github.com/mattn/go-colorable"
|
||||
"github.com/peterh/liner"
|
||||
"github.com/robertkrimen/otto"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -52,7 +54,7 @@ const DefaultPrompt = "> "
|
|||
type Config struct {
|
||||
DataDir string // Data directory to store the console history at
|
||||
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)
|
||||
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)
|
||||
|
|
@ -63,13 +65,14 @@ type Config struct {
|
|||
// JavaScript console attached to a running node via an external or in-process RPC
|
||||
// client.
|
||||
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
|
||||
prompt string // Input prompt prefix string
|
||||
prompter UserPrompter // Input prompter to allow interactive user feedback
|
||||
histPath string // Absolute path to the console scrollback history
|
||||
history []string // Scroll history maintained by the console
|
||||
printer io.Writer // Output writer to serialize any display strings to
|
||||
setContext func(context.Context)
|
||||
}
|
||||
|
||||
func New(config Config) (*Console, error) {
|
||||
|
|
@ -103,6 +106,7 @@ func New(config Config) (*Console, error) {
|
|||
func (c *Console) init(preload []string) error {
|
||||
// Initialize the JavaScript <-> Go RPC bridge
|
||||
bridge := newBridge(c.client, c.prompter, c.printer)
|
||||
c.setContext = bridge.setContext
|
||||
c.jsre.Set("jeth", struct{}{})
|
||||
|
||||
jethObj, _ := c.jsre.Get("jeth")
|
||||
|
|
@ -127,7 +131,7 @@ func (c *Console) init(preload []string) error {
|
|||
return fmt.Errorf("web3 provider: %v", err)
|
||||
}
|
||||
// Load the supported APIs into the JavaScript runtime environment
|
||||
apis, err := c.client.SupportedModules()
|
||||
apis, err := c.client.Client().SupportedModules()
|
||||
if err != nil {
|
||||
return fmt.Errorf("api modules: %v", err)
|
||||
}
|
||||
|
|
@ -253,7 +257,7 @@ func (c *Console) Welcome() {
|
|||
console.log(" datadir: " + admin.datadir);
|
||||
`)
|
||||
// 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))
|
||||
for api, version := range apis {
|
||||
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.setContext(nil)
|
||||
close(done)
|
||||
input = ""
|
||||
}
|
||||
}
|
||||
|
|
|
|||
28
rpc/types.go
28
rpc/types.go
|
|
@ -284,3 +284,31 @@ type Client interface {
|
|||
|
||||
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