diff --git a/cmd/counter/main.go b/cmd/counter/main.go new file mode 100644 index 00000000..36485996 --- /dev/null +++ b/cmd/counter/main.go @@ -0,0 +1,22 @@ +package main + +import ( + . "github.com/tendermint/go-common" + "github.com/tendermint/tmsp/example" + "github.com/tendermint/tmsp/server" +) + +func main() { + + // Start the listener + _, err := server.StartListener("tcp://127.0.0.1:8080", example.NewCounterApplication()) + if err != nil { + Exit(err.Error()) + } + + // Wait forever + TrapSignal(func() { + // Cleanup + }) + +} diff --git a/cmd/tmsp/cli.go b/cmd/tmsp/cli.go index 1eab662e..242ffa60 100644 --- a/cmd/tmsp/cli.go +++ b/cmd/tmsp/cli.go @@ -1,9 +1,13 @@ package main import ( + "bufio" + "encoding/hex" "fmt" + "io" "net" "os" + "strings" . "github.com/tendermint/go-common" "github.com/tendermint/go-wire" @@ -12,6 +16,9 @@ import ( "github.com/codegangsta/cli" ) +// connection is a global variable so it can be reused by the console +var conn net.Conn + func main() { app := cli.NewApp() app.Name = "cli" @@ -24,6 +31,34 @@ func main() { }, } app.Commands = []cli.Command{ + { + Name: "batch", + Usage: "Run a batch of tmsp commands against an application", + Action: func(c *cli.Context) { + cmdBatch(app, c) + }, + }, + { + Name: "console", + Usage: "Start an interactive tmsp console for multiple commands", + Action: func(c *cli.Context) { + cmdConsole(app, c) + }, + }, + { + Name: "info", + Usage: "Get some info about the application", + Action: func(c *cli.Context) { + cmdInfo(c) + }, + }, + { + Name: "set_option", + Usage: "Set an option on the application", + Action: func(c *cli.Context) { + cmdSetOption(c) + }, + }, { Name: "append_tx", Usage: "Append a new tx to application", @@ -53,46 +88,115 @@ func main() { }, }, } + app.Before = before app.Run(os.Args) } +func before(c *cli.Context) error { + if conn == nil { + var err error + conn, err = Connect(c.GlobalString("address")) + if err != nil { + Exit(err.Error()) + } + } + return nil +} + //-------------------------------------------------------------------------------- +func cmdBatch(app *cli.App, c *cli.Context) { + bufReader := bufio.NewReader(os.Stdin) + for { + line, more, err := bufReader.ReadLine() + if more { + Exit("input line is too long") + } else if err == io.EOF { + break + } else if err != nil { + Exit(err.Error()) + } + args := []string{"tmsp"} + args = append(args, strings.Split(string(line), " ")...) + app.Run(args) + } +} + +func cmdConsole(app *cli.App, c *cli.Context) { + for { + fmt.Printf("> ") + bufReader := bufio.NewReader(os.Stdin) + line, more, err := bufReader.ReadLine() + if more { + Exit("input is too long") + } else if err != nil { + Exit(err.Error()) + } + + args := []string{"tmsp"} + args = append(args, strings.Split(string(line), " ")...) + app.Run(args) + } +} + +// Get some info from the application +func cmdInfo(c *cli.Context) { + res, err := makeRequest(conn, types.RequestInfo{}) + if err != nil { + Exit(err.Error()) + } + fmt.Println(res) +} + +// Set an option on the application +func cmdSetOption(c *cli.Context) { + args := c.Args() + if len(args) != 2 { + Exit("set_option takes 2 arguments (key, value)") + } + _, err := makeRequest(conn, types.RequestSetOption{args[0], args[1]}) + if err != nil { + Exit(err.Error()) + } + fmt.Printf("%s=%s\n", args[0], args[1]) +} + // Append a new tx to application func cmdAppendTx(c *cli.Context) { - args := c.Args() // Args to AppendTx - conn, err := Connect(c.GlobalString("address")) + args := c.Args() + if len(args) != 1 { + Exit("append_tx takes 1 argument") + } + txString := args[0] + tx := []byte(txString) + if len(txString) > 2 && strings.HasPrefix(txString, "0x") { + var err error + tx, err = hex.DecodeString(txString[2:]) + if err != nil { + Exit(err.Error()) + } + } + + res, err := makeRequest(conn, types.RequestAppendTx{tx}) if err != nil { Exit(err.Error()) } - res, err := makeRequest(conn, types.RequestAppendTx{[]byte(args[0])}) - if err != nil { - Exit(err.Error()) - } - fmt.Println("Sent tx:", args[0], "response:", res) + fmt.Println("Sent tx:", txString, "response:", res) } // Get application Merkle root hash func cmdGetHash(c *cli.Context) { - conn, err := Connect(c.GlobalString("address")) - if err != nil { - Exit(err.Error()) - } res, err := makeRequest(conn, types.RequestGetHash{}) if err != nil { Exit(err.Error()) } - fmt.Println("Got hash:", Fmt("%X", res.(types.ResponseGetHash).Hash)) + fmt.Printf("%X\n", res.(types.ResponseGetHash).Hash) } // Commit the application state func cmdCommit(c *cli.Context) { - conn, err := Connect(c.GlobalString("address")) - if err != nil { - Exit(err.Error()) - } - _, err = makeRequest(conn, types.RequestCommit{}) + _, err := makeRequest(conn, types.RequestCommit{}) if err != nil { Exit(err.Error()) } @@ -101,11 +205,7 @@ func cmdCommit(c *cli.Context) { // Roll back the application state to the latest commit func cmdRollback(c *cli.Context) { - conn, err := Connect(c.GlobalString("address")) - if err != nil { - Exit(err.Error()) - } - _, err = makeRequest(conn, types.RequestRollback{}) + _, err := makeRequest(conn, types.RequestRollback{}) if err != nil { Exit(err.Error()) } diff --git a/example/counter.go b/example/counter.go index 870d5a80..d4b0e477 100644 --- a/example/counter.go +++ b/example/counter.go @@ -69,7 +69,7 @@ func (appC *CounterAppContext) AppendTx(tx []byte) ([]types.Event, types.RetCode func (appC *CounterAppContext) GetHash() ([]byte, types.RetCode) { hash := make([]byte, 32) - binary.PutVarint(hash, int64(appC.hashCount)) + binary.PutVarint(hash, int64(appC.txCount)) appC.hashCount += 1 return hash, 0 } diff --git a/server/server.go b/server/server.go index 1ed2dec4..e5945c81 100644 --- a/server/server.go +++ b/server/server.go @@ -3,6 +3,7 @@ package server import ( "bufio" "fmt" + "io" "net" "strings" @@ -86,7 +87,11 @@ func handleRequests(appC types.AppContext, closeConn chan error, conn net.Conn, var req types.Request wire.ReadBinaryPtr(&req, bufReader, 0, &n, &err) if err != nil { - closeConn <- fmt.Errorf("Error in handleRequests: %v", err.Error()) + if err == io.EOF { + closeConn <- fmt.Errorf("Connection closed by client") + } else { + closeConn <- fmt.Errorf("Error in handleRequests: %v", err.Error()) + } return } count++ diff --git a/test.sh b/test.sh new file mode 100644 index 00000000..00c1f203 --- /dev/null +++ b/test.sh @@ -0,0 +1,100 @@ +#! /bin/bash + +# Make sure the tmsp cli can connect to the dummy +echo "Dummy test ..." +dummy &> /dev/null & +PID=`echo $!` +sleep 1 +RESULT_HASH=`tmsp get_hash` +if [[ "$RESULT_HASH" != "" ]]; then + echo "Expected nothing but got: $RESULT_HASH" + exit 1 +fi +echo "... Pass!" +echo "" + +# Add a tx, get hash, commit, get hash +# hashes should be non-empty and identical +echo "Dummy batch test ..." +OUTPUT=`(tmsp batch) < /dev/null & +PID=`echo $!` +sleep 1 +OUTPUT=`(tmsp batch) <